This commit is contained in:
agra
2026-02-14 22:26:58 +02:00
parent e7d2abdf0c
commit 3fd14bafac
4 changed files with 231 additions and 403 deletions

View File

@@ -11,6 +11,7 @@ const Parser = @import("parser.zig").Parser;
const errors = @import("errors.zig"); const errors = @import("errors.zig");
const sema = @import("sema.zig"); const sema = @import("sema.zig");
const comptime_mod = @import("comptime.zig"); const comptime_mod = @import("comptime.zig");
const unescape = @import("unescape.zig");
pub const TargetConfig = struct { pub const TargetConfig = struct {
/// Target triple (e.g. "aarch64-apple-darwin"). Null = host default. /// Target triple (e.g. "aarch64-apple-darwin"). Null = host default.
@@ -375,6 +376,14 @@ pub const CodeGen = struct {
c.LLVMContextDispose(self.context); c.LLVMContextDispose(self.context);
} }
fn getStructInfo(self: *CodeGen, name: []const u8) !StructInfo {
return self.struct_types.get(name) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{name});
}
fn getTaggedEnumInfo(self: *CodeGen, name: []const u8) !TaggedEnumInfo {
return self.tagged_enum_types.get(name) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{name});
}
fn emitError(self: *CodeGen, msg: []const u8) error{CodeGenError} { fn emitError(self: *CodeGen, msg: []const u8) error{CodeGenError} {
if (self.diagnostics) |diags| diags.add(.err, msg, self.current_span); if (self.diagnostics) |diags| diags.add(.err, msg, self.current_span);
return error.CodeGenError; return error.CodeGenError;
@@ -621,7 +630,7 @@ pub const CodeGen = struct {
.pointer_type, .many_pointer_type => c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_ptr"), .pointer_type, .many_pointer_type => c.LLVMBuildPtrToInt(self.builder, val, i64_ty, "any_ptr"),
.meta_type => |mt| blk: { .meta_type => |mt| blk: {
// Meta type: wrap raw char ptr in string slice {ptr, len} for extraction // Meta type: wrap raw char ptr in string slice {ptr, len} for extraction
const str_slice = self.buildStringSlice(val, mt.name.len); const str_slice = self.buildStringSlice(val, self.constInt64(mt.name.len));
const alloca = self.buildEntryBlockAlloca(self.getStringStructType(), "any_type_tmp"); const alloca = self.buildEntryBlockAlloca(self.getStringStructType(), "any_type_tmp");
_ = c.LLVMBuildStore(self.builder, str_slice, alloca); _ = c.LLVMBuildStore(self.builder, str_slice, alloca);
break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_type"); break :blk c.LLVMBuildPtrToInt(self.builder, alloca, i64_ty, "any_type");
@@ -641,21 +650,31 @@ pub const CodeGen = struct {
return self.string_struct_type.?; return self.string_struct_type.?;
} }
/// Build a string slice {ptr, len} from a raw pointer and a constant length. /// Build a string slice {ptr, len} from a raw pointer and a length value.
fn buildStringSlice(self: *CodeGen, ptr: c.LLVMValueRef, len: u64) c.LLVMValueRef { fn buildStringSlice(self: *CodeGen, ptr: c.LLVMValueRef, len_val: c.LLVMValueRef) c.LLVMValueRef {
const str_ty = self.getStringStructType(); const str_ty = self.getStringStructType();
const undef = c.LLVMGetUndef(str_ty); const undef = c.LLVMGetUndef(str_ty);
const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr"); const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr");
const len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), len, 0);
return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice"); return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice");
} }
/// Build a string slice {ptr, len} from a raw pointer and a runtime length value. fn constInt64(self: *CodeGen, val: u64) c.LLVMValueRef {
fn buildStringSliceRT(self: *CodeGen, ptr: c.LLVMValueRef, len_val: c.LLVMValueRef) c.LLVMValueRef { return c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), val, 0);
const str_ty = self.getStringStructType(); }
const undef = c.LLVMGetUndef(str_ty);
const with_ptr = c.LLVMBuildInsertValue(self.builder, undef, ptr, 0, "str_ptr"); fn constInt32(self: *CodeGen, val: u32) c.LLVMValueRef {
return c.LLVMBuildInsertValue(self.builder, with_ptr, len_val, 1, "str_slice"); return c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), val, 0);
}
/// Extract .len or .ptr from a fat pointer value ({ptr, len} struct).
fn extractFatPtrField(self: *CodeGen, val: c.LLVMValueRef, field: []const u8, type_name: []const u8) !c.LLVMValueRef {
if (std.mem.eql(u8, field, "len")) {
return c.LLVMBuildExtractValue(self.builder, val, 1, "len");
}
if (std.mem.eql(u8, field, "ptr")) {
return c.LLVMBuildExtractValue(self.builder, val, 0, "ptr");
}
return self.emitErrorFmt("no field '{s}' on {s} (available: .len, .ptr)", .{ field, type_name });
} }
fn pushScope(self: *CodeGen) !void { fn pushScope(self: *CodeGen) !void {
@@ -731,11 +750,11 @@ pub const CodeGen = struct {
try self.builtin_functions.put(fd.name, {}); try self.builtin_functions.put(fd.name, {});
} else if (fd.body.data == .foreign_expr) { } else if (fd.body.data == .foreign_expr) {
// External C function — register LLVM declaration (no body) // External C function — register LLVM declaration (no body)
try self.registerFnDecl(fd); try self.registerFnDecl(fd, fd.name);
} else if (fd.type_params.len > 0) { } else if (fd.type_params.len > 0) {
try self.generic_templates.put(fd.name, .{ .fd = fd }); try self.generic_templates.put(fd.name, .{ .fd = fd });
} else { } else {
try self.registerFnDecl(fd); try self.registerFnDecl(fd, fd.name);
} }
try self.fn_signatures.put(fd.name, self.buildFnSignature(fd)); try self.fn_signatures.put(fd.name, self.buildFnSignature(fd));
}, },
@@ -880,7 +899,7 @@ pub const CodeGen = struct {
if (shouldDeferFnBody(fd)) { if (shouldDeferFnBody(fd)) {
try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = fd.name }); try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = fd.name });
} else { } else {
try self.genFnBody(fd); try self.genFnBody(fd, fd.name);
} }
} }
}, },
@@ -901,7 +920,7 @@ pub const CodeGen = struct {
const saved_ns = self.current_namespace; const saved_ns = self.current_namespace;
self.current_namespace = deferred.namespace; self.current_namespace = deferred.namespace;
defer self.current_namespace = saved_ns; defer self.current_namespace = saved_ns;
try self.genFnBodyAs(deferred.fd, deferred.name); try self.genFnBody(deferred.fd, deferred.name);
} }
// Execute comptime side effects via bytecode VM (e.g., #run main();) // Execute comptime side effects via bytecode VM (e.g., #run main();)
@@ -990,9 +1009,9 @@ pub const CodeGen = struct {
.string_val => |v| blk: { .string_val => |v| blk: {
const z = self.allocator.dupeZ(u8, v) catch unreachable; const z = self.allocator.dupeZ(u8, v) catch unreachable;
const ptr = c.LLVMBuildGlobalStringPtr(self.builder, z.ptr, "comptime_str"); const ptr = c.LLVMBuildGlobalStringPtr(self.builder, z.ptr, "comptime_str");
break :blk self.buildStringSlice(ptr, @intCast(v.len)); break :blk self.buildStringSlice(ptr, self.constInt64(@intCast(v.len)));
}, },
.void_val => c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), .void_val => self.constInt32(0),
.pointer_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)), .pointer_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)),
.null_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)), .null_val => c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)),
.struct_val, .array_val, .type_val, .function_val => unreachable, .struct_val, .array_val, .type_val, .function_val => unreachable,
@@ -1365,11 +1384,7 @@ pub const CodeGen = struct {
return null; return null;
} }
fn buildFnType(self: *CodeGen, params: []const ast.Param, return_type: ?*Node, name: []const u8) !c.LLVMTypeRef { fn buildFnType(self: *CodeGen, params: []const ast.Param, return_type: ?*Node, name: []const u8, is_foreign: bool) !c.LLVMTypeRef {
return self.buildFnTypeEx(params, return_type, name, false);
}
fn buildFnTypeEx(self: *CodeGen, params: []const ast.Param, return_type: ?*Node, name: []const u8, is_foreign: bool) !c.LLVMTypeRef {
const ret_sx_type = self.resolveType(return_type); const ret_sx_type = self.resolveType(return_type);
const is_main = std.mem.eql(u8, name, "main"); const is_main = std.mem.eql(u8, name, "main");
const ret_llvm_type = if (is_main) const ret_llvm_type = if (is_main)
@@ -1599,13 +1614,9 @@ pub const CodeGen = struct {
} }
} }
fn registerFnDecl(self: *CodeGen, fd: ast.FnDecl) !void { fn registerFnDecl(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void {
return self.registerFnDeclAs(fd, fd.name);
}
fn registerFnDeclAs(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void {
const is_foreign = fd.body.data == .foreign_expr; const is_foreign = fd.body.data == .foreign_expr;
const fn_type = try self.buildFnTypeEx(fd.params, fd.return_type, fd.name, is_foreign); const fn_type = try self.buildFnType(fd.params, fd.return_type, fd.name, is_foreign);
const name_z = try self.allocator.dupeZ(u8, llvm_name); const name_z = try self.allocator.dupeZ(u8, llvm_name);
_ = c.LLVMAddFunction(self.module, name_z.ptr, fn_type); _ = c.LLVMAddFunction(self.module, name_z.ptr, fn_type);
// Track foreign functions for ABI lowering at call sites // Track foreign functions for ABI lowering at call sites
@@ -1651,7 +1662,7 @@ pub const CodeGen = struct {
const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name }); const qualified = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{ ns.name, fd.name });
if (fd.body.data == .foreign_expr) { if (fd.body.data == .foreign_expr) {
// External C function in namespace — register LLVM declaration with C name only // External C function in namespace — register LLVM declaration with C name only
try self.registerFnDeclAs(fd, fd.name); try self.registerFnDecl(fd, fd.name);
// Also track qualified name as foreign for ABI lowering at call sites // Also track qualified name as foreign for ABI lowering at call sites
try self.foreign_fns.put(qualified, {}); try self.foreign_fns.put(qualified, {});
// Store param types under qualified name so call-site type resolution works // Store param types under qualified name so call-site type resolution works
@@ -1664,7 +1675,7 @@ pub const CodeGen = struct {
} else if (fd.type_params.len > 0) { } else if (fd.type_params.len > 0) {
try self.generic_templates.put(qualified, .{ .fd = fd }); try self.generic_templates.put(qualified, .{ .fd = fd });
} else { } else {
try self.registerFnDeclAs(fd, qualified); try self.registerFnDecl(fd, qualified);
} }
}, },
.enum_decl => |ed| { .enum_decl => |ed| {
@@ -1730,7 +1741,7 @@ pub const CodeGen = struct {
if (shouldDeferFnBody(fd)) { if (shouldDeferFnBody(fd)) {
try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = ns.name }); try self.deferred_fn_bodies.append(self.allocator, .{ .fd = fd, .name = qualified, .namespace = ns.name });
} else { } else {
try self.genFnBodyAs(fd, qualified); try self.genFnBody(fd, qualified);
} }
} }
}, },
@@ -1885,11 +1896,27 @@ pub const CodeGen = struct {
try self.named_values.put(name, .{ .ptr = alloca, .ty = sx_ty }); try self.named_values.put(name, .{ .ptr = alloca, .ty = sx_ty });
} }
fn genFnBody(self: *CodeGen, fd: ast.FnDecl) !void { /// Prepare a return value: load structs/unions from alloca pointers, convert types.
return self.genFnBodyAs(fd, fd.name); 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 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 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;
} else {
const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val));
return self.convertValue(raw_val, src_ty, ret_type);
}
} }
fn genFnBodyAs(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void { fn genFnBody(self: *CodeGen, fd: ast.FnDecl, llvm_name: []const u8) !void {
self.named_values.clearRetainingCapacity(); self.named_values.clearRetainingCapacity();
const ret_sx_type = self.resolveType(fd.return_type); const ret_sx_type = self.resolveType(fd.return_type);
@@ -1944,27 +1971,8 @@ pub const CodeGen = struct {
if (ret_sx_type == .void_type and !is_main) { if (ret_sx_type == .void_type and !is_main) {
_ = c.LLVMBuildRetVoid(self.builder); _ = c.LLVMBuildRetVoid(self.builder);
} else if (effective_last_val) |val| { } else if (effective_last_val) |val| {
if (ret_sx_type.isStruct()) { const ret_val = try self.prepareReturnValue(val, ret_sx_type);
// Struct implicit return: val is an alloca pointer, load the value
const sname = ret_sx_type.struct_type;
const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname});
const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, val, "retval");
_ = c.LLVMBuildRet(self.builder, loaded);
} else if (ret_sx_type.isUnion()) {
// Tagged enum implicit return: val may be alloca or loaded value
const uname = ret_sx_type.union_type;
const resolved = self.type_aliases.get(uname) orelse uname;
const info = self.tagged_enum_types.get(resolved) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved});
const ret_val2 = if (c.LLVMGetTypeKind(c.LLVMTypeOf(val)) == c.LLVMPointerTypeKind)
c.LLVMBuildLoad2(self.builder, info.llvm_type, val, "retval")
else
val;
_ = c.LLVMBuildRet(self.builder, ret_val2);
} else {
const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(val));
const ret_val = self.convertValue(val, src_ty, self.current_return_type);
_ = c.LLVMBuildRet(self.builder, ret_val); _ = c.LLVMBuildRet(self.builder, ret_val);
}
} else if (is_main) { } else if (is_main) {
_ = c.LLVMBuildRet(self.builder, c.LLVMConstInt(ret_llvm_type, 0, 0)); _ = c.LLVMBuildRet(self.builder, c.LLVMConstInt(ret_llvm_type, 0, 0));
} else if (ret_sx_type != .void_type) { } else if (ret_sx_type != .void_type) {
@@ -2053,7 +2061,7 @@ pub const CodeGen = struct {
self.named_values = std.StringHashMap(NamedValue).init(self.allocator); self.named_values = std.StringHashMap(NamedValue).init(self.allocator);
// Register with correct types (null return_type = void) // Register with correct types (null return_type = void)
try self.registerFnDeclAs(fd, fd.name); try self.registerFnDecl(fd, fd.name);
// Generate body inline // Generate body inline
const ret_sx_type = self.resolveType(fd.return_type); const ret_sx_type = self.resolveType(fd.return_type);
@@ -2115,30 +2123,9 @@ pub const CodeGen = struct {
// Evaluate return value first, then emit all defers, then return // Evaluate return value first, then emit all defers, then return
if (rs.value) |val_node| { if (rs.value) |val_node| {
const raw_val = try self.genExpr(val_node); const raw_val = try self.genExpr(val_node);
if (self.current_return_type.isStruct()) { const ret_val = try self.prepareReturnValue(raw_val, self.current_return_type);
// Struct return: raw_val is an alloca pointer, load the value
const sname = self.current_return_type.struct_type;
const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname});
const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval");
try self.emitAllDefers();
_ = c.LLVMBuildRet(self.builder, loaded);
} else if (self.current_return_type.isUnion()) {
// Tagged enum return: raw_val may be alloca (enum literal) or loaded value (identifier/call)
const uname = self.current_return_type.union_type;
const resolved = self.type_aliases.get(uname) orelse uname;
const info = self.tagged_enum_types.get(resolved) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved});
const ret_val = if (c.LLVMGetTypeKind(c.LLVMTypeOf(raw_val)) == c.LLVMPointerTypeKind)
c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval")
else
raw_val;
try self.emitAllDefers(); try self.emitAllDefers();
_ = c.LLVMBuildRet(self.builder, ret_val); _ = c.LLVMBuildRet(self.builder, ret_val);
} else {
const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val));
const val = self.convertValue(raw_val, src_ty, self.current_return_type);
try self.emitAllDefers();
_ = c.LLVMBuildRet(self.builder, val);
}
} else { } else {
try self.emitAllDefers(); try self.emitAllDefers();
_ = c.LLVMBuildRetVoid(self.builder); _ = c.LLVMBuildRetVoid(self.builder);
@@ -2225,7 +2212,7 @@ pub const CodeGen = struct {
// Resolve type aliases (e.g. Vec3 -> Vec__3_f32) // 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.type_aliases.get(sx_ty.struct_type) orelse sx_ty.struct_type;
sx_ty = .{ .struct_type = sname }; sx_ty = .{ .struct_type = sname };
const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); const info = try self.getStructInfo(sname);
const name_z = try self.allocator.dupeZ(u8, vd.name); const name_z = try self.allocator.dupeZ(u8, vd.name);
const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr); const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr);
@@ -2280,7 +2267,7 @@ pub const CodeGen = struct {
} }
// Tagged enum // Tagged enum
const info = self.tagged_enum_types.get(uname) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{uname}); const info = try self.getTaggedEnumInfo(uname);
const name_z = try self.allocator.dupeZ(u8, vd.name); const name_z = try self.allocator.dupeZ(u8, vd.name);
const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr); const alloca = self.buildEntryBlockAlloca(info.llvm_type, name_z.ptr);
@@ -2339,8 +2326,8 @@ pub const CodeGen = struct {
for (0..len) |i| { for (0..len) |i| {
const val = try self.genExprAsType(al.elements[i], elem_sx_ty); const val = try self.genExprAsType(al.elements[i], elem_sx_ty);
var indices = [_]c.LLVMValueRef{ var indices = [_]c.LLVMValueRef{
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), self.constInt32(0),
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), self.constInt32(@intCast(i)),
}; };
const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "arr_elem"); const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "arr_elem");
_ = c.LLVMBuildStore(self.builder, val, gep); _ = c.LLVMBuildStore(self.builder, val, gep);
@@ -2348,8 +2335,8 @@ pub const CodeGen = struct {
// Zero-init remaining elements // Zero-init remaining elements
for (len..arr_info.length) |i| { for (len..arr_info.length) |i| {
var indices = [_]c.LLVMValueRef{ var indices = [_]c.LLVMValueRef{
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), self.constInt32(0),
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), self.constInt32(@intCast(i)),
}; };
const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "arr_elem"); 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); _ = c.LLVMBuildStore(self.builder, c.LLVMConstNull(elem_llvm_ty), gep);
@@ -2656,8 +2643,8 @@ pub const CodeGen = struct {
return self.emitError("unknown pointee type for field assignment"); return self.emitError("unknown pointee type for field assignment");
if (pointee_ty.isStruct()) { if (pointee_ty.isStruct()) {
const sname = pointee_ty.struct_type; 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 fi = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const fi = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname });
const field_ty = info.field_types[fi]; const field_ty = info.field_types[fi];
const loaded_ptr = c.LLVMBuildLoad2(self.builder, const loaded_ptr = c.LLVMBuildLoad2(self.builder,
c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load");
@@ -2679,7 +2666,7 @@ pub const CodeGen = struct {
if (entry.ty.isUnion()) { if (entry.ty.isUnion()) {
const uname = entry.ty.union_type; const uname = entry.ty.union_type;
if (self.union_types.get(uname)) |info| { if (self.union_types.get(uname)) |info| {
if (self.findUnionFieldIndex(info, fa.field)) |fidx| { if (self.findNameIndex(info.field_names, fa.field)) |fidx| {
const field_ty = info.field_types[fidx]; const field_ty = info.field_types[fidx];
const rhs = try self.genExprAsType(asgn.value, field_ty); const rhs = try self.genExprAsType(asgn.value, field_ty);
if (asgn.op == .assign) { if (asgn.op == .assign) {
@@ -2714,8 +2701,8 @@ pub const CodeGen = struct {
if (!entry.ty.isStruct()) return self.emitErrorFmt("field access on non-struct variable '{s}'", .{obj_name}); if (!entry.ty.isStruct()) return self.emitErrorFmt("field access on non-struct variable '{s}'", .{obj_name});
const sname = entry.ty.struct_type; const sname = entry.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 fi = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const fi = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname });
const field_ty = info.field_types[fi]; const field_ty = info.field_types[fi];
const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(fi), "fassign"); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(fi), "fassign");
@@ -2754,7 +2741,7 @@ pub const CodeGen = struct {
if (self.named_values.get(ie.object.data.identifier.name)) |entry| { if (self.named_values.get(ie.object.data.identifier.name)) |entry| {
const idx = try self.genExpr(ie.index); const idx = try self.genExpr(ie.index);
const val = try self.genExpr(asgn.value); const val = try self.genExpr(asgn.value);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
var indices = [_]c.LLVMValueRef{ zero, idx }; 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 = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "arridx");
_ = c.LLVMBuildStore(self.builder, val, gep_ptr); _ = c.LLVMBuildStore(self.builder, val, gep_ptr);
@@ -2766,7 +2753,7 @@ pub const CodeGen = struct {
const field_ptr = try self.genAddressOf(ie.object); const field_ptr = try self.genAddressOf(ie.object);
const idx = try self.genExpr(ie.index); const idx = try self.genExpr(ie.index);
const val = try self.genExpr(asgn.value); const val = try self.genExpr(asgn.value);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
var indices = [_]c.LLVMValueRef{ zero, idx }; 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 = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx");
_ = c.LLVMBuildStore(self.builder, val, gep_ptr); _ = c.LLVMBuildStore(self.builder, val, gep_ptr);
@@ -2809,50 +2796,6 @@ pub const CodeGen = struct {
return self.emitError("index assignment requires a string, array, slice, or [*] pointer target"); return self.emitError("index assignment requires a string, array, slice, or [*] pointer target");
} }
fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 {
var result = try allocator.alloc(u8, raw.len);
var i: usize = 0;
var j: usize = 0;
while (i < raw.len) {
if (raw[i] == '\\' and i + 1 < raw.len) {
i += 1;
switch (raw[i]) {
'n' => {
result[j] = '\n';
},
't' => {
result[j] = '\t';
},
'r' => {
result[j] = '\r';
},
'\\' => {
result[j] = '\\';
},
'"' => {
result[j] = '"';
},
'0' => {
result[j] = 0;
},
'`' => {
result[j] = '`';
},
else => {
result[j] = raw[i];
},
}
j += 1;
i += 1;
} else {
result[j] = raw[i];
j += 1;
i += 1;
}
}
return result[0..j];
}
fn genExpr(self: *CodeGen, node: *Node) anyerror!c.LLVMValueRef { fn genExpr(self: *CodeGen, node: *Node) anyerror!c.LLVMValueRef {
self.current_span = node.span; self.current_span = node.span;
switch (node.data) { switch (node.data) {
@@ -2869,10 +2812,10 @@ pub const CodeGen = struct {
return c.LLVMConstInt(i1_type, if (lit.value) 1 else 0, 0); return c.LLVMConstInt(i1_type, if (lit.value) 1 else 0, 0);
}, },
.string_literal => |lit| { .string_literal => |lit| {
const content = if (lit.is_raw) lit.raw else try unescapeString(self.allocator, lit.raw); const content = if (lit.is_raw) lit.raw else try unescape.unescapeString(self.allocator, lit.raw);
const str_z = try self.allocator.dupeZ(u8, content); const str_z = try self.allocator.dupeZ(u8, content);
const ptr = c.LLVMBuildGlobalStringPtr(self.builder, str_z.ptr, "str"); const ptr = c.LLVMBuildGlobalStringPtr(self.builder, str_z.ptr, "str");
return self.buildStringSlice(ptr, @intCast(content.len)); return self.buildStringSlice(ptr, self.constInt64(@intCast(content.len)));
}, },
.identifier => |ident| { .identifier => |ident| {
if (self.named_values.get(ident.name)) |entry| { if (self.named_values.get(ident.name)) |entry| {
@@ -2909,8 +2852,8 @@ pub const CodeGen = struct {
return self.emitErrorFmt("undefined identifier '{s}'", .{ident.name}); return self.emitErrorFmt("undefined identifier '{s}'", .{ident.name});
}, },
.binary_op => |binop| { .binary_op => |binop| {
if (binop.op == .and_op) return self.genAndOp(binop); if (binop.op == .and_op) return self.genShortCircuitOp(binop, true);
if (binop.op == .or_op) return self.genOrOp(binop); if (binop.op == .or_op) return self.genShortCircuitOp(binop, false);
const lhs_ty = self.inferType(binop.lhs); const lhs_ty = self.inferType(binop.lhs);
const rhs_ty = self.inferType(binop.rhs); const rhs_ty = self.inferType(binop.rhs);
const result_type = Type.widen(lhs_ty, rhs_ty); const result_type = Type.widen(lhs_ty, rhs_ty);
@@ -3066,29 +3009,9 @@ pub const CodeGen = struct {
.return_stmt => |rs| { .return_stmt => |rs| {
if (rs.value) |val_node| { if (rs.value) |val_node| {
const raw_val = try self.genExpr(val_node); const raw_val = try self.genExpr(val_node);
if (self.current_return_type.isStruct()) { const ret_val = try self.prepareReturnValue(raw_val, self.current_return_type);
const sname = self.current_return_type.struct_type;
const resolved = self.type_aliases.get(sname) orelse sname;
const sinfo = self.struct_types.get(resolved) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{resolved});
const loaded = c.LLVMBuildLoad2(self.builder, sinfo.llvm_type, raw_val, "retval");
try self.emitAllDefers();
_ = c.LLVMBuildRet(self.builder, loaded);
} else if (self.current_return_type.isUnion()) {
const uname = self.current_return_type.union_type;
const resolved = self.type_aliases.get(uname) orelse uname;
const info = self.tagged_enum_types.get(resolved) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved});
const ret_val = if (c.LLVMGetTypeKind(c.LLVMTypeOf(raw_val)) == c.LLVMPointerTypeKind)
c.LLVMBuildLoad2(self.builder, info.llvm_type, raw_val, "retval")
else
raw_val;
try self.emitAllDefers(); try self.emitAllDefers();
_ = c.LLVMBuildRet(self.builder, ret_val); _ = c.LLVMBuildRet(self.builder, ret_val);
} else {
const src_ty = self.llvmTypeToSxType(c.LLVMTypeOf(raw_val));
const val = self.convertValue(raw_val, src_ty, self.current_return_type);
try self.emitAllDefers();
_ = c.LLVMBuildRet(self.builder, val);
}
} else { } else {
try self.emitAllDefers(); try self.emitAllDefers();
_ = c.LLVMBuildRetVoid(self.builder); _ = c.LLVMBuildRetVoid(self.builder);
@@ -3133,7 +3056,7 @@ pub const CodeGen = struct {
if (obj_ty.isArray()) { if (obj_ty.isArray()) {
if (ie.object.data == .identifier) { if (ie.object.data == .identifier) {
if (self.named_values.get(ie.object.data.identifier.name)) |entry| { if (self.named_values.get(ie.object.data.identifier.name)) |entry| {
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
var indices = [_]c.LLVMValueRef{ zero, idx }; var indices = [_]c.LLVMValueRef{ zero, idx };
return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "addr_elem"); return c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "addr_elem");
} }
@@ -3157,14 +3080,14 @@ pub const CodeGen = struct {
if (self.named_values.get(fa.object.data.identifier.name)) |entry| { if (self.named_values.get(fa.object.data.identifier.name)) |entry| {
if (entry.ty.isStruct()) { if (entry.ty.isStruct()) {
const sname = entry.ty.struct_type; const sname = entry.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.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname });
return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, entry.ptr, @intCast(idx), "addr_field"); 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 // &u.field where u is a C-style union — all fields at offset 0
if (entry.ty.isUnion()) { if (entry.ty.isUnion()) {
if (self.union_types.get(entry.ty.union_type)) |info| { if (self.union_types.get(entry.ty.union_type)) |info| {
if (self.findUnionFieldIndex(info, fa.field) != null) { if (self.findNameIndex(info.field_names, fa.field) != null) {
return entry.ptr; return entry.ptr;
} }
if (info.promoted_fields.get(fa.field)) |pf| { if (info.promoted_fields.get(fa.field)) |pf| {
@@ -3180,7 +3103,7 @@ pub const CodeGen = struct {
if (self.struct_types.get(pointee_name)) |info| { if (self.struct_types.get(pointee_name)) |info| {
const loaded_ptr = c.LLVMBuildLoad2(self.builder, const loaded_ptr = c.LLVMBuildLoad2(self.builder,
c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load"); c.LLVMPointerTypeInContext(self.context, 0), entry.ptr, "ptr_load");
const idx = self.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, pointee_name }); const idx = self.findNameIndex(info.field_names, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, pointee_name });
return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "addr_pfield"); return c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, @intCast(idx), "addr_pfield");
} }
} }
@@ -3571,7 +3494,7 @@ pub const CodeGen = struct {
(if (self.current_return_type.isUnion()) self.current_return_type.union_type else null) 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"); return self.emitError("cannot infer enum type for literal");
const resolved_name = self.type_aliases.get(uname) orelse uname; const resolved_name = self.type_aliases.get(uname) orelse uname;
const info = self.tagged_enum_types.get(resolved_name) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{resolved_name}); const info = try self.getTaggedEnumInfo(resolved_name);
// Find variant index // Find variant index
var variant_idx: ?u32 = null; var variant_idx: ?u32 = null;
@@ -3616,7 +3539,7 @@ pub const CodeGen = struct {
}; };
// Resolve type aliases (e.g. Vec3 -> Vec__3_f32) // Resolve type aliases (e.g. Vec3 -> Vec__3_f32)
const sname = self.type_aliases.get(raw_name) orelse raw_name; const sname = self.type_aliases.get(raw_name) orelse raw_name;
const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); const info = try self.getStructInfo(sname);
// Alloca the struct and default-init all fields (zero or declared defaults) // Alloca the struct and default-init all fields (zero or declared defaults)
const name_z = try self.allocator.dupeZ(u8, sname); const name_z = try self.allocator.dupeZ(u8, sname);
@@ -3638,7 +3561,7 @@ pub const CodeGen = struct {
const fname = fi.name orelse { const fname = fi.name orelse {
// Positional field mixed with named — treat as identifier shorthand // Positional field mixed with named — treat as identifier shorthand
if (fi.value.data == .identifier) { if (fi.value.data == .identifier) {
const idx = self.findFieldIndex(info, fi.value.data.identifier.name) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fi.value.data.identifier.name, sname }); 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 val = try self.genExprAsType(fi.value, info.field_types[idx]); const val = try self.genExprAsType(fi.value, info.field_types[idx]);
const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field"); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field");
_ = c.LLVMBuildStore(self.builder, val, gep); _ = c.LLVMBuildStore(self.builder, val, gep);
@@ -3646,7 +3569,7 @@ pub const CodeGen = struct {
} }
return self.emitError("mixed positional and named fields in struct literal"); return self.emitError("mixed positional and named fields in struct literal");
}; };
const idx = self.findFieldIndex(info, fname) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fname, sname }); const idx = self.findNameIndex(info.field_names, fname) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fname, sname });
const val = try self.genExprAsType(fi.value, info.field_types[idx]); const val = try self.genExprAsType(fi.value, info.field_types[idx]);
const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field"); const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, alloca, @intCast(idx), "field");
_ = c.LLVMBuildStore(self.builder, val, gep); _ = c.LLVMBuildStore(self.builder, val, gep);
@@ -3684,8 +3607,8 @@ pub const CodeGen = struct {
for (0..len) |i| { for (0..len) |i| {
const val = try self.genExprAsType(al.elements[i], elem_sx_ty); const val = try self.genExprAsType(al.elements[i], elem_sx_ty);
var indices = [_]c.LLVMValueRef{ var indices = [_]c.LLVMValueRef{
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), self.constInt32(0),
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), self.constInt32(@intCast(i)),
}; };
const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, alloca, &indices, 2, "arr_elem"); const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, alloca, &indices, 2, "arr_elem");
_ = c.LLVMBuildStore(self.builder, val, gep); _ = c.LLVMBuildStore(self.builder, val, gep);
@@ -3707,8 +3630,8 @@ pub const CodeGen = struct {
for (0..n) |i| { for (0..n) |i| {
const val = try self.genExprAsType(al.elements[i], elem_sx_ty); const val = try self.genExprAsType(al.elements[i], elem_sx_ty);
var indices = [_]c.LLVMValueRef{ var indices = [_]c.LLVMValueRef{
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), self.constInt32(0),
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), self.constInt32(@intCast(i)),
}; };
const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "slice_elem"); const gep = c.LLVMBuildGEP2(self.builder, llvm_arr_ty, arr_alloca, &indices, 2, "slice_elem");
_ = c.LLVMBuildStore(self.builder, val, gep); _ = c.LLVMBuildStore(self.builder, val, gep);
@@ -3736,7 +3659,7 @@ pub const CodeGen = struct {
const len = @min(al.elements.len, vec_info.length); const len = @min(al.elements.len, vec_info.length);
for (0..len) |i| { for (0..len) |i| {
const elem_val = try self.genExprAsType(al.elements[i], elem_sx_ty); const elem_val = try self.genExprAsType(al.elements[i], elem_sx_ty);
const idx = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0); const idx = self.constInt32(@intCast(i));
vec_val = c.LLVMBuildInsertElement(self.builder, vec_val, elem_val, idx, "vec_ins"); vec_val = c.LLVMBuildInsertElement(self.builder, vec_val, elem_val, idx, "vec_ins");
} }
return vec_val; return vec_val;
@@ -3747,7 +3670,7 @@ pub const CodeGen = struct {
const llvm_vec_ty = self.typeToLLVM(vec_ty); const llvm_vec_ty = self.typeToLLVM(vec_ty);
// Insert scalar at index 0 of undef vector // Insert scalar at index 0 of undef vector
var vec = c.LLVMGetUndef(llvm_vec_ty); var vec = c.LLVMGetUndef(llvm_vec_ty);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
vec = c.LLVMBuildInsertElement(self.builder, vec, scalar, zero, "splat_ins"); vec = c.LLVMBuildInsertElement(self.builder, vec, scalar, zero, "splat_ins");
// Shuffle with zeroinitializer mask to broadcast element 0 to all lanes // 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(c.LLVMInt32TypeInContext(self.context), vec_info.length);
@@ -3773,7 +3696,7 @@ pub const CodeGen = struct {
// String literal → pointer context: produce raw pointer directly (no {ptr, len} wrapping) // String literal → pointer context: produce raw pointer directly (no {ptr, len} wrapping)
if (node.data == .string_literal and target_ty.isPointer()) { if (node.data == .string_literal and target_ty.isPointer()) {
const lit = node.data.string_literal; const lit = node.data.string_literal;
const content = if (lit.is_raw) lit.raw else try unescapeString(self.allocator, lit.raw); const content = if (lit.is_raw) lit.raw else try unescape.unescapeString(self.allocator, lit.raw);
const str_z = try self.allocator.dupeZ(u8, content); const str_z = try self.allocator.dupeZ(u8, content);
return c.LLVMBuildGlobalStringPtr(self.builder, str_z.ptr, "str"); return c.LLVMBuildGlobalStringPtr(self.builder, str_z.ptr, "str");
} }
@@ -4197,16 +4120,9 @@ pub const CodeGen = struct {
return val; return val;
} }
fn findFieldIndex(_: *CodeGen, info: StructInfo, name: []const u8) ?usize { fn findNameIndex(_: *CodeGen, names: []const []const u8, name: []const u8) ?usize {
for (info.field_names, 0..) |fn_name, i| { for (names, 0..) |n, i| {
if (std.mem.eql(u8, fn_name, name)) return i; if (std.mem.eql(u8, n, name)) return i;
}
return null;
}
fn findUnionFieldIndex(_: *CodeGen, info: UnionInfo, name: []const u8) ?usize {
for (info.field_names, 0..) |fn_name, i| {
if (std.mem.eql(u8, fn_name, name)) return i;
} }
return null; return null;
} }
@@ -4221,47 +4137,14 @@ pub const CodeGen = struct {
}; };
} }
fn genSqrt(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { fn genMathIntrinsic(self: *CodeGen, call_node: ast.Call, comptime name: []const u8) !c.LLVMValueRef {
if (call_node.args.len != 1) return self.emitError("sqrt expects exactly 1 argument"); if (call_node.args.len != 1) return self.emitError(name ++ " expects exactly 1 argument");
const arg_val = try self.genExpr(call_node.args[0]); const arg_val = try self.genExpr(call_node.args[0]);
const arg_ty = self.inferType(call_node.args[0]); const arg_ty = self.inferType(call_node.args[0]);
// Pick the right LLVM intrinsic based on float type const is_f64 = std.meta.eql(arg_ty, Type.f64);
const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.sqrt.f64" else "llvm.sqrt.f32"; const intrinsic_name: [*c]const u8 = if (is_f64) "llvm." ++ name ++ ".f64" else "llvm." ++ name ++ ".f32";
const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64)) const llvm_float_ty = if (is_f64) c.LLVMDoubleTypeInContext(self.context) else c.LLVMFloatTypeInContext(self.context);
c.LLVMDoubleTypeInContext(self.context)
else
c.LLVMFloatTypeInContext(self.context);
// Get or declare the intrinsic
var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name);
if (intrinsic_fn == null) {
var param_types = [_]c.LLVMTypeRef{llvm_float_ty};
const fn_type = c.LLVMFunctionType(llvm_float_ty, &param_types, 1, 0);
intrinsic_fn = c.LLVMAddFunction(self.module, intrinsic_name, fn_type);
}
var args = [_]c.LLVMValueRef{arg_val};
return c.LLVMBuildCall2(
self.builder,
c.LLVMGlobalGetValueType(intrinsic_fn.?),
intrinsic_fn.?,
&args,
1,
"sqrt",
);
}
fn genSin(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
if (call_node.args.len != 1) return self.emitError("sin expects exactly 1 argument");
const arg_val = try self.genExpr(call_node.args[0]);
const arg_ty = self.inferType(call_node.args[0]);
const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.sin.f64" else "llvm.sin.f32";
const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64))
c.LLVMDoubleTypeInContext(self.context)
else
c.LLVMFloatTypeInContext(self.context);
var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name); var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name);
if (intrinsic_fn == null) { if (intrinsic_fn == null) {
@@ -4271,29 +4154,7 @@ pub const CodeGen = struct {
} }
var args = [_]c.LLVMValueRef{arg_val}; var args = [_]c.LLVMValueRef{arg_val};
return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, "sin"); return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, name.ptr);
}
fn genCos(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
if (call_node.args.len != 1) return self.emitError("cos expects exactly 1 argument");
const arg_val = try self.genExpr(call_node.args[0]);
const arg_ty = self.inferType(call_node.args[0]);
const intrinsic_name: [*c]const u8 = if (std.meta.eql(arg_ty, Type.f64)) "llvm.cos.f64" else "llvm.cos.f32";
const llvm_float_ty = if (std.meta.eql(arg_ty, Type.f64))
c.LLVMDoubleTypeInContext(self.context)
else
c.LLVMFloatTypeInContext(self.context);
var intrinsic_fn = c.LLVMGetNamedFunction(self.module, intrinsic_name);
if (intrinsic_fn == null) {
var param_types = [_]c.LLVMTypeRef{llvm_float_ty};
const fn_type = c.LLVMFunctionType(llvm_float_ty, &param_types, 1, 0);
intrinsic_fn = c.LLVMAddFunction(self.module, intrinsic_name, fn_type);
}
var args = [_]c.LLVMValueRef{arg_val};
return c.LLVMBuildCall2(self.builder, c.LLVMGlobalGetValueType(intrinsic_fn.?), intrinsic_fn.?, &args, 1, "cos");
} }
fn genSizeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef { fn genSizeOf(self: *CodeGen, call_node: ast.Call) !c.LLVMValueRef {
@@ -4413,7 +4274,7 @@ pub const CodeGen = struct {
// GEP into the array with runtime index // GEP into the array with runtime index
const idx = try self.genExpr(call_node.args[1]); const idx = try self.genExpr(call_node.args[1]);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
var indices = [_]c.LLVMValueRef{ zero, idx }; var indices = [_]c.LLVMValueRef{ zero, idx };
const elem_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, global, &indices, 2, "field_name_ptr"); const elem_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, global, &indices, 2, "field_name_ptr");
return c.LLVMBuildLoad2(self.builder, str_ty, elem_ptr, "field_name"); return c.LLVMBuildLoad2(self.builder, str_ty, elem_ptr, "field_name");
@@ -4525,7 +4386,7 @@ pub const CodeGen = struct {
_ = c.LLVMBuildStore(self.builder, val, arr_alloca); _ = c.LLVMBuildStore(self.builder, val, arr_alloca);
const idx = try self.genExpr(call_node.args[1]); const idx = try self.genExpr(call_node.args[1]);
var gep_indices = [_]c.LLVMValueRef{ var gep_indices = [_]c.LLVMValueRef{
c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), self.constInt32(0),
idx, idx,
}; };
const elem_ptr = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, arr_alloca, &gep_indices, 2, "fv_aelem"); const elem_ptr = c.LLVMBuildGEP2(self.builder, arr_llvm_ty, arr_alloca, &gep_indices, 2, "fv_aelem");
@@ -4731,7 +4592,7 @@ pub const CodeGen = struct {
var calloc_args = [_]c.LLVMValueRef{ size_plus_one, one_i64 }; var calloc_args = [_]c.LLVMValueRef{ size_plus_one, one_i64 };
const ptr = c.LLVMBuildCall2(self.builder, calloc_ty, calloc_fn, &calloc_args, 2, "alloc_ptr"); const ptr = c.LLVMBuildCall2(self.builder, calloc_ty, calloc_fn, &calloc_args, 2, "alloc_ptr");
// Build string slice: {ptr, size} // Build string slice: {ptr, size}
return self.buildStringSliceRT(ptr, size_val); return self.buildStringSlice(ptr, size_val);
} }
fn genMalloc(self: *CodeGen, args: []const *Node) !c.LLVMValueRef { fn genMalloc(self: *CodeGen, args: []const *Node) !c.LLVMValueRef {
@@ -4768,7 +4629,7 @@ pub const CodeGen = struct {
fn genVectorExtract(self: *CodeGen, vec_val: c.LLVMValueRef, field: []const u8) !c.LLVMValueRef { fn genVectorExtract(self: *CodeGen, vec_val: c.LLVMValueRef, field: []const u8) !c.LLVMValueRef {
if (field.len == 1) { if (field.len == 1) {
const idx_val = componentToIndex(field[0]) orelse return self.emitErrorFmt("invalid vector component '{c}'", .{field[0]}); const idx_val = componentToIndex(field[0]) orelse return self.emitErrorFmt("invalid vector component '{c}'", .{field[0]});
const idx = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), idx_val, 0); const idx = self.constInt32(idx_val);
return c.LLVMBuildExtractElement(self.builder, vec_val, idx, "comp"); return c.LLVMBuildExtractElement(self.builder, vec_val, idx, "comp");
} }
return self.emitErrorFmt("unsupported vector swizzle '{s}'", .{field}); return self.emitErrorFmt("unsupported vector swizzle '{s}'", .{field});
@@ -4788,7 +4649,7 @@ pub const CodeGen = struct {
const sname = pointee_ty.struct_type; const sname = pointee_ty.struct_type;
const info = self.struct_types.get(sname) orelse const info = self.struct_types.get(sname) orelse
return self.emitErrorFmt("unknown struct type '{s}'", .{sname}); return self.emitErrorFmt("unknown struct type '{s}'", .{sname});
const idx = self.findFieldIndex(info, fa.field) orelse const idx = self.findNameIndex(info.field_names, fa.field) orelse
return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname }); return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, sname });
const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr, const gep = c.LLVMBuildStructGEP2(self.builder, info.llvm_type, loaded_ptr,
@intCast(idx), "pfield"); @intCast(idx), "pfield");
@@ -4796,20 +4657,14 @@ pub const CodeGen = struct {
} }
if (pointee_ty.isSlice()) { if (pointee_ty.isSlice()) {
const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), loaded_ptr, "pslice_load"); const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), loaded_ptr, "pslice_load");
if (std.mem.eql(u8, fa.field, "len")) { return self.extractFatPtrField(slice_val, fa.field, "*slice");
return c.LLVMBuildExtractValue(self.builder, slice_val, 1, "pslice_len");
}
if (std.mem.eql(u8, fa.field, "ptr")) {
return c.LLVMBuildExtractValue(self.builder, slice_val, 0, "pslice_ptr");
}
return self.emitErrorFmt("no field '{s}' on *slice (available: .len, .ptr)", .{fa.field});
} }
return self.emitErrorFmt("no field '{s}' on pointer", .{fa.field}); return self.emitErrorFmt("no field '{s}' on pointer", .{fa.field});
} }
if (entry.ty.isStruct()) { if (entry.ty.isStruct()) {
const sname = entry.ty.struct_type; const sname = entry.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.findFieldIndex(info, fa.field) orelse return self.emitErrorFmt("no field '{s}' in struct '{s}'", .{ fa.field, 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"); 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"); return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(info.field_types[idx]), gep, "fieldval");
} }
@@ -4817,7 +4672,7 @@ pub const CodeGen = struct {
const uname = entry.ty.union_type; const uname = entry.ty.union_type;
// C-style (untagged) union: bitcast pointer and load // C-style (untagged) union: bitcast pointer and load
if (self.union_types.get(uname)) |info| { if (self.union_types.get(uname)) |info| {
if (self.findUnionFieldIndex(info, fa.field)) |fidx| { if (self.findNameIndex(info.field_names, fa.field)) |fidx| {
const field_ty = info.field_types[fidx]; const field_ty = info.field_types[fidx];
return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(field_ty), entry.ptr, "union_field"); return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(field_ty), entry.ptr, "union_field");
} }
@@ -4832,7 +4687,7 @@ pub const CodeGen = struct {
return self.emitErrorFmt("no field '{s}' in union '{s}'", .{ fa.field, uname }); return self.emitErrorFmt("no field '{s}' in union '{s}'", .{ fa.field, uname });
} }
// Tagged enum: GEP to payload area // Tagged enum: GEP to payload area
const info = self.tagged_enum_types.get(uname) orelse return self.emitErrorFmt("unknown enum type '{s}'", .{uname}); const info = try self.getTaggedEnumInfo(uname);
// Find variant by name to determine payload type // Find variant by name to determine payload type
var vidx: ?usize = null; var vidx: ?usize = null;
for (info.variant_names, 0..) |vn, i| { for (info.variant_names, 0..) |vn, i| {
@@ -4854,23 +4709,11 @@ pub const CodeGen = struct {
} }
if (entry.ty == .string_type) { if (entry.ty == .string_type) {
const str_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "str_load"); const str_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "str_load");
if (std.mem.eql(u8, fa.field, "len")) { return self.extractFatPtrField(str_val, fa.field, "string");
return c.LLVMBuildExtractValue(self.builder, str_val, 1, "str_len");
}
if (std.mem.eql(u8, fa.field, "ptr")) {
return c.LLVMBuildExtractValue(self.builder, str_val, 0, "str_ptr");
}
return self.emitErrorFmt("no field '{s}' on string (available: .len, .ptr)", .{fa.field});
} }
if (entry.ty.isSlice()) { if (entry.ty.isSlice()) {
const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "slice_load"); const slice_val = c.LLVMBuildLoad2(self.builder, self.getStringStructType(), entry.ptr, "slice_load");
if (std.mem.eql(u8, fa.field, "len")) { return self.extractFatPtrField(slice_val, fa.field, "slice");
return c.LLVMBuildExtractValue(self.builder, slice_val, 1, "slice_len");
}
if (std.mem.eql(u8, fa.field, "ptr")) {
return c.LLVMBuildExtractValue(self.builder, slice_val, 0, "slice_ptr");
}
return self.emitErrorFmt("no field '{s}' on slice (available: .len, .ptr)", .{fa.field});
} }
if (entry.ty.isArray()) { if (entry.ty.isArray()) {
if (std.mem.eql(u8, fa.field, "len")) { if (std.mem.eql(u8, fa.field, "len")) {
@@ -4897,13 +4740,7 @@ pub const CodeGen = struct {
return self.genVectorExtract(obj_val, fa.field); return self.genVectorExtract(obj_val, fa.field);
} }
if (obj_ty == .string_type) { if (obj_ty == .string_type) {
if (std.mem.eql(u8, fa.field, "len")) { return self.extractFatPtrField(obj_val, fa.field, "string");
return c.LLVMBuildExtractValue(self.builder, obj_val, 1, "str_len");
}
if (std.mem.eql(u8, fa.field, "ptr")) {
return c.LLVMBuildExtractValue(self.builder, obj_val, 0, "str_ptr");
}
return self.emitErrorFmt("no field '{s}' on string (available: .len, .ptr)", .{fa.field});
} }
return self.emitError("field access on non-struct/non-vector expression"); return self.emitError("field access on non-struct/non-vector expression");
} }
@@ -4915,9 +4752,9 @@ pub const CodeGen = struct {
else else
(if (op == .eq) c.LLVMBuildICmp(self.builder, c.LLVMIntEQ, lhs, rhs, "vcmp") else c.LLVMBuildICmp(self.builder, c.LLVMIntNE, lhs, rhs, "vcmp")); (if (op == .eq) c.LLVMBuildICmp(self.builder, c.LLVMIntEQ, lhs, rhs, "vcmp") else c.LLVMBuildICmp(self.builder, c.LLVMIntNE, lhs, rhs, "vcmp"));
// Reduce: extract each i1 and AND (eq) or OR (neq) // Reduce: extract each i1 and AND (eq) or OR (neq)
var result = c.LLVMBuildExtractElement(self.builder, cmp, c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0), "cmp0"); var result = c.LLVMBuildExtractElement(self.builder, cmp, self.constInt32(0), "cmp0");
for (1..vec_info.length) |i| { for (1..vec_info.length) |i| {
const elem = c.LLVMBuildExtractElement(self.builder, cmp, c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(i), 0), "cmpi"); const elem = c.LLVMBuildExtractElement(self.builder, cmp, self.constInt32(@intCast(i)), "cmpi");
result = if (op == .eq) result = if (op == .eq)
c.LLVMBuildAnd(self.builder, result, elem, "andcmp") c.LLVMBuildAnd(self.builder, result, elem, "andcmp")
else else
@@ -4940,7 +4777,7 @@ pub const CodeGen = struct {
if (ie.object.data == .identifier) { if (ie.object.data == .identifier) {
if (self.named_values.get(ie.object.data.identifier.name)) |entry| { if (self.named_values.get(ie.object.data.identifier.name)) |entry| {
const idx = try self.genExpr(ie.index); const idx = try self.genExpr(ie.index);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
var indices = [_]c.LLVMValueRef{ zero, idx }; var indices = [_]c.LLVMValueRef{ zero, idx };
const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), entry.ptr, &indices, 2, "arridx"); 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"); return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "arrval");
@@ -4950,7 +4787,7 @@ pub const CodeGen = struct {
if (ie.object.data == .field_access) { if (ie.object.data == .field_access) {
const field_ptr = try self.genAddressOf(ie.object); const field_ptr = try self.genAddressOf(ie.object);
const idx = try self.genExpr(ie.index); const idx = try self.genExpr(ie.index);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
var indices = [_]c.LLVMValueRef{ zero, idx }; var indices = [_]c.LLVMValueRef{ zero, idx };
const gep = c.LLVMBuildGEP2(self.builder, self.typeToLLVM(obj_ty), field_ptr, &indices, 2, "field_arridx"); 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"); return c.LLVMBuildLoad2(self.builder, self.typeToLLVM(elem_ty), gep, "field_arrval");
@@ -5104,7 +4941,7 @@ pub const CodeGen = struct {
}; };
} }
fn genAndOp(self: *CodeGen, binop: ast.BinaryOp) !c.LLVMValueRef { fn genShortCircuitOp(self: *CodeGen, binop: ast.BinaryOp, is_and: bool) !c.LLVMValueRef {
const function = self.current_function; const function = self.current_function;
const i1_type = c.LLVMInt1TypeInContext(self.context); const i1_type = c.LLVMInt1TypeInContext(self.context);
@@ -5114,41 +4951,16 @@ pub const CodeGen = struct {
} }
const lhs_bb = c.LLVMGetInsertBlock(self.builder); const lhs_bb = c.LLVMGetInsertBlock(self.builder);
const rhs_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "and.rhs"); const rhs_label: [*c]const u8 = if (is_and) "and.rhs" else "or.rhs";
const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "and.merge"); const merge_label: [*c]const u8 = if (is_and) "and.merge" else "or.merge";
const rhs_bb = c.LLVMAppendBasicBlockInContext(self.context, function, rhs_label);
_ = c.LLVMBuildCondBr(self.builder, lhs_val, rhs_bb, merge_bb); const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, merge_label);
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_end_bb = c.LLVMGetInsertBlock(self.builder);
_ = c.LLVMBuildBr(self.builder, merge_bb);
c.LLVMPositionBuilderAtEnd(self.builder, merge_bb);
const phi = c.LLVMBuildPhi(self.builder, i1_type, "and.result");
var vals = [2]c.LLVMValueRef{ c.LLVMConstInt(i1_type, 0, 0), rhs_val };
var blocks = [2]c.LLVMBasicBlockRef{ lhs_bb, rhs_end_bb };
c.LLVMAddIncoming(phi, &vals, &blocks, 2);
return phi;
}
fn genOrOp(self: *CodeGen, binop: ast.BinaryOp) !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_bb = c.LLVMGetInsertBlock(self.builder);
const rhs_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "or.rhs");
const merge_bb = c.LLVMAppendBasicBlockInContext(self.context, function, "or.merge");
// AND: true → evaluate rhs, false → short-circuit to merge
// OR: true → short-circuit to merge, false → evaluate rhs
if (is_and)
_ = c.LLVMBuildCondBr(self.builder, lhs_val, rhs_bb, merge_bb)
else
_ = c.LLVMBuildCondBr(self.builder, lhs_val, merge_bb, rhs_bb); _ = c.LLVMBuildCondBr(self.builder, lhs_val, merge_bb, rhs_bb);
c.LLVMPositionBuilderAtEnd(self.builder, rhs_bb); c.LLVMPositionBuilderAtEnd(self.builder, rhs_bb);
@@ -5160,8 +4972,10 @@ pub const CodeGen = struct {
_ = c.LLVMBuildBr(self.builder, merge_bb); _ = c.LLVMBuildBr(self.builder, merge_bb);
c.LLVMPositionBuilderAtEnd(self.builder, merge_bb); c.LLVMPositionBuilderAtEnd(self.builder, merge_bb);
const phi = c.LLVMBuildPhi(self.builder, i1_type, "or.result"); const short_circuit_val: u64 = if (is_and) 0 else 1;
var vals = [2]c.LLVMValueRef{ c.LLVMConstInt(i1_type, 1, 0), rhs_val }; 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 };
var blocks = [2]c.LLVMBasicBlockRef{ lhs_bb, rhs_end_bb }; var blocks = [2]c.LLVMBasicBlockRef{ lhs_bb, rhs_end_bb };
c.LLVMAddIncoming(phi, &vals, &blocks, 2); c.LLVMAddIncoming(phi, &vals, &blocks, 2);
@@ -5285,13 +5099,13 @@ pub const CodeGen = struct {
// Compiler intrinsics (always available, no #builtin declaration needed) // Compiler intrinsics (always available, no #builtin declaration needed)
if (std.mem.eql(u8, callee_name, "sqrt")) { if (std.mem.eql(u8, callee_name, "sqrt")) {
return self.genSqrt(call_node); return self.genMathIntrinsic(call_node, "sqrt");
} }
if (std.mem.eql(u8, callee_name, "sin")) { if (std.mem.eql(u8, callee_name, "sin")) {
return self.genSin(call_node); return self.genMathIntrinsic(call_node, "sin");
} }
if (std.mem.eql(u8, callee_name, "cos")) { if (std.mem.eql(u8, callee_name, "cos")) {
return self.genCos(call_node); return self.genMathIntrinsic(call_node, "cos");
} }
if (std.mem.eql(u8, callee_name, "cast")) { if (std.mem.eql(u8, callee_name, "cast")) {
return self.genCast(call_node); return self.genCast(call_node);
@@ -5387,7 +5201,7 @@ pub const CodeGen = struct {
var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; 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 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 len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), arr_info.length, 0);
const slice_val = self.buildStringSliceRT(arr_ptr, len_val); const slice_val = self.buildStringSlice(arr_ptr, len_val);
try arg_vals.append(self.allocator, slice_val); try arg_vals.append(self.allocator, slice_val);
} else { } else {
return self.emitError("spread operand not found"); return self.emitError("spread operand not found");
@@ -5414,8 +5228,8 @@ pub const CodeGen = struct {
const arg_ty = self.inferType(call_node.args[fixed_count + vi_idx]); const arg_ty = self.inferType(call_node.args[fixed_count + vi_idx]);
break :blk try self.buildAnyValue(raw_val, arg_ty); break :blk try self.buildAnyValue(raw_val, arg_ty);
} else try self.genExprAsType(call_node.args[fixed_count + vi_idx], elem_ty); } else try self.genExprAsType(call_node.args[fixed_count + vi_idx], elem_ty);
const zero = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), 0, 0); const zero = self.constInt32(0);
const idx_val = c.LLVMConstInt(c.LLVMInt32TypeInContext(self.context), @intCast(vi_idx), 0); const idx_val = self.constInt32(@intCast(vi_idx));
var indices = [_]c.LLVMValueRef{ zero, idx_val }; var indices = [_]c.LLVMValueRef{ zero, idx_val };
const gep = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &indices, 2, "vararg_elem"); const gep = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &indices, 2, "vararg_elem");
_ = c.LLVMBuildStore(self.builder, arg_val, gep); _ = c.LLVMBuildStore(self.builder, arg_val, gep);
@@ -5425,13 +5239,13 @@ pub const CodeGen = struct {
var ptr_indices = [_]c.LLVMValueRef{ zero, zero }; var ptr_indices = [_]c.LLVMValueRef{ zero, zero };
const arr_ptr = c.LLVMBuildGEP2(self.builder, arr_ty, arr_alloca, &ptr_indices, 2, "varargs_ptr"); 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 len_val = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), @intCast(var_arg_count), 0);
const slice_val = self.buildStringSliceRT(arr_ptr, len_val); const slice_val = self.buildStringSlice(arr_ptr, len_val);
try arg_vals.append(self.allocator, slice_val); try arg_vals.append(self.allocator, slice_val);
} else { } else {
// Zero variadic args: pass empty slice {null, 0} // Zero variadic args: pass empty slice {null, 0}
const null_ptr = c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0)); const null_ptr = c.LLVMConstNull(c.LLVMPointerTypeInContext(self.context, 0));
const zero_len = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0); const zero_len = c.LLVMConstInt(c.LLVMInt64TypeInContext(self.context), 0, 0);
const slice_val = self.buildStringSliceRT(null_ptr, zero_len); const slice_val = self.buildStringSlice(null_ptr, zero_len);
try arg_vals.append(self.allocator, slice_val); try arg_vals.append(self.allocator, slice_val);
} }
} else { } else {
@@ -6008,7 +5822,7 @@ pub const CodeGen = struct {
break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_str"); break :blk c.LLVMBuildLoad2(self.builder, self.getStringStructType(), ptr, "any_to_str");
}, },
.struct_type => |sname| blk: { .struct_type => |sname| blk: {
const info = self.struct_types.get(sname) orelse return self.emitErrorFmt("unknown struct '{s}'", .{sname}); const info = try self.getStructInfo(sname);
const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_struct_ptr"); 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 c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_struct");
}, },
@@ -6021,7 +5835,7 @@ pub const CodeGen = struct {
break :blk any_i64; break :blk any_i64;
}, },
.union_type => |uname| blk: { .union_type => |uname| blk: {
const info = self.tagged_enum_types.get(uname) orelse return self.emitErrorFmt("unknown enum '{s}'", .{uname}); const info = try self.getTaggedEnumInfo(uname);
const ptr = c.LLVMBuildIntToPtr(self.builder, any_i64, c.LLVMPointerTypeInContext(self.context, 0), "any_to_union_ptr"); 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 c.LLVMBuildLoad2(self.builder, info.llvm_type, ptr, "any_to_union");
}, },
@@ -6107,7 +5921,7 @@ pub const CodeGen = struct {
defer self.type_param_bindings = saved_bindings; defer self.type_param_bindings = saved_bindings;
// Build the specialized function type // Build the specialized function type
const fn_type = try self.buildFnType(fd.params, fd.return_type, mangled); const fn_type = try self.buildFnType(fd.params, fd.return_type, mangled, false);
const mangled_z = try self.allocator.dupeZ(u8, mangled); const mangled_z = try self.allocator.dupeZ(u8, mangled);
const function = c.LLVMAddFunction(self.module, mangled_z.ptr, fn_type); const function = c.LLVMAddFunction(self.module, mangled_z.ptr, fn_type);
@@ -6135,7 +5949,7 @@ pub const CodeGen = struct {
raw[1 .. raw.len - 1] raw[1 .. raw.len - 1]
else else
raw; raw;
const content = if (slit.is_raw) inner else try unescapeString(self.allocator, inner); const content = if (slit.is_raw) inner else try unescape.unescapeString(self.allocator, inner);
const str_val = self.buildConstStr(content); const str_val = self.buildConstStr(content);
const param_name_z = try self.allocator.dupeZ(u8, param.name); const param_name_z = try self.allocator.dupeZ(u8, param.name);
const alloca = c.LLVMBuildAlloca(self.builder, self.getStringStructType(), param_name_z.ptr); const alloca = c.LLVMBuildAlloca(self.builder, self.getStringStructType(), param_name_z.ptr);
@@ -6154,7 +5968,7 @@ pub const CodeGen = struct {
} }
continue; continue;
} }
// Variadic params: use slice_type (same as genFnBodyAs) // Variadic params: use slice_type (same as genFnBody)
const sx_ty = if (param.is_variadic) blk: { const sx_ty = if (param.is_variadic) blk: {
const elem_name = if (param.type_expr.data == .type_expr) param.type_expr.data.type_expr.name else "s32"; const elem_name = if (param.type_expr.data == .type_expr) param.type_expr.data.type_expr.name else "s32";
break :blk Type{ .slice_type = .{ .element_name = elem_name } }; break :blk Type{ .slice_type = .{ .element_name = elem_name } };
@@ -6183,7 +5997,7 @@ pub const CodeGen = struct {
} else if (last_val) |val| { } else if (last_val) |val| {
if (ret_sx_type.isStruct()) { if (ret_sx_type.isStruct()) {
const sname = ret_sx_type.struct_type; const sname = ret_sx_type.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 loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, val, "retval"); const loaded = c.LLVMBuildLoad2(self.builder, info.llvm_type, val, "retval");
_ = c.LLVMBuildRet(self.builder, loaded); _ = c.LLVMBuildRet(self.builder, loaded);
} else { } else {
@@ -6774,9 +6588,9 @@ pub const CodeGen = struct {
// Extract base name (strip namespace prefix) // Extract base name (strip namespace prefix)
const base = if (std.mem.lastIndexOfScalar(u8, name, '.')) |idx| name[idx + 1 ..] else name; const base = if (std.mem.lastIndexOfScalar(u8, name, '.')) |idx| name[idx + 1 ..] else name;
if (std.mem.eql(u8, base, "write")) return self.genWriteCall(call_node.args); if (std.mem.eql(u8, base, "write")) return self.genWriteCall(call_node.args);
if (std.mem.eql(u8, base, "sqrt")) return self.genSqrt(call_node); if (std.mem.eql(u8, base, "sqrt")) return self.genMathIntrinsic(call_node, "sqrt");
if (std.mem.eql(u8, base, "sin")) return self.genSin(call_node); if (std.mem.eql(u8, base, "sin")) return self.genMathIntrinsic(call_node, "sin");
if (std.mem.eql(u8, base, "cos")) return self.genCos(call_node); if (std.mem.eql(u8, base, "cos")) return self.genMathIntrinsic(call_node, "cos");
if (std.mem.eql(u8, base, "size_of")) return self.genSizeOf(call_node); if (std.mem.eql(u8, base, "size_of")) return self.genSizeOf(call_node);
if (std.mem.eql(u8, base, "cast")) return self.genCast(call_node); if (std.mem.eql(u8, base, "cast")) return self.genCast(call_node);
if (std.mem.eql(u8, base, "alloc")) return self.genAlloc(call_node.args); if (std.mem.eql(u8, base, "alloc")) return self.genAlloc(call_node.args);
@@ -6815,7 +6629,7 @@ pub const CodeGen = struct {
fn buildConstStr(self: *CodeGen, s: []const u8) c.LLVMValueRef { fn buildConstStr(self: *CodeGen, s: []const u8) c.LLVMValueRef {
const sz = self.allocator.dupeZ(u8, s) catch unreachable; const sz = self.allocator.dupeZ(u8, s) catch unreachable;
const ptr = c.LLVMBuildGlobalStringPtr(self.builder, sz.ptr, "cstr"); const ptr = c.LLVMBuildGlobalStringPtr(self.builder, sz.ptr, "cstr");
return self.buildStringSlice(ptr, @intCast(s.len)); return self.buildStringSlice(ptr, self.constInt64(@intCast(s.len)));
} }
/// Helper: build a constant string slice as a global constant (no builder needed). /// Helper: build a constant string slice as a global constant (no builder needed).
@@ -7154,14 +6968,14 @@ pub const CodeGen = struct {
} }
if (obj_ty.isStruct()) { if (obj_ty.isStruct()) {
if (self.struct_types.get(obj_ty.struct_type)) |info| { if (self.struct_types.get(obj_ty.struct_type)) |info| {
if (self.findFieldIndex(info, fa.field)) |idx| { if (self.findNameIndex(info.field_names, fa.field)) |idx| {
return info.field_types[idx]; return info.field_types[idx];
} }
} }
} }
if (obj_ty.isUnion()) { if (obj_ty.isUnion()) {
if (self.union_types.get(obj_ty.union_type)) |info| { if (self.union_types.get(obj_ty.union_type)) |info| {
if (self.findUnionFieldIndex(info, fa.field)) |idx| { if (self.findNameIndex(info.field_names, fa.field)) |idx| {
return info.field_types[idx]; return info.field_types[idx];
} }
if (info.promoted_fields.get(fa.field)) |pf| { if (info.promoted_fields.get(fa.field)) |pf| {

View File

@@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const types = @import("types.zig"); const types = @import("types.zig");
const Type = types.Type; const Type = types.Type;
const unescape = @import("unescape.zig");
/// Runtime value for comptime evaluation. /// Runtime value for comptime evaluation.
/// Replaces codegen's JitResult with richer type support. /// Replaces codegen's JitResult with richer type support.
@@ -351,34 +352,6 @@ pub const Compiler = struct {
return null; return null;
} }
/// Process escape sequences in a raw string literal.
fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 {
var result = try allocator.alloc(u8, raw.len);
var i: usize = 0;
var j: usize = 0;
while (i < raw.len) {
if (raw[i] == '\\' and i + 1 < raw.len) {
i += 1;
switch (raw[i]) {
'n' => result[j] = '\n',
't' => result[j] = '\t',
'r' => result[j] = '\r',
'\\' => result[j] = '\\',
'"' => result[j] = '"',
'0' => result[j] = 0,
else => result[j] = raw[i],
}
j += 1;
i += 1;
} else {
result[j] = raw[i];
j += 1;
i += 1;
}
}
return result[0..j];
}
/// Compile a string literal with escape sequences and interpolation support. /// Compile a string literal with escape sequences and interpolation support.
/// Handles `{expr}` patterns by parsing and compiling the inner expressions, /// Handles `{expr}` patterns by parsing and compiling the inner expressions,
/// then concatenating all segments together. /// then concatenating all segments together.
@@ -389,7 +362,7 @@ pub const Compiler = struct {
fn compileStringLiteral(self: *Compiler, raw: []const u8) !void { fn compileStringLiteral(self: *Compiler, raw: []const u8) !void {
// String literals are plain text — {} is NOT interpolated here. // String literals are plain text — {} is NOT interpolated here.
// String interpolation is handled by print() at the call site. // String interpolation is handled by print() at the call site.
const unescaped = try unescapeString(self.allocator, raw); const unescaped = try unescape.unescapeString(self.allocator, raw);
const idx = try self.addString(unescaped); const idx = try self.addString(unescaped);
try self.emit(.{ .push_string = idx }); try self.emit(.{ .push_string = idx });
} }

View File

@@ -455,10 +455,7 @@ pub const Analyzer = struct {
switch (node.data) { switch (node.data) {
.fn_decl => |fd| { .fn_decl => |fd| {
try self.pushScope(); try self.pushScope();
for (fd.params) |param| { try self.analyzeParams(fd.params);
const param_type = Type.fromTypeExpr(param.type_expr);
try self.addSymbol(param.name, .param, param_type, param.name_span);
}
try self.analyzeNode(fd.body); try self.analyzeNode(fd.body);
self.popScope(); self.popScope();
}, },
@@ -499,6 +496,13 @@ pub const Analyzer = struct {
} }
} }
fn analyzeParams(self: *Analyzer, params: []const ast.Param) !void {
for (params) |param| {
const param_type = Type.fromTypeExpr(param.type_expr);
try self.addSymbol(param.name, .param, param_type, param.name_span);
}
}
fn addSymbol(self: *Analyzer, name: []const u8, kind: SymbolKind, ty: ?Type, span: Span) !void { fn addSymbol(self: *Analyzer, name: []const u8, kind: SymbolKind, ty: ?Type, span: Span) !void {
// Check for duplicate only within the current scope window. // Check for duplicate only within the current scope window.
const scope_start: usize = if (self.scope_starts.items.len > 0) const scope_start: usize = if (self.scope_starts.items.len > 0)
@@ -563,11 +567,7 @@ pub const Analyzer = struct {
.fn_decl => |fd| { .fn_decl => |fd| {
try self.addSymbol(fd.name, .function, resolveReturnType(fd), node.span); try self.addSymbol(fd.name, .function, resolveReturnType(fd), node.span);
try self.pushScope(); try self.pushScope();
// Add params as symbols try self.analyzeParams(fd.params);
for (fd.params) |param| {
const param_type = Type.fromTypeExpr(param.type_expr);
try self.addSymbol(param.name, .param, param_type, param.name_span);
}
try self.analyzeNode(fd.body); try self.analyzeNode(fd.body);
self.popScope(); self.popScope();
}, },
@@ -675,10 +675,7 @@ pub const Analyzer = struct {
}, },
.lambda => |lam| { .lambda => |lam| {
try self.pushScope(); try self.pushScope();
for (lam.params) |param| { try self.analyzeParams(lam.params);
const param_type = Type.fromTypeExpr(param.type_expr);
try self.addSymbol(param.name, .param, param_type, param.name_span);
}
try self.analyzeNode(lam.body); try self.analyzeNode(lam.body);
self.popScope(); self.popScope();
}, },
@@ -824,24 +821,22 @@ pub fn analyzeSource(allocator: std.mem.Allocator, root: *Node) !SemaResult {
return analyzer.analyze(root); return analyzer.analyze(root);
} }
/// Find the symbol whose definition span contains the given byte offset. fn findSpanAtOffset(comptime T: type, items: []const T, offset: u32, comptime span_field: []const u8) ?usize {
pub fn findSymbolAtOffset(symbols: []const Symbol, offset: u32) ?usize { for (items, 0..) |item, i| {
for (symbols, 0..) |sym, i| { const span = @field(item, span_field);
if (offset >= sym.def_span.start and offset < sym.def_span.end) { if (offset >= span.start and offset < span.end) return i;
return i;
}
} }
return null; return null;
} }
/// Find the symbol whose definition span contains the given byte offset.
pub fn findSymbolAtOffset(symbols: []const Symbol, offset: u32) ?usize {
return findSpanAtOffset(Symbol, symbols, offset, "def_span");
}
/// Find the reference at the given byte offset. /// Find the reference at the given byte offset.
pub fn findReferenceAtOffset(references: []const Reference, offset: u32) ?usize { pub fn findReferenceAtOffset(references: []const Reference, offset: u32) ?usize {
for (references, 0..) |ref_, i| { return findSpanAtOffset(Reference, references, offset, "span");
if (offset >= ref_.span.start and offset < ref_.span.end) {
return i;
}
}
return null;
} }
/// Walk the AST to find the innermost node whose span contains the offset. /// Walk the AST to find the innermost node whose span contains the offset.

46
src/unescape.zig Normal file
View File

@@ -0,0 +1,46 @@
const std = @import("std");
/// Process escape sequences in a raw string literal.
pub fn unescapeString(allocator: std.mem.Allocator, raw: []const u8) ![]u8 {
var result = try allocator.alloc(u8, raw.len);
var i: usize = 0;
var j: usize = 0;
while (i < raw.len) {
if (raw[i] == '\\' and i + 1 < raw.len) {
i += 1;
switch (raw[i]) {
'n' => {
result[j] = '\n';
},
't' => {
result[j] = '\t';
},
'r' => {
result[j] = '\r';
},
'\\' => {
result[j] = '\\';
},
'"' => {
result[j] = '"';
},
'0' => {
result[j] = 0;
},
'`' => {
result[j] = '`';
},
else => {
result[j] = raw[i];
},
}
j += 1;
i += 1;
} else {
result[j] = raw[i];
j += 1;
i += 1;
}
}
return result[0..j];
}