diff --git a/examples/28-sdl-graphics.sx b/examples/28-sdl-graphics.sx index fd32f7a..70bdeba 100644 --- a/examples/28-sdl-graphics.sx +++ b/examples/28-sdl-graphics.sx @@ -190,7 +190,7 @@ void main() { } GLSL; - program : u32 = create_program(vert_src, frag_src); + program := create_program(vert_src, frag_src); glUseProgram(program); mvp_loc : s32 = glGetUniformLocation(program, "uMVP"); @@ -200,7 +200,7 @@ GLSL; // Cube vertices: pos(vec4 w=1) + normal(vec4 w=0), 36 vertices × 2 vec4s = 72 vertices : []Vector(4, f32) = .[ // Front face (z = +0.5) - vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), + .[-0.5, -0.5, 0.5, 1.0], vec4( 0.0, 0.0, 1.0, 0.0), vec4( 0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), vec4( 0.5, 0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), vec4(-0.5, -0.5, 0.5, 1.0), vec4( 0.0, 0.0, 1.0, 0.0), @@ -246,6 +246,7 @@ GLSL; // Create VAO and VBO vao : u32 = 0; vbo : u32 = 0; + glGenVertexArrays(1, vao); glGenBuffers(1, vbo); @@ -276,6 +277,7 @@ GLSL; case .key_up: (e) { if e.key == { case .escape: running = false; + //case . } } case .window_exposed: (e) { diff --git a/src/codegen.zig b/src/codegen.zig index 46bf4a2..bea1515 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -384,6 +384,11 @@ pub const CodeGen = struct { return self.tagged_enum_types.get(name) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{name}); } + fn resolveElementType(self: *CodeGen, name: []const u8, comptime kind: []const u8) !Type { + return Type.fromName(name) orelse + return self.emitErrorFmt("unknown " ++ kind ++ " element type '{s}'", .{name}); + } + fn emitError(self: *CodeGen, msg: []const u8) error{CodeGenError} { if (self.diagnostics) |diags| diags.add(.err, msg, self.current_span); return error.CodeGenError; @@ -394,6 +399,10 @@ pub const CodeGen = struct { return error.CodeGenError; } + fn requireBuiltins(self: *CodeGen) !Builtins { + return self.builtins orelse return self.emitError("builtins not available"); + } + /// Build an alloca in the entry block of the current function so that /// stack space is reserved once, not on every loop iteration. fn buildEntryBlockAlloca(self: *CodeGen, ty: c.LLVMTypeRef, name: [*:0]const u8) c.LLVMValueRef { @@ -409,14 +418,19 @@ pub const CodeGen = struct { return c.LLVMBuildAlloca(tmp_builder, ty, name); } + fn buildNamedAlloca(self: *CodeGen, ty: c.LLVMTypeRef, name: []const u8) !c.LLVMValueRef { + const name_z = try self.allocator.dupeZ(u8, name); + return self.buildEntryBlockAlloca(ty, name_z.ptr); + } + pub fn typeToLLVM(self: *CodeGen, ty: Type) c.LLVMTypeRef { return switch (ty) { .signed => |w| c.LLVMIntTypeInContext(self.context, w), .unsigned => |w| c.LLVMIntTypeInContext(self.context, w), - .f32 => c.LLVMFloatTypeInContext(self.context), - .f64 => c.LLVMDoubleTypeInContext(self.context), - .void_type => c.LLVMVoidTypeInContext(self.context), - .boolean => c.LLVMInt1TypeInContext(self.context), + .f32 => self.f32Type(), + .f64 => self.f64Type(), + .void_type => self.voidType(), + .boolean => self.i1Type(), .string_type, .slice_type => self.getStringStructType(), // slices use same {ptr, i32} layout .enum_type => |name| self.getEnumLLVMType(name), .struct_type => |name| if (self.struct_types.get(name)) |info| info.llvm_type else unreachable, @@ -429,22 +443,22 @@ pub const CodeGen = struct { const elem_ty = Type.fromName(info.element_name) orelse unreachable; return c.LLVMVectorType(self.typeToLLVM(elem_ty), info.length); }, - .pointer_type, .many_pointer_type, .function_type => c.LLVMPointerTypeInContext(self.context, 0), + .pointer_type, .many_pointer_type, .function_type => self.ptrType(), .any_type => self.getAnyStructType(), - .meta_type => c.LLVMPointerTypeInContext(self.context, 0), + .meta_type => self.ptrType(), }; } fn getEnumLLVMType(self: *CodeGen, enum_name: []const u8) c.LLVMTypeRef { if (self.enum_backing_types.get(enum_name)) |llvm_ty| return llvm_ty; - return c.LLVMInt64TypeInContext(self.context); + return self.i64Type(); } fn getAnyStructType(self: *CodeGen) c.LLVMTypeRef { if (self.any_struct_type) |t| return t; var field_types = [_]c.LLVMTypeRef{ - c.LLVMInt64TypeInContext(self.context), // type tag - c.LLVMInt64TypeInContext(self.context), // value (fits all primitives) + self.i64Type(), // type tag + self.i64Type(), // value (fits all primitives) }; self.any_struct_type = c.LLVMStructTypeInContext(self.context, &field_types, 2, 0); return self.any_struct_type.?; @@ -534,7 +548,7 @@ pub const CodeGen = struct { /// Complex values (strings, structs, unions) are stored via pointer (alloca + ptr-to-int). fn buildAnyValue(self: *CodeGen, val: c.LLVMValueRef, in_ty: Type) !c.LLVMValueRef { const any_ty = self.getAnyStructType(); - const i64_ty = c.LLVMInt64TypeInContext(self.context); + const i64_ty = self.i64Type(); const undef = c.LLVMGetUndef(any_ty); // []u8 boxes as string (same repr, same Any tag) @@ -579,7 +593,7 @@ pub const CodeGen = struct { val, .f32 => blk: { // f32 -> f64 -> bitcast to i64 - const as_f64 = c.LLVMBuildFPExt(self.builder, val, c.LLVMDoubleTypeInContext(self.context), "f32_to_f64"); + const as_f64 = c.LLVMBuildFPExt(self.builder, val, self.f64Type(), "f32_to_f64"); break :blk c.LLVMBuildBitCast(self.builder, as_f64, i64_ty, "any_f32"); }, .f64 => c.LLVMBuildBitCast(self.builder, val, i64_ty, "any_f64"), @@ -643,27 +657,137 @@ pub const CodeGen = struct { fn getStringStructType(self: *CodeGen) c.LLVMTypeRef { if (self.string_struct_type) |t| return t; var field_types = [_]c.LLVMTypeRef{ - c.LLVMPointerTypeInContext(self.context, 0), // ptr - c.LLVMInt64TypeInContext(self.context), // len + self.ptrType(), // ptr + self.i64Type(), // len }; self.string_struct_type = c.LLVMStructTypeInContext(self.context, &field_types, 2, 0); return self.string_struct_type.?; } + /// Build a fat pointer {ptr, len} struct from a type, pointer, and length value. + fn buildFatPointer(self: *CodeGen, ty: c.LLVMTypeRef, ptr: c.LLVMValueRef, len: c.LLVMValueRef) c.LLVMValueRef { + const undef = c.LLVMGetUndef(ty); + const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "ptr"); + return c.LLVMBuildInsertValue(self.builder, with_ptr, len, 1, "len"); + } + /// Build a string slice {ptr, len} from a raw pointer and a length value. fn buildStringSlice(self: *CodeGen, ptr: c.LLVMValueRef, len_val: c.LLVMValueRef) c.LLVMValueRef { - const str_ty = self.getStringStructType(); - const undef = c.LLVMGetUndef(str_ty); - const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr"); - return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice"); + return self.buildFatPointer(self.getStringStructType(), ptr, len_val); + } + + // LLVM type shortcuts + fn i1Type(self: *CodeGen) c.LLVMTypeRef { return c.LLVMInt1TypeInContext(self.context); } + fn i8Type(self: *CodeGen) c.LLVMTypeRef { return c.LLVMInt8TypeInContext(self.context); } + fn i32Type(self: *CodeGen) c.LLVMTypeRef { return c.LLVMInt32TypeInContext(self.context); } + fn i64Type(self: *CodeGen) c.LLVMTypeRef { return c.LLVMInt64TypeInContext(self.context); } + fn f32Type(self: *CodeGen) c.LLVMTypeRef { return c.LLVMFloatTypeInContext(self.context); } + fn f64Type(self: *CodeGen) c.LLVMTypeRef { return c.LLVMDoubleTypeInContext(self.context); } + fn ptrType(self: *CodeGen) c.LLVMTypeRef { return c.LLVMPointerTypeInContext(self.context, 0); } + fn voidType(self: *CodeGen) c.LLVMTypeRef { return c.LLVMVoidTypeInContext(self.context); } + + fn gepArrayElement(self: *CodeGen, arr_ty: c.LLVMTypeRef, arr_ptr: c.LLVMValueRef, idx: c.LLVMValueRef, name: [*c]const u8) c.LLVMValueRef { + var indices = [_]c.LLVMValueRef{ self.constInt32(0), idx }; + return c.LLVMBuildGEP2(self.builder, arr_ty, arr_ptr, &indices, 2, name); + } + + fn gepPointerElement(self: *CodeGen, elem_ty: c.LLVMTypeRef, ptr: c.LLVMValueRef, idx: c.LLVMValueRef, name: [*c]const u8) c.LLVMValueRef { + var indices = [_]c.LLVMValueRef{idx}; + return c.LLVMBuildGEP2(self.builder, elem_ty, ptr, &indices, 1, name); + } + + fn arrayDecayToPointer(self: *CodeGen, arr_ty: c.LLVMTypeRef, arr_ptr: c.LLVMValueRef, name: [*c]const u8) c.LLVMValueRef { + const zero = self.constInt64(0); + var indices = [_]c.LLVMValueRef{ zero, zero }; + return c.LLVMBuildGEP2(self.builder, arr_ty, arr_ptr, &indices, 2, name); + } + + fn storeStructField(self: *CodeGen, struct_ty: c.LLVMTypeRef, ptr: c.LLVMValueRef, field_idx: c_uint, val: c.LLVMValueRef) void { + const gep = c.LLVMBuildStructGEP2(self.builder, struct_ty, ptr, field_idx, "field"); + _ = c.LLVMBuildStore(self.builder, val, gep); + } + + fn loadStructField(self: *CodeGen, struct_ty: c.LLVMTypeRef, ptr: c.LLVMValueRef, field_idx: c_uint, field_ty: c.LLVMTypeRef) c.LLVMValueRef { + const gep = c.LLVMBuildStructGEP2(self.builder, struct_ty, ptr, field_idx, "field"); + return c.LLVMBuildLoad2(self.builder, field_ty, gep, "fieldval"); + } + + fn storeUndef(self: *CodeGen, ty: c.LLVMTypeRef, ptr: c.LLVMValueRef) void { + _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(ty), ptr); + } + + fn storeNull(self: *CodeGen, ty: c.LLVMTypeRef, ptr: c.LLVMValueRef) void { + _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(ty), ptr); + } + + fn loadTyped(self: *CodeGen, ty: Type, ptr: c.LLVMValueRef, name: [*c]const u8) c.LLVMValueRef { + return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(ty), ptr, name); + } + + const SwitchBlock = struct { + merge_bb: c.LLVMBasicBlockRef, + default_bb: c.LLVMBasicBlockRef, + sw: c.LLVMValueRef, + }; + + fn buildSwitch(self: *CodeGen, cond: c.LLVMValueRef, case_count: c_uint, merge_name: [*c]const u8, default_name: [*c]const u8) SwitchBlock { + const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, merge_name); + const default_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, default_name); + const sw = c.LLVMBuildSwitch(self.builder, cond, default_bb, case_count); + return .{ .merge_bb = merge_bb, .default_bb = default_bb, .sw = sw }; + } + + fn loadIfPointer(self: *CodeGen, val: c.LLVMValueRef, ty: c.LLVMTypeRef, name: [*c]const u8) c.LLVMValueRef { + if (c.LLVMGetTypeKind(c.LLVMTypeOf(val)) == c.LLVMPointerTypeKind) { + return c.LLVMBuildLoad2(self.builder, ty, val, name); + } + return val; + } + + fn loadFromI64Ptr(self: *CodeGen, i64_val: c.LLVMValueRef, ty: c.LLVMTypeRef, name: [*c]const u8) c.LLVMValueRef { + const ptr = c.LLVMBuildIntToPtr(self.builder, i64_val, self.ptrType(), name); + return c.LLVMBuildLoad2(self.builder, ty, ptr, name); + } + + fn resolveAlias(self: *CodeGen, name: []const u8) []const u8 { + return self.type_aliases.get(name) orelse name; + } + + fn buildPhiNode(self: *CodeGen, phi_vals: *std.ArrayList(c.LLVMValueRef), phi_bbs: *std.ArrayList(c.LLVMBasicBlockRef), ty: c.LLVMTypeRef, name: [*c]const u8) !c.LLVMValueRef { + const vals_slice = try phi_vals.toOwnedSlice(self.allocator); + const bbs_slice = try phi_bbs.toOwnedSlice(self.allocator); + const phi = c.LLVMBuildPhi(self.builder, ty, name); + c.LLVMAddIncoming(phi, vals_slice.ptr, bbs_slice.ptr, @intCast(vals_slice.len)); + return phi; + } + + fn addPhiCase(self: *CodeGen, phi_vals: *std.ArrayList(c.LLVMValueRef), phi_bbs: *std.ArrayList(c.LLVMBasicBlockRef), val: c.LLVMValueRef, merge_bb: c.LLVMBasicBlockRef) !void { + try phi_vals.append(self.allocator, val); + try phi_bbs.append(self.allocator, c.LLVMGetInsertBlock(self.builder)); + _ = c.LLVMBuildBr(self.builder, merge_bb); + } + + fn getTypeSize(self: *CodeGen, ty: c.LLVMTypeRef) u64 { + return c.LLVMStoreSizeOfType(c.LLVMGetModuleDataLayout(self.module), ty); + } + + fn appendBlock(self: *CodeGen, function: c.LLVMValueRef, name: [*c]const u8) c.LLVMBasicBlockRef { + const bb = c.LLVMAppendBasicBlockInContext(self.context, function, name); + c.LLVMPositionBuilderAtEnd(self.builder, bb); + return bb; + } + + fn valueToBool(self: *CodeGen, val: c.LLVMValueRef) c.LLVMValueRef { + if (c.LLVMTypeOf(val) == self.i1Type()) return val; + return c.LLVMBuildICmp(self.builder, c.LLVMIntNE, val, c.LLVMConstInt(c.LLVMTypeOf(val), 0, 0), "tobool"); } fn constInt64(self: *CodeGen, val: u64) c.LLVMValueRef { - return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), val, 0); + return c.LLVMConstInt(self.i64Type(), val, 0); } fn constInt32(self: *CodeGen, val: u32) c.LLVMValueRef { - return c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), val, 0); + return c.LLVMConstInt(self.i32Type(), val, 0); } /// Extract .len or .ptr from a fat pointer value ({ptr, len} struct). @@ -735,6 +859,11 @@ pub const CodeGen = struct { try top.append(self.allocator, .{ .name = name, .prev = prev }); } + fn registerVariable(self: *CodeGen, name: []const u8, ptr: c.LLVMValueRef, ty: Type) !void { + try self.saveShadowed(name); + try self.named_values.put(name, .{ .ptr = ptr, .ty = ty }); + } + pub fn generate(self: *CodeGen, root: *Node) !void { if (root.data != .root) return self.emitError("expected root node for code generation"); // Store root decls for VM on-demand function compilation @@ -1003,17 +1132,17 @@ pub const CodeGen = struct { fn comptimeValueToLLVM(self: *CodeGen, value: comptime_mod.Value, ty: Type) c.LLVMValueRef { return switch (value) { .int_val => |v| c.LLVMConstInt(self.typeToLLVM(ty), @bitCast(v), 0), - .float_val => |v| c.LLVMConstReal(c.LLVMDoubleTypeInContext(self.context), v), - .float32_val => |v| c.LLVMConstReal(c.LLVMFloatTypeInContext(self.context), @as(f64, v)), - .bool_val => |v| c.LLVMConstInt(c.LLVMInt1TypeInContext(self.context), if (v) 1 else 0, 0), + .float_val => |v| c.LLVMConstReal(self.f64Type(), v), + .float32_val => |v| c.LLVMConstReal(self.f32Type(), @as(f64, v)), + .bool_val => |v| c.LLVMConstInt(self.i1Type(), if (v) 1 else 0, 0), .string_val => |v| blk: { const z = self.allocator.dupeZ(u8, v) catch unreachable; const ptr = c.LLVMBuildGlobalStringPtr(self.builder, z.ptr, "comptime_str"); break :blk self.buildStringSlice(ptr, self.constInt64(@intCast(v.len))); }, .void_val => self.constInt32(0), - .pointer_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)), - .null_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)), + .pointer_val => c.LLVMConstNull(self.ptrType()), + .null_val => c.LLVMConstNull(self.ptrType()), .struct_val, .array_val, .type_val, .function_val => unreachable, }; } @@ -1388,7 +1517,7 @@ pub const CodeGen = struct { const ret_sx_type = self.resolveType(return_type); const is_main = std.mem.eql(u8, name, "main"); const ret_llvm_type = if (is_main) - c.LLVMInt32TypeInContext(self.context) + self.i32Type() else if (is_foreign and ret_sx_type.isStruct()) self.getForeignReturnABIType(ret_sx_type) else @@ -1405,12 +1534,12 @@ pub const CodeGen = struct { if (sx_ty == .void_type) return self.emitErrorFmt("parameter '{s}' in function '{s}' has unresolved type", .{ param.name, name }); // Foreign functions: apply C ABI lowering if (is_foreign and sx_ty == .string_type) { - try param_llvm_types.append(self.allocator, c.LLVMPointerTypeInContext(self.context, 0)); + try param_llvm_types.append(self.allocator, self.ptrType()); } else if (is_foreign and sx_ty.isStruct()) { try param_llvm_types.append(self.allocator, self.getForeignParamABIType(sx_ty)); } else if (is_foreign and sx_ty.isArray()) { // [N]T → pointer in C ABI (C arrays decay to pointers) - try param_llvm_types.append(self.allocator, c.LLVMPointerTypeInContext(self.context, 0)); + try param_llvm_types.append(self.allocator, self.ptrType()); } else { try param_llvm_types.append(self.allocator, self.typeToLLVM(sx_ty)); } @@ -1432,7 +1561,7 @@ pub const CodeGen = struct { fn getForeignParamABIType(self: *CodeGen, sx_ty: Type) c.LLVMTypeRef { if (!sx_ty.isStruct()) return self.typeToLLVM(sx_ty); - const sname = self.type_aliases.get(sx_ty.struct_type) orelse sx_ty.struct_type; + const sname = self.resolveAlias(sx_ty.struct_type); const info = self.struct_types.get(sname) orelse return self.typeToLLVM(sx_ty); if (self.target_config.isAarch64()) { @@ -1467,19 +1596,18 @@ pub const CodeGen = struct { } if (all_same) { const elem_ty = if (first == .f32) - c.LLVMFloatTypeInContext(self.context) + self.f32Type() else - c.LLVMDoubleTypeInContext(self.context); + self.f64Type(); return c.LLVMArrayType2(elem_ty, @intCast(field_types.len)); } } } // Non-HFA: pack into integer registers - const data_layout = c.LLVMGetModuleDataLayout(self.module); - const size = c.LLVMStoreSizeOfType(data_layout, info.llvm_type); - if (size <= 8) return c.LLVMInt64TypeInContext(self.context); - if (size <= 16) return c.LLVMArrayType2(c.LLVMInt64TypeInContext(self.context), 2); + const size = self.getTypeSize(info.llvm_type); + if (size <= 8) return self.i64Type(); + if (size <= 16) return c.LLVMArrayType2(self.i64Type(), 2); return info.llvm_type; } @@ -1490,7 +1618,7 @@ pub const CodeGen = struct { /// - Structs > 16 bytes: passed in memory (by pointer) fn x86_64SysVParamABI(self: *CodeGen, info: StructInfo) c.LLVMTypeRef { const data_layout = c.LLVMGetModuleDataLayout(self.module); - const size = c.LLVMStoreSizeOfType(data_layout, info.llvm_type); + const size = self.getTypeSize(info.llvm_type); // > 16 bytes: MEMORY class (passed by pointer, handled by LLVM backend) if (size > 16) return info.llvm_type; @@ -1514,7 +1642,7 @@ pub const CodeGen = struct { } else { second_eb_types.append(self.allocator, ft) catch return info.llvm_type; const field_llvm = self.typeToLLVM(ft); - second_eb_size += c.LLVMStoreSizeOfType(data_layout, field_llvm); + second_eb_size += self.getTypeSize(field_llvm); } } @@ -1552,14 +1680,14 @@ pub const CodeGen = struct { if (all_sse) { // SSE class: return appropriate float type if (double_count > 0 and float_count == 0) { - if (double_count == 1) return c.LLVMDoubleTypeInContext(self.context); + if (double_count == 1) return self.f64Type(); // Multiple doubles shouldn't fit in one eightbyte (double = 8 bytes) - return c.LLVMDoubleTypeInContext(self.context); + return self.f64Type(); } if (float_count > 0 and double_count == 0) { - if (float_count == 1) return c.LLVMFloatTypeInContext(self.context); + if (float_count == 1) return self.f32Type(); // 2 floats = 8 bytes, fits in one eightbyte - return c.LLVMArrayType2(c.LLVMFloatTypeInContext(self.context), @intCast(float_count)); + return c.LLVMArrayType2(self.f32Type(), @intCast(float_count)); } // Mixed float/double in one eightbyte shouldn't happen (float=4, double=8) // but fall through to integer just in case @@ -1573,8 +1701,7 @@ pub const CodeGen = struct { /// Only structs of exactly 1, 2, 4, or 8 bytes are passed in a register. /// Everything else is passed by pointer (handled by LLVM backend). fn win64ParamABI(self: *CodeGen, info: StructInfo) c.LLVMTypeRef { - const data_layout = c.LLVMGetModuleDataLayout(self.module); - const size = c.LLVMStoreSizeOfType(data_layout, info.llvm_type); + const size = self.getTypeSize(info.llvm_type); // Windows x64: only power-of-2 sizes ≤ 8 passed in register if (size == 1 or size == 2 or size == 4 or size == 8) { @@ -1596,9 +1723,8 @@ pub const CodeGen = struct { /// Convert a struct value to its C ABI representation for a foreign call. /// Stores the struct to memory, then loads as the ABI type. fn convertStructToABI(self: *CodeGen, struct_val: c.LLVMValueRef, struct_ty: c.LLVMTypeRef, abi_ty: c.LLVMTypeRef) c.LLVMValueRef { - const data_layout = c.LLVMGetModuleDataLayout(self.module); - const struct_size = c.LLVMStoreSizeOfType(data_layout, struct_ty); - const abi_size = c.LLVMStoreSizeOfType(data_layout, abi_ty); + const struct_size = self.getTypeSize(struct_ty); + const abi_size = self.getTypeSize(abi_ty); if (struct_size == abi_size) { // Same size (e.g. {float, float} → [2 x float]): store and reload @@ -1608,7 +1734,7 @@ pub const CodeGen = struct { } else { // Struct smaller than ABI type (e.g. {i8,i8,i8,i8} → i64): zero-init, then store struct const alloca = self.buildEntryBlockAlloca(abi_ty, "abi_tmp"); - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(abi_ty), alloca); + self.storeNull(abi_ty, alloca); _ = c.LLVMBuildStore(self.builder, struct_val, alloca); return c.LLVMBuildLoad2(self.builder, abi_ty, alloca, "abi_arg"); } @@ -1776,17 +1902,17 @@ pub const CodeGen = struct { /// Map an LLVM type back to a sx Type fn llvmTypeToSxType(self: *CodeGen, llvm_ty: c.LLVMTypeRef) Type { - if (llvm_ty == c.LLVMInt1TypeInContext(self.context)) return .boolean; - if (llvm_ty == c.LLVMInt8TypeInContext(self.context)) return Type.s(8); + if (llvm_ty == self.i1Type()) return .boolean; + if (llvm_ty == self.i8Type()) return Type.s(8); if (llvm_ty == c.LLVMInt16TypeInContext(self.context)) return Type.s(16); - if (llvm_ty == c.LLVMInt32TypeInContext(self.context)) return Type.s(32); - if (llvm_ty == c.LLVMInt64TypeInContext(self.context)) return Type.s(64); - if (llvm_ty == c.LLVMFloatTypeInContext(self.context)) return .f32; - if (llvm_ty == c.LLVMDoubleTypeInContext(self.context)) return .f64; - if (llvm_ty == c.LLVMVoidTypeInContext(self.context)) return .void_type; + if (llvm_ty == self.i32Type()) return Type.s(32); + if (llvm_ty == self.i64Type()) return Type.s(64); + if (llvm_ty == self.f32Type()) return .f32; + if (llvm_ty == self.f64Type()) return .f64; + if (llvm_ty == self.voidType()) return .void_type; if (llvm_ty == self.getStringStructType()) return .string_type; if (self.any_struct_type != null and llvm_ty == self.any_struct_type.?) return .any_type; - if (llvm_ty == c.LLVMPointerTypeInContext(self.context, 0)) return .string_type; // raw ptr fallback (meta_type) + if (llvm_ty == self.ptrType()) return .string_type; // raw ptr fallback (meta_type) // Handle arbitrary-width integer types (e.g. i3, i7, i12) if (c.LLVMGetTypeKind(llvm_ty) == c.LLVMIntegerTypeKind) { const width = c.LLVMGetIntTypeWidth(llvm_ty); @@ -1899,17 +2025,14 @@ pub const CodeGen = struct { /// Prepare a return value: load structs/unions from alloca pointers, convert types. fn prepareReturnValue(self: *CodeGen, raw_val: c.LLVMValueRef, ret_type: Type) !c.LLVMValueRef { if (ret_type.isStruct()) { - const sname = self.type_aliases.get(ret_type.struct_type) orelse ret_type.struct_type; + const sname = self.resolveAlias(ret_type.struct_type); const info = try self.getStructInfo(sname); return c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval"); } else if (ret_type.isUnion()) { const uname = ret_type.union_type; - const resolved = self.type_aliases.get(uname) orelse uname; + const resolved = self.resolveAlias(uname); const info = try self.getTaggedEnumInfo(resolved); - return if (c.LLVMGetTypeKind(c.LLVMTypeOf(raw_val)) == c.LLVMPointerTypeKind) - c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval") - else - raw_val; + return self.loadIfPointer(raw_val, info.llvm_type, "retval"); } else { const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val)); return self.convertValue(raw_val, src_ty, ret_type); @@ -1922,7 +2045,7 @@ pub const CodeGen = struct { const ret_sx_type = self.resolveType(fd.return_type); const is_main = std.mem.eql(u8, llvm_name, "main"); const ret_llvm_type = if (is_main) - c.LLVMInt32TypeInContext(self.context) + self.i32Type() else self.typeToLLVM(ret_sx_type); @@ -1932,8 +2055,7 @@ pub const CodeGen = struct { const function = c.LLVMGetNamedFunction(self.module, name_z.ptr) orelse return self.emitErrorFmt("function '{s}' not found in LLVM module", .{llvm_name}); self.current_function = function; - const entry = c.LLVMAppendBasicBlockInContext(self.context, function, "entry"); - c.LLVMPositionBuilderAtEnd(self.builder, entry); + _ = self.appendBlock(function, "entry"); // Create allocas for parameters and store incoming values for (fd.params, 0..) |param, i| { @@ -1964,7 +2086,7 @@ pub const CodeGen = struct { try self.popScope(); // Check if last_val is void-typed (e.g. call to void-returning function) const effective_last_val: ?c.LLVMValueRef = if (last_val) |val| - (if (c.LLVMTypeOf(val) == c.LLVMVoidTypeInContext(self.context)) null else val) + (if (c.LLVMTypeOf(val) == self.voidType()) null else val) else null; @@ -2019,8 +2141,7 @@ pub const CodeGen = struct { const function = c.LLVMGetNamedFunction(self.module, name_z.ptr) orelse return self.emitErrorFmt("lambda '{s}' not found in LLVM module", .{name}); self.current_function = function; - const entry = c.LLVMAppendBasicBlockInContext(self.context, function, "entry"); - c.LLVMPositionBuilderAtEnd(self.builder, entry); + _ = self.appendBlock(function, "entry"); for (lambda.params, 0..) |param, i| { const sx_ty = self.resolveType(param.type_expr); @@ -2070,8 +2191,7 @@ pub const CodeGen = struct { const function = c.LLVMGetNamedFunction(self.module, name_z.ptr) orelse return self.emitErrorFmt("local function '{s}' not found", .{fd.name}); self.current_function = function; - const entry = c.LLVMAppendBasicBlockInContext(self.context, function, "entry"); - c.LLVMPositionBuilderAtEnd(self.builder, entry); + _ = self.appendBlock(function, "entry"); for (fd.params, 0..) |param, i| { const sx_ty = self.resolveType(param.type_expr); @@ -2131,8 +2251,7 @@ pub const CodeGen = struct { _ = c.LLVMBuildRetVoid(self.builder); } // Create a dead basic block for any subsequent instructions - const dead_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "after_ret"); - c.LLVMPositionBuilderAtEnd(self.builder, dead_bb); + _ = self.appendBlock(self.current_function, "after_ret"); return null; }, .defer_stmt => |ds| { @@ -2173,12 +2292,11 @@ pub const CodeGen = struct { if (meta_name) |raw_name| { const type_name = try self.allocator.dupeZ(u8, raw_name); const name_z = try self.allocator.dupeZ(u8, vd.name); - const ptr_ty = c.LLVMPointerTypeInContext(self.context, 0); + const ptr_ty = self.ptrType(); const alloca = self.buildEntryBlockAlloca(ptr_ty, name_z.ptr); const str_val = c.LLVMBuildGlobalStringPtr(self.builder, type_name.ptr, "type_name"); _ = c.LLVMBuildStore(self.builder, str_val, alloca); - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = .{ .meta_type = .{ .name = raw_name } } }); + try self.registerVariable(vd.name, alloca, .{ .meta_type = .{ .name = raw_name } }); return null; } } @@ -2210,23 +2328,21 @@ pub const CodeGen = struct { // Struct-typed variable if (sx_ty.isStruct()) { // Resolve type aliases (e.g. Vec3 -> Vec__3_f32) - const sname = self.type_aliases.get(sx_ty.struct_type) orelse sx_ty.struct_type; + const sname = self.resolveAlias(sx_ty.struct_type); sx_ty = .{ .struct_type = sname }; const info = try self.getStructInfo(sname); - const name_z = try self.allocator.dupeZ(u8, vd.name); - const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr); + const alloca = try self.buildNamedAlloca(info.llvm_type, vd.name); if (vd.value == null) { // Default-init: per-field defaults or zero try self.genStructDefaultInit(alloca, info); } else if (vd.value.?.data == .undef_literal) { // Undef-init: entire struct is undefined - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(info.llvm_type), alloca); + self.storeUndef(info.llvm_type, alloca); } else if (vd.value.?.data == .struct_literal) { // Struct literal codegen returns an alloca — use it directly instead const lit_alloca = try self.genStructLiteral(vd.value.?.data.struct_literal, sname); - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = lit_alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, lit_alloca, sx_ty); return null; } else if (vd.value.?.data == .call) { // Function call returning a struct — result is a value, store to alloca @@ -2238,72 +2354,56 @@ pub const CodeGen = struct { _ = c.LLVMBuildStore(self.builder, val, alloca); } - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, alloca, sx_ty); return null; } // Union-typed variable (tagged enum or C-style union) if (sx_ty.isUnion()) { - const uname = self.type_aliases.get(sx_ty.union_type) orelse sx_ty.union_type; + const uname = self.resolveAlias(sx_ty.union_type); sx_ty = .{ .union_type = uname }; // C-style (untagged) union if (self.union_types.get(uname)) |info| { - const name_z = try self.allocator.dupeZ(u8, vd.name); - const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr); + const alloca = try self.buildNamedAlloca(info.llvm_type, vd.name); if (vd.value == null) { - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(info.llvm_type), alloca); + self.storeNull(info.llvm_type, alloca); } else if (vd.value.?.data == .undef_literal) { - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(info.llvm_type), alloca); + self.storeUndef(info.llvm_type, alloca); } else { return self.emitErrorFmt("union '{s}' must be initialized with '---' or field assignment", .{uname}); } - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, alloca, sx_ty); return null; } // Tagged enum const info = try self.getTaggedEnumInfo(uname); - const name_z = try self.allocator.dupeZ(u8, vd.name); - const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr); + const alloca = try self.buildNamedAlloca(info.llvm_type, vd.name); if (vd.value == null) { // Zero-init: tag=0, payload zeroed - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(info.llvm_type), alloca); + self.storeNull(info.llvm_type, alloca); } else if (vd.value.?.data == .undef_literal) { - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(info.llvm_type), alloca); + self.storeUndef(info.llvm_type, alloca); } else if (vd.value.?.data == .enum_literal) { const el = vd.value.?.data.enum_literal; const lit_alloca = try self.genTaggedEnumLiteral(el, uname); - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = lit_alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, lit_alloca, sx_ty); return null; } else if (vd.value.?.data == .call) { // Call returning a union — could be enum construction (alloca) or function call (value) const result = try self.genExpr(vd.value.?); - if (c.LLVMGetTypeKind(c.LLVMTypeOf(result)) == c.LLVMPointerTypeKind) { - const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, result, "union_load"); - _ = c.LLVMBuildStore(self.builder, loaded, alloca); - } else { - _ = c.LLVMBuildStore(self.builder, result, alloca); - } + _ = c.LLVMBuildStore(self.builder, self.loadIfPointer(result, info.llvm_type, "union_load"), alloca); } else { // Other expression — try genExprAsType const result = try self.genExprAsType(vd.value.?, sx_ty); - if (c.LLVMGetTypeKind(c.LLVMTypeOf(result)) == c.LLVMPointerTypeKind) { - const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, result, "union_load"); - _ = c.LLVMBuildStore(self.builder, loaded, alloca); - } else { - _ = c.LLVMBuildStore(self.builder, result, alloca); - } + _ = c.LLVMBuildStore(self.builder, self.loadIfPointer(result, info.llvm_type, "union_load"), alloca); } - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, alloca, sx_ty); return null; } @@ -2311,55 +2411,44 @@ pub const CodeGen = struct { if (sx_ty.isArray()) { const arr_info = sx_ty.array_type; const llvm_arr_ty = self.typeToLLVM(sx_ty); - const arr_name_z = try self.allocator.dupeZ(u8, vd.name); - const arr_alloca = self.buildEntryBlockAlloca(llvm_arr_ty, arr_name_z.ptr); + const arr_alloca = try self.buildNamedAlloca(llvm_arr_ty, vd.name); if (vd.value == null) { - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(llvm_arr_ty), arr_alloca); + self.storeNull(llvm_arr_ty, arr_alloca); } else if (vd.value.?.data == .undef_literal) { - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(llvm_arr_ty), arr_alloca); + self.storeUndef(llvm_arr_ty, arr_alloca); } else if (vd.value.?.data == .array_literal) { const al = vd.value.?.data.array_literal; - const elem_sx_ty = Type.fromName(arr_info.element_name) orelse return self.emitErrorFmt("unknown array element type '{s}'", .{arr_info.element_name}); + const elem_sx_ty = try self.resolveElementType(arr_info.element_name, "array"); const elem_llvm_ty = self.typeToLLVM(elem_sx_ty); const len = @min(al.elements.len, arr_info.length); for (0..len) |i| { const val = try self.genExprAsType(al.elements[i], elem_sx_ty); - var indices = [_]c.LLVMValueRef{ - self.constInt32(0), - self.constInt32(@intCast(i)), - }; - const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "arr_elem"); + const gep = self.gepArrayElement(llvm_arr_ty, arr_alloca, self.constInt32(@intCast(i)), "arr_elem"); _ = c.LLVMBuildStore(self.builder, val, gep); } // Zero-init remaining elements for (len..arr_info.length) |i| { - var indices = [_]c.LLVMValueRef{ - self.constInt32(0), - self.constInt32(@intCast(i)), - }; - const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "arr_elem"); - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(elem_llvm_ty), gep); + const gep = self.gepArrayElement(llvm_arr_ty, arr_alloca, self.constInt32(@intCast(i)), "arr_elem"); + self.storeNull(elem_llvm_ty, gep); } } else { return self.emitErrorFmt("unsupported initializer for array variable '{s}'", .{vd.name}); } - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = arr_alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, arr_alloca, sx_ty); return null; } // Vector-typed variable if (sx_ty.isVector()) { const llvm_vec_ty = self.typeToLLVM(sx_ty); - const vec_name_z = try self.allocator.dupeZ(u8, vd.name); - const vec_alloca = self.buildEntryBlockAlloca(llvm_vec_ty, vec_name_z.ptr); + const vec_alloca = try self.buildNamedAlloca(llvm_vec_ty, vd.name); if (vd.value == null) { - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(llvm_vec_ty), vec_alloca); + self.storeNull(llvm_vec_ty, vec_alloca); } else if (vd.value.?.data == .undef_literal) { - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(llvm_vec_ty), vec_alloca); + self.storeUndef(llvm_vec_ty, vec_alloca); } else if (vd.value.?.data == .array_literal) { const vec_val = try self.genVectorLiteral(vd.value.?.data.array_literal, sx_ty); _ = c.LLVMBuildStore(self.builder, vec_val, vec_alloca); @@ -2369,21 +2458,19 @@ pub const CodeGen = struct { _ = c.LLVMBuildStore(self.builder, val, vec_alloca); } - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = vec_alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, vec_alloca, sx_ty); return null; } // Function pointer typed variable if (sx_ty.isFunctionType()) { - const llvm_ty = c.LLVMPointerTypeInContext(self.context, 0); - const name_z = try self.allocator.dupeZ(u8, vd.name); - const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr); + const llvm_ty = self.ptrType(); + const alloca = try self.buildNamedAlloca(llvm_ty, vd.name); if (vd.value == null) { - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(llvm_ty), alloca); + self.storeNull(llvm_ty, alloca); } else if (vd.value.?.data == .undef_literal) { - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(llvm_ty), alloca); + self.storeUndef(llvm_ty, alloca); } else if (vd.value.?.data == .unary_op and vd.value.?.data.unary_op.op == .xx) { // xx cast: e.g. xx SDL_GL_GetProcAddress("glClear") const inner = vd.value.?.data.unary_op.operand; @@ -2397,8 +2484,7 @@ pub const CodeGen = struct { _ = c.LLVMBuildStore(self.builder, val, alloca); } - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, alloca, sx_ty); return null; } @@ -2409,15 +2495,14 @@ pub const CodeGen = struct { // Non-struct types const llvm_ty = self.typeToLLVM(sx_ty); - const name_z = try self.allocator.dupeZ(u8, vd.name); - const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr); + const alloca = try self.buildNamedAlloca(llvm_ty, vd.name); if (vd.value == null) { // Default-init: zero - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(llvm_ty), alloca); + self.storeNull(llvm_ty, alloca); } else if (vd.value.?.data == .undef_literal) { // Undef-init - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(llvm_ty), alloca); + self.storeUndef(llvm_ty, alloca); } else { const val = vd.value.?; const enum_name: ?[]const u8 = if (sx_ty.isEnum()) sx_ty.enum_type else null; @@ -2430,8 +2515,7 @@ pub const CodeGen = struct { _ = c.LLVMBuildStore(self.builder, init_val, alloca); } - try self.saveShadowed(vd.name); - try self.named_values.put(vd.name, .{ .ptr = alloca, .ty = sx_ty }); + try self.registerVariable(vd.name, alloca, sx_ty); return null; } @@ -2445,7 +2529,7 @@ pub const CodeGen = struct { const default_node = info.field_defaults[fi].?; if (default_node.data == .undef_literal) { // Field default is --- → store undef - _ = c.LLVMBuildStore(self.builder, c.LLVMGetUndef(ft_llvm), gep); + self.storeUndef(ft_llvm, gep); } else { // Field has expression default → evaluate and convert const val = try self.genExprAsType(default_node, ft); @@ -2459,7 +2543,7 @@ pub const CodeGen = struct { } } else { // No default → zero - _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(ft_llvm), gep); + self.storeNull(ft_llvm, gep); } } } @@ -2483,16 +2567,14 @@ pub const CodeGen = struct { // Enum-typed constant: delegate to genExprAsType which handles enum_literal if (sx_ty.isUnion()) { const val = try self.genExprAsType(cd.value, sx_ty); - try self.saveShadowed(cd.name); - try self.named_values.put(cd.name, .{ .ptr = val, .ty = sx_ty }); + try self.registerVariable(cd.name, val, sx_ty); return null; } // Function pointer typed constant if (sx_ty.isFunctionType()) { - const llvm_ty = c.LLVMPointerTypeInContext(self.context, 0); - const name_z = try self.allocator.dupeZ(u8, cd.name); - const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr); + const llvm_ty = self.ptrType(); + const alloca = try self.buildNamedAlloca(llvm_ty, cd.name); if (cd.value.data == .unary_op and cd.value.data.unary_op.op == .xx) { const inner = cd.value.data.unary_op.operand; const val = try self.genExpr(inner); @@ -2503,8 +2585,7 @@ pub const CodeGen = struct { const val = try self.genExpr(cd.value); _ = c.LLVMBuildStore(self.builder, val, alloca); } - try self.saveShadowed(cd.name); - try self.named_values.put(cd.name, .{ .ptr = alloca, .ty = sx_ty }); + try self.registerVariable(cd.name, alloca, sx_ty); return null; } @@ -2515,11 +2596,9 @@ pub const CodeGen = struct { try self.genExpr(cd.value); const llvm_ty = self.typeToLLVM(sx_ty); - const name_z = try self.allocator.dupeZ(u8, cd.name); - const alloca = self.buildEntryBlockAlloca(llvm_ty, name_z.ptr); + const alloca = try self.buildNamedAlloca(llvm_ty, cd.name); _ = c.LLVMBuildStore(self.builder, init_val, alloca); - try self.saveShadowed(cd.name); - try self.named_values.put(cd.name, .{ .ptr = alloca, .ty = sx_ty }); + try self.registerVariable(cd.name, alloca, sx_ty); return null; } @@ -2534,6 +2613,16 @@ pub const CodeGen = struct { }; } + fn storeOrCompound(self: *CodeGen, op: ast.Assignment.Op, ptr: c.LLVMValueRef, rhs: c.LLVMValueRef, field_ty: Type, name: [*c]const u8) void { + if (op == .assign) { + _ = c.LLVMBuildStore(self.builder, rhs, ptr); + } else { + const llvm_ty = self.typeToLLVM(field_ty); + const cur = c.LLVMBuildLoad2(self.builder, llvm_ty, ptr, name); + _ = c.LLVMBuildStore(self.builder, self.genCompoundOp(op, cur, rhs, field_ty), ptr); + } + } + fn genAssignment(self: *CodeGen, asgn: ast.Assignment) !c.LLVMValueRef { // Field assignment: expr.field = value; if (asgn.target.data == .field_access) { @@ -2607,11 +2696,7 @@ pub const CodeGen = struct { if (self.tagged_enum_types.get(entry.ty.union_type)) |info| { const new_val = try self.genExprAsType(asgn.value, entry.ty); // genExprAsType returns alloca for enum literals, loaded value for calls - const store_val = if (c.LLVMGetTypeKind(c.LLVMTypeOf(new_val)) == c.LLVMPointerTypeKind) - c.LLVMBuildLoad2(self.builder, info.llvm_type, new_val, "union_load") - else - new_val; - _ = c.LLVMBuildStore(self.builder, store_val, entry.ptr); + _ = c.LLVMBuildStore(self.builder, self.loadIfPointer(new_val, info.llvm_type, "union_load"), entry.ptr); return null; } // C-style union: full assignment not supported, use field assignment @@ -2644,19 +2729,13 @@ pub const CodeGen = struct { if (pointee_ty.isStruct()) { const sname = pointee_ty.struct_type; const info = try self.getStructInfo(sname); - const fi = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); + const fi = try self.findFieldIndex(info.field_names, fa.field, sname); const field_ty = info.field_types[fi]; const loaded_ptr = c.LLVMBuildLoad2(self.builder, - c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); + self.ptrType(), entry.ptr, "ptr_load"); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(fi), "pfield_ptr"); const rhs = try self.genExprAsType(asgn.value, field_ty); - if (asgn.op == .assign) { - _ = c.LLVMBuildStore(self.builder, rhs, gep); - } else { - const field_llvm_ty = self.typeToLLVM(field_ty); - const cur = c.LLVMBuildLoad2(self.builder, field_llvm_ty, gep, "pcur"); - _ = c.LLVMBuildStore(self.builder, self.genCompoundOp(asgn.op, cur, rhs, field_ty), gep); - } + self.storeOrCompound(asgn.op, gep, rhs, field_ty, "pcur"); return null; } return self.emitError("field assignment through pointer requires a struct pointee"); @@ -2669,28 +2748,15 @@ pub const CodeGen = struct { if (self.findNameIndex(info.field_names, fa.field)) |fidx| { const field_ty = info.field_types[fidx]; const rhs = try self.genExprAsType(asgn.value, field_ty); - if (asgn.op == .assign) { - _ = c.LLVMBuildStore(self.builder, rhs, entry.ptr); - } else { - const field_llvm_ty = self.typeToLLVM(field_ty); - const cur = c.LLVMBuildLoad2(self.builder, field_llvm_ty, entry.ptr, "ucur"); - _ = c.LLVMBuildStore(self.builder, self.genCompoundOp(asgn.op, cur, rhs, field_ty), entry.ptr); - } + self.storeOrCompound(asgn.op, entry.ptr, rhs, field_ty, "ucur"); return null; } // Check promoted fields from anonymous structs if (info.promoted_fields.get(fa.field)) |pf| { - const sinfo = self.struct_types.get(pf.struct_name) orelse - return self.emitErrorFmt("unknown promoted struct '{s}'", .{pf.struct_name}); + const sinfo = try self.getStructInfo(pf.struct_name); const gep = c.LLVMBuildStructGEP2(self.builder, sinfo.llvm_type, entry.ptr, @intCast(pf.field_index), "promoted_ptr"); const rhs = try self.genExprAsType(asgn.value, pf.field_type); - if (asgn.op == .assign) { - _ = c.LLVMBuildStore(self.builder, rhs, gep); - } else { - const field_llvm_ty = self.typeToLLVM(pf.field_type); - const cur = c.LLVMBuildLoad2(self.builder, field_llvm_ty, gep, "ucur"); - _ = c.LLVMBuildStore(self.builder, self.genCompoundOp(asgn.op, cur, rhs, pf.field_type), gep); - } + self.storeOrCompound(asgn.op, gep, rhs, pf.field_type, "ucur"); return null; } return self.emitErrorFmt("no field '{s}' in union '{s}'", .{ fa.field, uname }); @@ -2702,21 +2768,14 @@ pub const CodeGen = struct { const sname = entry.ty.struct_type; const info = try self.getStructInfo(sname); - const fi = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); + const fi = try self.findFieldIndex(info.field_names, fa.field, sname); const field_ty = info.field_types[fi]; const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(fi), "fassign"); // Generate RHS and convert to field type const rhs = try self.genExprAsType(asgn.value, field_ty); - - if (asgn.op == .assign) { - _ = c.LLVMBuildStore(self.builder, rhs, gep); - } else { - const field_llvm_ty = self.typeToLLVM(field_ty); - const cur = c.LLVMBuildLoad2(self.builder, field_llvm_ty, gep, "fcur"); - _ = c.LLVMBuildStore(self.builder, self.genCompoundOp(asgn.op, cur, rhs, field_ty), gep); - } + self.storeOrCompound(asgn.op, gep, rhs, field_ty, "fcur"); return null; } @@ -2729,9 +2788,8 @@ pub const CodeGen = struct { const ptr = c.LLVMBuildExtractValue(self.builder, str_val, 0, "str_ptr"); const idx = try self.genExpr(ie.index); const val = try self.genExpr(asgn.value); - const i8_type = c.LLVMInt8TypeInContext(self.context); - var gep_indices = [_]c.LLVMValueRef{idx}; - const gep_ptr = c.LLVMBuildGEP2(self.builder, i8_type, ptr, &gep_indices, 1, "stridx"); + const i8_type = self.i8Type(); + const gep_ptr = self.gepPointerElement(i8_type, ptr, idx, "stridx"); const byte_val = c.LLVMBuildTrunc(self.builder, val, i8_type, "trunc_byte"); _ = c.LLVMBuildStore(self.builder, byte_val, gep_ptr); return null; @@ -2741,9 +2799,7 @@ pub const CodeGen = struct { if (self.named_values.get(ie.object.data.identifier.name)) |entry| { const idx = try self.genExpr(ie.index); const val = try self.genExpr(asgn.value); - const zero = self.constInt32(0); - var indices = [_]c.LLVMValueRef{ zero, idx }; - const gep_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "arridx"); + const gep_ptr = self.gepArrayElement(self.typeToLLVM(obj_ty), entry.ptr, idx, "arridx"); _ = c.LLVMBuildStore(self.builder, val, gep_ptr); return null; } @@ -2753,9 +2809,7 @@ pub const CodeGen = struct { const field_ptr = try self.genAddressOf(ie.object); const idx = try self.genExpr(ie.index); const val = try self.genExpr(asgn.value); - const zero = self.constInt32(0); - var indices = [_]c.LLVMValueRef{ zero, idx }; - const gep_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx"); + const gep_ptr = self.gepArrayElement(self.typeToLLVM(obj_ty), field_ptr, idx, "field_arridx"); _ = c.LLVMBuildStore(self.builder, val, gep_ptr); return null; } @@ -2776,8 +2830,7 @@ pub const CodeGen = struct { const ptr = c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_ptr"); const idx = try self.genExpr(ie.index); const val = try self.genExpr(asgn.value); - var gep_indices = [_]c.LLVMValueRef{idx}; - const gep_ptr = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr, &gep_indices, 1, "sliceidx"); + const gep_ptr = self.gepPointerElement(elem_llvm_ty, ptr, idx, "sliceidx"); _ = c.LLVMBuildStore(self.builder, val, gep_ptr); return null; } @@ -2788,8 +2841,7 @@ pub const CodeGen = struct { const ptr_val = try self.genExpr(ie.object); const idx = try self.genExpr(ie.index); const val = try self.genExprAsType(asgn.value, elem_ty); - var gep_indices = [_]c.LLVMValueRef{idx}; - const gep_ptr = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr_val, &gep_indices, 1, "mptridx"); + const gep_ptr = self.gepPointerElement(elem_llvm_ty, ptr_val, idx, "mptridx"); _ = c.LLVMBuildStore(self.builder, val, gep_ptr); return null; } @@ -2800,15 +2852,15 @@ pub const CodeGen = struct { self.current_span = node.span; switch (node.data) { .int_literal => |lit| { - const i64_type = c.LLVMInt64TypeInContext(self.context); + const i64_type = self.i64Type(); return c.LLVMConstInt(i64_type, @bitCast(@as(i64, lit.value)), 0); }, .float_literal => |lit| { - const f32_type = c.LLVMFloatTypeInContext(self.context); + const f32_type = self.f32Type(); return c.LLVMConstReal(f32_type, lit.value); }, .bool_literal => |lit| { - const i1_type = c.LLVMInt1TypeInContext(self.context); + const i1_type = self.i1Type(); return c.LLVMConstInt(i1_type, if (lit.value) 1 else 0, 0); }, .string_literal => |lit| { @@ -2861,7 +2913,7 @@ pub const CodeGen = struct { // Tagged enum comparison: compare tags only if (result_type.isUnion() and (binop.op == .eq or binop.op == .neq)) { const uname = result_type.union_type; - const resolved = self.type_aliases.get(uname) orelse uname; + const resolved = self.resolveAlias(uname); const info = self.tagged_enum_types.get(resolved) orelse return self.emitError("unknown tagged enum type"); const tag_ty = self.getEnumLLVMType(resolved); @@ -2869,12 +2921,8 @@ pub const CodeGen = struct { var rhs_val = try self.genExprAsType(binop.rhs, result_type); // If either side is a pointer (alloca from genTaggedEnumLiteral), load it - if (c.LLVMGetTypeKind(c.LLVMTypeOf(lhs_val)) == c.LLVMPointerTypeKind) { - lhs_val = c.LLVMBuildLoad2(self.builder, info.llvm_type, lhs_val, "union_load_l"); - } - if (c.LLVMGetTypeKind(c.LLVMTypeOf(rhs_val)) == c.LLVMPointerTypeKind) { - rhs_val = c.LLVMBuildLoad2(self.builder, info.llvm_type, rhs_val, "union_load_r"); - } + lhs_val = self.loadIfPointer(lhs_val, info.llvm_type, "union_load_l"); + rhs_val = self.loadIfPointer(rhs_val, info.llvm_type, "union_load_r"); // Extract tags (field 0) and compare const lhs_tag = c.LLVMBuildExtractValue(self.builder, lhs_val, 0, "lhs_tag"); @@ -2973,8 +3021,7 @@ pub const CodeGen = struct { .break_expr => { if (self.loop_break_bb) |break_bb| { _ = c.LLVMBuildBr(self.builder, break_bb); - const dead_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "after_break"); - c.LLVMPositionBuilderAtEnd(self.builder, dead_bb); + _ = self.appendBlock(self.current_function, "after_break"); return null; } return self.emitError("'break' outside of loop"); @@ -2982,8 +3029,7 @@ pub const CodeGen = struct { .continue_expr => { if (self.loop_continue_bb) |continue_bb| { _ = c.LLVMBuildBr(self.builder, continue_bb); - const dead_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "after_continue"); - c.LLVMPositionBuilderAtEnd(self.builder, dead_bb); + _ = self.appendBlock(self.current_function, "after_continue"); return null; } return self.emitError("'continue' outside of loop"); @@ -3016,8 +3062,7 @@ pub const CodeGen = struct { try self.emitAllDefers(); _ = c.LLVMBuildRetVoid(self.builder); } - const dead_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "after_ret"); - c.LLVMPositionBuilderAtEnd(self.builder, dead_bb); + _ = self.appendBlock(self.current_function, "after_ret"); return null; }, .deref_expr => |de| { @@ -3025,12 +3070,12 @@ pub const CodeGen = struct { const ptr_ty = self.inferType(de.operand); if (ptr_ty.isPointer()) { const pointee_ty = self.resolveTypeFromName(ptr_ty.pointer_type.pointee_name) orelse return self.emitError("unknown pointee type"); - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(pointee_ty), ptr_val, "deref"); + return self.loadTyped(pointee_ty, ptr_val, "deref"); } return self.emitError("dereference requires a pointer type"); }, .null_literal => { - return c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)); + return c.LLVMConstNull(self.ptrType()); }, .comptime_expr => |ct| { return self.genExpr(ct.expr); @@ -3056,9 +3101,7 @@ pub const CodeGen = struct { if (obj_ty.isArray()) { if (ie.object.data == .identifier) { if (self.named_values.get(ie.object.data.identifier.name)) |entry| { - const zero = self.constInt32(0); - var indices = [_]c.LLVMValueRef{ zero, idx }; - return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "addr_elem"); + return self.gepArrayElement(self.typeToLLVM(obj_ty), entry.ptr, idx, "addr_elem"); } } } @@ -3069,8 +3112,7 @@ pub const CodeGen = struct { obj_ty.sliceElementType() orelse return self.emitError("unknown slice element type") else Type.u(8); - var gep_indices = [_]c.LLVMValueRef{idx}; - return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(elem_ty), ptr, &gep_indices, 1, "addr_elem"); + return self.gepPointerElement(self.typeToLLVM(elem_ty), ptr, idx, "addr_elem"); } } // &s.field — return GEP pointer to the struct field @@ -3081,7 +3123,7 @@ pub const CodeGen = struct { if (entry.ty.isStruct()) { const sname = entry.ty.struct_type; const info = try self.getStructInfo(sname); - const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); + const idx = try self.findFieldIndex(info.field_names, fa.field, sname); return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "addr_field"); } // &u.field where u is a C-style union — all fields at offset 0 @@ -3091,8 +3133,7 @@ pub const CodeGen = struct { return entry.ptr; } if (info.promoted_fields.get(fa.field)) |pf| { - const sinfo = self.struct_types.get(pf.struct_name) orelse - return self.emitErrorFmt("unknown promoted struct '{s}'", .{pf.struct_name}); + const sinfo = try self.getStructInfo(pf.struct_name); return c.LLVMBuildStructGEP2(self.builder, sinfo.llvm_type, entry.ptr, @intCast(pf.field_index), "addr_promoted"); } } @@ -3102,8 +3143,8 @@ pub const CodeGen = struct { const pointee_name = entry.ty.pointer_type.pointee_name; if (self.struct_types.get(pointee_name)) |info| { const loaded_ptr = c.LLVMBuildLoad2(self.builder, - c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); - const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, pointee_name }); + self.ptrType(), entry.ptr, "ptr_load"); + const idx = try self.findFieldIndex(info.field_names, fa.field, pointee_name); return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "addr_pfield"); } } @@ -3149,14 +3190,13 @@ pub const CodeGen = struct { fn buildUnionFields(self: *CodeGen, name: []const u8, variant_type_nodes: []const ?*Node) !UnionBuildResult { var variant_sx_types = std.ArrayList(Type).empty; var max_payload_size: u64 = 0; - const data_layout = c.LLVMGetModuleDataLayout(self.module); for (variant_type_nodes) |vt| { if (vt) |type_node| { const sx_ty = self.resolveType(type_node); try variant_sx_types.append(self.allocator, sx_ty); const llvm_ty = self.typeToLLVM(sx_ty); - const size = c.LLVMStoreSizeOfType(data_layout, llvm_ty); + const size = self.getTypeSize(llvm_ty); if (size > max_payload_size) max_payload_size = size; } else { try variant_sx_types.append(self.allocator, .void_type); @@ -3166,7 +3206,7 @@ pub const CodeGen = struct { const name_z = try self.allocator.dupeZ(u8, name); const union_ty = c.LLVMStructCreateNamed(self.context, name_z.ptr); const tag_ty = self.getEnumLLVMType(name); - const i8_ty = c.LLVMInt8TypeInContext(self.context); + const i8_ty = self.i8Type(); const payload_array_ty = c.LLVMArrayType2(i8_ty, max_payload_size); var union_fields = [2]c.LLVMTypeRef{ tag_ty, payload_array_ty }; @@ -3354,7 +3394,7 @@ pub const CodeGen = struct { // If it resolves to a primitive type, it's not a layout struct if (Type.fromName(name) != null) return null; // Check type aliases - const resolved = self.type_aliases.get(name) orelse name; + const resolved = self.resolveAlias(name); if (Type.fromName(resolved) != null) return null; // Must be a registered struct if (self.struct_types.contains(resolved)) { @@ -3416,8 +3456,7 @@ pub const CodeGen = struct { ); }; const elem_llvm = self.typeToLLVM(elem_ty); - const data_layout = c.LLVMGetModuleDataLayout(self.module); - break :blk c.LLVMStoreSizeOfType(data_layout, elem_llvm) * info.length; + break :blk self.getTypeSize(elem_llvm) * info.length; }, else => return self.emitErrorFmt( "enum '{s}': layout field 'payload' must be an array type (e.g. [30]u32), got '{s}'", @@ -3452,7 +3491,7 @@ pub const CodeGen = struct { } // LLVM type: byte array sized to the largest field - const byte_ty = c.LLVMInt8TypeInContext(self.context); + const byte_ty = self.i8Type(); const llvm_type = c.LLVMArrayType(byte_ty, @intCast(max_size)); const resolved_field_types = try field_sx_types.toOwnedSlice(self.allocator); @@ -3493,7 +3532,7 @@ pub const CodeGen = struct { const uname = expected_union_name orelse (if (self.current_return_type.isUnion()) self.current_return_type.union_type else null) orelse return self.emitError("cannot infer enum type for literal"); - const resolved_name = self.type_aliases.get(uname) orelse uname; + const resolved_name = self.resolveAlias(uname); const info = try self.getTaggedEnumInfo(resolved_name); // Find variant index @@ -3520,9 +3559,7 @@ pub const CodeGen = struct { const variant_ty = info.variant_types[idx]; if (variant_ty != .void_type) { const payload_val = try self.genExprAsType(payload_node, variant_ty); - const payload_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, info.payload_field_index, "payload"); - // genExprAsType returns a loaded value for all types (including structs) - _ = c.LLVMBuildStore(self.builder, payload_val, payload_gep); + self.storeStructField(info.llvm_type, alloca, info.payload_field_index, payload_val); } } @@ -3538,7 +3575,7 @@ pub const CodeGen = struct { break :blk expected_struct_name orelse return self.emitError("cannot infer struct type for literal"); }; // Resolve type aliases (e.g. Vec3 -> Vec__3_f32) - const sname = self.type_aliases.get(raw_name) orelse raw_name; + const sname = self.resolveAlias(raw_name); const info = try self.getStructInfo(sname); // Alloca the struct and default-init all fields (zero or declared defaults) @@ -3561,26 +3598,23 @@ pub const CodeGen = struct { const fname = fi.name orelse { // Positional field mixed with named — treat as identifier shorthand if (fi.value.data == .identifier) { - const idx = self.findNameIndex(info.field_names, fi.value.data.identifier.name) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fi.value.data.identifier.name, sname }); + const idx = try self.findFieldIndex(info.field_names, fi.value.data.identifier.name, sname); const val = try self.genExprAsType(fi.value, info.field_types[idx]); - const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field"); - _ = c.LLVMBuildStore(self.builder, val, gep); + self.storeStructField(info.llvm_type, alloca, @intCast(idx), val); continue; } return self.emitError("mixed positional and named fields in struct literal"); }; - const idx = self.findNameIndex(info.field_names, fname) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fname, sname }); + const idx = try self.findFieldIndex(info.field_names, fname, sname); const val = try self.genExprAsType(fi.value, info.field_types[idx]); - const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field"); - _ = c.LLVMBuildStore(self.builder, val, gep); + self.storeStructField(info.llvm_type, alloca, @intCast(idx), val); } } else { // Positional mode: assign in order for (sl.field_inits, 0..) |fi, i| { if (i >= info.field_names.len) return self.emitErrorFmt("too many fields in struct literal (expected {d})", .{info.field_names.len}); const val = try self.genExprAsType(fi.value, info.field_types[i]); - const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(i), "field"); - _ = c.LLVMBuildStore(self.builder, val, gep); + self.storeStructField(info.llvm_type, alloca, @intCast(i), val); } } @@ -3599,18 +3633,14 @@ pub const CodeGen = struct { break :blk .{ .array_type = .{ .element_name = elem_name, .length = @intCast(al.elements.len) } }; }; const arr_info = arr_ty.array_type; - const elem_sx_ty = Type.fromName(arr_info.element_name) orelse return self.emitErrorFmt("unknown array element type '{s}'", .{arr_info.element_name}); + const elem_sx_ty = try self.resolveElementType(arr_info.element_name, "array"); const llvm_arr_ty = self.typeToLLVM(arr_ty); const alloca = self.buildEntryBlockAlloca(llvm_arr_ty, "arr"); const len = @min(al.elements.len, arr_info.length); for (0..len) |i| { const val = try self.genExprAsType(al.elements[i], elem_sx_ty); - var indices = [_]c.LLVMValueRef{ - self.constInt32(0), - self.constInt32(@intCast(i)), - }; - const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, alloca, &indices, 2, "arr_elem"); + const gep = self.gepArrayElement(llvm_arr_ty, alloca, self.constInt32(@intCast(i)), "arr_elem"); _ = c.LLVMBuildStore(self.builder, val, gep); } return alloca; @@ -3618,7 +3648,7 @@ pub const CodeGen = struct { fn genSliceLiteral(self: *CodeGen, al: ast.ArrayLiteral, slice_ty: Type) !c.LLVMValueRef { const elem_name = slice_ty.slice_type.element_name; - const elem_sx_ty = Type.fromName(elem_name) orelse return self.emitErrorFmt("unknown slice element type '{s}'", .{elem_name}); + const elem_sx_ty = try self.resolveElementType(elem_name, "slice"); const n: u32 = @intCast(al.elements.len); // Create backing array [N]elem on the stack @@ -3629,30 +3659,18 @@ pub const CodeGen = struct { // Fill elements for (0..n) |i| { const val = try self.genExprAsType(al.elements[i], elem_sx_ty); - var indices = [_]c.LLVMValueRef{ - self.constInt32(0), - self.constInt32(@intCast(i)), - }; - const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "slice_elem"); + const gep = self.gepArrayElement(llvm_arr_ty, arr_alloca, self.constInt32(@intCast(i)), "slice_elem"); _ = c.LLVMBuildStore(self.builder, val, gep); } // Build slice {ptr, len} - const i64_ty = c.LLVMInt64TypeInContext(self.context); - const zero = c.LLVMConstInt(i64_ty, 0, 0); - var gep_indices = [_]c.LLVMValueRef{ zero, zero }; - const elem_ptr = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &gep_indices, 2, "slice_data"); - const slice_llvm_ty = self.getStringStructType(); - var slice_val = c.LLVMGetUndef(slice_llvm_ty); - slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr"); - const len_val = c.LLVMConstInt(i64_ty, n, 0); - slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len"); - return slice_val; + const elem_ptr = self.arrayDecayToPointer(llvm_arr_ty, arr_alloca, "slice_data"); + return self.buildFatPointer(self.getStringStructType(), elem_ptr, self.constInt64(n)); } fn genVectorLiteral(self: *CodeGen, al: ast.ArrayLiteral, vec_ty: Type) !c.LLVMValueRef { const vec_info = vec_ty.vector_type; - const elem_sx_ty = Type.fromName(vec_info.element_name) orelse return self.emitErrorFmt("unknown vector element type '{s}'", .{vec_info.element_name}); + const elem_sx_ty = try self.resolveElementType(vec_info.element_name, "vector"); const llvm_vec_ty = self.typeToLLVM(vec_ty); var vec_val = c.LLVMGetUndef(llvm_vec_ty); @@ -3673,7 +3691,7 @@ pub const CodeGen = struct { const zero = self.constInt32(0); vec = c.LLVMBuildInsertElement(self.builder, vec, scalar, zero, "splat_ins"); // Shuffle with zeroinitializer mask to broadcast element 0 to all lanes - const mask_ty = c.LLVMVectorType(c.LLVMInt32TypeInContext(self.context), vec_info.length); + const mask_ty = c.LLVMVectorType(self.i32Type(), vec_info.length); const mask = c.LLVMConstNull(mask_ty); return c.LLVMBuildShuffleVector(self.builder, vec, c.LLVMGetUndef(llvm_vec_ty), mask, "splat"); } @@ -3732,9 +3750,8 @@ pub const CodeGen = struct { if (sl.type_expr) |te| { if (te.data == .enum_literal) { const variant_name = te.data.enum_literal.name; - const uname = self.type_aliases.get(target_ty.union_type) orelse target_ty.union_type; - const info = self.tagged_enum_types.get(uname) orelse - return self.emitErrorFmt("unknown enum type '{s}'", .{uname}); + const uname = self.resolveAlias(target_ty.union_type); + const info = try self.getTaggedEnumInfo(uname); // Find variant index var variant_idx: ?u32 = null; @@ -3752,8 +3769,7 @@ pub const CodeGen = struct { // Alloca union, store tag const alloca = self.buildEntryBlockAlloca(info.llvm_type, "union_lit"); const tag_llvm_ty = self.getEnumLLVMType(uname); - const tag_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, 0, "tag"); - _ = c.LLVMBuildStore(self.builder, c.LLVMConstInt(tag_llvm_ty, idx, 0), tag_gep); + self.storeStructField(info.llvm_type, alloca, 0, c.LLVMConstInt(tag_llvm_ty, idx, 0)); // Store struct payload if (variant_ty != .void_type) { @@ -3779,7 +3795,7 @@ pub const CodeGen = struct { if (node.data == .struct_literal and target_ty.isStruct()) { const alloca = try self.genStructLiteral(node.data.struct_literal, target_ty.struct_type); // genStructLiteral returns an alloca pointer — load the value for by-value passing - const sname = self.type_aliases.get(target_ty.struct_type) orelse target_ty.struct_type; + const sname = self.resolveAlias(target_ty.struct_type); if (self.struct_types.get(sname)) |si| { return c.LLVMBuildLoad2(self.builder, si.llvm_type, alloca, "struct_val"); } @@ -3820,17 +3836,9 @@ pub const CodeGen = struct { break :blk try self.genExpr(node); }; // GEP to get pointer to first element - const i64_ty = c.LLVMInt64TypeInContext(self.context); - const zero = c.LLVMConstInt(i64_ty, 0, 0); - var indices = [_]c.LLVMValueRef{ zero, zero }; - const elem_ptr = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(src_ty), arr_alloca, &indices, 2, "arr_data"); + const elem_ptr = self.arrayDecayToPointer(self.typeToLLVM(src_ty), arr_alloca, "arr_data"); // Build slice struct {ptr, len} - const slice_ty = self.getStringStructType(); - var slice_val = c.LLVMGetUndef(slice_ty); - slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, elem_ptr, 0, "slice_ptr"); - const len_val = c.LLVMConstInt(i64_ty, arr_info.length, 0); - slice_val = c.LLVMBuildInsertValue(self.builder, slice_val, len_val, 1, "slice_len"); - return slice_val; + return self.buildFatPointer(self.getStringStructType(), elem_ptr, self.constInt64(arr_info.length)); } } @@ -3851,10 +3859,7 @@ pub const CodeGen = struct { } break :blk try self.genExpr(node); }; - const i64_ty = c.LLVMInt64TypeInContext(self.context); - const zero = c.LLVMConstInt(i64_ty, 0, 0); - var indices = [_]c.LLVMValueRef{ zero, zero }; - return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(src_ty), arr_alloca, &indices, 2, "arr_decay"); + return self.arrayDecayToPointer(self.typeToLLVM(src_ty), arr_alloca, "arr_decay"); } } } @@ -3903,7 +3908,7 @@ pub const CodeGen = struct { if (src_ty.isStruct() and target_ty.isStruct()) { if (c.LLVMGetTypeKind(c.LLVMTypeOf(val)) == c.LLVMPointerTypeKind) { const info = self.struct_types.get(src_ty.struct_type) orelse - self.struct_types.get(self.type_aliases.get(src_ty.struct_type) orelse src_ty.struct_type); + self.struct_types.get(self.resolveAlias(src_ty.struct_type)); if (info) |si| { val = c.LLVMBuildLoad2(self.builder, si.llvm_type, val, "struct_load"); } @@ -3958,25 +3963,23 @@ pub const CodeGen = struct { return i64_val; } if (target_ty == .boolean) { - return c.LLVMBuildTrunc(self.builder, i64_val, c.LLVMInt1TypeInContext(self.context), "any_to_bool"); + return c.LLVMBuildTrunc(self.builder, i64_val, self.i1Type(), "any_to_bool"); } if (target_ty == .f64) { - return c.LLVMBuildBitCast(self.builder, i64_val, c.LLVMDoubleTypeInContext(self.context), "any_to_f64"); + return c.LLVMBuildBitCast(self.builder, i64_val, self.f64Type(), "any_to_f64"); } if (target_ty == .f32) { - const as_f64 = c.LLVMBuildBitCast(self.builder, i64_val, c.LLVMDoubleTypeInContext(self.context), "any_f64_tmp"); - return c.LLVMBuildFPTrunc(self.builder, as_f64, c.LLVMFloatTypeInContext(self.context), "any_to_f32"); + const as_f64 = c.LLVMBuildBitCast(self.builder, i64_val, self.f64Type(), "any_f64_tmp"); + return c.LLVMBuildFPTrunc(self.builder, as_f64, self.f32Type(), "any_to_f32"); } if (target_ty == .string_type) { // i64 is a pointer to {ptr, i32} on the stack - const ptr = c.LLVMBuildIntToPtr(self.builder, i64_val, c.LLVMPointerTypeInContext(self.context, 0), "any_str_ptr"); - return c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_str"); + return self.loadFromI64Ptr(i64_val, self.getStringStructType(), "any_to_str"); } if (target_ty.isStruct()) { const sname = target_ty.struct_type; if (self.struct_types.get(sname)) |info| { - const ptr = c.LLVMBuildIntToPtr(self.builder, i64_val, c.LLVMPointerTypeInContext(self.context, 0), "any_struct_ptr"); - return c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_struct"); + return self.loadFromI64Ptr(i64_val, info.llvm_type, "any_to_struct"); } } if (target_ty.isEnum()) { @@ -3988,12 +3991,11 @@ pub const CodeGen = struct { if (target_ty.isUnion()) { const uname = target_ty.union_type; if (self.tagged_enum_types.get(uname)) |info| { - const ptr = c.LLVMBuildIntToPtr(self.builder, i64_val, c.LLVMPointerTypeInContext(self.context, 0), "any_union_ptr"); - return c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_union"); + return self.loadFromI64Ptr(i64_val, info.llvm_type, "any_to_union"); } } if (target_ty.isPointer() or target_ty.isManyPointer()) { - return c.LLVMBuildIntToPtr(self.builder, i64_val, c.LLVMPointerTypeInContext(self.context, 0), "any_to_ptr"); + return c.LLVMBuildIntToPtr(self.builder, i64_val, self.ptrType(), "any_to_ptr"); } return i64_val; } @@ -4027,7 +4029,7 @@ pub const CodeGen = struct { // Pointer → int: PtrToInt if ((src_ty.isPointer() or src_ty.isManyPointer()) and target_ty.isInt()) { - const as_i64 = c.LLVMBuildPtrToInt(self.builder, val, c.LLVMInt64TypeInContext(self.context), "ptrtoint"); + const as_i64 = c.LLVMBuildPtrToInt(self.builder, val, self.i64Type(), "ptrtoint"); if (target_ty.bitWidth() < 64) { return c.LLVMBuildTrunc(self.builder, as_i64, target_llvm, "ptr_trunc"); } @@ -4042,8 +4044,7 @@ pub const CodeGen = struct { const tag_bits = c.LLVMGetIntTypeWidth(tag_llvm_ty); const tmp = self.buildEntryBlockAlloca(info.llvm_type, "union_cast"); _ = c.LLVMBuildStore(self.builder, val, tmp); - const tag_ptr = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, tmp, 0, "tag_ptr"); - const tag_val = c.LLVMBuildLoad2(self.builder, tag_llvm_ty, tag_ptr, "tag_val"); + const tag_val = self.loadStructField(info.llvm_type, tmp, 0, tag_llvm_ty); if (target_ty.bitWidth() == tag_bits) return tag_val; if (target_ty.bitWidth() > tag_bits) return c.LLVMBuildSExt(self.builder, tag_val, target_llvm, "tag_ext"); return c.LLVMBuildTrunc(self.builder, tag_val, target_llvm, "tag_trunc"); @@ -4071,7 +4072,7 @@ pub const CodeGen = struct { // Int → pointer/function_type: IntToPtr (for xx cast from integer to pointer) if (src_ty.isInt() and (target_ty.isPointer() or target_ty.isManyPointer() or target_ty.isFunctionType())) { - return c.LLVMBuildIntToPtr(self.builder, val, c.LLVMPointerTypeInContext(self.context, 0), "inttoptr"); + return c.LLVMBuildIntToPtr(self.builder, val, self.ptrType(), "inttoptr"); } // Slice/string → pointer: extract .ptr from fat pointer @@ -4127,6 +4128,11 @@ pub const CodeGen = struct { return null; } + fn findFieldIndex(self: *CodeGen, field_names: []const []const u8, field: []const u8, struct_name: []const u8) !usize { + return self.findNameIndex(field_names, field) orelse + return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ field, struct_name }); + } + fn componentToIndex(ch: u8) ?u32 { return switch (ch) { 'x', 'r', 'u' => 0, @@ -4144,7 +4150,7 @@ pub const CodeGen = struct { const is_f64 = std.meta.eql(arg_ty, Type.f64); const intrinsic_name: [*c]const u8 = if (is_f64) "llvm." ++ name ++ ".f64" else "llvm." ++ name ++ ".f32"; - const llvm_float_ty = if (is_f64) c.LLVMDoubleTypeInContext(self.context) else c.LLVMFloatTypeInContext(self.context); + const llvm_float_ty = if (is_f64) self.f64Type() else self.f32Type(); var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name); if (intrinsic_fn == null) { @@ -4161,19 +4167,18 @@ pub const CodeGen = struct { if (call_node.args.len != 1) return self.emitError("size_of expects exactly 1 argument"); const ty = self.resolveType(call_node.args[0]); if (std.meta.eql(ty, Type.void_type)) { - return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); + return self.constInt64(0); } const llvm_ty = self.typeToLLVM(ty); - const data_layout = c.LLVMGetModuleDataLayout(self.module); - const size = c.LLVMStoreSizeOfType(data_layout, llvm_ty); - return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), size, 0); + const size = self.getTypeSize(llvm_ty); + return self.constInt64(size); } fn genTypeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { if (call_node.args.len != 1) return self.emitError("type_of expects exactly 1 argument"); const arg = call_node.args[0]; const arg_ty = self.inferType(arg); - const i64_ty = c.LLVMInt64TypeInContext(self.context); + const i64_ty = self.i64Type(); // For Any values: extract the runtime tag (field 0) if (arg_ty.isAny()) { @@ -4209,10 +4214,9 @@ pub const CodeGen = struct { fn genFieldCount(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { if (call_node.args.len != 1) return self.emitError("field_count expects exactly 1 argument"); const ty = self.resolveType(call_node.args[0]); - const i64_ty = c.LLVMInt64TypeInContext(self.context); + const i64_ty = self.i64Type(); if (ty.isStruct()) { - const info = self.struct_types.get(ty.struct_type) orelse - return self.emitErrorFmt("unknown struct type '{s}'", .{ty.struct_type}); + const info = try self.getStructInfo(ty.struct_type); return c.LLVMConstInt(i64_ty, info.field_names.len, 0); } if (ty.isEnum()) { @@ -4224,8 +4228,7 @@ pub const CodeGen = struct { return c.LLVMConstInt(i64_ty, ty.vector_type.length, 0); } if (ty.isUnion()) { - const info = self.tagged_enum_types.get(ty.union_type) orelse - return self.emitErrorFmt("unknown enum type '{s}'", .{ty.union_type}); + const info = try self.getTaggedEnumInfo(ty.union_type); return c.LLVMConstInt(i64_ty, info.variant_names.len, 0); } if (ty.isArray()) { @@ -4240,16 +4243,14 @@ pub const CodeGen = struct { // Get the name list and type key const names: []const []const u8, const type_key: []const u8 = if (ty.isStruct()) blk: { - const info = self.struct_types.get(ty.struct_type) orelse - return self.emitErrorFmt("unknown struct type '{s}'", .{ty.struct_type}); + const info = try self.getStructInfo(ty.struct_type); break :blk .{ info.field_names, ty.struct_type }; } else if (ty.isEnum()) blk: { const variants = self.enum_types.get(ty.enum_type) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{ty.enum_type}); break :blk .{ variants, ty.enum_type }; } else if (ty.isUnion()) blk: { - const info = self.tagged_enum_types.get(ty.union_type) orelse - return self.emitErrorFmt("unknown enum type '{s}'", .{ty.union_type}); + const info = try self.getTaggedEnumInfo(ty.union_type); break :blk .{ info.variant_names, ty.union_type }; } else return self.emitError("field_name requires a struct or enum type"); @@ -4274,9 +4275,7 @@ pub const CodeGen = struct { // GEP into the array with runtime index const idx = try self.genExpr(call_node.args[1]); - const zero = self.constInt32(0); - var indices = [_]c.LLVMValueRef{ zero, idx }; - const elem_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, global, &indices, 2, "field_name_ptr"); + const elem_ptr = self.gepArrayElement(arr_ty, global, idx, "field_name_ptr"); return c.LLVMBuildLoad2(self.builder, str_ty, elem_ptr, "field_name"); } @@ -4298,65 +4297,53 @@ pub const CodeGen = struct { // Payload-less enum: return void Any (no payload to extract) if (val_ty.isEnum() and !val_ty.isUnion()) { - return self.buildAnyValue(c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0), .void_type); + return self.buildAnyValue(self.constInt64(0), .void_type); } // Tagged enum (with payloads): switch over tag, extract payload with correct type if (val_ty.isUnion()) { - const uinfo = self.tagged_enum_types.get(val_ty.union_type) orelse - return self.emitErrorFmt("unknown enum type '{s}'", .{val_ty.union_type}); + const uinfo = try self.getTaggedEnumInfo(val_ty.union_type); const union_alloca = self.buildEntryBlockAlloca(uinfo.llvm_type, "fv_union"); _ = c.LLVMBuildStore(self.builder, val, union_alloca); // Read tag (field 0) - const tag_ptr = c.LLVMBuildStructGEP2(self.builder, uinfo.llvm_type, union_alloca, 0, "fv_tag_ptr"); - const tag_val = c.LLVMBuildLoad2(self.builder, self.getEnumLLVMType(val_ty.union_type), tag_ptr, "fv_tag"); + const tag_val = self.loadStructField(uinfo.llvm_type, union_alloca, 0, self.getEnumLLVMType(val_ty.union_type)); const payload_ptr = c.LLVMBuildStructGEP2(self.builder, uinfo.llvm_type, union_alloca, uinfo.payload_field_index, "fv_payload_ptr"); const n = uinfo.variant_names.len; - const function = self.current_function; const any_ty = self.getAnyStructType(); - const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_merge"); - const default_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_default"); - const sw = c.LLVMBuildSwitch(self.builder, tag_val, default_bb, @intCast(n)); + const sb = self.buildSwitch(tag_val, @intCast(n), "fv_merge", "fv_default"); var phi_vals = std.ArrayList(c.LLVMValueRef).empty; var phi_bbs = std.ArrayList(c.LLVMBasicBlockRef).empty; const tag_llvm_ty = self.getEnumLLVMType(val_ty.union_type); for (uinfo.variant_types, 0..) |vty, vi| { - const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_ucase"); - c.LLVMAddCase(sw, c.LLVMConstInt(tag_llvm_ty, @intCast(vi), 0), case_bb); + const case_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "fv_ucase"); + c.LLVMAddCase(sb.sw, c.LLVMConstInt(tag_llvm_ty, @intCast(vi), 0), case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb); const any_val = if (vty == .void_type) blk: { // Void variant: return Any with void tag const undef = c.LLVMGetUndef(any_ty); - const void_tag = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), ANY_TAG_VOID, 0); + const void_tag = self.constInt64(ANY_TAG_VOID); const with_tag = c.LLVMBuildInsertValue(self.builder, undef, void_tag, 0, "void_tag"); - const zero_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); + const zero_val = self.constInt64(0); break :blk c.LLVMBuildInsertValue(self.builder, with_tag, zero_val, 1, "void_any"); } else blk: { - const payload = c.LLVMBuildLoad2(self.builder, self.typeToLLVM(vty), payload_ptr, "fv_payload"); + const payload = self.loadTyped(vty, payload_ptr, "fv_payload"); break :blk try self.buildAnyValue(payload, vty); }; - try phi_vals.append(self.allocator, any_val); - try phi_bbs.append(self.allocator, c.LLVMGetInsertBlock(self.builder)); - _ = c.LLVMBuildBr(self.builder, merge_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, any_val, sb.merge_bb); } // Default: undef - c.LLVMPositionBuilderAtEnd(self.builder, default_bb); - try phi_vals.append(self.allocator, c.LLVMGetUndef(any_ty)); - try phi_bbs.append(self.allocator, default_bb); - _ = c.LLVMBuildBr(self.builder, merge_bb); + c.LLVMPositionBuilderAtEnd(self.builder, sb.default_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, c.LLVMGetUndef(any_ty), sb.merge_bb); - c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); - const vals_slice = try phi_vals.toOwnedSlice(self.allocator); - const bbs_slice = try phi_bbs.toOwnedSlice(self.allocator); - const phi = c.LLVMBuildPhi(self.builder, any_ty, "fv_uresult"); - c.LLVMAddIncoming(phi, vals_slice.ptr, bbs_slice.ptr, @intCast(vals_slice.len)); + c.LLVMPositionBuilderAtEnd(self.builder, sb.merge_bb); + const phi = try self.buildPhiNode(&phi_vals, &phi_bbs, any_ty, "fv_uresult"); return phi; } @@ -4369,8 +4356,7 @@ pub const CodeGen = struct { // val is {ptr, i32} — extract ptr const data_ptr = c.LLVMBuildExtractValue(self.builder, val, 0, "fv_sdata"); const idx = try self.genExpr(call_node.args[1]); - var gep_indices = [_]c.LLVMValueRef{idx}; - const elem_ptr = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, data_ptr, &gep_indices, 1, "fv_selem"); + const elem_ptr = self.gepPointerElement(elem_llvm_ty, data_ptr, idx, "fv_selem"); const elem = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, elem_ptr, "fv_seval"); return self.buildAnyValue(elem, elem_ty); } @@ -4399,8 +4385,7 @@ pub const CodeGen = struct { const struct_ty = val_ty; if (!struct_ty.isStruct()) return self.emitError("field_value requires a struct, vector, enum, or array value"); - const info = self.struct_types.get(struct_ty.struct_type) orelse - return self.emitErrorFmt("unknown struct type '{s}'", .{struct_ty.struct_type}); + const info = try self.getStructInfo(struct_ty.struct_type); const idx = try self.genExpr(call_node.args[1]); const n = info.field_names.len; @@ -4410,19 +4395,16 @@ pub const CodeGen = struct { _ = c.LLVMBuildStore(self.builder, struct_val, struct_alloca); // Generate switch on idx with N cases - const function = self.current_function; - const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_merge"); - const default_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_default"); - const sw = c.LLVMBuildSwitch(self.builder, idx, default_bb, @intCast(n)); + const sb = self.buildSwitch(idx, @intCast(n), "fv_merge", "fv_default"); const any_ty = self.getAnyStructType(); var phi_vals = std.ArrayList(c.LLVMValueRef).empty; var phi_bbs = std.ArrayList(c.LLVMBasicBlockRef).empty; for (0..n) |i| { - const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fv_case"); - const case_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), i, 0); - c.LLVMAddCase(sw, case_val, case_bb); + const case_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "fv_case"); + const case_val = self.constInt64(i); + c.LLVMAddCase(sb.sw, case_val, case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb); // Extract field i via GEP + load @@ -4430,29 +4412,22 @@ pub const CodeGen = struct { const field_llvm_ty = c.LLVMStructGetTypeAtIndex(info.llvm_type, @intCast(i)); const field_val = c.LLVMBuildLoad2(self.builder, field_llvm_ty, field_ptr, "fv_field"); const any_val = try self.buildAnyValue(field_val, info.field_types[i]); - try phi_vals.append(self.allocator, any_val); - try phi_bbs.append(self.allocator, c.LLVMGetInsertBlock(self.builder)); - _ = c.LLVMBuildBr(self.builder, merge_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, any_val, sb.merge_bb); } // Default: return undef Any - c.LLVMPositionBuilderAtEnd(self.builder, default_bb); - try phi_vals.append(self.allocator, c.LLVMGetUndef(any_ty)); - try phi_bbs.append(self.allocator, default_bb); - _ = c.LLVMBuildBr(self.builder, merge_bb); + c.LLVMPositionBuilderAtEnd(self.builder, sb.default_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, c.LLVMGetUndef(any_ty), sb.merge_bb); - c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); - const vals_slice = try phi_vals.toOwnedSlice(self.allocator); - const bbs_slice = try phi_bbs.toOwnedSlice(self.allocator); - const phi = c.LLVMBuildPhi(self.builder, any_ty, "fv_result"); - c.LLVMAddIncoming(phi, vals_slice.ptr, bbs_slice.ptr, @intCast(vals_slice.len)); + c.LLVMPositionBuilderAtEnd(self.builder, sb.merge_bb); + const phi = try self.buildPhiNode(&phi_vals, &phi_bbs, any_ty, "fv_result"); return phi; } fn genIsFlags(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { if (call_node.args.len != 1) return self.emitError("is_flags expects exactly 1 argument"); const ty = self.resolveType(call_node.args[0]); - const i1_type = c.LLVMInt1TypeInContext(self.context); + const i1_type = self.i1Type(); if (ty.isEnum()) { const is_flags = self.flags_enum_types.contains(ty.enum_type); return c.LLVMConstInt(i1_type, @intFromBool(is_flags), 0); @@ -4463,7 +4438,7 @@ pub const CodeGen = struct { fn genFieldValueInt(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { if (call_node.args.len != 2) return self.emitError("field_value_int expects 2 arguments: field_value_int(T, idx)"); const ty = self.resolveType(call_node.args[0]); - const i64_type = c.LLVMInt64TypeInContext(self.context); + const i64_type = self.i64Type(); // For non-enum types (e.g. tagged enums compiled via dead code), return the index as value if (!ty.isEnum()) { return try self.genExpr(call_node.args[1]); @@ -4474,41 +4449,31 @@ pub const CodeGen = struct { const n = variants.len; const idx = try self.genExpr(call_node.args[1]); - const function = self.current_function; - const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fvi_merge"); - const default_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fvi_default"); - const sw = c.LLVMBuildSwitch(self.builder, idx, default_bb, @intCast(n)); + const sb = self.buildSwitch(idx, @intCast(n), "fvi_merge", "fvi_default"); var phi_vals = std.ArrayList(c.LLVMValueRef).empty; var phi_bbs = std.ArrayList(c.LLVMBasicBlockRef).empty; for (0..n) |i| { - const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fvi_case"); - c.LLVMAddCase(sw, c.LLVMConstInt(i64_type, i, 0), case_bb); + const case_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "fvi_case"); + c.LLVMAddCase(sb.sw, c.LLVMConstInt(i64_type, i, 0), case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb); const val: u64 = if (values) |vals| @bitCast(vals[i]) else i; - try phi_vals.append(self.allocator, c.LLVMConstInt(i64_type, val, 0)); - try phi_bbs.append(self.allocator, case_bb); - _ = c.LLVMBuildBr(self.builder, merge_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, c.LLVMConstInt(i64_type, val, 0), sb.merge_bb); } - c.LLVMPositionBuilderAtEnd(self.builder, default_bb); - try phi_vals.append(self.allocator, c.LLVMConstInt(i64_type, 0, 0)); - try phi_bbs.append(self.allocator, default_bb); - _ = c.LLVMBuildBr(self.builder, merge_bb); + c.LLVMPositionBuilderAtEnd(self.builder, sb.default_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, c.LLVMConstInt(i64_type, 0, 0), sb.merge_bb); - c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); - const vals_slice = try phi_vals.toOwnedSlice(self.allocator); - const bbs_slice = try phi_bbs.toOwnedSlice(self.allocator); - const phi = c.LLVMBuildPhi(self.builder, i64_type, "fvi_result"); - c.LLVMAddIncoming(phi, vals_slice.ptr, bbs_slice.ptr, @intCast(vals_slice.len)); + c.LLVMPositionBuilderAtEnd(self.builder, sb.merge_bb); + const phi = try self.buildPhiNode(&phi_vals, &phi_bbs, i64_type, "fvi_result"); return phi; } fn genFieldIndex(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { if (call_node.args.len != 2) return self.emitError("field_index expects 2 arguments: field_index(T, value)"); const ty = self.resolveType(call_node.args[0]); - const i64_type = c.LLVMInt64TypeInContext(self.context); + const i64_type = self.i64Type(); if (!ty.isEnum()) { _ = try self.genExpr(call_node.args[1]); return c.LLVMConstInt(i64_type, 0, 0); @@ -4531,10 +4496,7 @@ pub const CodeGen = struct { else val; - const function = self.current_function; - const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fi_merge"); - const default_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fi_default"); - const sw = c.LLVMBuildSwitch(self.builder, sw_val, default_bb, @intCast(n)); + const sb = self.buildSwitch(sw_val, @intCast(n), "fi_merge", "fi_default"); var phi_vals = std.ArrayList(c.LLVMValueRef).empty; var phi_bbs = std.ArrayList(c.LLVMBasicBlockRef).empty; @@ -4549,25 +4511,18 @@ pub const CodeGen = struct { } if (is_dup) continue; try seen_values.append(self.allocator, explicit_val); - const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "fi_case"); - c.LLVMAddCase(sw, c.LLVMConstInt(enum_llvm_ty, explicit_val, 0), case_bb); + const case_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "fi_case"); + c.LLVMAddCase(sb.sw, c.LLVMConstInt(enum_llvm_ty, explicit_val, 0), case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb); - try phi_vals.append(self.allocator, c.LLVMConstInt(i64_type, i, 0)); - try phi_bbs.append(self.allocator, case_bb); - _ = c.LLVMBuildBr(self.builder, merge_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, c.LLVMConstInt(i64_type, i, 0), sb.merge_bb); } - c.LLVMPositionBuilderAtEnd(self.builder, default_bb); + c.LLVMPositionBuilderAtEnd(self.builder, sb.default_bb); const neg_one = c.LLVMConstInt(i64_type, @bitCast(@as(i64, -1)), 0); - try phi_vals.append(self.allocator, neg_one); - try phi_bbs.append(self.allocator, default_bb); - _ = c.LLVMBuildBr(self.builder, merge_bb); + try self.addPhiCase(&phi_vals, &phi_bbs, neg_one, sb.merge_bb); - c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); - const vals_slice = try phi_vals.toOwnedSlice(self.allocator); - const bbs_slice = try phi_bbs.toOwnedSlice(self.allocator); - const phi = c.LLVMBuildPhi(self.builder, i64_type, "fi_result"); - c.LLVMAddIncoming(phi, vals_slice.ptr, bbs_slice.ptr, @intCast(vals_slice.len)); + c.LLVMPositionBuilderAtEnd(self.builder, sb.merge_bb); + const phi = try self.buildPhiNode(&phi_vals, &phi_bbs, i64_type, "fi_result"); return phi; } @@ -4581,9 +4536,9 @@ pub const CodeGen = struct { fn genAlloc(self: *CodeGen, args: []const *Node) !c.LLVMValueRef { if (args.len != 1) return self.emitError("alloc expects exactly 1 argument: alloc(size)"); - const builtins = self.builtins orelse return self.emitError("builtins not available (missing #builtin import)"); + const builtins = try self.requireBuiltins(); const size_val = try self.genExpr(args[0]); - const i64_type = c.LLVMInt64TypeInContext(self.context); + const i64_type = self.i64Type(); // calloc(size + 1, 1) — extra byte for null terminator const one_i64 = c.LLVMConstInt(i64_type, 1, 0); const size_plus_one = c.LLVMBuildAdd(self.builder, size_val, one_i64, "szp1"); @@ -4597,7 +4552,7 @@ pub const CodeGen = struct { fn genMalloc(self: *CodeGen, args: []const *Node) !c.LLVMValueRef { if (args.len != 1) return self.emitError("malloc expects exactly 1 argument: malloc(size)"); - const builtins = self.builtins orelse return self.emitError("builtins not available"); + const builtins = try self.requireBuiltins(); const size_val = try self.genExpr(args[0]); const fn_ty = c.LLVMGlobalGetValueType(builtins.malloc_fn); var call_args = [_]c.LLVMValueRef{size_val}; @@ -4606,7 +4561,7 @@ pub const CodeGen = struct { fn genFree(self: *CodeGen, args: []const *Node) !c.LLVMValueRef { if (args.len != 1) return self.emitError("free expects exactly 1 argument: free(ptr)"); - const builtins = self.builtins orelse return self.emitError("builtins not available"); + const builtins = try self.requireBuiltins(); const ptr_val = try self.genExpr(args[0]); const fn_ty = c.LLVMGlobalGetValueType(builtins.free_fn); var call_args = [_]c.LLVMValueRef{ptr_val}; @@ -4616,7 +4571,7 @@ pub const CodeGen = struct { fn genMemcpy(self: *CodeGen, args: []const *Node) !c.LLVMValueRef { if (args.len != 3) return self.emitError("memcpy expects 3 arguments: memcpy(dst, src, size)"); - const builtins = self.builtins orelse return self.emitError("builtins not available"); + const builtins = try self.requireBuiltins(); const dst = try self.genExpr(args[0]); const src = try self.genExpr(args[1]); const size_val = try self.genExpr(args[2]); @@ -4644,16 +4599,15 @@ pub const CodeGen = struct { const pointee_ty = self.resolveTypeFromName(entry.ty.pointer_type.pointee_name) orelse return self.emitError("unknown pointee type for auto-deref"); const loaded_ptr = c.LLVMBuildLoad2(self.builder, - c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); + self.ptrType(), entry.ptr, "ptr_load"); if (pointee_ty.isStruct()) { const sname = pointee_ty.struct_type; - const info = self.struct_types.get(sname) orelse - return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); + const info = try self.getStructInfo(sname); const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "pfield"); - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(info.field_types[idx]), gep, "pfieldval"); + return self.loadTyped(info.field_types[idx], gep, "pfieldval"); } if (pointee_ty.isSlice()) { const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), loaded_ptr, "pslice_load"); @@ -4664,9 +4618,8 @@ pub const CodeGen = struct { if (entry.ty.isStruct()) { const sname = entry.ty.struct_type; const info = try self.getStructInfo(sname); - const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); - const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "field"); - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(info.field_types[idx]), gep, "fieldval"); + const idx = try self.findFieldIndex(info.field_names, fa.field, sname); + return self.loadStructField(info.llvm_type, entry.ptr, @intCast(idx), self.typeToLLVM(info.field_types[idx])); } if (entry.ty.isUnion()) { const uname = entry.ty.union_type; @@ -4674,15 +4627,13 @@ pub const CodeGen = struct { if (self.union_types.get(uname)) |info| { if (self.findNameIndex(info.field_names, fa.field)) |fidx| { const field_ty = info.field_types[fidx]; - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(field_ty), entry.ptr, "union_field"); + return self.loadTyped(field_ty, entry.ptr, "union_field"); } // Check promoted fields from anonymous structs if (info.promoted_fields.get(fa.field)) |pf| { - const sinfo = self.struct_types.get(pf.struct_name) orelse - return self.emitErrorFmt("unknown promoted struct '{s}'", .{pf.struct_name}); + const sinfo = try self.getStructInfo(pf.struct_name); // GEP through union pointer as struct type, then access field - const gep = c.LLVMBuildStructGEP2(self.builder, sinfo.llvm_type, entry.ptr, @intCast(pf.field_index), "promoted_field"); - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(pf.field_type), gep, "promoted_val"); + return self.loadStructField(sinfo.llvm_type, entry.ptr, @intCast(pf.field_index), self.typeToLLVM(pf.field_type)); } return self.emitErrorFmt("no field '{s}' in union '{s}'", .{ fa.field, uname }); } @@ -4700,11 +4651,10 @@ pub const CodeGen = struct { const variant_ty = info.variant_types[idx]; if (variant_ty == .void_type) return self.emitErrorFmt("cannot access payload of void variant '{s}'", .{fa.field}); // GEP to payload area, load as variant type - const payload_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, info.payload_field_index, "payload"); - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(variant_ty), payload_gep, "union_payload"); + return self.loadStructField(info.llvm_type, entry.ptr, info.payload_field_index, self.typeToLLVM(variant_ty)); } if (entry.ty.isVector()) { - const vec_val = c.LLVMBuildLoad2(self.builder, self.typeToLLVM(entry.ty), entry.ptr, "vec_load"); + const vec_val = self.loadTyped(entry.ty, entry.ptr, "vec_load"); return self.genVectorExtract(vec_val, fa.field); } if (entry.ty == .string_type) { @@ -4717,7 +4667,7 @@ pub const CodeGen = struct { } if (entry.ty.isArray()) { if (std.mem.eql(u8, fa.field, "len")) { - return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), entry.ty.array_type.length, 0); + return self.constInt64(entry.ty.array_type.length); } return self.emitErrorFmt("no field '{s}' on array (available: .len)", .{fa.field}); } @@ -4772,25 +4722,21 @@ pub const CodeGen = struct { } if (obj_ty.isArray()) { const arr_info = obj_ty.array_type; - const elem_ty = Type.fromName(arr_info.element_name) orelse return self.emitErrorFmt("unknown array element type '{s}'", .{arr_info.element_name}); + const elem_ty = try self.resolveElementType(arr_info.element_name, "array"); // Array index via identifier: load from GEP if (ie.object.data == .identifier) { if (self.named_values.get(ie.object.data.identifier.name)) |entry| { const idx = try self.genExpr(ie.index); - const zero = self.constInt32(0); - var indices = [_]c.LLVMValueRef{ zero, idx }; - const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "arridx"); - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "arrval"); + const gep = self.gepArrayElement(self.typeToLLVM(obj_ty), entry.ptr, idx, "arridx"); + return self.loadTyped(elem_ty, gep, "arrval"); } } // Array index via field access: GEP through struct field if (ie.object.data == .field_access) { const field_ptr = try self.genAddressOf(ie.object); const idx = try self.genExpr(ie.index); - const zero = self.constInt32(0); - var indices = [_]c.LLVMValueRef{ zero, idx }; - const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx"); - return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "field_arrval"); + const gep = self.gepArrayElement(self.typeToLLVM(obj_ty), field_ptr, idx, "field_arridx"); + return self.loadTyped(elem_ty, gep, "field_arrval"); } } if (obj_ty == .string_type) { @@ -4798,15 +4744,14 @@ pub const CodeGen = struct { const str_val = try self.genExpr(ie.object); const ptr = c.LLVMBuildExtractValue(self.builder, str_val, 0, "str_ptr"); const idx = try self.genExpr(ie.index); - const i8_type = c.LLVMInt8TypeInContext(self.context); - var gep_indices = [_]c.LLVMValueRef{idx}; - const gep = c.LLVMBuildGEP2(self.builder, i8_type, ptr, &gep_indices, 1, "stridx"); + const i8_type = self.i8Type(); + const gep = self.gepPointerElement(i8_type, ptr, idx, "stridx"); return c.LLVMBuildLoad2(self.builder, i8_type, gep, "byte"); } if (obj_ty.isSlice()) { // Slice indexing: extract ptr, GEP with element type, load const slice_info = obj_ty.slice_type; - const elem_ty = Type.fromName(slice_info.element_name) orelse return self.emitErrorFmt("unknown slice element type '{s}'", .{slice_info.element_name}); + const elem_ty = try self.resolveElementType(slice_info.element_name, "slice"); const elem_llvm_ty = self.typeToLLVM(elem_ty); // For identifier objects, load the slice from alloca if (ie.object.data == .identifier) { @@ -4814,8 +4759,7 @@ pub const CodeGen = struct { const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "slice_load"); const ptr = c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_ptr"); const idx = try self.genExpr(ie.index); - var gep_indices = [_]c.LLVMValueRef{idx}; - const gep = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr, &gep_indices, 1, "sliceidx"); + const gep = self.gepPointerElement(elem_llvm_ty, ptr, idx, "sliceidx"); return c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "sliceval"); } } @@ -4823,8 +4767,7 @@ pub const CodeGen = struct { const slice_val = try self.genExpr(ie.object); const ptr = c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_ptr"); const idx = try self.genExpr(ie.index); - var gep_indices = [_]c.LLVMValueRef{idx}; - const gep = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr, &gep_indices, 1, "sliceidx"); + const gep = self.gepPointerElement(elem_llvm_ty, ptr, idx, "sliceidx"); return c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "sliceval"); } // Many-pointer indexing: [*]T — GEP + load @@ -4833,8 +4776,7 @@ pub const CodeGen = struct { const elem_llvm_ty = self.typeToLLVM(elem_ty); const ptr_val = try self.genExpr(ie.object); const idx = try self.genExpr(ie.index); - var gep_indices = [_]c.LLVMValueRef{idx}; - const gep = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, ptr_val, &gep_indices, 1, "mptridx"); + const gep = self.gepPointerElement(elem_llvm_ty, ptr_val, idx, "mptridx"); return c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "mptrval"); } return self.emitError("index expression requires an array, vector, string, slice, or [*] pointer"); @@ -4842,7 +4784,7 @@ pub const CodeGen = struct { fn genSliceExpr(self: *CodeGen, se: ast.SliceExpr) !c.LLVMValueRef { const obj_ty = self.inferType(se.object); - const i64_ty = c.LLVMInt64TypeInContext(self.context); + const i64_ty = self.i64Type(); const zero = c.LLVMConstInt(i64_ty, 0, 0); const slice_struct_ty = self.getStringStructType(); @@ -4868,10 +4810,7 @@ pub const CodeGen = struct { // len = end - start const len_val = c.LLVMBuildSub(self.builder, end_val, start_val, "slice_len"); // Build {ptr, len} - var result = c.LLVMGetUndef(slice_struct_ty); - result = c.LLVMBuildInsertValue(self.builder, result, elem_ptr, 0, "slice_ptr"); - result = c.LLVMBuildInsertValue(self.builder, result, len_val, 1, "slice_len"); - return result; + return self.buildFatPointer(slice_struct_ty, elem_ptr, len_val); } if (obj_ty == .string_type or obj_ty.isSlice()) { @@ -4890,18 +4829,14 @@ pub const CodeGen = struct { const end_val = if (se.end) |e| try self.genExpr(e) else base_len; // GEP base_ptr + start const elem_llvm_ty = if (obj_ty == .string_type) - c.LLVMInt8TypeInContext(self.context) + self.i8Type() else self.typeToLLVM(Type.fromName(obj_ty.slice_type.element_name) orelse return self.emitError("unknown slice element type")); - var gep_indices = [_]c.LLVMValueRef{start_val}; - const new_ptr = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, base_ptr, &gep_indices, 1, "sslice_off"); + const new_ptr = self.gepPointerElement(elem_llvm_ty, base_ptr, start_val, "sslice_off"); // len = end - start const len_val = c.LLVMBuildSub(self.builder, end_val, start_val, "sslice_len"); // Build {ptr, len} - var result = c.LLVMGetUndef(slice_struct_ty); - result = c.LLVMBuildInsertValue(self.builder, result, new_ptr, 0, "sslice_ptr"); - result = c.LLVMBuildInsertValue(self.builder, result, len_val, 1, "sslice_len"); - return result; + return self.buildFatPointer(slice_struct_ty, new_ptr, len_val); } return self.emitError("slice expression requires an array, string, or slice"); @@ -4943,12 +4878,8 @@ pub const CodeGen = struct { fn genShortCircuitOp(self: *CodeGen, binop: ast.BinaryOp, is_and: bool) !c.LLVMValueRef { const function = self.current_function; - const i1_type = c.LLVMInt1TypeInContext(self.context); - var lhs_val = try self.genExpr(binop.lhs); - if (c.LLVMTypeOf(lhs_val) != i1_type) { - lhs_val = c.LLVMBuildICmp(self.builder, c.LLVMIntNE, lhs_val, c.LLVMConstInt(c.LLVMTypeOf(lhs_val), 0, 0), "tobool"); - } + const lhs_val = self.valueToBool(try self.genExpr(binop.lhs)); const lhs_bb = c.LLVMGetInsertBlock(self.builder); const rhs_label: [*c]const u8 = if (is_and) "and.rhs" else "or.rhs"; @@ -4964,18 +4895,16 @@ pub const CodeGen = struct { _ = c.LLVMBuildCondBr(self.builder, lhs_val, merge_bb, rhs_bb); c.LLVMPositionBuilderAtEnd(self.builder, rhs_bb); - var rhs_val = try self.genExpr(binop.rhs); - if (c.LLVMTypeOf(rhs_val) != i1_type) { - rhs_val = c.LLVMBuildICmp(self.builder, c.LLVMIntNE, rhs_val, c.LLVMConstInt(c.LLVMTypeOf(rhs_val), 0, 0), "tobool"); - } + const rhs_val = self.valueToBool(try self.genExpr(binop.rhs)); const rhs_end_bb = c.LLVMGetInsertBlock(self.builder); _ = c.LLVMBuildBr(self.builder, merge_bb); c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); const short_circuit_val: u64 = if (is_and) 0 else 1; const result_label: [*c]const u8 = if (is_and) "and.result" else "or.result"; - const phi = c.LLVMBuildPhi(self.builder, i1_type, result_label); - var vals = [2]c.LLVMValueRef{ c.LLVMConstInt(i1_type, short_circuit_val, 0), rhs_val }; + const i1_ty = self.i1Type(); + const phi = c.LLVMBuildPhi(self.builder, i1_ty, result_label); + var vals = [2]c.LLVMValueRef{ c.LLVMConstInt(i1_ty, short_circuit_val, 0), rhs_val }; var blocks = [2]c.LLVMBasicBlockRef{ lhs_bb, rhs_end_bb }; c.LLVMAddIncoming(phi, &vals, &blocks, 2); @@ -5019,7 +4948,7 @@ pub const CodeGen = struct { // Union construction: Shape.variant(payload) const resolved_type: ?Type = blk: { if (fa.object.data == .identifier) { - const name = self.type_aliases.get(fa.object.data.identifier.name) orelse fa.object.data.identifier.name; + const name = self.resolveAlias(fa.object.data.identifier.name); if (self.tagged_enum_types.contains(name)) break :blk .{ .union_type = name }; if (self.struct_types.contains(name)) break :blk .{ .struct_type = name }; } else { @@ -5197,10 +5126,8 @@ pub const CodeGen = struct { if (spread_operand.data == .identifier) { if (self.named_values.get(spread_operand.data.identifier.name)) |entry| { const arr_llvm_ty = self.typeToLLVM(spread_ty); - const zero = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); - var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; - const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, entry.ptr, &ptr_indices, 2, "spread_ptr"); - const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), arr_info.length, 0); + const arr_ptr = self.arrayDecayToPointer(arr_llvm_ty, entry.ptr, "spread_ptr"); + const len_val = self.constInt64(arr_info.length); const slice_val = self.buildStringSlice(arr_ptr, len_val); try arg_vals.append(self.allocator, slice_val); } else { @@ -5228,23 +5155,18 @@ pub const CodeGen = struct { const arg_ty = self.inferType(call_node.args[fixed_count + vi_idx]); break :blk try self.buildAnyValue(raw_val, arg_ty); } else try self.genExprAsType(call_node.args[fixed_count + vi_idx], elem_ty); - const zero = self.constInt32(0); - const idx_val = self.constInt32(@intCast(vi_idx)); - var indices = [_]c.LLVMValueRef{ zero, idx_val }; - const gep = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &indices, 2, "vararg_elem"); + const gep = self.gepArrayElement(arr_ty, arr_alloca, self.constInt32(@intCast(vi_idx)), "vararg_elem"); _ = c.LLVMBuildStore(self.builder, arg_val, gep); } // Build slice: {ptr, len} - const zero = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); - var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; - const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &ptr_indices, 2, "varargs_ptr"); - const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), @intCast(var_arg_count), 0); + const arr_ptr = self.arrayDecayToPointer(arr_ty, arr_alloca, "varargs_ptr"); + const len_val = self.constInt64(@intCast(var_arg_count)); const slice_val = self.buildStringSlice(arr_ptr, len_val); try arg_vals.append(self.allocator, slice_val); } else { // Zero variadic args: pass empty slice {null, 0} - const null_ptr = c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)); - const zero_len = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); + const null_ptr = c.LLVMConstNull(self.ptrType()); + const zero_len = self.constInt64(0); const slice_val = self.buildStringSlice(null_ptr, zero_len); try arg_vals.append(self.allocator, slice_val); } @@ -5299,7 +5221,7 @@ pub const CodeGen = struct { const args_slice = try arg_vals.toOwnedSlice(self.allocator); const ret_ty = c.LLVMGetReturnType(fn_type); - const call_name: [*c]const u8 = if (ret_ty == c.LLVMVoidTypeInContext(self.context)) "" else "calltmp"; + const call_name: [*c]const u8 = if (ret_ty == self.voidType()) "" else "calltmp"; return c.LLVMBuildCall2( self.builder, fn_type, @@ -5314,11 +5236,11 @@ pub const CodeGen = struct { const fti = entry.ty.function_type; // Load the function pointer from the alloca - const ptr_ty = c.LLVMPointerTypeInContext(self.context, 0); + const ptr_ty = self.ptrType(); const fn_ptr = c.LLVMBuildLoad2(self.builder, ptr_ty, entry.ptr, "fn_ptr"); // Build LLVM function type from FunctionTypeInfo - const ptr_ty_llvm = c.LLVMPointerTypeInContext(self.context, 0); + const ptr_ty_llvm = self.ptrType(); var param_llvm_types: [64]c.LLVMTypeRef = undefined; for (fti.param_types, 0..) |pt, i| { // [N]T and [:0]T params are pointers at the ABI level @@ -5354,7 +5276,7 @@ pub const CodeGen = struct { } const args_slice = try arg_vals.toOwnedSlice(self.allocator); - const call_name: [*c]const u8 = if (ret_llvm == c.LLVMVoidTypeInContext(self.context)) "" else "calltmp"; + const call_name: [*c]const u8 = if (ret_llvm == self.voidType()) "" else "calltmp"; return c.LLVMBuildCall2( self.builder, fn_type, @@ -5537,7 +5459,7 @@ pub const CodeGen = struct { const fn_type = c.LLVMGlobalGetValueType(callee_fn); const ret_ty = c.LLVMGetReturnType(fn_type); - const call_name: [*c]const u8 = if (ret_ty == c.LLVMVoidTypeInContext(self.context)) "" else "calltmp"; + const call_name: [*c]const u8 = if (ret_ty == self.voidType()) "" else "calltmp"; return c.LLVMBuildCall2( self.builder, fn_type, @@ -5671,10 +5593,7 @@ pub const CodeGen = struct { const any_i64 = c.LLVMBuildExtractValue(self.builder, any_val, 1, "any_payload"); // Build dispatch switch - const function = self.current_function; - const dispatch_merge = c.LLVMAppendBasicBlockInContext(self.context, function, "dispatch_merge"); - const dispatch_default = c.LLVMAppendBasicBlockInContext(self.context, function, "dispatch_default"); - const sw = c.LLVMBuildSwitch(self.builder, type_tag_val, dispatch_default, @intCast(match_tags.len)); + const sb = self.buildSwitch(type_tag_val, @intCast(match_tags.len), "dispatch_merge", "dispatch_default"); // Determine return type from function signature const ret_ty = if (fd.return_type) |rt| self.resolveType(rt) else Type.void_type; @@ -5697,8 +5616,8 @@ pub const CodeGen = struct { const sx_type = entry_type orelse continue; // Create case BB - const case_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "dispatch_case"); - c.LLVMAddCase(sw, c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), tag, 0), case_bb); + const case_bb = c.LLVMAppendBasicBlockInContext(self.context, self.current_function, "dispatch_case"); + c.LLVMAddCase(sb.sw, self.constInt64(tag), case_bb); c.LLVMPositionBuilderAtEnd(self.builder, case_bb); // Convert Any payload to the concrete type @@ -5776,24 +5695,21 @@ pub const CodeGen = struct { try phi_vals.append(self.allocator, call_result); try phi_bbs.append(self.allocator, c.LLVMGetInsertBlock(self.builder)); } - _ = c.LLVMBuildBr(self.builder, dispatch_merge); + _ = c.LLVMBuildBr(self.builder, sb.merge_bb); } // Default case: return undef (should not be reached) - c.LLVMPositionBuilderAtEnd(self.builder, dispatch_default); + c.LLVMPositionBuilderAtEnd(self.builder, sb.default_bb); if (ret_ty != .void_type and result_llvm_ty != null) { try phi_vals.append(self.allocator, c.LLVMGetUndef(result_llvm_ty.?)); - try phi_bbs.append(self.allocator, dispatch_default); + try phi_bbs.append(self.allocator, sb.default_bb); } - _ = c.LLVMBuildBr(self.builder, dispatch_merge); + _ = c.LLVMBuildBr(self.builder, sb.merge_bb); // Merge - c.LLVMPositionBuilderAtEnd(self.builder, dispatch_merge); + c.LLVMPositionBuilderAtEnd(self.builder, sb.merge_bb); if (ret_ty != .void_type and result_llvm_ty != null) { - const vals_slice = try phi_vals.toOwnedSlice(self.allocator); - const bbs_slice = try phi_bbs.toOwnedSlice(self.allocator); - const phi = c.LLVMBuildPhi(self.builder, result_llvm_ty.?, "dispatch_phi"); - c.LLVMAddIncoming(phi, vals_slice.ptr, bbs_slice.ptr, @intCast(vals_slice.len)); + const phi = try self.buildPhiNode(&phi_vals, &phi_bbs, result_llvm_ty.?, "dispatch_phi"); return phi; } @@ -5803,7 +5719,7 @@ pub const CodeGen = struct { /// Extract a concrete typed value from an Any i64 payload. fn extractAnyToConcreteType(self: *CodeGen, any_i64: c.LLVMValueRef, sx_type: Type) !c.LLVMValueRef { return switch (sx_type) { - .boolean => c.LLVMBuildTrunc(self.builder, any_i64, c.LLVMInt1TypeInContext(self.context), "any_to_bool"), + .boolean => c.LLVMBuildTrunc(self.builder, any_i64, self.i1Type(), "any_to_bool"), .signed => |w| if (w <= 32) c.LLVMBuildTrunc(self.builder, any_i64, c.LLVMIntTypeInContext(self.context, w), "any_to_int") else @@ -5813,18 +5729,14 @@ pub const CodeGen = struct { else any_i64, .f32 => blk: { - const as_f64 = c.LLVMBuildBitCast(self.builder, any_i64, c.LLVMDoubleTypeInContext(self.context), "i64_to_f64"); - break :blk c.LLVMBuildFPTrunc(self.builder, as_f64, c.LLVMFloatTypeInContext(self.context), "any_to_f32"); - }, - .f64 => c.LLVMBuildBitCast(self.builder, any_i64, c.LLVMDoubleTypeInContext(self.context), "any_to_f64"), - .string_type => blk: { - const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_str_ptr"); - break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_str"); + const as_f64 = c.LLVMBuildBitCast(self.builder, any_i64, self.f64Type(), "i64_to_f64"); + break :blk c.LLVMBuildFPTrunc(self.builder, as_f64, self.f32Type(), "any_to_f32"); }, + .f64 => c.LLVMBuildBitCast(self.builder, any_i64, self.f64Type(), "any_to_f64"), + .string_type => self.loadFromI64Ptr(any_i64, self.getStringStructType(), "any_to_str"), .struct_type => |sname| blk: { const info = try self.getStructInfo(sname); - const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_struct_ptr"); - break :blk c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_struct"); + break :blk self.loadFromI64Ptr(any_i64, info.llvm_type, "any_to_struct"); }, .enum_type => |ename| blk: { const enum_llvm_ty = self.getEnumLLVMType(ename); @@ -5836,19 +5748,14 @@ pub const CodeGen = struct { }, .union_type => |uname| blk: { const info = try self.getTaggedEnumInfo(uname); - const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_union_ptr"); - break :blk c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_union"); + break :blk self.loadFromI64Ptr(any_i64, info.llvm_type, "any_to_union"); }, .vector_type, .array_type => blk: { const llvm_ty = self.typeToLLVM(sx_type); - const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_vec_ptr"); - break :blk c.LLVMBuildLoad2(self.builder, llvm_ty, ptr, "any_to_vec"); + break :blk self.loadFromI64Ptr(any_i64, llvm_ty, "any_to_vec"); }, - .slice_type => blk: { - const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_slice_ptr"); - break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_slice"); - }, - .pointer_type, .many_pointer_type => c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_ptr"), + .slice_type => self.loadFromI64Ptr(any_i64, self.getStringStructType(), "any_to_slice"), + .pointer_type, .many_pointer_type => c.LLVMBuildIntToPtr(self.builder, any_i64, self.ptrType(), "any_to_ptr"), else => any_i64, }; } @@ -5932,8 +5839,7 @@ pub const CodeGen = struct { self.named_values.clearRetainingCapacity(); self.current_function = function; - const entry_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "entry"); - c.LLVMPositionBuilderAtEnd(self.builder, entry_bb); + _ = self.appendBlock(function, "entry"); // Create allocas for parameters var llvm_param_idx: u32 = 0; @@ -6032,14 +5938,7 @@ pub const CodeGen = struct { fn genIfExpr(self: *CodeGen, if_expr: ast.IfExpr) !c.LLVMValueRef { // Generate condition - var cond_val = try self.genExpr(if_expr.condition); - - // Ensure condition is i1 (bool) - const cond_type = c.LLVMTypeOf(cond_val); - const i1_type = c.LLVMInt1TypeInContext(self.context); - if (cond_type != i1_type) { - cond_val = c.LLVMBuildICmp(self.builder, c.LLVMIntNE, cond_val, c.LLVMConstInt(cond_type, 0, 0), "tobool"); - } + const cond_val = self.valueToBool(try self.genExpr(if_expr.condition)); const function = self.current_function; const has_else = if_expr.else_branch != null; @@ -6097,14 +5996,7 @@ pub const CodeGen = struct { // Condition block c.LLVMPositionBuilderAtEnd(self.builder, cond_bb); - var cond_val = try self.genExpr(while_expr.condition); - - // Ensure condition is i1 (bool) - const cond_type = c.LLVMTypeOf(cond_val); - const i1_type = c.LLVMInt1TypeInContext(self.context); - if (cond_type != i1_type) { - cond_val = c.LLVMBuildICmp(self.builder, c.LLVMIntNE, cond_val, c.LLVMConstInt(cond_type, 0, 0), "tobool"); - } + const cond_val = self.valueToBool(try self.genExpr(while_expr.condition)); _ = c.LLVMBuildCondBr(self.builder, cond_val, body_bb, after_bb); @@ -6135,7 +6027,7 @@ pub const CodeGen = struct { fn genForExpr(self: *CodeGen, for_expr: ast.ForExpr) !c.LLVMValueRef { const function = self.current_function; - const i64_type = c.LLVMInt64TypeInContext(self.context); + const i64_type = self.i64Type(); // Determine iterable type and get length + element access info const iter_ty = self.inferType(for_expr.iterable); @@ -6201,8 +6093,7 @@ pub const CodeGen = struct { if (is_slice) { // Slice: GEP through data pointer - var gep_indices = [_]c.LLVMValueRef{body_idx}; - const gep = c.LLVMBuildGEP2(self.builder, elem_llvm_ty, iter_ptr, &gep_indices, 1, "for_elem"); + const gep = self.gepPointerElement(elem_llvm_ty, iter_ptr, body_idx, "for_elem"); const elem_val = c.LLVMBuildLoad2(self.builder, elem_llvm_ty, gep, "it_val"); _ = c.LLVMBuildStore(self.builder, elem_val, it_alloca); } else { @@ -6285,8 +6176,7 @@ pub const CodeGen = struct { // Union: load tag from field 0 of the alloca const entry = self.named_values.get(match.subject.data.identifier.name).?; const info = self.tagged_enum_types.get(union_name.?).?; - const tag_gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, 0, "tag"); - break :blk c.LLVMBuildLoad2(self.builder, self.getEnumLLVMType(union_name.?), tag_gep, "tag_val"); + break :blk self.loadStructField(info.llvm_type, entry.ptr, 0, self.getEnumLLVMType(union_name.?)); } else try self.genExpr(match.subject); const variants: ?[]const []const u8 = if (union_name) |un| @@ -6297,7 +6187,7 @@ pub const CodeGen = struct { null; const function = self.current_function; - const i64_type = c.LLVMInt64TypeInContext(self.context); + const i64_type = self.i64Type(); // Enum/union case constants use the backing type; Any dispatch uses i64 const case_int_type = if (enum_name) |en| self.getEnumLLVMType(en) else if (union_name) |un| self.getEnumLLVMType(un) else i64_type; const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "match_end"); @@ -6439,10 +6329,7 @@ pub const CodeGen = struct { try phi_bbs.append(self.allocator, default_bb); } - const vals_slice = try phi_vals.toOwnedSlice(self.allocator); - const bbs_slice = try phi_bbs.toOwnedSlice(self.allocator); - const phi = c.LLVMBuildPhi(self.builder, value_type, "matchtmp"); - c.LLVMAddIncoming(phi, vals_slice.ptr, bbs_slice.ptr, @intCast(vals_slice.len)); + const phi = try self.buildPhiNode(&phi_vals, &phi_bbs, value_type, "matchtmp"); return phi; } @@ -6610,13 +6497,13 @@ pub const CodeGen = struct { fn genWriteCall(self: *CodeGen, args: []const *Node) !c.LLVMValueRef { if (args.len != 1) return self.emitError("write expects exactly 1 argument"); - const builtins = self.builtins orelse return self.emitError("builtins not available (missing #builtin import)"); + const builtins = try self.requireBuiltins(); const val = try self.genExpr(args[0]); // Extract ptr and len from string slice const ptr = c.LLVMBuildExtractValue(self.builder, val, 0, "str_ptr"); const len_i64 = c.LLVMBuildExtractValue(self.builder, val, 1, "str_len"); // printf %.*s precision is C int (i32) — truncate from i64 - const len = c.LLVMBuildTrunc(self.builder, len_i64, c.LLVMInt32TypeInContext(self.context), "len_trunc"); + const len = c.LLVMBuildTrunc(self.builder, len_i64, self.i32Type(), "len_trunc"); const fmt = c.LLVMBuildGlobalStringPtr(self.builder, "%.*s", "write_fmt"); const printf_fn = builtins.printf_fn; const fn_type = c.LLVMGlobalGetValueType(printf_fn); @@ -6635,8 +6522,8 @@ pub const CodeGen = struct { /// Helper: build a constant string slice as a global constant (no builder needed). fn buildConstStrGlobal(self: *CodeGen, s: []const u8) c.LLVMValueRef { const sz = self.allocator.dupeZ(u8, s) catch unreachable; - const i64_ty = c.LLVMInt64TypeInContext(self.context); - const i8_ty = c.LLVMInt8TypeInContext(self.context); + const i64_ty = self.i64Type(); + const i8_ty = self.i8Type(); // Create a global string constant const str_const = c.LLVMConstStringInContext(self.context, sz.ptr, @intCast(s.len), 0); const global_name = (self.allocator.dupeZ(u8, std.fmt.allocPrint(self.allocator, ".str.{s}", .{s}) catch unreachable)) catch unreachable; @@ -6650,7 +6537,7 @@ pub const CodeGen = struct { } // Build constant struct {ptr, i64} var fields = [_]c.LLVMValueRef{ - c.LLVMConstBitCast(global.?, c.LLVMPointerTypeInContext(self.context, 0)), + c.LLVMConstBitCast(global.?, self.ptrType()), c.LLVMConstInt(i64_ty, s.len, 0), }; return c.LLVMConstStructInContext(self.context, &fields, 2, 0); @@ -6721,7 +6608,7 @@ pub const CodeGen = struct { /// Resolve a name to a type display string, or null if not a type. fn resolveTypeName(self: *CodeGen, name: []const u8) ?[]const u8 { - const resolved = self.type_aliases.get(name) orelse name; + const resolved = self.resolveAlias(name); if (self.struct_types.get(resolved)) |info| return info.display_name orelse resolved; if (self.resolveTypeFromName(name) != null) return resolved; return null; @@ -6805,7 +6692,7 @@ pub const CodeGen = struct { const fa = call_node.callee.data.field_access; const obj_ty = blk: { if (fa.object.data == .identifier) { - const name = self.type_aliases.get(fa.object.data.identifier.name) orelse fa.object.data.identifier.name; + const name = self.resolveAlias(fa.object.data.identifier.name); if (self.tagged_enum_types.contains(name)) break :blk Type{ .union_type = name }; } const ty = self.resolveType(fa.object); diff --git a/src/comptime.zig b/src/comptime.zig index 9ac0d32..c687282 100644 --- a/src/comptime.zig +++ b/src/comptime.zig @@ -57,6 +57,18 @@ pub const Value = union(enum) { }; } + pub fn asIndex(self: Value) !usize { + return @intCast(self.asInt() orelse return error.TypeError); + } + + pub fn isTruthy(self: Value) bool { + return switch (self) { + .bool_val => |bv| bv, + .int_val => |iv| iv != 0, + else => true, + }; + } + pub fn asFloat(self: Value) ?f64 { return switch (self) { .float_val => |v| v, @@ -312,6 +324,18 @@ pub const Compiler = struct { try self.instructions.append(self.allocator, instruction); } + fn patchJump(self: *Compiler, idx: usize) void { + self.instructions.items[idx] = .{ .jump = @intCast(@as(i64, @intCast(self.instructions.items.len)) - @as(i64, @intCast(idx)) - 1) }; + } + + fn patchJumpIfFalse(self: *Compiler, idx: usize) void { + self.instructions.items[idx] = .{ .jump_if_false = @intCast(@as(i64, @intCast(self.instructions.items.len)) - @as(i64, @intCast(idx)) - 1) }; + } + + fn patchJumpIfTrue(self: *Compiler, idx: usize) void { + self.instructions.items[idx] = .{ .jump_if_true = @intCast(@as(i64, @intCast(self.instructions.items.len)) - @as(i64, @intCast(idx)) - 1) }; + } + fn addString(self: *Compiler, str: []const u8) !u32 { const idx: u32 = @intCast(self.strings.items.len); try self.strings.append(self.allocator, str); @@ -399,9 +423,7 @@ pub const Compiler = struct { try self.emit(.{ .jump_if_false = 0 }); try self.emit(.pop); try self.compileNode(binop.rhs); - self.instructions.items[jump_idx] = .{ - .jump_if_false = @intCast(@as(i64, @intCast(self.instructions.items.len)) - @as(i64, @intCast(jump_idx)) - 1), - }; + self.patchJumpIfFalse(jump_idx); } else if (binop.op == .or_op) { // Short-circuit OR: LHS, dup, jump_if_true +N, pop, RHS try self.compileNode(binop.lhs); @@ -410,9 +432,7 @@ pub const Compiler = struct { try self.emit(.{ .jump_if_true = 0 }); try self.emit(.pop); try self.compileNode(binop.rhs); - self.instructions.items[jump_idx] = .{ - .jump_if_true = @intCast(@as(i64, @intCast(self.instructions.items.len)) - @as(i64, @intCast(jump_idx)) - 1), - }; + self.patchJumpIfTrue(jump_idx); } else { try self.compileNode(binop.lhs); try self.compileNode(binop.rhs); @@ -465,9 +485,7 @@ pub const Compiler = struct { .neq => .neq, else => unreachable, }); - self.instructions.items[jump_idx] = .{ - .jump_if_false = @intCast(@as(i64, @intCast(self.instructions.items.len)) - @as(i64, @intCast(jump_idx)) - 1), - }; + self.patchJumpIfFalse(jump_idx); } }, .unary_op => |unop| { @@ -608,14 +626,11 @@ pub const Compiler = struct { if (ie.else_branch) |eb| { const jump_end_idx = self.instructions.items.len; try self.emit(.{ .jump = 0 }); // placeholder - // Patch jump_if_false to here - self.instructions.items[jump_false_idx] = .{ .jump_if_false = @intCast(self.instructions.items.len - jump_false_idx - 1) }; + self.patchJumpIfFalse(jump_false_idx); try self.compileNode(eb); - // Patch jump to end - self.instructions.items[jump_end_idx] = .{ .jump = @intCast(self.instructions.items.len - jump_end_idx - 1) }; + self.patchJump(jump_end_idx); } else { - // Patch jump_if_false to here - self.instructions.items[jump_false_idx] = .{ .jump_if_false = @intCast(self.instructions.items.len - jump_false_idx - 1) }; + self.patchJumpIfFalse(jump_false_idx); } }, .call => |call_node| { @@ -637,18 +652,8 @@ pub const Compiler = struct { if (callee_name) |name| { // Check if it's a builtin const base = if (std.mem.lastIndexOfScalar(u8, name, '.')) |idx| name[idx + 1 ..] else name; - if (std.mem.eql(u8, base, "print")) { - try self.emit(.{ .call_builtin = .{ .id = .print, .arg_count = @intCast(call_node.args.len) } }); - } else if (std.mem.eql(u8, base, "write")) { - try self.emit(.{ .call_builtin = .{ .id = .write, .arg_count = @intCast(call_node.args.len) } }); - } else if (std.mem.eql(u8, base, "sqrt")) { - try self.emit(.{ .call_builtin = .{ .id = .sqrt, .arg_count = @intCast(call_node.args.len) } }); - } else if (std.mem.eql(u8, base, "size_of")) { - try self.emit(.{ .call_builtin = .{ .id = .size_of, .arg_count = @intCast(call_node.args.len) } }); - } else if (std.mem.eql(u8, base, "cast")) { - try self.emit(.{ .call_builtin = .{ .id = .cast, .arg_count = @intCast(call_node.args.len) } }); - } else if (std.mem.eql(u8, base, "alloc")) { - try self.emit(.{ .call_builtin = .{ .id = .alloc, .arg_count = @intCast(call_node.args.len) } }); + if (std.meta.stringToEnum(BuiltinId, base)) |id| { + try self.emit(.{ .call_builtin = .{ .id = id, .arg_count = @intCast(call_node.args.len) } }); } else { try self.emit(.{ .call = .{ .func_name = name, .arg_count = @intCast(call_node.args.len) } }); } @@ -670,8 +675,7 @@ pub const Compiler = struct { try self.compileNode(arm.body); try end_jumps.append(self.allocator, self.instructions.items.len); try self.emit(.{ .jump = 0 }); // placeholder jump to end - // Patch jump_if_false - self.instructions.items[jump_next_idx] = .{ .jump_if_false = @intCast(self.instructions.items.len - jump_next_idx - 1) }; + self.patchJumpIfFalse(jump_next_idx); } else { // else arm: unconditionally execute body try self.emit(.pop); // pop the subject copy @@ -683,7 +687,7 @@ pub const Compiler = struct { try self.emit(.pop); // pop remaining subject // Patch all end jumps for (end_jumps.items) |idx| { - self.instructions.items[idx] = .{ .jump = @intCast(self.instructions.items.len - idx - 1) }; + self.patchJump(idx); } }, .struct_literal => |sl| { @@ -772,12 +776,11 @@ pub const Compiler = struct { try self.emit(.{ .jump = back_offset }); // Patch jump_if_false to after the loop - const after_loop = self.instructions.items.len; - self.instructions.items[jump_false_idx] = .{ .jump_if_false = @intCast(after_loop - jump_false_idx - 1) }; + self.patchJumpIfFalse(jump_false_idx); // Patch all break jumps to after the loop for (self.break_patches.items) |patch_idx| { - self.instructions.items[patch_idx] = .{ .jump = @as(i32, @intCast(after_loop)) - @as(i32, @intCast(patch_idx)) - 1 }; + self.patchJump(patch_idx); } // Restore outer loop context @@ -942,7 +945,7 @@ pub const VM = struct { .address_of_index => { const idx_val = try self.pop(); const arr = try self.pop(); - const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError); + const idx: usize = try idx_val.asIndex(); if (arr == .array_val) { if (idx >= arr.array_val.elements.len) return error.IndexOutOfBounds; try self.push(.{ .pointer_val = .{ .target = arr.array_val.elements.ptr + idx } }); @@ -980,31 +983,11 @@ pub const VM = struct { }, // Arithmetic - .add => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(try self.arith(a, b, .add_op)); - }, - .sub => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(try self.arith(a, b, .sub_op)); - }, - .mul => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(try self.arith(a, b, .mul_op)); - }, - .div => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(try self.arith(a, b, .div_op)); - }, - .mod => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(try self.arith(a, b, .mod_op)); - }, + .add => try self.execArith(.add_op), + .sub => try self.execArith(.sub_op), + .mul => try self.execArith(.mul_op), + .div => try self.execArith(.div_op), + .mod => try self.execArith(.mod_op), .bit_and => { const b = try self.pop(); const a = try self.pop(); @@ -1030,44 +1013,15 @@ pub const VM = struct { }, // Comparison - .eq => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(.{ .bool_val = self.valEqual(a, b) }); - }, - .neq => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(.{ .bool_val = !self.valEqual(a, b) }); - }, - .lt => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(.{ .bool_val = self.valLess(a, b) }); - }, - .lte => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(.{ .bool_val = self.valLess(a, b) or self.valEqual(a, b) }); - }, - .gt => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(.{ .bool_val = self.valLess(b, a) }); - }, - .gte => { - const b = try self.pop(); - const a = try self.pop(); - try self.push(.{ .bool_val = self.valLess(b, a) or self.valEqual(a, b) }); - }, + .eq => try self.execComparison(.eq), + .neq => try self.execComparison(.neq), + .lt => try self.execComparison(.lt), + .lte => try self.execComparison(.lte), + .gt => try self.execComparison(.gt), + .gte => try self.execComparison(.gte), .not => { const v = try self.pop(); - const b = switch (v) { - .bool_val => |bv| bv, - .int_val => |iv| iv != 0, - else => true, - }; - try self.push(.{ .bool_val = !b }); + try self.push(.{ .bool_val = !v.isTruthy() }); }, // Control flow @@ -1076,23 +1030,13 @@ pub const VM = struct { }, .jump_if_false => |offset| { const v = try self.pop(); - const is_true = switch (v) { - .bool_val => |bv| bv, - .int_val => |iv| iv != 0, - else => true, - }; - if (!is_true) { + if (!v.isTruthy()) { frame.ip = @intCast(@as(i64, frame.ip) + offset); } }, .jump_if_true => |offset| { const v = try self.pop(); - const is_true = switch (v) { - .bool_val => |bv| bv, - .int_val => |iv| iv != 0, - else => true, - }; - if (is_true) { + if (v.isTruthy()) { frame.ip = @intCast(@as(i64, frame.ip) + offset); } }, @@ -1187,7 +1131,7 @@ pub const VM = struct { const idx_val = try self.pop(); const arr = try self.pop(); if (arr == .array_val) { - const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError); + const idx: usize = try idx_val.asIndex(); if (idx < arr.array_val.elements.len) { try self.push(arr.array_val.elements[idx]); } else { @@ -1195,7 +1139,7 @@ pub const VM = struct { } } else if (arr == .string_val) { // String indexing: return byte as int - const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError); + const idx: usize = try idx_val.asIndex(); if (idx < arr.string_val.len) { try self.push(.{ .int_val = @intCast(arr.string_val[idx]) }); } else { @@ -1203,7 +1147,7 @@ pub const VM = struct { } } else if (arr == .pointer_val) { // Many-pointer indexing: ptr[i] - const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError); + const idx: usize = try idx_val.asIndex(); try self.push(arr.pointer_val.target[idx]); } else { return error.TypeError; @@ -1214,14 +1158,14 @@ pub const VM = struct { const idx_val = try self.pop(); const arr = try self.pop(); if (arr == .array_val) { - const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError); + const idx: usize = try idx_val.asIndex(); if (idx < arr.array_val.elements.len) { arr.array_val.elements[idx] = val; } try self.push(arr); } else if (arr == .string_val) { // String index assignment: mutate byte - const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError); + const idx: usize = try idx_val.asIndex(); const byte_val: u8 = @intCast(val.asInt() orelse return error.TypeError); if (idx < arr.string_val.len) { const mutable = @constCast(arr.string_val); @@ -1230,7 +1174,7 @@ pub const VM = struct { try self.push(arr); } else if (arr == .pointer_val) { // Many-pointer index assignment: ptr[i] = val - const idx: usize = @intCast(idx_val.asInt() orelse return error.TypeError); + const idx: usize = try idx_val.asIndex(); arr.pointer_val.target[idx] = val; try self.push(arr); } else { @@ -1260,6 +1204,28 @@ pub const VM = struct { const ArithOp = enum { add_op, sub_op, mul_op, div_op, mod_op }; + fn execArith(self: *VM, op: ArithOp) !void { + const b = try self.pop(); + const a = try self.pop(); + try self.push(try self.arith(a, b, op)); + } + + const CmpOp = enum { eq, neq, lt, lte, gt, gte }; + + fn execComparison(self: *VM, comptime op: CmpOp) !void { + const b = try self.pop(); + const a = try self.pop(); + const result = switch (op) { + .eq => self.valEqual(a, b), + .neq => !self.valEqual(a, b), + .lt => self.valLess(a, b), + .lte => self.valLess(a, b) or self.valEqual(a, b), + .gt => self.valLess(b, a), + .gte => self.valLess(b, a) or self.valEqual(a, b), + }; + try self.push(.{ .bool_val = result }); + } + fn arith(self: *VM, a: Value, b: Value, op: ArithOp) !Value { _ = self; // Both int @@ -1357,27 +1323,13 @@ pub const VM = struct { for (self.root_decls) |decl| { switch (decl.data) { .fn_decl => |fd| { - if (std.mem.eql(u8, fd.name, name)) { - var compiler = Compiler.init(self.allocator, self.sema_result, self.root_decls, self.codegen); - const chunk = try compiler.compileFunction(fd); - try self.functions.put(name, chunk); - if (self.functions.getPtr(name)) |ptr| { - return self.invokeChunk(ptr, arg_count); - } - } + if (std.mem.eql(u8, fd.name, name)) + return self.compileFunctionAndInvoke(name, fd, arg_count); }, .namespace_decl => |ns| { for (ns.decls) |d| { - if (d.data == .fn_decl) { - if (std.mem.eql(u8, d.data.fn_decl.name, name)) { - var compiler = Compiler.init(self.allocator, self.sema_result, self.root_decls, self.codegen); - const chunk = try compiler.compileFunction(d.data.fn_decl); - try self.functions.put(name, chunk); - if (self.functions.getPtr(name)) |ptr| { - return self.invokeChunk(ptr, arg_count); - } - } - } + if (d.data == .fn_decl and std.mem.eql(u8, d.data.fn_decl.name, name)) + return self.compileFunctionAndInvoke(name, d.data.fn_decl, arg_count); } }, else => {}, @@ -1553,6 +1505,29 @@ pub const VM = struct { return result; } + fn cacheTypeGlobal(self: *VM, name: []const u8, ty: Type) VMError!Value { + const val = Value{ .type_val = ty }; + try self.globals.put(name, val); + return val; + } + + fn compileAndEvalGlobal(self: *VM, name: []const u8, expr: *Node) VMError!Value { + var compiler = Compiler.init(self.allocator, self.sema_result, self.root_decls, self.codegen); + const chunk = compiler.compile(expr) catch return error.CompileError; + const result = self.evalInFreshVM(&chunk) catch return error.CompileError; + try self.globals.put(name, result); + return result; + } + + fn compileFunctionAndInvoke(self: *VM, name: []const u8, fd: ast.FnDecl, arg_count: u8) !void { + var compiler = Compiler.init(self.allocator, self.sema_result, self.root_decls, self.codegen); + const chunk = try compiler.compileFunction(fd); + try self.functions.put(name, chunk); + if (self.functions.getPtr(name)) |ptr| { + return self.invokeChunk(ptr, arg_count); + } + } + fn resolveGlobal(self: *VM, name: []const u8) VMError!Value { // Check cache first if (self.globals.get(name)) |val| return val; @@ -1562,21 +1537,13 @@ pub const VM = struct { switch (decl.data) { .const_decl => |cd| { if (std.mem.eql(u8, cd.name, name)) { - var compiler = Compiler.init(self.allocator, self.sema_result, self.root_decls, self.codegen); - const chunk = compiler.compile(cd.value) catch return error.CompileError; - const result = self.evalInFreshVM(&chunk) catch return error.CompileError; - try self.globals.put(name, result); - return result; + return self.compileAndEvalGlobal(name, cd.value); } }, .var_decl => |vd| { if (std.mem.eql(u8, vd.name, name)) { if (vd.value) |val_expr| { - var compiler = Compiler.init(self.allocator, self.sema_result, self.root_decls, self.codegen); - const chunk = compiler.compile(val_expr) catch return error.CompileError; - const result = self.evalInFreshVM(&chunk) catch return error.CompileError; - try self.globals.put(name, result); - return result; + return self.compileAndEvalGlobal(name, val_expr); } return .{ .void_val = {} }; } @@ -1585,11 +1552,7 @@ pub const VM = struct { // Check inside namespace for matching declarations for (ns.decls) |d| { if (d.data == .const_decl and std.mem.eql(u8, d.data.const_decl.name, name)) { - var compiler = Compiler.init(self.allocator, self.sema_result, self.root_decls, self.codegen); - const chunk = compiler.compile(d.data.const_decl.value) catch return error.CompileError; - const result = self.evalInFreshVM(&chunk) catch return error.CompileError; - try self.globals.put(name, result); - return result; + return self.compileAndEvalGlobal(name, d.data.const_decl.value); } } }, @@ -1601,37 +1564,26 @@ pub const VM = struct { for (self.root_decls) |decl| { switch (decl.data) { .struct_decl => |sd| { - if (std.mem.eql(u8, sd.name, name)) { - const val = Value{ .type_val = .{ .struct_type = name } }; - try self.globals.put(name, val); - return val; - } + if (std.mem.eql(u8, sd.name, name)) + return self.cacheTypeGlobal(name, .{ .struct_type = name }); }, .enum_decl => |ed| { if (std.mem.eql(u8, ed.name, name)) { const ty: Type = if (ed.variant_types.len > 0) .{ .union_type = name } else .{ .enum_type = name }; - const val = Value{ .type_val = ty }; - try self.globals.put(name, val); - return val; + return self.cacheTypeGlobal(name, ty); } }, .union_decl => |ud| { - if (std.mem.eql(u8, ud.name, name)) { - const val = Value{ .type_val = .{ .union_type = name } }; - try self.globals.put(name, val); - return val; - } + if (std.mem.eql(u8, ud.name, name)) + return self.cacheTypeGlobal(name, .{ .union_type = name }); }, else => {}, } } // Check if it's a primitive type name (s32, f64, bool, etc.) - if (Type.fromName(name)) |ty| { - const val = Value{ .type_val = ty }; - try self.globals.put(name, val); - return val; - } + if (Type.fromName(name)) |ty| + return self.cacheTypeGlobal(name, ty); return error.UndefinedVariable; }