code wiki / _hdl_build / nx_x86emit.nx
nx_x86emit.nx
buildroot/runtime/_hdl_build/nx_x86emit.nx
about
nx_x86emit.nx -- SOVEREIGN x86-64 MACHINE-CODE EMITTER (P5 baseline-JIT foundation; SOTA ladder).
The keystone (nx_js_jit_keystone) proved runtime EMISSION+EXECUTION works with hand-hex. A template
JIT needs to emit code PROGRAMMATICALLY, so this is the assembler: one function per instruction form
the Sparkplug-tier compiler will template, each producing exact x86-64 bytes into a code buffer.
Verified end-to-end by nx_x86emit_gate (emits functions through THIS api, executes them, checks
results) -- cardinal rule 2: verify the primitive before building the JIT on it.
Conventions: REX.W (64-bit) on everything. Register numbers = hardware encoding (RAX=0..R15=15).
Memory base MUST NOT be RSP/R12 (low3==100 forces a SIB the callers here never need); the JIT uses
RBX (stack base) + R14 (env base), both SIB-free. load_idx/store_idx emit SIB for [base+index+disp8].
Wide immediates + rel patches are written byte-by-byte with a VARIABLE shift (nx_cc blanks the .s on
an IMMEDIATE shift count >31 -- reference-gotcha; i*8 is a register shift, legal for 0..63).
license_tier: ORIGINAL
dependencies 1 imports · 2 importers
imports: nx_syscalls.nx
imported by: nx_js_vm.nxnx_x86emit_gate.nx
structs
| none |
consts
| 17 | const RAX: i64 = 0 |
| 18 | const RCX: i64 = 1 |
| 19 | const RDX: i64 = 2 |
| 20 | const RBX: i64 = 3 |
| 21 | const RSP: i64 = 4 |
| 22 | const RBP: i64 = 5 |
| 23 | const RSI: i64 = 6 |
| 24 | const RDI: i64 = 7 |
| 25 | const R8: i64 = 8 |
| 26 | const R9: i64 = 9 |
| 27 | const R10: i64 = 10 |
| 28 | const R11: i64 = 11 |
| 29 | const R12: i64 = 12 |
| 30 | const R13: i64 = 13 |
| 31 | const R14: i64 = 14 |
| 32 | const R15: i64 = 15 |
| 35 | const CC_E: i64 = 0x84 |
| 36 | const CC_NE: i64 = 0x85 |
| 37 | const CC_B: i64 = 0x82 |
| 38 | const CC_AE: i64 = 0x83 |
| 39 | const CC_L: i64 = 0x8C |
| 40 | const CC_GE: i64 = 0x8D |
| 41 | const CC_LE: i64 = 0x8E |
| 42 | const CC_G: i64 = 0x8F |
| 43 | const SET_E: i64 = 0x94 |
| 44 | const SET_NE: i64 = 0x95 |
| 45 | const SET_L: i64 = 0x9C |
| 46 | const SET_GE: i64 = 0x9D |
| 47 | const SET_LE: i64 = 0x9E |
| 48 | const SET_G: i64 = 0x9F |
functions
| 50 | func xe1(cb: *u8, pb: *i64, b: i64) -> i64 { let p: i64 = pb[0]; cb[p] = (b & 0xff) as u8; pb[0] = p + 1; return 0 } |
| 51 | func xe_u32(cb: *u8, pb: *i64, v: i64) -> i64 { var i: i64 = 0; while i < 4 { let sh: i64 = i * 8; xe1(cb, pb, (v >> sh) & 0xff); i = i + 1 } return 0 } |
| 52 | func xe_u64(cb: *u8, pb: *i64, v: i64) -> i64 { var i: i64 = 0; while i < 8 { let sh: i64 = i * 8; xe1(cb, pb, (v >> sh) & 0xff); i = i + 1 } return 0 } |
| 53 | func xhi(r: i64) -> i64 { if r >= 8 { return 1 } return 0 } |
| 54 | func xlo(r: i64) -> i64 { return r & 7 } |
| 55 | func xe_rex(cb: *u8, pb: *i64, w: i64, reg: i64, idx: i64, base: i64) -> i64 { let by: i64 = 0x40 | (w << 3) | (xhi(reg) << 2) | (xhi(idx) << 1) | xhi(base); xe1(cb, pb, by); return 0 } |
| 56 | func xe_modrm(cb: *u8, pb: *i64, mod: i64, reg: i64, rm: i64) -> i64 { xe1(cb, pb, (mod << 6) | (xlo(reg) << 3) | xlo(rm)); return 0 } |
| 57 | func xe_sib(cb: *u8, pb: *i64, scale: i64, index: i64, base: i64) -> i64 { xe1(cb, pb, (scale << 6) | (xlo(index) << 3) | xlo(base)); return 0 } |
| 60 | func xe_mov_rr(cb: *u8, pb: *i64, dst: i64, src: i64) -> i64 { xe_rex(cb, pb, 1, src, 0, dst); xe1(cb, pb, 0x89); xe_modrm(cb, pb, 3, src, dst); return 0 } called by 11: jit_addrjit_slow_binopjit_binopjit_binop_constjit_emit_ctjit_emit_callseq+5 calls 3: xe_rexxe1xe_modrm |
| 62 | func xe_mov_ri32(cb: *u8, pb: *i64, dst: i64, imm: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0xC7); xe_modrm(cb, pb, 3, 0, dst); xe_u32(cb, pb, imm); return 0 } |
| 64 | func xe_mov_ri64(cb: *u8, pb: *i64, dst: i64, imm: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0xB8 | xlo(dst)); xe_u64(cb, pb, imm); return 0 } called by 10: jit_slow_binopjit_binop_constjit_jmpcondjit_emit_ctjit_emit_callseqjit_emit_safepoint+4 calls 4: xe_rexxe1xloxe_u64 |
| 66 | func xe_load(cb: *u8, pb: *i64, dst: i64, base: i64, disp: i64) -> i64 |
| 75 | func xe_store(cb: *u8, pb: *i64, base: i64, disp: i64, src: i64) -> i64 |
| 84 | func xe_load_idx(cb: *u8, pb: *i64, dst: i64, base: i64, index: i64, disp8: i64) -> i64 |
| 93 | func xe_store_idx(cb: *u8, pb: *i64, base: i64, index: i64, disp8: i64, src: i64) -> i64 |
| 102 | func xe_add_rr(cb: *u8, pb: *i64, dst: i64, src: i64) -> i64 { xe_rex(cb, pb, 1, src, 0, dst); xe1(cb, pb, 0x01); xe_modrm(cb, pb, 3, src, dst); return 0 } |
| 103 | func xe_sub_rr(cb: *u8, pb: *i64, dst: i64, src: i64) -> i64 { xe_rex(cb, pb, 1, src, 0, dst); xe1(cb, pb, 0x29); xe_modrm(cb, pb, 3, src, dst); return 0 } |
| 104 | func xe_cmp_rr(cb: *u8, pb: *i64, a: i64, b: i64) -> i64 { xe_rex(cb, pb, 1, b, 0, a); xe1(cb, pb, 0x39); xe_modrm(cb, pb, 3, b, a); return 0 } |
| 106 | func xe_imul_rr(cb: *u8, pb: *i64, dst: i64, src: i64) -> i64 { xe_rex(cb, pb, 1, dst, 0, src); xe1(cb, pb, 0x0F); xe1(cb, pb, 0xAF); xe_modrm(cb, pb, 3, dst, src); return 0 } |
| 109 | func xe_movq_xr(cb: *u8, pb: *i64, xmm: i64, gpr: i64) -> i64 { xe1(cb, pb, 0x66); xe_rex(cb, pb, 1, xmm, 0, gpr); xe1(cb, pb, 0x0F); xe1(cb, pb, 0x6E); xe_modrm(cb, pb, 3, xmm, gpr); return 0 } // movq xmm,r64 |
| 110 | func xe_movq_rx(cb: *u8, pb: *i64, gpr: i64, xmm: i64) -> i64 { xe1(cb, pb, 0x66); xe_rex(cb, pb, 1, xmm, 0, gpr); xe1(cb, pb, 0x0F); xe1(cb, pb, 0x7E); xe_modrm(cb, pb, 3, xmm, gpr); return 0 } // movq r64,xmm |
| 111 | func xe_sse_arith(cb: *u8, pb: *i64, opc: i64, xd: i64, xs: i64) -> i64 { xe1(cb, pb, 0xF2); xe1(cb, pb, 0x0F); xe1(cb, pb, opc); xe_modrm(cb, pb, 3, xd, xs); return 0 } // <op>sd xd,xs (add58 sub5C mul59 div5E) |
| 112 | func xe_cvtsi2sd(cb: *u8, pb: *i64, xmm: i64, gpr: i64) -> i64 { xe1(cb, pb, 0xF2); xe_rex(cb, pb, 1, xmm, 0, gpr); xe1(cb, pb, 0x0F); xe1(cb, pb, 0x2A); xe_modrm(cb, pb, 3, xmm, gpr); return 0 } // cvtsi2sd xmm,r64 (int->double) |
| 114 | func xe_cqo(cb: *u8, pb: *i64) -> i64 { xe1(cb, pb, 0x48); xe1(cb, pb, 0x99); return 0 } |
| 115 | func xe_idiv(cb: *u8, pb: *i64, src: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, src); xe1(cb, pb, 0xF7); xe_modrm(cb, pb, 3, 7, src); return 0 } |
| 116 | func xe_inc(cb: *u8, pb: *i64, dst: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0xFF); xe_modrm(cb, pb, 3, 0, dst); return 0 } |
| 117 | func xe_add_ri32(cb: *u8, pb: *i64, dst: i64, imm: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0x81); xe_modrm(cb, pb, 3, 0, dst); xe_u32(cb, pb, imm); return 0 } |
| 118 | func xe_sub_ri32(cb: *u8, pb: *i64, dst: i64, imm: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0x81); xe_modrm(cb, pb, 3, 5, dst); xe_u32(cb, pb, imm); return 0 } called by 7: jit_addrjit_slow_binopjit_binopjit_binop_constjit_jmpcondjit_setprop_inline+1 calls 4: xe_rexxe1xe_modrmxe_u32 |
| 119 | func xe_cmp_ri32(cb: *u8, pb: *i64, dst: i64, imm: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0x81); xe_modrm(cb, pb, 3, 7, dst); xe_u32(cb, pb, imm); return 0 } called by 11: jit_slow_binopjit_float_operandjit_binopjit_binop_constjit_jmpcondjit_emit_ct+5 calls 4: xe_rexxe1xe_modrmxe_u32 |
| 120 | func xe_shl_imm(cb: *u8, pb: *i64, dst: i64, cnt: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0xC1); xe_modrm(cb, pb, 3, 4, dst); xe1(cb, pb, cnt & 0xff); return 0 } |
| 121 | func xe_shr_imm(cb: *u8, pb: *i64, dst: i64, cnt: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0xC1); xe_modrm(cb, pb, 3, 5, dst); xe1(cb, pb, cnt & 0xff); return 0 } |
| 122 | func xe_sar_imm(cb: *u8, pb: *i64, dst: i64, cnt: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, dst); xe1(cb, pb, 0xC1); xe_modrm(cb, pb, 3, 7, dst); xe1(cb, pb, cnt & 0xff); return 0 } // arithmetic shift right (sign-fill) |
| 124 | func xe_imul1(cb: *u8, pb: *i64, src: i64) -> i64 { xe_rex(cb, pb, 1, 0, 0, src); xe1(cb, pb, 0xF7); xe_modrm(cb, pb, 3, 5, src); return 0 } |
| 126 | func xe_setcc(cb: *u8, pb: *i64, cc: i64, dst: i64) -> i64 { xe1(cb, pb, 0x40 | xhi(dst)); xe1(cb, pb, 0x0F); xe1(cb, pb, cc); xe_modrm(cb, pb, 3, 0, dst); return 0 } |
| 127 | func xe_movzx_rb(cb: *u8, pb: *i64, dst: i64, src: i64) -> i64 { xe_rex(cb, pb, 1, dst, 0, src); xe1(cb, pb, 0x0F); xe1(cb, pb, 0xB6); xe_modrm(cb, pb, 3, dst, src); return 0 } |
| 129 | func xe_jmp(cb: *u8, pb: *i64) -> i64 { xe1(cb, pb, 0xE9); let at: i64 = pb[0]; xe_u32(cb, pb, 0); return at } called by 9: jit_float_operandjit_binopjit_binop_constjit_jmpcondjit_getprop_inlinejit_setprop_inline+3 calls 2: xe1xe_u32 |
| 130 | func xe_jcc(cb: *u8, pb: *i64, cc: i64) -> i64 { xe1(cb, pb, 0x0F); xe1(cb, pb, cc); let at: i64 = pb[0]; xe_u32(cb, pb, 0); return at } called by 12: jit_slow_binopjit_float_operandjit_binopjit_binop_constjit_jmpcondjit_emit_ct+6 calls 2: xe1xe_u32 |
| 132 | func xe_patch(cb: *u8, at: i64, target: i64) -> i64 { let rel: i64 = target - (at + 4); var i: i64 = 0; while i < 4 { let sh: i64 = i * 8; cb[at + i] = ((rel >> sh) & 0xff) as u8; i = i + 1 } return 0 } |
| 134 | func xe_call_reg(cb: *u8, pb: *i64, reg: i64) -> i64 { if xhi(reg) == 1 { xe1(cb, pb, 0x41) } xe1(cb, pb, 0xFF); xe_modrm(cb, pb, 3, 2, reg); return 0 } called by 8: jit_slow_binopjit_jmpcondjit_emit_ctjit_emit_callseqjit_emit_safepointjit_emit_retv+2 calls 3: xhixe1xe_modrm |
| 135 | func xe_push(cb: *u8, pb: *i64, reg: i64) -> i64 { if xhi(reg) == 1 { xe1(cb, pb, 0x41) } xe1(cb, pb, 0x50 | xlo(reg)); return 0 } |
| 136 | func xe_pop(cb: *u8, pb: *i64, reg: i64) -> i64 { if xhi(reg) == 1 { xe1(cb, pb, 0x41) } xe1(cb, pb, 0x58 | xlo(reg)); return 0 } |
| 137 | func xe_ret(cb: *u8, pb: *i64) -> i64 { xe1(cb, pb, 0xC3); return 0 } |
| 140 | func xe_mmap_rwx(size: i64) -> *u8 { let r: i64 = __syscall(SYS_MMAP, 0, size, 7, 0x22, -1, 0); return r as *u8 } |