CrownyJIT 8차 (대공사) — asm operand-stack 레지스터화
개요
2026-07-07 8차 세션. 6차(변수 레지스터캐싱)·7차(baldiv 인라인)가 회당 콜백 blr을 0으로 만든 뒤
남은
유일 지배 병목 = operand-stack 메모리 트래픽(스니펫 간 값전달이
[x20,x21] ldr/str 경유)을
제거했다. 스택머신 바이트코드는 조립시점에 스택 깊이가 결정적이므로, 깊이 슬롯을 고정 레지스터풀에
매핑해 operand-stack push/pop을 전부 레지스터 연산으로 바꿨다(
top-2가 아니라 전 깊이 레지스터화 완주).
공유
crownyc.c/
crownyc 무접촉(20:01). 모든 채택은 런타임 차등검증(byte-identical) 통과분만.
결과 요지 (native-region best-of-5, JIT'd 루프만)
| 커널 | 7차 mem-ostk | 8차 reg-stack | 배속 | 비고 |
|---|
| canon 3M | 47.05 ms | 15.19 ms | 3.1× | 스텐실(18.3ms)도 상회, 0.02s 목표 달성 |
| finance 3M(4 DIV) | 79.81 ms | 36.95 ms | 2.2× | |
| FP지역 10M | 196.58 ms | 31.85 ms | 6.2× | ostk 트래픽 최다였던 케이스 |
| deep 2M(깊이5) | 59.34 ms | 18.16 ms | 3.3× | 신규 중첩산술 회귀커널 |
- canon 목표(스텐실급 0.02s 이하) 달성: reg-stack 15.2ms=0.0152s < 스텐실 18.3ms.
- FP 6.2×: 지역var 루프가 operand-stack 트래픽이 가장 많아 레지스터화 이득 최대.
무엇을 배선했나
스니펫 라이브러리 (crownyc_jit_snippets.s)
- norm_blk / baldiv_blk 자족 블록 신설: 검증된 NORMINLINE(15instr,60B)·BALDIV(18instr,72B)
매크로를 심볼(
_cjs_norm_blk·
_cjs_baldiv_blk)로 감싼 위치독립 span. 내부 브랜치 전부 상대라
memcpy 삽입해도 정확. 레지스터 코드젠이 산술 norm/나눗셈에 이 블록을 그대로 복사 →
직접 인코딩
위험을 mov/ldr/str/add/sub/mul/cmp/cset/b 등 단순명령으로만 국한.
조립기 (crownyc_jit_hot.c)
- AArch64 명령어 인코더 신설(aw_movz/movk/mov/ldr_h/str_h/add/sub/mul/msub/addi/subi/cmp_rr/cmp_ri/
cset/b_rel/bcond + cj_emit_w/cj_emit_lit): 32bit 워드 직접 방출. 조건코드 매핑 cj_cmp_cc.
- cjasm_build → cjasm_build_mode(…,regmode) 리팩터 + wrapper. wrapper가 regmode=1(레지스터스택)
우선 시도, 실패 시 regmode=0(7차 메모리 ostk)로 자동 폴백.
CROWNY_JIT_NOREG=1로 reg 비활성.
- 레지스터-스택 PASS2: pass1(검증·접촉주소 수집)은 공유. pass2에서 operand-stack을 고정
레지스터풀
{x12~x17, x7, x8, x25, x27, x28}(11개)에 매핑. 깊이 추적은 기존 expr-kind 시뮬(esp)
재사용. DATA=movz/movk→레지스터, LOAD/STORE=캐싱슬롯 ldr/str(hole), 산술=레지스터간 op + norm_blk/
baldiv_blk 삽입, 비교=cmp/cset/×2−1, guard/backedge=cmp+b.cond(패치).
깊이>11 = ABORT→mem 폴백.
- 프롤로그/cacheload/cacheflush/에필로그는 6·7차 그대로 재사용(x19 hole·x25 rd·x26 wr). 본체가
x25/x27/x28을 스택슬롯으로 재사용해도 안전(cacheload는 body_start 前 완료, cacheflush는 x26만 사용).
레지스터 규약 (본체, 콜백 blr 0이므로 대부분 자유)
- 예약: x19=hole · x20=ostk(미사용) · x21=idx(미사용) · x22=maxiter · x23=it · x24=exited · x26=wr(에필로그 보존)
- 스크래치: x0..x6(baldiv_blk) · x9,x10,x11(norm_blk) · x0(산술 작업값)
- 레지스터스택 풀: x12,x13,x14,x15,x16,x17,x7,x8,x25,x27,x28 (11 슬롯). 깊이 초과=mem 폴백.
회귀 (base==on(cc)==asm(reg)==shared, md5 IDENTICAL)
| 커널 | 결과 |
|---|
| canon·arb·finance·fp지역 | ✅ IDENTICAL(4모드) |
| deep(신규, 중첩산술 깊이5) | ✅ IDENTICAL(4모드), reg-stack ADOPT |
| depth10(신규, 우중첩 깊이10) | ✅ IDENTICAL, x25/x27/x28 풀슬롯 검증·ADOPT |
| 배열합·배열쓰기·3D렌더러·유체시뮬·ARIMA | ✅ IDENTICAL(4모드) |
| 작업구분·브레인결재(stdin 판정코어) | ✅ IDENTICAL(4모드) |
| jithot 동반본(8차 증분P 추가·재컴파일) | ✅ IDENTICAL(base/asm/shared) |
-
14커널+동반본 전부 IDENTICAL. 진짜 off=
env -u CROWNY_JIT. JIT-off
crownyc-jit==공유
crownyc.
- 공유
crownyc(20:01:53)·crownyc.c(20:01) mtime 무손상.
전체 벤치 (total wall best-of-5, startup 포함)
| 커널 | base(off) | asm(8th reg) | on(cc) |
|---|
| canon 3M | 3.60s | 0.057s | 0.35s |
| finance 3M | 2.45s | 0.079s | 0.83s |
| FP지역 10M | 8.35s | 0.076s | 0.43s |
| deep 2M | 2.46s | 0.063s | 0.46s |
-
정직 소견: total wall은 이제 프로세스 startup(~0.05s: 바이너리 로드+self-test)이 지배해,
canon처럼 짧은 루프는 3× 루프이득이 벽시계에 안 드러난다(canon wall 0.057 ≈ 7차 0.054). 루프가 긴
FP 10M에서만 벽시계로도 뚜렷(7차 0.203s→8차 0.076s, 2.7×).
루프 자체 속도는 native-region 표가
정본(3~6× 실이득). 과장 없이: 잔여 startup은 별건(W^X 서명배포·self-test 캐시 최적화 영역).
관련 파일 (전부 /Users/ef/CrownyOS/crownyc/)
crownyc_jit_snippets.s — ★norm_blk·baldiv_blk 자족 블록 2개 신설(파일 끝, 매크로 인스턴스화).
crownyc_jit_hot.c — ★AArch64 인코더 + cj_emit_w/cj_emit_lit + cj_cmp_cc 신설. cjasm_build_mode(regmode)
리팩터 + 레지스터-스택 PASS2 + cjasm_build wrapper(reg 우선·mem 폴백).
crownyc_jit_hot.한선 — 동반본에 8차 증분P(레지스터스택판정·블록재사용판정) 2함수+4출력. 4모드 일치.
crownyc-jit — 재빌드: cc -O2 -o crownyc-jit crownyc_jitmain.c crownyc_jit_snippets.s -framework Security -framework CoreFoundation
crownyc.c/crownyc — 공유, 무접촉(20:01).
- 벤치/회귀:
/tmp/JIT실적용/{canon,finance,fp지역,deep,depth10,배열합,배열쓰기,3D렌더러,작업구분,브레인결재}.toau.
deep.한선·
depth10.한선=8차 신규(중첩산술 깊이5/우중첩 깊이10 회귀).
env 플래그
CROWNY_JIT=1(켬) · CROWNY_JIT_ASM=1(asm 조립, 8차부터 reg-stack 기본) · CROWNY_JIT_NOREG=1(reg 비활성=7차 mem경로)
CROWNY_JIT_STENCIL=1 · CROWNY_JIT_GATE=1 · CROWNY_JIT_DUMP=1 · CROWNY_JIT_TIME=1 · CROWNY_JIT_HOT=N
남은 작업 (다음 세션 인수인계)
- startup 고정비(~0.05s): 짧은루프 벽시계 지배항. 바이너리 로드+self-test. self-test 캐시(5차)로
cc-spawn은 생략됐으나 잔여. 루프이득이 이미 native-region에서 최대치라 별건 최적화 영역.
- TSHR/TSHL(op94/95) 스니펫:
×3ⁿ/÷3ⁿ 피홀이 asm BAIL 유발(deep 커널 최초판이 *3→op94로 bail).
레지스터모드에도 op94/95 추가하면 arb(
i/3)·
*3 포함 루프까지 커버. 4차부터 이월.
- 차등게이트 CAP 커버리지: gate가 첫 CAP=4096 iter만 검증 → i가 큰 후반부의 정수오버플로
(i
ii>3^24 후 int64 wrap) 발산을 놓칠 수 있음(reg/mem/cc 공통 기존 한계, 8차 무관). overflow 많은
커널은 채택돼도 base와 다를 수 있음 — CAP를 잔여추정 비례로 늘리거나 오버플로 가드 검토.
- asm 배열(증분3) 저ROI · 다탈출 가드 · W^X 서명배포 시 MAP_JIT/entitlement.
안전수칙 준수
- 공유
crownyc.c/crownyc 무수정·무재빌드(20:01). 산출물 crownyc-jit 별도. 데몬·커밋·영속인프라 없음.
- 기본 오프(opt-in). 부팅 self-test + 런타임 차등검증 게이트 이중 안전. 레지스터스택 코드젠 버그는
출력 무오염(MISMATCH=BAIL=인터프리터, 깊이초과=mem폴백). 차등검증이 최종 심판.