컴파일러 내부 구조

Perry를 빠르게 만드는 핵심 설계 결정

01NaN-Boxing

값은 포인터를 위한 특수 비트 패턴을 가진 64비트 부동소수점으로 저장되어 런타임 오버헤드 없이 유니온 타입을 구현합니다. 이를 통해 (string | number)[]가 효율적으로 작동합니다.

IEEE 754 NaN 페이로드 비트에 타입 정보를 직접 인코딩함으로써, Perry는 태그드 유니온이나 박싱된 값의 필요성을 제거합니다. 단일 64비트 워드가 모든 JavaScript 값 타입을 표현할 수 있습니다 — 숫자는 자연스러운 표현을 사용하고, 문자열, 객체 및 기타 힙 할당 값은 NaN 범위 내의 포인터 비트 패턴을 사용합니다.

02Monomorphization

제네릭은 Rust처럼 컴파일 타임에 특수화됩니다. 각 타입 인스턴스화가 최적화된 코드를 생성하여 런타임 타입 체크 오버헤드를 제거합니다.

Array'<'number'>'와 Array'<'string'>'을 작성하면, Perry는 두 개의 별도로 완전히 최적화된 구현을 생성합니다. 숫자 배열 연산은 직접적인 머신 산술을 사용합니다 — 타입 가드 없음, 동적 디스패치 없음, 박싱 없음.

03Static Dispatch

가상 테이블 없음. 메서드 호출은 컴파일 타임에 해결되어 직접적인 함수 호출과 인라인 최적화를 가능하게 합니다.

전통적인 OOP 런타임은 메서드 해결에 vtable을 사용하여 모든 호출에 간접 참조 계층을 추가합니다. Perry는 컴파일 중에 모든 메서드 호출을 정적으로 해결하여 인터페이스 메서드 호출을 직접 점프로 변환합니다. 이는 공격적인 인라인화도 가능하게 합니다 — 작은 메서드는 종종 호출 사이트에 직접 삽입됩니다.

04Zero-Cost Abstractions

TypeScript 클래스, 인터페이스, 제네릭은 런타임 표현 오버헤드 없이 효율적인 네이티브 코드로 컴파일됩니다.

TypeScript의 타입 시스템은 컴파일 타임에만 존재하며, Perry는 이를 그대로 구현합니다. 인터페이스는 런타임 코드를 전혀 생성하지 않습니다. 클래스 계층은 평탄화됩니다. 제네릭 제약은 구체적인 타입으로 해결됩니다. 컴파일된 바이너리에는 실제 로직만 포함되며, 인터프리터가 일반적으로 가지고 있는 추상화 메커니즘은 포함되지 않습니다.