From b9852d23089a524dafaa2487f207457b2a183af1 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Tue, 4 Aug 2026 11:43:46 -0400 Subject: [PATCH] Legalize: fix packed struct with OPV and multiple other fields In legalization for packed struct init with an OPV field, we first try to see if any field accounts for the entire bit size of the struct and otherwise fall back to a sequence of bitcasts and shifts on each field (added in fc1c83a363d). However, OPV fields are not accounted for in the fallback case, which means that codegen eventually panics when seeing the bit size of 0. This change makes it so that we ignore all OPV fields in `packedAggregateInitBlockPayload` since they have no runtime bits. --- src/Air/Legalize.zig | 9 ++++++++- test/behavior/bitcast.zig | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Air/Legalize.zig b/src/Air/Legalize.zig index 535d9394654a130ef856342f3390473d6664cf93..64fd462b7bd8e041a65c3ec89616db830776349b 100644 --- a/src/Air/Legalize.zig +++ b/src/Air/Legalize.zig @@ -2906,12 +2906,18 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro const orig_ty_pl = l.air_instructions.items(.data)[@backingInt(orig_inst)].ty_pl; const agg_ty = orig_ty_pl.ty.toType(); const agg_field_count = agg_ty.structFieldCount(zcu); + var opv_field_count: u32 = 0; + for (0..agg_field_count) |field_idx| { + const field_ty = agg_ty.fieldType(field_idx, zcu); + const field_bits: u16 = @intCast(field_ty.bitSize(zcu)); + if (field_bits == 0) opv_field_count += 1; + } var bfa_buf: [4 * 32 + 2]Air.Inst.Index = undefined; var bfa_state: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), gpa); const bfa = bfa_state.allocator(); - const inst_buf = try bfa.alloc(Air.Inst.Index, 4 * agg_field_count + 2); + const inst_buf = try bfa.alloc(Air.Inst.Index, 4 * (agg_field_count - opv_field_count) + 2); defer bfa.free(inst_buf); var main_block: Block = .init(inst_buf); @@ -2927,6 +2933,7 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro field_idx -= 1; const field_ty = agg_ty.fieldType(field_idx, zcu); const field_bits: u16 = @intCast(field_ty.bitSize(zcu)); + if (field_bits == 0) continue; assert(field_bits < num_bits); const field_uint_ty = try pt.intType(.unsigned, field_bits); const field_bit_size_ref: Air.Inst.Ref = .fromValue(try pt.intValue(shift_ty, field_bits)); diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index 785daa0d3194b00d43c00760a11199075d9377dd..d790d68416add0ba62603eaabaa691d1bdfe9c32 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -432,6 +432,22 @@ test "@bitCast of packed struct with void field to integer" { try comptime S.doTheTest(123); } +test "@bitCast of packed struct with void field and multiple integers" { + const S = packed struct { + x: u8, + v: void, + y: u8, + + fn doTheTest(x: u8, y: u8) !void { + const foo = @as(@This(), .{ .x = x, .v = {}, .y = y }); + const as_int: u16 = @bitCast(foo); + try expect(as_int == @as(u16, y) << 8 | x); + } + }; + try S.doTheTest(123, 45); + try comptime S.doTheTest(123, 45); +} + test "@bitCast vector to array with different element size" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; -- 2.54.0