P5.7 Step B1: remove the compiler_call IR op + the hook Registry
The compiler_call op + #compiler hook mechanism was fully superseded by abi(.compiler) VM-native dispatch (P5.5) — no sx code emits it anymore. Remove: the compiler_call op variant + CompilerCall struct (inst.zig); the Builder.compilerCall emitter (module.zig); the two dead producer blocks in lower/call.zig (compiler_expr-bodied free fns + methods); every consumer switch arm (emit_llvm, ops.emitCompilerCall, print, interp dispatch); the interp.hooks field + init/deinit. Strip compiler_hooks.zig down to the still- live BuildConfig / BuildHooks / AssetDir (delete HookError/HookFn/Registry/ registerDefaults + all hookXxx, and the now-unused interp/Value imports). Test refs that used compiler_call as a sample unported op now use vec_splat. 501/501 unit + 706/0 corpus.
This commit is contained in:
@@ -1459,11 +1459,6 @@ pub const Ops = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emitCompilerCall(self: Ops, instruction: *const Inst) void {
|
||||
// Compiler hooks are comptime-only; if one reaches emission, produce undef
|
||||
self.e.mapRef(c.LLVMGetUndef(self.e.toLLVMType(instruction.ty)));
|
||||
}
|
||||
|
||||
pub fn emitCallClosure(self: Ops, instruction: *const Inst, call_op: CallIndirect) void {
|
||||
// Closure: { fn_ptr, env }.
|
||||
//
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const interp_mod = @import("interp.zig");
|
||||
const Value = interp_mod.Value;
|
||||
const Interpreter = interp_mod.Interpreter;
|
||||
const inst = @import("inst.zig");
|
||||
const FuncId = inst.FuncId;
|
||||
|
||||
// ── BuildConfig ─────────────────────────────────────────────────────────
|
||||
// Mutable build configuration accumulated by #run blocks via #compiler methods.
|
||||
// Mutable build configuration accumulated by the sx-driven build pipeline
|
||||
// (`default_pipeline` / an `on_build` callback) running on the comptime VM,
|
||||
// which reads/writes these fields via the `abi(.compiler)` primitives.
|
||||
|
||||
/// `(src_dir, dest_in_bundle)` pair recorded by
|
||||
/// `BuildOptions.add_asset_dir(src, dest)`. The sx bundler walks the
|
||||
@@ -144,440 +143,3 @@ pub const BuildHooks = struct {
|
||||
) anyerror!void,
|
||||
};
|
||||
|
||||
// ── Hook system ─────────────────────────────────────────────────────────
|
||||
|
||||
pub const HookError = error{
|
||||
CannotEvalComptime,
|
||||
TypeError,
|
||||
};
|
||||
|
||||
/// Hook function signature. Receives the interpreter (for heap/string access),
|
||||
/// resolved argument values, and the mutable build config.
|
||||
pub const HookFn = *const fn (
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value;
|
||||
|
||||
pub const Registry = struct {
|
||||
hooks: std.StringHashMap(HookFn),
|
||||
|
||||
pub fn init(alloc: Allocator) Registry {
|
||||
return .{ .hooks = std.StringHashMap(HookFn).init(alloc) };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Registry) void {
|
||||
self.hooks.deinit();
|
||||
}
|
||||
|
||||
pub fn get(self: *const Registry, name: []const u8) ?HookFn {
|
||||
return self.hooks.get(name);
|
||||
}
|
||||
|
||||
/// Register all built-in compiler hooks.
|
||||
pub fn registerDefaults(self: *Registry) void {
|
||||
self.hooks.put("build_options", &hookBuildOptions) catch {};
|
||||
self.hooks.put("BuildOptions.add_link_flag", &hookAddLinkFlag) catch {};
|
||||
self.hooks.put("BuildOptions.add_framework", &hookAddFramework) catch {};
|
||||
self.hooks.put("BuildOptions.add_asset_dir", &hookAddAssetDir) catch {};
|
||||
self.hooks.put("BuildOptions.asset_dir_count", &hookAssetDirCount) catch {};
|
||||
self.hooks.put("BuildOptions.asset_dir_src_at", &hookAssetDirSrcAt) catch {};
|
||||
self.hooks.put("BuildOptions.asset_dir_dest_at", &hookAssetDirDestAt) catch {};
|
||||
self.hooks.put("BuildOptions.set_output_path", &hookSetOutputPath) catch {};
|
||||
self.hooks.put("BuildOptions.set_wasm_shell", &hookSetWasmShell) catch {};
|
||||
self.hooks.put("BuildOptions.set_post_link_callback", &hookSetPostLinkCallback) catch {};
|
||||
self.hooks.put("BuildOptions.set_post_link_module", &hookSetPostLinkModule) catch {};
|
||||
self.hooks.put("BuildOptions.binary_path", &hookGetBinaryPath) catch {};
|
||||
// Bundling setters
|
||||
self.hooks.put("BuildOptions.set_bundle_path", &hookSetBundlePath) catch {};
|
||||
self.hooks.put("BuildOptions.set_bundle_id", &hookSetBundleId) catch {};
|
||||
self.hooks.put("BuildOptions.set_codesign_identity", &hookSetCodesignIdentity) catch {};
|
||||
self.hooks.put("BuildOptions.set_provisioning_profile", &hookSetProvisioningProfile) catch {};
|
||||
// Bundling accessors
|
||||
self.hooks.put("BuildOptions.bundle_path", &hookGetBundlePath) catch {};
|
||||
self.hooks.put("BuildOptions.bundle_id", &hookGetBundleId) catch {};
|
||||
self.hooks.put("BuildOptions.codesign_identity", &hookGetCodesignIdentity) catch {};
|
||||
self.hooks.put("BuildOptions.provisioning_profile", &hookGetProvisioningProfile) catch {};
|
||||
// Target accessors — mirror TargetConfig.is{MacOS,IOS,IOSDevice,IOSSimulator,Android}()
|
||||
self.hooks.put("BuildOptions.target_triple", &hookGetTargetTriple) catch {};
|
||||
self.hooks.put("BuildOptions.is_macos", &hookIsMacOS) catch {};
|
||||
self.hooks.put("BuildOptions.is_ios", &hookIsIOS) catch {};
|
||||
self.hooks.put("BuildOptions.is_ios_device", &hookIsIOSDevice) catch {};
|
||||
self.hooks.put("BuildOptions.is_ios_simulator", &hookIsIOSSimulator) catch {};
|
||||
self.hooks.put("BuildOptions.is_android", &hookIsAndroid) catch {};
|
||||
// Android-specific setters + accessors
|
||||
self.hooks.put("BuildOptions.set_manifest_path", &hookSetManifestPath) catch {};
|
||||
self.hooks.put("BuildOptions.manifest_path", &hookGetManifestPath) catch {};
|
||||
self.hooks.put("BuildOptions.set_keystore_path", &hookSetKeystorePath) catch {};
|
||||
self.hooks.put("BuildOptions.keystore_path", &hookGetKeystorePath) catch {};
|
||||
// #jni_main class emissions, exposed by index so bundle.sx can iterate.
|
||||
self.hooks.put("BuildOptions.jni_main_count", &hookJniMainCount) catch {};
|
||||
self.hooks.put("BuildOptions.jni_main_runtime_path_at", &hookJniMainRuntimePathAt) catch {};
|
||||
self.hooks.put("BuildOptions.jni_main_java_source_at", &hookJniMainJavaSourceAt) catch {};
|
||||
// Framework list accessors (for `.app/Frameworks/` embedding)
|
||||
self.hooks.put("BuildOptions.framework_count", &hookFrameworkCount) catch {};
|
||||
self.hooks.put("BuildOptions.framework_at", &hookFrameworkAt) catch {};
|
||||
self.hooks.put("BuildOptions.framework_path_count", &hookFrameworkPathCount) catch {};
|
||||
self.hooks.put("BuildOptions.framework_path_at", &hookFrameworkPathAt) catch {};
|
||||
}
|
||||
};
|
||||
|
||||
// ── build_options() hook ────────────────────────────────────────────────
|
||||
|
||||
fn hookBuildOptions(
|
||||
_: *const Interpreter,
|
||||
_: []const Value,
|
||||
_: *BuildConfig,
|
||||
_: Allocator,
|
||||
) HookError!Value {
|
||||
// build_options() returns a sentinel value; the real work happens
|
||||
// when methods like add_link_flag/set_output_path are called on it.
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
// ── BuildOptions hooks ──────────────────────────────────────────────────
|
||||
|
||||
fn hookAddLinkFlag(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
// args: [self (BuildOptions value), flag_string]
|
||||
if (args.len < 2) return .void_val;
|
||||
const str_val = args[1];
|
||||
if (str_val.asString(interp)) |s| {
|
||||
bc.link_flags.append(alloc, alloc.dupe(u8, s) catch return error.CannotEvalComptime) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookAddFramework(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
// args: [self (BuildOptions value), framework_name]
|
||||
if (args.len < 2) return .void_val;
|
||||
const str_val = args[1];
|
||||
if (str_val.asString(interp)) |s| {
|
||||
bc.frameworks.append(alloc, alloc.dupe(u8, s) catch return error.CannotEvalComptime) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookAddAssetDir(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
// args: [self (BuildOptions value), src_path, dest_path_in_bundle]
|
||||
if (args.len < 3) return .void_val;
|
||||
const src = args[1].asString(interp) orelse return error.TypeError;
|
||||
const dest = args[2].asString(interp) orelse return error.TypeError;
|
||||
const src_dup = alloc.dupe(u8, src) catch return error.CannotEvalComptime;
|
||||
const dest_dup = alloc.dupe(u8, dest) catch return error.CannotEvalComptime;
|
||||
bc.asset_dirs.append(alloc, .{ .src = src_dup, .dest = dest_dup }) catch return error.CannotEvalComptime;
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookAssetDirCount(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .int = @intCast(bc.asset_dirs.items.len) };
|
||||
}
|
||||
|
||||
fn hookAssetDirSrcAt(_: *const Interpreter, args: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
if (args.len < 2) return Value{ .string = "" };
|
||||
const idx = args[1].asInt() orelse return error.TypeError;
|
||||
if (idx < 0 or @as(usize, @intCast(idx)) >= bc.asset_dirs.items.len) return Value{ .string = "" };
|
||||
return Value{ .string = bc.asset_dirs.items[@intCast(idx)].src };
|
||||
}
|
||||
|
||||
fn hookAssetDirDestAt(_: *const Interpreter, args: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
if (args.len < 2) return Value{ .string = "" };
|
||||
const idx = args[1].asInt() orelse return error.TypeError;
|
||||
if (idx < 0 or @as(usize, @intCast(idx)) >= bc.asset_dirs.items.len) return Value{ .string = "" };
|
||||
return Value{ .string = bc.asset_dirs.items[@intCast(idx)].dest };
|
||||
}
|
||||
|
||||
fn hookSetOutputPath(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
// args: [self (BuildOptions value), path_string]
|
||||
if (args.len < 2) return .void_val;
|
||||
const str_val = args[1];
|
||||
if (str_val.asString(interp)) |s| {
|
||||
bc.output_path = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookSetWasmShell(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
// args: [self (BuildOptions value), path_string]
|
||||
if (args.len < 2) return .void_val;
|
||||
const str_val = args[1];
|
||||
if (str_val.asString(interp)) |s| {
|
||||
bc.wasm_shell_path = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookSetPostLinkCallback(
|
||||
_: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
_: Allocator,
|
||||
) HookError!Value {
|
||||
// args: [self (BuildOptions value), fn_value]. We accept a function
|
||||
// value (.func_ref) and stash the FuncId so `main.zig` can re-enter
|
||||
// the interpreter after linking.
|
||||
if (args.len < 2) return .void_val;
|
||||
switch (args[1]) {
|
||||
.func_ref => |id| bc.post_link_callback_fn = id,
|
||||
else => return error.TypeError,
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookSetPostLinkModule(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
if (args.len < 2) return .void_val;
|
||||
if (args[1].asString(interp)) |s| {
|
||||
bc.post_link_module = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
/// Read the linked-binary path that main.zig populated right before
|
||||
/// invoking the post-link callback. Returns the fat-string aggregate
|
||||
/// the interpreter normally hands out for sx `string` values.
|
||||
fn hookGetBinaryPath(
|
||||
interp: *const Interpreter,
|
||||
_: []const Value,
|
||||
bc: *BuildConfig,
|
||||
_: Allocator,
|
||||
) HookError!Value {
|
||||
_ = interp;
|
||||
const path = bc.binary_path orelse "";
|
||||
return Value{ .string = path };
|
||||
}
|
||||
|
||||
// ── Bundling setters & accessors ─────────────────────────────────────
|
||||
// Same pattern as set_output_path: take a string arg, dupe into the
|
||||
// long-lived allocator, store on BuildConfig. The companion accessor
|
||||
// reads back the same field; empty string when unset.
|
||||
|
||||
fn hookSetBundlePath(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
if (args.len < 2) return .void_val;
|
||||
if (args[1].asString(interp)) |s| {
|
||||
bc.bundle_path = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookSetBundleId(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
if (args.len < 2) return .void_val;
|
||||
if (args[1].asString(interp)) |s| {
|
||||
bc.bundle_id = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookSetCodesignIdentity(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
if (args.len < 2) return .void_val;
|
||||
if (args[1].asString(interp)) |s| {
|
||||
bc.codesign_identity = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookSetProvisioningProfile(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
if (args.len < 2) return .void_val;
|
||||
if (args[1].asString(interp)) |s| {
|
||||
bc.provisioning_profile = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookGetBundlePath(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .string = bc.bundle_path orelse "" };
|
||||
}
|
||||
|
||||
fn hookGetBundleId(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .string = bc.bundle_id orelse "" };
|
||||
}
|
||||
|
||||
fn hookGetCodesignIdentity(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .string = bc.codesign_identity orelse "" };
|
||||
}
|
||||
|
||||
fn hookGetProvisioningProfile(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .string = bc.provisioning_profile orelse "" };
|
||||
}
|
||||
|
||||
// ── Target accessors ──────────────────────────────────────────────────
|
||||
// These look at the target_triple that main.zig populates and answer
|
||||
// the same questions TargetConfig's helpers do for Zig callers.
|
||||
|
||||
fn tripleContains(triple: ?[]const u8, needle: []const u8) bool {
|
||||
const t = triple orelse return false;
|
||||
return std.mem.indexOf(u8, t, needle) != null;
|
||||
}
|
||||
|
||||
fn isIOSTriple(triple: ?[]const u8) bool {
|
||||
return tripleContains(triple, "apple-ios");
|
||||
}
|
||||
|
||||
fn hookGetTargetTriple(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .string = bc.target_triple orelse "" };
|
||||
}
|
||||
|
||||
fn hookIsMacOS(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
if (isIOSTriple(bc.target_triple)) return Value{ .boolean = false };
|
||||
const t = bc.target_triple orelse "";
|
||||
const is_mac = std.mem.indexOf(u8, t, "apple-macosx") != null or
|
||||
std.mem.indexOf(u8, t, "apple-macos") != null or
|
||||
std.mem.indexOf(u8, t, "apple-darwin") != null;
|
||||
return Value{ .boolean = is_mac };
|
||||
}
|
||||
|
||||
fn hookIsIOS(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .boolean = isIOSTriple(bc.target_triple) };
|
||||
}
|
||||
|
||||
fn hookIsIOSDevice(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
const ios = isIOSTriple(bc.target_triple);
|
||||
const sim = tripleContains(bc.target_triple, "simulator");
|
||||
return Value{ .boolean = ios and !sim };
|
||||
}
|
||||
|
||||
fn hookIsIOSSimulator(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
const ios = isIOSTriple(bc.target_triple);
|
||||
const sim = tripleContains(bc.target_triple, "simulator");
|
||||
return Value{ .boolean = ios and sim };
|
||||
}
|
||||
|
||||
fn hookIsAndroid(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .boolean = tripleContains(bc.target_triple, "android") };
|
||||
}
|
||||
|
||||
// ── Android-specific bundling setters + accessors ─────────────────────
|
||||
|
||||
fn hookSetManifestPath(interp: *const Interpreter, args: []const Value, bc: *BuildConfig, alloc: Allocator) HookError!Value {
|
||||
if (args.len < 2) return .void_val;
|
||||
if (args[1].asString(interp)) |s| {
|
||||
bc.manifest_path = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookGetManifestPath(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .string = bc.manifest_path orelse "" };
|
||||
}
|
||||
|
||||
fn hookSetKeystorePath(interp: *const Interpreter, args: []const Value, bc: *BuildConfig, alloc: Allocator) HookError!Value {
|
||||
if (args.len < 2) return .void_val;
|
||||
if (args[1].asString(interp)) |s| {
|
||||
bc.keystore_path = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookGetKeystorePath(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .string = bc.keystore_path orelse "" };
|
||||
}
|
||||
|
||||
// ── #jni_main emission accessors ──────────────────────────────────────
|
||||
// The Android bundler walks these as `0..jni_main_count()` and reads
|
||||
// each entry's `(runtime_path, java_source)` pair so it can write a
|
||||
// `.java` file per decl, compile via javac, and produce classes.dex
|
||||
// via d8 before zipping into the APK.
|
||||
|
||||
fn hookJniMainCount(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return Value{ .int = @intCast(bc.jni_main_runtime_paths.len) };
|
||||
}
|
||||
|
||||
fn hookJniMainRuntimePathAt(_: *const Interpreter, args: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
if (args.len < 2) return Value{ .string = "" };
|
||||
const idx = args[1].asInt() orelse return error.TypeError;
|
||||
if (idx < 0 or @as(usize, @intCast(idx)) >= bc.jni_main_runtime_paths.len) return Value{ .string = "" };
|
||||
return Value{ .string = bc.jni_main_runtime_paths[@intCast(idx)] };
|
||||
}
|
||||
|
||||
fn hookJniMainJavaSourceAt(_: *const Interpreter, args: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
if (args.len < 2) return Value{ .string = "" };
|
||||
const idx = args[1].asInt() orelse return error.TypeError;
|
||||
if (idx < 0 or @as(usize, @intCast(idx)) >= bc.jni_main_java_sources.len) return Value{ .string = "" };
|
||||
return Value{ .string = bc.jni_main_java_sources[@intCast(idx)] };
|
||||
}
|
||||
|
||||
// ── Framework list accessors ──────────────────────────────────────────
|
||||
// The Apple .app bundler in `library/modules/platform/bundle.sx` walks
|
||||
// the framework list to recursively copy each `<Name>.framework`
|
||||
// directory from the user's -F search paths into `<bundle>/Frameworks/`.
|
||||
// Slice-of-string returns aren't natively expressible as a Value, so we
|
||||
// expose count + indexed lookups instead.
|
||||
|
||||
fn intValue(n: i64) Value {
|
||||
return Value{ .int = n };
|
||||
}
|
||||
|
||||
fn hookFrameworkCount(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return intValue(@intCast(bc.target_frameworks.len));
|
||||
}
|
||||
|
||||
fn hookFrameworkAt(_: *const Interpreter, args: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
if (args.len < 2) return Value{ .string = "" };
|
||||
const idx_i64 = args[1].asInt() orelse return error.TypeError;
|
||||
if (idx_i64 < 0 or @as(usize, @intCast(idx_i64)) >= bc.target_frameworks.len) {
|
||||
return Value{ .string = "" };
|
||||
}
|
||||
return Value{ .string = bc.target_frameworks[@intCast(idx_i64)] };
|
||||
}
|
||||
|
||||
fn hookFrameworkPathCount(_: *const Interpreter, _: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
return intValue(@intCast(bc.target_framework_paths.len));
|
||||
}
|
||||
|
||||
fn hookFrameworkPathAt(_: *const Interpreter, args: []const Value, bc: *BuildConfig, _: Allocator) HookError!Value {
|
||||
if (args.len < 2) return Value{ .string = "" };
|
||||
const idx_i64 = args[1].asInt() orelse return error.TypeError;
|
||||
if (idx_i64 < 0 or @as(usize, @intCast(idx_i64)) >= bc.target_framework_paths.len) {
|
||||
return Value{ .string = "" };
|
||||
}
|
||||
return Value{ .string = bc.target_framework_paths[@intCast(idx_i64)] };
|
||||
}
|
||||
|
||||
@@ -1314,12 +1314,12 @@ test "comptime_vm tryEval: pure function → Value; unsupported → null" {
|
||||
const v = vm.tryEval(alloc, &module, ok_id, null, null) orelse return error.VmShouldHaveHandledIt;
|
||||
try std.testing.expectEqual(@as(i64, 42), v.int);
|
||||
|
||||
// fn bad() { compiler_call() } → an unported op → tryEval yields null (caller
|
||||
// falls back to legacy). (box_any/unbox_any are now VM-native; compiler_call is
|
||||
// still unported until Phase 4D.)
|
||||
// fn bad() { vec_splat(...) } → an unported op → tryEval yields null. The VM
|
||||
// bails loudly on any op it does not model (never a silent default); vec_splat
|
||||
// is a stable example of one.
|
||||
var fb2 = Fb.init(alloc, &.{}, .void);
|
||||
const c0 = fb2.block(&.{});
|
||||
_ = fb2.add(c0, inst(.{ .compiler_call = .{ .name = 0, .args = &.{} } }, .void));
|
||||
_ = fb2.add(c0, inst(.{ .vec_splat = .{ .operand = ref(0) } }, .void));
|
||||
_ = fb2.add(c0, inst(.ret_void, .void));
|
||||
const bad_id = module.addFunction(fb2.func);
|
||||
|
||||
@@ -1341,18 +1341,18 @@ test "comptime_vm exec: division by zero and unsupported op bail loudly" {
|
||||
try std.testing.expectEqual(@as(i64, 4), toI64(try v.run(&fb.func, &.{ fromI64(12), fromI64(3) })));
|
||||
try std.testing.expectError(error.DivisionByZero, v.run(&fb.func, &.{ fromI64(12), fromI64(0) }));
|
||||
}
|
||||
// A not-yet-ported op (compiler_call) → Unsupported with the op name in `detail`.
|
||||
// A not-yet-ported op (vec_splat) → Unsupported with the op name in `detail`.
|
||||
{
|
||||
var fb = Fb.init(std.testing.allocator, &.{}, .void);
|
||||
defer fb.deinit();
|
||||
const b0 = fb.block(&.{});
|
||||
_ = fb.add(b0, inst(.{ .compiler_call = .{ .name = 0, .args = &.{} } }, .void));
|
||||
_ = fb.add(b0, inst(.{ .vec_splat = .{ .operand = ref(0) } }, .void));
|
||||
_ = fb.add(b0, inst(.ret_void, .void));
|
||||
|
||||
var v = vm.Vm.init(std.testing.allocator);
|
||||
defer v.deinit();
|
||||
try std.testing.expectError(error.Unsupported, v.run(&fb.func, &.{}));
|
||||
try std.testing.expectEqualStrings("compiler_call", v.detail.?);
|
||||
try std.testing.expectEqualStrings("vec_splat", v.detail.?);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1632,7 +1632,6 @@ pub const LLVMEmitter = struct {
|
||||
|
||||
// ── Call extensions ───────────────────────────────────────
|
||||
.call_builtin => |bi| self.ops().emitCallBuiltin(instruction, bi),
|
||||
.compiler_call => self.ops().emitCompilerCall(instruction),
|
||||
.call_closure => |call_op| self.ops().emitCallClosure(instruction, call_op),
|
||||
|
||||
// ── Tuple ops ────────────────────────────────────────────
|
||||
|
||||
@@ -207,7 +207,6 @@ pub const Op = union(enum) {
|
||||
call_indirect: CallIndirect,
|
||||
call_closure: CallIndirect,
|
||||
call_builtin: BuiltinCall,
|
||||
compiler_call: CompilerCall,
|
||||
|
||||
/// `#objc_call(ReturnT)(recv, sel, args...)` — dispatched through
|
||||
/// `objc_msgSend`. emit_llvm.zig synthesizes a per-call-site LLVM
|
||||
@@ -473,11 +472,6 @@ pub const BuiltinId = enum(u16) {
|
||||
type_info,
|
||||
};
|
||||
|
||||
pub const CompilerCall = struct {
|
||||
name: u32, // StringPool id for qualified name (e.g. "BuildOptions.add_link_flag")
|
||||
args: []const Ref,
|
||||
};
|
||||
|
||||
pub const ClosureCreate = struct {
|
||||
func: FuncId, // trampoline function
|
||||
env: Ref, // allocated env pointer (or Ref.none for no captures)
|
||||
|
||||
@@ -200,9 +200,6 @@ pub const Interpreter = struct {
|
||||
// Mutable build configuration — set by LLVMEmitter, written by #run blocks
|
||||
build_config: ?*BuildConfig = null,
|
||||
|
||||
// Compiler hook registry for #compiler methods
|
||||
hooks: compiler_hooks.Registry,
|
||||
|
||||
// First op tag that bailed with InterpError, captured the first
|
||||
// time the interpreter unwinds so callers can surface "op=foo at
|
||||
// <file>:<offset>" alongside the bare error name. Static so it
|
||||
@@ -234,15 +231,12 @@ pub const Interpreter = struct {
|
||||
}
|
||||
|
||||
pub fn init(module: *const Module, alloc: Allocator) Interpreter {
|
||||
var hooks = compiler_hooks.Registry.init(alloc);
|
||||
hooks.registerDefaults();
|
||||
return .{
|
||||
.module = module,
|
||||
.alloc = alloc,
|
||||
.output = std.ArrayList(u8).empty,
|
||||
.heap = std.ArrayList([]u8).empty,
|
||||
.global_values = std.AutoHashMap(u32, Value).init(alloc),
|
||||
.hooks = hooks,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -268,7 +262,6 @@ pub const Interpreter = struct {
|
||||
self.output.deinit(self.alloc);
|
||||
self.call_chain.deinit(self.alloc);
|
||||
self.global_values.deinit();
|
||||
self.hooks.deinit();
|
||||
}
|
||||
|
||||
/// Write `val` to the raw host address `addr` using exactly the
|
||||
@@ -1126,31 +1119,6 @@ pub const Interpreter = struct {
|
||||
return self.execBuiltin(bi, frame, instruction.ty);
|
||||
},
|
||||
|
||||
// ── Compiler hook calls (#compiler methods) ────────
|
||||
.compiler_call => |cc| {
|
||||
const name = self.module.types.getString(@enumFromInt(cc.name));
|
||||
if (self.hooks.get(name)) |hook| {
|
||||
// Resolve args from Ref to Value
|
||||
var resolved_args = std.ArrayList(Value).empty;
|
||||
defer resolved_args.deinit(self.alloc);
|
||||
for (cc.args) |arg| {
|
||||
resolved_args.append(self.alloc, frame.getRef(arg)) catch return error.CannotEvalComptime;
|
||||
}
|
||||
if (self.build_config) |bc| {
|
||||
const result = hook(self, resolved_args.items, bc, self.alloc) catch return bailDetail("#compiler hook returned an error (see hook impl)");
|
||||
return .{ .value = result };
|
||||
}
|
||||
return .{ .value = .void_val };
|
||||
}
|
||||
if (last_bail_detail == null) {
|
||||
// Capture which hook name failed so the host diag
|
||||
// surfaces "compiler_call: unknown hook 'X'" instead
|
||||
// of a bare CannotEvalComptime.
|
||||
last_bail_detail = "#compiler hook not registered (likely a target-specific BuildOptions setter)";
|
||||
}
|
||||
return error.CannotEvalComptime;
|
||||
},
|
||||
|
||||
// ── Struct GEP (field pointer) ─────────────────────
|
||||
.struct_gep => |fa| {
|
||||
const base = frame.getRef(fa.base);
|
||||
|
||||
@@ -494,14 +494,6 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
return self.lowerGenericCall(fd, func_name, c, args.items);
|
||||
}
|
||||
}
|
||||
// Check for #compiler free functions
|
||||
if (self.program_index.fn_ast_map.get(func_name)) |fd_check| {
|
||||
if (fd_check.body.data == .compiler_expr) {
|
||||
const ret_ty = if (fd_check.return_type) |rt| type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map) else TypeId.void;
|
||||
return self.builder.compilerCall(func_name, args.items, ret_ty);
|
||||
}
|
||||
}
|
||||
|
||||
// Look up declared/extern function — try lazy lowering if not yet lowered
|
||||
{
|
||||
// First attempt: function may already be declared (from scanDecls)
|
||||
@@ -907,17 +899,6 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// Try direct qualified name: StructName.method
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sname, fa.field }) catch fa.field;
|
||||
|
||||
// Generic #compiler method dispatch
|
||||
if (self.program_index.fn_ast_map.get(qualified)) |method_fd| {
|
||||
if (method_fd.body.data == .compiler_expr) {
|
||||
const ret_ty = if (method_fd.return_type) |rt|
|
||||
type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map)
|
||||
else
|
||||
.void;
|
||||
return self.builder.compilerCall(qualified, method_args.items, ret_ty);
|
||||
}
|
||||
}
|
||||
|
||||
// Generic-struct instance method: select the body via the
|
||||
// instance's STAMPED author (CP-4), so the dispatched method is
|
||||
// the one authored alongside this instance's layout — never the
|
||||
|
||||
@@ -570,12 +570,6 @@ pub const Builder = struct {
|
||||
return self.emit(.{ .call_builtin = .{ .builtin = builtin, .args = owned } }, ret_ty);
|
||||
}
|
||||
|
||||
pub fn compilerCall(self: *Builder, name: []const u8, args: []const Ref, ret_ty: TypeId) Ref {
|
||||
const name_id = self.module.types.strings.intern(self.module.alloc, name);
|
||||
const owned = self.module.slice_arena.allocator().dupe(Ref, args) catch unreachable;
|
||||
return self.emit(.{ .compiler_call = .{ .name = @intFromEnum(name_id), .args = owned } }, ret_ty);
|
||||
}
|
||||
|
||||
// ── Closure ─────────────────────────────────────────────────────
|
||||
|
||||
pub fn closureCreate(self: *Builder, func_id: FuncId, env: Ref, ty: TypeId) Ref {
|
||||
|
||||
@@ -336,13 +336,6 @@ fn printInst(instruction: *const Inst, ref_idx: u32, tt: *const TypeTable, write
|
||||
a.clobbers.len,
|
||||
});
|
||||
},
|
||||
.compiler_call => |cc| {
|
||||
const name = tt.getString(@enumFromInt(cc.name));
|
||||
try writer.print("compiler_call \"{s}\"(", .{name});
|
||||
try writeArgs(cc.args, writer);
|
||||
try writer.writeAll(") : ");
|
||||
},
|
||||
|
||||
// ── Closure ─────────────────────────────────────────────
|
||||
.closure_create => |cc| {
|
||||
try writer.print("closure_create @{d}", .{cc.func.index()});
|
||||
|
||||
Reference in New Issue
Block a user