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 까지 같음 | 같음 |
| Backend | native, host 에 로드 | native (-O0) 또는 MLIR → LLVM, 그 뒤 lld |
| 단위 | 셀 (라이브러리는 세션에 한 번) | 패키지 |
| 결과 | host 메모리 | 실행 파일 / .a |
두 엔진은 같은 단형화와 rok-mir 를 공유하고 두 backend 가 같은 ABI 를 따르므로 의미가 같습니다.
9.6.7. 다음: 셀 실행 / 상태 / 에러
JIT 셀 실행 흐름과 상태 관리 에서 셀별 동작 (top-level 변수, 재정의, 에러) 을 정의합니다.