From c2c9b240f1c7f8928df141ef1ec1d6be364f6e67 Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Sat, 22 Aug 2026 21:38:22 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20iteration=2036=20task=204=20=E2=80=94?= =?UTF-8?q?=20runtime=20bitwise=20opcodes,=20.wob=20v6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - WOP_BAND..WOP_SHR = 42..46 (wob.h), WOP_MAX 46, WOB_VERSION 6 - trap kind WO_T_SHIFT=12: shift count outside 0..63 traps (DIV0 precedent, never x86's silent count%64); SHR arithmetic - vm.c: one shared case-body serves both dispatch flavors; SHL shifts the unsigned word (wrapping), SHR casts int64_t (sign extends) - loader.c + test runner battery + disasm: v6 accepted, new opcodes validated three-register, rendered BAND/BOR/BXOR/SHL/SHR - woc-test 543/0, wovm-test ASan both flavors green, cli_smoke OK Co-Authored-By: Claude Fable 5 --- compiler/src/disasm.ml | 11 +++++++++-- compiler/src/emit.ml | 2 +- compiler/test/runner.ml | 6 ++++-- runtime/src/loader.c | 8 ++++++++ runtime/src/vm.c | 36 ++++++++++++++++++++++++++++++++++++ runtime/src/wob.h | 21 +++++++++++++++++++-- 6 files changed, 77 insertions(+), 7 deletions(-) diff --git a/compiler/src/disasm.ml b/compiler/src/disasm.ml index 1a752eb..91a151f 100644 --- a/compiler/src/disasm.ml +++ b/compiler/src/disasm.ml @@ -159,6 +159,12 @@ let ins_str (i : int) (pc : int) : string = | 39 -> Printf.sprintf "FEQ r%d, r%d, r%d" a b c | 40 -> Printf.sprintf "FLT r%d, r%d, r%d" a b c | 41 -> Printf.sprintf "FLE r%d, r%d, r%d" a b c + (* iteration 36 (v6): the Int bitwise set, same three-register shape *) + | 42 -> Printf.sprintf "BAND r%d, r%d, r%d" a b c + | 43 -> Printf.sprintf "BOR r%d, r%d, r%d" a b c + | 44 -> Printf.sprintf "BXOR r%d, r%d, r%d" a b c + | 45 -> Printf.sprintf "SHL r%d, r%d, r%d" a b c + | 46 -> Printf.sprintf "SHR r%d, r%d, r%d" a b c | op -> Printf.sprintf "?OP%d" op (* ---- the dump ---- *) @@ -173,10 +179,11 @@ let dump (img : string) : string = let line fmt = Buffer.add_string out (fmt ^ "\n") in if u32 img 0 <> magic then raise (Bad "bad magic"); let ver = u32 img 4 in - (* iteration 19 bumped the format to v5 (Float constant tag, kinds 6/7, + (* iteration 36 bumped the format to v6 (opcodes 42-46, the Int bitwise + set; iteration 19's v5 added the Float constant tag, kinds 6/7 and opcodes 34-41). The disassembler tracks the emitter, not a range: an old image is a different format and reading it as this one would misrender. *) - if ver <> 5 then raise (Bad (Printf.sprintf "unsupported version %d" ver)); + if ver <> 6 then raise (Bad (Printf.sprintf "unsupported version %d" ver)); let coff = u32 img 8 and ccnt = u32 img 12 in let koff = u32 img 16 and kcnt = u32 img 20 in let ioff = u32 img 24 and icnt = u32 img 28 in diff --git a/compiler/src/emit.ml b/compiler/src/emit.ml index 072b44c..f4a418a 100644 --- a/compiler/src/emit.ml +++ b/compiler/src/emit.ml @@ -154,7 +154,7 @@ let wob_magic = 0x31424F57 (* "WOB1" read as an LE u32 *) (* v5 (iteration 19): the Float constant tag, field kinds 6/7, opcodes 34-41, builtins 70-83. v4 (iteration 7b): RC opcodes retired; gc mask = GC roots *) -let wob_version = 5 +let wob_version = 6 let wob_hdr_size = 44 let wob_none = 0xFFFFFFFF diff --git a/compiler/test/runner.ml b/compiler/test/runner.ml index 3cde539..61c8875 100644 --- a/compiler/test/runner.ml +++ b/compiler/test/runner.ml @@ -2278,7 +2278,7 @@ let validate_image (img : string) : string list = let u64 o = if ok 8 o then String.get_int64_le img o else 0L in let none = 0xFFFFFFFF in if u32 0 <> 0x31424F57 then fail "bad magic"; - if u32 4 <> 5 then fail "unsupported version"; (* v5: iteration 19 *) + if u32 4 <> 6 then fail "unsupported version"; (* v6: iteration 36 *) let coff = u32 8 and ccnt = u32 12 in let koff = u32 16 and kcnt = u32 20 in let ioff = u32 24 and icnt = u32 28 in @@ -2554,7 +2554,9 @@ let validate_image (img : string) : string list = | 38 -> rchk pc a; rchk pc b - | 34 | 35 | 36 | 37 | 39 | 40 | 41 -> + (* iteration 36 (v6): 42-46, the Int bitwise set — same + three-register shape *) + | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 -> rchk pc a; rchk pc b; rchk pc c diff --git a/runtime/src/loader.c b/runtime/src/loader.c index 81791b4..0ab48d3 100644 --- a/runtime/src/loader.c +++ b/runtime/src/loader.c @@ -441,6 +441,14 @@ int wo_load_buf(wo_module *m, const uint8_t *buf, size_t len, char *err, case WOP_FEQ: case WOP_FLT: case WOP_FLE: + /* iteration 36 (v6): same three-register shape; the shift + count is range-checked at RUN time (WO_T_SHIFT), not here + — it lives in a register, not an immediate. */ + case WOP_BAND: + case WOP_BOR: + case WOP_BXOR: + case WOP_SHL: + case WOP_SHR: RCHK(A); RCHK(B); RCHK(C); diff --git a/runtime/src/vm.c b/runtime/src/vm.c index 1aa3cfd..a9de7fb 100644 --- a/runtime/src/vm.c +++ b/runtime/src/vm.c @@ -859,6 +859,10 @@ static int vm_run(wo_vm *vm, uint64_t *ret, wo_err *err) { [WOP_FMUL] = &&L_FMUL, [WOP_FDIV] = &&L_FDIV, [WOP_FNEG] = &&L_FNEG, [WOP_FEQ] = &&L_FEQ, [WOP_FLT] = &&L_FLT, [WOP_FLE] = &&L_FLE, + /* iteration 36 (v6): the Int bitwise set */ + [WOP_BAND] = &&L_BAND, [WOP_BOR] = &&L_BOR, + [WOP_BXOR] = &&L_BXOR, [WOP_SHL] = &&L_SHL, + [WOP_SHR] = &&L_SHR, }; #define CASE(name) L_##name #define NEXT() \ @@ -935,6 +939,38 @@ dispatch: NEXT(); } + /* iteration 36 (v6): Int bitwise. AND/OR/XOR are sign-agnostic on + * the u64 register word. SHL shifts the unsigned word (wrapping, + * like ADD); SHR casts to int64_t first — ARITHMETIC, the sign bit + * extends (gcc/clang define signed >> as arithmetic, the only + * compilers this runtime targets). A count outside 0..63 traps + * WO_T_SHIFT rather than silently masking like the hardware would; + * literal counts were rejected at compile time (WO-E223). */ + CASE(BAND) : { + R[wo_ins_a(ins)] = R[wo_ins_b(ins)] & R[wo_ins_c(ins)]; + NEXT(); + } + CASE(BOR) : { + R[wo_ins_a(ins)] = R[wo_ins_b(ins)] | R[wo_ins_c(ins)]; + NEXT(); + } + CASE(BXOR) : { + R[wo_ins_a(ins)] = R[wo_ins_b(ins)] ^ R[wo_ins_c(ins)]; + NEXT(); + } + CASE(SHL) : { + int64_t s = (int64_t)R[wo_ins_c(ins)]; + if (s < 0 || s > 63) TRAPF(WO_T_SHIFT, "shift count out of range 0..63"); + R[wo_ins_a(ins)] = R[wo_ins_b(ins)] << s; + NEXT(); + } + CASE(SHR) : { + int64_t s = (int64_t)R[wo_ins_c(ins)]; + if (s < 0 || s > 63) TRAPF(WO_T_SHIFT, "shift count out of range 0..63"); + R[wo_ins_a(ins)] = (uint64_t)((int64_t)R[wo_ins_b(ins)] >> s); + NEXT(); + } + /* iteration 19: f64 arithmetic. Registers are u64, so each op bitcasts in * and out (wo_f64/wo_bits — memcpy-based, the only strict-aliasing-clean * way). Nothing here traps: IEEE 754 quiet semantics are the contract, so diff --git a/runtime/src/wob.h b/runtime/src/wob.h index ac5613a..8a05b54 100644 --- a/runtime/src/wob.h +++ b/runtime/src/wob.h @@ -13,7 +13,9 @@ /* ---- file header (44 bytes, absolute offsets) ---- */ #define WOB_MAGIC 0x31424F57u /* "WOB1" read as LE u32 */ -#define WOB_VERSION 5u /* v5 (iteration 19): the two missing scalars. New +#define WOB_VERSION 6u /* v6 (iteration 36): opcodes 42-46 (the Int + * bitwise set BAND/BOR/BXOR/SHL/SHR), trap kind WO_T_SHIFT. + * v5 (iteration 19): the two missing scalars. New * constant tag WOB_K_FLOAT, field kinds WO_K_FLOAT/WO_K_BYTES (WO_K_MAX 5->7), * opcodes 34-41 (the f64 arithmetic/compare set), builtins 70-82. * v4 (iteration 7b): opcodes 27-28 (RC_INC/RC_DEC) retired; the drop table's @@ -184,6 +186,10 @@ enum { /* iteration 9b: deleting a row still referenced by a `ref` traps here (restrict) — the employee sample's DROP-of-a-department-with-staff */ WO_T_FK = 11, + /* iteration 36: a shift count outside 0..63 at run time — the Int + honesty precedent DIV0 set (trap, never x86's silent count%64). + Literal counts never get here: woc rejects them (WO-E223). */ + WO_T_SHIFT = 12, }; /* ---- opcodes (spec section 5; semantics in the format doc) ---- */ @@ -247,8 +253,19 @@ enum { WOP_FEQ = 39, /* A B C: IEEE equality, so NaN == NaN is 0 */ WOP_FLT = 40, /* IEEE ordered <: any comparison with NaN is 0 */ WOP_FLE = 41, + /* iteration 36 (v6): the Int bitwise set. A B C on i64, Int-only — + * woc's checker refuses Float/Bool/Text operands, so no F-twin + * exists. SHL/SHR trap WO_T_SHIFT when the count register is + * outside 0..63 (never x86's silent count%64; literal counts were + * already rejected at compile time as WO-E223). SHR is ARITHMETIC: + * the sign bit extends, Go's own choice for a signed integer. */ + WOP_BAND = 42, /* A B C: r[A] = r[B] & r[C] */ + WOP_BOR = 43, + WOP_BXOR = 44, + WOP_SHL = 45, /* A B C: r[A] = r[B] << r[C]; C outside 0..63 traps */ + WOP_SHR = 46, /* A B C: arithmetic; C outside 0..63 traps */ }; -#define WOP_MAX 41u +#define WOP_MAX 46u /* ---- builtin ids (WOP_BUILTIN operand C) ---- */ enum {