← 목록
기타 2026-07-07 8KB 읽기 9분

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-ostk8차 reg-stack배속비고
canon 3M47.05 ms15.19 ms3.1×스텐실(18.3ms)도 상회, 0.02s 목표 달성
finance 3M(4 DIV)79.81 ms36.95 ms2.2×
FP지역 10M196.58 ms31.85 ms6.2×ostk 트래픽 최다였던 케이스
deep 2M(깊이5)59.34 ms18.16 ms3.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 3M3.60s0.057s0.35s
finance 3M2.45s0.079s0.83s
FP지역 10M8.35s0.076s0.43s
deep 2M2.46s0.063s0.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

남은 작업 (다음 세션 인수인계)

  1. startup 고정비(~0.05s): 짧은루프 벽시계 지배항. 바이너리 로드+self-test. self-test 캐시(5차)로
cc-spawn은 생략됐으나 잔여. 루프이득이 이미 native-region에서 최대치라 별건 최적화 영역.
  1. TSHR/TSHL(op94/95) 스니펫: ×3ⁿ/÷3ⁿ 피홀이 asm BAIL 유발(deep 커널 최초판이 *3→op94로 bail).
레지스터모드에도 op94/95 추가하면 arb(i/3*3 포함 루프까지 커버. 4차부터 이월.
  1. 차등게이트 CAP 커버리지: gate가 첫 CAP=4096 iter만 검증 → i가 큰 후반부의 정수오버플로
(iii>3^24 후 int64 wrap) 발산을 놓칠 수 있음(reg/mem/cc 공통 기존 한계, 8차 무관). overflow 많은 커널은 채택돼도 base와 다를 수 있음 — CAP를 잔여추정 비례로 늘리거나 오버플로 가드 검토.
  1. asm 배열(증분3) 저ROI · 다탈출 가드 · W^X 서명배포 시 MAP_JIT/entitlement.

안전수칙 준수

  • 공유 crownyc.c/crownyc 무수정·무재빌드(20:01). 산출물 crownyc-jit 별도. 데몬·커밋·영속인프라 없음.
  • 기본 오프(opt-in). 부팅 self-test + 런타임 차등검증 게이트 이중 안전. 레지스터스택 코드젠 버그는
출력 무오염(MISMATCH=BAIL=인터프리터, 깊이초과=mem폴백). 차등검증이 최종 심판.