From ffcf6549076401abe8d4af0754fbfe35f28df204 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Wed, 28 Jan 2026 13:10:07 +0000 Subject: [PATCH] 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. --- src/Air/print.zig | 44 +++----- src/InternPool.zig | 63 +++++++++-- src/Sema.zig | 50 +++++++-- src/Sema/bitcast.zig | 171 +++++++++++++++--------------- src/Sema/type_resolution.zig | 1 + src/Type.zig | 41 ++++++-- src/Value.zig | 160 +++++++++++++--------------- src/Zcu/PerThread.zig | 9 ++ src/codegen.zig | 42 ++------ src/codegen/aarch64/Select.zig | 46 ++++---- src/codegen/c.zig | 180 ++++++++++++-------------------- src/codegen/llvm.zig | 39 +++---- src/codegen/riscv64/CodeGen.zig | 45 ++++---- src/codegen/spirv/CodeGen.zig | 2 +- src/codegen/wasm/CodeGen.zig | 4 +- src/codegen/x86_64/CodeGen.zig | 65 ++++++------ src/link/Dwarf.zig | 11 ++ src/mutable_value.zig | 26 +++-- src/print_value.zig | 28 ++++- 19 files changed, 528 insertions(+), 499 deletions(-) diff --git a/src/Air/print.zig b/src/Air/print.zig index 0c126fab22..f6c0f5a03b 100644 --- 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 => "", - .bool_false => "", - 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 index 230ef78d63..7028d69009 100644 --- 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 index a506a7e6bb..60f0497437 100644 --- 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 index b496db1ce0..f06528b624 100644 --- 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. + .@"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 prev_unpacked = pack.unpacked; + const prev_bit_offset = pack.bit_offset; - const backing_ty = try ty.unionBackingType(pt); + const backing_ty = try ty.externUnionBackingType(pt); - backing: { - const backing_val = pack.get(backing_ty) catch |err| switch (err) { - error.ReinterpretDeclRef => { + 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(), + })); } + + 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; + } + 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(), + })); + } + + // 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(), })); - } - - 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; - } - 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(), - })); - } - - // 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 index 970cf4f011..1096c9cbe7 100644 --- 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 index 5b5839cf6f..9cca3d1043 100644 --- 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 index d513e5d07d..774a6758ce 100644 --- 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 index 00c911caf4..186d6147d1 100644 --- 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 index e9acab66e4..9edb90fb51 100644 --- 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 index 7449464905..f9ff787477 100644 --- 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 index 5e9f21e1aa..69c4e91d99 100644 --- 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 (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); - }, + if (!location.isInitializer()) { + try w.writeByte('('); + try dg.renderCType(w, ctype); + try w.writeByte(')'); } + + 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 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; + 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"), + 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 index ad115504ba..695bb82133 100644 --- 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 index 72174554ad..1f5b6224d7 100644 --- 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 index 5217002f22..709de66a41 100644 --- 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 index 955e9b51d3..cdcaac9302 100644 --- 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 index a9898398d7..2a7558505b 100644 --- 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); + 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); - } - }, - .bool_false => continue, - else => unreachable, - }, - .repeated_elem => |elem| switch (elem) { - .bool_true => @panic("TODO"), - .bool_false => {}, - else => unreachable, - }, - .bytes => @panic("TODO"), + 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 index f77518d465..027d5391a5 100644 --- 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 index 97d6f22b3b..8ddd434837 100644 --- 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 index 5b29bb04d6..d472472481 100644 --- 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, } }