| author | |
| committer | |
| log | 8c367ef99aae05ddba800a6c01cb07677e2f512c |
| tree | a9fecf3efe8e4ed6285f4de7d11ad02edb86e3d1 |
| parent | 46abf2045476a32b6f4dd939679c0fbc7a639133 |
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 in addition to the field offset itself.
resolves https://github.com/ziglang/zig/issues/166095 files changed, 115 insertions(+), 12 deletions(-)
src/arch/wasm/CodeGen.zig+10-5| ... | ... | @@ -3675,8 +3675,9 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3675 | 3675 | const extra = func.air.extraData(Air.StructField, ty_pl.payload); |
| 3676 | 3676 | |
| 3677 | 3677 | const struct_ptr = try func.resolveInst(extra.data.struct_operand); |
| 3678 | const struct_ty = func.typeOf(extra.data.struct_operand).childType(mod); | |
| 3679 | const result = try func.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ty, extra.data.field_index); | |
| 3678 | const struct_ptr_ty = func.typeOf(extra.data.struct_operand); | |
| 3679 | const struct_ty = struct_ptr_ty.childType(mod); | |
| 3680 | const result = try func.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ptr_ty, struct_ty, extra.data.field_index); | |
| 3680 | 3681 | func.finishAir(inst, result, &.{extra.data.struct_operand}); |
| 3681 | 3682 | } |
| 3682 | 3683 | |
| ... | ... | @@ -3684,9 +3685,10 @@ fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) Inne |
| 3684 | 3685 | const mod = func.bin_file.base.options.module.?; |
| 3685 | 3686 | const ty_op = func.air.instructions.items(.data)[inst].ty_op; |
| 3686 | 3687 | const struct_ptr = try func.resolveInst(ty_op.operand); |
| 3687 | const struct_ty = func.typeOf(ty_op.operand).childType(mod); | |
| 3688 | const struct_ptr_ty = func.typeOf(ty_op.operand); | |
| 3689 | const struct_ty = struct_ptr_ty.childType(mod); | |
| 3688 | 3690 | |
| 3689 | const result = try func.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ty, index); | |
| 3691 | const result = try func.structFieldPtr(inst, ty_op.operand, struct_ptr, struct_ptr_ty, struct_ty, index); | |
| 3690 | 3692 | func.finishAir(inst, result, &.{ty_op.operand}); |
| 3691 | 3693 | } |
| 3692 | 3694 | |
| ... | ... | @@ -3695,18 +3697,21 @@ fn structFieldPtr( |
| 3695 | 3697 | inst: Air.Inst.Index, |
| 3696 | 3698 | ref: Air.Inst.Ref, |
| 3697 | 3699 | struct_ptr: WValue, |
| 3700 | struct_ptr_ty: Type, | |
| 3698 | 3701 | struct_ty: Type, |
| 3699 | 3702 | index: u32, |
| 3700 | 3703 | ) InnerError!WValue { |
| 3701 | 3704 | const mod = func.bin_file.base.options.module.?; |
| 3702 | 3705 | const result_ty = func.typeOfIndex(inst); |
| 3706 | const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); | |
| 3707 | ||
| 3703 | 3708 | const offset = switch (struct_ty.containerLayout(mod)) { |
| 3704 | 3709 | .Packed => switch (struct_ty.zigTypeTag(mod)) { |
| 3705 | 3710 | .Struct => offset: { |
| 3706 | 3711 | if (result_ty.ptrInfo(mod).packed_offset.host_size != 0) { |
| 3707 | 3712 | break :offset @as(u32, 0); |
| 3708 | 3713 | } |
| 3709 | break :offset struct_ty.packedStructFieldByteOffset(index, mod); | |
| 3714 | break :offset struct_ty.packedStructFieldByteOffset(index, mod) + @divExact(struct_ptr_ty_info.packed_offset.bit_offset, 8); | |
| 3710 | 3715 | }, |
| 3711 | 3716 | .Union => 0, |
| 3712 | 3717 | else => unreachable, |
src/arch/x86_64/CodeGen.zig+3-1| ... | ... | @@ -5556,12 +5556,14 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32 |
| 5556 | 5556 | const mod = self.bin_file.options.module.?; |
| 5557 | 5557 | const ptr_field_ty = self.typeOfIndex(inst); |
| 5558 | 5558 | const ptr_container_ty = self.typeOf(operand); |
| 5559 | const ptr_container_ty_info = ptr_container_ty.ptrInfo(mod); | |
| 5559 | 5560 | const container_ty = ptr_container_ty.childType(mod); |
| 5561 | ||
| 5560 | 5562 | const field_offset: i32 = @intCast(switch (container_ty.containerLayout(mod)) { |
| 5561 | 5563 | .Auto, .Extern => container_ty.structFieldOffset(index, mod), |
| 5562 | 5564 | .Packed => if (container_ty.zigTypeTag(mod) == .Struct and |
| 5563 | 5565 | ptr_field_ty.ptrInfo(mod).packed_offset.host_size == 0) |
| 5564 | container_ty.packedStructFieldByteOffset(index, mod) | |
| 5566 | container_ty.packedStructFieldByteOffset(index, mod) + @divExact(ptr_container_ty_info.packed_offset.bit_offset, 8) | |
| 5565 | 5567 | else |
| 5566 | 5568 | 0, |
| 5567 | 5569 | }); |
src/codegen/c.zig+6-5| ... | ... | @@ -661,7 +661,7 @@ pub const DeclGen = struct { |
| 661 | 661 | try dg.renderCType(writer, ptr_cty); |
| 662 | 662 | try writer.writeByte(')'); |
| 663 | 663 | } |
| 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)) { | |
| 665 | 665 | .begin => try dg.renderParentPtr(writer, field.base, location), |
| 666 | 666 | .field => |name| { |
| 667 | 667 | try writer.writeAll("&("); |
| ... | ... | @@ -5187,7 +5187,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5187 | 5187 | } |
| 5188 | 5188 | |
| 5189 | 5189 | fn fieldLocation( |
| 5190 | container_ty: Type, | |
| 5190 | container_ptr_ty: Type, | |
| 5191 | 5191 | field_ptr_ty: Type, |
| 5192 | 5192 | field_index: u32, |
| 5193 | 5193 | mod: *Module, |
| ... | ... | @@ -5198,6 +5198,7 @@ fn fieldLocation( |
| 5198 | 5198 | end: void, |
| 5199 | 5199 | } { |
| 5200 | 5200 | const ip = &mod.intern_pool; |
| 5201 | const container_ty = container_ptr_ty.childType(mod); | |
| 5201 | 5202 | return switch (container_ty.zigTypeTag(mod)) { |
| 5202 | 5203 | .Struct => switch (container_ty.containerLayout(mod)) { |
| 5203 | 5204 | .Auto, .Extern => for (field_index..container_ty.structFieldCount(mod)) |next_field_index| { |
| ... | ... | @@ -5211,7 +5212,7 @@ fn fieldLocation( |
| 5211 | 5212 | .{ .identifier = ip.stringToSlice(container_ty.structFieldName(next_field_index, mod)) } }; |
| 5212 | 5213 | } else if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin, |
| 5213 | 5214 | .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) } | |
| 5215 | 5216 | else |
| 5216 | 5217 | .begin, |
| 5217 | 5218 | }, |
| ... | ... | @@ -5282,7 +5283,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 5282 | 5283 | try f.renderType(writer, container_ptr_ty); |
| 5283 | 5284 | try writer.writeByte(')'); |
| 5284 | 5285 | |
| 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)) { | |
| 5286 | 5287 | .begin => try f.writeCValue(writer, field_ptr_val, .Initializer), |
| 5287 | 5288 | .field => |field| { |
| 5288 | 5289 | const u8_ptr_ty = try mod.adjustPtrTypeChild(field_ptr_ty, Type.u8); |
| ... | ... | @@ -5339,7 +5340,7 @@ fn fieldPtr( |
| 5339 | 5340 | try f.renderType(writer, field_ptr_ty); |
| 5340 | 5341 | try writer.writeByte(')'); |
| 5341 | 5342 | |
| 5342 | switch (fieldLocation(container_ty, field_ptr_ty, field_index, mod)) { | |
| 5343 | switch (fieldLocation(container_ptr_ty, field_ptr_ty, field_index, mod)) { | |
| 5343 | 5344 | .begin => try f.writeCValue(writer, container_ptr_val, .Initializer), |
| 5344 | 5345 | .field => |field| { |
| 5345 | 5346 | try writer.writeByte('&'); |
src/codegen/llvm.zig+2-1| ... | ... | @@ -10611,6 +10611,7 @@ pub const FuncGen = struct { |
| 10611 | 10611 | .Packed => { |
| 10612 | 10612 | const result_ty = self.typeOfIndex(inst); |
| 10613 | 10613 | const result_ty_info = result_ty.ptrInfo(mod); |
| 10614 | const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); | |
| 10614 | 10615 | |
| 10615 | 10616 | if (result_ty_info.packed_offset.host_size != 0) { |
| 10616 | 10617 | // From LLVM's perspective, a pointer to a packed struct and a pointer |
| ... | ... | @@ -10622,7 +10623,7 @@ pub const FuncGen = struct { |
| 10622 | 10623 | |
| 10623 | 10624 | // We have a pointer to a packed struct field that happens to be byte-aligned. |
| 10624 | 10625 | // 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); | |
| 10626 | 10627 | if (byte_offset == 0) return struct_ptr; |
| 10627 | 10628 | const usize_ty = try o.lowerType(Type.usize); |
| 10628 | 10629 | const llvm_index = try o.builder.intValue(usize_ty, byte_offset); |
test/behavior/packed-struct.zig+94| ... | ... | @@ -528,6 +528,100 @@ test "nested packed struct field access test" { |
| 528 | 528 | try std.testing.expect(arg.g.i == 8); |
| 529 | 529 | } |
| 530 | 530 | |
| 531 | test "nested packed struct at non-zero offset" { | |
| 532 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | |
| 533 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 534 | ||
| 535 | const Pair = packed struct(u24) { | |
| 536 | a: u16 = 0, | |
| 537 | b: u8 = 0, | |
| 538 | }; | |
| 539 | const A = packed struct { | |
| 540 | p1: Pair, | |
| 541 | p2: Pair, | |
| 542 | }; | |
| 543 | ||
| 544 | var k: u8 = 123; | |
| 545 | var v: A = .{ | |
| 546 | .p1 = .{ .a = k + 1, .b = k }, | |
| 547 | .p2 = .{ .a = k + 1, .b = k }, | |
| 548 | }; | |
| 549 | ||
| 550 | try expect(v.p1.a == k + 1 and v.p1.b == k); | |
| 551 | try expect(v.p2.a == k + 1 and v.p2.b == k); | |
| 552 | ||
| 553 | v.p2.a -= v.p1.a; | |
| 554 | v.p2.b -= v.p1.b; | |
| 555 | try expect(v.p2.a == 0 and v.p2.b == 0); | |
| 556 | try expect(v.p1.a == k + 1 and v.p1.b == k); | |
| 557 | } | |
| 558 | ||
| 559 | test "nested packed struct at non-zero offset 2" { | |
| 560 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | |
| 561 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 562 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | |
| 563 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | |
| 564 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 565 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 566 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 567 | ||
| 568 | const S = struct { | |
| 569 | const Pair = packed struct(u40) { | |
| 570 | a: u32 = 0, | |
| 571 | b: u8 = 0, | |
| 572 | }; | |
| 573 | const A = packed struct { | |
| 574 | p1: Pair, | |
| 575 | p2: Pair, | |
| 576 | c: C, | |
| 577 | }; | |
| 578 | const C = packed struct { | |
| 579 | p1: Pair, | |
| 580 | pad1: u5, | |
| 581 | p2: Pair, | |
| 582 | pad2: u3, | |
| 583 | last: i16, | |
| 584 | }; | |
| 585 | ||
| 586 | fn doTheTest() !void { | |
| 587 | var k: u8 = 123; | |
| 588 | var v: A = .{ | |
| 589 | .p1 = .{ .a = k + 1, .b = k }, | |
| 590 | .p2 = .{ .a = k + 1, .b = k }, | |
| 591 | .c = .{ | |
| 592 | .pad1 = 11, | |
| 593 | .pad2 = 2, | |
| 594 | .p1 = .{ .a = k + 1, .b = k }, | |
| 595 | .p2 = .{ .a = k + 1, .b = k }, | |
| 596 | .last = -12345, | |
| 597 | }, | |
| 598 | }; | |
| 599 | ||
| 600 | try expect(v.p1.a == k + 1 and v.p1.b == k); | |
| 601 | try expect(v.p2.a == k + 1 and v.p2.b == k); | |
| 602 | try expect(v.c.p2.a == k + 1 and v.c.p2.b == k); | |
| 603 | try expect(v.c.p2.a == k + 1 and v.c.p2.b == k); | |
| 604 | try expect(v.c.last == -12345); | |
| 605 | try expect(v.c.pad1 == 11 and v.c.pad2 == 2); | |
| 606 | ||
| 607 | v.p2.a -= v.p1.a; | |
| 608 | v.p2.b -= v.p1.b; | |
| 609 | v.c.p2.a -= v.c.p1.a; | |
| 610 | v.c.p2.b -= v.c.p1.b; | |
| 611 | v.c.last -|= 32000; | |
| 612 | try expect(v.p2.a == 0 and v.p2.b == 0); | |
| 613 | try expect(v.p1.a == k + 1 and v.p1.b == k); | |
| 614 | try expect(v.c.p2.a == 0 and v.c.p2.b == 0); | |
| 615 | try expect(v.c.p1.a == k + 1 and v.c.p1.b == k); | |
| 616 | try expect(v.c.last == -32768); | |
| 617 | try expect(v.c.pad1 == 11 and v.c.pad2 == 2); | |
| 618 | } | |
| 619 | }; | |
| 620 | ||
| 621 | try S.doTheTest(); | |
| 622 | try comptime S.doTheTest(); | |
| 623 | } | |
| 624 | ||
| 531 | 625 | test "runtime init of unnamed packed struct type" { |
| 532 | 626 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 533 | 627 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |