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

CrownyJIT 4차 — 진짜 copy-and-patch 스니펫조립·immediate패치·손익분기 게이팅

개요

2026-07-05 4차 세션. 3차(자족 스텐실 1종=캐노니컬 shape 통짜, cc호출0·setup~4µs·relocation-0)가 남긴 이월 3과제를 배선. 전부 차등검증 게이트(채택 전 네이티브 vs 인터프리터 bit-identical, 불일치=영구 BAIL) 통과분만 채택. 공유 crownyc.c/crownyc 무수정·무재빌드(20:01 그대로). 산출물 crownyc-jit 별도. 신규영역(asm 스니펫·immediate패치·게이팅) 전부 동일 게이트 통과분만 채택.

3종 상태

#목표상태요지
1스니펫 다종 조립 (진짜 copy-and-patch)완주opcode 단위 aarch64 스니펫 라이브러리를 W^X 페이지에 순차 memcpy+patch로 임의 정수루프 조립
2immediate 패치완주리터럴(bound N·divisor D·주소) 전부 DATA 스니펫 movz/movk immediate 직접 패치 → struct 간접로드 제거
3손익분기 게이팅완주(셋업비용 vs 잔여반복×절감/회) 추정, 짧은 루프 스킵. 임계=setup/save, setup 부팅 실측

무엇을 배선했나

증분1·2: aarch64 스니펫 조립기 (crownyc_jit_snippets.s + cjasm_build)

  • 스니펫 라이브러리(.s): opcode 하나당 위치독립 기계어 조각. 실제 어셈블러(as)가 인코딩 생성
→ 정확성 보장. 외부심볼 참조 0 = relocation-0 = copy-safe. 심볼 span [_cjs_X, _cjs_X_end). 종류: prologue/epilogue, data(imm), load, store, dup, pop, add/sub/mul/div/mod, cmp_eq/ne/gt/lt/ge/le(6), guard(JMPIFN), backedge(JMP).
  • 고정 레지스터 규약(전부 callee-saved → 콜백 blr 넘어 보존): x19=hole struct, x20=&ostk,
x21=opstack index, x22=maxiter, x23=it, x24=exited, x25=rd·x26=wr·x27=norm·x28=baldiv. 값 전달 = 메모리 operand-stack(struct 내 ostk[128]) + 위 레지스터.
  • 조립: cjasm_build가 루프 바이트코드(header..site)를 opcode 단위로 walk하며 해당 스니펫 span을
memcpy로 이어붙임. mmap RW → 복사·패치 → mprotect RX → clear_cache. cc 런타임 호출 0(setup≈15µs).
  • immediate 패치(증분2): DATA opcode마다 data 스니펫 복사본의 movz/movk imm16 필드(bits[20:5])를
실제 리터럴로 직접 패치. 스텐실이 h->bound/h->divisor를 struct에서 ldr(간접)하던 것을 코드 immediate로 대체 → 간접로드 제거. 주소도 DATA-before-LOAD/STORE로 immediate. 남는 간접=rd/wr/norm/baldiv 콜백(레지스터 규약 x25-x28)뿐.
  • branch 패치: guard의 탈출(b)·maxiter(b.ge), backedge(b)를 심볼차로 위치 계산 후 오프셋 패치.
  • 범위: 전역(ABS) 정수루프. FP상대 지역루프·TSHR/TSHL(÷×3ⁿ 피홀)은 cc 경로가 커버(폴백).
  • 핵심 함정 해소: VM의 JMPIFN=target 후 cond를 pop, JMP=target을 pop.
조립기는 DATA opcode마다 스니펫을 방출하므로 exit-target(DATA)·header-target(DATA)이 런타임 operand-stack에 push됨 → guard가 target을 폐기 후 cond pop, backedge가 target 폐기하도록 스니펫 수정. (미수정 시 guard가 target을 cond로 오독·operand-stack 무한증가 → 차등게이트가 MISMATCH BAIL로 포착.)

증분3: 손익분기 게이팅 (cj_est_remaining + cj_backedge 게이트, opt-in CROWNY_JIT_GATE)

  • 부팅 self-test 실측: cc 컴파일 셋업(실측 424~547ms), 네이티브 회당비용 → 기본 절감/회.
차등게이트 CAP(4096) 실측으로 절감/회 보정(canon: ≈554~584ns/회).
  • 잔여 추정: 캐노니컬 LT루프(DATA addr_i;LOAD;DATA N;LT...)면 remaining=N−현재i. 불명=−1(큰루프 간주).
  • 판정: 임계=setup/save. remaining<임계면 스킵(영구 인터프리터 완주). cc 경로(setup~0.5s)에서
임계≈수천만 iter → 소형 루프는 컴파일 낭비 회피. asm 경로(setup~15µs)는 임계~27 iter(거의 항상 채택).

벤치 (전부 인터프리터와 BYTE-IDENTICAL, 단일 wall-clock, 변동有)

커널인터프리터스텐실asm(copy&patch)cc비고
canon s+=(i*i)/7 3M4.34s0.43s0.85s1.22sasm=101677582015 ✅ (3차 스텐실 0.46s 재현)
arb 3var/SUB/2DIV 2M3.50sBAIL(비캐노니컬)1.02s0.92sasm=−78815…/… ✅ 스텐실 못하는 임의루프를 조립
finance복리 3M(4 DIV)1.87s1.12s0.97sacc=1471117420 bal=141200787754 ✅
  • asm 경로 요지: setup≈15µs(스텐실급), cc호출0. 스텐실(캐노니컬 1종)과 달리 임의 정수루프 조립
(arb: 변수3·SUB·DIV2·MUL2 → 스텐실 BAIL, asm 채택). 캐노니컬에선 asm 0.85s < cc 1.22s (컴파일 대기 제거). 강세는 무오염·즉시셋업·범용성. 열세는 steady-state(메모리접근·산술이 rd/wr/norm/baldiv 콜백 blr이라 스텐실/cc의 레지스터캐싱보다 느림 — DIV 많을수록 격차).

손익분기 게이팅 벤치 (소형 10000회 루프, cc 경로, HOT=100)

구성총 real판정
게이팅 OFF1.00scc 컴파일 채택(≈0.55s 낭비, 잔여 10000회에 회수불가)
게이팅 ON0.42sbreak-even SKIP(remaining≈9900 < 임계≈8e7) → 인터프리터 완주
- 게이팅이 0.58s 승(동일출력 47611905000). (두 값 모두 CROWNY_JIT=1 부팅 self-test cc컴파일≈0.4s 포함; 순수 인터프리터 no-jit=0.01s. 델타 0.58s = 회피한 per-loop cc 컴파일.)

회귀 (base==jitoff==jiton==asm==gate 전부 IDENTICAL, md5)

프로그램입력결과
작업구분0+포트 상태 조회해줘✅ IDENTICAL(5모드)
브레인결재0 안건C 85 4 2 0✅ IDENTICAL(5모드)
crownycode-brain0+정수 배열 정렬✅ IDENTICAL(5모드)
finance복리 3M✅ IDENTICAL(base/jitoff/jiton/asm)
canon3m·arb2m·small10k✅ IDENTICAL(전 모드)
- JIT-off crownyc-jit == 공유 crownyc(드리프트 0). 공유 crownyc.c/crownyc 20:01 무손상.

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

  • crownyc_jit_snippets.s — ★신규. aarch64 copy-and-patch 스니펫 라이브러리(위치독립, 심볼 span).
  • crownyc_jit_hot.c — 트랜스파일러 본체. 4차 추가:
cj_norm(콜백용 24트릿절단), 스니펫 extern·cjasm_hole·cj_patch_imm/b/bcond·cj_emit· cjasm_build(조립기)·cj_est_remaining(잔여추정), cj_dispatch asm 라우팅, cj_backedge에 asm 경로선택+손익분기 게이팅+CAP 절감실측, self-test 셋업실측, stats(gated/asm_adopts).
  • crownyc_jitmain.c — crownyc.c 복사본. 4차 무수정.
  • crownyc-jit — 산출 바이너리. 빌드(.s 추가):
cc -O2 -o crownyc-jit crownyc_jitmain.c crownyc_jit_snippets.s -framework Security -framework CoreFoundation
  • crownyc_jit_hot.한선 — 판정 로직 한선씨 동반본. 4차 추가: 스니펫이름(opcode→스니펫)·
immediate패치대상·손익분기판정(C 게이팅과 동일 판정, 컴파일·실행 확인).
  • crownyc.c/crownyc공유, 무손상(20:01).

env 플래그

  • CROWNY_JIT=1 기본 켬(부팅 self-test 통과 시 armed). CROWNY_JIT_HOT=N 핫 임계(백엣지 수).
  • CROWNY_JIT_ASM=1 asm 스니펫조립 우선(미매칭=스텐실/cc 폴백).
  • CROWNY_JIT_STENCIL=1 자족 스텐실 우선. 미설정=cc 트랜스파일.
  • CROWNY_JIT_GATE=1 손익분기 게이팅(짧은 루프 스킵). CROWNY_JIT_DUMP=1 opcode 덤프.

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

  1. asm 레지스터 캐싱: 현재 asm은 메모리접근·산술마다 rd/wr/norm/baldiv 콜백 blr → 스텐실/cc의
레지스터캐싱보다 느림. 접촉주소를 callee-saved 레지스터에 호이스팅(경계서만 rd/wr)하면 스텐실급 가속.
  1. asm FP상대 지역루프: 현재 전역(ABS)만. FP는 프레임마다 주소가 달라 dispatch시 frame_ptr+off 계산
스니펫 필요(cc 경로는 이미 지원).
  1. TSHR/TSHL 스니펫: ÷×3ⁿ 피홀최적화(op 95 등)를 트릿시프트 스니펫으로 → arb 원본(i/3) 조립 가능.
  2. 손익분기 절감/회 사전보정: 현재 첫 CAP 실측 전엔 부팅 기본(네이티브×7)에 의존. cc 경로는 setup이 커서
robust하나, asm/저셋업 경로의 정밀 임계는 인터프리터 마이크로프로브로 보강 여지.
  1. 다탈출 가드·비순수 잎(2차 이월 잔여). W^X 서명배포 시 MAP_JIT/entitlement.

안전수칙 준수

  • 공유 crownyc.c/crownyc 무수정·무재빌드(20:01). 산출물 crownyc-jit 별도.
  • 기본 오프(opt-in). 부팅 self-test + 런타임 차등검증 게이트 이중 안전. 신규영역(asm 조립·immediate패치·
게이팅) 전부 동일 게이트 통과분만 채택 — 조립/복사/패치 버그는 출력 무오염(MISMATCH=BAIL=인터프리터). (실제로 이번 guard/backedge target-pop 함정을 차등게이트가 MISMATCH로 포착 → 수정 → 통과.)
  • 데몬·커밋·영속인프라 생성 없음.