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 {
|
pub fn emitCallClosure(self: Ops, instruction: *const Inst, call_op: CallIndirect) void {
|
||||||
// Closure: { fn_ptr, env }.
|
// Closure: { fn_ptr, env }.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
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 inst = @import("inst.zig");
|
||||||
const FuncId = inst.FuncId;
|
const FuncId = inst.FuncId;
|
||||||
|
|
||||||
// ── BuildConfig ─────────────────────────────────────────────────────────
|
// ── 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
|
/// `(src_dir, dest_in_bundle)` pair recorded by
|
||||||
/// `BuildOptions.add_asset_dir(src, dest)`. The sx bundler walks the
|
/// `BuildOptions.add_asset_dir(src, dest)`. The sx bundler walks the
|
||||||
@@ -144,440 +143,3 @@ pub const BuildHooks = struct {
|
|||||||
) anyerror!void,
|
) 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;
|
const v = vm.tryEval(alloc, &module, ok_id, null, null) orelse return error.VmShouldHaveHandledIt;
|
||||||
try std.testing.expectEqual(@as(i64, 42), v.int);
|
try std.testing.expectEqual(@as(i64, 42), v.int);
|
||||||
|
|
||||||
// fn bad() { compiler_call() } → an unported op → tryEval yields null (caller
|
// fn bad() { vec_splat(...) } → an unported op → tryEval yields null. The VM
|
||||||
// falls back to legacy). (box_any/unbox_any are now VM-native; compiler_call is
|
// bails loudly on any op it does not model (never a silent default); vec_splat
|
||||||
// still unported until Phase 4D.)
|
// is a stable example of one.
|
||||||
var fb2 = Fb.init(alloc, &.{}, .void);
|
var fb2 = Fb.init(alloc, &.{}, .void);
|
||||||
const c0 = fb2.block(&.{});
|
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));
|
_ = fb2.add(c0, inst(.ret_void, .void));
|
||||||
const bad_id = module.addFunction(fb2.func);
|
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.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) }));
|
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);
|
var fb = Fb.init(std.testing.allocator, &.{}, .void);
|
||||||
defer fb.deinit();
|
defer fb.deinit();
|
||||||
const b0 = fb.block(&.{});
|
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));
|
_ = fb.add(b0, inst(.ret_void, .void));
|
||||||
|
|
||||||
var v = vm.Vm.init(std.testing.allocator);
|
var v = vm.Vm.init(std.testing.allocator);
|
||||||
defer v.deinit();
|
defer v.deinit();
|
||||||
try std.testing.expectError(error.Unsupported, v.run(&fb.func, &.{}));
|
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 extensions ───────────────────────────────────────
|
||||||
.call_builtin => |bi| self.ops().emitCallBuiltin(instruction, bi),
|
.call_builtin => |bi| self.ops().emitCallBuiltin(instruction, bi),
|
||||||
.compiler_call => self.ops().emitCompilerCall(instruction),
|
|
||||||
.call_closure => |call_op| self.ops().emitCallClosure(instruction, call_op),
|
.call_closure => |call_op| self.ops().emitCallClosure(instruction, call_op),
|
||||||
|
|
||||||
// ── Tuple ops ────────────────────────────────────────────
|
// ── Tuple ops ────────────────────────────────────────────
|
||||||
|
|||||||
@@ -207,7 +207,6 @@ pub const Op = union(enum) {
|
|||||||
call_indirect: CallIndirect,
|
call_indirect: CallIndirect,
|
||||||
call_closure: CallIndirect,
|
call_closure: CallIndirect,
|
||||||
call_builtin: BuiltinCall,
|
call_builtin: BuiltinCall,
|
||||||
compiler_call: CompilerCall,
|
|
||||||
|
|
||||||
/// `#objc_call(ReturnT)(recv, sel, args...)` — dispatched through
|
/// `#objc_call(ReturnT)(recv, sel, args...)` — dispatched through
|
||||||
/// `objc_msgSend`. emit_llvm.zig synthesizes a per-call-site LLVM
|
/// `objc_msgSend`. emit_llvm.zig synthesizes a per-call-site LLVM
|
||||||
@@ -473,11 +472,6 @@ pub const BuiltinId = enum(u16) {
|
|||||||
type_info,
|
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 {
|
pub const ClosureCreate = struct {
|
||||||
func: FuncId, // trampoline function
|
func: FuncId, // trampoline function
|
||||||
env: Ref, // allocated env pointer (or Ref.none for no captures)
|
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
|
// Mutable build configuration — set by LLVMEmitter, written by #run blocks
|
||||||
build_config: ?*BuildConfig = null,
|
build_config: ?*BuildConfig = null,
|
||||||
|
|
||||||
// Compiler hook registry for #compiler methods
|
|
||||||
hooks: compiler_hooks.Registry,
|
|
||||||
|
|
||||||
// First op tag that bailed with InterpError, captured the first
|
// First op tag that bailed with InterpError, captured the first
|
||||||
// time the interpreter unwinds so callers can surface "op=foo at
|
// time the interpreter unwinds so callers can surface "op=foo at
|
||||||
// <file>:<offset>" alongside the bare error name. Static so it
|
// <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 {
|
pub fn init(module: *const Module, alloc: Allocator) Interpreter {
|
||||||
var hooks = compiler_hooks.Registry.init(alloc);
|
|
||||||
hooks.registerDefaults();
|
|
||||||
return .{
|
return .{
|
||||||
.module = module,
|
.module = module,
|
||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.output = std.ArrayList(u8).empty,
|
.output = std.ArrayList(u8).empty,
|
||||||
.heap = std.ArrayList([]u8).empty,
|
.heap = std.ArrayList([]u8).empty,
|
||||||
.global_values = std.AutoHashMap(u32, Value).init(alloc),
|
.global_values = std.AutoHashMap(u32, Value).init(alloc),
|
||||||
.hooks = hooks,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +262,6 @@ pub const Interpreter = struct {
|
|||||||
self.output.deinit(self.alloc);
|
self.output.deinit(self.alloc);
|
||||||
self.call_chain.deinit(self.alloc);
|
self.call_chain.deinit(self.alloc);
|
||||||
self.global_values.deinit();
|
self.global_values.deinit();
|
||||||
self.hooks.deinit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write `val` to the raw host address `addr` using exactly the
|
/// 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);
|
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 (field pointer) ─────────────────────
|
||||||
.struct_gep => |fa| {
|
.struct_gep => |fa| {
|
||||||
const base = frame.getRef(fa.base);
|
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);
|
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
|
// Look up declared/extern function — try lazy lowering if not yet lowered
|
||||||
{
|
{
|
||||||
// First attempt: function may already be declared (from scanDecls)
|
// 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
|
// Try direct qualified name: StructName.method
|
||||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sname, fa.field }) catch fa.field;
|
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
|
// Generic-struct instance method: select the body via the
|
||||||
// instance's STAMPED author (CP-4), so the dispatched method is
|
// instance's STAMPED author (CP-4), so the dispatched method is
|
||||||
// the one authored alongside this instance's layout — never the
|
// 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);
|
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 ─────────────────────────────────────────────────────
|
// ── Closure ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub fn closureCreate(self: *Builder, func_id: FuncId, env: Ref, ty: TypeId) Ref {
|
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,
|
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 ─────────────────────────────────────────────
|
||||||
.closure_create => |cc| {
|
.closure_create => |cc| {
|
||||||
try writer.print("closure_create @{d}", .{cc.func.index()});
|
try writer.print("closure_create @{d}", .{cc.func.index()});
|
||||||
|
|||||||
Reference in New Issue
Block a user