authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-28 13:10:07+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:08+00:00
logb19074d252e7eb833b653263acd20e64a7fe26ff
treecec720566242209acffeb94f5afff3ad582fee14
parent911294116d5df0db3b431f9117f42bf8074a3b83
signaturelock-open Commit is signed but in an unrecognized format.

compiler: represent bitpacks as their backing integer

Now that https://github.com/ziglang/zig/issues/24657 has been implemented, the compiler can simplify its internal representation of comptime-known `packed struct` and `packed union` values. Instead of storing them field-wise, we can simply store their backing integer value. This simplifies many operations and improves efficiency in some cases.

19 files changed, 530 insertions(+), 501 deletions(-)

src/Air/print.zig+17-27
......@@ -692,33 +692,23 @@ const Writer = struct {
692692
693693 const zcu = w.pt.zcu;
694694 const ip = &zcu.intern_pool;
695 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
696 const struct_type: Type = .fromInterned(aggregate.ty);
697 switch (aggregate.storage) {
698 .elems => |elems| for (elems, 0..) |elem, i| {
699 switch (elem) {
700 .bool_true => {
701 const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;
702 assert(clobber.len != 0);
703 try s.writeAll(", ~{");
704 try s.writeAll(clobber);
705 try s.writeAll("}");
706 },
707 .bool_false => continue,
708 else => unreachable,
709 }
710 },
711 .repeated_elem => |elem| {
712 try s.writeAll(", ");
713 try s.writeAll(switch (elem) {
714 .bool_true => "<all clobbers>",
715 .bool_false => "<no clobbers>",
716 else => unreachable,
717 });
718 },
719 .bytes => |bytes| {
720 try s.print(", {x}", .{bytes});
721 },
695 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
696 const clobbers_ty = clobbers_val.typeOf(zcu);
697 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
698 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
699 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
700 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
701 const limb_bits = @bitSizeOf(std.math.big.Limb);
702 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
703 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
704 0 => continue, // field is false
705 1 => {}, // field is true
706 }
707 const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
708 assert(clobber.len != 0);
709 try s.writeAll(", ~{");
710 try s.writeAll(clobber);
711 try s.writeAll("}");
722712 }
723713 const asm_source = unwrapped_asm.source;
724714 try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)});
src/InternPool.zig+52-11
......@@ -2130,6 +2130,8 @@ pub const Key = union(enum) {
21302130 aggregate: Aggregate,
21312131 /// An instance of a union.
21322132 un: Union,
2133 /// An instance of a `packed struct` or `packed union`.
2134 bitpack: Bitpack,
21332135
21342136 /// A comptime function call with a memoized result.
21352137 memoized_call: Key.MemoizedCall,
......@@ -2681,6 +2683,15 @@ pub const Key = union(enum) {
26812683 };
26822684 };
26832685
2686 /// As well as a key, this type doubles as the payload in `extra` for `Tag.bitpack`.
2687 pub const Bitpack = struct {
2688 /// The `packed struct` or `packed union` type.
2689 ty: Index,
2690 /// The contents of the bitpack, represented as the backing integer value. The type of this
2691 /// value is the same as the backing integer type of `ty`.
2692 backing_int_val: Index,
2693 };
2694
26842695 pub const MemoizedCall = struct {
26852696 func: Index,
26862697 arg_values: []const Index,
......@@ -2919,6 +2930,8 @@ pub const Key = union(enum) {
29192930 asBytes(&e.relocation) ++
29202931 asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++
29212932 asBytes(&e.zir_index) ++ &[1]u8{@intFromEnum(e.source)}),
2933
2934 .bitpack => |bitpack| Hash.hash(seed, asBytes(&bitpack.ty) ++ asBytes(&bitpack.backing_int_val)),
29222935 };
29232936 }
29242937
......@@ -2996,6 +3009,10 @@ pub const Key = union(enum) {
29963009 const b_info = b.empty_enum_value;
29973010 return a_info == b_info;
29983011 },
3012 .bitpack => |a_info| {
3013 const b_info = b.bitpack;
3014 return a_info.ty == b_info.ty and a_info.backing_int_val == b_info.backing_int_val;
3015 },
29993016
30003017 .variable => |a_info| {
30013018 const b_info = b.variable;
......@@ -3271,6 +3288,7 @@ pub const Key = union(enum) {
32713288 .enum_tag,
32723289 .aggregate,
32733290 .un,
3291 .bitpack,
32743292 => |x| x.ty,
32753293
32763294 .enum_literal => .enum_literal_type,
......@@ -4417,6 +4435,7 @@ pub const Index = enum(u32) {
44174435 trailing: struct { element_values: []Index },
44184436 },
44194437 repeated: struct { data: *Repeated },
4438 bitpack: struct { data: *Key.Bitpack },
44204439
44214440 memoized_call: struct {
44224441 const @"data.args_len" = opaque {};
......@@ -5152,6 +5171,9 @@ pub const Tag = enum(u8) {
51525171 /// An instance of an array or vector with every element being the same value.
51535172 /// data is extra index to `Repeated`.
51545173 repeated,
5174 /// An instance of a `packed struct` or `packed union`.
5175 /// data is extra index to `Key.Bitpack`.
5176 bitpack,
51555177
51565178 /// A memoized comptime function call result.
51575179 /// data is extra index to `MemoizedCall`
......@@ -5485,6 +5507,7 @@ pub const Tag = enum(u8) {
54855507 .config = .{ .@"trailing.elements.len" = .@"payload.ty.payload.fields_len" },
54865508 },
54875509 .repeated = .{ .summary = .@"@as({.payload.ty%summary}, @splat({.payload.elem_val%summary}))", .payload = Repeated },
5510 .bitpack = .{ .summary = .@"@as({.payload.ty%summary}, {})", .payload = Key.Bitpack },
54885511
54895512 .memoized_call = .{
54905513 .summary = .@"@memoize({.payload.func%summary})",
......@@ -7043,6 +7066,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
70437066 },
70447067 .enum_literal => .{ .enum_literal = @enumFromInt(data) },
70457068 .enum_tag => .{ .enum_tag = extraData(unwrapped_index.getExtra(ip), Tag.EnumTag, data) },
7069 .bitpack => .{ .bitpack = extraData(unwrapped_index.getExtra(ip), Key.Bitpack, data) },
70467070
70477071 .memoized_call => {
70487072 const extra_list = unwrapped_index.getExtra(ip);
......@@ -7938,15 +7962,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
79387962 .aggregate => |aggregate| {
79397963 const ty_key = ip.indexToKey(aggregate.ty);
79407964 const len = ip.aggregateTypeLen(aggregate.ty);
7941 const child = switch (ty_key) {
7942 .array_type => |array_type| array_type.child,
7943 .vector_type => |vector_type| vector_type.child,
7944 .tuple_type, .struct_type => .none,
7945 else => unreachable,
7946 };
7947 const sentinel = switch (ty_key) {
7948 .array_type => |array_type| array_type.sentinel,
7949 .vector_type, .tuple_type, .struct_type => .none,
7965 const child: Index, const sentinel: Index = switch (ty_key) {
7966 .array_type => |array_type| .{ array_type.child, array_type.sentinel },
7967 .vector_type => |vector_type| .{ vector_type.child, .none },
7968 .tuple_type => .{ .none, .none },
7969 .struct_type => child: {
7970 assert(ip.loadStructType(aggregate.ty).layout != .@"packed");
7971 break :child .{ .none, .none };
7972 },
79507973 else => unreachable,
79517974 };
79527975 const len_including_sentinel = len + @intFromBool(sentinel != .none);
......@@ -8128,6 +8151,18 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
81288151 extra.appendSliceAssumeCapacity(.{@ptrCast(aggregate.storage.elems)});
81298152 if (sentinel != .none) extra.appendAssumeCapacity(.{@intFromEnum(sentinel)});
81308153 },
8154 .bitpack => |bitpack| {
8155 switch (ip.zigTypeTag(bitpack.ty)) {
8156 .@"struct" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadStructType(bitpack.ty).packed_backing_int_type),
8157 .@"union" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadUnionType(bitpack.ty).packed_backing_int_type),
8158 else => unreachable,
8159 }
8160 assert(!ip.isUndef(bitpack.backing_int_val));
8161 items.appendAssumeCapacity(.{
8162 .tag = .bitpack,
8163 .data = try addExtra(extra, bitpack),
8164 });
8165 },
81318166
81328167 .memoized_call => |memoized_call| {
81338168 for (memoized_call.arg_values) |arg| assert(arg != .none);
......@@ -9095,6 +9130,10 @@ pub fn getUnion(
90959130 tid: Zcu.PerThread.Id,
90969131 un: Key.Union,
90979132) Allocator.Error!Index {
9133 assert(un.ty != .none);
9134 assert(un.val != .none);
9135 assert(ip.loadUnionType(un.ty).layout != .@"packed");
9136
90989137 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });
90999138 defer gop.deinit();
91009139 if (gop == .existing) return gop.existing;
......@@ -9103,8 +9142,6 @@ pub fn getUnion(
91039142 const extra = local.getMutableExtra(gpa, io);
91049143 try items.ensureUnusedCapacity(1);
91059144
9106 assert(un.ty != .none);
9107 assert(un.val != .none);
91089145 items.appendAssumeCapacity(.{
91099146 .tag = .union_value,
91109147 .data = try addExtra(extra, un),
......@@ -11003,6 +11040,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
1100311040 .func_coerced => @sizeOf(Tag.FuncCoerced),
1100411041 .only_possible_value => 0,
1100511042 .union_value => @sizeOf(Key.Union),
11043 .bitpack => 2 * @sizeOf(u32),
1100611044
1100711045 .memoized_call => b: {
1100811046 const info = extraData(extra_list, MemoizedCall, data);
......@@ -11117,6 +11155,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
1111711155 .func_instance,
1111811156 .func_coerced,
1111911157 .union_value,
11158 .bitpack,
1112011159 .memoized_call,
1112111160 => try w.print("{d}", .{data}),
1112211161
......@@ -11871,6 +11910,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
1187111910 .bytes,
1187211911 .aggregate,
1187311912 .repeated,
11913 .bitpack,
1187411914 => |t| {
1187511915 const extra_list = unwrapped_index.getExtra(ip);
1187611916 return @enumFromInt(extra_list.view().items(.@"0")[item.data + std.meta.fieldIndex(t.Payload(), "ty").?]);
......@@ -12264,6 +12304,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
1226412304 .bytes,
1226512305 .aggregate,
1226612306 .repeated,
12307 .bitpack,
1226712308 // memoization, not types
1226812309 .memoized_call,
1226912310 => unreachable,
src/Sema.zig+39-11
......@@ -3583,7 +3583,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
35833583 },
35843584 .field => |idx| ptr: {
35853585 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
3586 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| {
3586 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| if (union_obj.layout == .auto) {
35873587 // As this is a union field, we must store to the pointer now to set the tag.
35883588 // The payload value will be stored later, so undef is a sufficent payload for now.
35893589 const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);
......@@ -3591,7 +3591,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
35913591 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx);
35923592 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
35933593 try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
3594 }
3594 };
35953595 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
35963596 },
35973597 .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(),
......@@ -18510,6 +18510,10 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1851018510
1851118511 const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);
1851218512
18513 if (union_ty.containerLayout(zcu) == .@"packed") {
18514 return sema.bitCast(block, union_ty, payload, block.nodeOffset(inst_data.src_node), payload_src);
18515 }
18516
1851318517 if (sema.resolveValue(payload)) |payload_val| {
1851418518 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
1851518519 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
......@@ -18643,6 +18647,10 @@ fn zirStructInit(
1864318647 const uncoerced_init_inst = sema.resolveInst(item.data.init);
1864418648 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
1864518649
18650 if (resolved_ty.containerLayout(zcu) == .@"packed") {
18651 return sema.bitCast(block, resolved_ty, init_inst, src, field_src);
18652 }
18653
1864618654 if (sema.resolveValue(init_inst)) |val| {
1864718655 const struct_val = Value.fromInterned(try pt.internUnion(.{
1864818656 .ty = resolved_ty.toIntern(),
......@@ -18789,15 +18797,35 @@ fn finishStructInit(
1878918797 }
1879018798 } else null;
1879118799
18792 const runtime_index = opt_runtime_index orelse {
18793 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
18794 for (elems, field_inits) |*elem, field_init| {
18795 elem.* = sema.resolveValue(field_init).?.toIntern();
18796 }
18797 const struct_val = try pt.aggregateValue(struct_ty, elems);
18798 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), init_src);
18799 const final_val = sema.resolveValue(final_val_inst).?;
18800 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
18800 const runtime_index = opt_runtime_index orelse switch (struct_ty.containerLayout(zcu)) {
18801 .auto, .@"extern" => {
18802 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
18803 for (elems, field_inits) |*elem, field_init| {
18804 elem.* = sema.resolveValue(field_init).?.toIntern();
18805 }
18806 const struct_val = try pt.aggregateValue(struct_ty, elems);
18807 const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
18808 return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
18809 },
18810 .@"packed" => {
18811 const buf = try sema.arena.alloc(u8, (struct_ty.bitSize(zcu) + 7) / 8);
18812 var bit_offset: u16 = 0;
18813 for (field_inits) |field_init| {
18814 const field_val = sema.resolveValue(field_init).?;
18815 field_val.writeToPackedMemory(pt, buf, bit_offset) catch |err| switch (err) {
18816 error.ReinterpretDeclRef => unreachable, // bitpack fields cannot be pointers
18817 error.OutOfMemory => |e| return e,
18818 };
18819 bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu));
18820 }
18821 assert(bit_offset == struct_ty.bitSize(zcu));
18822 const struct_val = Value.readFromPackedMemory(struct_ty, pt, buf, 0, sema.arena) catch |err| switch (err) {
18823 error.IllDefinedMemoryLayout => unreachable, // bitpacks have well-defined layout
18824 error.OutOfMemory => |e| return e,
18825 };
18826 const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
18827 return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
18828 },
1880118829 };
1880218830
1880318831 if (struct_ty.comptimeOnly(zcu)) {
src/Sema/bitcast.zig+87-84
......@@ -273,6 +273,8 @@ const UnpackValueBits = struct {
273273 .opt,
274274 => try unpack.primitive(val),
275275
276 .bitpack => |bitpack| try unpack.primitive(.fromInterned(bitpack.backing_int_val)),
277
276278 .aggregate => switch (ty.zigTypeTag(zcu)) {
277279 .vector => {
278280 const len: usize = @intCast(ty.arrayLen(zcu));
......@@ -443,7 +445,7 @@ const UnpackValueBits = struct {
443445 // This @intCast is okay because no primitive can exceed the size of a u16.
444446 const int_ty = try unpack.pt.intType(.unsigned, @intCast(bit_count));
445447 const buf = try unpack.arena.alloc(u8, @intCast((val_bits + 7) / 8));
446 try val.writeToPackedMemory(ty, unpack.pt, buf, 0);
448 try val.writeToPackedMemory(unpack.pt, buf, 0);
447449 const sub_val = try Value.readFromPackedMemory(int_ty, unpack.pt, buf, @intCast(bit_offset), unpack.arena);
448450 try unpack.primitive(sub_val);
449451 },
......@@ -565,102 +567,103 @@ const PackValueBits = struct {
565567 return pt.aggregateValue(ty, elems);
566568 },
567569 .@"packed" => {
568 // All fields are in order with no padding.
569 // This is identical between LE and BE targets.
570 const elems = try arena.alloc(InternPool.Index, ty.structFieldCount(zcu));
571 for (elems, 0..) |*elem, i| {
572 const field_ty = ty.fieldType(i, zcu);
573 elem.* = (try pack.get(field_ty)).toIntern();
574 }
575 return pt.aggregateValue(ty, elems);
570 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
571 return pt.bitpackValue(ty, backing_int_val);
576572 },
577573 },
578 .@"union" => {
579 // We will attempt to read as the backing representation. If this emits
580 // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones.
581 // We will also attempt smaller fields when we get `undefined`, as if some bits are
582 // defined we want to include them.
583 // TODO: this is very very bad. We need a more sophisticated union representation.
584
585 const prev_unpacked = pack.unpacked;
586 const prev_bit_offset = pack.bit_offset;
587
588 const backing_ty = try ty.unionBackingType(pt);
589
590 backing: {
591 const backing_val = pack.get(backing_ty) catch |err| switch (err) {
592 error.ReinterpretDeclRef => {
574 .@"union" => switch (ty.containerLayout(zcu)) {
575 .auto => unreachable, // ill-defined layout
576 .@"extern" => {
577 // We will attempt to read as the backing representation. If this emits
578 // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones.
579 // We will also attempt smaller fields when we get `undefined`, as if some bits are
580 // defined we want to include them.
581 // TODO: this is very very bad. We need a more sophisticated union representation.
582
583 const prev_unpacked = pack.unpacked;
584 const prev_bit_offset = pack.bit_offset;
585
586 const backing_ty = try ty.externUnionBackingType(pt);
587
588 backing: {
589 const backing_val = pack.get(backing_ty) catch |err| switch (err) {
590 error.ReinterpretDeclRef => {
591 pack.unpacked = prev_unpacked;
592 pack.bit_offset = prev_bit_offset;
593 break :backing;
594 },
595 else => |e| return e,
596 };
597 if (backing_val.isUndef(zcu)) {
593598 pack.unpacked = prev_unpacked;
594599 pack.bit_offset = prev_bit_offset;
595600 break :backing;
596 },
597 else => |e| return e,
598 };
599 if (backing_val.isUndef(zcu)) {
600 pack.unpacked = prev_unpacked;
601 pack.bit_offset = prev_bit_offset;
602 break :backing;
601 }
602 return Value.fromInterned(try pt.internUnion(.{
603 .ty = ty.toIntern(),
604 .tag = .none,
605 .val = backing_val.toIntern(),
606 }));
603607 }
604 return Value.fromInterned(try pt.internUnion(.{
605 .ty = ty.toIntern(),
606 .tag = .none,
607 .val = backing_val.toIntern(),
608 }));
609 }
610608
611 const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu));
612 for (field_order, 0..) |*f, i| f.* = @intCast(i);
613 // Sort `field_order` to put the fields with the largest bit sizes first.
614 const SizeSortCtx = struct {
615 zcu: *Zcu,
616 field_types: []const InternPool.Index,
617 fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool {
618 const a_ty = Type.fromInterned(ctx.field_types[a_idx]);
619 const b_ty = Type.fromInterned(ctx.field_types[b_idx]);
620 return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu);
621 }
622 };
623 std.mem.sortUnstable(u32, field_order, SizeSortCtx{
624 .zcu = zcu,
625 .field_types = zcu.typeToUnion(ty).?.field_types.get(ip),
626 }, SizeSortCtx.lessThan);
627
628 const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed";
629
630 for (field_order) |field_idx| {
631 const field_ty = Type.fromInterned(zcu.typeToUnion(ty).?.field_types.get(ip)[field_idx]);
632 const pad_bits = ty.bitSize(zcu) - field_ty.bitSize(zcu);
633 if (!padding_after) try pack.padding(pad_bits);
634 const field_val = pack.get(field_ty) catch |err| switch (err) {
635 error.ReinterpretDeclRef => {
609 const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu));
610 for (field_order, 0..) |*f, i| f.* = @intCast(i);
611 // Sort `field_order` to put the fields with the largest bit sizes first.
612 const SizeSortCtx = struct {
613 zcu: *Zcu,
614 field_types: []const InternPool.Index,
615 fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool {
616 const a_ty = Type.fromInterned(ctx.field_types[a_idx]);
617 const b_ty = Type.fromInterned(ctx.field_types[b_idx]);
618 return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu);
619 }
620 };
621 std.mem.sortUnstable(u32, field_order, SizeSortCtx{
622 .zcu = zcu,
623 .field_types = zcu.typeToUnion(ty).?.field_types.get(ip),
624 }, SizeSortCtx.lessThan);
625
626 const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed";
627
628 for (field_order) |field_idx| {
629 const field_ty = Type.fromInterned(zcu.typeToUnion(ty).?.field_types.get(ip)[field_idx]);
630 const pad_bits = ty.bitSize(zcu) - field_ty.bitSize(zcu);
631 if (!padding_after) try pack.padding(pad_bits);
632 const field_val = pack.get(field_ty) catch |err| switch (err) {
633 error.ReinterpretDeclRef => {
634 pack.unpacked = prev_unpacked;
635 pack.bit_offset = prev_bit_offset;
636 continue;
637 },
638 else => |e| return e,
639 };
640 if (padding_after) try pack.padding(pad_bits);
641 if (field_val.isUndef(zcu)) {
636642 pack.unpacked = prev_unpacked;
637643 pack.bit_offset = prev_bit_offset;
638644 continue;
639 },
640 else => |e| return e,
641 };
642 if (padding_after) try pack.padding(pad_bits);
643 if (field_val.isUndef(zcu)) {
644 pack.unpacked = prev_unpacked;
645 pack.bit_offset = prev_bit_offset;
646 continue;
645 }
646 const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx);
647 return Value.fromInterned(try pt.internUnion(.{
648 .ty = ty.toIntern(),
649 .tag = tag_val.toIntern(),
650 .val = field_val.toIntern(),
651 }));
647652 }
648 const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx);
653
654 // No field could represent the value. Just do whatever happens when we try to read
655 // the backing type - either `undefined` or `error.ReinterpretDeclRef`.
656 const backing_val = try pack.get(backing_ty);
649657 return Value.fromInterned(try pt.internUnion(.{
650658 .ty = ty.toIntern(),
651 .tag = tag_val.toIntern(),
652 .val = field_val.toIntern(),
659 .tag = .none,
660 .val = backing_val.toIntern(),
653661 }));
654 }
655
656 // No field could represent the value. Just do whatever happens when we try to read
657 // the backing type - either `undefined` or `error.ReinterpretDeclRef`.
658 const backing_val = try pack.get(backing_ty);
659 return Value.fromInterned(try pt.internUnion(.{
660 .ty = ty.toIntern(),
661 .tag = .none,
662 .val = backing_val.toIntern(),
663 }));
662 },
663 .@"packed" => {
664 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
665 return pt.bitpackValue(ty, backing_int_val);
666 },
664667 },
665668 else => return pack.primitive(ty),
666669 }
......@@ -722,7 +725,7 @@ const PackValueBits = struct {
722725 const val = Value.fromInterned(ip_val);
723726 const ty = val.typeOf(zcu);
724727 if (!val.isUndef(zcu)) {
725 try val.writeToPackedMemory(ty, pt, buf, cur_bit_off);
728 try val.writeToPackedMemory(pt, buf, cur_bit_off);
726729 }
727730 cur_bit_off += @intCast(ty.bitSize(zcu));
728731 }
src/Sema/type_resolution.zig+1
......@@ -79,6 +79,7 @@ pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!vo
7979 .opt,
8080 .aggregate,
8181 .un,
82 .bitpack,
8283 // memoization, not types
8384 .memoized_call,
8485 => unreachable,
src/Type.zig+33-8
......@@ -407,6 +407,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari
407407 .opt,
408408 .aggregate,
409409 .un,
410 .bitpack,
410411 // memoization, not types
411412 .memoized_call,
412413 => unreachable,
......@@ -543,6 +544,7 @@ pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
543544 .opt,
544545 .aggregate,
545546 .un,
547 .bitpack,
546548 // memoization, not types
547549 .memoized_call,
548550 => unreachable,
......@@ -639,6 +641,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
639641 .opt,
640642 .aggregate,
641643 .un,
644 .bitpack,
642645 // memoization, not types
643646 .memoized_call,
644647 => unreachable,
......@@ -846,6 +849,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
846849 .opt,
847850 .aggregate,
848851 .un,
852 .bitpack,
849853 // memoization, not types
850854 .memoized_call,
851855 => unreachable,
......@@ -978,6 +982,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
978982 .opt,
979983 .aggregate,
980984 .un,
985 .bitpack,
981986 // memoization, not types
982987 .memoized_call,
983988 => unreachable,
......@@ -1102,6 +1107,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
11021107 .opt,
11031108 .aggregate,
11041109 .un,
1110 .bitpack,
11051111 // memoization, not types
11061112 .memoized_call,
11071113 => unreachable,
......@@ -1393,16 +1399,16 @@ pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *Zcu) bool {
13931399}
13941400
13951401/// Returns the type used for backing storage of this union during comptime operations.
1396/// Asserts the type is either an extern or packed union.
1397pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
1402/// Asserts the type is an extern union.
1403pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
13981404 const zcu = pt.zcu;
13991405 assertHasLayout(ty, zcu);
14001406 const loaded_union = zcu.intern_pool.loadUnionType(ty.toIntern());
1401 return switch (loaded_union.layout) {
1402 .@"extern" => try pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),
1403 .@"packed" => .fromInterned(loaded_union.packed_backing_int_type),
1407 switch (loaded_union.layout) {
1408 .@"extern" => return pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),
1409 .@"packed" => unreachable,
14041410 .auto => unreachable,
1405 };
1411 }
14061412}
14071413
14081414pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {
......@@ -1421,6 +1427,15 @@ pub fn containerLayout(ty: Type, zcu: *const Zcu) std.builtin.Type.ContainerLayo
14211427 };
14221428}
14231429
1430pub fn bitpackBackingInt(ty: Type, zcu: *const Zcu) Type {
1431 const ip = &zcu.intern_pool;
1432 return switch (ip.indexToKey(ty.toIntern())) {
1433 .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),
1434 .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),
1435 else => unreachable,
1436 };
1437}
1438
14241439/// Asserts that the type is an error union.
14251440pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
14261441 return Type.fromInterned(zcu.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type);
......@@ -1635,6 +1650,7 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
16351650 .opt,
16361651 .aggregate,
16371652 .un,
1653 .bitpack,
16381654 // memoization, not types
16391655 .memoized_call,
16401656 => unreachable,
......@@ -1842,7 +1858,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
18421858 if (struct_obj.layout == .@"packed") {
18431859 const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
18441860 const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
1845 _ = backing_val; // MLUGG TODO: represent unions as their bits!
1861 return try pt.bitpackValue(ty, backing_val);
18461862 } else {
18471863 if (!struct_obj.has_one_possible_value) return null;
18481864 }
......@@ -1893,8 +1909,13 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
18931909 },
18941910
18951911 .union_type => {
1896 // MLUGG TODO: is this nonsensical or what!!!!!!
18971912 const union_obj = ip.loadUnionType(ty.toIntern());
1913 if (union_obj.layout == .@"packed") {
1914 const backing_ty: Type = .fromInterned(union_obj.packed_backing_int_type);
1915 const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
1916 return try pt.bitpackValue(ty, backing_val);
1917 }
1918 // MLUGG TODO: is this nonsensical or what!!!!!!
18981919 const tag_val = (try Type.fromInterned(union_obj.enum_tag_type).onePossibleValue(pt)) orelse
18991920 return null;
19001921 if (union_obj.field_types.len == 0) {
......@@ -1957,6 +1978,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
19571978 .opt,
19581979 .aggregate,
19591980 .un,
1981 .bitpack,
19601982 // memoization, not types
19611983 .memoized_call,
19621984 => unreachable,
......@@ -2061,6 +2083,7 @@ pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {
20612083 .opt,
20622084 .aggregate,
20632085 .un,
2086 .bitpack,
20642087 // memoization, not types
20652088 .memoized_call,
20662089 => unreachable,
......@@ -3080,6 +3103,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
30803103 .opt,
30813104 .aggregate,
30823105 .un,
3106 .bitpack,
30833107 .undef,
30843108 // memoization, not types
30853109 .memoized_call,
......@@ -3158,6 +3182,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn
31583182 .opt,
31593183 .aggregate,
31603184 .un,
3185 .bitpack,
31613186 // memoization, not types
31623187 .memoized_call,
31633188 => unreachable,
src/Value.zig+75-85
......@@ -158,6 +158,7 @@ pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *Zcu) BigIntConst {
158158 const ip = &zcu.intern_pool;
159159 const int_key = switch (ip.indexToKey(val.toIntern())) {
160160 .enum_tag => |enum_tag| ip.indexToKey(enum_tag.int).int,
161 .bitpack => |bitpack| ip.indexToKey(bitpack.backing_int_val).int,
161162 .int => |int| int,
162163 else => unreachable,
163164 };
......@@ -216,6 +217,7 @@ pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
216217 else => |payload| Value.fromInterned(payload).getUnsignedInt(zcu),
217218 },
218219 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).getUnsignedInt(zcu),
220 .bitpack => |bitpack| Value.fromInterned(bitpack.backing_int_val).getUnsignedInt(zcu),
219221 .err => |err| zcu.intern_pool.getErrorValueIfExists(err.name).?,
220222 else => null,
221223 },
......@@ -309,7 +311,7 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
309311 // We use byte_count instead of abi_size here, so that any padding bytes
310312 // follow the data bytes, on both big- and little-endian systems.
311313 const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;
312 return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
314 return writeToPackedMemory(val, pt, buffer[0..byte_count], 0);
313315 },
314316 .@"struct" => {
315317 const struct_type = zcu.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout;
......@@ -328,8 +330,8 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
328330 try writeToMemory(field_val, pt, buffer[off..]);
329331 },
330332 .@"packed" => {
331 const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;
332 return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
333 const int_index = ip.indexToKey(val.toIntern()).bitpack.backing_int_val;
334 return Value.fromInterned(int_index).writeToMemory(pt, buffer);
333335 },
334336 }
335337 },
......@@ -344,15 +346,14 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
344346 const byte_count: usize = @intCast(field_type.abiSize(zcu));
345347 return writeToMemory(field_val, pt, buffer[0..byte_count]);
346348 } else {
347 const backing_ty = try ty.unionBackingType(pt);
349 const backing_ty = try ty.externUnionBackingType(pt);
348350 const byte_count: usize = @intCast(backing_ty.abiSize(zcu));
349351 return writeToMemory(val.unionValue(zcu), pt, buffer[0..byte_count]);
350352 }
351353 },
352354 .@"packed" => {
353 const backing_ty = try ty.unionBackingType(pt);
354 const byte_count: usize = @intCast(backing_ty.abiSize(zcu));
355 return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
355 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
356 return writeToMemory(int_val, pt, buffer);
356357 },
357358 },
358359 .optional => {
......@@ -374,7 +375,6 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
374375/// big-endian packed memory layouts start at the end of the buffer.
375376pub fn writeToPackedMemory(
376377 val: Value,
377 ty: Type,
378378 pt: Zcu.PerThread,
379379 buffer: []u8,
380380 bit_offset: usize,
......@@ -383,6 +383,7 @@ pub fn writeToPackedMemory(
383383 const ip = &zcu.intern_pool;
384384 const target = zcu.getTarget();
385385 const endian = target.cpu.arch.endian();
386 const ty = val.typeOf(zcu);
386387 if (val.isUndef(zcu)) {
387388 const bit_size: usize = @intCast(ty.bitSize(zcu));
388389 if (bit_size != 0) {
......@@ -405,7 +406,13 @@ pub fn writeToPackedMemory(
405406 },
406407 .@"enum" => {
407408 const int_val = val.intFromEnum(zcu);
408 return int_val.writeToPackedMemory(int_val.typeOf(zcu), pt, buffer, bit_offset);
409 return int_val.writeToPackedMemory(pt, buffer, bit_offset);
410 },
411 .pointer => {
412 assert(!ty.isSlice(zcu)); // No well defined layout.
413 if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef;
414 const addr = val.toUnsignedInt(zcu);
415 std.mem.writeVarPackedInt(buffer, bit_offset, zcu.getTarget().ptrBitWidth(), addr, endian);
409416 },
410417 .int => {
411418 const bits = ty.intInfo(zcu).bits;
......@@ -434,54 +441,21 @@ pub fn writeToPackedMemory(
434441 // On big-endian systems, LLVM reverses the element order of vectors by default
435442 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
436443 const elem_val = try val.elemValue(pt, tgt_elem_i);
437 try elem_val.writeToPackedMemory(elem_ty, pt, buffer, bit_offset + bits);
444 try elem_val.writeToPackedMemory(pt, buffer, bit_offset + bits);
438445 bits += elem_bit_size;
439446 }
440447 },
441 .@"struct" => {
442 const struct_type = ip.loadStructType(ty.toIntern());
443 // Sema is supposed to have emitted a compile error already in the case of Auto,
444 // and Extern is handled in non-packed writeToMemory.
445 assert(struct_type.layout == .@"packed");
446 var bits: u16 = 0;
447 for (0..struct_type.field_types.len) |i| {
448 const field_val = Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
449 .bytes => unreachable,
450 .elems => |elems| elems[i],
451 .repeated_elem => |elem| elem,
452 });
453 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
454 const field_bits: u16 = @intCast(field_ty.bitSize(zcu));
455 try field_val.writeToPackedMemory(field_ty, pt, buffer, bit_offset + bits);
456 bits += field_bits;
457 }
458 },
459 .@"union" => {
460 const union_obj = zcu.typeToUnion(ty).?;
461 assert(union_obj.layout == .@"packed");
462 if (val.unionTag(zcu)) |union_tag| {
463 const field_index = zcu.unionTagFieldIndex(union_obj, union_tag).?;
464 const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
465 const field_val = try val.fieldValue(pt, field_index);
466 return field_val.writeToPackedMemory(field_type, pt, buffer, bit_offset);
467 } else {
468 const backing_ty = try ty.unionBackingType(pt);
469 return val.unionValue(zcu).writeToPackedMemory(backing_ty, pt, buffer, bit_offset);
470 }
471 },
472 .pointer => {
473 assert(!ty.isSlice(zcu)); // No well defined layout.
474 if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef;
475 return val.writeToPackedMemory(Type.usize, pt, buffer, bit_offset);
448 .@"struct", .@"union" => {
449 assert(ty.containerLayout(zcu) == .@"packed");
450 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
451 return int_val.writeToPackedMemory(pt, buffer, bit_offset);
476452 },
477453 .optional => {
478454 assert(ty.isPtrLikeOptional(zcu));
479 const child = ty.optionalChild(zcu);
480 const opt_val = val.optionalValue(zcu);
481 if (opt_val) |some| {
482 return some.writeToPackedMemory(child, pt, buffer, bit_offset);
455 if (val.optionalValue(zcu)) |ptr_val| {
456 return ptr_val.writeToPackedMemory(pt, buffer, bit_offset);
483457 } else {
484 return writeToPackedMemory(try pt.intValue(Type.usize, 0), Type.usize, pt, buffer, bit_offset);
458 return Value.zero_usize.writeToPackedMemory(pt, buffer, bit_offset);
485459 }
486460 },
487461 else => @panic("TODO implement writeToPackedMemory for more types"),
......@@ -531,13 +505,12 @@ pub fn readFromPackedMemory(
531505 pt: Zcu.PerThread,
532506 buffer: []const u8,
533507 bit_offset: usize,
534 arena: Allocator,
508 gpa: Allocator,
535509) error{
536510 IllDefinedMemoryLayout,
537511 OutOfMemory,
538512}!Value {
539513 const zcu = pt.zcu;
540 const ip = &zcu.intern_pool;
541514 const target = zcu.getTarget();
542515 const endian = target.cpu.arch.endian();
543516 switch (ty.zigTypeTag(zcu)) {
......@@ -571,7 +544,8 @@ pub fn readFromPackedMemory(
571544 const abi_size: usize = @intCast(ty.abiSize(zcu));
572545 const Limb = std.math.big.Limb;
573546 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
574 const limbs_buffer = try arena.alloc(Limb, limb_count);
547 const limbs_buffer = try gpa.alloc(Limb, limb_count);
548 defer gpa.free(limbs_buffer);
575549
576550 var bigint = BigIntMutable.init(limbs_buffer, 0);
577551 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
......@@ -579,7 +553,7 @@ pub fn readFromPackedMemory(
579553 },
580554 .@"enum" => {
581555 const int_ty = ty.intTagType(zcu);
582 const int_val = try Value.readFromPackedMemory(int_ty, pt, buffer, bit_offset, arena);
556 const int_val = try Value.readFromPackedMemory(int_ty, pt, buffer, bit_offset, gpa);
583557 return pt.getCoerced(int_val, ty);
584558 },
585559 .float => return Value.fromInterned(try pt.intern(.{ .float = .{
......@@ -595,52 +569,32 @@ pub fn readFromPackedMemory(
595569 } })),
596570 .vector => {
597571 const elem_ty = ty.childType(zcu);
598 const elems = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu)));
572 const elems = try gpa.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu)));
573 defer gpa.free(elems);
599574
600575 var bits: u16 = 0;
601576 const elem_bit_size: u16 = @intCast(elem_ty.bitSize(zcu));
602577 for (elems, 0..) |_, i| {
603578 // On big-endian systems, LLVM reverses the element order of vectors by default
604579 const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;
605 elems[tgt_elem_i] = (try readFromPackedMemory(elem_ty, pt, buffer, bit_offset + bits, arena)).toIntern();
580 elems[tgt_elem_i] = (try readFromPackedMemory(elem_ty, pt, buffer, bit_offset + bits, gpa)).toIntern();
606581 bits += elem_bit_size;
607582 }
608583 return pt.aggregateValue(ty, elems);
609584 },
610 .@"struct" => {
611 // Sema is supposed to have emitted a compile error already for Auto layout structs,
612 // and Extern is handled by non-packed readFromMemory.
613 const struct_type = zcu.typeToPackedStruct(ty).?;
614 var bits: u16 = 0;
615 const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);
616 for (field_vals, 0..) |*field_val, i| {
617 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
618 const field_bits: u16 = @intCast(field_ty.bitSize(zcu));
619 field_val.* = (try readFromPackedMemory(field_ty, pt, buffer, bit_offset + bits, arena)).toIntern();
620 bits += field_bits;
621 }
622 return pt.aggregateValue(ty, field_vals);
623 },
624 .@"union" => switch (ty.containerLayout(zcu)) {
625 .auto, .@"extern" => unreachable, // Handled by non-packed readFromMemory
626 .@"packed" => {
627 const backing_ty = try ty.unionBackingType(pt);
628 const val = (try readFromPackedMemory(backing_ty, pt, buffer, bit_offset, arena)).toIntern();
629 return Value.fromInterned(try pt.internUnion(.{
630 .ty = ty.toIntern(),
631 .tag = .none,
632 .val = val,
633 }));
634 },
585 .@"struct", .@"union" => {
586 assert(ty.containerLayout(zcu) == .@"packed");
587 const int_val: Value = try .readFromPackedMemory(ty.bitpackBackingInt(zcu), pt, buffer, bit_offset, gpa);
588 return pt.bitpackValue(ty, int_val);
635589 },
636590 .pointer => {
637591 assert(!ty.isSlice(zcu)); // No well defined layout.
638 const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena)).toUnsignedInt(zcu);
592 const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, gpa)).toUnsignedInt(zcu);
639593 return pt.ptrIntValue(ty, addr);
640594 },
641595 .optional => {
642596 assert(ty.isPtrLikeOptional(zcu));
643 const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena)).toUnsignedInt(zcu);
597 const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, gpa)).toUnsignedInt(zcu);
644598 return .fromInterned(try pt.intern(.{ .opt = .{
645599 .ty = ty.toIntern(),
646600 .val = if (addr == 0) .none else (try pt.ptrIntValue(ty.childType(zcu), addr)).toIntern(),
......@@ -915,8 +869,44 @@ pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value {
915869 .elems => |elems| elems[index],
916870 .repeated_elem => |elem| elem,
917871 }),
918 // TODO assert the tag is correct
919 .un => |un| Value.fromInterned(un.val),
872 .un => |un| {
873 switch (Type.fromInterned(un.ty).containerLayout(zcu)) {
874 .auto, .@"extern" => {}, // TODO assert the tag is correct
875 .@"packed" => unreachable,
876 }
877 return .fromInterned(un.val);
878 },
879 .bitpack => |bitpack| {
880 const ty: Type = .fromInterned(bitpack.ty);
881 assert(ty.containerLayout(zcu) == .@"packed");
882 const int_val: Value = .fromInterned(bitpack.backing_int_val);
883 assert(!int_val.isUndef(zcu));
884 const field_ty = ty.fieldType(index, zcu);
885 const field_bit_offset: u16 = switch (ty.zigTypeTag(zcu)) {
886 .@"union" => 0,
887 .@"struct" => off: {
888 var off: u16 = 0;
889 for (0..index) |preceding_field_index| {
890 off += @intCast(ty.fieldType(preceding_field_index, zcu).bitSize(zcu));
891 }
892 break :off off;
893 },
894 else => unreachable,
895 };
896 // Avoid hitting gpa for accesses to small packed structs
897 var sfba_state = std.heap.stackFallback(128, zcu.comp.gpa);
898 const sfba = sfba_state.get();
899 const buf = try sfba.alloc(u8, (ty.bitSize(zcu) + 7) / 8);
900 defer sfba.free(buf);
901 int_val.writeToPackedMemory(pt, buf, 0) catch |err| switch (err) {
902 error.ReinterpretDeclRef => unreachable, // it's an integer
903 error.OutOfMemory => |e| return e,
904 };
905 return Value.readFromPackedMemory(field_ty, pt, buf, field_bit_offset, sfba) catch |err| switch (err) {
906 error.IllDefinedMemoryLayout => unreachable, // it's a bitpack
907 error.OutOfMemory => |e| return e,
908 };
909 },
920910 else => unreachable,
921911 };
922912}
src/Zcu/PerThread.zig+9
......@@ -3950,6 +3950,15 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value
39503950 } }));
39513951}
39523952
3953/// Create a value whose type is a `packed struct` or `packed union`, from the backing integer value.
3954pub fn bitpackValue(pt: Zcu.PerThread, ty: Type, backing_int_val: Value) Allocator.Error!Value {
3955 assert(backing_int_val.typeOf(pt.zcu).toIntern() == ty.bitpackBackingInt(pt.zcu).toIntern());
3956 return .fromInterned(try pt.intern(.{ .bitpack = .{
3957 .ty = ty.toIntern(),
3958 .backing_int_val = backing_int_val.toIntern(),
3959 } }));
3960}
3961
39533962pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value {
39543963 assert(pt.zcu.intern_pool.isOptionalType(opt_ty.toIntern()));
39553964 return Value.fromInterned(try pt.intern(.{ .opt = .{
src/codegen.zig+6-36
......@@ -570,42 +570,7 @@ pub fn generateSymbol(
570570 .struct_type => {
571571 const struct_type = ip.loadStructType(ty.toIntern());
572572 switch (struct_type.layout) {
573 .@"packed" => {
574 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
575 const start = w.end;
576 const buffer = try w.writableSlice(abi_size);
577 @memset(buffer, 0);
578 var bits: u16 = 0;
579
580 for (struct_type.field_types.get(ip), 0..) |field_ty, index| {
581 const field_val = switch (aggregate.storage) {
582 .bytes => |bytes| try pt.intern(.{ .int = .{
583 .ty = field_ty,
584 .storage = .{ .u64 = bytes.at(index, ip) },
585 } }),
586 .elems => |elems| elems[index],
587 .repeated_elem => |elem| elem,
588 };
589
590 // pointer may point to a decl which must be marked used
591 // but can also result in a relocation. Therefore we handle those separately.
592 if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) {
593 const field_offset = std.math.divExact(u16, bits, 8) catch |err| switch (err) {
594 error.DivisionByZero => unreachable,
595 error.UnexpectedRemainder => return error.RelocationNotByteAligned,
596 };
597 w.end = start + field_offset;
598 defer {
599 assert(w.end == start + field_offset + @divExact(target.ptrBitWidth(), 8));
600 w.end = start + abi_size;
601 }
602 try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
603 } else {
604 Value.fromInterned(field_val).writeToPackedMemory(.fromInterned(field_ty), pt, buffer, bits) catch unreachable;
605 }
606 bits += @intCast(Type.fromInterned(field_ty).bitSize(zcu));
607 }
608 },
573 .@"packed" => unreachable,
609574 .auto, .@"extern" => {
610575 const struct_begin = w.end;
611576 const field_types = struct_type.field_types.get(ip);
......@@ -683,6 +648,7 @@ pub fn generateSymbol(
683648 }
684649 }
685650 },
651 .bitpack => |bitpack| try generateSymbol(bin_file, pt, src_loc, .fromInterned(bitpack.backing_int_val), w, reloc_parent),
686652 .memoized_call => unreachable,
687653 }
688654}
......@@ -1120,6 +1086,10 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo
11201086 target,
11211087 );
11221088 },
1089 .@"struct", .@"union" => if (ty.containerLayout(zcu) == .@"packed") {
1090 const bitpack = ip.indexToKey(val.toIntern()).bitpack;
1091 return lowerValue(pt, .fromInterned(bitpack.backing_int_val), target);
1092 },
11231093 .error_set => {
11241094 const err_name = ip.indexToKey(val.toIntern()).err.name;
11251095 const error_index = ip.getErrorValueIfExists(err_name).?;
src/codegen/aarch64/Select.zig+20-26
......@@ -2791,17 +2791,17 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
27912791 } else return isel.fail("invalid constraint: '{s}'", .{constraint});
27922792 }
27932793
2794 const clobbers = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
2795 const clobbers_ty: ZigType = .fromInterned(clobbers.ty);
2794 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
2795 const clobbers_ty = clobbers_val.typeOf(zcu);
2796 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
2797 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
27962798 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
2797 switch (switch (clobbers.storage) {
2798 .bytes => unreachable,
2799 .elems => |elems| elems[field_index],
2800 .repeated_elem => |repeated_elem| repeated_elem,
2801 }) {
2802 else => unreachable,
2803 .bool_false => continue,
2804 .bool_true => {},
2799 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
2800 const limb_bits = @bitSizeOf(std.math.big.Limb);
2801 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
2802 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
2803 0 => continue, // field is false
2804 1 => {}, // field is true
28052805 }
28062806 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
28072807 if (std.mem.eql(u8, clobber_name, "memory")) continue;
......@@ -2816,14 +2816,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
28162816 }
28172817 }
28182818 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
2819 switch (switch (clobbers.storage) {
2820 .bytes => unreachable,
2821 .elems => |elems| elems[field_index],
2822 .repeated_elem => |repeated_elem| repeated_elem,
2823 }) {
2824 else => unreachable,
2825 .bool_false => continue,
2826 .bool_true => {},
2819 const limb_bits = @bitSizeOf(std.math.big.Limb);
2820 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
2821 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> field_index % limb_bits))) {
2822 0 => continue, // field is false
2823 1 => {}, // field is true
28272824 }
28282825 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
28292826 if (std.mem.eql(u8, clobber_name, "memory")) continue;
......@@ -2872,14 +2869,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
28722869 }
28732870
28742871 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
2875 switch (switch (clobbers.storage) {
2876 .bytes => unreachable,
2877 .elems => |elems| elems[field_index],
2878 .repeated_elem => |repeated_elem| repeated_elem,
2879 }) {
2880 else => unreachable,
2881 .bool_false => continue,
2882 .bool_true => {},
2872 const limb_bits = @bitSizeOf(std.math.big.Limb);
2873 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
2874 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> field_index % limb_bits))) {
2875 0 => continue, // field is false
2876 1 => {}, // field is true
28832877 }
28842878 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
28852879 if (std.mem.eql(u8, clobber_name, "memory")) continue;
src/codegen/c.zig+68-114
......@@ -1362,77 +1362,42 @@ pub const DeclGen = struct {
13621362 },
13631363 .struct_type => {
13641364 const loaded_struct = ip.loadStructType(ty.toIntern());
1365 switch (loaded_struct.layout) {
1366 .auto, .@"extern" => {
1367 if (!location.isInitializer()) {
1368 try w.writeByte('(');
1369 try dg.renderCType(w, ctype);
1370 try w.writeByte(')');
1371 }
1365 assert(loaded_struct.layout != .@"packed");
13721366
1373 try w.writeByte('{');
1374 var field_it = loaded_struct.iterateRuntimeOrder(ip);
1375 var need_comma = false;
1376 while (field_it.next()) |field_index| {
1377 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
1378 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
1367 if (!location.isInitializer()) {
1368 try w.writeByte('(');
1369 try dg.renderCType(w, ctype);
1370 try w.writeByte(')');
1371 }
13791372
1380 if (need_comma) try w.writeByte(',');
1381 need_comma = true;
1382 const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
1383 .bytes => |bytes| try pt.intern(.{ .int = .{
1384 .ty = field_ty.toIntern(),
1385 .storage = .{ .u64 = bytes.at(field_index, ip) },
1386 } }),
1387 .elems => |elems| elems[field_index],
1388 .repeated_elem => |elem| elem,
1389 };
1390 try dg.renderValue(w, Value.fromInterned(field_val), initializer_type);
1391 }
1392 try w.writeByte('}');
1393 },
1394 .@"packed" => {
1395 // https://github.com/ziglang/zig/issues/24657 will eliminate most of the
1396 // following logic, leaving only the recursive `renderValue` call. Once
1397 // that proposal is implemented, a `packed struct` will literally be
1398 // represented in the InternPool by its comptime-known backing integer.
1399 var arena: std.heap.ArenaAllocator = .init(zcu.gpa);
1400 defer arena.deinit();
1401 const backing_ty: Type = .fromInterned(loaded_struct.backingIntTypeUnordered(ip));
1402 const buf = try arena.allocator().alloc(u8, @intCast(ty.abiSize(zcu)));
1403 val.writeToMemory(pt, buf) catch |err| switch (err) {
1404 error.IllDefinedMemoryLayout => unreachable,
1405 error.OutOfMemory => |e| return e,
1406 error.ReinterpretDeclRef, error.Unimplemented => return dg.fail("TODO: C backend: lower packed struct value", .{}),
1407 };
1408 const backing_val: Value = try .readUintFromMemory(backing_ty, pt, buf, arena.allocator());
1409 return dg.renderValue(w, backing_val, location);
1410 },
1373 try w.writeByte('{');
1374 var field_it = loaded_struct.iterateRuntimeOrder(ip);
1375 var need_comma = false;
1376 while (field_it.next()) |field_index| {
1377 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
1378 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
1379
1380 if (need_comma) try w.writeByte(',');
1381 need_comma = true;
1382 const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
1383 .bytes => |bytes| try pt.intern(.{ .int = .{
1384 .ty = field_ty.toIntern(),
1385 .storage = .{ .u64 = bytes.at(field_index, ip) },
1386 } }),
1387 .elems => |elems| elems[field_index],
1388 .repeated_elem => |elem| elem,
1389 };
1390 try dg.renderValue(w, Value.fromInterned(field_val), initializer_type);
14111391 }
1392 try w.writeByte('}');
14121393 },
14131394 else => unreachable,
14141395 },
1396 .bitpack => |bitpack| return dg.renderValue(w, .fromInterned(bitpack.backing_int_val), location),
14151397 .un => |un| {
14161398 const loaded_union = ip.loadUnionType(ty.toIntern());
1417 if (loaded_union.flagsUnordered(ip).layout == .@"packed") {
1418 // https://github.com/ziglang/zig/issues/24657 will eliminate most of the
1419 // following logic, leaving only the recursive `renderValue` call. Once
1420 // that proposal is implemented, a `packed union` will literally be
1421 // represented in the InternPool by its comptime-known backing integer.
1422 var arena: std.heap.ArenaAllocator = .init(zcu.gpa);
1423 defer arena.deinit();
1424 const backing_ty = try ty.unionBackingType(pt);
1425 const buf = try arena.allocator().alloc(u8, @intCast(ty.abiSize(zcu)));
1426 val.writeToMemory(pt, buf) catch |err| switch (err) {
1427 error.IllDefinedMemoryLayout => unreachable,
1428 error.OutOfMemory => |e| return e,
1429 error.ReinterpretDeclRef, error.Unimplemented => return dg.fail("TODO: C backend: lower packed union value", .{}),
1430 };
1431 const backing_val: Value = try .readUintFromMemory(backing_ty, pt, buf, arena.allocator());
1432 return dg.renderValue(w, backing_val, location);
1433 }
14341399 if (un.tag == .none) {
1435 const backing_ty = try ty.unionBackingType(pt);
1400 const backing_ty = try ty.externUnionBackingType(pt);
14361401 assert(loaded_union.flagsUnordered(ip).layout == .@"extern");
14371402 if (location == .StaticInitializer) {
14381403 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});
......@@ -1642,11 +1607,7 @@ pub const DeclGen = struct {
16421607 }
16431608 return w.writeByte('}');
16441609 },
1645 .@"packed" => return dg.renderUndefValue(
1646 w,
1647 .fromInterned(loaded_struct.backingIntTypeUnordered(ip)),
1648 location,
1649 ),
1610 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
16501611 }
16511612 },
16521613 .tuple_type => |tuple_info| {
......@@ -1714,11 +1675,7 @@ pub const DeclGen = struct {
17141675 }
17151676 if (has_tag) try w.writeByte('}');
17161677 },
1717 .@"packed" => return dg.renderUndefValue(
1718 w,
1719 try ty.unionBackingType(pt),
1720 location,
1721 ),
1678 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
17221679 }
17231680 },
17241681 .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) {
......@@ -5623,48 +5580,45 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
56235580 }
56245581 try w.writeByte(':');
56255582 const ip = &zcu.intern_pool;
5626 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
5627 const struct_type: Type = .fromInterned(aggregate.ty);
5628 switch (aggregate.storage) {
5629 .elems => |elems| for (elems, 0..) |elem, i| switch (elem) {
5630 .bool_true => {
5631 const field_name = struct_type.structFieldName(i, zcu).toSlice(ip).?;
5632 assert(field_name.len != 0);
5633
5634 const target = &f.object.dg.mod.resolved_target.result;
5635 var c_name_buf: [16]u8 = undefined;
5636 const name =
5637 if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {
5638 // Convert "rN" to "$N"
5639 const c_name = (&c_name_buf)[0..field_name.len];
5640 @memcpy(c_name, field_name);
5641 c_name_buf[0] = '$';
5642 break :name c_name;
5643 } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or
5644 ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or
5645 (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: {
5646 // "$" prefix for these registers
5647 c_name_buf[0] = '$';
5648 @memcpy((&c_name_buf)[1..][0..field_name.len], field_name);
5649 break :name (&c_name_buf)[0 .. 1 + field_name.len];
5650 } else if (target.cpu.arch.isSPARC() and
5651 (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: {
5652 // C compilers just use `icc` to encompass all of these.
5653 break :name "icc";
5654 } else field_name;
5655
5656 try w.print(" {f}", .{fmtStringLiteral(name, null)});
5657 (try w.writableArray(1))[0] = ',';
5658 },
5659 .bool_false => continue,
5660 else => unreachable,
5661 },
5662 .repeated_elem => |elem| switch (elem) {
5663 .bool_true => @panic("TODO"),
5664 .bool_false => {},
5665 else => unreachable,
5666 },
5667 .bytes => @panic("TODO"),
5583 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
5584 const clobbers_ty = clobbers_val.typeOf(zcu);
5585 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
5586 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
5587 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
5588 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
5589 const limb_bits = @bitSizeOf(std.math.big.Limb);
5590 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
5591 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
5592 0 => continue, // field is false
5593 1 => {}, // field is true
5594 }
5595 const field_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
5596 assert(field_name.len != 0);
5597
5598 const target = &f.object.dg.mod.resolved_target.result;
5599 var c_name_buf: [16]u8 = undefined;
5600 const name =
5601 if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {
5602 // Convert "rN" to "$N"
5603 const c_name = (&c_name_buf)[0..field_name.len];
5604 @memcpy(c_name, field_name);
5605 c_name_buf[0] = '$';
5606 break :name c_name;
5607 } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or
5608 ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or
5609 (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: {
5610 // "$" prefix for these registers
5611 c_name_buf[0] = '$';
5612 @memcpy((&c_name_buf)[1..][0..field_name.len], field_name);
5613 break :name (&c_name_buf)[0 .. 1 + field_name.len];
5614 } else if (target.cpu.arch.isSPARC() and
5615 (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: {
5616 // C compilers just use `icc` to encompass all of these.
5617 break :name "icc";
5618 } else field_name;
5619
5620 try w.print(" {f}", .{fmtStringLiteral(name, null)});
5621 (try w.writableArray(1))[0] = ',';
56685622 }
56695623 w.undo(1); // erase the last comma
56705624 try w.writeAll(");");
src/codegen/llvm.zig+15-24
......@@ -3680,7 +3680,7 @@ pub const Object = struct {
36803680 const limbs = try allocator.alloc(std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(bits));
36813681 defer allocator.free(limbs);
36823682
3683 val.writeToPackedMemory(ty, pt, buffer, 0) catch unreachable;
3683 val.writeToPackedMemory(pt, buffer, 0) catch unreachable;
36843684
36853685 var big: std.math.big.int.Mutable = .init(limbs, 0);
36863686 big.readTwosComplement(buffer, bits, target.cpu.arch.endian(), .unsigned);
......@@ -7467,29 +7467,20 @@ pub const FuncGen = struct {
74677467 }
74687468
74697469 const ip = &zcu.intern_pool;
7470 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
7471 const struct_type: Type = .fromInterned(aggregate.ty);
7472 if (total_i != 0) try llvm_constraints.append(gpa, ',');
7473 switch (aggregate.storage) {
7474 .elems => |elems| for (elems, 0..) |elem, i| {
7475 switch (elem) {
7476 .bool_true => {
7477 const name = struct_type.structFieldName(i, zcu).toSlice(ip).?;
7478 total_i += try appendConstraints(gpa, &llvm_constraints, name, target);
7479 },
7480 .bool_false => continue,
7481 else => unreachable,
7482 }
7483 },
7484 .repeated_elem => |elem| switch (elem) {
7485 .bool_true => for (0..struct_type.structFieldCount(zcu)) |i| {
7486 const name = struct_type.structFieldName(i, zcu).toSlice(ip).?;
7487 total_i += try appendConstraints(gpa, &llvm_constraints, name, target);
7488 },
7489 .bool_false => {},
7490 else => unreachable,
7491 },
7492 .bytes => @panic("TODO"),
7470 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
7471 const clobbers_ty = clobbers_val.typeOf(zcu);
7472 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
7473 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
7474 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
7475 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
7476 const limb_bits = @bitSizeOf(std.math.big.Limb);
7477 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
7478 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
7479 0 => continue, // field is false
7480 1 => {}, // field is true
7481 }
7482 const name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
7483 total_i += try appendConstraints(gpa, &llvm_constraints, name, target);
74937484 }
74947485
74957486 // We have finished scanning through all inputs/outputs, so the number of
src/codegen/riscv64/CodeGen.zig+20-25
......@@ -6149,31 +6149,26 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
61496149
61506150 const zcu = func.pt.zcu;
61516151 const ip = &zcu.intern_pool;
6152 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
6153 const struct_type: Type = .fromInterned(aggregate.ty);
6154 switch (aggregate.storage) {
6155 .elems => |elems| for (elems, 0..) |elem, i| {
6156 switch (elem) {
6157 .bool_true => {
6158 const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;
6159 assert(clobber.len != 0);
6160 if (std.mem.eql(u8, clobber, "memory")) {
6161 // nothing really to do
6162 } else {
6163 try func.register_manager.getReg(parseRegName(clobber) orelse
6164 return func.fail("invalid clobber: '{s}'", .{clobber}), null);
6165 }
6166 },
6167 .bool_false => continue,
6168 else => unreachable,
6169 }
6170 },
6171 .repeated_elem => |elem| switch (elem) {
6172 .bool_true => @panic("TODO"),
6173 .bool_false => {},
6174 else => unreachable,
6175 },
6176 .bytes => @panic("TODO"),
6152 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
6153 const clobbers_ty = clobbers_val.typeOf(zcu);
6154 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
6155 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
6156 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
6157 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
6158 const limb_bits = @bitSizeOf(std.math.big.Limb);
6159 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
6160 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
6161 0 => continue, // field is false
6162 1 => {}, // field is true
6163 }
6164 const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
6165 assert(clobber.len != 0);
6166 if (std.mem.eql(u8, clobber, "memory")) {
6167 // nothing really to do
6168 } else {
6169 try func.register_manager.getReg(parseRegName(clobber) orelse
6170 return func.fail("invalid clobber: '{s}'", .{clobber}), null);
6171 }
61776172 }
61786173
61796174 const Label = struct {
src/codegen/spirv/CodeGen.zig+1-1
......@@ -969,7 +969,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
969969 const bytes = std.mem.alignForward(u16, cg.module.backingIntBits(bits).@"0", 8) / 8;
970970 var limbs: [8]u8 = undefined;
971971 @memset(&limbs, 0);
972 val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;
972 val.writeToPackedMemory(pt, limbs[0..bytes], 0) catch unreachable;
973973 const backing_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip));
974974 return try cg.constInt(backing_ty, @as(u64, @bitCast(limbs)));
975975 }
src/codegen/wasm/CodeGen.zig+2-2
......@@ -3253,7 +3253,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
32533253 // are by-ref types.
32543254 assert(struct_type.layout == .@"packed");
32553255 var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer
3256 val.writeToPackedMemory(ty, pt, &buf, 0) catch unreachable;
3256 val.writeToPackedMemory(pt, &buf, 0) catch unreachable;
32573257 const backing_int_ty = Type.fromInterned(struct_type.backingIntTypeUnordered(ip));
32583258 const int_val = try pt.intValue(
32593259 backing_int_ty,
......@@ -3267,7 +3267,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
32673267 const int_type = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu)));
32683268
32693269 var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer
3270 val.writeToPackedMemory(ty, pt, &buf, 0) catch unreachable;
3270 val.writeToPackedMemory(pt, &buf, 0) catch unreachable;
32713271 const int_val = try pt.intValue(
32723272 int_type,
32733273 mem.readInt(u64, &buf, .little),
src/codegen/x86_64/CodeGen.zig+32-35
......@@ -177294,41 +177294,38 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
177294177294 }
177295177295
177296177296 const ip = &zcu.intern_pool;
177297 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
177298 const struct_type: Type = .fromInterned(aggregate.ty);
177299 switch (aggregate.storage) {
177300 .elems => |elems| for (elems, 0..) |elem, i| switch (elem) {
177301 .bool_true => {
177302 const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;
177303 assert(clobber.len != 0);
177304
177305 if (std.mem.eql(u8, clobber, "memory") or
177306 std.mem.eql(u8, clobber, "fpsr") or
177307 std.mem.eql(u8, clobber, "fpcr") or
177308 std.mem.eql(u8, clobber, "mxcsr") or
177309 std.mem.eql(u8, clobber, "dirflag"))
177310 {
177311 // ok, sure
177312 } else if (std.mem.eql(u8, clobber, "cc") or
177313 std.mem.eql(u8, clobber, "flags") or
177314 std.mem.eql(u8, clobber, "eflags") or
177315 std.mem.eql(u8, clobber, "rflags"))
177316 {
177317 try self.spillEflagsIfOccupied();
177318 } else {
177319 try self.register_manager.getReg(parseRegName(clobber) orelse
177320 return self.fail("invalid clobber: '{s}'", .{clobber}), null);
177321 }
177322 },
177323 .bool_false => continue,
177324 else => unreachable,
177325 },
177326 .repeated_elem => |elem| switch (elem) {
177327 .bool_true => @panic("TODO"),
177328 .bool_false => {},
177329 else => unreachable,
177330 },
177331 .bytes => @panic("TODO"),
177297 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
177298 const clobbers_ty = clobbers_val.typeOf(zcu);
177299 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
177300 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
177301 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
177302 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
177303 const limb_bits = @bitSizeOf(std.math.big.Limb);
177304 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
177305 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
177306 0 => continue, // field is false
177307 1 => {}, // field is true
177308 }
177309 const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
177310 assert(clobber.len != 0);
177311
177312 if (std.mem.eql(u8, clobber, "memory") or
177313 std.mem.eql(u8, clobber, "fpsr") or
177314 std.mem.eql(u8, clobber, "fpcr") or
177315 std.mem.eql(u8, clobber, "mxcsr") or
177316 std.mem.eql(u8, clobber, "dirflag"))
177317 {
177318 // ok, sure
177319 } else if (std.mem.eql(u8, clobber, "cc") or
177320 std.mem.eql(u8, clobber, "flags") or
177321 std.mem.eql(u8, clobber, "eflags") or
177322 std.mem.eql(u8, clobber, "rflags"))
177323 {
177324 try self.spillEflagsIfOccupied();
177325 } else {
177326 try self.register_manager.getReg(parseRegName(clobber) orelse
177327 return self.fail("invalid clobber: '{s}'", .{clobber}), null);
177328 }
177332177329 }
177333177330
177334177331 const Label = struct {
src/link/Dwarf.zig+11
......@@ -3378,6 +3378,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
33783378 .opt,
33793379 .aggregate,
33803380 .un,
3381 .bitpack,
33813382 => .decl_const,
33823383 .variable => .decl_var,
33833384 .@"extern" => unreachable,
......@@ -4014,6 +4015,7 @@ fn updateLazyType(
40144015 .opt,
40154016 .aggregate,
40164017 .un,
4018 .bitpack,
40174019 // memoization, not types
40184020 .memoized_call,
40194021 => unreachable,
......@@ -4092,6 +4094,15 @@ fn updateLazyValue(
40924094 }, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu));
40934095 try wip_nav.refType(.fromInterned(int.ty));
40944096 },
4097 .bitpack => |bitpack| {
4098 const backing_int_val: Value = .fromInterned(bitpack.backing_int_val);
4099 try wip_nav.bigIntConstValue(.{
4100 .sdata = .sdata_comptime_value,
4101 .udata = .udata_comptime_value,
4102 .block = .block_comptime_value,
4103 }, backing_int_val.typeOf(zcu), backing_int_val.toBigInt(&big_int_space, zcu));
4104 try wip_nav.refType(.fromInterned(bitpack.ty));
4105 },
40954106 .err => |err| {
40964107 try wip_nav.abbrevCode(.udata_comptime_value);
40974108 try wip_nav.refType(.fromInterned(err.ty));
src/mutable_value.zig+15-11
......@@ -97,8 +97,8 @@ pub const MutableValue = union(enum) {
9797 /// * Non-error error unions use `eu_payload`
9898 /// * Non-null optionals use `eu_payload
9999 /// * Slices use `slice`
100 /// * Unions use `un`
101 /// * Aggregates use `repeated` or `bytes` or `aggregate`
100 /// * Unions use `un` (excluding packed unions)
101 /// * Aggregates use `repeated` or `bytes` or `aggregate` (excluding packed structs)
102102 /// If `!allow_bytes`, the `bytes` representation will not be used.
103103 /// If `!allow_repeated`, the `repeated` representation will not be used.
104104 pub fn unintern(
......@@ -209,6 +209,7 @@ pub const MutableValue = union(enum) {
209209 .undef => |ty_ip| switch (Type.fromInterned(ty_ip).zigTypeTag(zcu)) {
210210 .@"struct", .array, .vector => |type_tag| {
211211 const ty = Type.fromInterned(ty_ip);
212 if (type_tag == .@"struct" and ty.containerLayout(zcu) == .@"packed") return;
212213 const opt_sent = ty.sentinel(zcu);
213214 if (type_tag == .@"struct" or opt_sent != null or !allow_repeated) {
214215 const len_no_sent = ip.aggregateTypeLen(ty_ip);
......@@ -241,15 +242,18 @@ pub const MutableValue = union(enum) {
241242 } };
242243 }
243244 },
244 .@"union" => {
245 const payload = try arena.create(MutableValue);
246 const backing_ty = try Type.fromInterned(ty_ip).unionBackingType(pt);
247 payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) };
248 mv.* = .{ .un = .{
249 .ty = ty_ip,
250 .tag = .none,
251 .payload = payload,
252 } };
245 .@"union" => switch (Type.fromInterned(ty_ip).containerLayout(zcu)) {
246 .auto, .@"packed" => {},
247 .@"extern" => {
248 const payload = try arena.create(MutableValue);
249 const backing_ty = try Type.fromInterned(ty_ip).externUnionBackingType(pt);
250 payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) };
251 mv.* = .{ .un = .{
252 .ty = ty_ip,
253 .tag = .none,
254 .payload = payload,
255 } };
256 },
253257 },
254258 .pointer => {
255259 const ptr_ty = ip.indexToKey(ty_ip).ptr_type;
src/print_value.zig+27-1
......@@ -164,7 +164,7 @@ pub fn print(
164164 return;
165165 }
166166 if (un.tag == .none) {
167 const backing_ty = try val.typeOf(zcu).unionBackingType(pt);
167 const backing_ty = try val.typeOf(zcu).externUnionBackingType(pt);
168168 try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});
169169 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
170170 try writer.writeAll("))");
......@@ -176,6 +176,32 @@ pub fn print(
176176 try writer.writeAll(" }");
177177 }
178178 },
179 .bitpack => |bitpack| {
180 const ty: Type = .fromInterned(bitpack.ty);
181 switch (ty.zigTypeTag(zcu)) {
182 .@"struct" => {
183 if (ty.structFieldCount(zcu) == 0) {
184 return writer.writeAll(".{}");
185 }
186 try writer.writeAll(".{ ");
187 const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items);
188 for (0..max_len) |i| {
189 if (i != 0) try writer.writeAll(", ");
190 const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?;
191 try writer.print(".{f} = ", .{field_name.fmt(ip)});
192 try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
193 }
194 try writer.writeAll(" }");
195 return;
196 },
197 .@"union" => {
198 try writer.print("@bitCast(@as({f}, ", .{ty.bitpackBackingInt(zcu).fmt(pt)});
199 try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
200 try writer.writeAll("))");
201 },
202 else => unreachable,
203 }
204 },
179205 .memoized_call => unreachable,
180206 }
181207}