This commit is contained in:
agra
2026-03-02 17:18:47 +02:00
parent ba9c4d69ce
commit 2f4f898d54
20 changed files with 418 additions and 49 deletions

View File

@@ -21,10 +21,11 @@ pub const TypeId = enum(u32) {
string = 12, // [:0]u8
any = 13,
noreturn = 14,
_reserved = 15,
_, // user-defined types start at 16
isize = 15,
usize = 16,
_, // user-defined types start at 17
pub const first_user: u32 = 16;
pub const first_user: u32 = 17;
pub fn index(self: TypeId) u32 {
return @intFromEnum(self);
@@ -69,6 +70,8 @@ pub const TypeInfo = union(enum) {
any,
protocol: ProtocolInfo,
noreturn,
usize,
isize,
pub const StructInfo = struct {
name: StringId,
@@ -225,6 +228,8 @@ pub const TypeTable = struct {
/// Maps TypeInfo → TypeId for dedup of structural types
intern_map: std.HashMap(TypeKey, TypeId, TypeKeyContext, 80),
alloc: Allocator,
/// Target pointer size in bytes (4 for wasm32, 8 for 64-bit targets).
pointer_size: u8 = 8,
pub fn init(alloc: Allocator) TypeTable {
var table = TypeTable{
@@ -251,7 +256,8 @@ pub const TypeTable = struct {
.string, // 12
.any, // 13
.noreturn, // 14
.void, // 15: reserved (placeholder)
.isize, // 15: isize (pointer-sized signed)
.usize, // 16: usize (pointer-sized unsigned)
};
for (&builtins) |info| {
table.infos.append(alloc, info) catch unreachable;
@@ -402,25 +408,27 @@ pub const TypeTable = struct {
/// This is the authoritative size computation used for closure env sizing and
/// verified against LLVMABISizeOfType.
pub fn typeSizeBytes(self: *const TypeTable, ty: TypeId) usize {
const ptr_size: usize = self.pointer_size;
if (ty == .void) return 0;
if (ty == .bool) return 1;
if (ty == .u8 or ty == .s8) return 1;
if (ty == .u16 or ty == .s16) return 2;
if (ty == .s32 or ty == .u32 or ty == .f32) return 4;
if (ty == .s64 or ty == .u64 or ty == .f64) return 8;
if (ty == .string) return 16; // {ptr, i64}
if (ty.isBuiltin()) return 8; // default for unknown builtins
if (ty == .usize or ty == .isize) return ptr_size;
if (ty == .string) return 16; // {ptr, i64} — always 16 (i64 alignment pads on wasm32)
if (ty.isBuiltin()) return ptr_size; // default for unknown builtins
const info = self.get(ty);
return switch (info) {
.pointer, .many_pointer, .function => 8,
.slice => 16, // {ptr, i64}
.closure => 16, // {fn_ptr, env_ptr}
.pointer, .many_pointer, .function => ptr_size,
.slice => 16, // {ptr, i64} — same layout as string
.closure => 2 * ptr_size, // {fn_ptr, env_ptr}
.optional => |o| blk: {
const child_info = self.get(o.child);
if (child_info == .pointer or child_info == .many_pointer or child_info == .function)
break :blk 8;
break :blk ptr_size;
if (child_info == .closure)
break :blk 16; // {fn_ptr, env_ptr}
break :blk 2 * ptr_size;
const cs = self.typeSizeBytes(o.child);
const ca = self.typeAlignBytes(o.child);
// { T, i1 } — i1 goes right after T, then pad to struct alignment
@@ -480,8 +488,8 @@ pub const TypeTable = struct {
}
break :blk if (offset == 0) 0 else (offset + max_a - 1) & ~(max_a - 1);
},
.any => 16,
.protocol => 16,
.any => 2 * ptr_size, // {type_tag, data_ptr}
.protocol => 2 * ptr_size, // {ctx, vtable}
.@"enum" => |e| {
if (e.backing_type) |bt| return self.typeSizeBytes(bt);
return 8;
@@ -492,22 +500,25 @@ pub const TypeTable = struct {
/// Compute the ABI alignment in bytes for a type, matching LLVM's rules.
pub fn typeAlignBytes(self: *const TypeTable, ty: TypeId) usize {
const ptr_align: usize = self.pointer_size;
if (ty == .void) return 1;
if (ty == .bool) return 1;
if (ty == .u8 or ty == .s8) return 1;
if (ty == .u16 or ty == .s16) return 2;
if (ty == .s32 or ty == .u32 or ty == .f32) return 4;
if (ty == .s64 or ty == .u64 or ty == .f64) return 8;
if (ty == .string) return 8;
if (ty.isBuiltin()) return 8;
if (ty == .usize or ty == .isize) return ptr_align;
if (ty == .string) return 8; // i64 drives alignment
if (ty.isBuiltin()) return ptr_align;
const info = self.get(ty);
return switch (info) {
.pointer, .many_pointer, .function => 8,
.slice, .closure => 8,
.pointer, .many_pointer, .function => ptr_align,
.slice => 8, // i64 drives alignment
.closure => ptr_align, // {ptr, ptr}
.optional => |o| blk: {
const child_info = self.get(o.child);
if (child_info == .pointer or child_info == .many_pointer or child_info == .function or child_info == .closure)
break :blk 8;
break :blk ptr_align;
break :blk self.typeAlignBytes(o.child);
},
.@"struct" => |s| blk: {
@@ -566,6 +577,8 @@ pub const TypeTable = struct {
.string => "string",
.any => "Any",
.noreturn => "noreturn",
.isize => "isize",
.usize => "usize",
else => {
// User types — format from TypeInfo
const info = self.get(id);
@@ -609,7 +622,7 @@ fn hashTypeInfo(h: *std.hash.Wyhash, info: TypeInfo) void {
switch (info) {
.signed => |w| h.update(&.{w}),
.unsigned => |w| h.update(&.{w}),
.f32, .f64, .void, .bool, .string, .any, .noreturn => {},
.f32, .f64, .void, .bool, .string, .any, .noreturn, .usize, .isize => {},
.pointer => |p| h.update(std.mem.asBytes(&p.pointee)),
.many_pointer => |p| h.update(std.mem.asBytes(&p.element)),
.slice => |s| h.update(std.mem.asBytes(&s.element)),
@@ -650,7 +663,7 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
return switch (a) {
.signed => |w| w == b.signed,
.unsigned => |w| w == b.unsigned,
.f32, .f64, .void, .bool, .string, .any, .noreturn => true,
.f32, .f64, .void, .bool, .string, .any, .noreturn, .usize, .isize => true,
.pointer => |p| p.pointee == b.pointer.pointee,
.many_pointer => |p| p.element == b.many_pointer.element,
.slice => |s| s.element == b.slice.element,