Skip to content

Commit d161e27

Browse files
committed
Codegen: fix nested packed struct byte-aligned access
When acessing a packed struct member via a byte aligned ptr (from the optimisation in Sema.structFieldPtrByIndex()) the codegen must apply the parent ptr packed_offset. resolves #16609
1 parent 46abf20 commit d161e27

File tree

3 files changed

+52
-6
lines changed

3 files changed

+52
-6
lines changed

src/codegen/c.zig

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ pub const DeclGen = struct {
661661
try dg.renderCType(writer, ptr_cty);
662662
try writer.writeByte(')');
663663
}
664-
switch (fieldLocation(base_ty, ptr_ty, @as(u32, @intCast(field.index)), mod)) {
664+
switch (fieldLocation(ptr_base_ty, ptr_ty, @as(u32, @intCast(field.index)), mod)) {
665665
.begin => try dg.renderParentPtr(writer, field.base, location),
666666
.field => |name| {
667667
try writer.writeAll("&(");
@@ -5187,7 +5187,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
51875187
}
51885188

51895189
fn fieldLocation(
5190-
container_ty: Type,
5190+
container_ptr_ty: Type,
51915191
field_ptr_ty: Type,
51925192
field_index: u32,
51935193
mod: *Module,
@@ -5198,6 +5198,7 @@ fn fieldLocation(
51985198
end: void,
51995199
} {
52005200
const ip = &mod.intern_pool;
5201+
const container_ty = container_ptr_ty.childType(mod);
52015202
return switch (container_ty.zigTypeTag(mod)) {
52025203
.Struct => switch (container_ty.containerLayout(mod)) {
52035204
.Auto, .Extern => for (field_index..container_ty.structFieldCount(mod)) |next_field_index| {
@@ -5211,7 +5212,7 @@ fn fieldLocation(
52115212
.{ .identifier = ip.stringToSlice(container_ty.structFieldName(next_field_index, mod)) } };
52125213
} else if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin,
52135214
.Packed => if (field_ptr_ty.ptrInfo(mod).packed_offset.host_size == 0)
5214-
.{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, mod) }
5215+
.{ .byte_offset = container_ty.packedStructFieldByteOffset(field_index, mod) + @divExact(container_ptr_ty.ptrInfo(mod).packed_offset.bit_offset, 8) }
52155216
else
52165217
.begin,
52175218
},
@@ -5282,7 +5283,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
52825283
try f.renderType(writer, container_ptr_ty);
52835284
try writer.writeByte(')');
52845285

5285-
switch (fieldLocation(container_ty, field_ptr_ty, extra.field_index, mod)) {
5286+
switch (fieldLocation(container_ptr_ty, field_ptr_ty, extra.field_index, mod)) {
52865287
.begin => try f.writeCValue(writer, field_ptr_val, .Initializer),
52875288
.field => |field| {
52885289
const u8_ptr_ty = try mod.adjustPtrTypeChild(field_ptr_ty, Type.u8);
@@ -5339,7 +5340,7 @@ fn fieldPtr(
53395340
try f.renderType(writer, field_ptr_ty);
53405341
try writer.writeByte(')');
53415342

5342-
switch (fieldLocation(container_ty, field_ptr_ty, field_index, mod)) {
5343+
switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, mod)) {
53435344
.begin => try f.writeCValue(writer, container_ptr_val, .Initializer),
53445345
.field => |field| {
53455346
try writer.writeByte('&');

src/codegen/llvm.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10611,6 +10611,7 @@ pub const FuncGen = struct {
1061110611
.Packed => {
1061210612
const result_ty = self.typeOfIndex(inst);
1061310613
const result_ty_info = result_ty.ptrInfo(mod);
10614+
const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod);
1061410615

1061510616
if (result_ty_info.packed_offset.host_size != 0) {
1061610617
// From LLVM's perspective, a pointer to a packed struct and a pointer
@@ -10622,7 +10623,7 @@ pub const FuncGen = struct {
1062210623

1062310624
// We have a pointer to a packed struct field that happens to be byte-aligned.
1062410625
// Offset our operand pointer by the correct number of bytes.
10625-
const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, mod);
10626+
const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, mod) + @divExact(struct_ptr_ty_info.packed_offset.bit_offset, 8);
1062610627
if (byte_offset == 0) return struct_ptr;
1062710628
const usize_ty = try o.lowerType(Type.usize);
1062810629
const llvm_index = try o.builder.intValue(usize_ty, byte_offset);

test/behavior/packed-struct.zig

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,50 @@ test "nested packed struct field access test" {
528528
try std.testing.expect(arg.g.i == 8);
529529
}
530530

531+
test "nested packed struct at non-zero offset" {
532+
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
533+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
534+
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
535+
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
536+
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
537+
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
538+
539+
const S = struct {
540+
const Pair = packed struct(u40) {
541+
a: u32 = 0,
542+
b: u8 = 0,
543+
};
544+
const A = packed struct {
545+
p1: Pair,
546+
p2: Pair,
547+
};
548+
549+
fn doTheTest() !void {
550+
var v: u8 = 123;
551+
var a: A = .{
552+
.p1 = .{ .a = v + 1, .b = v },
553+
.p2 = .{ .a = v + 1, .b = v },
554+
};
555+
556+
try expect(a.p1.a == v + 1);
557+
try expect(a.p2.a == v + 1);
558+
try expect(a.p1.b == v);
559+
try expect(a.p2.b == v);
560+
try expect(a.p1.a == a.p2.a and a.p1.b == a.p2.b);
561+
562+
a.p2.a -= a.p1.a;
563+
a.p2.b -= a.p1.b;
564+
try expect(a.p2.a == 0);
565+
try expect(a.p2.b == 0);
566+
try expect(a.p1.a == v + 1);
567+
try expect(a.p1.b == v);
568+
}
569+
};
570+
571+
try S.doTheTest();
572+
try comptime S.doTheTest();
573+
}
574+
531575
test "runtime init of unnamed packed struct type" {
532576
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
533577
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;

0 commit comments

Comments
 (0)