diff --git a/current/CHECKPOINT-COMPILER-API.md b/current/CHECKPOINT-COMPILER-API.md index 855bcfc2..16f9f0b3 100644 --- a/current/CHECKPOINT-COMPILER-API.md +++ b/current/CHECKPOINT-COMPILER-API.md @@ -427,6 +427,14 @@ when reached (sentinels or accessor fns; see the design doc Risks). `List` growth; orthogonal, see `current/CHECKPOINT-METATYPE.md`.) ## Log +- **P5.6 prerequisite — bitwise/shift ops ported into the VM (2026-06-19).** `comptime_vm` exec now handles + `bit_and`/`bit_or`/`bit_xor`/`bit_not`/`shl`/`shr` (new `bitwise` helper next to `arith`), mirroring the legacy + interp's i64 model EXACTLY: shift amount clamps to `@min(rhs, 63)`, `shr` is an ARITHMETIC right shift + (sign-extending). These were unported and bailed — the `shr` gap surfaced via the iOS-device bundler once P5.5 + let it run further (1616). With the port, 1616's strict VM run reaches the real bundler logic (no more `shr` + bail; it now stops only at the genuinely-unavailable iOS runtime on macOS — `_UIApplicationMain` / no linked + binary under `sx run`, expected). New focused corpus test `examples/0639-comptime-bitwise-shift.sx` (`::` consts + fold AND/OR/XOR/NOT/shl/shr/arith-shr; identical on both evaluators). **704/0 BOTH gates.** - **P5.5 — the 35 `BuildOptions` accessors migrated off `struct #compiler` onto VM-native `abi(.compiler)` (2026-06-19).** `BuildOptions :: struct #compiler { ...35 methods... }` → `BuildOptions :: struct { }` (an opaque null-sentinel handle) + 35 free `ufcs (self: BuildOptions, …) abi(.compiler)` decls in diff --git a/examples/0639-comptime-bitwise-shift.sx b/examples/0639-comptime-bitwise-shift.sx new file mode 100644 index 00000000..c6567979 --- /dev/null +++ b/examples/0639-comptime-bitwise-shift.sx @@ -0,0 +1,20 @@ +// Comptime bitwise + shift ops on the VM: AND/OR/XOR/NOT and shl/shr all fold +// at compile time. Regression for the VM op port (P5.6) — these were unported in +// `comptime_vm` and bailed (`shr` surfaced via the iOS-device bundler). `shr` is +// an arithmetic right shift (sign-extending), mirroring the legacy interp's i64 +// model; the shift amount clamps to 63. + +#import "modules/std.sx"; + +AND :: 0b1100 & 0b1010; // 0b1000 = 8 +OR :: 0b1100 | 0b1010; // 0b1110 = 14 +XOR :: 0b1100 ^ 0b1010; // 0b0110 = 6 +NOT :: ~0; // -1 +SHL :: 1 << 10; // 1024 +SHR :: 1024 >> 3; // 128 +ASHR :: (-8) >> 1; // -4 (arithmetic, sign-extending) + +main :: () { + print("and={} or={} xor={} not={}\n", AND, OR, XOR, NOT); + print("shl={} shr={} ashr={}\n", SHL, SHR, ASHR); +} diff --git a/examples/expected/0639-comptime-bitwise-shift.exit b/examples/expected/0639-comptime-bitwise-shift.exit new file mode 100644 index 00000000..573541ac --- /dev/null +++ b/examples/expected/0639-comptime-bitwise-shift.exit @@ -0,0 +1 @@ +0 diff --git a/examples/expected/0639-comptime-bitwise-shift.stderr b/examples/expected/0639-comptime-bitwise-shift.stderr new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/examples/expected/0639-comptime-bitwise-shift.stderr @@ -0,0 +1 @@ + diff --git a/examples/expected/0639-comptime-bitwise-shift.stdout b/examples/expected/0639-comptime-bitwise-shift.stdout new file mode 100644 index 00000000..b4ee6612 --- /dev/null +++ b/examples/expected/0639-comptime-bitwise-shift.stdout @@ -0,0 +1,2 @@ +and=8 or=14 xor=6 not=-1 +shl=1024 shr=128 ashr=-4 diff --git a/src/ir/comptime_vm.zig b/src/ir/comptime_vm.zig index 63cfc099..9de53f39 100644 --- a/src/ir/comptime_vm.zig +++ b/src/ir/comptime_vm.zig @@ -625,6 +625,11 @@ pub const Vm = struct { .add, .sub, .mul, .div, .mod => |b| return .{ .value = try arith(std.meta.activeTag(ins.op), ins.ty, frame.get(b.lhs.index()), frame.get(b.rhs.index())), }, + // ── Bitwise + shift (i64, mirroring the legacy interp) ─ + .bit_and, .bit_or, .bit_xor, .shl, .shr => |b| return .{ + .value = bitwise(std.meta.activeTag(ins.op), frame.get(b.lhs.index()), frame.get(b.rhs.index())), + }, + .bit_not => |u| return .{ .value = @bitCast(~@as(i64, @bitCast(frame.get(u.operand.index())))) }, .neg => |u| { const x = frame.get(u.operand.index()); if (isFloat(ins.ty)) return .{ .value = @bitCast(-@as(f64, @bitCast(x))) }; @@ -1129,6 +1134,24 @@ pub const Vm = struct { return @bitCast(res); } + /// 64-bit bitwise AND/OR/XOR and shifts — mirrors the legacy interp's i64 + /// model exactly: shifts clamp the amount to `@min(rhs, 63)` and `shr` is an + /// ARITHMETIC right shift (signed `>>`, sign-extending), matching the legacy + /// `.int` representation. + fn bitwise(tag: OpTag, l: Reg, r: Reg) Reg { + const li: i64 = @bitCast(l); + const ri: i64 = @bitCast(r); + const res: i64 = switch (tag) { + .bit_and => li & ri, + .bit_or => li | ri, + .bit_xor => li ^ ri, + .shl => li << @as(u6, @intCast(@min(ri, 63))), + .shr => li >> @as(u6, @intCast(@min(ri, 63))), + else => unreachable, + }; + return @bitCast(res); + } + /// Comparison keyed on the operand type: f64 for floats, == / != only for /// bool, else signed i64 — mirrors the legacy `evalCmp`. fn cmp(self: *Vm, tag: OpTag, lty: TypeId, l: Reg, r: Reg) Error!bool {