목표 및 아키텍처 개요

상위 수준 목표

전통적으로 언어 인터프리터는 C/Posix, Java, C# 같은 대상 플랫폼 언어로 작성됩니다. 각 구현은 애플리케이션 소스 코드와 대상 환경 사이의 기본적인 매핑을 제공합니다. .NET 프레임워크나, 어느 정도는 Java 가상 머신처럼 “모든 것을 아우르는” 환경의 목표 중 하나는 표준화된 고수준 기능을 제공하여 언어 구현자가 언어 구현을 작성할 수 있도록 지원하는 것입니다.

PyPy는 더 야심 찬 접근 방식을 실험하고 있습니다. 저희는 RPython 언어이라고 하는 고수준 언어 Python의 부분집합을 사용하며, 이 언어로 저수준 세부 사항을 거의 참조하지 않고 그에 거의 의존하지도 않는 단순한 인터프리터 형태로 언어를 작성합니다. RPython 툴체인은 적절한 저수준 측면을 삽입하여 저희가 선택한 플랫폼에 맞는 구체적인 가상 머신을 만들어 냅니다. 다른 기능 구성과 플랫폼 구성을 선택하여 그 결과물을 사용자 정의할 수 있습니다.

저희의 목표는 언어 구현자가 겪는 문제, 즉 o개의 핵심 설계 결정을 두고 l개의 동적 언어와 p개의 플랫폼을 위해 l * o * p개의 인터프리터를 작성해야 하는 문제에 대해 가능한 해결책을 제시하는 것입니다. PyPy는 이 변수들을 각각 독립적으로 바꿀 수 있게 하여 다음과 같이 되도록 하는 것을 목표로 합니다:

  • l: 분석 대상 언어는 발전시키거나 완전히 교체할 수 있습니다.
  • o: 변환 과정을 조정하고 최적화하여 다양한 모델과 트레이드오프에 따라 플랫폼별 코드를 생성할 수 있습니다.
  • p: 새로운 변환기 백엔드를 작성하여 다양한 물리적 플랫폼과 가상 플랫폼을 대상으로 삼을 수 있습니다.

이와 달리, 표준화된 대상 환경(예를 들어 .NET)은 그 환경이 관여하는 범위 안에서 p=1을 강제합니다. 이는 그 위에 무언가를 빌드할 수 있는 더 높은 수준의 기반을 제공함으로써 o를 조금 더 작게 만드는 데 도움이 됩니다. 그럼에도 저희는 하나의 공통 환경을 사용하도록 강제할 필요는 없다고 생각합니다. PyPy의 목표는 적어도 언어 구현에 관한 한, 표준화에 의존하지 않는 l * o * p 문제 접근 방식을 보여 줌으로써 이 주장에 힘을 싣는 것입니다.

이 목표에서 가장 야심 찬 부분은 소스 인터프리터를 대상 플랫폼용 인터프리터로 변환하는 데 그치지 않고, 언어에 독립적인 방식으로 JIT(Just-In-Time) 컴파일러를 생성하는 것입니다. 이는 관련된 복잡성 때문에 일반적으로 매우 어렵다고 여겨지는 언어 구현 분야입니다.

아키텍처

RPython 툴체인의 역할은 RPython 언어 프로그램을 여러 대상 플랫폼 중 하나에 맞는 효율적인 버전으로 변환하는 것입니다. 대상 플랫폼은 일반적으로 Python보다 상당히 저수준입니다.

저희가 택한 접근 방식은 소스 RPython 프로그램의 추상화 수준을 여러 단계에 걸쳐 높은 수준에서 대상 플랫폼의 수준까지 낮추는 것이며, 대상 플랫폼이 무엇이든 관계없습니다. 현재 저희는 크게 두 가지 종류의 대상 플랫폼을 지원합니다. 하나는 구조체와 포인터를 갖춘 C와 유사한 메모리 모델을 전제로 하는 플랫폼이고, 다른 하나는 (예를 들어 Java 및 .NET 가상 머신처럼) 클래스, 인스턴스, 메서드를 갖춘 객체 지향 모델을 전제로 하는 플랫폼입니다.

RPython 툴체인은 RPython 소스 코드나 구문 트리를 전혀 보지 않으며, 입력으로 주어진 함수 객체의 동작을 정의하는 코드 객체에서 시작합니다. 이는 미리 임포트된 RPython 프로그램을 대상 플랫폼에 적합한 실행 가능한 형태로 “동결”하는 것이라고 볼 수 있습니다.

변환 과정의 단계를 요약하면 다음과 같습니다.

  • 각 소스 함수의 코드 객체는 흐름 그래프 빌더에 의해 제어 흐름 그래프로 변환됩니다.
  • 제어 흐름 그래프는 어노테이터가 처리합니다. 어노테이터는 프로그램 전체에 대한 타입 추론을 수행하여, 제어 흐름 그래프의 각 변수에 런타임에 가질 수 있는 타입을 어노테이션합니다.
  • 어노테이터가 제공한 정보는 RTyper가 제어 흐름 그래프의 고수준 연산을 대상 플랫폼의 추상화 수준에 더 가까운 연산으로 변환하는 데 사용됩니다.
  • 그다음에는 선택적으로 다양한 변환 <optional-transformations>을 적용할 수 있습니다. 이러한 변환은 예를 들어 인라이닝 같은 최적화를 수행하거나, 스택리스(stackless) 방식의 동시성 같은 기능을 추가하거나, 가비지 컬렉터를 위한 코드를 삽입합니다.
  • 그런 다음, 그래프는 대상 플랫폼용 소스 코드로 변환되고 실행 파일로 컴파일됩니다.

이 과정은 RPython 툴체인에 관한 문서와 논문 Compiling dynamic language implementations에 훨씬 더 자세히 설명되어 있습니다.

더 읽을거리