rokdocs
v0.1

9.6. JIT 엔진

이 절에서는 REPL 과 Jupyter 가 사용하는 JIT 실행 엔진을 정의합니다. 실행 엔진과 사용자 표면 에서 정한 JIT 엔진의 구현입니다. 셀 세션의 규칙은 9.7.0 이 정합니다.

9.6.1. 구조: rokc 와 JIT host

세션은 런타임을 담고 세션 동안 사는 별도의 host 프로세스 에서 실행됩니다. host 는 rokc 와 함께 미리 빌드되어 내장 lld 로 링크되며, libLLVM 이나 ORC 없이 셀 object 를 직접 로드합니다.

rokc (front end)                         JIT host (세션 동안 하나)
  셀 → parse → HIR → MIR                     librokr.a 전체
     → native backend                        런타임 초기화는 한 번
     → 기계어 + relocation  ─────────────►   실행 가능 메모리에 로드, 심볼 해소
                                             → 셀 진입 함수를 한 번 실행
  • 셀은 9.2.10 의 backend 선택을 따릅니다. -O0 에서 셀은 host 가 매핑하고 링크하는 native object (9.2.11) 이며 LLVM 을 쓰지 않습니다. -O0 보다 높거나 native backend 가 맡지 않는 코드 (__mlir_op, 그 밖의 대상) 는 내장 MLIR → LLVM 파이프라인으로 object 를 만들어 같은 실행 가능 메모리에 로드합니다.
  • host 는 코드를 실행 가능 메모리에 놓고, relocation 을 런타임, libc, 앞 셀에 대해 해소한 뒤 셀의 진입 함수를 한 번 실행합니다.
  • host 는 런타임 (librokr.a) 의 모든 심볼을 export 하며, 셀 코드는 런타임과 libc 심볼을 host 프로세스에서 찾습니다.

9.6.2. 코드 로드

셀은 object 파일입니다: -O0 에서는 native, 그보다 높으면 LLVM 이 컴파일한 것. 그 코드는 relocation 을 가진 위치 독립 코드입니다. host 는 세션에 하나의 심볼 테이블을 두며, 셀의 정의는 셀을 로드한 뒤 여기에 더해집니다. 해소할 수 없는 심볼은 그 이름을 밝히는 에러로 셀을 실패시킵니다 (9.7).

9.6.3. 심볼과 재정의

한 세션은 심볼 테이블 하나를 씁니다. 모든 셀의 정의가 여기에 쌓입니다.

  • 이미 정의된 심볼을 셀이 다시 정의하면, host 는 그 정의의 이름을 <name>.r<cell> 로 바꿉니다. 이후 셀의 참조는 가장 새 이름에 링크됩니다.
  • 재정의는 다음 셀부터 효력이 있습니다 (9.7.0 규칙 3).
  • 재정의된 심볼에 의존하는 함수는 rokc 가 다시 컴파일합니다. 그래서 재정의 전에 컴파일된 코드라도 이후 셀이 다시 닿으면 새 정의를 씁니다 (재정의된 f 를 부르는 g).
  • 셀 코드와 옛 정의는 세션이 끝날 때까지 해제하지 않습니다.

9.6.4. 라이브러리 코드의 재사용

표준 라이브러리와 의존 패키지의 코드는 세션에 한 번만 컴파일합니다.

  • rokc 는 host 가 이미 로드한 함수와 그 MIR 을 기억합니다. 셀의 함수가 이미 로드된 것과 같으면 코드 대신 참조만 넘깁니다.
  • MIR 이 같으면 뜻도 같도록 셀 사이의 이름을 고정합니다. 문자열 리터럴 global 은 내용 hash 로 이름을 짓고, GC type id 는 ScanTable 내용을 키로 세션 동안 고정합니다.
  • 이 셀이 새로 정의하는 것 (바뀐 함수, static, 바인딩 슬롯) 을 직접 또는 간접으로 부르는 함수는 다시 컴파일합니다.
  • 셀의 객체는 셀이 더하는 것만 담습니다: 새로 정의하는 함수와, 그 코드가 읽는 이전 셀의 static 에 대한 초기화 guard. 그 코드가 가리키는 심볼만 이름으로 부릅니다.

9.6.5. 실행 흐름 (REPL 한 줄 / Jupyter 한 셀)

1. rokc: 입력 → parse → HIR (에러면 셀을 버림, 세션 상태 그대로)
2. rokc: 셀 → MIR → native backend → 기계어
         (이미 로드된 함수는 참조로, 재정의는 새 이름으로)
3. host: 세션 스냅샷을 fork 한 프로세스에서 코드를 로드하고 셀을 한 번 실행
4. 성공: 그 프로세스가 새 스냅샷이 됨. 값이 있으면 `타입: 값` 출력
   실패 (panic, 종료, 시그널, 로드 실패): 이전 스냅샷이 이어받음 (9.7.0 규칙 5)

3 단계는 Unix 에서 fork 합니다. fork 가 없는 host (Windows) 는 셀 전에 세션의 메모리와 스레드를 저장하고 셀이 실패하면 되돌려 놓으며, 관찰되는 결과는 fork 한 스냅샷과 같습니다.

셀 하나의 지연은 약 0.1 초이며, 대부분은 front end 가 차지합니다 (참고값).

9.6.6. JIT 와 AOT 의 차이

측면JIT (REPL / Jupyter)AOT
파이프라인rok-mir 까지 같음같음
Backendnative, host 에 로드native (-O0) 또는 MLIR → LLVM, 그 뒤 lld
단위셀 (라이브러리는 세션에 한 번)패키지
결과host 메모리실행 파일 / .a

두 엔진은 같은 단형화와 rok-mir 를 공유하고 두 backend 가 같은 ABI 를 따르므로 의미가 같습니다.

9.6.7. 다음: 셀 실행 / 상태 / 에러

JIT 셀 실행 흐름과 상태 관리 에서 셀별 동작 (top-level 변수, 재정의, 에러) 을 정의합니다.

GitHub에서 이 페이지 고치기