authorgravatar for ryan.mehri1@gmail.comRyan Mehri <ryan.mehri1@gmail.com> 2026-08-04 11:43:46-04:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-08-07 15:29:04+02:00
logb9852d23089a524dafaa2487f207457b2a183af1
tree6f39d844db264505e301b12eb6fabc405fee702f
parent17e07ffc6381a1650b6bac5948b9f22d24411982

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.

2 files changed, 24 insertions(+), 1 deletions(-)

src/Air/Legalize.zig+8-1
...@@ -2906,12 +2906,18 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro...@@ -2906,12 +2906,18 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro
2906 const orig_ty_pl = l.air_instructions.items(.data)[@backingInt(orig_inst)].ty_pl;2906 const orig_ty_pl = l.air_instructions.items(.data)[@backingInt(orig_inst)].ty_pl;
2907 const agg_ty = orig_ty_pl.ty.toType();2907 const agg_ty = orig_ty_pl.ty.toType();
2908 const agg_field_count = agg_ty.structFieldCount(zcu);2908 const agg_field_count = agg_ty.structFieldCount(zcu);
2909 var opv_field_count: u32 = 0;
2910 for (0..agg_field_count) |field_idx| {
2911 const field_ty = agg_ty.fieldType(field_idx, zcu);
2912 const field_bits: u16 = @intCast(field_ty.bitSize(zcu));
2913 if (field_bits == 0) opv_field_count += 1;
2914 }
29092915
2910 var bfa_buf: [4 * 32 + 2]Air.Inst.Index = undefined;2916 var bfa_buf: [4 * 32 + 2]Air.Inst.Index = undefined;
2911 var bfa_state: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), gpa);2917 var bfa_state: std.heap.BufferFirstAllocator = .init(@ptrCast(&bfa_buf), gpa);
2912 const bfa = bfa_state.allocator();2918 const bfa = bfa_state.allocator();
29132919
2914 const inst_buf = try bfa.alloc(Air.Inst.Index, 4 * agg_field_count + 2);2920 const inst_buf = try bfa.alloc(Air.Inst.Index, 4 * (agg_field_count - opv_field_count) + 2);
2915 defer bfa.free(inst_buf);2921 defer bfa.free(inst_buf);
29162922
2917 var main_block: Block = .init(inst_buf);2923 var main_block: Block = .init(inst_buf);
...@@ -2927,6 +2933,7 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro...@@ -2927,6 +2933,7 @@ fn packedAggregateInitBlockPayload(l: *Legalize, orig_inst: Air.Inst.Index) Erro
2927 field_idx -= 1;2933 field_idx -= 1;
2928 const field_ty = agg_ty.fieldType(field_idx, zcu);2934 const field_ty = agg_ty.fieldType(field_idx, zcu);
2929 const field_bits: u16 = @intCast(field_ty.bitSize(zcu));2935 const field_bits: u16 = @intCast(field_ty.bitSize(zcu));
2936 if (field_bits == 0) continue;
2930 assert(field_bits < num_bits);2937 assert(field_bits < num_bits);
2931 const field_uint_ty = try pt.intType(.unsigned, field_bits);2938 const field_uint_ty = try pt.intType(.unsigned, field_bits);
2932 const field_bit_size_ref: Air.Inst.Ref = .fromValue(try pt.intValue(shift_ty, field_bits));2939 const field_bit_size_ref: Air.Inst.Ref = .fromValue(try pt.intValue(shift_ty, field_bits));
test/behavior/bitcast.zig+16
...@@ -432,6 +432,22 @@ test "@bitCast of packed struct with void field to integer" {...@@ -432,6 +432,22 @@ test "@bitCast of packed struct with void field to integer" {
432 try comptime S.doTheTest(123);432 try comptime S.doTheTest(123);
433}433}
434434
435test "@bitCast of packed struct with void field and multiple integers" {
436 const S = packed struct {
437 x: u8,
438 v: void,
439 y: u8,
440
441 fn doTheTest(x: u8, y: u8) !void {
442 const foo = @as(@This(), .{ .x = x, .v = {}, .y = y });
443 const as_int: u16 = @bitCast(foo);
444 try expect(as_int == @as(u16, y) << 8 | x);
445 }
446 };
447 try S.doTheTest(123, 45);
448 try comptime S.doTheTest(123, 45);
449}
450
435test "@bitCast vector to array with different element size" {451test "@bitCast vector to array with different element size" {
436 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;452 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
437453