ir done'ish
This commit is contained in:
172
src/ir/types.zig
172
src/ir/types.zig
@@ -56,6 +56,7 @@ pub const TypeInfo = union(enum) {
|
||||
@"struct": StructInfo,
|
||||
@"enum": EnumInfo,
|
||||
@"union": UnionInfo,
|
||||
tagged_union: TaggedUnionInfo,
|
||||
array: ArrayInfo,
|
||||
slice: SliceInfo,
|
||||
pointer: PointerInfo,
|
||||
@@ -84,12 +85,20 @@ pub const TypeInfo = union(enum) {
|
||||
variants: []const StringId,
|
||||
is_flags: bool = false,
|
||||
explicit_values: ?[]const i64 = null, // for flags (power-of-2) or custom values
|
||||
backing_type: ?TypeId = null, // e.g. u32 for `enum u32 { ... }`
|
||||
};
|
||||
|
||||
pub const UnionInfo = struct {
|
||||
name: StringId,
|
||||
fields: []const StructInfo.Field,
|
||||
tag_type: ?TypeId, // tagged union enum type, null if untagged
|
||||
};
|
||||
|
||||
pub const TaggedUnionInfo = struct {
|
||||
name: StringId,
|
||||
fields: []const StructInfo.Field,
|
||||
tag_type: TypeId, // tag integer type (e.g. .u32, .s64)
|
||||
backing_type: ?TypeId = null, // enum struct backing (e.g. { tag: u32; _: u32; payload: [30]u32; })
|
||||
explicit_tag_values: ?[]const i64 = null, // explicit variant values (e.g., quit :: 0x100)
|
||||
};
|
||||
|
||||
pub const ArrayInfo = struct {
|
||||
@@ -290,6 +299,7 @@ pub const TypeTable = struct {
|
||||
const n: ?StringId = switch (info) {
|
||||
.@"struct" => |s| s.name,
|
||||
.@"union" => |u| u.name,
|
||||
.tagged_union => |u| u.name,
|
||||
.@"enum" => |e| e.name,
|
||||
else => null,
|
||||
};
|
||||
@@ -358,15 +368,27 @@ pub const TypeTable = struct {
|
||||
return if (total == 0) 8 else total;
|
||||
},
|
||||
.@"union" => |u| {
|
||||
// Size of union = tag + max(field sizes)
|
||||
var max_field: u32 = 0;
|
||||
for (u.fields) |f| {
|
||||
const sz = self.sizeOf(f.ty);
|
||||
if (sz > max_field) max_field = sz;
|
||||
}
|
||||
return 8 + @max(max_field, 8);
|
||||
return @max(max_field, 8);
|
||||
},
|
||||
.tagged_union => |u| {
|
||||
if (u.backing_type) |bt| return self.sizeOf(bt);
|
||||
var max_field: u32 = 0;
|
||||
for (u.fields) |f| {
|
||||
const sz = self.sizeOf(f.ty);
|
||||
if (sz > max_field) max_field = sz;
|
||||
}
|
||||
const tag_sz = @as(u32, @intCast(self.typeSizeBytes(u.tag_type)));
|
||||
return tag_sz + @max(max_field, 8);
|
||||
},
|
||||
.@"enum" => |e| {
|
||||
if (e.backing_type) |bt| return self.sizeOf(bt);
|
||||
return 8;
|
||||
},
|
||||
.@"enum" => 8, // plain enums are just integer tags
|
||||
.tuple => |t| {
|
||||
var total: u32 = 0;
|
||||
for (t.fields) |f| total += @max(self.sizeOf(f), 8);
|
||||
@@ -376,6 +398,145 @@ pub const TypeTable = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Compute the ABI size in bytes for a type, matching LLVM's struct layout rules.
|
||||
/// This is the authoritative size computation used for closure env sizing and
|
||||
/// verified against LLVMABISizeOfType.
|
||||
pub fn typeSizeBytes(self: *const TypeTable, ty: TypeId) usize {
|
||||
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
|
||||
const info = self.get(ty);
|
||||
return switch (info) {
|
||||
.pointer, .many_pointer, .function => 8,
|
||||
.slice => 16, // {ptr, i64}
|
||||
.closure => 16, // {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;
|
||||
if (child_info == .closure)
|
||||
break :blk 16; // {fn_ptr, env_ptr}
|
||||
const cs = self.typeSizeBytes(o.child);
|
||||
const ca = self.typeAlignBytes(o.child);
|
||||
// { T, i1 } — i1 goes right after T, then pad to struct alignment
|
||||
const unpadded = cs + 1;
|
||||
break :blk (unpadded + ca - 1) & ~(ca - 1);
|
||||
},
|
||||
.@"struct" => |s| blk: {
|
||||
var offset: usize = 0;
|
||||
var max_a: usize = 1;
|
||||
for (s.fields) |f| {
|
||||
const fs = self.typeSizeBytes(f.ty);
|
||||
const fa = self.typeAlignBytes(f.ty);
|
||||
if (fa > max_a) max_a = fa;
|
||||
offset = (offset + fa - 1) & ~(fa - 1);
|
||||
offset += fs;
|
||||
}
|
||||
break :blk if (offset == 0) 0 else (offset + max_a - 1) & ~(max_a - 1);
|
||||
},
|
||||
.@"union" => |u| blk: {
|
||||
var max_payload: usize = 0;
|
||||
for (u.fields) |f| {
|
||||
const fs = self.typeSizeBytes(f.ty);
|
||||
if (fs > max_payload) max_payload = fs;
|
||||
}
|
||||
break :blk if (max_payload == 0) 8 else max_payload;
|
||||
},
|
||||
.tagged_union => |u| blk: {
|
||||
if (u.backing_type) |bt| break :blk self.typeSizeBytes(bt);
|
||||
var max_payload: usize = 0;
|
||||
for (u.fields) |f| {
|
||||
const fs = self.typeSizeBytes(f.ty);
|
||||
if (fs > max_payload) max_payload = fs;
|
||||
}
|
||||
const tag_size = self.typeSizeBytes(u.tag_type);
|
||||
const raw = max_payload + tag_size;
|
||||
break :blk (raw + 7) & ~@as(usize, 7);
|
||||
},
|
||||
.array => |a| blk: {
|
||||
const elem_size = self.typeSizeBytes(a.element);
|
||||
break :blk elem_size * @as(usize, @intCast(a.length));
|
||||
},
|
||||
.vector => |v| blk: {
|
||||
const elem_size = self.typeSizeBytes(v.element);
|
||||
const raw = elem_size * @as(usize, @intCast(v.length));
|
||||
// LLVM vectors round ABI size up to next power of 2
|
||||
break :blk std.math.ceilPowerOfTwo(usize, raw) catch raw;
|
||||
},
|
||||
.tuple => |t| blk: {
|
||||
var offset: usize = 0;
|
||||
var max_a: usize = 1;
|
||||
for (t.fields) |f| {
|
||||
const fs = self.typeSizeBytes(f);
|
||||
const fa = self.typeAlignBytes(f);
|
||||
if (fa > max_a) max_a = fa;
|
||||
offset = (offset + fa - 1) & ~(fa - 1);
|
||||
offset += fs;
|
||||
}
|
||||
break :blk if (offset == 0) 0 else (offset + max_a - 1) & ~(max_a - 1);
|
||||
},
|
||||
.any => 16,
|
||||
.protocol => 16,
|
||||
.@"enum" => |e| {
|
||||
if (e.backing_type) |bt| return self.typeSizeBytes(bt);
|
||||
return 8;
|
||||
},
|
||||
else => 8,
|
||||
};
|
||||
}
|
||||
|
||||
/// Compute the ABI alignment in bytes for a type, matching LLVM's rules.
|
||||
pub fn typeAlignBytes(self: *const TypeTable, ty: TypeId) usize {
|
||||
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;
|
||||
const info = self.get(ty);
|
||||
return switch (info) {
|
||||
.pointer, .many_pointer, .function => 8,
|
||||
.slice, .closure => 8,
|
||||
.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 self.typeAlignBytes(o.child);
|
||||
},
|
||||
.@"struct" => |s| blk: {
|
||||
var max_a: usize = 1;
|
||||
for (s.fields) |f| {
|
||||
const fa = self.typeAlignBytes(f.ty);
|
||||
if (fa > max_a) max_a = fa;
|
||||
}
|
||||
break :blk max_a;
|
||||
},
|
||||
.@"union", .tagged_union => 8,
|
||||
.@"enum" => |e| {
|
||||
if (e.backing_type) |bt| return self.typeAlignBytes(bt);
|
||||
return 8;
|
||||
},
|
||||
.array => |a| self.typeAlignBytes(a.element),
|
||||
.vector => |v| self.typeAlignBytes(v.element),
|
||||
.tuple => |t| blk: {
|
||||
var max_a: usize = 1;
|
||||
for (t.fields) |f| {
|
||||
const fa = self.typeAlignBytes(f);
|
||||
if (fa > max_a) max_a = fa;
|
||||
}
|
||||
break :blk max_a;
|
||||
},
|
||||
else => 8,
|
||||
};
|
||||
}
|
||||
|
||||
/// Intern a string into the pool.
|
||||
pub fn internString(self: *TypeTable, str: []const u8) StringId {
|
||||
return self.strings.intern(self.alloc, str);
|
||||
@@ -412,6 +573,7 @@ pub const TypeTable = struct {
|
||||
.@"struct" => |s| self.getString(s.name),
|
||||
.@"enum" => |e| self.getString(e.name),
|
||||
.@"union" => |u| self.getString(u.name),
|
||||
.tagged_union => |u| self.getString(u.name),
|
||||
.protocol => |p| self.getString(p.name),
|
||||
else => "?",
|
||||
};
|
||||
@@ -471,6 +633,7 @@ fn hashTypeInfo(h: *std.hash.Wyhash, info: TypeInfo) void {
|
||||
.@"struct" => |s| h.update(std.mem.asBytes(&s.name)),
|
||||
.@"enum" => |e| h.update(std.mem.asBytes(&e.name)),
|
||||
.@"union" => |u| h.update(std.mem.asBytes(&u.name)),
|
||||
.tagged_union => |u| h.update(std.mem.asBytes(&u.name)),
|
||||
.protocol => |p| h.update(std.mem.asBytes(&p.name)),
|
||||
.tuple => |t| {
|
||||
for (t.fields) |f| h.update(std.mem.asBytes(&f));
|
||||
@@ -513,6 +676,7 @@ fn typeInfoEql(a: TypeInfo, b: TypeInfo) bool {
|
||||
.@"struct" => |s| s.name == b.@"struct".name,
|
||||
.@"enum" => |e| e.name == b.@"enum".name,
|
||||
.@"union" => |u| u.name == b.@"union".name,
|
||||
.tagged_union => |u| u.name == b.tagged_union.name,
|
||||
.protocol => |p| p.name == b.protocol.name,
|
||||
.tuple => |t| {
|
||||
const u = b.tuple;
|
||||
|
||||
Reference in New Issue
Block a user