소개
일반적인 컴파일러는 다음 단계를 수행합니다.
- 구문 분석 : 소스 텍스트가 추상 구문 트리 (AST)로 변환됩니다.
- 다른 모듈에 대한 참조 해결 (C는 링크 할 때까지이 단계를 연기합니다).
- 의미 론적 검증 : 도달 할 수없는 코드 또는 중복 선언과 같이 의미가 맞지 않는 구문 적으로 올바른 문장을 제거합니다.
- 동등한 변환 및 높은 수준의 최적화 : AST는 동일한 의미로보다 효율적인 계산을 나타내도록 변환됩니다. 여기에는 공통 하위 표현식 및 상수 표현식의 조기 계산, 과도한 로컬 할당 제거 ( SSA 참조 ) 등이 포함됩니다.
- 코드 생성 : AST는 점프, 레지스터 할당 등을 통해 선형 저수준 코드로 변환됩니다. 이 단계에서 일부 함수 호출을 인라인하고 일부 루프를 풀 수 있습니다.
- 들여다 보는 구멍 최적화 : 낮은 수준의 코드를 스캔하여 간단한 로컬 비 효율성을 제거합니다.
대부분의 최신 컴파일러 (예 : gcc 및 clang)는 마지막 두 단계를 한 번 더 반복합니다. 초기 코드 생성을 위해 중간 수준의 저급이지만 플랫폼 독립적 인 언어를 사용합니다. 그런 다음 해당 언어는 플랫폼 최적화 방식으로 거의 동일한 작업을 수행하는 플랫폼 별 코드 (x86, ARM 등)로 변환됩니다. 여기에는 가능한 경우 벡터 명령어 사용, 분기 예측 효율을 높이기위한 명령어 순서 변경 등이 포함됩니다.
그런 다음 객체 코드를 연결할 준비가되었습니다. 대부분의 네이티브 코드 컴파일러는 실행 파일을 생성하기 위해 링커를 호출하는 방법을 알고 있지만 컴파일 단계는 아닙니다. Java 및 C #과 같은 언어에서는로드시 VM에 의해 링크가 완전히 동적 일 수 있습니다.
기본 사항 기억
이 고전적인 순서는 모든 소프트웨어 개발에 적용되지만 반복됩니다.
순서의 첫 번째 단계에 집중하십시오. 작동 할 수있는 가장 간단한 것을 만듭니다.
책을 읽으십시오!
Aho와 Ullman 의 Dragon Book 을 읽으십시오 . 이것은 고전적이며 오늘날에도 여전히 적용 가능합니다.
현대 컴파일러 디자인 도 칭찬합니다.
이 내용이 지금 너무 어려우면 먼저 구문 분석에 대한 소개를 읽으십시오. 일반적으로 구문 분석 라이브러리에는 소개 및 예제가 포함됩니다.
그래프, 특히 나무로 작업하는 것이 편안해야합니다. 이것들은 프로그램이 논리적 수준에서 만들어진 것들입니다.
언어를 잘 정의하십시오
원하는 표기법을 사용하되, 언어에 대한 완전하고 일관된 설명이 있어야합니다. 여기에는 구문과 의미가 모두 포함됩니다.
미래의 컴파일러를위한 테스트 사례로 새로운 언어로 된 코드 스 니펫을 작성할 때가되었습니다.
좋아하는 언어를 사용하십시오
파이썬이나 루비 또는 쉬운 언어로 컴파일러를 작성해도 괜찮습니다. 잘 알고있는 간단한 알고리즘을 사용하십시오. 첫 번째 버전은 빠르거나 효율적이거나 기능이 완전하지 않아도됩니다. 정확하고 수정하기 만하면됩니다.
필요한 경우 다른 언어의 컴파일러 단계를 다른 언어로 작성해도됩니다.
많은 테스트 작성 준비
전체 언어는 테스트 사례로 다루어야합니다. 효과적으로 그것들에 의해 정의 될 것입니다. 선호하는 테스트 프레임 워크에 익숙해 지십시오. 첫날부터 테스트를 작성하십시오. 잘못된 코드를 감지하는 대신 올바른 코드를 받아들이는 '긍정적 인'테스트에 집중하십시오.
모든 테스트를 정기적으로 실행하십시오. 진행하기 전에 깨진 테스트를 수정하십시오. 유효한 코드를 받아 들일 수없는 잘못 정의 된 언어로 끝나는 것은 부끄러운 일입니다.
좋은 파서 만들기
파서 생성기는 많이 있습니다. 원하는 것을 고르세요. 또한 처음부터 자신의 파서를 쓸 수 있습니다,하지만 그것은 단지 가치가 언어의 구문은 경우 죽은 간단합니다.
파서는 구문 오류를 감지하고보고해야합니다. 긍정적이든 부정적이든 많은 테스트 사례를 작성하십시오. 언어를 정의하면서 작성한 코드를 재사용하십시오.
파서의 출력은 추상 구문 트리입니다.
언어에 모듈이있는 경우 파서의 출력은 생성 한 '객체 코드'의 가장 간단한 표현 일 수 있습니다. 트리를 파일로 덤프하고 빠르게 다시로드하는 방법에는 여러 가지가 있습니다.
시맨틱 유효성 검사기 만들기
대부분의 언어는 특정 상황에서 의미가 없을 수있는 구문 적으로 올바른 구성을 허용합니다. 예를 들어 동일한 변수를 중복 선언하거나 잘못된 유형의 매개 변수를 전달하는 것이 있습니다. 유효성 검사기는 트리를 보면서 이러한 오류를 감지합니다.
또한 유효성 검사기는 사용자 언어로 작성된 다른 모듈에 대한 참조를 확인하고 이러한 다른 모듈을로드 한 다음 유효성 검사 프로세스에 사용합니다. 예를 들어,이 단계는 다른 모듈에서 함수에 전달 된 매개 변수의 수가 올바른지 확인합니다.
다시 말하지만 많은 테스트 사례를 작성하고 실행하십시오. 사소한 경우는 스마트하고 복잡한 문제 해결에 없어서는 안될 필수 요소입니다.
코드 생성
가장 간단한 기술을 사용하십시오. if
HTML 템플릿과 달리 언어 구문 (예 : 명령문)을 매개 변수가 적은 코드 템플릿 으로 직접 변환하는 것이 좋습니다.
다시 한 번 효율성을 무시하고 정확성에 집중하십시오.
플랫폼 독립적 인 저수준 VM을 대상으로 함
하드웨어 관련 세부 사항에 관심이 없다면 저수준 항목을 무시한다고 가정합니다. 이러한 세부 사항은 복잡하고 복잡합니다.
귀하의 옵션 :
- LLVM : 일반적으로 x86 및 ARM에 대해 효율적인 기계 코드 생성이 가능합니다.
- CLR : 주로 x86 / Windows 기반의 .NET을 대상으로합니다. 좋은 JIT가 있습니다.
- JVM : 매우 다중 플랫폼 인 Java 세계를 대상으로 JIT가 우수합니다.
최적화 무시
최적화는 어렵다. 거의 항상 최적화는 시기상조입니다. 비효율적이지만 올바른 코드를 생성하십시오. 결과 코드를 최적화하기 전에 전체 언어를 구현하십시오.
물론 사소한 최적화도 도입 할 수 있습니다. 그러나 컴파일러가 안정되기 전에 교활하고 털이 많은 것을 피하십시오.
그래서 무엇?
이 모든 것이 당신을 너무 위협하지 않으면 계속 진행하십시오! 간단한 언어의 경우 각 단계는 생각보다 간단 할 수 있습니다.
컴파일러가 만든 프로그램에서 'Hello world'를 보는 것이 그만한 가치가 있습니다.