authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-19 18:26:32-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-21 14:48:40-07:00
logfa1beba74fe2c5104bea16a90daa314ba713f287
treedadbd1d18e0940edcad6dc3d6d066e6a7de18d68
parentaccd5701c251c2741479fe08e56c8271c444f021

InternPool: implement getStructType

This also modifies AstGen so that struct types use 1 bit each from the flags to communicate if there are nonzero inits, alignments, or comptime fields. This allows adding a struct type to the InternPool without looking ahead in memory to find out the answers to these questions, which is easier for CPUs as well as for me, coding this logic right now.

4 files changed, 158 insertions(+), 21 deletions(-)

src/AstGen.zig+24-7
......@@ -4758,6 +4758,9 @@ fn structDeclInner(
47584758 .known_non_opv = false,
47594759 .known_comptime_only = false,
47604760 .is_tuple = false,
4761 .any_comptime_fields = false,
4762 .any_default_inits = false,
4763 .any_aligned_fields = false,
47614764 });
47624765 return indexToRef(decl_inst);
47634766 }
......@@ -4881,6 +4884,9 @@ fn structDeclInner(
48814884
48824885 var known_non_opv = false;
48834886 var known_comptime_only = false;
4887 var any_comptime_fields = false;
4888 var any_aligned_fields = false;
4889 var any_default_inits = false;
48844890 for (container_decl.ast.members) |member_node| {
48854891 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
48864892 .decl => continue,
......@@ -4910,13 +4916,13 @@ fn structDeclInner(
49104916 const have_value = member.ast.value_expr != 0;
49114917 const is_comptime = member.comptime_token != null;
49124918
4913 if (is_comptime and layout == .Packed) {
4914 return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{});
4915 } else if (is_comptime and layout == .Extern) {
4916 return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{});
4917 }
4918
4919 if (!is_comptime) {
4919 if (is_comptime) {
4920 switch (layout) {
4921 .Packed => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),
4922 .Extern => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
4923 .Auto => any_comptime_fields = true,
4924 }
4925 } else {
49204926 known_non_opv = known_non_opv or
49214927 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
49224928 known_comptime_only = known_comptime_only or
......@@ -4942,6 +4948,7 @@ fn structDeclInner(
49424948 if (layout == .Packed) {
49434949 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
49444950 }
4951 any_aligned_fields = true;
49454952 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
49464953 if (!block_scope.endsWithNoReturn()) {
49474954 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
......@@ -4955,6 +4962,7 @@ fn structDeclInner(
49554962 }
49564963
49574964 if (have_value) {
4965 any_default_inits = true;
49584966 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = field_type } };
49594967
49604968 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
......@@ -4982,6 +4990,9 @@ fn structDeclInner(
49824990 .known_non_opv = known_non_opv,
49834991 .known_comptime_only = known_comptime_only,
49844992 .is_tuple = is_tuple,
4993 .any_comptime_fields = any_comptime_fields,
4994 .any_default_inits = any_default_inits,
4995 .any_aligned_fields = any_aligned_fields,
49854996 });
49864997
49874998 wip_members.finishBits(bits_per_field);
......@@ -12080,6 +12091,9 @@ const GenZir = struct {
1208012091 known_non_opv: bool,
1208112092 known_comptime_only: bool,
1208212093 is_tuple: bool,
12094 any_comptime_fields: bool,
12095 any_default_inits: bool,
12096 any_aligned_fields: bool,
1208312097 }) !void {
1208412098 const astgen = gz.astgen;
1208512099 const gpa = astgen.gpa;
......@@ -12117,6 +12131,9 @@ const GenZir = struct {
1211712131 .is_tuple = args.is_tuple,
1211812132 .name_strategy = gz.anon_name_strategy,
1211912133 .layout = args.layout,
12134 .any_comptime_fields = args.any_comptime_fields,
12135 .any_default_inits = args.any_default_inits,
12136 .any_aligned_fields = args.any_aligned_fields,
1212012137 }),
1212112138 .operand = payload_index,
1212212139 } },
src/InternPool.zig+120-12
......@@ -576,8 +576,8 @@ pub const Key = union(enum) {
576576 return s.layout != .Packed and s.flagsPtr(ip).is_tuple;
577577 }
578578
579 pub fn hasReorderedFields(s: @This(), ip: *InternPool) bool {
580 return s.layout == .Auto and s.flagsPtr(ip).has_reordered_fields;
579 pub fn hasReorderedFields(s: @This()) bool {
580 return s.layout == .Auto;
581581 }
582582
583583 pub const RuntimeOrderIterator = struct {
......@@ -591,7 +591,7 @@ pub const Key = union(enum) {
591591 if (i >= it.struct_type.field_types.len)
592592 return null;
593593
594 if (it.struct_type.hasReorderedFields(it.ip)) {
594 if (it.struct_type.hasReorderedFields()) {
595595 it.field_index += 1;
596596 return it.struct_type.runtime_order.get(it.ip)[i].toInt();
597597 }
......@@ -2935,7 +2935,7 @@ pub const Tag = enum(u8) {
29352935 /// align: Alignment // for each field in declared order
29362936 /// 5. if any_comptime_fields:
29372937 /// field_is_comptime_bits: u32 // minimal number of u32s needed, LSB is field 0
2938 /// 6. if has_reordered_fields:
2938 /// 6. if not is_extern:
29392939 /// field_index: RuntimeOrder // for each field in runtime order
29402940 /// 7. field_offset: u32 // for each field in declared order, undef until layout_resolved
29412941 pub const TypeStruct = struct {
......@@ -2946,14 +2946,12 @@ pub const Tag = enum(u8) {
29462946 size: u32,
29472947
29482948 pub const Flags = packed struct(u32) {
2949 has_runtime_order: bool,
29502949 is_extern: bool,
29512950 known_non_opv: bool,
29522951 requires_comptime: RequiresComptime,
29532952 is_tuple: bool,
29542953 assumed_runtime_bits: bool,
29552954 has_namespace: bool,
2956 has_reordered_fields: bool,
29572955 any_comptime_fields: bool,
29582956 any_default_inits: bool,
29592957 any_aligned_fields: bool,
......@@ -2970,7 +2968,7 @@ pub const Tag = enum(u8) {
29702968 // which `layout_resolved` does not ensure.
29712969 fully_resolved: bool,
29722970
2973 _: u10 = 0,
2971 _: u12 = 0,
29742972 };
29752973 };
29762974};
......@@ -5092,6 +5090,9 @@ pub const StructTypeInit = struct {
50925090 known_non_opv: bool,
50935091 requires_comptime: RequiresComptime,
50945092 is_tuple: bool,
5093 any_comptime_fields: bool,
5094 any_default_inits: bool,
5095 any_aligned_fields: bool,
50955096};
50965097
50975098pub fn getStructType(
......@@ -5099,10 +5100,116 @@ pub fn getStructType(
50995100 gpa: Allocator,
51005101 ini: StructTypeInit,
51015102) Allocator.Error!Index {
5102 _ = ip;
5103 _ = gpa;
5104 _ = ini;
5105 @panic("TODO");
5103 const adapter: KeyAdapter = .{ .intern_pool = ip };
5104 const key: Key = .{
5105 .struct_type = .{
5106 // Only the decl matters for hashing and equality purposes.
5107 .decl = ini.decl.toOptional(),
5108
5109 .extra_index = undefined,
5110 .namespace = undefined,
5111 .zir_index = undefined,
5112 .layout = undefined,
5113 .field_names = undefined,
5114 .field_types = undefined,
5115 .field_inits = undefined,
5116 .field_aligns = undefined,
5117 .runtime_order = undefined,
5118 .comptime_bits = undefined,
5119 .offsets = undefined,
5120 .names_map = undefined,
5121 },
5122 };
5123 const gop = try ip.map.getOrPutAdapted(gpa, key, adapter);
5124 if (gop.found_existing) return @enumFromInt(gop.index);
5125 errdefer _ = ip.map.pop();
5126
5127 const names_map = try ip.addMap(gpa, ini.fields_len);
5128 errdefer _ = ip.maps.pop();
5129
5130 const is_extern = switch (ini.layout) {
5131 .Auto => false,
5132 .Extern => true,
5133 .Packed => {
5134 try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStructPacked).Struct.fields.len +
5135 ini.fields_len + // types
5136 ini.fields_len + // names
5137 ini.fields_len); // inits
5138 try ip.items.append(gpa, .{
5139 .tag = if (ini.any_default_inits) .type_struct_packed_inits else .type_struct_packed,
5140 .data = ip.addExtraAssumeCapacity(Tag.TypeStructPacked{
5141 .decl = ini.decl,
5142 .zir_index = ini.zir_index,
5143 .fields_len = ini.fields_len,
5144 .namespace = ini.namespace,
5145 .backing_int_ty = .none,
5146 .names_map = names_map,
5147 }),
5148 });
5149 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
5150 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len);
5151 if (ini.any_default_inits) {
5152 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
5153 }
5154 return @enumFromInt(ip.items.len - 1);
5155 },
5156 };
5157
5158 const align_elements_len = if (ini.any_aligned_fields) (ini.fields_len + 3) / 4 else 0;
5159 const align_element: u32 = @bitCast([1]u8{@intFromEnum(Alignment.none)} ** 4);
5160 const comptime_elements_len = if (ini.any_comptime_fields) (ini.fields_len + 31) / 32 else 0;
5161
5162 try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStruct).Struct.fields.len +
5163 (ini.fields_len * 5) + // types, names, inits, runtime order, offsets
5164 align_elements_len + comptime_elements_len +
5165 2); // names_map + namespace
5166 try ip.items.append(gpa, .{
5167 .tag = .type_struct,
5168 .data = ip.addExtraAssumeCapacity(Tag.TypeStruct{
5169 .decl = ini.decl,
5170 .zir_index = ini.zir_index,
5171 .fields_len = ini.fields_len,
5172 .size = std.math.maxInt(u32),
5173 .flags = .{
5174 .is_extern = is_extern,
5175 .known_non_opv = ini.known_non_opv,
5176 .requires_comptime = ini.requires_comptime,
5177 .is_tuple = ini.is_tuple,
5178 .assumed_runtime_bits = false,
5179 .has_namespace = ini.namespace != .none,
5180 .any_comptime_fields = ini.any_comptime_fields,
5181 .any_default_inits = ini.any_default_inits,
5182 .any_aligned_fields = ini.any_aligned_fields,
5183 .alignment = .none,
5184 .field_types_wip = false,
5185 .layout_wip = false,
5186 .layout_resolved = false,
5187 .fully_resolved = false,
5188 },
5189 }),
5190 });
5191 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
5192 if (!ini.is_tuple) {
5193 ip.extra.appendAssumeCapacity(@intFromEnum(names_map));
5194 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len);
5195 }
5196 if (ini.any_default_inits) {
5197 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len);
5198 }
5199 if (ini.namespace.unwrap()) |namespace| {
5200 ip.extra.appendAssumeCapacity(@intFromEnum(namespace));
5201 }
5202 if (ini.any_aligned_fields) {
5203 ip.extra.appendNTimesAssumeCapacity(align_element, align_elements_len);
5204 }
5205 if (ini.any_comptime_fields) {
5206 ip.extra.appendNTimesAssumeCapacity(0, comptime_elements_len);
5207 }
5208 if (ini.layout == .Auto) {
5209 ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Key.StructType.RuntimeOrder.unresolved), ini.fields_len);
5210 }
5211 ip.extra.appendNTimesAssumeCapacity(std.math.maxInt(u32), ini.fields_len);
5212 return @enumFromInt(ip.items.len - 1);
51065213}
51075214
51085215pub const AnonStructTypeInit = struct {
......@@ -5468,6 +5575,7 @@ pub fn getErrorSetType(
54685575 errdefer ip.items.len -= 1;
54695576
54705577 const names_map = try ip.addMap(gpa, names.len);
5578 assert(names_map == predicted_names_map);
54715579 errdefer _ = ip.maps.pop();
54725580
54735581 addStringsToMap(ip, names_map, names);
......@@ -6846,7 +6954,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
68466954 ints += (info.fields_len + 3) / 4; // aligns
68476955 if (info.flags.any_comptime_fields)
68486956 ints += (info.fields_len + 31) / 32; // comptime bits
6849 if (info.flags.has_reordered_fields)
6957 if (!info.flags.is_extern)
68506958 ints += info.fields_len; // runtime order
68516959 ints += info.fields_len; // offsets
68526960 break :b @sizeOf(u32) * ints;
src/Sema.zig+10-1
......@@ -2847,6 +2847,9 @@ pub fn getStructType(
28472847 .is_tuple = small.is_tuple,
28482848 .fields_len = fields_len,
28492849 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
2850 .any_default_inits = small.any_default_inits,
2851 .any_comptime_fields = small.any_comptime_fields,
2852 .any_aligned_fields = small.any_aligned_fields,
28502853 });
28512854
28522855 return ty;
......@@ -20992,6 +20995,12 @@ fn reifyStruct(
2099220995 .fields_len = fields_len,
2099320996 .requires_comptime = .unknown,
2099420997 .is_tuple = is_tuple,
20998 // So that we don't have to scan ahead, we allocate space in the struct for
20999 // alignments, comptime fields, and default inits. This might result in wasted
21000 // space, however, this is a permitted encoding of struct types.
21001 .any_comptime_fields = true,
21002 .any_default_inits = true,
21003 .any_aligned_fields = true,
2099521004 });
2099621005 // TODO: figure out InternPool removals for incremental compilation
2099721006 //errdefer ip.remove(ty);
......@@ -34312,7 +34321,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
3431234321 return sema.failWithOwnedErrorMsg(null, msg);
3431334322 }
3431434323
34315 if (struct_type.hasReorderedFields(ip)) {
34324 if (struct_type.hasReorderedFields()) {
3431634325 for (sizes, struct_type.runtime_order.get(ip), 0..) |size, *ro, i| {
3431734326 ro.* = if (size != 0) @enumFromInt(i) else .omitted;
3431834327 }
src/Zir.zig+4-1
......@@ -2840,7 +2840,10 @@ pub const Inst = struct {
28402840 is_tuple: bool,
28412841 name_strategy: NameStrategy,
28422842 layout: std.builtin.Type.ContainerLayout,
2843 _: u5 = undefined,
2843 any_default_inits: bool,
2844 any_comptime_fields: bool,
2845 any_aligned_fields: bool,
2846 _: u2 = undefined,
28442847 };
28452848 };
28462849