cleanup
This commit is contained in:
500
src/codegen.zig
500
src/codegen.zig
@@ -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, ¶m_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, ¶m_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| {
|
||||||
|
|||||||
@@ -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 });
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/sema.zig
45
src/sema.zig
@@ -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
46
src/unescape.zig
Normal 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];
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user