This commit is contained in:
agra
2026-03-02 21:00:55 +02:00
parent 2f4f898d54
commit bbb5426777
42 changed files with 483 additions and 9023 deletions

View File

@@ -86,6 +86,9 @@ pub const LLVMEmitter = struct {
// Target configuration (stored for ABI decisions during emission)
target_config: TargetConfig,
// Build configuration accumulated from #run blocks
build_config: interp_mod.BuildConfig,
const PendingPhi = struct {
phi: c.LLVMValueRef,
block_id: BlockId, // the block this phi belongs to
@@ -158,10 +161,12 @@ pub const LLVMEmitter = struct {
.closure_struct_type = null,
.field_name_arrays = std.AutoHashMap(u32, c.LLVMValueRef).init(alloc),
.target_config = target_config,
.build_config = .{},
};
}
pub fn deinit(self: *LLVMEmitter) void {
self.build_config.deinit(self.alloc);
self.ref_map.deinit();
self.func_map.deinit();
self.field_name_arrays.deinit();
@@ -199,9 +204,9 @@ pub const LLVMEmitter = struct {
/// Compare IR typeSizeBytes against LLVMABISizeOfType for all user-defined types.
fn verifySizes(self: *LLVMEmitter) void {
// Skip for WASM: wasm32 has 4-byte pointers vs IR's assumed 8-byte,
// Skip for wasm32: 4-byte pointers vs IR's assumed 8-byte,
// so struct sizes will differ. LLVM handles emission correctly.
if (self.target_config.isWasm()) return;
if (self.target_config.isWasm32()) return;
const dl = c.LLVMGetModuleDataLayout(self.llvm_module);
if (dl == null) return;
const type_count = self.ir_mod.types.infos.items.len;
@@ -241,6 +246,7 @@ pub const LLVMEmitter = struct {
// Run the side-effect function via interpreter
const func_id = ir_inst.FuncId.fromIndex(@intCast(i));
var interp_inst = Interpreter.init(self.ir_mod, self.alloc);
interp_inst.build_config = &self.build_config;
_ = interp_inst.call(func_id, &.{}) catch {};
// Write comptime output to stderr (same as old comptime VM)
if (interp_inst.output.items.len > 0) {
@@ -263,6 +269,7 @@ pub const LLVMEmitter = struct {
// Evaluate comptime initializer if present
if (global.comptime_func) |func_id| {
var interp_inst = Interpreter.init(self.ir_mod, self.alloc);
interp_inst.build_config = &self.build_config;
const result = interp_inst.call(func_id, &.{}) catch .void_val;
const init_val = self.valueToLLVMConst(result, llvm_ty);
c.LLVMSetInitializer(llvm_global, init_val);
@@ -793,6 +800,7 @@ pub const LLVMEmitter = struct {
const callee_func = &self.ir_mod.functions.items[call_op.callee.index()];
if (callee_func.is_comptime and call_op.args.len == 0) {
var interp_inst = Interpreter.init(self.ir_mod, self.alloc);
interp_inst.build_config = &self.build_config;
defer interp_inst.deinit();
if (interp_inst.call(call_op.callee, &.{})) |result| {
if (result.asInt()) |v| {
@@ -1427,7 +1435,7 @@ pub const LLVMEmitter = struct {
const raw_ptr = c.LLVMBuildExtractValue(self.builder, str_val, 0, "str.ptr");
const str_len = c.LLVMBuildExtractValue(self.builder, str_val, 1, "str.len");
// On wasm32, count param is i32 (size_t)
const count = if (self.target_config.isWasm())
const count = if (self.target_config.isWasm32())
c.LLVMBuildTrunc(self.builder, str_len, self.cached_i32, "len.tr")
else
str_len;
@@ -2132,9 +2140,9 @@ pub const LLVMEmitter = struct {
return c.LLVMAddFunction(self.llvm_module, "free", fn_ty);
}
/// Returns the LLVM type for C `size_t`: i32 on wasm32, i64 on 64-bit targets.
/// Returns the LLVM type for C `size_t`: i32 on wasm32, i64 on 64-bit targets (including wasm64).
fn sizeType(self: *LLVMEmitter) c.LLVMTypeRef {
return if (self.target_config.isWasm()) self.cached_i32 else self.cached_i64;
return if (self.target_config.isWasm32()) self.cached_i32 else self.cached_i64;
}
fn getMallocType(self: *LLVMEmitter) c.LLVMTypeRef {
@@ -2540,7 +2548,7 @@ pub const LLVMEmitter = struct {
.string => self.getStringStructType(),
.any => self.getAnyStructType(),
.noreturn => self.cached_void,
.isize, .usize => if (self.target_config.isWasm()) self.cached_i32 else self.cached_i64,
.isize, .usize => if (self.target_config.isWasm32()) self.cached_i32 else self.cached_i64,
else => self.toLLVMTypeInfo(ty),
};
}
@@ -2667,7 +2675,7 @@ pub const LLVMEmitter = struct {
// For now, use opaque ptr
return self.cached_ptr;
},
.usize, .isize => if (self.target_config.isWasm()) self.cached_i32 else self.cached_i64,
.usize, .isize => if (self.target_config.isWasm32()) self.cached_i32 else self.cached_i64,
};
}
@@ -2690,7 +2698,7 @@ pub const LLVMEmitter = struct {
// WASM32: usize/isize are pointer-sized (i32 on wasm32).
// Other integer types (s64, u64) keep their declared size — they represent
// genuinely 64-bit values (SDL_WindowFlags, timestamps, etc.).
if (self.target_config.isWasm()) {
if (self.target_config.isWasm32()) {
if (ir_ty == .usize or ir_ty == .isize) return self.cached_i32;
return llvm_ty;
}
@@ -3007,6 +3015,13 @@ pub const LLVMEmitter = struct {
return self.emitToFile(output_path, c.LLVMAssemblyFile);
}
/// Emit the module as LLVM bitcode to disk (for emcc to recompile with a newer LLVM).
pub fn emitBitcode(self: *LLVMEmitter, output_path: [*:0]const u8) !void {
if (c.LLVMWriteBitcodeToFile(self.llvm_module, output_path) != 0) {
return error.EmitFailed;
}
}
/// Dump the LLVM IR to a file for debugging.
pub fn dumpIRToFile(self: *LLVMEmitter, path: [*:0]const u8) void {
_ = c.LLVMPrintModuleToFile(self.llvm_module, path, null);