| author | |
| committer | |
| log | a7088fd9a3edb037f0f51bb402a3c557334634f3 |
| tree | dba5fd9f32b341d1cb64b9813033e1558c1e8d2b |
| parent | 8eff0a0a669dbdacf9cebbc96fdf20536f3073ee |
Instead of linear search every time a packed struct field's bit or byte
offset is wanted, they are computed once during resolution of the packed
struct's backing int type, and stored in InternPool for O(1) lookup.
Closes #171789 files changed, 75 insertions(+), 62 deletions(-)
src/InternPool.zig+46-6| ... | ... | @@ -105,6 +105,25 @@ pub const MapIndex = enum(u32) { |
| 105 | 105 | } |
| 106 | 106 | }; |
| 107 | 107 | |
| 108 | pub const OptionalInt = enum(u32) { | |
| 109 | none = std.math.maxInt(u32), | |
| 110 | _, | |
| 111 | ||
| 112 | pub fn init(x: u32) @This() { | |
| 113 | const result: @This() = @enumFromInt(x); | |
| 114 | assert(result != .none); | |
| 115 | return result; | |
| 116 | } | |
| 117 | ||
| 118 | pub fn initOptional(opt_x: ?u32) @This() { | |
| 119 | return @This().init(opt_x orelse return .none); | |
| 120 | } | |
| 121 | ||
| 122 | pub fn unwrap(this: @This()) ?u32 { | |
| 123 | return if (this == .none) null else @intFromEnum(this); | |
| 124 | } | |
| 125 | }; | |
| 126 | ||
| 108 | 127 | pub const RuntimeIndex = enum(u32) { |
| 109 | 128 | zero = 0, |
| 110 | 129 | comptime_field_ptr = std.math.maxInt(u32), |
| ... | ... | @@ -377,6 +396,8 @@ pub const Key = union(enum) { |
| 377 | 396 | field_aligns: Alignment.Slice, |
| 378 | 397 | runtime_order: RuntimeOrder.Slice, |
| 379 | 398 | comptime_bits: ComptimeBits, |
| 399 | /// In the case of packed structs these are bit offsets; in the case of | |
| 400 | /// non-packed structs these are byte offsets. | |
| 380 | 401 | offsets: Offsets, |
| 381 | 402 | names_map: OptionalMapIndex, |
| 382 | 403 | |
| ... | ... | @@ -475,6 +496,15 @@ pub const Key = union(enum) { |
| 475 | 496 | return s.field_names.get(ip)[i].toOptional(); |
| 476 | 497 | } |
| 477 | 498 | |
| 499 | /// Asserts it is a packed struct. | |
| 500 | /// Asserts the layout is resolved. | |
| 501 | pub fn fieldBitOffset(s: @This(), ip: *InternPool, i: usize) u32 { | |
| 502 | assert(s.layout == .Packed); | |
| 503 | assert(s.haveLayout(ip)); | |
| 504 | const result: OptionalInt = @enumFromInt(s.offsets.get(ip)[i]); | |
| 505 | return result.unwrap().?; | |
| 506 | } | |
| 507 | ||
| 478 | 508 | pub fn fieldIsComptime(s: @This(), ip: *const InternPool, i: usize) bool { |
| 479 | 509 | return s.comptime_bits.getBit(ip, i); |
| 480 | 510 | } |
| ... | ... | @@ -593,7 +623,11 @@ pub const Key = union(enum) { |
| 593 | 623 | |
| 594 | 624 | pub fn haveLayout(s: @This(), ip: *InternPool) bool { |
| 595 | 625 | return switch (s.layout) { |
| 596 | .Packed => s.backingIntType(ip).* != .none, | |
| 626 | .Packed => { | |
| 627 | if (s.offsets.len == 0) return true; | |
| 628 | const first_offset: OptionalInt = @enumFromInt(ip.extra.items[s.offsets.start]); | |
| 629 | return first_offset != .none; | |
| 630 | }, | |
| 597 | 631 | .Auto, .Extern => s.flagsPtr(ip).layout_resolved, |
| 598 | 632 | }; |
| 599 | 633 | } |
| ... | ... | @@ -2936,7 +2970,8 @@ pub const Tag = enum(u8) { |
| 2936 | 2970 | /// Trailing: |
| 2937 | 2971 | /// 0. type: Index for each fields_len |
| 2938 | 2972 | /// 1. name: NullTerminatedString for each fields_len |
| 2939 | /// 2. init: Index for each fields_len // if tag is type_struct_packed_inits | |
| 2973 | /// 2. bit_offset: OptionalInt for each fields_len // none until layout resolved | |
| 2974 | /// 3. init: Index for each fields_len // if tag is type_struct_packed_inits | |
| 2940 | 2975 | pub const TypeStructPacked = struct { |
| 2941 | 2976 | decl: Module.Decl.Index, |
| 2942 | 2977 | zir_index: Zir.Inst.Index, |
| ... | ... | @@ -4194,8 +4229,12 @@ fn extraPackedStructType(ip: *const InternPool, extra_index: u32, inits: bool) K |
| 4194 | 4229 | .start = type_struct_packed.end + fields_len, |
| 4195 | 4230 | .len = fields_len, |
| 4196 | 4231 | }, |
| 4232 | .offsets = .{ | |
| 4233 | .start = type_struct_packed.end + fields_len * 2, | |
| 4234 | .len = fields_len, | |
| 4235 | }, | |
| 4197 | 4236 | .field_inits = if (inits) .{ |
| 4198 | .start = type_struct_packed.end + fields_len + fields_len, | |
| 4237 | .start = type_struct_packed.end + fields_len * 3, | |
| 4199 | 4238 | .len = fields_len, |
| 4200 | 4239 | } else .{ |
| 4201 | 4240 | .start = 0, |
| ... | ... | @@ -4204,7 +4243,6 @@ fn extraPackedStructType(ip: *const InternPool, extra_index: u32, inits: bool) K |
| 4204 | 4243 | .field_aligns = .{ .start = 0, .len = 0 }, |
| 4205 | 4244 | .runtime_order = .{ .start = 0, .len = 0 }, |
| 4206 | 4245 | .comptime_bits = .{ .start = 0, .len = 0 }, |
| 4207 | .offsets = .{ .start = 0, .len = 0 }, | |
| 4208 | 4246 | .names_map = type_struct_packed.data.names_map.toOptional(), |
| 4209 | 4247 | }; |
| 4210 | 4248 | } |
| ... | ... | @@ -5279,6 +5317,7 @@ pub fn getStructType( |
| 5279 | 5317 | try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Tag.TypeStructPacked).Struct.fields.len + |
| 5280 | 5318 | ini.fields_len + // types |
| 5281 | 5319 | ini.fields_len + // names |
| 5320 | ini.fields_len + // offsets | |
| 5282 | 5321 | ini.fields_len); // inits |
| 5283 | 5322 | try ip.items.append(gpa, .{ |
| 5284 | 5323 | .tag = if (ini.any_default_inits) .type_struct_packed_inits else .type_struct_packed, |
| ... | ... | @@ -5293,6 +5332,7 @@ pub fn getStructType( |
| 5293 | 5332 | }); |
| 5294 | 5333 | ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); |
| 5295 | 5334 | ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalNullTerminatedString.none), ini.fields_len); |
| 5335 | ip.extra.appendNTimesAssumeCapacity(@intFromEnum(OptionalInt.none), ini.fields_len); | |
| 5296 | 5336 | if (ini.any_default_inits) { |
| 5297 | 5337 | ip.extra.appendNTimesAssumeCapacity(@intFromEnum(Index.none), ini.fields_len); |
| 5298 | 5338 | } |
| ... | ... | @@ -7113,12 +7153,12 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { |
| 7113 | 7153 | .type_struct_packed => b: { |
| 7114 | 7154 | const info = ip.extraData(Tag.TypeStructPacked, data); |
| 7115 | 7155 | break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).Struct.fields.len + |
| 7116 | info.fields_len + info.fields_len); | |
| 7156 | info.fields_len + info.fields_len + info.fields_len); | |
| 7117 | 7157 | }, |
| 7118 | 7158 | .type_struct_packed_inits => b: { |
| 7119 | 7159 | const info = ip.extraData(Tag.TypeStructPacked, data); |
| 7120 | 7160 | break :b @sizeOf(u32) * (@typeInfo(Tag.TypeStructPacked).Struct.fields.len + |
| 7121 | info.fields_len + info.fields_len + info.fields_len); | |
| 7161 | info.fields_len + info.fields_len + info.fields_len + info.fields_len); | |
| 7122 | 7162 | }, |
| 7123 | 7163 | .type_tuple_anon => b: { |
| 7124 | 7164 | const info = ip.extraData(TypeStructAnon, data); |
src/Module.zig-23| ... | ... | @@ -6649,26 +6649,3 @@ pub fn structFieldAlignmentExtern(mod: *Module, field_ty: Type) Alignment { |
| 6649 | 6649 | |
| 6650 | 6650 | return ty_abi_align; |
| 6651 | 6651 | } |
| 6652 | ||
| 6653 | /// TODO: avoid linear search by storing these in trailing data of packed struct types | |
| 6654 | /// then packedStructFieldByteOffset can be expressed in terms of bits / 8, fixing | |
| 6655 | /// that one too. | |
| 6656 | /// https://github.com/ziglang/zig/issues/17178 | |
| 6657 | pub fn structPackedFieldBitOffset( | |
| 6658 | mod: *Module, | |
| 6659 | struct_type: InternPool.Key.StructType, | |
| 6660 | field_index: u32, | |
| 6661 | ) u16 { | |
| 6662 | const ip = &mod.intern_pool; | |
| 6663 | assert(struct_type.layout == .Packed); | |
| 6664 | assert(struct_type.haveLayout(ip)); | |
| 6665 | var bit_sum: u64 = 0; | |
| 6666 | for (0..struct_type.field_types.len) |i| { | |
| 6667 | if (i == field_index) { | |
| 6668 | return @intCast(bit_sum); | |
| 6669 | } | |
| 6670 | const field_ty = struct_type.field_types.get(ip)[i].toType(); | |
| 6671 | bit_sum += field_ty.bitSize(mod); | |
| 6672 | } | |
| 6673 | unreachable; // index out of bounds | |
| 6674 | } |
src/Sema.zig+5-2| ... | ... | @@ -21342,8 +21342,10 @@ fn reifyStruct( |
| 21342 | 21342 | } |
| 21343 | 21343 | |
| 21344 | 21344 | var fields_bit_sum: u64 = 0; |
| 21345 | for (struct_type.field_types.get(ip)) |field_ty| { | |
| 21346 | fields_bit_sum += field_ty.toType().bitSize(mod); | |
| 21345 | for (0..struct_type.field_types.len) |i| { | |
| 21346 | struct_type.offsets.get(ip)[i] = @intCast(fields_bit_sum); | |
| 21347 | const field_ty = struct_type.field_types.get(ip)[i].toType(); | |
| 21348 | fields_bit_sum += field_ty.bitSize(mod); | |
| 21347 | 21349 | } |
| 21348 | 21350 | |
| 21349 | 21351 | if (backing_int_val.optionalValue(mod)) |backing_int_ty_val| { |
| ... | ... | @@ -34772,6 +34774,7 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp |
| 34772 | 34774 | var accumulator: u64 = 0; |
| 34773 | 34775 | for (0..struct_type.field_types.len) |i| { |
| 34774 | 34776 | const field_ty = struct_type.field_types.get(ip)[i].toType(); |
| 34777 | struct_type.offsets.get(ip)[i] = @intCast(accumulator); | |
| 34775 | 34778 | accumulator += try field_ty.bitSizeAdvanced(mod, &sema); |
| 34776 | 34779 | } |
| 34777 | 34780 | break :blk accumulator; |
src/arch/wasm/CodeGen.zig+1-1| ... | ... | @@ -3779,7 +3779,7 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3779 | 3779 | .Packed => switch (struct_ty.zigTypeTag(mod)) { |
| 3780 | 3780 | .Struct => result: { |
| 3781 | 3781 | const packed_struct = mod.typeToPackedStruct(struct_ty).?; |
| 3782 | const offset = mod.structPackedFieldBitOffset(packed_struct, field_index); | |
| 3782 | const offset = packed_struct.fieldBitOffset(ip, field_index); | |
| 3783 | 3783 | const backing_ty = packed_struct.backingIntType(ip).toType(); |
| 3784 | 3784 | const wasm_bits = toWasmBits(backing_ty.intInfo(mod).bits) orelse { |
| 3785 | 3785 | return func.fail("TODO: airStructFieldVal for packed structs larger than 128 bits", .{}); |
src/arch/x86_64/CodeGen.zig+4-2| ... | ... | @@ -5593,6 +5593,7 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32 |
| 5593 | 5593 | |
| 5594 | 5594 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 5595 | 5595 | const mod = self.bin_file.options.module.?; |
| 5596 | const ip = &mod.intern_pool; | |
| 5596 | 5597 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 5597 | 5598 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 5598 | 5599 | const result: MCValue = result: { |
| ... | ... | @@ -5610,7 +5611,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 5610 | 5611 | const field_off: u32 = switch (container_ty.containerLayout(mod)) { |
| 5611 | 5612 | .Auto, .Extern => @intCast(container_ty.structFieldOffset(index, mod) * 8), |
| 5612 | 5613 | .Packed => if (mod.typeToStruct(container_ty)) |struct_type| |
| 5613 | mod.structPackedFieldBitOffset(struct_type, index) | |
| 5614 | struct_type.fieldBitOffset(ip, index) | |
| 5614 | 5615 | else |
| 5615 | 5616 | 0, |
| 5616 | 5617 | }; |
| ... | ... | @@ -11410,6 +11411,7 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { |
| 11410 | 11411 | |
| 11411 | 11412 | fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 11412 | 11413 | const mod = self.bin_file.options.module.?; |
| 11414 | const ip = &mod.intern_pool; | |
| 11413 | 11415 | const result_ty = self.typeOfIndex(inst); |
| 11414 | 11416 | const len: usize = @intCast(result_ty.arrayLen(mod)); |
| 11415 | 11417 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| ... | ... | @@ -11440,7 +11442,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { |
| 11440 | 11442 | } |
| 11441 | 11443 | const elem_abi_size: u32 = @intCast(elem_ty.abiSize(mod)); |
| 11442 | 11444 | const elem_abi_bits = elem_abi_size * 8; |
| 11443 | const elem_off = mod.structPackedFieldBitOffset(struct_type, elem_i); | |
| 11445 | const elem_off = struct_type.fieldBitOffset(ip, elem_i); | |
| 11444 | 11446 | const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size); |
| 11445 | 11447 | const elem_bit_off = elem_off % elem_abi_bits; |
| 11446 | 11448 | const elem_mcv = try self.resolveInst(elem); |
src/codegen.zig+9-8| ... | ... | @@ -630,7 +630,8 @@ fn lowerParentPtr( |
| 630 | 630 | reloc_info: RelocInfo, |
| 631 | 631 | ) CodeGenError!Result { |
| 632 | 632 | const mod = bin_file.options.module.?; |
| 633 | const ptr = mod.intern_pool.indexToKey(parent_ptr).ptr; | |
| 633 | const ip = &mod.intern_pool; | |
| 634 | const ptr = ip.indexToKey(parent_ptr).ptr; | |
| 634 | 635 | assert(ptr.len == .none); |
| 635 | 636 | return switch (ptr.addr) { |
| 636 | 637 | .decl, .mut_decl => try lowerDeclRef( |
| ... | ... | @@ -656,7 +657,7 @@ fn lowerParentPtr( |
| 656 | 657 | code, |
| 657 | 658 | debug_output, |
| 658 | 659 | reloc_info.offset(@as(u32, @intCast(errUnionPayloadOffset( |
| 659 | mod.intern_pool.typeOf(eu_payload).toType(), | |
| 660 | ip.typeOf(eu_payload).toType(), | |
| 660 | 661 | mod, |
| 661 | 662 | )))), |
| 662 | 663 | ), |
| ... | ... | @@ -675,17 +676,17 @@ fn lowerParentPtr( |
| 675 | 676 | code, |
| 676 | 677 | debug_output, |
| 677 | 678 | reloc_info.offset(@as(u32, @intCast(elem.index * |
| 678 | mod.intern_pool.typeOf(elem.base).toType().elemType2(mod).abiSize(mod)))), | |
| 679 | ip.typeOf(elem.base).toType().elemType2(mod).abiSize(mod)))), | |
| 679 | 680 | ), |
| 680 | 681 | .field => |field| { |
| 681 | const base_type = mod.intern_pool.indexToKey(mod.intern_pool.typeOf(field.base)).ptr_type.child; | |
| 682 | const base_type = ip.indexToKey(ip.typeOf(field.base)).ptr_type.child; | |
| 682 | 683 | return lowerParentPtr( |
| 683 | 684 | bin_file, |
| 684 | 685 | src_loc, |
| 685 | 686 | field.base, |
| 686 | 687 | code, |
| 687 | 688 | debug_output, |
| 688 | reloc_info.offset(switch (mod.intern_pool.indexToKey(base_type)) { | |
| 689 | reloc_info.offset(switch (ip.indexToKey(base_type)) { | |
| 689 | 690 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { |
| 690 | 691 | .One, .Many, .C => unreachable, |
| 691 | 692 | .Slice => switch (field.index) { |
| ... | ... | @@ -703,9 +704,9 @@ fn lowerParentPtr( |
| 703 | 704 | mod, |
| 704 | 705 | )), |
| 705 | 706 | .Packed => if (mod.typeToStruct(base_type.toType())) |struct_type| |
| 706 | math.divExact(u16, mod.structPackedFieldBitOffset( | |
| 707 | struct_type, | |
| 708 | @intCast(field.index), | |
| 707 | math.divExact(u32, struct_type.fieldBitOffset( | |
| 708 | ip, | |
| 709 | field.index, | |
| 709 | 710 | ), 8) catch |err| switch (err) { |
| 710 | 711 | error.UnexpectedRemainder => 0, |
| 711 | 712 | error.DivisionByZero => unreachable, |
src/codegen/c.zig+1-1| ... | ... | @@ -5429,7 +5429,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5429 | 5429 | |
| 5430 | 5430 | const bit_offset_ty = try mod.intType(.unsigned, Type.smallestUnsignedBits(int_info.bits - 1)); |
| 5431 | 5431 | |
| 5432 | const bit_offset = mod.structPackedFieldBitOffset(struct_type, extra.field_index); | |
| 5432 | const bit_offset = struct_type.fieldBitOffset(ip, extra.field_index); | |
| 5433 | 5433 | const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); |
| 5434 | 5434 | |
| 5435 | 5435 | const field_int_signedness = if (inst_ty.isAbiInt(mod)) |
src/codegen/llvm.zig+2-1| ... | ... | @@ -6192,6 +6192,7 @@ pub const FuncGen = struct { |
| 6192 | 6192 | fn airStructFieldVal(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { |
| 6193 | 6193 | const o = self.dg.object; |
| 6194 | 6194 | const mod = o.module; |
| 6195 | const ip = &mod.intern_pool; | |
| 6195 | 6196 | const inst = body_tail[0]; |
| 6196 | 6197 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 6197 | 6198 | const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| ... | ... | @@ -6207,7 +6208,7 @@ pub const FuncGen = struct { |
| 6207 | 6208 | .Struct => switch (struct_ty.containerLayout(mod)) { |
| 6208 | 6209 | .Packed => { |
| 6209 | 6210 | const struct_type = mod.typeToStruct(struct_ty).?; |
| 6210 | const bit_offset = mod.structPackedFieldBitOffset(struct_type, field_index); | |
| 6211 | const bit_offset = struct_type.fieldBitOffset(ip, field_index); | |
| 6211 | 6212 | const containing_int = struct_llvm_val; |
| 6212 | 6213 | const shift_amt = |
| 6213 | 6214 | try o.builder.intValue(containing_int.typeOfWip(&self.wip), bit_offset); |
src/type.zig+7-18| ... | ... | @@ -3021,27 +3021,16 @@ pub const Type = struct { |
| 3021 | 3021 | }; |
| 3022 | 3022 | } |
| 3023 | 3023 | |
| 3024 | pub fn packedStructFieldByteOffset(ty: Type, field_index: usize, mod: *Module) u32 { | |
| 3024 | pub fn packedStructFieldBitOffset(ty: Type, field_index: usize, mod: *Module) u32 { | |
| 3025 | 3025 | const ip = &mod.intern_pool; |
| 3026 | 3026 | const struct_type = ip.indexToKey(ty.toIntern()).struct_type; |
| 3027 | 3027 | assert(struct_type.layout == .Packed); |
| 3028 | comptime assert(Type.packed_struct_layout_version == 2); | |
| 3029 | ||
| 3030 | var bit_offset: u16 = undefined; | |
| 3031 | var elem_size_bits: u16 = undefined; | |
| 3032 | var running_bits: u16 = 0; | |
| 3033 | for (struct_type.field_types.get(ip), 0..) |field_ty, i| { | |
| 3034 | if (!field_ty.toType().hasRuntimeBits(mod)) continue; | |
| 3035 | ||
| 3036 | const field_bits: u16 = @intCast(field_ty.toType().bitSize(mod)); | |
| 3037 | if (i == field_index) { | |
| 3038 | bit_offset = running_bits; | |
| 3039 | elem_size_bits = field_bits; | |
| 3040 | } | |
| 3041 | running_bits += field_bits; | |
| 3042 | } | |
| 3043 | const byte_offset = bit_offset / 8; | |
| 3044 | return byte_offset; | |
| 3028 | assert(struct_type.haveLayout(ip)); | |
| 3029 | return struct_type.offsets.get(ip)[field_index]; | |
| 3030 | } | |
| 3031 | ||
| 3032 | pub fn packedStructFieldByteOffset(ty: Type, field_index: usize, mod: *Module) u32 { | |
| 3033 | return packedStructFieldBitOffset(ty, field_index, mod) / 8; | |
| 3045 | 3034 | } |
| 3046 | 3035 | |
| 3047 | 3036 | pub const FieldOffset = struct { |