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(...@@ -4758,6 +4758,9 @@ fn structDeclInner(
4758 .known_non_opv = false,4758 .known_non_opv = false,
4759 .known_comptime_only = false,4759 .known_comptime_only = false,
4760 .is_tuple = false,4760 .is_tuple = false,
4761 .any_comptime_fields = false,
4762 .any_default_inits = false,
4763 .any_aligned_fields = false,
4761 });4764 });
4762 return indexToRef(decl_inst);4765 return indexToRef(decl_inst);
4763 }4766 }
...@@ -4881,6 +4884,9 @@ fn structDeclInner(...@@ -4881,6 +4884,9 @@ fn structDeclInner(
48814884
4882 var known_non_opv = false;4885 var known_non_opv = false;
4883 var known_comptime_only = false;4886 var known_comptime_only = false;
4887 var any_comptime_fields = false;
4888 var any_aligned_fields = false;
4889 var any_default_inits = false;
4884 for (container_decl.ast.members) |member_node| {4890 for (container_decl.ast.members) |member_node| {
4885 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {4891 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
4886 .decl => continue,4892 .decl => continue,
...@@ -4910,13 +4916,13 @@ fn structDeclInner(...@@ -4910,13 +4916,13 @@ fn structDeclInner(
4910 const have_value = member.ast.value_expr != 0;4916 const have_value = member.ast.value_expr != 0;
4911 const is_comptime = member.comptime_token != null;4917 const is_comptime = member.comptime_token != null;
49124918
4913 if (is_comptime and layout == .Packed) {4919 if (is_comptime) {
4914 return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{});4920 switch (layout) {
4915 } else if (is_comptime and layout == .Extern) {4921 .Packed => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),
4916 return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{});4922 .Extern => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
4917 }4923 .Auto => any_comptime_fields = true,
49184924 }
4919 if (!is_comptime) {4925 } else {
4920 known_non_opv = known_non_opv or4926 known_non_opv = known_non_opv or
4921 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);4927 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
4922 known_comptime_only = known_comptime_only or4928 known_comptime_only = known_comptime_only or
...@@ -4942,6 +4948,7 @@ fn structDeclInner(...@@ -4942,6 +4948,7 @@ fn structDeclInner(
4942 if (layout == .Packed) {4948 if (layout == .Packed) {
4943 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});4949 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
4944 }4950 }
4951 any_aligned_fields = true;
4945 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);4952 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
4946 if (!block_scope.endsWithNoReturn()) {4953 if (!block_scope.endsWithNoReturn()) {
4947 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);4954 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
...@@ -4955,6 +4962,7 @@ fn structDeclInner(...@@ -4955,6 +4962,7 @@ fn structDeclInner(
4955 }4962 }
49564963
4957 if (have_value) {4964 if (have_value) {
4965 any_default_inits = true;
4958 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = field_type } };4966 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = field_type } };
49594967
4960 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);4968 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
...@@ -4982,6 +4990,9 @@ fn structDeclInner(...@@ -4982,6 +4990,9 @@ fn structDeclInner(
4982 .known_non_opv = known_non_opv,4990 .known_non_opv = known_non_opv,
4983 .known_comptime_only = known_comptime_only,4991 .known_comptime_only = known_comptime_only,
4984 .is_tuple = is_tuple,4992 .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,
4985 });4996 });
49864997
4987 wip_members.finishBits(bits_per_field);4998 wip_members.finishBits(bits_per_field);
...@@ -12080,6 +12091,9 @@ const GenZir = struct {...@@ -12080,6 +12091,9 @@ const GenZir = struct {
12080 known_non_opv: bool,12091 known_non_opv: bool,
12081 known_comptime_only: bool,12092 known_comptime_only: bool,
12082 is_tuple: bool,12093 is_tuple: bool,
12094 any_comptime_fields: bool,
12095 any_default_inits: bool,
12096 any_aligned_fields: bool,
12083 }) !void {12097 }) !void {
12084 const astgen = gz.astgen;12098 const astgen = gz.astgen;
12085 const gpa = astgen.gpa;12099 const gpa = astgen.gpa;
...@@ -12117,6 +12131,9 @@ const GenZir = struct {...@@ -12117,6 +12131,9 @@ const GenZir = struct {
12117 .is_tuple = args.is_tuple,12131 .is_tuple = args.is_tuple,
12118 .name_strategy = gz.anon_name_strategy,12132 .name_strategy = gz.anon_name_strategy,
12119 .layout = args.layout,12133 .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,
12120 }),12137 }),
12121 .operand = payload_index,12138 .operand = payload_index,
12122 } },12139 } },
src/InternPool.zig+120-12
...@@ -576,8 +576,8 @@ pub const Key = union(enum) {...@@ -576,8 +576,8 @@ pub const Key = union(enum) {
576 return s.layout != .Packed and s.flagsPtr(ip).is_tuple;576 return s.layout != .Packed and s.flagsPtr(ip).is_tuple;
577 }577 }
578578
579 pub fn hasReorderedFields(s: @This(), ip: *InternPool) bool {579 pub fn hasReorderedFields(s: @This()) bool {
580 return s.layout == .Auto and s.flagsPtr(ip).has_reordered_fields;580 return s.layout == .Auto;
581 }581 }
582582
583 pub const RuntimeOrderIterator = struct {583 pub const RuntimeOrderIterator = struct {
...@@ -591,7 +591,7 @@ pub const Key = union(enum) {...@@ -591,7 +591,7 @@ pub const Key = union(enum) {
591 if (i >= it.struct_type.field_types.len)591 if (i >= it.struct_type.field_types.len)
592 return null;592 return null;
593593
594 if (it.struct_type.hasReorderedFields(it.ip)) {594 if (it.struct_type.hasReorderedFields()) {
595 it.field_index += 1;595 it.field_index += 1;
596 return it.struct_type.runtime_order.get(it.ip)[i].toInt();596 return it.struct_type.runtime_order.get(it.ip)[i].toInt();
597 }597 }
...@@ -2935,7 +2935,7 @@ pub const Tag = enum(u8) {...@@ -2935,7 +2935,7 @@ pub const Tag = enum(u8) {
2935 /// align: Alignment // for each field in declared order2935 /// align: Alignment // for each field in declared order
2936 /// 5. if any_comptime_fields:2936 /// 5. if any_comptime_fields:
2937 /// field_is_comptime_bits: u32 // minimal number of u32s needed, LSB is field 02937 /// 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:
2939 /// field_index: RuntimeOrder // for each field in runtime order2939 /// field_index: RuntimeOrder // for each field in runtime order
2940 /// 7. field_offset: u32 // for each field in declared order, undef until layout_resolved2940 /// 7. field_offset: u32 // for each field in declared order, undef until layout_resolved
2941 pub const TypeStruct = struct {2941 pub const TypeStruct = struct {
...@@ -2946,14 +2946,12 @@ pub const Tag = enum(u8) {...@@ -2946,14 +2946,12 @@ pub const Tag = enum(u8) {
2946 size: u32,2946 size: u32,
29472947
2948 pub const Flags = packed struct(u32) {2948 pub const Flags = packed struct(u32) {
2949 has_runtime_order: bool,
2950 is_extern: bool,2949 is_extern: bool,
2951 known_non_opv: bool,2950 known_non_opv: bool,
2952 requires_comptime: RequiresComptime,2951 requires_comptime: RequiresComptime,
2953 is_tuple: bool,2952 is_tuple: bool,
2954 assumed_runtime_bits: bool,2953 assumed_runtime_bits: bool,
2955 has_namespace: bool,2954 has_namespace: bool,
2956 has_reordered_fields: bool,
2957 any_comptime_fields: bool,2955 any_comptime_fields: bool,
2958 any_default_inits: bool,2956 any_default_inits: bool,
2959 any_aligned_fields: bool,2957 any_aligned_fields: bool,
...@@ -2970,7 +2968,7 @@ pub const Tag = enum(u8) {...@@ -2970,7 +2968,7 @@ pub const Tag = enum(u8) {
2970 // which `layout_resolved` does not ensure.2968 // which `layout_resolved` does not ensure.
2971 fully_resolved: bool,2969 fully_resolved: bool,
29722970
2973 _: u10 = 0,2971 _: u12 = 0,
2974 };2972 };
2975 };2973 };
2976};2974};
...@@ -5092,6 +5090,9 @@ pub const StructTypeInit = struct {...@@ -5092,6 +5090,9 @@ pub const StructTypeInit = struct {
5092 known_non_opv: bool,5090 known_non_opv: bool,
5093 requires_comptime: RequiresComptime,5091 requires_comptime: RequiresComptime,
5094 is_tuple: bool,5092 is_tuple: bool,
5093 any_comptime_fields: bool,
5094 any_default_inits: bool,
5095 any_aligned_fields: bool,
5095};5096};
50965097
5097pub fn getStructType(5098pub fn getStructType(
...@@ -5099,10 +5100,116 @@ pub fn getStructType(...@@ -5099,10 +5100,116 @@ pub fn getStructType(
5099 gpa: Allocator,5100 gpa: Allocator,
5100 ini: StructTypeInit,5101 ini: StructTypeInit,
5101) Allocator.Error!Index {5102) Allocator.Error!Index {
5102 _ = ip;5103 const adapter: KeyAdapter = .{ .intern_pool = ip };
5103 _ = gpa;5104 const key: Key = .{
5104 _ = ini;5105 .struct_type = .{
5105 @panic("TODO");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);
5106}5213}
51075214
5108pub const AnonStructTypeInit = struct {5215pub const AnonStructTypeInit = struct {
...@@ -5468,6 +5575,7 @@ pub fn getErrorSetType(...@@ -5468,6 +5575,7 @@ pub fn getErrorSetType(
5468 errdefer ip.items.len -= 1;5575 errdefer ip.items.len -= 1;
54695576
5470 const names_map = try ip.addMap(gpa, names.len);5577 const names_map = try ip.addMap(gpa, names.len);
5578 assert(names_map == predicted_names_map);
5471 errdefer _ = ip.maps.pop();5579 errdefer _ = ip.maps.pop();
54725580
5473 addStringsToMap(ip, names_map, names);5581 addStringsToMap(ip, names_map, names);
...@@ -6846,7 +6954,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {...@@ -6846,7 +6954,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
6846 ints += (info.fields_len + 3) / 4; // aligns6954 ints += (info.fields_len + 3) / 4; // aligns
6847 if (info.flags.any_comptime_fields)6955 if (info.flags.any_comptime_fields)
6848 ints += (info.fields_len + 31) / 32; // comptime bits6956 ints += (info.fields_len + 31) / 32; // comptime bits
6849 if (info.flags.has_reordered_fields)6957 if (!info.flags.is_extern)
6850 ints += info.fields_len; // runtime order6958 ints += info.fields_len; // runtime order
6851 ints += info.fields_len; // offsets6959 ints += info.fields_len; // offsets
6852 break :b @sizeOf(u32) * ints;6960 break :b @sizeOf(u32) * ints;
src/Sema.zig+10-1
...@@ -2847,6 +2847,9 @@ pub fn getStructType(...@@ -2847,6 +2847,9 @@ pub fn getStructType(
2847 .is_tuple = small.is_tuple,2847 .is_tuple = small.is_tuple,
2848 .fields_len = fields_len,2848 .fields_len = fields_len,
2849 .requires_comptime = if (small.known_comptime_only) .yes else .unknown,2849 .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,
2850 });2853 });
28512854
2852 return ty;2855 return ty;
...@@ -20992,6 +20995,12 @@ fn reifyStruct(...@@ -20992,6 +20995,12 @@ fn reifyStruct(
20992 .fields_len = fields_len,20995 .fields_len = fields_len,
20993 .requires_comptime = .unknown,20996 .requires_comptime = .unknown,
20994 .is_tuple = is_tuple,20997 .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,
20995 });21004 });
20996 // TODO: figure out InternPool removals for incremental compilation21005 // TODO: figure out InternPool removals for incremental compilation
20997 //errdefer ip.remove(ty);21006 //errdefer ip.remove(ty);
...@@ -34312,7 +34321,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {...@@ -34312,7 +34321,7 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
34312 return sema.failWithOwnedErrorMsg(null, msg);34321 return sema.failWithOwnedErrorMsg(null, msg);
34313 }34322 }
3431434323
34315 if (struct_type.hasReorderedFields(ip)) {34324 if (struct_type.hasReorderedFields()) {
34316 for (sizes, struct_type.runtime_order.get(ip), 0..) |size, *ro, i| {34325 for (sizes, struct_type.runtime_order.get(ip), 0..) |size, *ro, i| {
34317 ro.* = if (size != 0) @enumFromInt(i) else .omitted;34326 ro.* = if (size != 0) @enumFromInt(i) else .omitted;
34318 }34327 }
src/Zir.zig+4-1
...@@ -2840,7 +2840,10 @@ pub const Inst = struct {...@@ -2840,7 +2840,10 @@ pub const Inst = struct {
2840 is_tuple: bool,2840 is_tuple: bool,
2841 name_strategy: NameStrategy,2841 name_strategy: NameStrategy,
2842 layout: std.builtin.Type.ContainerLayout,2842 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,
2844 };2847 };
2845 };2848 };
28462849