← 목록
기타 2026-07-06 14KB 읽기 17분

CrownyJIT 6차 — asm 조립 레지스터 캐싱 + 인라인 norm + FP상대 지역루프 asm

개요

2026-07-06 6차 세션. 4차/5차가 남긴 이월 1과제(asm 레지스터 캐싱)와 2과제(FP상대 asm)를 배선. 4차 asm 조립경로는 정확하나 steady-state가 스텐실보다 20배 느렸다 — 회당 ~9개 콜백 blr (LOAD마다 rd·STORE마다 wr·산술마다 norm·DIV마다 baldiv)이 병목. 6차는 루프변수를 레지스터/스크래치에 캐싱하고 norm을 완전 인라인화해 회당 콜백을 9→1(baldiv만)로 줄였고, 그 과정에서 FP상대 지역루프 asm 지원까지 통합 달성. 공유 crownyc.c/crownyc 무접촉(20:01). 모든 채택은 런타임 차등검증 (byte-identical) 통과분만.

시작 시 그라운딩 (warm, self-test 캐시됨) — 4차 0.85s의 실체

canon 3M실측비고
스텐실0.02s완전 네이티브 인라인(콜백 0)
asm(4차/5차)0.45s정확하나 회당 ~9 blr → 스텐실의 20배
cc0.58scc 컴파일 대기 포함
- 4차 문서의 "asm 0.85s"는 self-test cc스폰 미캐시 콜드값. 캐시 warm 재실측 = 0.45s. 어느 쪽이든 병목은 동일: 회당 콜백 blr(rd/wr/norm/baldiv).

증분별 상태

#목표상태요지
1asm 레지스터 캐싱완주접촉 스칼라변수를 프롤로그 rd()1회→hole.cached[k], 본체 ldr/str(blr無), 에필로그 wr()1회
2asm FP상대 지역루프완주주소=fpbase(=frame_ptr, 디스패치시 hole 전달)+off. 캐싱과 통합(본체 주소무관)이라 cacheload_fp/flush_fp만 추가
+인라인 norm(계획외 필수)완주산술 norm을 범위검사+모듈러감축(sdiv/msub/csel)까지 완전 인라인 → blr x27 소멸. canon ii는 대부분 범위초과라 이게 최대 병목이었음
3asm 배열 원소/설정미착수(구조적 저ROI)배열 index는 회당 가변 → 캐싱 불가(6차 핵심 이점 무효). 배열루프는 cc경로가 이미 6.4~8.4×. 아래 판단 근거 참조

무엇을 배선했나

스니펫 라이브러리 (crownyc_jit_snippets.s)

  • NORMINLINE 매크로: ldr lim; cmp; b.gt/b.lt 범위검사 → 범위내 무동작. 초과 시 sdiv/msub
v%M, csel로 부호/범위 보정 — 완전 인라인, blr 없음. cj_norm/CJN_S와 bit-identical(모듈러식).
  • cacheload_abs/fp: 프롤로그단계, 접촉주소마다 rd() 1회 → cached[k]. abs=movz/movk immediate addr,
fp=ldr fpbase; add off. cacheflush_abs/fp: 에필로그단계 wr() 1회.
  • load_var/store_var: 본체 LOAD/STORE. 직전 DATA/loadfp/ADD가 push한 주소를 폐기(sub x21) +
cached[k] ldr/str. 콜백 blr 없음. cached 오프셋은 str/ldr imm12(uoff) 패치.
  • loadfp: LOAD_FP(80) — fpbase를 operand-stack에 push(주소형성용, 이후 load_var가 폐기).
  • add/sub/mul/div/mod에 NORMINLINE 적용(div/mod의 baldiv만 blr x28 유지).
  • 새 hole struct: fpbase@32 lim@40 modM@64 cached[64]@72 ostk[128]@584 (x20=x19+584).

조립기 (crownyc_jit_hot.c cjasm_build — 2패스 재작성)

  • pass1: expr-kind 시뮬(0=value 1=literal 2=fpbase 3=fpaddr)로 검증 + 접촉주소 (kind,off) 수집.
LOAD_FP;DATA off;ADD → FPADDR(off) 폴딩(cc경로와 동일). 미지원/계산된주소/주소산술 = BAIL(cc폴백).
  • prologue → cacheload×N → body_start → pass2(본체 방출) → epi:cacheflush×N → epilogue.
guard 탈출/maxiter는 epi(cacheflush)로 분기 → 반드시 cacheflush 후 return.
  • 캐싱 통합 덕에 본체는 cached[k]만 접근(주소 무관) → FP지원이 cacheload/flush 주소계산에만 국한.
  • 슬롯메타 cj_taddr/cj_tkind(ABS/FP 혼합)로 차등게이트가 ABS·FP 양쪽 정확 검증.
  • 새 패치헬퍼: cj_patch_uoff(LDR/STR imm12=byte_off/8), cj_patch_add_imm(ADD imm12).
  • cj_dispatch: h.fpbase=frame_ptr, h.lim=141214768240, h.modM=282429536481 세팅.

회당 콜백 blr 감축 (canon 캐노니컬 loop 기준)

연산4차 asm6차 asm
LOAD i×4 (guard/mul/incr용)4 blr(rd)0 (cached ldr)
STORE s,i ×22 blr(wr)0 (cached str)
norm ×4 (mul/div/add/add)4 blr0 (인라인)
baldiv ×1 (DIV /7)1 blr1 blr(유지)
회당 계~11 blr1 blr
- 잔여 격차(asm 0.05 vs 스텐실 0.02) = baldiv blr 1회 + operand-stack 메모리 트래픽(스텐실은 전부 레지스터).

벤치 (best-of-3, warm, 단일 wall-clock, 전부 BYTE-IDENTICAL)

커널base(no-JIT)asm(6차)stencilcc승자
canon s+=(i*i)/7 3M10.31s0.05s0.02s0.58sstencil(캐노니컬)·asm 2위
finance 복리 3M(4 DIV)2.48s0.10s0.80s0.84sasm(스텐실 미매칭→cc)
FP지역 10M(지역var loop)8.30s0.20s0.39s0.39sasm(FP=6차 신규, 스텐실 미매칭)
- \ 스텐실은 캐노니컬 1종만 매칭 → finance(다DIV)·FP는 BAIL→cc 폴백값. 6차 asm은 셋 모두 최고 또는 근접, 그리고 FP·다DIV 임의루프를 레지스터캐싱 속도로 가속하는 유일한 경로.
  • canon asm: 0.45s→0.05s(9×), 목표(스텐실급 0.43s 이하) 초과 달성. asm_setup≈9µs.
  • FP 10M asm(신규 능력): base 8.30s→0.20s(41×), cc 0.39s 대비 2×.

회귀 (base==cc==asm==shared 전부 IDENTICAL, md5)

커널결과
canon·arb·finance·fp지역✅ IDENTICAL(4모드)
배열합·배열쓰기·3D렌더러✅ IDENTICAL(4모드)
작업구분·브레인결재(stdin 판정코어)✅ IDENTICAL(4모드)
jithot 동반본(2488B stdout)✅ IDENTICAL(base/cc/asm/shared, diff clean)
- 9커널+동반본 전부 IDENTICAL. 진짜 off = env -u CROWNY_JIT.
  • 공유 crownyc(20:01:53)·crownyc.c(20:01) mtime 무손상. JIT-off crownyc-jit==공유 crownyc.
  • arb: opcode 94(TSHL/TSHR 트릿시프트, i/3 피홀)로 asm BAIL→인터프리터(출력정확). 4차 남은과제 #3(미해소),
구 빌더도 동일 BAIL이라 회귀 아님.
  • 배열 커널(asm 모드): asm 조립기가 배열 opcode(407/415)에서 BAIL→cc폴백. 배열합 8.7×·배열쓰기 4.5×·
3D 1.6× 유지(회귀 없음). asm은 순수 스칼라 전용.

왜 증분3(배열 asm)을 미착수했나 (판단 근거)

  • 6차의 핵심 이점 = 루프변수를 경계로 호이스팅. 그러나 배열 원소 rd(base+i)의 index i는
회당 가변 → 프롤로그로 호이스팅 불가. 배열루프 asm은 결국 cc경로처럼 회당 rd/wr 콜백이 필요 → 캐싱 이득이 구조적으로 소멸(base만 캐싱 가능하나 index/값 콜백은 잔존).
  • cc경로가 이미 배열 읽기 8.4×·쓰기 6.4×. asm 배열은 cc를 복제할 뿐 6차 캐싱 이점을 못 살림 → 저ROID.
  • "여유 시" 항목이라 스칼라 캐싱(주 목표)에 집중. 배열 asm은 후순위 유지.

관련 파일 (전부 /Users/ef/CrownyOS/crownyc/)

  • crownyc_jit_snippets.s — ★NORMINLINE 매크로, cacheload/flush_abs/fp, load_var/store_var, loadfp
스니펫 추가. prologue x20=+584. add/sub/mul/div/mod에 인라인 norm. 새 hole 오프셋.
  • crownyc_jit_hot.c — ★cjasm_build 2패스 재작성(레지스터캐싱+FP+ABS 혼합), cjasm_hole 확장
(fpbase/lim/modM/cached[64]), cj_patch_uoff·cj_patch_add_imm, cjasm_find_slot, cj_dispatch fpbase/lim/modM.
  • crownyc_jit_hot.한선 — 판정 로직 동반본. 6차 증분L~N(레지스터캐싱·인라인norm·FP asm) 추가. 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,arb,finance,fp지역,배열합,배열쓰기,3D렌더러,작업구분,브레인결재}.toau.
fp지역.한선=6차 신규 FP 테스트(함수 지역var loop).

env 플래그 (불변)

  • CROWNY_JIT=1(켬), CROWNY_JIT_HOT=N(백엣지 임계), CROWNY_JIT_ASM=1(asm 조립 우선),
CROWNY_JIT_STENCIL=1, CROWNY_JIT_GATE=1, CROWNY_JIT_DUMP=1, CROWNY_JIT_NOCACHE=1, CROWNY_JIT_TIME=1.

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

  1. asm baldiv 인라인: 마지막 1 blr/iter(DIV/MOD의 baldiv). sdiv+msub+abs+csel로 인라인하면
canon이 스텐실(0.02s)에 근접 가능. 정의 명확(잘 정의된 스니펫), 게이트가 검증. 저위험.
  1. operand-stack 레지스터화: 스텐실과의 잔여격차는 ostk 메모리 트래픽. 슬롯을 레지스터할당하려면
copy-and-patch 스니펫 연접 모델을 넘어 레지스터할당 컴파일러 필요 → 큰 작업. cc경로가 이미 그 역할.
  1. TSHR/TSHL(op94/95) 스니펫: ÷×3ⁿ 피홀. arb(i/3) 등 asm 조립 가능케. 4차부터 이월.
  2. asm 배열(증분3): 위 판단대로 저ROI. base 캐싱만 부분이득 — 필요시 index 가변 콜백 유지 설계.
  3. 다탈출 가드·비순수 잎(2차 이월). W^X 서명배포 시 MAP_JIT/entitlement.

안전수칙 준수

  • 공유 crownyc.c/crownyc 무수정·무재빌드(20:01). 산출물 crownyc-jit 별도.
  • 기본 오프(opt-in). 부팅 self-test + 런타임 차등검증 게이트 이중 안전. 신규영역(레지스터캐싱·인라인norm·
FP조립) 전부 동일 게이트 통과분만 채택 — 캐싱/패치/FP 버그는 출력 무오염(MISMATCH=BAIL=인터프리터).
  • 데몬·커밋·영속인프라 생성 없음.

CrownyJIT 7차 (2026-07-06) — asm baldiv 인라인 (마지막 1 blr/iter 제거)

개요

6차가 회당 콜백을 9→1(baldiv만)로 줄인 뒤 남은 이월 1과제. DIV/MOD의 마지막 blr x28(=cj_baldiv 콜백)을 sdiv+msub+cneg(abs)+eor(부호)+cneg(±1 보정)로 완전 인라인해 회당 콜백을 1→0으로 만들었다. 공유 crownyc.c/crownyc 무접촉(20:01). 모든 채택은 런타임 차등검증(byte-identical) 통과분만.

무엇을 배선했나 (crownyc_jit_snippets.s만 수정, C·조립기 무변경)

  • BALDIV 매크로 신설: cj_baldiv/crownyc.c cube_divmod와 bit-identical.
cbz b→q=0 · sdiv q=a/b · msub r=a-q*b · cneg |r|,|b| · 2|r|>|b|이면 eor a,b로 부호동일(≥0) 판정 후 cneg ±1 보정. clobber x2..x6(+x0), a·b 불변. blr 없음.
  • _cjs_div: blr x28BALDIV x0,x1 치환. 뒤이어 기존 NORMINLINE 유지.
  • _cjs_mod: a=x7·b=x8로 로드(BALDIV·msub 불변 보존) → BALDIVmsub r=a-q*b → NORMINLINE.
6차의 scr0/scr1(hole@48/56) 스크래치 왕복 제거(콜백 없어져 불필요).
  • C측(crownyc_jit_hot.c cjasm_build)은 무변경 — EMIT(div)/EMIT(mod)이 스팬 memcpy만 하고
div/mod엔 immediate 패치가 없어서 .s 재작성만으로 반영. prologue의 x28(bdiv) 로드는 잔존(무해).

회당 콜백 blr (canon 캐노니컬 loop)

연산4차6차7차
LOAD/STORE/norm다수 blr00
baldiv (DIV/MOD)1 blr1 blr0 (인라인)
회당 계~1110

A/B 실측 (같은 머신·빌드, best-of-N, asm+gate, BYTE-IDENTICAL)

6차 등가본(blr baldiv)을 별도 빌드해 7차(인라인)와 head-to-head:
커널6차 blr7차 인라인delta
canon 30M (30M DIV)0.495s0.486s0.009s (0.30 ns/DIV)
finance 3M (12M DIV)0.103s0.092s0.011s (~0.9 ns/DIV)
canon 3M (3M DIV)0.058s0.057s0.001s(startup 노이즈 내)
- 정직 소견: baldiv blr 제거는 회당 ~0.3~0.9ns 실이득(작지만 일관·양(+)). canon 3M 벽시계가 ~0.05s에 머무는 건 operand-stack 메모리 트래픽(잔여 지배항, 레지스터할당 필요=별건 대공사)과 프로세스 startup(바이너리 로드+self-test)이 지배하기 때문 — baldiv는 이미 회당 최소항이었음. "canon→스텐실 0.02s"는 이 증분 단독으론 미달(예상대로, 잔여=ostk 메모리·2번 이월과제).

전체 벤치 (best-of-5, wall-clock, warm, 4모드 BYTE-IDENTICAL)

커널base(no-JIT)asm(7차)on(default JIT)
canon 3M3.52s0.054s0.54s
finance 3M(4 DIV)2.41s0.088s0.80s
FP지역 10M8.31s0.203s0.40s

회귀 (base==on==asm==shared md5 IDENTICAL)

커널결과
canon·arb·finance·fp지역✅ IDENTICAL(4모드)
배열합·배열쓰기·3D렌더러✅ IDENTICAL(4모드)
작업구분·브레인결재(stdin 판정코어)✅ IDENTICAL(base/asm/shared)
jithot 동반본(7차 섹션 추가·재컴파일)✅ IDENTICAL(base/asm/shared)
- 인라인 DIV/MOD 스니펫 직접 검증(신규, asm ADOPT verified bit-identical):
  • canon(양수 DIV) ADOPT ✅ · div_neg(음수 피제수 DIV) divneg=-329671 4모드 일치 ADOPT ✅
  • mod2(양수 MOD) mod2=3 ADOPT ✅ · mod3(음수 피제수 MOD) mod3=-3 균형나머지 ADOPT ✅
  • → 부호보정·|r|≤|b|/2 균형 시맨틱을 인라인이 정확 복제(게이트가 채택=byte-identical 확인).
  • 관련 파일 (전부 /Users/ef/CrownyOS/crownyc/)

    • crownyc_jit_snippets.s — ★BALDIV 매크로 신설, _cjs_div/_cjs_mod 인라인 재작성(blr x28 제거).
    • crownyc_jit_hot.한선 — 동반본에 7차 증분O(발디브인라인판정) 1함수+3출력 추가. 4모드 일치.
    • crownyc_jit_hot.c무변경(cjasm_build·hole struct 그대로, div/mod은 스팬 copy만).
    • crownyc-jit — 재빌드: cc -O2 -o crownyc-jit crownyc_jitmain.c crownyc_jit_snippets.s -framework Security -framework CoreFoundation
    • crownyc.c/crownyc — 공유, 무접촉(20:01).

    남은 작업 (다음 세션)

    1. operand-stack 레지스터화 — 스텐실과의 잔여격차 지배항. copy-and-patch 연접 모델 넘어 레지스터할당
    컴파일러 필요(대공사). cc경로가 이미 그 역할.
    1. TSHR/TSHL(op94/95) 스니펫 — arb(i/3) asm 조립 가능케(4차부터 이월).
    2. asm 배열(증분3) — 저ROI(index 회당 가변→캐싱 불가). base만 부분이득.
    3. 다탈출 가드·비순수 잎(2차 이월). W^X 서명배포 시 MAP_JIT/entitlement.

    안전수칙 준수

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