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 {...@@ -692,33 +692,23 @@ const Writer = struct {
692692
693 const zcu = w.pt.zcu;693 const zcu = w.pt.zcu;
694 const ip = &zcu.intern_pool;694 const ip = &zcu.intern_pool;
695 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;695 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
696 const struct_type: Type = .fromInterned(aggregate.ty);696 const clobbers_ty = clobbers_val.typeOf(zcu);
697 switch (aggregate.storage) {697 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
698 .elems => |elems| for (elems, 0..) |elem, i| {698 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
699 switch (elem) {699 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
700 .bool_true => {700 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
701 const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;701 const limb_bits = @bitSizeOf(std.math.big.Limb);
702 assert(clobber.len != 0);702 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
703 try s.writeAll(", ~{");703 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
704 try s.writeAll(clobber);704 0 => continue, // field is false
705 try s.writeAll("}");705 1 => {}, // field is true
706 },706 }
707 .bool_false => continue,707 const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
708 else => unreachable,708 assert(clobber.len != 0);
709 }709 try s.writeAll(", ~{");
710 },710 try s.writeAll(clobber);
711 .repeated_elem => |elem| {711 try s.writeAll("}");
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 },
722 }712 }
723 const asm_source = unwrapped_asm.source;713 const asm_source = unwrapped_asm.source;
724 try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)});714 try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)});
src/InternPool.zig+52-11
...@@ -2130,6 +2130,8 @@ pub const Key = union(enum) {...@@ -2130,6 +2130,8 @@ pub const Key = union(enum) {
2130 aggregate: Aggregate,2130 aggregate: Aggregate,
2131 /// An instance of a union.2131 /// An instance of a union.
2132 un: Union,2132 un: Union,
2133 /// An instance of a `packed struct` or `packed union`.
2134 bitpack: Bitpack,
21332135
2134 /// A comptime function call with a memoized result.2136 /// A comptime function call with a memoized result.
2135 memoized_call: Key.MemoizedCall,2137 memoized_call: Key.MemoizedCall,
...@@ -2681,6 +2683,15 @@ pub const Key = union(enum) {...@@ -2681,6 +2683,15 @@ pub const Key = union(enum) {
2681 };2683 };
2682 };2684 };
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
2684 pub const MemoizedCall = struct {2695 pub const MemoizedCall = struct {
2685 func: Index,2696 func: Index,
2686 arg_values: []const Index,2697 arg_values: []const Index,
...@@ -2919,6 +2930,8 @@ pub const Key = union(enum) {...@@ -2919,6 +2930,8 @@ pub const Key = union(enum) {
2919 asBytes(&e.relocation) ++2930 asBytes(&e.relocation) ++
2920 asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++2931 asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++
2921 asBytes(&e.zir_index) ++ &[1]u8{@intFromEnum(e.source)}),2932 asBytes(&e.zir_index) ++ &[1]u8{@intFromEnum(e.source)}),
2933
2934 .bitpack => |bitpack| Hash.hash(seed, asBytes(&bitpack.ty) ++ asBytes(&bitpack.backing_int_val)),
2922 };2935 };
2923 }2936 }
29242937
...@@ -2996,6 +3009,10 @@ pub const Key = union(enum) {...@@ -2996,6 +3009,10 @@ pub const Key = union(enum) {
2996 const b_info = b.empty_enum_value;3009 const b_info = b.empty_enum_value;
2997 return a_info == b_info;3010 return a_info == b_info;
2998 },3011 },
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
3000 .variable => |a_info| {3017 .variable => |a_info| {
3001 const b_info = b.variable;3018 const b_info = b.variable;
...@@ -3271,6 +3288,7 @@ pub const Key = union(enum) {...@@ -3271,6 +3288,7 @@ pub const Key = union(enum) {
3271 .enum_tag,3288 .enum_tag,
3272 .aggregate,3289 .aggregate,
3273 .un,3290 .un,
3291 .bitpack,
3274 => |x| x.ty,3292 => |x| x.ty,
32753293
3276 .enum_literal => .enum_literal_type,3294 .enum_literal => .enum_literal_type,
...@@ -4417,6 +4435,7 @@ pub const Index = enum(u32) {...@@ -4417,6 +4435,7 @@ pub const Index = enum(u32) {
4417 trailing: struct { element_values: []Index },4435 trailing: struct { element_values: []Index },
4418 },4436 },
4419 repeated: struct { data: *Repeated },4437 repeated: struct { data: *Repeated },
4438 bitpack: struct { data: *Key.Bitpack },
44204439
4421 memoized_call: struct {4440 memoized_call: struct {
4422 const @"data.args_len" = opaque {};4441 const @"data.args_len" = opaque {};
...@@ -5152,6 +5171,9 @@ pub const Tag = enum(u8) {...@@ -5152,6 +5171,9 @@ pub const Tag = enum(u8) {
5152 /// An instance of an array or vector with every element being the same value.5171 /// An instance of an array or vector with every element being the same value.
5153 /// data is extra index to `Repeated`.5172 /// data is extra index to `Repeated`.
5154 repeated,5173 repeated,
5174 /// An instance of a `packed struct` or `packed union`.
5175 /// data is extra index to `Key.Bitpack`.
5176 bitpack,
51555177
5156 /// A memoized comptime function call result.5178 /// A memoized comptime function call result.
5157 /// data is extra index to `MemoizedCall`5179 /// data is extra index to `MemoizedCall`
...@@ -5485,6 +5507,7 @@ pub const Tag = enum(u8) {...@@ -5485,6 +5507,7 @@ pub const Tag = enum(u8) {
5485 .config = .{ .@"trailing.elements.len" = .@"payload.ty.payload.fields_len" },5507 .config = .{ .@"trailing.elements.len" = .@"payload.ty.payload.fields_len" },
5486 },5508 },
5487 .repeated = .{ .summary = .@"@as({.payload.ty%summary}, @splat({.payload.elem_val%summary}))", .payload = Repeated },5509 .repeated = .{ .summary = .@"@as({.payload.ty%summary}, @splat({.payload.elem_val%summary}))", .payload = Repeated },
5510 .bitpack = .{ .summary = .@"@as({.payload.ty%summary}, {})", .payload = Key.Bitpack },
54885511
5489 .memoized_call = .{5512 .memoized_call = .{
5490 .summary = .@"@memoize({.payload.func%summary})",5513 .summary = .@"@memoize({.payload.func%summary})",
...@@ -7043,6 +7066,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {...@@ -7043,6 +7066,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
7043 },7066 },
7044 .enum_literal => .{ .enum_literal = @enumFromInt(data) },7067 .enum_literal => .{ .enum_literal = @enumFromInt(data) },
7045 .enum_tag => .{ .enum_tag = extraData(unwrapped_index.getExtra(ip), Tag.EnumTag, data) },7068 .enum_tag => .{ .enum_tag = extraData(unwrapped_index.getExtra(ip), Tag.EnumTag, data) },
7069 .bitpack => .{ .bitpack = extraData(unwrapped_index.getExtra(ip), Key.Bitpack, data) },
70467070
7047 .memoized_call => {7071 .memoized_call => {
7048 const extra_list = unwrapped_index.getExtra(ip);7072 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:...@@ -7938,15 +7962,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
7938 .aggregate => |aggregate| {7962 .aggregate => |aggregate| {
7939 const ty_key = ip.indexToKey(aggregate.ty);7963 const ty_key = ip.indexToKey(aggregate.ty);
7940 const len = ip.aggregateTypeLen(aggregate.ty);7964 const len = ip.aggregateTypeLen(aggregate.ty);
7941 const child = switch (ty_key) {7965 const child: Index, const sentinel: Index = switch (ty_key) {
7942 .array_type => |array_type| array_type.child,7966 .array_type => |array_type| .{ array_type.child, array_type.sentinel },
7943 .vector_type => |vector_type| vector_type.child,7967 .vector_type => |vector_type| .{ vector_type.child, .none },
7944 .tuple_type, .struct_type => .none,7968 .tuple_type => .{ .none, .none },
7945 else => unreachable,7969 .struct_type => child: {
7946 };7970 assert(ip.loadStructType(aggregate.ty).layout != .@"packed");
7947 const sentinel = switch (ty_key) {7971 break :child .{ .none, .none };
7948 .array_type => |array_type| array_type.sentinel,7972 },
7949 .vector_type, .tuple_type, .struct_type => .none,
7950 else => unreachable,7973 else => unreachable,
7951 };7974 };
7952 const len_including_sentinel = len + @intFromBool(sentinel != .none);7975 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:...@@ -8128,6 +8151,18 @@ pub fn get(ip: *InternPool, gpa: Allocator, io: Io, tid: Zcu.PerThread.Id, key:
8128 extra.appendSliceAssumeCapacity(.{@ptrCast(aggregate.storage.elems)});8151 extra.appendSliceAssumeCapacity(.{@ptrCast(aggregate.storage.elems)});
8129 if (sentinel != .none) extra.appendAssumeCapacity(.{@intFromEnum(sentinel)});8152 if (sentinel != .none) extra.appendAssumeCapacity(.{@intFromEnum(sentinel)});
8130 },8153 },
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
8132 .memoized_call => |memoized_call| {8167 .memoized_call => |memoized_call| {
8133 for (memoized_call.arg_values) |arg| assert(arg != .none);8168 for (memoized_call.arg_values) |arg| assert(arg != .none);
...@@ -9095,6 +9130,10 @@ pub fn getUnion(...@@ -9095,6 +9130,10 @@ pub fn getUnion(
9095 tid: Zcu.PerThread.Id,9130 tid: Zcu.PerThread.Id,
9096 un: Key.Union,9131 un: Key.Union,
9097) Allocator.Error!Index {9132) Allocator.Error!Index {
9133 assert(un.ty != .none);
9134 assert(un.val != .none);
9135 assert(ip.loadUnionType(un.ty).layout != .@"packed");
9136
9098 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });9137 var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });
9099 defer gop.deinit();9138 defer gop.deinit();
9100 if (gop == .existing) return gop.existing;9139 if (gop == .existing) return gop.existing;
...@@ -9103,8 +9142,6 @@ pub fn getUnion(...@@ -9103,8 +9142,6 @@ pub fn getUnion(
9103 const extra = local.getMutableExtra(gpa, io);9142 const extra = local.getMutableExtra(gpa, io);
9104 try items.ensureUnusedCapacity(1);9143 try items.ensureUnusedCapacity(1);
91059144
9106 assert(un.ty != .none);
9107 assert(un.val != .none);
9108 items.appendAssumeCapacity(.{9145 items.appendAssumeCapacity(.{
9109 .tag = .union_value,9146 .tag = .union_value,
9110 .data = try addExtra(extra, un),9147 .data = try addExtra(extra, un),
...@@ -11003,6 +11040,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo...@@ -11003,6 +11040,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
11003 .func_coerced => @sizeOf(Tag.FuncCoerced),11040 .func_coerced => @sizeOf(Tag.FuncCoerced),
11004 .only_possible_value => 0,11041 .only_possible_value => 0,
11005 .union_value => @sizeOf(Key.Union),11042 .union_value => @sizeOf(Key.Union),
11043 .bitpack => 2 * @sizeOf(u32),
1100611044
11007 .memoized_call => b: {11045 .memoized_call => b: {
11008 const info = extraData(extra_list, MemoizedCall, data);11046 const info = extraData(extra_list, MemoizedCall, data);
...@@ -11117,6 +11155,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {...@@ -11117,6 +11155,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
11117 .func_instance,11155 .func_instance,
11118 .func_coerced,11156 .func_coerced,
11119 .union_value,11157 .union_value,
11158 .bitpack,
11120 .memoized_call,11159 .memoized_call,
11121 => try w.print("{d}", .{data}),11160 => try w.print("{d}", .{data}),
1112211161
...@@ -11871,6 +11910,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {...@@ -11871,6 +11910,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
11871 .bytes,11910 .bytes,
11872 .aggregate,11911 .aggregate,
11873 .repeated,11912 .repeated,
11913 .bitpack,
11874 => |t| {11914 => |t| {
11875 const extra_list = unwrapped_index.getExtra(ip);11915 const extra_list = unwrapped_index.getExtra(ip);
11876 return @enumFromInt(extra_list.view().items(.@"0")[item.data + std.meta.fieldIndex(t.Payload(), "ty").?]);11916 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 {...@@ -12264,6 +12304,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
12264 .bytes,12304 .bytes,
12265 .aggregate,12305 .aggregate,
12266 .repeated,12306 .repeated,
12307 .bitpack,
12267 // memoization, not types12308 // memoization, not types
12268 .memoized_call,12309 .memoized_call,
12269 => unreachable,12310 => unreachable,
src/Sema.zig+39-11
...@@ -3583,7 +3583,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,...@@ -3583,7 +3583,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
3583 },3583 },
3584 .field => |idx| ptr: {3584 .field => |idx| ptr: {
3585 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);3585 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) {
3587 // As this is a union field, we must store to the pointer now to set the tag.3587 // As this is a union field, we must store to the pointer now to set the tag.
3588 // The payload value will be stored later, so undef is a sufficent payload for now.3588 // The payload value will be stored later, so undef is a sufficent payload for now.
3589 const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);3589 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,...@@ -3591,7 +3591,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
3591 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx);3591 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx);
3592 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);3592 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
3593 try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);3593 try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
3594 }3594 };
3595 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();3595 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
3596 },3596 },
3597 .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(),3597 .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...@@ -18510,6 +18510,10 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1851018510
18511 const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);18511 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
18513 if (sema.resolveValue(payload)) |payload_val| {18517 if (sema.resolveValue(payload)) |payload_val| {
18514 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);18518 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
18515 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);18519 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
...@@ -18643,6 +18647,10 @@ fn zirStructInit(...@@ -18643,6 +18647,10 @@ fn zirStructInit(
18643 const uncoerced_init_inst = sema.resolveInst(item.data.init);18647 const uncoerced_init_inst = sema.resolveInst(item.data.init);
18644 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);18648 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
18646 if (sema.resolveValue(init_inst)) |val| {18654 if (sema.resolveValue(init_inst)) |val| {
18647 const struct_val = Value.fromInterned(try pt.internUnion(.{18655 const struct_val = Value.fromInterned(try pt.internUnion(.{
18648 .ty = resolved_ty.toIntern(),18656 .ty = resolved_ty.toIntern(),
...@@ -18789,15 +18797,35 @@ fn finishStructInit(...@@ -18789,15 +18797,35 @@ fn finishStructInit(
18789 }18797 }
18790 } else null;18798 } else null;
1879118799
18792 const runtime_index = opt_runtime_index orelse {18800 const runtime_index = opt_runtime_index orelse switch (struct_ty.containerLayout(zcu)) {
18793 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);18801 .auto, .@"extern" => {
18794 for (elems, field_inits) |*elem, field_init| {18802 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
18795 elem.* = sema.resolveValue(field_init).?.toIntern();18803 for (elems, field_inits) |*elem, field_init| {
18796 }18804 elem.* = sema.resolveValue(field_init).?.toIntern();
18797 const struct_val = try pt.aggregateValue(struct_ty, elems);18805 }
18798 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), init_src);18806 const struct_val = try pt.aggregateValue(struct_ty, elems);
18799 const final_val = sema.resolveValue(final_val_inst).?;18807 const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
18800 return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);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 },
18801 };18829 };
1880218830
18803 if (struct_ty.comptimeOnly(zcu)) {18831 if (struct_ty.comptimeOnly(zcu)) {
src/Sema/bitcast.zig+87-84
...@@ -273,6 +273,8 @@ const UnpackValueBits = struct {...@@ -273,6 +273,8 @@ const UnpackValueBits = struct {
273 .opt,273 .opt,
274 => try unpack.primitive(val),274 => try unpack.primitive(val),
275275
276 .bitpack => |bitpack| try unpack.primitive(.fromInterned(bitpack.backing_int_val)),
277
276 .aggregate => switch (ty.zigTypeTag(zcu)) {278 .aggregate => switch (ty.zigTypeTag(zcu)) {
277 .vector => {279 .vector => {
278 const len: usize = @intCast(ty.arrayLen(zcu));280 const len: usize = @intCast(ty.arrayLen(zcu));
...@@ -443,7 +445,7 @@ const UnpackValueBits = struct {...@@ -443,7 +445,7 @@ const UnpackValueBits = struct {
443 // This @intCast is okay because no primitive can exceed the size of a u16.445 // This @intCast is okay because no primitive can exceed the size of a u16.
444 const int_ty = try unpack.pt.intType(.unsigned, @intCast(bit_count));446 const int_ty = try unpack.pt.intType(.unsigned, @intCast(bit_count));
445 const buf = try unpack.arena.alloc(u8, @intCast((val_bits + 7) / 8));447 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);
447 const sub_val = try Value.readFromPackedMemory(int_ty, unpack.pt, buf, @intCast(bit_offset), unpack.arena);449 const sub_val = try Value.readFromPackedMemory(int_ty, unpack.pt, buf, @intCast(bit_offset), unpack.arena);
448 try unpack.primitive(sub_val);450 try unpack.primitive(sub_val);
449 },451 },
...@@ -565,102 +567,103 @@ const PackValueBits = struct {...@@ -565,102 +567,103 @@ const PackValueBits = struct {
565 return pt.aggregateValue(ty, elems);567 return pt.aggregateValue(ty, elems);
566 },568 },
567 .@"packed" => {569 .@"packed" => {
568 // All fields are in order with no padding.570 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
569 // This is identical between LE and BE targets.571 return pt.bitpackValue(ty, backing_int_val);
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);
576 },572 },
577 },573 },
578 .@"union" => {574 .@"union" => switch (ty.containerLayout(zcu)) {
579 // We will attempt to read as the backing representation. If this emits575 .auto => unreachable, // ill-defined layout
580 // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones.576 .@"extern" => {
581 // We will also attempt smaller fields when we get `undefined`, as if some bits are577 // We will attempt to read as the backing representation. If this emits
582 // defined we want to include them.578 // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones.
583 // TODO: this is very very bad. We need a more sophisticated union representation.579 // We will also attempt smaller fields when we get `undefined`, as if some bits are
584580 // defined we want to include them.
585 const prev_unpacked = pack.unpacked;581 // TODO: this is very very bad. We need a more sophisticated union representation.
586 const prev_bit_offset = pack.bit_offset;582
587583 const prev_unpacked = pack.unpacked;
588 const backing_ty = try ty.unionBackingType(pt);584 const prev_bit_offset = pack.bit_offset;
589585
590 backing: {586 const backing_ty = try ty.externUnionBackingType(pt);
591 const backing_val = pack.get(backing_ty) catch |err| switch (err) {587
592 error.ReinterpretDeclRef => {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)) {
593 pack.unpacked = prev_unpacked;598 pack.unpacked = prev_unpacked;
594 pack.bit_offset = prev_bit_offset;599 pack.bit_offset = prev_bit_offset;
595 break :backing;600 break :backing;
596 },601 }
597 else => |e| return e,602 return Value.fromInterned(try pt.internUnion(.{
598 };603 .ty = ty.toIntern(),
599 if (backing_val.isUndef(zcu)) {604 .tag = .none,
600 pack.unpacked = prev_unpacked;605 .val = backing_val.toIntern(),
601 pack.bit_offset = prev_bit_offset;606 }));
602 break :backing;
603 }607 }
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));609 const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu));
612 for (field_order, 0..) |*f, i| f.* = @intCast(i);610 for (field_order, 0..) |*f, i| f.* = @intCast(i);
613 // Sort `field_order` to put the fields with the largest bit sizes first.611 // Sort `field_order` to put the fields with the largest bit sizes first.
614 const SizeSortCtx = struct {612 const SizeSortCtx = struct {
615 zcu: *Zcu,613 zcu: *Zcu,
616 field_types: []const InternPool.Index,614 field_types: []const InternPool.Index,
617 fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool {615 fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool {
618 const a_ty = Type.fromInterned(ctx.field_types[a_idx]);616 const a_ty = Type.fromInterned(ctx.field_types[a_idx]);
619 const b_ty = Type.fromInterned(ctx.field_types[b_idx]);617 const b_ty = Type.fromInterned(ctx.field_types[b_idx]);
620 return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu);618 return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu);
621 }619 }
622 };620 };
623 std.mem.sortUnstable(u32, field_order, SizeSortCtx{621 std.mem.sortUnstable(u32, field_order, SizeSortCtx{
624 .zcu = zcu,622 .zcu = zcu,
625 .field_types = zcu.typeToUnion(ty).?.field_types.get(ip),623 .field_types = zcu.typeToUnion(ty).?.field_types.get(ip),
626 }, SizeSortCtx.lessThan);624 }, SizeSortCtx.lessThan);
627625
628 const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed";626 const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed";
629627
630 for (field_order) |field_idx| {628 for (field_order) |field_idx| {
631 const field_ty = Type.fromInterned(zcu.typeToUnion(ty).?.field_types.get(ip)[field_idx]);629 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);630 const pad_bits = ty.bitSize(zcu) - field_ty.bitSize(zcu);
633 if (!padding_after) try pack.padding(pad_bits);631 if (!padding_after) try pack.padding(pad_bits);
634 const field_val = pack.get(field_ty) catch |err| switch (err) {632 const field_val = pack.get(field_ty) catch |err| switch (err) {
635 error.ReinterpretDeclRef => {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)) {
636 pack.unpacked = prev_unpacked;642 pack.unpacked = prev_unpacked;
637 pack.bit_offset = prev_bit_offset;643 pack.bit_offset = prev_bit_offset;
638 continue;644 continue;
639 },645 }
640 else => |e| return e,646 const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx);
641 };647 return Value.fromInterned(try pt.internUnion(.{
642 if (padding_after) try pack.padding(pad_bits);648 .ty = ty.toIntern(),
643 if (field_val.isUndef(zcu)) {649 .tag = tag_val.toIntern(),
644 pack.unpacked = prev_unpacked;650 .val = field_val.toIntern(),
645 pack.bit_offset = prev_bit_offset;651 }));
646 continue;
647 }652 }
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);
649 return Value.fromInterned(try pt.internUnion(.{657 return Value.fromInterned(try pt.internUnion(.{
650 .ty = ty.toIntern(),658 .ty = ty.toIntern(),
651 .tag = tag_val.toIntern(),659 .tag = .none,
652 .val = field_val.toIntern(),660 .val = backing_val.toIntern(),
653 }));661 }));
654 }662 },
655663 .@"packed" => {
656 // No field could represent the value. Just do whatever happens when we try to read664 const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
657 // the backing type - either `undefined` or `error.ReinterpretDeclRef`.665 return pt.bitpackValue(ty, backing_int_val);
658 const backing_val = try pack.get(backing_ty);666 },
659 return Value.fromInterned(try pt.internUnion(.{
660 .ty = ty.toIntern(),
661 .tag = .none,
662 .val = backing_val.toIntern(),
663 }));
664 },667 },
665 else => return pack.primitive(ty),668 else => return pack.primitive(ty),
666 }669 }
...@@ -722,7 +725,7 @@ const PackValueBits = struct {...@@ -722,7 +725,7 @@ const PackValueBits = struct {
722 const val = Value.fromInterned(ip_val);725 const val = Value.fromInterned(ip_val);
723 const ty = val.typeOf(zcu);726 const ty = val.typeOf(zcu);
724 if (!val.isUndef(zcu)) {727 if (!val.isUndef(zcu)) {
725 try val.writeToPackedMemory(ty, pt, buf, cur_bit_off);728 try val.writeToPackedMemory(pt, buf, cur_bit_off);
726 }729 }
727 cur_bit_off += @intCast(ty.bitSize(zcu));730 cur_bit_off += @intCast(ty.bitSize(zcu));
728 }731 }
src/Sema/type_resolution.zig+1
...@@ -79,6 +79,7 @@ pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!vo...@@ -79,6 +79,7 @@ pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!vo
79 .opt,79 .opt,
80 .aggregate,80 .aggregate,
81 .un,81 .un,
82 .bitpack,
82 // memoization, not types83 // memoization, not types
83 .memoized_call,84 .memoized_call,
84 => unreachable,85 => unreachable,
src/Type.zig+33-8
...@@ -407,6 +407,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari...@@ -407,6 +407,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari
407 .opt,407 .opt,
408 .aggregate,408 .aggregate,
409 .un,409 .un,
410 .bitpack,
410 // memoization, not types411 // memoization, not types
411 .memoized_call,412 .memoized_call,
412 => unreachable,413 => unreachable,
...@@ -543,6 +544,7 @@ pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {...@@ -543,6 +544,7 @@ pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
543 .opt,544 .opt,
544 .aggregate,545 .aggregate,
545 .un,546 .un,
547 .bitpack,
546 // memoization, not types548 // memoization, not types
547 .memoized_call,549 .memoized_call,
548 => unreachable,550 => unreachable,
...@@ -639,6 +641,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {...@@ -639,6 +641,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
639 .opt,641 .opt,
640 .aggregate,642 .aggregate,
641 .un,643 .un,
644 .bitpack,
642 // memoization, not types645 // memoization, not types
643 .memoized_call,646 .memoized_call,
644 => unreachable,647 => unreachable,
...@@ -846,6 +849,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {...@@ -846,6 +849,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
846 .opt,849 .opt,
847 .aggregate,850 .aggregate,
848 .un,851 .un,
852 .bitpack,
849 // memoization, not types853 // memoization, not types
850 .memoized_call,854 .memoized_call,
851 => unreachable,855 => unreachable,
...@@ -978,6 +982,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {...@@ -978,6 +982,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
978 .opt,982 .opt,
979 .aggregate,983 .aggregate,
980 .un,984 .un,
985 .bitpack,
981 // memoization, not types986 // memoization, not types
982 .memoized_call,987 .memoized_call,
983 => unreachable,988 => unreachable,
...@@ -1102,6 +1107,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {...@@ -1102,6 +1107,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
1102 .opt,1107 .opt,
1103 .aggregate,1108 .aggregate,
1104 .un,1109 .un,
1110 .bitpack,
1105 // memoization, not types1111 // memoization, not types
1106 .memoized_call,1112 .memoized_call,
1107 => unreachable,1113 => unreachable,
...@@ -1393,16 +1399,16 @@ pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *Zcu) bool {...@@ -1393,16 +1399,16 @@ pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *Zcu) bool {
1393}1399}
13941400
1395/// Returns the type used for backing storage of this union during comptime operations.1401/// Returns the type used for backing storage of this union during comptime operations.
1396/// Asserts the type is either an extern or packed union.1402/// Asserts the type is an extern union.
1397pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type {1403pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
1398 const zcu = pt.zcu;1404 const zcu = pt.zcu;
1399 assertHasLayout(ty, zcu);1405 assertHasLayout(ty, zcu);
1400 const loaded_union = zcu.intern_pool.loadUnionType(ty.toIntern());1406 const loaded_union = zcu.intern_pool.loadUnionType(ty.toIntern());
1401 return switch (loaded_union.layout) {1407 switch (loaded_union.layout) {
1402 .@"extern" => try pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),1408 .@"extern" => return pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),
1403 .@"packed" => .fromInterned(loaded_union.packed_backing_int_type),1409 .@"packed" => unreachable,
1404 .auto => unreachable,1410 .auto => unreachable,
1405 };1411 }
1406}1412}
14071413
1408pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {1414pub 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...@@ -1421,6 +1427,15 @@ pub fn containerLayout(ty: Type, zcu: *const Zcu) std.builtin.Type.ContainerLayo
1421 };1427 };
1422}1428}
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
1424/// Asserts that the type is an error union.1439/// Asserts that the type is an error union.
1425pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {1440pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
1426 return Type.fromInterned(zcu.intern_pool.indexToKey(ty.toIntern()).error_union_type.payload_type);1441 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 {...@@ -1635,6 +1650,7 @@ pub fn intInfo(starting_ty: Type, zcu: *const Zcu) InternPool.Key.IntType {
1635 .opt,1650 .opt,
1636 .aggregate,1651 .aggregate,
1637 .un,1652 .un,
1653 .bitpack,
1638 // memoization, not types1654 // memoization, not types
1639 .memoized_call,1655 .memoized_call,
1640 => unreachable,1656 => unreachable,
...@@ -1842,7 +1858,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {...@@ -1842,7 +1858,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
1842 if (struct_obj.layout == .@"packed") {1858 if (struct_obj.layout == .@"packed") {
1843 const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);1859 const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
1844 const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;1860 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);
1846 } else {1862 } else {
1847 if (!struct_obj.has_one_possible_value) return null;1863 if (!struct_obj.has_one_possible_value) return null;
1848 }1864 }
...@@ -1893,8 +1909,13 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {...@@ -1893,8 +1909,13 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
1893 },1909 },
18941910
1895 .union_type => {1911 .union_type => {
1896 // MLUGG TODO: is this nonsensical or what!!!!!!
1897 const union_obj = ip.loadUnionType(ty.toIntern());1912 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!!!!!!
1898 const tag_val = (try Type.fromInterned(union_obj.enum_tag_type).onePossibleValue(pt)) orelse1919 const tag_val = (try Type.fromInterned(union_obj.enum_tag_type).onePossibleValue(pt)) orelse
1899 return null;1920 return null;
1900 if (union_obj.field_types.len == 0) {1921 if (union_obj.field_types.len == 0) {
...@@ -1957,6 +1978,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {...@@ -1957,6 +1978,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
1957 .opt,1978 .opt,
1958 .aggregate,1979 .aggregate,
1959 .un,1980 .un,
1981 .bitpack,
1960 // memoization, not types1982 // memoization, not types
1961 .memoized_call,1983 .memoized_call,
1962 => unreachable,1984 => unreachable,
...@@ -2061,6 +2083,7 @@ pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {...@@ -2061,6 +2083,7 @@ pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {
2061 .opt,2083 .opt,
2062 .aggregate,2084 .aggregate,
2063 .un,2085 .un,
2086 .bitpack,
2064 // memoization, not types2087 // memoization, not types
2065 .memoized_call,2088 .memoized_call,
2066 => unreachable,2089 => unreachable,
...@@ -3080,6 +3103,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {...@@ -3080,6 +3103,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
3080 .opt,3103 .opt,
3081 .aggregate,3104 .aggregate,
3082 .un,3105 .un,
3106 .bitpack,
3083 .undef,3107 .undef,
3084 // memoization, not types3108 // memoization, not types
3085 .memoized_call,3109 .memoized_call,
...@@ -3158,6 +3182,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn...@@ -3158,6 +3182,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn
3158 .opt,3182 .opt,
3159 .aggregate,3183 .aggregate,
3160 .un,3184 .un,
3185 .bitpack,
3161 // memoization, not types3186 // memoization, not types
3162 .memoized_call,3187 .memoized_call,
3163 => unreachable,3188 => unreachable,
src/Value.zig+75-85
...@@ -158,6 +158,7 @@ pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *Zcu) BigIntConst {...@@ -158,6 +158,7 @@ pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *Zcu) BigIntConst {
158 const ip = &zcu.intern_pool;158 const ip = &zcu.intern_pool;
159 const int_key = switch (ip.indexToKey(val.toIntern())) {159 const int_key = switch (ip.indexToKey(val.toIntern())) {
160 .enum_tag => |enum_tag| ip.indexToKey(enum_tag.int).int,160 .enum_tag => |enum_tag| ip.indexToKey(enum_tag.int).int,
161 .bitpack => |bitpack| ip.indexToKey(bitpack.backing_int_val).int,
161 .int => |int| int,162 .int => |int| int,
162 else => unreachable,163 else => unreachable,
163 };164 };
...@@ -216,6 +217,7 @@ pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {...@@ -216,6 +217,7 @@ pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
216 else => |payload| Value.fromInterned(payload).getUnsignedInt(zcu),217 else => |payload| Value.fromInterned(payload).getUnsignedInt(zcu),
217 },218 },
218 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).getUnsignedInt(zcu),219 .enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).getUnsignedInt(zcu),
220 .bitpack => |bitpack| Value.fromInterned(bitpack.backing_int_val).getUnsignedInt(zcu),
219 .err => |err| zcu.intern_pool.getErrorValueIfExists(err.name).?,221 .err => |err| zcu.intern_pool.getErrorValueIfExists(err.name).?,
220 else => null,222 else => null,
221 },223 },
...@@ -309,7 +311,7 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{...@@ -309,7 +311,7 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
309 // We use byte_count instead of abi_size here, so that any padding bytes311 // We use byte_count instead of abi_size here, so that any padding bytes
310 // follow the data bytes, on both big- and little-endian systems.312 // follow the data bytes, on both big- and little-endian systems.
311 const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;313 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);
313 },315 },
314 .@"struct" => {316 .@"struct" => {
315 const struct_type = zcu.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout;317 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{...@@ -328,8 +330,8 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
328 try writeToMemory(field_val, pt, buffer[off..]);330 try writeToMemory(field_val, pt, buffer[off..]);
329 },331 },
330 .@"packed" => {332 .@"packed" => {
331 const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;333 const int_index = ip.indexToKey(val.toIntern()).bitpack.backing_int_val;
332 return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);334 return Value.fromInterned(int_index).writeToMemory(pt, buffer);
333 },335 },
334 }336 }
335 },337 },
...@@ -344,15 +346,14 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{...@@ -344,15 +346,14 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
344 const byte_count: usize = @intCast(field_type.abiSize(zcu));346 const byte_count: usize = @intCast(field_type.abiSize(zcu));
345 return writeToMemory(field_val, pt, buffer[0..byte_count]);347 return writeToMemory(field_val, pt, buffer[0..byte_count]);
346 } else {348 } else {
347 const backing_ty = try ty.unionBackingType(pt);349 const backing_ty = try ty.externUnionBackingType(pt);
348 const byte_count: usize = @intCast(backing_ty.abiSize(zcu));350 const byte_count: usize = @intCast(backing_ty.abiSize(zcu));
349 return writeToMemory(val.unionValue(zcu), pt, buffer[0..byte_count]);351 return writeToMemory(val.unionValue(zcu), pt, buffer[0..byte_count]);
350 }352 }
351 },353 },
352 .@"packed" => {354 .@"packed" => {
353 const backing_ty = try ty.unionBackingType(pt);355 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
354 const byte_count: usize = @intCast(backing_ty.abiSize(zcu));356 return writeToMemory(int_val, pt, buffer);
355 return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
356 },357 },
357 },358 },
358 .optional => {359 .optional => {
...@@ -374,7 +375,6 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{...@@ -374,7 +375,6 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
374/// big-endian packed memory layouts start at the end of the buffer.375/// big-endian packed memory layouts start at the end of the buffer.
375pub fn writeToPackedMemory(376pub fn writeToPackedMemory(
376 val: Value,377 val: Value,
377 ty: Type,
378 pt: Zcu.PerThread,378 pt: Zcu.PerThread,
379 buffer: []u8,379 buffer: []u8,
380 bit_offset: usize,380 bit_offset: usize,
...@@ -383,6 +383,7 @@ pub fn writeToPackedMemory(...@@ -383,6 +383,7 @@ pub fn writeToPackedMemory(
383 const ip = &zcu.intern_pool;383 const ip = &zcu.intern_pool;
384 const target = zcu.getTarget();384 const target = zcu.getTarget();
385 const endian = target.cpu.arch.endian();385 const endian = target.cpu.arch.endian();
386 const ty = val.typeOf(zcu);
386 if (val.isUndef(zcu)) {387 if (val.isUndef(zcu)) {
387 const bit_size: usize = @intCast(ty.bitSize(zcu));388 const bit_size: usize = @intCast(ty.bitSize(zcu));
388 if (bit_size != 0) {389 if (bit_size != 0) {
...@@ -405,7 +406,13 @@ pub fn writeToPackedMemory(...@@ -405,7 +406,13 @@ pub fn writeToPackedMemory(
405 },406 },
406 .@"enum" => {407 .@"enum" => {
407 const int_val = val.intFromEnum(zcu);408 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);
409 },416 },
410 .int => {417 .int => {
411 const bits = ty.intInfo(zcu).bits;418 const bits = ty.intInfo(zcu).bits;
...@@ -434,54 +441,21 @@ pub fn writeToPackedMemory(...@@ -434,54 +441,21 @@ pub fn writeToPackedMemory(
434 // On big-endian systems, LLVM reverses the element order of vectors by default441 // On big-endian systems, LLVM reverses the element order of vectors by default
435 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;442 const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
436 const elem_val = try val.elemValue(pt, tgt_elem_i);443 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);
438 bits += elem_bit_size;445 bits += elem_bit_size;
439 }446 }
440 },447 },
441 .@"struct" => {448 .@"struct", .@"union" => {
442 const struct_type = ip.loadStructType(ty.toIntern());449 assert(ty.containerLayout(zcu) == .@"packed");
443 // Sema is supposed to have emitted a compile error already in the case of Auto,450 const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
444 // and Extern is handled in non-packed writeToMemory.451 return int_val.writeToPackedMemory(pt, buffer, bit_offset);
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);
476 },452 },
477 .optional => {453 .optional => {
478 assert(ty.isPtrLikeOptional(zcu));454 assert(ty.isPtrLikeOptional(zcu));
479 const child = ty.optionalChild(zcu);455 if (val.optionalValue(zcu)) |ptr_val| {
480 const opt_val = val.optionalValue(zcu);456 return ptr_val.writeToPackedMemory(pt, buffer, bit_offset);
481 if (opt_val) |some| {
482 return some.writeToPackedMemory(child, pt, buffer, bit_offset);
483 } else {457 } 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);
485 }459 }
486 },460 },
487 else => @panic("TODO implement writeToPackedMemory for more types"),461 else => @panic("TODO implement writeToPackedMemory for more types"),
...@@ -531,13 +505,12 @@ pub fn readFromPackedMemory(...@@ -531,13 +505,12 @@ pub fn readFromPackedMemory(
531 pt: Zcu.PerThread,505 pt: Zcu.PerThread,
532 buffer: []const u8,506 buffer: []const u8,
533 bit_offset: usize,507 bit_offset: usize,
534 arena: Allocator,508 gpa: Allocator,
535) error{509) error{
536 IllDefinedMemoryLayout,510 IllDefinedMemoryLayout,
537 OutOfMemory,511 OutOfMemory,
538}!Value {512}!Value {
539 const zcu = pt.zcu;513 const zcu = pt.zcu;
540 const ip = &zcu.intern_pool;
541 const target = zcu.getTarget();514 const target = zcu.getTarget();
542 const endian = target.cpu.arch.endian();515 const endian = target.cpu.arch.endian();
543 switch (ty.zigTypeTag(zcu)) {516 switch (ty.zigTypeTag(zcu)) {
...@@ -571,7 +544,8 @@ pub fn readFromPackedMemory(...@@ -571,7 +544,8 @@ pub fn readFromPackedMemory(
571 const abi_size: usize = @intCast(ty.abiSize(zcu));544 const abi_size: usize = @intCast(ty.abiSize(zcu));
572 const Limb = std.math.big.Limb;545 const Limb = std.math.big.Limb;
573 const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);546 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
576 var bigint = BigIntMutable.init(limbs_buffer, 0);550 var bigint = BigIntMutable.init(limbs_buffer, 0);
577 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);551 bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
...@@ -579,7 +553,7 @@ pub fn readFromPackedMemory(...@@ -579,7 +553,7 @@ pub fn readFromPackedMemory(
579 },553 },
580 .@"enum" => {554 .@"enum" => {
581 const int_ty = ty.intTagType(zcu);555 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);
583 return pt.getCoerced(int_val, ty);557 return pt.getCoerced(int_val, ty);
584 },558 },
585 .float => return Value.fromInterned(try pt.intern(.{ .float = .{559 .float => return Value.fromInterned(try pt.intern(.{ .float = .{
...@@ -595,52 +569,32 @@ pub fn readFromPackedMemory(...@@ -595,52 +569,32 @@ pub fn readFromPackedMemory(
595 } })),569 } })),
596 .vector => {570 .vector => {
597 const elem_ty = ty.childType(zcu);571 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
600 var bits: u16 = 0;575 var bits: u16 = 0;
601 const elem_bit_size: u16 = @intCast(elem_ty.bitSize(zcu));576 const elem_bit_size: u16 = @intCast(elem_ty.bitSize(zcu));
602 for (elems, 0..) |_, i| {577 for (elems, 0..) |_, i| {
603 // On big-endian systems, LLVM reverses the element order of vectors by default578 // On big-endian systems, LLVM reverses the element order of vectors by default
604 const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;579 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();
606 bits += elem_bit_size;581 bits += elem_bit_size;
607 }582 }
608 return pt.aggregateValue(ty, elems);583 return pt.aggregateValue(ty, elems);
609 },584 },
610 .@"struct" => {585 .@"struct", .@"union" => {
611 // Sema is supposed to have emitted a compile error already for Auto layout structs,586 assert(ty.containerLayout(zcu) == .@"packed");
612 // and Extern is handled by non-packed readFromMemory.587 const int_val: Value = try .readFromPackedMemory(ty.bitpackBackingInt(zcu), pt, buffer, bit_offset, gpa);
613 const struct_type = zcu.typeToPackedStruct(ty).?;588 return pt.bitpackValue(ty, int_val);
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 },
635 },589 },
636 .pointer => {590 .pointer => {
637 assert(!ty.isSlice(zcu)); // No well defined layout.591 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);
639 return pt.ptrIntValue(ty, addr);593 return pt.ptrIntValue(ty, addr);
640 },594 },
641 .optional => {595 .optional => {
642 assert(ty.isPtrLikeOptional(zcu));596 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);
644 return .fromInterned(try pt.intern(.{ .opt = .{598 return .fromInterned(try pt.intern(.{ .opt = .{
645 .ty = ty.toIntern(),599 .ty = ty.toIntern(),
646 .val = if (addr == 0) .none else (try pt.ptrIntValue(ty.childType(zcu), addr)).toIntern(),600 .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 {...@@ -915,8 +869,44 @@ pub fn fieldValue(val: Value, pt: Zcu.PerThread, index: usize) !Value {
915 .elems => |elems| elems[index],869 .elems => |elems| elems[index],
916 .repeated_elem => |elem| elem,870 .repeated_elem => |elem| elem,
917 }),871 }),
918 // TODO assert the tag is correct872 .un => |un| {
919 .un => |un| Value.fromInterned(un.val),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 },
920 else => unreachable,910 else => unreachable,
921 };911 };
922}912}
src/Zcu/PerThread.zig+9
...@@ -3950,6 +3950,15 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value...@@ -3950,6 +3950,15 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value
3950 } }));3950 } }));
3951}3951}
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
3953pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value {3962pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value {
3954 assert(pt.zcu.intern_pool.isOptionalType(opt_ty.toIntern()));3963 assert(pt.zcu.intern_pool.isOptionalType(opt_ty.toIntern()));
3955 return Value.fromInterned(try pt.intern(.{ .opt = .{3964 return Value.fromInterned(try pt.intern(.{ .opt = .{
src/codegen.zig+6-36
...@@ -570,42 +570,7 @@ pub fn generateSymbol(...@@ -570,42 +570,7 @@ pub fn generateSymbol(
570 .struct_type => {570 .struct_type => {
571 const struct_type = ip.loadStructType(ty.toIntern());571 const struct_type = ip.loadStructType(ty.toIntern());
572 switch (struct_type.layout) {572 switch (struct_type.layout) {
573 .@"packed" => {573 .@"packed" => unreachable,
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 },
609 .auto, .@"extern" => {574 .auto, .@"extern" => {
610 const struct_begin = w.end;575 const struct_begin = w.end;
611 const field_types = struct_type.field_types.get(ip);576 const field_types = struct_type.field_types.get(ip);
...@@ -683,6 +648,7 @@ pub fn generateSymbol(...@@ -683,6 +648,7 @@ pub fn generateSymbol(
683 }648 }
684 }649 }
685 },650 },
651 .bitpack => |bitpack| try generateSymbol(bin_file, pt, src_loc, .fromInterned(bitpack.backing_int_val), w, reloc_parent),
686 .memoized_call => unreachable,652 .memoized_call => unreachable,
687 }653 }
688}654}
...@@ -1120,6 +1086,10 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo...@@ -1120,6 +1086,10 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo
1120 target,1086 target,
1121 );1087 );
1122 },1088 },
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 },
1123 .error_set => {1093 .error_set => {
1124 const err_name = ip.indexToKey(val.toIntern()).err.name;1094 const err_name = ip.indexToKey(val.toIntern()).err.name;
1125 const error_index = ip.getErrorValueIfExists(err_name).?;1095 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,...@@ -2791,17 +2791,17 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
2791 } else return isel.fail("invalid constraint: '{s}'", .{constraint});2791 } else return isel.fail("invalid constraint: '{s}'", .{constraint});
2792 }2792 }
27932793
2794 const clobbers = ip.indexToKey(unwrapped_asm.clobbers).aggregate;2794 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
2795 const clobbers_ty: ZigType = .fromInterned(clobbers.ty);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);
2796 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {2798 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
2797 switch (switch (clobbers.storage) {2799 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
2798 .bytes => unreachable,2800 const limb_bits = @bitSizeOf(std.math.big.Limb);
2799 .elems => |elems| elems[field_index],2801 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
2800 .repeated_elem => |repeated_elem| repeated_elem,2802 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
2801 }) {2803 0 => continue, // field is false
2802 else => unreachable,2804 1 => {}, // field is true
2803 .bool_false => continue,
2804 .bool_true => {},
2805 }2805 }
2806 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;2806 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
2807 if (std.mem.eql(u8, clobber_name, "memory")) continue;2807 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,...@@ -2816,14 +2816,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
2816 }2816 }
2817 }2817 }
2818 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {2818 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
2819 switch (switch (clobbers.storage) {2819 const limb_bits = @bitSizeOf(std.math.big.Limb);
2820 .bytes => unreachable,2820 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
2821 .elems => |elems| elems[field_index],2821 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> field_index % limb_bits))) {
2822 .repeated_elem => |repeated_elem| repeated_elem,2822 0 => continue, // field is false
2823 }) {2823 1 => {}, // field is true
2824 else => unreachable,
2825 .bool_false => continue,
2826 .bool_true => {},
2827 }2824 }
2828 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;2825 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
2829 if (std.mem.eql(u8, clobber_name, "memory")) continue;2826 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,...@@ -2872,14 +2869,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
2872 }2869 }
28732870
2874 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {2871 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
2875 switch (switch (clobbers.storage) {2872 const limb_bits = @bitSizeOf(std.math.big.Limb);
2876 .bytes => unreachable,2873 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
2877 .elems => |elems| elems[field_index],2874 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> field_index % limb_bits))) {
2878 .repeated_elem => |repeated_elem| repeated_elem,2875 0 => continue, // field is false
2879 }) {2876 1 => {}, // field is true
2880 else => unreachable,
2881 .bool_false => continue,
2882 .bool_true => {},
2883 }2877 }
2884 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;2878 const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
2885 if (std.mem.eql(u8, clobber_name, "memory")) continue;2879 if (std.mem.eql(u8, clobber_name, "memory")) continue;
src/codegen/c.zig+68-114
...@@ -1362,77 +1362,42 @@ pub const DeclGen = struct {...@@ -1362,77 +1362,42 @@ pub const DeclGen = struct {
1362 },1362 },
1363 .struct_type => {1363 .struct_type => {
1364 const loaded_struct = ip.loadStructType(ty.toIntern());1364 const loaded_struct = ip.loadStructType(ty.toIntern());
1365 switch (loaded_struct.layout) {1365 assert(loaded_struct.layout != .@"packed");
1366 .auto, .@"extern" => {
1367 if (!location.isInitializer()) {
1368 try w.writeByte('(');
1369 try dg.renderCType(w, ctype);
1370 try w.writeByte(')');
1371 }
13721366
1373 try w.writeByte('{');1367 if (!location.isInitializer()) {
1374 var field_it = loaded_struct.iterateRuntimeOrder(ip);1368 try w.writeByte('(');
1375 var need_comma = false;1369 try dg.renderCType(w, ctype);
1376 while (field_it.next()) |field_index| {1370 try w.writeByte(')');
1377 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);1371 }
1378 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
13791372
1380 if (need_comma) try w.writeByte(',');1373 try w.writeByte('{');
1381 need_comma = true;1374 var field_it = loaded_struct.iterateRuntimeOrder(ip);
1382 const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {1375 var need_comma = false;
1383 .bytes => |bytes| try pt.intern(.{ .int = .{1376 while (field_it.next()) |field_index| {
1384 .ty = field_ty.toIntern(),1377 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
1385 .storage = .{ .u64 = bytes.at(field_index, ip) },1378 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
1386 } }),1379
1387 .elems => |elems| elems[field_index],1380 if (need_comma) try w.writeByte(',');
1388 .repeated_elem => |elem| elem,1381 need_comma = true;
1389 };1382 const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
1390 try dg.renderValue(w, Value.fromInterned(field_val), initializer_type);1383 .bytes => |bytes| try pt.intern(.{ .int = .{
1391 }1384 .ty = field_ty.toIntern(),
1392 try w.writeByte('}');1385 .storage = .{ .u64 = bytes.at(field_index, ip) },
1393 },1386 } }),
1394 .@"packed" => {1387 .elems => |elems| elems[field_index],
1395 // https://github.com/ziglang/zig/issues/24657 will eliminate most of the1388 .repeated_elem => |elem| elem,
1396 // following logic, leaving only the recursive `renderValue` call. Once1389 };
1397 // that proposal is implemented, a `packed struct` will literally be1390 try dg.renderValue(w, Value.fromInterned(field_val), initializer_type);
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 },
1411 }1391 }
1392 try w.writeByte('}');
1412 },1393 },
1413 else => unreachable,1394 else => unreachable,
1414 },1395 },
1396 .bitpack => |bitpack| return dg.renderValue(w, .fromInterned(bitpack.backing_int_val), location),
1415 .un => |un| {1397 .un => |un| {
1416 const loaded_union = ip.loadUnionType(ty.toIntern());1398 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 }
1434 if (un.tag == .none) {1399 if (un.tag == .none) {
1435 const backing_ty = try ty.unionBackingType(pt);1400 const backing_ty = try ty.externUnionBackingType(pt);
1436 assert(loaded_union.flagsUnordered(ip).layout == .@"extern");1401 assert(loaded_union.flagsUnordered(ip).layout == .@"extern");
1437 if (location == .StaticInitializer) {1402 if (location == .StaticInitializer) {
1438 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});1403 return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});
...@@ -1642,11 +1607,7 @@ pub const DeclGen = struct {...@@ -1642,11 +1607,7 @@ pub const DeclGen = struct {
1642 }1607 }
1643 return w.writeByte('}');1608 return w.writeByte('}');
1644 },1609 },
1645 .@"packed" => return dg.renderUndefValue(1610 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
1646 w,
1647 .fromInterned(loaded_struct.backingIntTypeUnordered(ip)),
1648 location,
1649 ),
1650 }1611 }
1651 },1612 },
1652 .tuple_type => |tuple_info| {1613 .tuple_type => |tuple_info| {
...@@ -1714,11 +1675,7 @@ pub const DeclGen = struct {...@@ -1714,11 +1675,7 @@ pub const DeclGen = struct {
1714 }1675 }
1715 if (has_tag) try w.writeByte('}');1676 if (has_tag) try w.writeByte('}');
1716 },1677 },
1717 .@"packed" => return dg.renderUndefValue(1678 .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
1718 w,
1719 try ty.unionBackingType(pt),
1720 location,
1721 ),
1722 }1679 }
1723 },1680 },
1724 .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) {1681 .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) {
...@@ -5623,48 +5580,45 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5623,48 +5580,45 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
5623 }5580 }
5624 try w.writeByte(':');5581 try w.writeByte(':');
5625 const ip = &zcu.intern_pool;5582 const ip = &zcu.intern_pool;
5626 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;5583 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
5627 const struct_type: Type = .fromInterned(aggregate.ty);5584 const clobbers_ty = clobbers_val.typeOf(zcu);
5628 switch (aggregate.storage) {5585 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
5629 .elems => |elems| for (elems, 0..) |elem, i| switch (elem) {5586 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
5630 .bool_true => {5587 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
5631 const field_name = struct_type.structFieldName(i, zcu).toSlice(ip).?;5588 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
5632 assert(field_name.len != 0);5589 const limb_bits = @bitSizeOf(std.math.big.Limb);
56335590 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
5634 const target = &f.object.dg.mod.resolved_target.result;5591 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
5635 var c_name_buf: [16]u8 = undefined;5592 0 => continue, // field is false
5636 const name =5593 1 => {}, // field is true
5637 if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {5594 }
5638 // Convert "rN" to "$N"5595 const field_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
5639 const c_name = (&c_name_buf)[0..field_name.len];5596 assert(field_name.len != 0);
5640 @memcpy(c_name, field_name);5597
5641 c_name_buf[0] = '$';5598 const target = &f.object.dg.mod.resolved_target.result;
5642 break :name c_name;5599 var c_name_buf: [16]u8 = undefined;
5643 } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or5600 const name =
5644 ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or5601 if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {
5645 (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: {5602 // Convert "rN" to "$N"
5646 // "$" prefix for these registers5603 const c_name = (&c_name_buf)[0..field_name.len];
5647 c_name_buf[0] = '$';5604 @memcpy(c_name, field_name);
5648 @memcpy((&c_name_buf)[1..][0..field_name.len], field_name);5605 c_name_buf[0] = '$';
5649 break :name (&c_name_buf)[0 .. 1 + field_name.len];5606 break :name c_name;
5650 } else if (target.cpu.arch.isSPARC() and5607 } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or
5651 (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: {5608 ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or
5652 // C compilers just use `icc` to encompass all of these.5609 (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: {
5653 break :name "icc";5610 // "$" prefix for these registers
5654 } else field_name;5611 c_name_buf[0] = '$';
56555612 @memcpy((&c_name_buf)[1..][0..field_name.len], field_name);
5656 try w.print(" {f}", .{fmtStringLiteral(name, null)});5613 break :name (&c_name_buf)[0 .. 1 + field_name.len];
5657 (try w.writableArray(1))[0] = ',';5614 } else if (target.cpu.arch.isSPARC() and
5658 },5615 (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: {
5659 .bool_false => continue,5616 // C compilers just use `icc` to encompass all of these.
5660 else => unreachable,5617 break :name "icc";
5661 },5618 } else field_name;
5662 .repeated_elem => |elem| switch (elem) {5619
5663 .bool_true => @panic("TODO"),5620 try w.print(" {f}", .{fmtStringLiteral(name, null)});
5664 .bool_false => {},5621 (try w.writableArray(1))[0] = ',';
5665 else => unreachable,
5666 },
5667 .bytes => @panic("TODO"),
5668 }5622 }
5669 w.undo(1); // erase the last comma5623 w.undo(1); // erase the last comma
5670 try w.writeAll(");");5624 try w.writeAll(");");
src/codegen/llvm.zig+15-24
...@@ -3680,7 +3680,7 @@ pub const Object = struct {...@@ -3680,7 +3680,7 @@ pub const Object = struct {
3680 const limbs = try allocator.alloc(std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(bits));3680 const limbs = try allocator.alloc(std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(bits));
3681 defer allocator.free(limbs);3681 defer allocator.free(limbs);
36823682
3683 val.writeToPackedMemory(ty, pt, buffer, 0) catch unreachable;3683 val.writeToPackedMemory(pt, buffer, 0) catch unreachable;
36843684
3685 var big: std.math.big.int.Mutable = .init(limbs, 0);3685 var big: std.math.big.int.Mutable = .init(limbs, 0);
3686 big.readTwosComplement(buffer, bits, target.cpu.arch.endian(), .unsigned);3686 big.readTwosComplement(buffer, bits, target.cpu.arch.endian(), .unsigned);
...@@ -7467,29 +7467,20 @@ pub const FuncGen = struct {...@@ -7467,29 +7467,20 @@ pub const FuncGen = struct {
7467 }7467 }
74687468
7469 const ip = &zcu.intern_pool;7469 const ip = &zcu.intern_pool;
7470 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;7470 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
7471 const struct_type: Type = .fromInterned(aggregate.ty);7471 const clobbers_ty = clobbers_val.typeOf(zcu);
7472 if (total_i != 0) try llvm_constraints.append(gpa, ',');7472 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
7473 switch (aggregate.storage) {7473 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
7474 .elems => |elems| for (elems, 0..) |elem, i| {7474 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
7475 switch (elem) {7475 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
7476 .bool_true => {7476 const limb_bits = @bitSizeOf(std.math.big.Limb);
7477 const name = struct_type.structFieldName(i, zcu).toSlice(ip).?;7477 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
7478 total_i += try appendConstraints(gpa, &llvm_constraints, name, target);7478 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
7479 },7479 0 => continue, // field is false
7480 .bool_false => continue,7480 1 => {}, // field is true
7481 else => unreachable,7481 }
7482 }7482 const name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
7483 },7483 total_i += try appendConstraints(gpa, &llvm_constraints, name, target);
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"),
7493 }7484 }
74947485
7495 // We have finished scanning through all inputs/outputs, so the number of7486 // 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 {...@@ -6149,31 +6149,26 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
61496149
6150 const zcu = func.pt.zcu;6150 const zcu = func.pt.zcu;
6151 const ip = &zcu.intern_pool;6151 const ip = &zcu.intern_pool;
6152 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;6152 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
6153 const struct_type: Type = .fromInterned(aggregate.ty);6153 const clobbers_ty = clobbers_val.typeOf(zcu);
6154 switch (aggregate.storage) {6154 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
6155 .elems => |elems| for (elems, 0..) |elem, i| {6155 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
6156 switch (elem) {6156 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
6157 .bool_true => {6157 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
6158 const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;6158 const limb_bits = @bitSizeOf(std.math.big.Limb);
6159 assert(clobber.len != 0);6159 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
6160 if (std.mem.eql(u8, clobber, "memory")) {6160 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
6161 // nothing really to do6161 0 => continue, // field is false
6162 } else {6162 1 => {}, // field is true
6163 try func.register_manager.getReg(parseRegName(clobber) orelse6163 }
6164 return func.fail("invalid clobber: '{s}'", .{clobber}), null);6164 const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
6165 }6165 assert(clobber.len != 0);
6166 },6166 if (std.mem.eql(u8, clobber, "memory")) {
6167 .bool_false => continue,6167 // nothing really to do
6168 else => unreachable,6168 } else {
6169 }6169 try func.register_manager.getReg(parseRegName(clobber) orelse
6170 },6170 return func.fail("invalid clobber: '{s}'", .{clobber}), null);
6171 .repeated_elem => |elem| switch (elem) {6171 }
6172 .bool_true => @panic("TODO"),
6173 .bool_false => {},
6174 else => unreachable,
6175 },
6176 .bytes => @panic("TODO"),
6177 }6172 }
61786173
6179 const Label = struct {6174 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 {...@@ -969,7 +969,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
969 const bytes = std.mem.alignForward(u16, cg.module.backingIntBits(bits).@"0", 8) / 8;969 const bytes = std.mem.alignForward(u16, cg.module.backingIntBits(bits).@"0", 8) / 8;
970 var limbs: [8]u8 = undefined;970 var limbs: [8]u8 = undefined;
971 @memset(&limbs, 0);971 @memset(&limbs, 0);
972 val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;972 val.writeToPackedMemory(pt, limbs[0..bytes], 0) catch unreachable;
973 const backing_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip));973 const backing_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip));
974 return try cg.constInt(backing_ty, @as(u64, @bitCast(limbs)));974 return try cg.constInt(backing_ty, @as(u64, @bitCast(limbs)));
975 }975 }
src/codegen/wasm/CodeGen.zig+2-2
...@@ -3253,7 +3253,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3253,7 +3253,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3253 // are by-ref types.3253 // are by-ref types.
3254 assert(struct_type.layout == .@"packed");3254 assert(struct_type.layout == .@"packed");
3255 var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer3255 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;
3257 const backing_int_ty = Type.fromInterned(struct_type.backingIntTypeUnordered(ip));3257 const backing_int_ty = Type.fromInterned(struct_type.backingIntTypeUnordered(ip));
3258 const int_val = try pt.intValue(3258 const int_val = try pt.intValue(
3259 backing_int_ty,3259 backing_int_ty,
...@@ -3267,7 +3267,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {...@@ -3267,7 +3267,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
3267 const int_type = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu)));3267 const int_type = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu)));
32683268
3269 var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer3269 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;
3271 const int_val = try pt.intValue(3271 const int_val = try pt.intValue(
3272 int_type,3272 int_type,
3273 mem.readInt(u64, &buf, .little),3273 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 {...@@ -177294,41 +177294,38 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
177294 }177294 }
177295177295
177296 const ip = &zcu.intern_pool;177296 const ip = &zcu.intern_pool;
177297 const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;177297 const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
177298 const struct_type: Type = .fromInterned(aggregate.ty);177298 const clobbers_ty = clobbers_val.typeOf(zcu);
177299 switch (aggregate.storage) {177299 var clobbers_bigint_buf: Value.BigIntSpace = undefined;
177300 .elems => |elems| for (elems, 0..) |elem, i| switch (elem) {177300 const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
177301 .bool_true => {177301 for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
177302 const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;177302 assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
177303 assert(clobber.len != 0);177303 const limb_bits = @bitSizeOf(std.math.big.Limb);
177304177304 if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
177305 if (std.mem.eql(u8, clobber, "memory") or177305 switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
177306 std.mem.eql(u8, clobber, "fpsr") or177306 0 => continue, // field is false
177307 std.mem.eql(u8, clobber, "fpcr") or177307 1 => {}, // field is true
177308 std.mem.eql(u8, clobber, "mxcsr") or177308 }
177309 std.mem.eql(u8, clobber, "dirflag"))177309 const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
177310 {177310 assert(clobber.len != 0);
177311 // ok, sure177311
177312 } else if (std.mem.eql(u8, clobber, "cc") or177312 if (std.mem.eql(u8, clobber, "memory") or
177313 std.mem.eql(u8, clobber, "flags") or177313 std.mem.eql(u8, clobber, "fpsr") or
177314 std.mem.eql(u8, clobber, "eflags") or177314 std.mem.eql(u8, clobber, "fpcr") or
177315 std.mem.eql(u8, clobber, "rflags"))177315 std.mem.eql(u8, clobber, "mxcsr") or
177316 {177316 std.mem.eql(u8, clobber, "dirflag"))
177317 try self.spillEflagsIfOccupied();177317 {
177318 } else {177318 // ok, sure
177319 try self.register_manager.getReg(parseRegName(clobber) orelse177319 } else if (std.mem.eql(u8, clobber, "cc") or
177320 return self.fail("invalid clobber: '{s}'", .{clobber}), null);177320 std.mem.eql(u8, clobber, "flags") or
177321 }177321 std.mem.eql(u8, clobber, "eflags") or
177322 },177322 std.mem.eql(u8, clobber, "rflags"))
177323 .bool_false => continue,177323 {
177324 else => unreachable,177324 try self.spillEflagsIfOccupied();
177325 },177325 } else {
177326 .repeated_elem => |elem| switch (elem) {177326 try self.register_manager.getReg(parseRegName(clobber) orelse
177327 .bool_true => @panic("TODO"),177327 return self.fail("invalid clobber: '{s}'", .{clobber}), null);
177328 .bool_false => {},177328 }
177329 else => unreachable,
177330 },
177331 .bytes => @panic("TODO"),
177332 }177329 }
177333177330
177334 const Label = struct {177331 const Label = struct {
src/link/Dwarf.zig+11
...@@ -3378,6 +3378,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo...@@ -3378,6 +3378,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
3378 .opt,3378 .opt,
3379 .aggregate,3379 .aggregate,
3380 .un,3380 .un,
3381 .bitpack,
3381 => .decl_const,3382 => .decl_const,
3382 .variable => .decl_var,3383 .variable => .decl_var,
3383 .@"extern" => unreachable,3384 .@"extern" => unreachable,
...@@ -4014,6 +4015,7 @@ fn updateLazyType(...@@ -4014,6 +4015,7 @@ fn updateLazyType(
4014 .opt,4015 .opt,
4015 .aggregate,4016 .aggregate,
4016 .un,4017 .un,
4018 .bitpack,
4017 // memoization, not types4019 // memoization, not types
4018 .memoized_call,4020 .memoized_call,
4019 => unreachable,4021 => unreachable,
...@@ -4092,6 +4094,15 @@ fn updateLazyValue(...@@ -4092,6 +4094,15 @@ fn updateLazyValue(
4092 }, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu));4094 }, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu));
4093 try wip_nav.refType(.fromInterned(int.ty));4095 try wip_nav.refType(.fromInterned(int.ty));
4094 },4096 },
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 },
4095 .err => |err| {4106 .err => |err| {
4096 try wip_nav.abbrevCode(.udata_comptime_value);4107 try wip_nav.abbrevCode(.udata_comptime_value);
4097 try wip_nav.refType(.fromInterned(err.ty));4108 try wip_nav.refType(.fromInterned(err.ty));
src/mutable_value.zig+15-11
...@@ -97,8 +97,8 @@ pub const MutableValue = union(enum) {...@@ -97,8 +97,8 @@ pub const MutableValue = union(enum) {
97 /// * Non-error error unions use `eu_payload`97 /// * Non-error error unions use `eu_payload`
98 /// * Non-null optionals use `eu_payload98 /// * Non-null optionals use `eu_payload
99 /// * Slices use `slice`99 /// * Slices use `slice`
100 /// * Unions use `un`100 /// * Unions use `un` (excluding packed unions)
101 /// * Aggregates use `repeated` or `bytes` or `aggregate`101 /// * Aggregates use `repeated` or `bytes` or `aggregate` (excluding packed structs)
102 /// If `!allow_bytes`, the `bytes` representation will not be used.102 /// If `!allow_bytes`, the `bytes` representation will not be used.
103 /// If `!allow_repeated`, the `repeated` representation will not be used.103 /// If `!allow_repeated`, the `repeated` representation will not be used.
104 pub fn unintern(104 pub fn unintern(
...@@ -209,6 +209,7 @@ pub const MutableValue = union(enum) {...@@ -209,6 +209,7 @@ pub const MutableValue = union(enum) {
209 .undef => |ty_ip| switch (Type.fromInterned(ty_ip).zigTypeTag(zcu)) {209 .undef => |ty_ip| switch (Type.fromInterned(ty_ip).zigTypeTag(zcu)) {
210 .@"struct", .array, .vector => |type_tag| {210 .@"struct", .array, .vector => |type_tag| {
211 const ty = Type.fromInterned(ty_ip);211 const ty = Type.fromInterned(ty_ip);
212 if (type_tag == .@"struct" and ty.containerLayout(zcu) == .@"packed") return;
212 const opt_sent = ty.sentinel(zcu);213 const opt_sent = ty.sentinel(zcu);
213 if (type_tag == .@"struct" or opt_sent != null or !allow_repeated) {214 if (type_tag == .@"struct" or opt_sent != null or !allow_repeated) {
214 const len_no_sent = ip.aggregateTypeLen(ty_ip);215 const len_no_sent = ip.aggregateTypeLen(ty_ip);
...@@ -241,15 +242,18 @@ pub const MutableValue = union(enum) {...@@ -241,15 +242,18 @@ pub const MutableValue = union(enum) {
241 } };242 } };
242 }243 }
243 },244 },
244 .@"union" => {245 .@"union" => switch (Type.fromInterned(ty_ip).containerLayout(zcu)) {
245 const payload = try arena.create(MutableValue);246 .auto, .@"packed" => {},
246 const backing_ty = try Type.fromInterned(ty_ip).unionBackingType(pt);247 .@"extern" => {
247 payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) };248 const payload = try arena.create(MutableValue);
248 mv.* = .{ .un = .{249 const backing_ty = try Type.fromInterned(ty_ip).externUnionBackingType(pt);
249 .ty = ty_ip,250 payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) };
250 .tag = .none,251 mv.* = .{ .un = .{
251 .payload = payload,252 .ty = ty_ip,
252 } };253 .tag = .none,
254 .payload = payload,
255 } };
256 },
253 },257 },
254 .pointer => {258 .pointer => {
255 const ptr_ty = ip.indexToKey(ty_ip).ptr_type;259 const ptr_ty = ip.indexToKey(ty_ip).ptr_type;
src/print_value.zig+27-1
...@@ -164,7 +164,7 @@ pub fn print(...@@ -164,7 +164,7 @@ pub fn print(
164 return;164 return;
165 }165 }
166 if (un.tag == .none) {166 if (un.tag == .none) {
167 const backing_ty = try val.typeOf(zcu).unionBackingType(pt);167 const backing_ty = try val.typeOf(zcu).externUnionBackingType(pt);
168 try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});168 try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});
169 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);169 try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
170 try writer.writeAll("))");170 try writer.writeAll("))");
...@@ -176,6 +176,32 @@ pub fn print(...@@ -176,6 +176,32 @@ pub fn print(
176 try writer.writeAll(" }");176 try writer.writeAll(" }");
177 }177 }
178 },178 },
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 },
179 .memoized_call => unreachable,205 .memoized_call => unreachable,
180 }206 }
181}207}