P5.5: migrate the 35 BuildOptions accessors off #compiler to VM-native abi(.compiler)

`BuildOptions :: struct #compiler { ...35 methods... }` becomes
`BuildOptions :: struct { }` (an opaque null-sentinel handle) plus 35 free
`ufcs (self: BuildOptions, …) abi(.compiler)` decls in build.sx, each serviced
by a new `comptime_vm.callBuildOptionFn` arm (off `callCompilerFn`). No legacy
`compiler_lib` handler: the names are registered in `bound_fns` with a single
bailing stub only so `weldedCompilerFn` accepts them.

- String lifetime: setters dupe the arg into the persistent `Vm.gpa` (the
  Compilation allocator, threaded into both `tryEval` and `runBuildCallback` —
  not the per-eval VM arena) and write/append to the threaded `BuildConfig`.
  Getters read the field/slice or compute the target predicate from the triple.
- Dispatch routing (Option B): a `#run`/const-init entry that directly calls a
  compiler-domain/welded fn (`emit_llvm.entryNeedsVm`) runs on the VM with no
  legacy fallback regardless of the `-Dcomptime-flat` gate, so gate-OFF stays
  green without a legacy BuildOptions handler (P5.7 retires the legacy interp).
- Mark the 5 `platform/bundle.sx` getter-calling helpers `abi(.compiler)` (they
  are comptime-only bundler code; otherwise their now-welded getter calls trip
  the runtime-call gate).
- 37 `.ir` snapshots regenerated (std transitively imports build.sx → string-
  pool/type-table indices shift); verified `.ir`-only, zero behavior-stream diffs.

BuildOptions `compiler_call` strict bails gone (1609/1614/1615 strict-clean);
1616 now bails on a separate, pre-existing unported bitwise/shift VM gap (`shr`),
to port first in P5.6. 703/0 both gates.

Also sweep the outdated "flat memory" terminology to "comptime/byte-addressable"
across comptime_vm + the plan/checkpoint/CLAUDE docs: the comptime VM is
arena-backed, byte-addressable memory where `Addr` is a real host pointer, not a
flat contiguous address space (flag names `-Dcomptime-flat`/`SX_COMPTIME_FLAT` kept).
This commit is contained in:
agra
2026-06-19 13:21:09 +03:00
parent af32c3823c
commit ba28488d99
48 changed files with 13896 additions and 14974 deletions

View File

@@ -116,7 +116,7 @@ pub const LLVMEmitter = struct {
comptime_failed: bool = false,
// When set (env `SX_COMPTIME_FLAT`, → a `-Dcomptime-flat` build flag later),
// comptime const-init folds try the flat-memory VM (`comptime_vm.tryEval`)
// comptime const-init folds try the comptime VM (`comptime_vm.tryEval`)
// first and fall back to the legacy tagged interpreter on null. Default OFF so
// the corpus is unaffected until the VM reaches parity (Phase 1.final step d).
comptime_flat: bool = false,
@@ -855,6 +855,30 @@ pub const LLVMEmitter = struct {
std.debug.print("help: handle it at the `#run` site — `#run <expr> catch (e) {{ ... }}` or `#run <expr> or <default>`\n", .{});
}
/// True when comptime entry `func_id` directly calls a compiler-domain /
/// compiler-welded function (or carries a `compiler_call` op). Such an entry
/// MUST run on the comptime VM: the BuildOptions accessors (Phase 5.5) are
/// VM-only (`comptime_vm.callBuildOptionFn`) with no legacy handler, so a
/// legacy-interp run would bail. Routes the `#run` / const-init through the VM
/// (no legacy fallback) regardless of the `-Dcomptime-flat` gate, keeping
/// gate-OFF green until P5.7 retires the legacy interpreter entirely.
fn entryNeedsVm(self: *const LLVMEmitter, func_id: ir_inst.FuncId) bool {
const func = self.ir_mod.getFunction(func_id);
for (func.blocks.items) |blk| {
for (blk.insts.items) |inst| {
switch (inst.op) {
.call => |call_op| {
const callee = self.ir_mod.getFunction(call_op.callee);
if (callee.compiler_welded or callee.is_compiler_domain) return true;
},
.compiler_call => return true,
else => {},
}
}
}
return false;
}
/// Run comptime side-effect functions (e.g., `#run main();` at top level).
/// These are functions marked `is_comptime = true` with void return that
/// aren't associated with any global. They produce compile-time output.
@@ -880,7 +904,12 @@ pub const LLVMEmitter = struct {
// const-init fold: a VM-handled side-effect that needs no `print`/extern
// runs entirely on the VM (no buffered output); anything it can't handle
// (`print`, an unported op) bails → `null` → the legacy interpreter below.
const vm_result: ?Value = if (self.comptime_flat)
// A compiler-domain entry (calls a BuildOptions accessor / other
// compiler-welded fn) MUST run on the VM — its primitives have no legacy
// handler (Phase 5.5). Force the VM attempt + no-fallback for it,
// regardless of the `-Dcomptime-flat` gate.
const need_vm = self.entryNeedsVm(func_id);
const vm_result: ?Value = if (self.comptime_flat or need_vm)
comptime_vm.tryEval(self.alloc, self.ir_mod, func_id, &self.build_config, self.import_sources)
else
null;
@@ -891,9 +920,9 @@ pub const LLVMEmitter = struct {
std.debug.print("[comptime-vm] fallback run '{s}': {s}\n", .{ fname, comptime_vm.last_bail_reason orelse "<unknown>" });
}
const result = vm_result orelse fallback: {
// Strict mode: NO fallback — a VM bail is a build-gating error naming
// the reason (the interp-retirement enumeration gate).
if (self.comptime_flat_strict) {
// Strict mode (or a compiler-domain entry): NO fallback — a VM bail
// is a build-gating error naming the reason.
if (self.comptime_flat_strict or need_vm) {
std.debug.print("error: comptime `#run` ({s}) bailed on the VM (strict, no fallback): {s}\n", .{ fname, comptime_vm.last_bail_reason orelse "<unknown>" });
self.comptime_failed = true;
break :fallback Value.void_val;
@@ -987,7 +1016,11 @@ pub const LLVMEmitter = struct {
// comptime initializer on the VM; `null` (unsupported op / any
// bail / implicit-ctx) falls through to the legacy interpreter
// below, which produces the identical result. Default OFF.
const vm_result: ?Value = if (self.comptime_flat)
// A compiler-domain initializer (reaches a BuildOptions accessor /
// other compiler-welded fn) MUST run on the VM — no legacy handler
// exists (Phase 5.5). Force the VM + no-fallback for it.
const need_vm = self.entryNeedsVm(func_id);
const vm_result: ?Value = if (self.comptime_flat or need_vm)
comptime_vm.tryEval(self.alloc, self.ir_mod, func_id, &self.build_config, self.import_sources)
else
null;
@@ -1002,9 +1035,9 @@ pub const LLVMEmitter = struct {
}
}
const result = vm_result orelse fallback: {
// Strict mode: NO fallback — a VM bail is a build-gating error
// (the interp-retirement enumeration gate).
if (self.comptime_flat_strict) {
// Strict mode (or a compiler-domain init): NO fallback — a VM bail
// is a build-gating error.
if (self.comptime_flat_strict or need_vm) {
const gname = self.ir_mod.types.getString(global.name);
std.debug.print("error: comptime init of '{s}' bailed on the VM (strict, no fallback): {s}\n", .{ gname, comptime_vm.last_bail_reason orelse "<unknown>" });
self.comptime_failed = true;