aboutsummaryrefslogtreecommitdiff
path: root/src/InternPool.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/InternPool.zig')
-rw-r--r--src/InternPool.zig244
1 files changed, 204 insertions, 40 deletions
diff --git a/src/InternPool.zig b/src/InternPool.zig
index 2435e0ad31..d4bfe5a244 100644
--- a/src/InternPool.zig
+++ b/src/InternPool.zig
@@ -148,6 +148,7 @@ pub const Key = union(enum) {
union_type: UnionType,
opaque_type: OpaqueType,
enum_type: EnumType,
+ func_type: FuncType,
/// Typed `undefined`. This will never be `none`; untyped `undefined` is represented
/// via `simple_value` and has a named `Index` tag for it.
@@ -185,6 +186,13 @@ pub const Key = union(enum) {
/// If zero use pointee_type.abiAlignment()
/// When creating pointer types, if alignment is equal to pointee type
/// abi alignment, this value should be set to 0 instead.
+ ///
+ /// Please don't change this to u32 or u29. If you want to save bits,
+ /// migrate the rest of the codebase to use the `Alignment` type rather
+ /// than using byte units. The LLVM backend can only handle `c_uint`
+ /// byte units; we can emit a semantic analysis error if alignment that
+ /// overflows that amount is attempted to be used, but it shouldn't
+ /// affect the other backends.
alignment: u64 = 0,
/// If this is non-zero it means the pointer points to a sub-byte
/// range of data, which is backed by a "host integer" with this
@@ -358,6 +366,44 @@ pub const Key = union(enum) {
}
};
+ pub const FuncType = struct {
+ param_types: []Index,
+ return_type: Index,
+ /// Tells whether a parameter is comptime. See `paramIsComptime` helper
+ /// method for accessing this.
+ comptime_bits: u32,
+ /// Tells whether a parameter is noalias. See `paramIsNoalias` helper
+ /// method for accessing this.
+ noalias_bits: u32,
+ /// If zero use default target function code alignment.
+ ///
+ /// Please don't change this to u32 or u29. If you want to save bits,
+ /// migrate the rest of the codebase to use the `Alignment` type rather
+ /// than using byte units. The LLVM backend can only handle `c_uint`
+ /// byte units; we can emit a semantic analysis error if alignment that
+ /// overflows that amount is attempted to be used, but it shouldn't
+ /// affect the other backends.
+ alignment: u64,
+ cc: std.builtin.CallingConvention,
+ is_var_args: bool,
+ is_generic: bool,
+ is_noinline: bool,
+ align_is_generic: bool,
+ cc_is_generic: bool,
+ section_is_generic: bool,
+ addrspace_is_generic: bool,
+
+ pub fn paramIsComptime(self: @This(), i: u5) bool {
+ assert(i < self.param_types.len);
+ return @truncate(u1, self.comptime_bits >> i) != 0;
+ }
+
+ pub fn paramIsNoalias(self: @This(), i: u5) bool {
+ assert(i < self.param_types.len);
+ return @truncate(u1, self.noalias_bits >> i) != 0;
+ }
+ };
+
pub const Int = struct {
ty: Index,
storage: Storage,
@@ -512,6 +558,18 @@ pub const Key = union(enum) {
for (anon_struct_type.values) |elem| std.hash.autoHash(hasher, elem);
for (anon_struct_type.names) |elem| std.hash.autoHash(hasher, elem);
},
+
+ .func_type => |func_type| {
+ for (func_type.param_types) |param_type| std.hash.autoHash(hasher, param_type);
+ std.hash.autoHash(hasher, func_type.return_type);
+ std.hash.autoHash(hasher, func_type.comptime_bits);
+ std.hash.autoHash(hasher, func_type.noalias_bits);
+ std.hash.autoHash(hasher, func_type.alignment);
+ std.hash.autoHash(hasher, func_type.cc);
+ std.hash.autoHash(hasher, func_type.is_var_args);
+ std.hash.autoHash(hasher, func_type.is_generic);
+ std.hash.autoHash(hasher, func_type.is_noinline);
+ },
}
}
@@ -670,6 +728,20 @@ pub const Key = union(enum) {
std.mem.eql(Index, a_info.values, b_info.values) and
std.mem.eql(NullTerminatedString, a_info.names, b_info.names);
},
+
+ .func_type => |a_info| {
+ const b_info = b.func_type;
+
+ return std.mem.eql(Index, a_info.param_types, b_info.param_types) and
+ a_info.return_type == b_info.return_type and
+ a_info.comptime_bits == b_info.comptime_bits and
+ a_info.noalias_bits == b_info.noalias_bits and
+ a_info.alignment == b_info.alignment and
+ a_info.cc == b_info.cc and
+ a_info.is_var_args == b_info.is_var_args and
+ a_info.is_generic == b_info.is_generic and
+ a_info.is_noinline == b_info.is_noinline;
+ },
}
}
@@ -687,6 +759,7 @@ pub const Key = union(enum) {
.opaque_type,
.enum_type,
.anon_struct_type,
+ .func_type,
=> .type_type,
inline .ptr,
@@ -734,7 +807,6 @@ pub const Index = enum(u32) {
pub const last_value: Index = .empty_struct;
u1_type,
- u5_type,
u8_type,
i8_type,
u16_type,
@@ -811,10 +883,10 @@ pub const Index = enum(u32) {
one,
/// `1` (usize)
one_usize,
- /// `1` (u5)
- one_u5,
- /// `4` (u5)
- four_u5,
+ /// `1` (u8)
+ one_u8,
+ /// `4` (u8)
+ four_u8,
/// `-1` (comptime_int)
negative_one,
/// `std.builtin.CallingConvention.C`
@@ -880,12 +952,6 @@ pub const static_keys = [_]Key{
.bits = 1,
} },
- // u5_type
- .{ .int_type = .{
- .signedness = .unsigned,
- .bits = 5,
- } },
-
.{ .int_type = .{
.signedness = .unsigned,
.bits = 8,
@@ -1074,14 +1140,14 @@ pub const static_keys = [_]Key{
.storage = .{ .u64 = 1 },
} },
- // one_u5
+ // one_u8
.{ .int = .{
- .ty = .u5_type,
+ .ty = .u8_type,
.storage = .{ .u64 = 1 },
} },
- // four_u5
+ // four_u8
.{ .int = .{
- .ty = .u5_type,
+ .ty = .u8_type,
.storage = .{ .u64 = 4 },
} },
// negative_one
@@ -1092,12 +1158,12 @@ pub const static_keys = [_]Key{
// calling_convention_c
.{ .enum_tag = .{
.ty = .calling_convention_type,
- .int = .one_u5,
+ .int = .one_u8,
} },
// calling_convention_inline
.{ .enum_tag = .{
.ty = .calling_convention_type,
- .int = .four_u5,
+ .int = .four_u8,
} },
.{ .simple_value = .void },
@@ -1181,6 +1247,9 @@ pub const Tag = enum(u8) {
/// An untagged union type which has a safety tag.
/// `data` is `Module.Union.Index`.
type_union_safety,
+ /// A function body type.
+ /// `data` is extra index to `TypeFunction`.
+ type_function,
/// Typed `undefined`.
/// `data` is `Index` of the type.
@@ -1284,6 +1353,29 @@ pub const Tag = enum(u8) {
};
/// Trailing:
+/// 0. param_type: Index for each params_len
+pub const TypeFunction = struct {
+ params_len: u32,
+ return_type: Index,
+ comptime_bits: u32,
+ noalias_bits: u32,
+ flags: Flags,
+
+ pub const Flags = packed struct(u32) {
+ alignment: Alignment,
+ cc: std.builtin.CallingConvention,
+ is_var_args: bool,
+ is_generic: bool,
+ is_noinline: bool,
+ align_is_generic: bool,
+ cc_is_generic: bool,
+ section_is_generic: bool,
+ addrspace_is_generic: bool,
+ _: u11 = 0,
+ };
+};
+
+/// Trailing:
/// 0. element: Index for each len
/// len is determined by the aggregate type.
pub const Aggregate = struct {
@@ -1371,24 +1463,6 @@ pub const Pointer = struct {
flags: Flags,
packed_offset: PackedOffset,
- /// Stored as a power-of-two, with one special value to indicate none.
- pub const Alignment = enum(u6) {
- none = std.math.maxInt(u6),
- _,
-
- pub fn toByteUnits(a: Alignment, default: u64) u64 {
- return switch (a) {
- .none => default,
- _ => @as(u64, 1) << @enumToInt(a),
- };
- }
-
- pub fn fromByteUnits(n: u64) Alignment {
- if (n == 0) return .none;
- return @intToEnum(Alignment, @ctz(n));
- }
- };
-
pub const Flags = packed struct(u32) {
size: Size,
alignment: Alignment,
@@ -1409,6 +1483,24 @@ pub const Pointer = struct {
pub const VectorIndex = Key.PtrType.VectorIndex;
};
+/// Stored as a power-of-two, with one special value to indicate none.
+pub const Alignment = enum(u6) {
+ none = std.math.maxInt(u6),
+ _,
+
+ pub fn toByteUnits(a: Alignment, default: u64) u64 {
+ return switch (a) {
+ .none => default,
+ _ => @as(u64, 1) << @enumToInt(a),
+ };
+ }
+
+ pub fn fromByteUnits(n: u64) Alignment {
+ if (n == 0) return .none;
+ return @intToEnum(Alignment, @ctz(n));
+ }
+};
+
/// Used for non-sentineled arrays that have length fitting in u32, as well as
/// vectors.
pub const Vector = struct {
@@ -1765,6 +1857,7 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
},
.type_enum_explicit => indexToKeyEnum(ip, data, .explicit),
.type_enum_nonexhaustive => indexToKeyEnum(ip, data, .nonexhaustive),
+ .type_function => .{ .func_type = indexToKeyFuncType(ip, data) },
.undef => .{ .undef = @intToEnum(Index, data) },
.opt_null => .{ .opt = .{
@@ -1896,6 +1989,29 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
};
}
+fn indexToKeyFuncType(ip: InternPool, data: u32) Key.FuncType {
+ const type_function = ip.extraDataTrail(TypeFunction, data);
+ const param_types = @ptrCast(
+ []Index,
+ ip.extra.items[type_function.end..][0..type_function.data.params_len],
+ );
+ return .{
+ .param_types = param_types,
+ .return_type = type_function.data.return_type,
+ .comptime_bits = type_function.data.comptime_bits,
+ .noalias_bits = type_function.data.noalias_bits,
+ .alignment = type_function.data.flags.alignment.toByteUnits(0),
+ .cc = type_function.data.flags.cc,
+ .is_var_args = type_function.data.flags.is_var_args,
+ .is_generic = type_function.data.flags.is_generic,
+ .is_noinline = type_function.data.flags.is_noinline,
+ .align_is_generic = type_function.data.flags.align_is_generic,
+ .cc_is_generic = type_function.data.flags.cc_is_generic,
+ .section_is_generic = type_function.data.flags.section_is_generic,
+ .addrspace_is_generic = type_function.data.flags.addrspace_is_generic,
+ };
+}
+
/// Asserts the integer tag type is already present in the InternPool.
fn getEnumIntTagType(ip: InternPool, fields_len: u32) Index {
return ip.getAssumeExists(.{ .int_type = .{
@@ -1977,7 +2093,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
.child = ptr_type.elem_type,
.sentinel = ptr_type.sentinel,
.flags = .{
- .alignment = Pointer.Alignment.fromByteUnits(ptr_type.alignment),
+ .alignment = Alignment.fromByteUnits(ptr_type.alignment),
.is_const = ptr_type.is_const,
.is_volatile = ptr_type.is_volatile,
.is_allowzero = ptr_type.is_allowzero,
@@ -2163,6 +2279,37 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
}
},
+ .func_type => |func_type| {
+ assert(func_type.return_type != .none);
+ for (func_type.param_types) |param_type| assert(param_type != .none);
+
+ const params_len = @intCast(u32, func_type.param_types.len);
+
+ try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(TypeFunction).Struct.fields.len +
+ params_len);
+ ip.items.appendAssumeCapacity(.{
+ .tag = .type_function,
+ .data = ip.addExtraAssumeCapacity(TypeFunction{
+ .params_len = params_len,
+ .return_type = func_type.return_type,
+ .comptime_bits = func_type.comptime_bits,
+ .noalias_bits = func_type.noalias_bits,
+ .flags = .{
+ .alignment = Alignment.fromByteUnits(func_type.alignment),
+ .cc = func_type.cc,
+ .is_var_args = func_type.is_var_args,
+ .is_generic = func_type.is_generic,
+ .is_noinline = func_type.is_noinline,
+ .align_is_generic = func_type.align_is_generic,
+ .cc_is_generic = func_type.cc_is_generic,
+ .section_is_generic = func_type.section_is_generic,
+ .addrspace_is_generic = func_type.addrspace_is_generic,
+ },
+ }),
+ });
+ ip.extra.appendSliceAssumeCapacity(@ptrCast([]const u32, func_type.param_types));
+ },
+
.extern_func => @panic("TODO"),
.ptr => |ptr| switch (ptr.addr) {
@@ -2736,6 +2883,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
OptionalMapIndex => @enumToInt(@field(extra, field.name)),
i32 => @bitCast(u32, @field(extra, field.name)),
Pointer.Flags => @bitCast(u32, @field(extra, field.name)),
+ TypeFunction.Flags => @bitCast(u32, @field(extra, field.name)),
Pointer.PackedOffset => @bitCast(u32, @field(extra, field.name)),
Pointer.VectorIndex => @enumToInt(@field(extra, field.name)),
else => @compileError("bad field type: " ++ @typeName(field.type)),
@@ -2797,6 +2945,7 @@ fn extraDataTrail(ip: InternPool, comptime T: type, index: usize) struct { data:
OptionalMapIndex => @intToEnum(OptionalMapIndex, int32),
i32 => @bitCast(i32, int32),
Pointer.Flags => @bitCast(Pointer.Flags, int32),
+ TypeFunction.Flags => @bitCast(TypeFunction.Flags, int32),
Pointer.PackedOffset => @bitCast(Pointer.PackedOffset, int32),
Pointer.VectorIndex => @intToEnum(Pointer.VectorIndex, int32),
else => @compileError("bad field type: " ++ @typeName(field.type)),
@@ -2988,17 +3137,17 @@ pub fn getCoercedInts(ip: *InternPool, gpa: Allocator, int: Key.Int, new_ty: Ind
}
}
-pub fn indexToStruct(ip: *InternPool, val: Index) Module.Struct.OptionalIndex {
+pub fn indexToStructType(ip: InternPool, val: Index) Module.Struct.OptionalIndex {
+ assert(val != .none);
const tags = ip.items.items(.tag);
- if (val == .none) return .none;
if (tags[@enumToInt(val)] != .type_struct) return .none;
const datas = ip.items.items(.data);
return @intToEnum(Module.Struct.Index, datas[@enumToInt(val)]).toOptional();
}
-pub fn indexToUnion(ip: *InternPool, val: Index) Module.Union.OptionalIndex {
+pub fn indexToUnionType(ip: InternPool, val: Index) Module.Union.OptionalIndex {
+ assert(val != .none);
const tags = ip.items.items(.tag);
- if (val == .none) return .none;
switch (tags[@enumToInt(val)]) {
.type_union_tagged, .type_union_untagged, .type_union_safety => {},
else => return .none,
@@ -3007,6 +3156,16 @@ pub fn indexToUnion(ip: *InternPool, val: Index) Module.Union.OptionalIndex {
return @intToEnum(Module.Union.Index, datas[@enumToInt(val)]).toOptional();
}
+pub fn indexToFuncType(ip: InternPool, val: Index) ?Key.FuncType {
+ assert(val != .none);
+ const tags = ip.items.items(.tag);
+ const datas = ip.items.items(.data);
+ switch (tags[@enumToInt(val)]) {
+ .type_function => return indexToKeyFuncType(ip, datas[@enumToInt(val)]),
+ else => return null,
+ }
+}
+
pub fn isOptionalType(ip: InternPool, ty: Index) bool {
const tags = ip.items.items(.tag);
if (ty == .none) return false;
@@ -3092,6 +3251,11 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
.type_union_safety,
=> @sizeOf(Module.Union) + @sizeOf(Module.Namespace) + @sizeOf(Module.Decl),
+ .type_function => b: {
+ const info = ip.extraData(TypeFunction, data);
+ break :b @sizeOf(TypeFunction) + (@sizeOf(u32) * info.params_len);
+ },
+
.undef => 0,
.simple_type => 0,
.simple_value => 0,