P5.3: on_build(cb) build-callback registrar; callback takes BuildOptions
Per user design: on_build(build) is the build-callback registrar (a free fn), generalizing set_post_link_callback — the callback is (opt: BuildOptions) -> bool and the compiler invokes it post-codegen WITH the BuildOptions handle. - VM: callCompilerFn 'on_build' arm + legacy handleOnBuild, both set post_link_callback_fn + a new BuildConfig.post_link_takes_options flag. - comptime_vm: runEntry refactored to runEntryArgs(extra) (implicit ctx + explicit args); new public runBuildCallback(..., pass_options) passes the opaque BuildOptions handle (one word) after the ctx. The fat-config marshaling fear is moot — the handle is a single null-sentinel word. - core.invokeByFuncId/invokeByName take pass_options (was an unused args slice); main.zig passes comp.getPostLinkTakesOptions(). - build.sx: on_build decl (set_post_link_callback kept for now). Smoke test examples/1664-platform-on-build-callback (AOT): #run on_build(build) with build :: (opt: BuildOptions) -> bool; the callback is invoked with the handle arg (runEntryArgs param-count match) and runs the primitives. Benign .ir churn (37 snapshots: type table +1 for the on_build fn type + global renumber; behavior identical). 705/0 both gates.
This commit is contained in:
@@ -223,6 +223,37 @@ pub fn tryEval(gpa: std.mem.Allocator, module: *const Module, func_id: inst_mod.
|
||||
};
|
||||
}
|
||||
|
||||
/// Run a post-link build callback on the VM (the post-codegen build driver — see
|
||||
/// `core.invokeByFuncId`). Like `tryEval`, but for a callback that may take the
|
||||
/// opaque `BuildOptions` handle as an explicit arg (the `on_build(cb)` form,
|
||||
/// `cb: (opt: BuildOptions) -> bool`): when `pass_options` is set, the handle (a
|
||||
/// null sentinel — the real state is the threaded `BuildConfig`) is passed after
|
||||
/// the implicit ctx. Returns null on a bail (`last_bail_reason` names the cause).
|
||||
pub fn runBuildCallback(gpa: std.mem.Allocator, module: *const Module, func_id: inst_mod.FuncId, build_config: ?*interp_mod.BuildConfig, source_map: ?*const std.StringHashMap([:0]const u8), pass_options: bool) ?Value {
|
||||
last_bail_reason = null;
|
||||
const func = module.getFunction(func_id);
|
||||
if (func.is_extern or func.blocks.items.len == 0) {
|
||||
last_bail_reason = "extern / no body";
|
||||
return null;
|
||||
}
|
||||
var vm = Vm.init(gpa);
|
||||
defer vm.deinit();
|
||||
vm.table = &module.types;
|
||||
vm.module = module;
|
||||
vm.build_config = build_config;
|
||||
vm.source_map = source_map;
|
||||
const extra: []const Reg = if (pass_options) &.{null_addr} else &.{};
|
||||
const reg = vm.runEntryArgs(func_id, extra) catch |err| {
|
||||
last_bail_reason = vm.detail orelse @errorName(err);
|
||||
return null;
|
||||
};
|
||||
if (func.ret == .void or func.ret == .noreturn) return .void_val;
|
||||
return vm.regToValue(gpa, &module.types, reg, func.ret) catch |err| {
|
||||
last_bail_reason = vm.detail orelse @errorName(err);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
// ── Executor ────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Walks the SAME SSA IR the legacy interpreter (`interp.zig`) walks, but over
|
||||
@@ -327,18 +358,27 @@ pub const Vm = struct {
|
||||
/// materialized ctx is zeroed; a body that ignores it runs, one that uses the
|
||||
/// allocator hits unported `call_indirect` and bails.
|
||||
fn runEntry(self: *Vm, func_id: FuncId) Error!Reg {
|
||||
return self.runEntryArgs(func_id, &.{});
|
||||
}
|
||||
|
||||
/// Run a comptime entry with the materialized implicit `*Context` (when the
|
||||
/// function has one) PREPENDED to `extra` explicit arg words. A nullary
|
||||
/// const-init / `#run` passes `extra = &.{}`; a post-link build callback of
|
||||
/// the `on_build` form passes the opaque `BuildOptions` handle.
|
||||
fn runEntryArgs(self: *Vm, func_id: FuncId, extra: []const Reg) Error!Reg {
|
||||
const module = self.module orelse return self.failMsg("comptime VM: entry run needs a module");
|
||||
const func = module.getFunction(func_id);
|
||||
var argbuf: [1]Reg = undefined;
|
||||
var args: []const Reg = &.{};
|
||||
var argbuf: std.ArrayList(Reg) = .empty;
|
||||
defer argbuf.deinit(self.gpa);
|
||||
if (func.has_implicit_ctx) {
|
||||
if (func.params.len != 1) return self.failMsg("comptime VM: has_implicit_ctx with non-ctx params");
|
||||
argbuf[0] = try self.materializeDefaultContext(module);
|
||||
args = argbuf[0..1];
|
||||
argbuf.append(self.gpa, try self.materializeDefaultContext(module)) catch @panic("comptime VM: out of memory (entry args)");
|
||||
}
|
||||
for (extra) |a| argbuf.append(self.gpa, a) catch @panic("comptime VM: out of memory (entry args)");
|
||||
if (argbuf.items.len != func.params.len)
|
||||
return self.failMsg("comptime VM: entry arg count mismatch (ctx + explicit args vs params)");
|
||||
self.call_stack.append(self.gpa, func_id) catch @panic("comptime VM: out of memory (call stack)");
|
||||
defer _ = self.call_stack.pop();
|
||||
return self.run(func, args);
|
||||
return self.run(func, argbuf.items);
|
||||
}
|
||||
|
||||
/// Materialize the default `Context` in flat memory and return its address —
|
||||
@@ -1452,6 +1492,20 @@ pub const Vm = struct {
|
||||
bc.post_link_callback_fn = fid;
|
||||
return @as(Reg, null_addr);
|
||||
}
|
||||
// `on_build(cb)` — register the build callback (the Phase 5 form, `cb:
|
||||
// (opt: BuildOptions) -> bool`). Like `set_post_link_callback` but a free
|
||||
// fn (cb is arg 0, no self) and the callback receives the `BuildOptions`
|
||||
// handle when invoked (the `post_link_takes_options` flag drives that).
|
||||
if (std.mem.eql(u8, name, "on_build")) {
|
||||
if (args.len != 1) return self.failMsg("comptime on_build: expected (cb)");
|
||||
const bc = self.build_config orelse
|
||||
return self.failMsg("comptime on_build: no build config threaded into the VM");
|
||||
const fid = funcRefToId(frame.get(args[0].index())) orelse
|
||||
return self.failMsg("comptime on_build: cb arg is not a function value");
|
||||
bc.post_link_callback_fn = fid;
|
||||
bc.post_link_takes_options = true;
|
||||
return @as(Reg, null_addr);
|
||||
}
|
||||
// ── build-pipeline metadata queries (Phase 5.2) ─────────────────────
|
||||
// Read-only: the compiler answers them from the `BuildConfig` `main.zig`
|
||||
// forwards before the post-link callback runs. Each builds a fresh
|
||||
|
||||
Reference in New Issue
Block a user