commit b19074d252e7eb833b653263acd20e64a7fe26ff (tree)
parent 911294116d5df0db3b431f9117f42bf8074a3b83
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date: Wed, 28 Jan 2026 13:10:07 +0000
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.
Diffstat:
19 files changed, 530 insertions(+), 501 deletions(-)
diff --git a/src/Air/print.zig b/src/Air/print.zig
@@ -692,33 +692,23 @@ const Writer = struct {
const zcu = w.pt.zcu;
const ip = &zcu.intern_pool;
- const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
- const struct_type: Type = .fromInterned(aggregate.ty);
- switch (aggregate.storage) {
- .elems => |elems| for (elems, 0..) |elem, i| {
- switch (elem) {
- .bool_true => {
- const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;
- assert(clobber.len != 0);
- try s.writeAll(", ~{");
- try s.writeAll(clobber);
- try s.writeAll("}");
- },
- .bool_false => continue,
- else => unreachable,
- }
- },
- .repeated_elem => |elem| {
- try s.writeAll(", ");
- try s.writeAll(switch (elem) {
- .bool_true => "<all clobbers>",
- .bool_false => "<no clobbers>",
- else => unreachable,
- });
- },
- .bytes => |bytes| {
- try s.print(", {x}", .{bytes});
- },
+ const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
+ const clobbers_ty = clobbers_val.typeOf(zcu);
+ var clobbers_bigint_buf: Value.BigIntSpace = undefined;
+ const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
+ for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
+ assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
+ }
+ const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
+ assert(clobber.len != 0);
+ try s.writeAll(", ~{");
+ try s.writeAll(clobber);
+ try s.writeAll("}");
}
const asm_source = unwrapped_asm.source;
try s.print(", \"{f}\"", .{std.zig.fmtString(asm_source)});
diff --git a/src/InternPool.zig b/src/InternPool.zig
@@ -2130,6 +2130,8 @@ pub const Key = union(enum) {
aggregate: Aggregate,
/// An instance of a union.
un: Union,
+ /// An instance of a `packed struct` or `packed union`.
+ bitpack: Bitpack,
/// A comptime function call with a memoized result.
memoized_call: Key.MemoizedCall,
@@ -2681,6 +2683,15 @@ pub const Key = union(enum) {
};
};
+ /// As well as a key, this type doubles as the payload in `extra` for `Tag.bitpack`.
+ pub const Bitpack = struct {
+ /// The `packed struct` or `packed union` type.
+ ty: Index,
+ /// The contents of the bitpack, represented as the backing integer value. The type of this
+ /// value is the same as the backing integer type of `ty`.
+ backing_int_val: Index,
+ };
+
pub const MemoizedCall = struct {
func: Index,
arg_values: []const Index,
@@ -2919,6 +2930,8 @@ pub const Key = union(enum) {
asBytes(&e.relocation) ++
asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index) ++ &[1]u8{@intFromEnum(e.source)}),
+
+ .bitpack => |bitpack| Hash.hash(seed, asBytes(&bitpack.ty) ++ asBytes(&bitpack.backing_int_val)),
};
}
@@ -2996,6 +3009,10 @@ pub const Key = union(enum) {
const b_info = b.empty_enum_value;
return a_info == b_info;
},
+ .bitpack => |a_info| {
+ const b_info = b.bitpack;
+ return a_info.ty == b_info.ty and a_info.backing_int_val == b_info.backing_int_val;
+ },
.variable => |a_info| {
const b_info = b.variable;
@@ -3271,6 +3288,7 @@ pub const Key = union(enum) {
.enum_tag,
.aggregate,
.un,
+ .bitpack,
=> |x| x.ty,
.enum_literal => .enum_literal_type,
@@ -4417,6 +4435,7 @@ pub const Index = enum(u32) {
trailing: struct { element_values: []Index },
},
repeated: struct { data: *Repeated },
+ bitpack: struct { data: *Key.Bitpack },
memoized_call: struct {
const @"data.args_len" = opaque {};
@@ -5152,6 +5171,9 @@ pub const Tag = enum(u8) {
/// An instance of an array or vector with every element being the same value.
/// data is extra index to `Repeated`.
repeated,
+ /// An instance of a `packed struct` or `packed union`.
+ /// data is extra index to `Key.Bitpack`.
+ bitpack,
/// A memoized comptime function call result.
/// data is extra index to `MemoizedCall`
@@ -5485,6 +5507,7 @@ pub const Tag = enum(u8) {
.config = .{ .@"trailing.elements.len" = .@"payload.ty.payload.fields_len" },
},
.repeated = .{ .summary = .@"@as({.payload.ty%summary}, @splat({.payload.elem_val%summary}))", .payload = Repeated },
+ .bitpack = .{ .summary = .@"@as({.payload.ty%summary}, {})", .payload = Key.Bitpack },
.memoized_call = .{
.summary = .@"@memoize({.payload.func%summary})",
@@ -7043,6 +7066,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
},
.enum_literal => .{ .enum_literal = @enumFromInt(data) },
.enum_tag => .{ .enum_tag = extraData(unwrapped_index.getExtra(ip), Tag.EnumTag, data) },
+ .bitpack => .{ .bitpack = extraData(unwrapped_index.getExtra(ip), Key.Bitpack, data) },
.memoized_call => {
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:
.aggregate => |aggregate| {
const ty_key = ip.indexToKey(aggregate.ty);
const len = ip.aggregateTypeLen(aggregate.ty);
- const child = switch (ty_key) {
- .array_type => |array_type| array_type.child,
- .vector_type => |vector_type| vector_type.child,
- .tuple_type, .struct_type => .none,
- else => unreachable,
- };
- const sentinel = switch (ty_key) {
- .array_type => |array_type| array_type.sentinel,
- .vector_type, .tuple_type, .struct_type => .none,
+ const child: Index, const sentinel: Index = switch (ty_key) {
+ .array_type => |array_type| .{ array_type.child, array_type.sentinel },
+ .vector_type => |vector_type| .{ vector_type.child, .none },
+ .tuple_type => .{ .none, .none },
+ .struct_type => child: {
+ assert(ip.loadStructType(aggregate.ty).layout != .@"packed");
+ break :child .{ .none, .none };
+ },
else => unreachable,
};
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:
extra.appendSliceAssumeCapacity(.{@ptrCast(aggregate.storage.elems)});
if (sentinel != .none) extra.appendAssumeCapacity(.{@intFromEnum(sentinel)});
},
+ .bitpack => |bitpack| {
+ switch (ip.zigTypeTag(bitpack.ty)) {
+ .@"struct" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadStructType(bitpack.ty).packed_backing_int_type),
+ .@"union" => assert(ip.typeOf(bitpack.backing_int_val) == ip.loadUnionType(bitpack.ty).packed_backing_int_type),
+ else => unreachable,
+ }
+ assert(!ip.isUndef(bitpack.backing_int_val));
+ items.appendAssumeCapacity(.{
+ .tag = .bitpack,
+ .data = try addExtra(extra, bitpack),
+ });
+ },
.memoized_call => |memoized_call| {
for (memoized_call.arg_values) |arg| assert(arg != .none);
@@ -9095,6 +9130,10 @@ pub fn getUnion(
tid: Zcu.PerThread.Id,
un: Key.Union,
) Allocator.Error!Index {
+ assert(un.ty != .none);
+ assert(un.val != .none);
+ assert(ip.loadUnionType(un.ty).layout != .@"packed");
+
var gop = try ip.getOrPutKey(gpa, io, tid, .{ .un = un });
defer gop.deinit();
if (gop == .existing) return gop.existing;
@@ -9103,8 +9142,6 @@ pub fn getUnion(
const extra = local.getMutableExtra(gpa, io);
try items.ensureUnusedCapacity(1);
- assert(un.ty != .none);
- assert(un.val != .none);
items.appendAssumeCapacity(.{
.tag = .union_value,
.data = try addExtra(extra, un),
@@ -11003,6 +11040,7 @@ fn dumpStatsFallible(ip: *const InternPool, w: *Io.Writer, arena: Allocator) !vo
.func_coerced => @sizeOf(Tag.FuncCoerced),
.only_possible_value => 0,
.union_value => @sizeOf(Key.Union),
+ .bitpack => 2 * @sizeOf(u32),
.memoized_call => b: {
const info = extraData(extra_list, MemoizedCall, data);
@@ -11117,6 +11155,7 @@ fn dumpAllFallible(ip: *const InternPool, w: *Io.Writer) anyerror!void {
.func_instance,
.func_coerced,
.union_value,
+ .bitpack,
.memoized_call,
=> try w.print("{d}", .{data}),
@@ -11871,6 +11910,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.bytes,
.aggregate,
.repeated,
+ .bitpack,
=> |t| {
const extra_list = unwrapped_index.getExtra(ip);
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 {
.bytes,
.aggregate,
.repeated,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
diff --git a/src/Sema.zig b/src/Sema.zig
@@ -3583,7 +3583,7 @@ fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref,
},
.field => |idx| ptr: {
const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
- if (zcu.typeToUnion(maybe_union_ty)) |union_obj| {
+ if (zcu.typeToUnion(maybe_union_ty)) |union_obj| if (union_obj.layout == .auto) {
// As this is a union field, we must store to the pointer now to set the tag.
// The payload value will be stored later, so undef is a sufficent payload for now.
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,
const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx);
const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
- }
+ };
break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
},
.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
const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);
+ if (union_ty.containerLayout(zcu) == .@"packed") {
+ return sema.bitCast(block, union_ty, payload, block.nodeOffset(inst_data.src_node), payload_src);
+ }
+
if (sema.resolveValue(payload)) |payload_val| {
const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
@@ -18643,6 +18647,10 @@ fn zirStructInit(
const uncoerced_init_inst = sema.resolveInst(item.data.init);
const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
+ if (resolved_ty.containerLayout(zcu) == .@"packed") {
+ return sema.bitCast(block, resolved_ty, init_inst, src, field_src);
+ }
+
if (sema.resolveValue(init_inst)) |val| {
const struct_val = Value.fromInterned(try pt.internUnion(.{
.ty = resolved_ty.toIntern(),
@@ -18789,15 +18797,35 @@ fn finishStructInit(
}
} else null;
- const runtime_index = opt_runtime_index orelse {
- const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
- for (elems, field_inits) |*elem, field_init| {
- elem.* = sema.resolveValue(field_init).?.toIntern();
- }
- const struct_val = try pt.aggregateValue(struct_ty, elems);
- const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), init_src);
- const final_val = sema.resolveValue(final_val_inst).?;
- return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
+ const runtime_index = opt_runtime_index orelse switch (struct_ty.containerLayout(zcu)) {
+ .auto, .@"extern" => {
+ const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
+ for (elems, field_inits) |*elem, field_init| {
+ elem.* = sema.resolveValue(field_init).?.toIntern();
+ }
+ const struct_val = try pt.aggregateValue(struct_ty, elems);
+ const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
+ return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
+ },
+ .@"packed" => {
+ const buf = try sema.arena.alloc(u8, (struct_ty.bitSize(zcu) + 7) / 8);
+ var bit_offset: u16 = 0;
+ for (field_inits) |field_init| {
+ const field_val = sema.resolveValue(field_init).?;
+ field_val.writeToPackedMemory(pt, buf, bit_offset) catch |err| switch (err) {
+ error.ReinterpretDeclRef => unreachable, // bitpack fields cannot be pointers
+ error.OutOfMemory => |e| return e,
+ };
+ bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu));
+ }
+ assert(bit_offset == struct_ty.bitSize(zcu));
+ const struct_val = Value.readFromPackedMemory(struct_ty, pt, buf, 0, sema.arena) catch |err| switch (err) {
+ error.IllDefinedMemoryLayout => unreachable, // bitpacks have well-defined layout
+ error.OutOfMemory => |e| return e,
+ };
+ const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
+ return sema.addConstantMaybeRef(final_val_ref.toInterned().?, is_ref);
+ },
};
if (struct_ty.comptimeOnly(zcu)) {
diff --git a/src/Sema/bitcast.zig b/src/Sema/bitcast.zig
@@ -273,6 +273,8 @@ const UnpackValueBits = struct {
.opt,
=> try unpack.primitive(val),
+ .bitpack => |bitpack| try unpack.primitive(.fromInterned(bitpack.backing_int_val)),
+
.aggregate => switch (ty.zigTypeTag(zcu)) {
.vector => {
const len: usize = @intCast(ty.arrayLen(zcu));
@@ -443,7 +445,7 @@ const UnpackValueBits = struct {
// This @intCast is okay because no primitive can exceed the size of a u16.
const int_ty = try unpack.pt.intType(.unsigned, @intCast(bit_count));
const buf = try unpack.arena.alloc(u8, @intCast((val_bits + 7) / 8));
- try val.writeToPackedMemory(ty, unpack.pt, buf, 0);
+ try val.writeToPackedMemory(unpack.pt, buf, 0);
const sub_val = try Value.readFromPackedMemory(int_ty, unpack.pt, buf, @intCast(bit_offset), unpack.arena);
try unpack.primitive(sub_val);
},
@@ -565,102 +567,103 @@ const PackValueBits = struct {
return pt.aggregateValue(ty, elems);
},
.@"packed" => {
- // All fields are in order with no padding.
- // This is identical between LE and BE targets.
- const elems = try arena.alloc(InternPool.Index, ty.structFieldCount(zcu));
- for (elems, 0..) |*elem, i| {
- const field_ty = ty.fieldType(i, zcu);
- elem.* = (try pack.get(field_ty)).toIntern();
- }
- return pt.aggregateValue(ty, elems);
+ const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
+ return pt.bitpackValue(ty, backing_int_val);
},
},
- .@"union" => {
- // We will attempt to read as the backing representation. If this emits
- // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones.
- // We will also attempt smaller fields when we get `undefined`, as if some bits are
- // defined we want to include them.
- // TODO: this is very very bad. We need a more sophisticated union representation.
-
- const prev_unpacked = pack.unpacked;
- const prev_bit_offset = pack.bit_offset;
-
- const backing_ty = try ty.unionBackingType(pt);
-
- backing: {
- const backing_val = pack.get(backing_ty) catch |err| switch (err) {
- error.ReinterpretDeclRef => {
+ .@"union" => switch (ty.containerLayout(zcu)) {
+ .auto => unreachable, // ill-defined layout
+ .@"extern" => {
+ // We will attempt to read as the backing representation. If this emits
+ // `error.ReinterpretDeclRef`, we will try each union field, preferring larger ones.
+ // We will also attempt smaller fields when we get `undefined`, as if some bits are
+ // defined we want to include them.
+ // TODO: this is very very bad. We need a more sophisticated union representation.
+
+ const prev_unpacked = pack.unpacked;
+ const prev_bit_offset = pack.bit_offset;
+
+ const backing_ty = try ty.externUnionBackingType(pt);
+
+ backing: {
+ const backing_val = pack.get(backing_ty) catch |err| switch (err) {
+ error.ReinterpretDeclRef => {
+ pack.unpacked = prev_unpacked;
+ pack.bit_offset = prev_bit_offset;
+ break :backing;
+ },
+ else => |e| return e,
+ };
+ if (backing_val.isUndef(zcu)) {
pack.unpacked = prev_unpacked;
pack.bit_offset = prev_bit_offset;
break :backing;
- },
- else => |e| return e,
- };
- if (backing_val.isUndef(zcu)) {
- pack.unpacked = prev_unpacked;
- pack.bit_offset = prev_bit_offset;
- break :backing;
+ }
+ return Value.fromInterned(try pt.internUnion(.{
+ .ty = ty.toIntern(),
+ .tag = .none,
+ .val = backing_val.toIntern(),
+ }));
}
- return Value.fromInterned(try pt.internUnion(.{
- .ty = ty.toIntern(),
- .tag = .none,
- .val = backing_val.toIntern(),
- }));
- }
- const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu));
- for (field_order, 0..) |*f, i| f.* = @intCast(i);
- // Sort `field_order` to put the fields with the largest bit sizes first.
- const SizeSortCtx = struct {
- zcu: *Zcu,
- field_types: []const InternPool.Index,
- fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool {
- const a_ty = Type.fromInterned(ctx.field_types[a_idx]);
- const b_ty = Type.fromInterned(ctx.field_types[b_idx]);
- return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu);
- }
- };
- std.mem.sortUnstable(u32, field_order, SizeSortCtx{
- .zcu = zcu,
- .field_types = zcu.typeToUnion(ty).?.field_types.get(ip),
- }, SizeSortCtx.lessThan);
-
- const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed";
-
- for (field_order) |field_idx| {
- const field_ty = Type.fromInterned(zcu.typeToUnion(ty).?.field_types.get(ip)[field_idx]);
- const pad_bits = ty.bitSize(zcu) - field_ty.bitSize(zcu);
- if (!padding_after) try pack.padding(pad_bits);
- const field_val = pack.get(field_ty) catch |err| switch (err) {
- error.ReinterpretDeclRef => {
+ const field_order = try pack.arena.alloc(u32, ty.unionTagTypeHypothetical(zcu).enumFieldCount(zcu));
+ for (field_order, 0..) |*f, i| f.* = @intCast(i);
+ // Sort `field_order` to put the fields with the largest bit sizes first.
+ const SizeSortCtx = struct {
+ zcu: *Zcu,
+ field_types: []const InternPool.Index,
+ fn lessThan(ctx: @This(), a_idx: u32, b_idx: u32) bool {
+ const a_ty = Type.fromInterned(ctx.field_types[a_idx]);
+ const b_ty = Type.fromInterned(ctx.field_types[b_idx]);
+ return a_ty.bitSize(ctx.zcu) > b_ty.bitSize(ctx.zcu);
+ }
+ };
+ std.mem.sortUnstable(u32, field_order, SizeSortCtx{
+ .zcu = zcu,
+ .field_types = zcu.typeToUnion(ty).?.field_types.get(ip),
+ }, SizeSortCtx.lessThan);
+
+ const padding_after = endian == .little or ty.containerLayout(zcu) == .@"packed";
+
+ for (field_order) |field_idx| {
+ const field_ty = Type.fromInterned(zcu.typeToUnion(ty).?.field_types.get(ip)[field_idx]);
+ const pad_bits = ty.bitSize(zcu) - field_ty.bitSize(zcu);
+ if (!padding_after) try pack.padding(pad_bits);
+ const field_val = pack.get(field_ty) catch |err| switch (err) {
+ error.ReinterpretDeclRef => {
+ pack.unpacked = prev_unpacked;
+ pack.bit_offset = prev_bit_offset;
+ continue;
+ },
+ else => |e| return e,
+ };
+ if (padding_after) try pack.padding(pad_bits);
+ if (field_val.isUndef(zcu)) {
pack.unpacked = prev_unpacked;
pack.bit_offset = prev_bit_offset;
continue;
- },
- else => |e| return e,
- };
- if (padding_after) try pack.padding(pad_bits);
- if (field_val.isUndef(zcu)) {
- pack.unpacked = prev_unpacked;
- pack.bit_offset = prev_bit_offset;
- continue;
+ }
+ const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx);
+ return Value.fromInterned(try pt.internUnion(.{
+ .ty = ty.toIntern(),
+ .tag = tag_val.toIntern(),
+ .val = field_val.toIntern(),
+ }));
}
- const tag_val = try pt.enumValueFieldIndex(ty.unionTagTypeHypothetical(zcu), field_idx);
+
+ // No field could represent the value. Just do whatever happens when we try to read
+ // the backing type - either `undefined` or `error.ReinterpretDeclRef`.
+ const backing_val = try pack.get(backing_ty);
return Value.fromInterned(try pt.internUnion(.{
.ty = ty.toIntern(),
- .tag = tag_val.toIntern(),
- .val = field_val.toIntern(),
+ .tag = .none,
+ .val = backing_val.toIntern(),
}));
- }
-
- // No field could represent the value. Just do whatever happens when we try to read
- // the backing type - either `undefined` or `error.ReinterpretDeclRef`.
- const backing_val = try pack.get(backing_ty);
- return Value.fromInterned(try pt.internUnion(.{
- .ty = ty.toIntern(),
- .tag = .none,
- .val = backing_val.toIntern(),
- }));
+ },
+ .@"packed" => {
+ const backing_int_val = try pack.primitive(ty.bitpackBackingInt(zcu));
+ return pt.bitpackValue(ty, backing_int_val);
+ },
},
else => return pack.primitive(ty),
}
@@ -722,7 +725,7 @@ const PackValueBits = struct {
const val = Value.fromInterned(ip_val);
const ty = val.typeOf(zcu);
if (!val.isUndef(zcu)) {
- try val.writeToPackedMemory(ty, pt, buf, cur_bit_off);
+ try val.writeToPackedMemory(pt, buf, cur_bit_off);
}
cur_bit_off += @intCast(ty.bitSize(zcu));
}
diff --git a/src/Sema/type_resolution.zig b/src/Sema/type_resolution.zig
@@ -79,6 +79,7 @@ pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!vo
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
diff --git a/src/Type.zig b/src/Type.zig
@@ -407,6 +407,7 @@ pub fn print(ty: Type, writer: *std.Io.Writer, pt: Zcu.PerThread, ctx: ?*Compari
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -543,6 +544,7 @@ pub fn hasRuntimeBits(ty: Type, zcu: *const Zcu) bool {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -639,6 +641,7 @@ pub fn hasWellDefinedLayout(ty: Type, zcu: *const Zcu) bool {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -846,6 +849,7 @@ pub fn abiAlignment(ty: Type, zcu: *const Zcu) Alignment {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -978,6 +982,7 @@ pub fn abiSize(ty: Type, zcu: *const Zcu) u64 {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -1102,6 +1107,7 @@ pub fn bitSize(ty: Type, zcu: *const Zcu) u64 {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -1393,16 +1399,16 @@ pub fn unionHasAllZeroBitFieldTypes(ty: Type, zcu: *Zcu) bool {
}
/// Returns the type used for backing storage of this union during comptime operations.
-/// Asserts the type is either an extern or packed union.
-pub fn unionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
+/// Asserts the type is an extern union.
+pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
const zcu = pt.zcu;
assertHasLayout(ty, zcu);
const loaded_union = zcu.intern_pool.loadUnionType(ty.toIntern());
- return switch (loaded_union.layout) {
- .@"extern" => try pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),
- .@"packed" => .fromInterned(loaded_union.packed_backing_int_type),
+ switch (loaded_union.layout) {
+ .@"extern" => return pt.arrayType(.{ .len = ty.abiSize(zcu), .child = .u8_type }),
+ .@"packed" => unreachable,
.auto => unreachable,
- };
+ }
}
pub 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
};
}
+pub fn bitpackBackingInt(ty: Type, zcu: *const Zcu) Type {
+ const ip = &zcu.intern_pool;
+ return switch (ip.indexToKey(ty.toIntern())) {
+ .struct_type => .fromInterned(ip.loadStructType(ty.toIntern()).packed_backing_int_type),
+ .union_type => .fromInterned(ip.loadUnionType(ty.toIntern()).packed_backing_int_type),
+ else => unreachable,
+ };
+}
+
/// Asserts that the type is an error union.
pub fn errorUnionPayload(ty: Type, zcu: *const Zcu) Type {
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 {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -1842,7 +1858,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
if (struct_obj.layout == .@"packed") {
const backing_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
- _ = backing_val; // MLUGG TODO: represent unions as their bits!
+ return try pt.bitpackValue(ty, backing_val);
} else {
if (!struct_obj.has_one_possible_value) return null;
}
@@ -1893,8 +1909,13 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
},
.union_type => {
- // MLUGG TODO: is this nonsensical or what!!!!!!
const union_obj = ip.loadUnionType(ty.toIntern());
+ if (union_obj.layout == .@"packed") {
+ const backing_ty: Type = .fromInterned(union_obj.packed_backing_int_type);
+ const backing_val = try backing_ty.onePossibleValue(pt) orelse return null;
+ return try pt.bitpackValue(ty, backing_val);
+ }
+ // MLUGG TODO: is this nonsensical or what!!!!!!
const tag_val = (try Type.fromInterned(union_obj.enum_tag_type).onePossibleValue(pt)) orelse
return null;
if (union_obj.field_types.len == 0) {
@@ -1957,6 +1978,7 @@ pub fn onePossibleValue(starting_type: Type, pt: Zcu.PerThread) !?Value {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -2061,6 +2083,7 @@ pub fn comptimeOnly(ty: Type, zcu: *const Zcu) bool {
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -3080,6 +3103,7 @@ pub fn assertHasLayout(ty: Type, zcu: *const Zcu) void {
.opt,
.aggregate,
.un,
+ .bitpack,
.undef,
// memoization, not types
.memoized_call,
@@ -3158,6 +3182,7 @@ fn collectSubtypes(ty: Type, pt: Zcu.PerThread, visited: *std.AutoArrayHashMapUn
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
diff --git a/src/Value.zig b/src/Value.zig
@@ -158,6 +158,7 @@ pub fn toBigInt(val: Value, space: *BigIntSpace, zcu: *Zcu) BigIntConst {
const ip = &zcu.intern_pool;
const int_key = switch (ip.indexToKey(val.toIntern())) {
.enum_tag => |enum_tag| ip.indexToKey(enum_tag.int).int,
+ .bitpack => |bitpack| ip.indexToKey(bitpack.backing_int_val).int,
.int => |int| int,
else => unreachable,
};
@@ -216,6 +217,7 @@ pub fn getUnsignedInt(val: Value, zcu: *const Zcu) ?u64 {
else => |payload| Value.fromInterned(payload).getUnsignedInt(zcu),
},
.enum_tag => |enum_tag| Value.fromInterned(enum_tag.int).getUnsignedInt(zcu),
+ .bitpack => |bitpack| Value.fromInterned(bitpack.backing_int_val).getUnsignedInt(zcu),
.err => |err| zcu.intern_pool.getErrorValueIfExists(err.name).?,
else => null,
},
@@ -309,7 +311,7 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
// We use byte_count instead of abi_size here, so that any padding bytes
// follow the data bytes, on both big- and little-endian systems.
const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;
- return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
+ return writeToPackedMemory(val, pt, buffer[0..byte_count], 0);
},
.@"struct" => {
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{
try writeToMemory(field_val, pt, buffer[off..]);
},
.@"packed" => {
- const byte_count = (@as(usize, @intCast(ty.bitSize(zcu))) + 7) / 8;
- return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
+ const int_index = ip.indexToKey(val.toIntern()).bitpack.backing_int_val;
+ return Value.fromInterned(int_index).writeToMemory(pt, buffer);
},
}
},
@@ -344,15 +346,14 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
const byte_count: usize = @intCast(field_type.abiSize(zcu));
return writeToMemory(field_val, pt, buffer[0..byte_count]);
} else {
- const backing_ty = try ty.unionBackingType(pt);
+ const backing_ty = try ty.externUnionBackingType(pt);
const byte_count: usize = @intCast(backing_ty.abiSize(zcu));
return writeToMemory(val.unionValue(zcu), pt, buffer[0..byte_count]);
}
},
.@"packed" => {
- const backing_ty = try ty.unionBackingType(pt);
- const byte_count: usize = @intCast(backing_ty.abiSize(zcu));
- return writeToPackedMemory(val, ty, pt, buffer[0..byte_count], 0);
+ const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
+ return writeToMemory(int_val, pt, buffer);
},
},
.optional => {
@@ -374,7 +375,6 @@ pub fn writeToMemory(val: Value, pt: Zcu.PerThread, buffer: []u8) error{
/// big-endian packed memory layouts start at the end of the buffer.
pub fn writeToPackedMemory(
val: Value,
- ty: Type,
pt: Zcu.PerThread,
buffer: []u8,
bit_offset: usize,
@@ -383,6 +383,7 @@ pub fn writeToPackedMemory(
const ip = &zcu.intern_pool;
const target = zcu.getTarget();
const endian = target.cpu.arch.endian();
+ const ty = val.typeOf(zcu);
if (val.isUndef(zcu)) {
const bit_size: usize = @intCast(ty.bitSize(zcu));
if (bit_size != 0) {
@@ -405,7 +406,13 @@ pub fn writeToPackedMemory(
},
.@"enum" => {
const int_val = val.intFromEnum(zcu);
- return int_val.writeToPackedMemory(int_val.typeOf(zcu), pt, buffer, bit_offset);
+ return int_val.writeToPackedMemory(pt, buffer, bit_offset);
+ },
+ .pointer => {
+ assert(!ty.isSlice(zcu)); // No well defined layout.
+ if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef;
+ const addr = val.toUnsignedInt(zcu);
+ std.mem.writeVarPackedInt(buffer, bit_offset, zcu.getTarget().ptrBitWidth(), addr, endian);
},
.int => {
const bits = ty.intInfo(zcu).bits;
@@ -434,54 +441,21 @@ pub fn writeToPackedMemory(
// On big-endian systems, LLVM reverses the element order of vectors by default
const tgt_elem_i = if (endian == .big) len - elem_i - 1 else elem_i;
const elem_val = try val.elemValue(pt, tgt_elem_i);
- try elem_val.writeToPackedMemory(elem_ty, pt, buffer, bit_offset + bits);
+ try elem_val.writeToPackedMemory(pt, buffer, bit_offset + bits);
bits += elem_bit_size;
}
},
- .@"struct" => {
- const struct_type = ip.loadStructType(ty.toIntern());
- // Sema is supposed to have emitted a compile error already in the case of Auto,
- // and Extern is handled in non-packed writeToMemory.
- assert(struct_type.layout == .@"packed");
- var bits: u16 = 0;
- for (0..struct_type.field_types.len) |i| {
- const field_val = Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
- .bytes => unreachable,
- .elems => |elems| elems[i],
- .repeated_elem => |elem| elem,
- });
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
- const field_bits: u16 = @intCast(field_ty.bitSize(zcu));
- try field_val.writeToPackedMemory(field_ty, pt, buffer, bit_offset + bits);
- bits += field_bits;
- }
- },
- .@"union" => {
- const union_obj = zcu.typeToUnion(ty).?;
- assert(union_obj.layout == .@"packed");
- if (val.unionTag(zcu)) |union_tag| {
- const field_index = zcu.unionTagFieldIndex(union_obj, union_tag).?;
- const field_type = Type.fromInterned(union_obj.field_types.get(ip)[field_index]);
- const field_val = try val.fieldValue(pt, field_index);
- return field_val.writeToPackedMemory(field_type, pt, buffer, bit_offset);
- } else {
- const backing_ty = try ty.unionBackingType(pt);
- return val.unionValue(zcu).writeToPackedMemory(backing_ty, pt, buffer, bit_offset);
- }
- },
- .pointer => {
- assert(!ty.isSlice(zcu)); // No well defined layout.
- if (ip.getBackingAddrTag(val.toIntern()).? != .int) return error.ReinterpretDeclRef;
- return val.writeToPackedMemory(Type.usize, pt, buffer, bit_offset);
+ .@"struct", .@"union" => {
+ assert(ty.containerLayout(zcu) == .@"packed");
+ const int_val: Value = .fromInterned(ip.indexToKey(val.toIntern()).bitpack.backing_int_val);
+ return int_val.writeToPackedMemory(pt, buffer, bit_offset);
},
.optional => {
assert(ty.isPtrLikeOptional(zcu));
- const child = ty.optionalChild(zcu);
- const opt_val = val.optionalValue(zcu);
- if (opt_val) |some| {
- return some.writeToPackedMemory(child, pt, buffer, bit_offset);
+ if (val.optionalValue(zcu)) |ptr_val| {
+ return ptr_val.writeToPackedMemory(pt, buffer, bit_offset);
} else {
- return writeToPackedMemory(try pt.intValue(Type.usize, 0), Type.usize, pt, buffer, bit_offset);
+ return Value.zero_usize.writeToPackedMemory(pt, buffer, bit_offset);
}
},
else => @panic("TODO implement writeToPackedMemory for more types"),
@@ -531,13 +505,12 @@ pub fn readFromPackedMemory(
pt: Zcu.PerThread,
buffer: []const u8,
bit_offset: usize,
- arena: Allocator,
+ gpa: Allocator,
) error{
IllDefinedMemoryLayout,
OutOfMemory,
}!Value {
const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
const target = zcu.getTarget();
const endian = target.cpu.arch.endian();
switch (ty.zigTypeTag(zcu)) {
@@ -571,7 +544,8 @@ pub fn readFromPackedMemory(
const abi_size: usize = @intCast(ty.abiSize(zcu));
const Limb = std.math.big.Limb;
const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb);
- const limbs_buffer = try arena.alloc(Limb, limb_count);
+ const limbs_buffer = try gpa.alloc(Limb, limb_count);
+ defer gpa.free(limbs_buffer);
var bigint = BigIntMutable.init(limbs_buffer, 0);
bigint.readPackedTwosComplement(buffer, bit_offset, bits, endian, int_info.signedness);
@@ -579,7 +553,7 @@ pub fn readFromPackedMemory(
},
.@"enum" => {
const int_ty = ty.intTagType(zcu);
- const int_val = try Value.readFromPackedMemory(int_ty, pt, buffer, bit_offset, arena);
+ const int_val = try Value.readFromPackedMemory(int_ty, pt, buffer, bit_offset, gpa);
return pt.getCoerced(int_val, ty);
},
.float => return Value.fromInterned(try pt.intern(.{ .float = .{
@@ -595,52 +569,32 @@ pub fn readFromPackedMemory(
} })),
.vector => {
const elem_ty = ty.childType(zcu);
- const elems = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu)));
+ const elems = try gpa.alloc(InternPool.Index, @intCast(ty.arrayLen(zcu)));
+ defer gpa.free(elems);
var bits: u16 = 0;
const elem_bit_size: u16 = @intCast(elem_ty.bitSize(zcu));
for (elems, 0..) |_, i| {
// On big-endian systems, LLVM reverses the element order of vectors by default
const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i;
- elems[tgt_elem_i] = (try readFromPackedMemory(elem_ty, pt, buffer, bit_offset + bits, arena)).toIntern();
+ elems[tgt_elem_i] = (try readFromPackedMemory(elem_ty, pt, buffer, bit_offset + bits, gpa)).toIntern();
bits += elem_bit_size;
}
return pt.aggregateValue(ty, elems);
},
- .@"struct" => {
- // Sema is supposed to have emitted a compile error already for Auto layout structs,
- // and Extern is handled by non-packed readFromMemory.
- const struct_type = zcu.typeToPackedStruct(ty).?;
- var bits: u16 = 0;
- const field_vals = try arena.alloc(InternPool.Index, struct_type.field_types.len);
- for (field_vals, 0..) |*field_val, i| {
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
- const field_bits: u16 = @intCast(field_ty.bitSize(zcu));
- field_val.* = (try readFromPackedMemory(field_ty, pt, buffer, bit_offset + bits, arena)).toIntern();
- bits += field_bits;
- }
- return pt.aggregateValue(ty, field_vals);
- },
- .@"union" => switch (ty.containerLayout(zcu)) {
- .auto, .@"extern" => unreachable, // Handled by non-packed readFromMemory
- .@"packed" => {
- const backing_ty = try ty.unionBackingType(pt);
- const val = (try readFromPackedMemory(backing_ty, pt, buffer, bit_offset, arena)).toIntern();
- return Value.fromInterned(try pt.internUnion(.{
- .ty = ty.toIntern(),
- .tag = .none,
- .val = val,
- }));
- },
+ .@"struct", .@"union" => {
+ assert(ty.containerLayout(zcu) == .@"packed");
+ const int_val: Value = try .readFromPackedMemory(ty.bitpackBackingInt(zcu), pt, buffer, bit_offset, gpa);
+ return pt.bitpackValue(ty, int_val);
},
.pointer => {
assert(!ty.isSlice(zcu)); // No well defined layout.
- const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena)).toUnsignedInt(zcu);
+ const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, gpa)).toUnsignedInt(zcu);
return pt.ptrIntValue(ty, addr);
},
.optional => {
assert(ty.isPtrLikeOptional(zcu));
- const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, arena)).toUnsignedInt(zcu);
+ const addr = (try readFromPackedMemory(Type.usize, pt, buffer, bit_offset, gpa)).toUnsignedInt(zcu);
return .fromInterned(try pt.intern(.{ .opt = .{
.ty = ty.toIntern(),
.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 {
.elems => |elems| elems[index],
.repeated_elem => |elem| elem,
}),
- // TODO assert the tag is correct
- .un => |un| Value.fromInterned(un.val),
+ .un => |un| {
+ switch (Type.fromInterned(un.ty).containerLayout(zcu)) {
+ .auto, .@"extern" => {}, // TODO assert the tag is correct
+ .@"packed" => unreachable,
+ }
+ return .fromInterned(un.val);
+ },
+ .bitpack => |bitpack| {
+ const ty: Type = .fromInterned(bitpack.ty);
+ assert(ty.containerLayout(zcu) == .@"packed");
+ const int_val: Value = .fromInterned(bitpack.backing_int_val);
+ assert(!int_val.isUndef(zcu));
+ const field_ty = ty.fieldType(index, zcu);
+ const field_bit_offset: u16 = switch (ty.zigTypeTag(zcu)) {
+ .@"union" => 0,
+ .@"struct" => off: {
+ var off: u16 = 0;
+ for (0..index) |preceding_field_index| {
+ off += @intCast(ty.fieldType(preceding_field_index, zcu).bitSize(zcu));
+ }
+ break :off off;
+ },
+ else => unreachable,
+ };
+ // Avoid hitting gpa for accesses to small packed structs
+ var sfba_state = std.heap.stackFallback(128, zcu.comp.gpa);
+ const sfba = sfba_state.get();
+ const buf = try sfba.alloc(u8, (ty.bitSize(zcu) + 7) / 8);
+ defer sfba.free(buf);
+ int_val.writeToPackedMemory(pt, buf, 0) catch |err| switch (err) {
+ error.ReinterpretDeclRef => unreachable, // it's an integer
+ error.OutOfMemory => |e| return e,
+ };
+ return Value.readFromPackedMemory(field_ty, pt, buf, field_bit_offset, sfba) catch |err| switch (err) {
+ error.IllDefinedMemoryLayout => unreachable, // it's a bitpack
+ error.OutOfMemory => |e| return e,
+ };
+ },
else => unreachable,
};
}
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
@@ -3950,6 +3950,15 @@ pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value
} }));
}
+/// Create a value whose type is a `packed struct` or `packed union`, from the backing integer value.
+pub fn bitpackValue(pt: Zcu.PerThread, ty: Type, backing_int_val: Value) Allocator.Error!Value {
+ assert(backing_int_val.typeOf(pt.zcu).toIntern() == ty.bitpackBackingInt(pt.zcu).toIntern());
+ return .fromInterned(try pt.intern(.{ .bitpack = .{
+ .ty = ty.toIntern(),
+ .backing_int_val = backing_int_val.toIntern(),
+ } }));
+}
+
pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value {
assert(pt.zcu.intern_pool.isOptionalType(opt_ty.toIntern()));
return Value.fromInterned(try pt.intern(.{ .opt = .{
diff --git a/src/codegen.zig b/src/codegen.zig
@@ -570,42 +570,7 @@ pub fn generateSymbol(
.struct_type => {
const struct_type = ip.loadStructType(ty.toIntern());
switch (struct_type.layout) {
- .@"packed" => {
- const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse return error.Overflow;
- const start = w.end;
- const buffer = try w.writableSlice(abi_size);
- @memset(buffer, 0);
- var bits: u16 = 0;
-
- for (struct_type.field_types.get(ip), 0..) |field_ty, index| {
- const field_val = switch (aggregate.storage) {
- .bytes => |bytes| try pt.intern(.{ .int = .{
- .ty = field_ty,
- .storage = .{ .u64 = bytes.at(index, ip) },
- } }),
- .elems => |elems| elems[index],
- .repeated_elem => |elem| elem,
- };
-
- // pointer may point to a decl which must be marked used
- // but can also result in a relocation. Therefore we handle those separately.
- if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) {
- const field_offset = std.math.divExact(u16, bits, 8) catch |err| switch (err) {
- error.DivisionByZero => unreachable,
- error.UnexpectedRemainder => return error.RelocationNotByteAligned,
- };
- w.end = start + field_offset;
- defer {
- assert(w.end == start + field_offset + @divExact(target.ptrBitWidth(), 8));
- w.end = start + abi_size;
- }
- try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), w, reloc_parent);
- } else {
- Value.fromInterned(field_val).writeToPackedMemory(.fromInterned(field_ty), pt, buffer, bits) catch unreachable;
- }
- bits += @intCast(Type.fromInterned(field_ty).bitSize(zcu));
- }
- },
+ .@"packed" => unreachable,
.auto, .@"extern" => {
const struct_begin = w.end;
const field_types = struct_type.field_types.get(ip);
@@ -683,6 +648,7 @@ pub fn generateSymbol(
}
}
},
+ .bitpack => |bitpack| try generateSymbol(bin_file, pt, src_loc, .fromInterned(bitpack.backing_int_val), w, reloc_parent),
.memoized_call => unreachable,
}
}
@@ -1120,6 +1086,10 @@ pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allo
target,
);
},
+ .@"struct", .@"union" => if (ty.containerLayout(zcu) == .@"packed") {
+ const bitpack = ip.indexToKey(val.toIntern()).bitpack;
+ return lowerValue(pt, .fromInterned(bitpack.backing_int_val), target);
+ },
.error_set => {
const err_name = ip.indexToKey(val.toIntern()).err.name;
const error_index = ip.getErrorValueIfExists(err_name).?;
diff --git a/src/codegen/aarch64/Select.zig b/src/codegen/aarch64/Select.zig
@@ -2791,17 +2791,17 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
} else return isel.fail("invalid constraint: '{s}'", .{constraint});
}
- const clobbers = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
- const clobbers_ty: ZigType = .fromInterned(clobbers.ty);
+ const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
+ const clobbers_ty = clobbers_val.typeOf(zcu);
+ var clobbers_bigint_buf: Value.BigIntSpace = undefined;
+ const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
- switch (switch (clobbers.storage) {
- .bytes => unreachable,
- .elems => |elems| elems[field_index],
- .repeated_elem => |repeated_elem| repeated_elem,
- }) {
- else => unreachable,
- .bool_false => continue,
- .bool_true => {},
+ assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
}
const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
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,
}
}
for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
- switch (switch (clobbers.storage) {
- .bytes => unreachable,
- .elems => |elems| elems[field_index],
- .repeated_elem => |repeated_elem| repeated_elem,
- }) {
- else => unreachable,
- .bool_false => continue,
- .bool_true => {},
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> field_index % limb_bits))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
}
const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
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,
}
for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
- switch (switch (clobbers.storage) {
- .bytes => unreachable,
- .elems => |elems| elems[field_index],
- .repeated_elem => |repeated_elem| repeated_elem,
- }) {
- else => unreachable,
- .bool_false => continue,
- .bool_true => {},
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> field_index % limb_bits))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
}
const clobber_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
if (std.mem.eql(u8, clobber_name, "memory")) continue;
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
@@ -1362,77 +1362,42 @@ pub const DeclGen = struct {
},
.struct_type => {
const loaded_struct = ip.loadStructType(ty.toIntern());
- switch (loaded_struct.layout) {
- .auto, .@"extern" => {
- if (!location.isInitializer()) {
- try w.writeByte('(');
- try dg.renderCType(w, ctype);
- try w.writeByte(')');
- }
+ assert(loaded_struct.layout != .@"packed");
- try w.writeByte('{');
- var field_it = loaded_struct.iterateRuntimeOrder(ip);
- var need_comma = false;
- while (field_it.next()) |field_index| {
- const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
- if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
+ if (!location.isInitializer()) {
+ try w.writeByte('(');
+ try dg.renderCType(w, ctype);
+ try w.writeByte(')');
+ }
- if (need_comma) try w.writeByte(',');
- need_comma = true;
- const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
- .bytes => |bytes| try pt.intern(.{ .int = .{
- .ty = field_ty.toIntern(),
- .storage = .{ .u64 = bytes.at(field_index, ip) },
- } }),
- .elems => |elems| elems[field_index],
- .repeated_elem => |elem| elem,
- };
- try dg.renderValue(w, Value.fromInterned(field_val), initializer_type);
- }
- try w.writeByte('}');
- },
- .@"packed" => {
- // https://github.com/ziglang/zig/issues/24657 will eliminate most of the
- // following logic, leaving only the recursive `renderValue` call. Once
- // that proposal is implemented, a `packed struct` will literally be
- // represented in the InternPool by its comptime-known backing integer.
- var arena: std.heap.ArenaAllocator = .init(zcu.gpa);
- defer arena.deinit();
- const backing_ty: Type = .fromInterned(loaded_struct.backingIntTypeUnordered(ip));
- const buf = try arena.allocator().alloc(u8, @intCast(ty.abiSize(zcu)));
- val.writeToMemory(pt, buf) catch |err| switch (err) {
- error.IllDefinedMemoryLayout => unreachable,
- error.OutOfMemory => |e| return e,
- error.ReinterpretDeclRef, error.Unimplemented => return dg.fail("TODO: C backend: lower packed struct value", .{}),
- };
- const backing_val: Value = try .readUintFromMemory(backing_ty, pt, buf, arena.allocator());
- return dg.renderValue(w, backing_val, location);
- },
+ try w.writeByte('{');
+ var field_it = loaded_struct.iterateRuntimeOrder(ip);
+ var need_comma = false;
+ while (field_it.next()) |field_index| {
+ const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]);
+ if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue;
+
+ if (need_comma) try w.writeByte(',');
+ need_comma = true;
+ const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) {
+ .bytes => |bytes| try pt.intern(.{ .int = .{
+ .ty = field_ty.toIntern(),
+ .storage = .{ .u64 = bytes.at(field_index, ip) },
+ } }),
+ .elems => |elems| elems[field_index],
+ .repeated_elem => |elem| elem,
+ };
+ try dg.renderValue(w, Value.fromInterned(field_val), initializer_type);
}
+ try w.writeByte('}');
},
else => unreachable,
},
+ .bitpack => |bitpack| return dg.renderValue(w, .fromInterned(bitpack.backing_int_val), location),
.un => |un| {
const loaded_union = ip.loadUnionType(ty.toIntern());
- if (loaded_union.flagsUnordered(ip).layout == .@"packed") {
- // https://github.com/ziglang/zig/issues/24657 will eliminate most of the
- // following logic, leaving only the recursive `renderValue` call. Once
- // that proposal is implemented, a `packed union` will literally be
- // represented in the InternPool by its comptime-known backing integer.
- var arena: std.heap.ArenaAllocator = .init(zcu.gpa);
- defer arena.deinit();
- const backing_ty = try ty.unionBackingType(pt);
- const buf = try arena.allocator().alloc(u8, @intCast(ty.abiSize(zcu)));
- val.writeToMemory(pt, buf) catch |err| switch (err) {
- error.IllDefinedMemoryLayout => unreachable,
- error.OutOfMemory => |e| return e,
- error.ReinterpretDeclRef, error.Unimplemented => return dg.fail("TODO: C backend: lower packed union value", .{}),
- };
- const backing_val: Value = try .readUintFromMemory(backing_ty, pt, buf, arena.allocator());
- return dg.renderValue(w, backing_val, location);
- }
if (un.tag == .none) {
- const backing_ty = try ty.unionBackingType(pt);
+ const backing_ty = try ty.externUnionBackingType(pt);
assert(loaded_union.flagsUnordered(ip).layout == .@"extern");
if (location == .StaticInitializer) {
return dg.fail("TODO: C backend: implement extern union backing type rendering in static initializers", .{});
@@ -1642,11 +1607,7 @@ pub const DeclGen = struct {
}
return w.writeByte('}');
},
- .@"packed" => return dg.renderUndefValue(
- w,
- .fromInterned(loaded_struct.backingIntTypeUnordered(ip)),
- location,
- ),
+ .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
}
},
.tuple_type => |tuple_info| {
@@ -1714,11 +1675,7 @@ pub const DeclGen = struct {
}
if (has_tag) try w.writeByte('}');
},
- .@"packed" => return dg.renderUndefValue(
- w,
- try ty.unionBackingType(pt),
- location,
- ),
+ .@"packed" => return dg.renderUndefValue(w, ty.bitpackBackingInt(zcu), location),
}
},
.error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) {
@@ -5623,48 +5580,45 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue {
}
try w.writeByte(':');
const ip = &zcu.intern_pool;
- const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
- const struct_type: Type = .fromInterned(aggregate.ty);
- switch (aggregate.storage) {
- .elems => |elems| for (elems, 0..) |elem, i| switch (elem) {
- .bool_true => {
- const field_name = struct_type.structFieldName(i, zcu).toSlice(ip).?;
- assert(field_name.len != 0);
-
- const target = &f.object.dg.mod.resolved_target.result;
- var c_name_buf: [16]u8 = undefined;
- const name =
- if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {
- // Convert "rN" to "$N"
- const c_name = (&c_name_buf)[0..field_name.len];
- @memcpy(c_name, field_name);
- c_name_buf[0] = '$';
- break :name c_name;
- } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or
- ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or
- (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: {
- // "$" prefix for these registers
- c_name_buf[0] = '$';
- @memcpy((&c_name_buf)[1..][0..field_name.len], field_name);
- break :name (&c_name_buf)[0 .. 1 + field_name.len];
- } else if (target.cpu.arch.isSPARC() and
- (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: {
- // C compilers just use `icc` to encompass all of these.
- break :name "icc";
- } else field_name;
-
- try w.print(" {f}", .{fmtStringLiteral(name, null)});
- (try w.writableArray(1))[0] = ',';
- },
- .bool_false => continue,
- else => unreachable,
- },
- .repeated_elem => |elem| switch (elem) {
- .bool_true => @panic("TODO"),
- .bool_false => {},
- else => unreachable,
- },
- .bytes => @panic("TODO"),
+ const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
+ const clobbers_ty = clobbers_val.typeOf(zcu);
+ var clobbers_bigint_buf: Value.BigIntSpace = undefined;
+ const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
+ for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
+ assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
+ }
+ const field_name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
+ assert(field_name.len != 0);
+
+ const target = &f.object.dg.mod.resolved_target.result;
+ var c_name_buf: [16]u8 = undefined;
+ const name =
+ if ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'r') name: {
+ // Convert "rN" to "$N"
+ const c_name = (&c_name_buf)[0..field_name.len];
+ @memcpy(c_name, field_name);
+ c_name_buf[0] = '$';
+ break :name c_name;
+ } else if ((target.cpu.arch.isMIPS() and (mem.startsWith(u8, field_name, "fcc") or field_name[0] == 'w')) or
+ ((target.cpu.arch.isMIPS() or target.cpu.arch == .alpha) and field_name[0] == 'f') or
+ (target.cpu.arch == .kvx and !mem.eql(u8, field_name, "memory"))) name: {
+ // "$" prefix for these registers
+ c_name_buf[0] = '$';
+ @memcpy((&c_name_buf)[1..][0..field_name.len], field_name);
+ break :name (&c_name_buf)[0 .. 1 + field_name.len];
+ } else if (target.cpu.arch.isSPARC() and
+ (mem.eql(u8, field_name, "ccr") or mem.eql(u8, field_name, "icc") or mem.eql(u8, field_name, "xcc"))) name: {
+ // C compilers just use `icc` to encompass all of these.
+ break :name "icc";
+ } else field_name;
+
+ try w.print(" {f}", .{fmtStringLiteral(name, null)});
+ (try w.writableArray(1))[0] = ',';
}
w.undo(1); // erase the last comma
try w.writeAll(");");
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
@@ -3680,7 +3680,7 @@ pub const Object = struct {
const limbs = try allocator.alloc(std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(bits));
defer allocator.free(limbs);
- val.writeToPackedMemory(ty, pt, buffer, 0) catch unreachable;
+ val.writeToPackedMemory(pt, buffer, 0) catch unreachable;
var big: std.math.big.int.Mutable = .init(limbs, 0);
big.readTwosComplement(buffer, bits, target.cpu.arch.endian(), .unsigned);
@@ -7467,29 +7467,20 @@ pub const FuncGen = struct {
}
const ip = &zcu.intern_pool;
- const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
- const struct_type: Type = .fromInterned(aggregate.ty);
- if (total_i != 0) try llvm_constraints.append(gpa, ',');
- switch (aggregate.storage) {
- .elems => |elems| for (elems, 0..) |elem, i| {
- switch (elem) {
- .bool_true => {
- const name = struct_type.structFieldName(i, zcu).toSlice(ip).?;
- total_i += try appendConstraints(gpa, &llvm_constraints, name, target);
- },
- .bool_false => continue,
- else => unreachable,
- }
- },
- .repeated_elem => |elem| switch (elem) {
- .bool_true => for (0..struct_type.structFieldCount(zcu)) |i| {
- const name = struct_type.structFieldName(i, zcu).toSlice(ip).?;
- total_i += try appendConstraints(gpa, &llvm_constraints, name, target);
- },
- .bool_false => {},
- else => unreachable,
- },
- .bytes => @panic("TODO"),
+ const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
+ const clobbers_ty = clobbers_val.typeOf(zcu);
+ var clobbers_bigint_buf: Value.BigIntSpace = undefined;
+ const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
+ for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
+ assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
+ }
+ const name = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
+ total_i += try appendConstraints(gpa, &llvm_constraints, name, target);
}
// We have finished scanning through all inputs/outputs, so the number of
diff --git a/src/codegen/riscv64/CodeGen.zig b/src/codegen/riscv64/CodeGen.zig
@@ -6149,31 +6149,26 @@ fn airAsm(func: *Func, inst: Air.Inst.Index) !void {
const zcu = func.pt.zcu;
const ip = &zcu.intern_pool;
- const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
- const struct_type: Type = .fromInterned(aggregate.ty);
- switch (aggregate.storage) {
- .elems => |elems| for (elems, 0..) |elem, i| {
- switch (elem) {
- .bool_true => {
- const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;
- assert(clobber.len != 0);
- if (std.mem.eql(u8, clobber, "memory")) {
- // nothing really to do
- } else {
- try func.register_manager.getReg(parseRegName(clobber) orelse
- return func.fail("invalid clobber: '{s}'", .{clobber}), null);
- }
- },
- .bool_false => continue,
- else => unreachable,
- }
- },
- .repeated_elem => |elem| switch (elem) {
- .bool_true => @panic("TODO"),
- .bool_false => {},
- else => unreachable,
- },
- .bytes => @panic("TODO"),
+ const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
+ const clobbers_ty = clobbers_val.typeOf(zcu);
+ var clobbers_bigint_buf: Value.BigIntSpace = undefined;
+ const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
+ for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
+ assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
+ }
+ const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
+ assert(clobber.len != 0);
+ if (std.mem.eql(u8, clobber, "memory")) {
+ // nothing really to do
+ } else {
+ try func.register_manager.getReg(parseRegName(clobber) orelse
+ return func.fail("invalid clobber: '{s}'", .{clobber}), null);
+ }
}
const Label = struct {
diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig
@@ -969,7 +969,7 @@ fn constant(cg: *CodeGen, ty: Type, val: Value, repr: Repr) Error!Id {
const bytes = std.mem.alignForward(u16, cg.module.backingIntBits(bits).@"0", 8) / 8;
var limbs: [8]u8 = undefined;
@memset(&limbs, 0);
- val.writeToPackedMemory(ty, pt, limbs[0..bytes], 0) catch unreachable;
+ val.writeToPackedMemory(pt, limbs[0..bytes], 0) catch unreachable;
const backing_ty: Type = .fromInterned(struct_type.backingIntTypeUnordered(ip));
return try cg.constInt(backing_ty, @as(u64, @bitCast(limbs)));
}
diff --git a/src/codegen/wasm/CodeGen.zig b/src/codegen/wasm/CodeGen.zig
@@ -3253,7 +3253,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
// are by-ref types.
assert(struct_type.layout == .@"packed");
var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer
- val.writeToPackedMemory(ty, pt, &buf, 0) catch unreachable;
+ val.writeToPackedMemory(pt, &buf, 0) catch unreachable;
const backing_int_ty = Type.fromInterned(struct_type.backingIntTypeUnordered(ip));
const int_val = try pt.intValue(
backing_int_ty,
@@ -3267,7 +3267,7 @@ fn lowerConstant(cg: *CodeGen, val: Value, ty: Type) InnerError!WValue {
const int_type = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu)));
var buf: [8]u8 = .{0} ** 8; // zero the buffer so we do not read 0xaa as integer
- val.writeToPackedMemory(ty, pt, &buf, 0) catch unreachable;
+ val.writeToPackedMemory(pt, &buf, 0) catch unreachable;
const int_val = try pt.intValue(
int_type,
mem.readInt(u64, &buf, .little),
diff --git a/src/codegen/x86_64/CodeGen.zig b/src/codegen/x86_64/CodeGen.zig
@@ -177294,41 +177294,38 @@ fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void {
}
const ip = &zcu.intern_pool;
- const aggregate = ip.indexToKey(unwrapped_asm.clobbers).aggregate;
- const struct_type: Type = .fromInterned(aggregate.ty);
- switch (aggregate.storage) {
- .elems => |elems| for (elems, 0..) |elem, i| switch (elem) {
- .bool_true => {
- const clobber = struct_type.structFieldName(i, zcu).toSlice(ip).?;
- assert(clobber.len != 0);
-
- if (std.mem.eql(u8, clobber, "memory") or
- std.mem.eql(u8, clobber, "fpsr") or
- std.mem.eql(u8, clobber, "fpcr") or
- std.mem.eql(u8, clobber, "mxcsr") or
- std.mem.eql(u8, clobber, "dirflag"))
- {
- // ok, sure
- } else if (std.mem.eql(u8, clobber, "cc") or
- std.mem.eql(u8, clobber, "flags") or
- std.mem.eql(u8, clobber, "eflags") or
- std.mem.eql(u8, clobber, "rflags"))
- {
- try self.spillEflagsIfOccupied();
- } else {
- try self.register_manager.getReg(parseRegName(clobber) orelse
- return self.fail("invalid clobber: '{s}'", .{clobber}), null);
- }
- },
- .bool_false => continue,
- else => unreachable,
- },
- .repeated_elem => |elem| switch (elem) {
- .bool_true => @panic("TODO"),
- .bool_false => {},
- else => unreachable,
- },
- .bytes => @panic("TODO"),
+ const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers);
+ const clobbers_ty = clobbers_val.typeOf(zcu);
+ var clobbers_bigint_buf: Value.BigIntSpace = undefined;
+ const clobbers_bigint = clobbers_val.toBigInt(&clobbers_bigint_buf, zcu);
+ for (0..clobbers_ty.structFieldCount(zcu)) |field_index| {
+ assert(clobbers_ty.fieldType(field_index, zcu).toIntern() == .bool_type);
+ const limb_bits = @bitSizeOf(std.math.big.Limb);
+ if (field_index / limb_bits >= clobbers_bigint.limbs.len) continue; // field is false
+ switch (@as(u1, @truncate(clobbers_bigint.limbs[field_index / limb_bits] >> @intCast(field_index % limb_bits)))) {
+ 0 => continue, // field is false
+ 1 => {}, // field is true
+ }
+ const clobber = clobbers_ty.structFieldName(field_index, zcu).toSlice(ip).?;
+ assert(clobber.len != 0);
+
+ if (std.mem.eql(u8, clobber, "memory") or
+ std.mem.eql(u8, clobber, "fpsr") or
+ std.mem.eql(u8, clobber, "fpcr") or
+ std.mem.eql(u8, clobber, "mxcsr") or
+ std.mem.eql(u8, clobber, "dirflag"))
+ {
+ // ok, sure
+ } else if (std.mem.eql(u8, clobber, "cc") or
+ std.mem.eql(u8, clobber, "flags") or
+ std.mem.eql(u8, clobber, "eflags") or
+ std.mem.eql(u8, clobber, "rflags"))
+ {
+ try self.spillEflagsIfOccupied();
+ } else {
+ try self.register_manager.getReg(parseRegName(clobber) orelse
+ return self.fail("invalid clobber: '{s}'", .{clobber}), null);
+ }
}
const Label = struct {
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
@@ -3378,6 +3378,7 @@ fn updateComptimeNavInner(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPoo
.opt,
.aggregate,
.un,
+ .bitpack,
=> .decl_const,
.variable => .decl_var,
.@"extern" => unreachable,
@@ -4014,6 +4015,7 @@ fn updateLazyType(
.opt,
.aggregate,
.un,
+ .bitpack,
// memoization, not types
.memoized_call,
=> unreachable,
@@ -4092,6 +4094,15 @@ fn updateLazyValue(
}, .fromInterned(int.ty), Value.fromInterned(value_index).toBigInt(&big_int_space, zcu));
try wip_nav.refType(.fromInterned(int.ty));
},
+ .bitpack => |bitpack| {
+ const backing_int_val: Value = .fromInterned(bitpack.backing_int_val);
+ try wip_nav.bigIntConstValue(.{
+ .sdata = .sdata_comptime_value,
+ .udata = .udata_comptime_value,
+ .block = .block_comptime_value,
+ }, backing_int_val.typeOf(zcu), backing_int_val.toBigInt(&big_int_space, zcu));
+ try wip_nav.refType(.fromInterned(bitpack.ty));
+ },
.err => |err| {
try wip_nav.abbrevCode(.udata_comptime_value);
try wip_nav.refType(.fromInterned(err.ty));
diff --git a/src/mutable_value.zig b/src/mutable_value.zig
@@ -97,8 +97,8 @@ pub const MutableValue = union(enum) {
/// * Non-error error unions use `eu_payload`
/// * Non-null optionals use `eu_payload
/// * Slices use `slice`
- /// * Unions use `un`
- /// * Aggregates use `repeated` or `bytes` or `aggregate`
+ /// * Unions use `un` (excluding packed unions)
+ /// * Aggregates use `repeated` or `bytes` or `aggregate` (excluding packed structs)
/// If `!allow_bytes`, the `bytes` representation will not be used.
/// If `!allow_repeated`, the `repeated` representation will not be used.
pub fn unintern(
@@ -209,6 +209,7 @@ pub const MutableValue = union(enum) {
.undef => |ty_ip| switch (Type.fromInterned(ty_ip).zigTypeTag(zcu)) {
.@"struct", .array, .vector => |type_tag| {
const ty = Type.fromInterned(ty_ip);
+ if (type_tag == .@"struct" and ty.containerLayout(zcu) == .@"packed") return;
const opt_sent = ty.sentinel(zcu);
if (type_tag == .@"struct" or opt_sent != null or !allow_repeated) {
const len_no_sent = ip.aggregateTypeLen(ty_ip);
@@ -241,15 +242,18 @@ pub const MutableValue = union(enum) {
} };
}
},
- .@"union" => {
- const payload = try arena.create(MutableValue);
- const backing_ty = try Type.fromInterned(ty_ip).unionBackingType(pt);
- payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) };
- mv.* = .{ .un = .{
- .ty = ty_ip,
- .tag = .none,
- .payload = payload,
- } };
+ .@"union" => switch (Type.fromInterned(ty_ip).containerLayout(zcu)) {
+ .auto, .@"packed" => {},
+ .@"extern" => {
+ const payload = try arena.create(MutableValue);
+ const backing_ty = try Type.fromInterned(ty_ip).externUnionBackingType(pt);
+ payload.* = .{ .interned = try pt.intern(.{ .undef = backing_ty.toIntern() }) };
+ mv.* = .{ .un = .{
+ .ty = ty_ip,
+ .tag = .none,
+ .payload = payload,
+ } };
+ },
},
.pointer => {
const ptr_ty = ip.indexToKey(ty_ip).ptr_type;
diff --git a/src/print_value.zig b/src/print_value.zig
@@ -164,7 +164,7 @@ pub fn print(
return;
}
if (un.tag == .none) {
- const backing_ty = try val.typeOf(zcu).unionBackingType(pt);
+ const backing_ty = try val.typeOf(zcu).externUnionBackingType(pt);
try writer.print("@bitCast(@as({f}, ", .{backing_ty.fmt(pt)});
try print(Value.fromInterned(un.val), writer, level - 1, pt, opt_sema);
try writer.writeAll("))");
@@ -176,6 +176,32 @@ pub fn print(
try writer.writeAll(" }");
}
},
+ .bitpack => |bitpack| {
+ const ty: Type = .fromInterned(bitpack.ty);
+ switch (ty.zigTypeTag(zcu)) {
+ .@"struct" => {
+ if (ty.structFieldCount(zcu) == 0) {
+ return writer.writeAll(".{}");
+ }
+ try writer.writeAll(".{ ");
+ const max_len = @min(ty.structFieldCount(zcu), max_aggregate_items);
+ for (0..max_len) |i| {
+ if (i != 0) try writer.writeAll(", ");
+ const field_name = ty.structFieldName(@intCast(i), zcu).unwrap().?;
+ try writer.print(".{f} = ", .{field_name.fmt(ip)});
+ try print(try val.fieldValue(pt, i), writer, level - 1, pt, opt_sema);
+ }
+ try writer.writeAll(" }");
+ return;
+ },
+ .@"union" => {
+ try writer.print("@bitCast(@as({f}, ", .{ty.bitpackBackingInt(zcu).fmt(pt)});
+ try print(.fromInterned(bitpack.backing_int_val), writer, level - 1, pt, opt_sema);
+ try writer.writeAll("))");
+ },
+ else => unreachable,
+ }
+ },
.memoized_call => unreachable,
}
}