authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-07-29 23:49:14-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-07-29 23:49:14-04:00
log6f0a613b6f2d070196d47cb2932f7c728c63542a
treeb756e4c5d60eac6bf5e60122c46f2be939794b68
parent7ad4aede7a94ce336cd635bb2d622257a6ba0572
parent8c367ef99aae05ddba800a6c01cb07677e2f512c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16611 from xxxbxxx/packed-struct

codegen: fix various packed struct issues Closes #16609 Closes #15337

5 files changed, 124 insertions(+), 13 deletions(-)

src/arch/wasm/CodeGen.zig+10-5
...@@ -3675,8 +3675,9 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -3675,8 +3675,9 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
3675 const extra = func.air.extraData(Air.StructField, ty_pl.payload);3675 const extra = func.air.extraData(Air.StructField, ty_pl.payload);
36763676
3677 const struct_ptr = try func.resolveInst(extra.data.struct_operand);3677 const struct_ptr = try func.resolveInst(extra.data.struct_operand);
3678 const struct_ty = func.typeOf(extra.data.struct_operand).childType(mod);3678 const struct_ptr_ty = func.typeOf(extra.data.struct_operand);
3679 const result = try func.structFieldPtr(inst, extra.data.struct_operand, struct_ptr, struct_ty, extra.data.field_index);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 func.finishAir(inst, result, &.{extra.data.struct_operand});3681 func.finishAir(inst, result, &.{extra.data.struct_operand});
3681}3682}
36823683
...@@ -3684,9 +3685,10 @@ fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) Inne...@@ -3684,9 +3685,10 @@ fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) Inne
3684 const mod = func.bin_file.base.options.module.?;3685 const mod = func.bin_file.base.options.module.?;
3685 const ty_op = func.air.instructions.items(.data)[inst].ty_op;3686 const ty_op = func.air.instructions.items(.data)[inst].ty_op;
3686 const struct_ptr = try func.resolveInst(ty_op.operand);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);
36883690
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 func.finishAir(inst, result, &.{ty_op.operand});3692 func.finishAir(inst, result, &.{ty_op.operand});
3691}3693}
36923694
...@@ -3695,18 +3697,21 @@ fn structFieldPtr(...@@ -3695,18 +3697,21 @@ fn structFieldPtr(
3695 inst: Air.Inst.Index,3697 inst: Air.Inst.Index,
3696 ref: Air.Inst.Ref,3698 ref: Air.Inst.Ref,
3697 struct_ptr: WValue,3699 struct_ptr: WValue,
3700 struct_ptr_ty: Type,
3698 struct_ty: Type,3701 struct_ty: Type,
3699 index: u32,3702 index: u32,
3700) InnerError!WValue {3703) InnerError!WValue {
3701 const mod = func.bin_file.base.options.module.?;3704 const mod = func.bin_file.base.options.module.?;
3702 const result_ty = func.typeOfIndex(inst);3705 const result_ty = func.typeOfIndex(inst);
3706 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod);
3707
3703 const offset = switch (struct_ty.containerLayout(mod)) {3708 const offset = switch (struct_ty.containerLayout(mod)) {
3704 .Packed => switch (struct_ty.zigTypeTag(mod)) {3709 .Packed => switch (struct_ty.zigTypeTag(mod)) {
3705 .Struct => offset: {3710 .Struct => offset: {
3706 if (result_ty.ptrInfo(mod).packed_offset.host_size != 0) {3711 if (result_ty.ptrInfo(mod).packed_offset.host_size != 0) {
3707 break :offset @as(u32, 0);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 .Union => 0,3716 .Union => 0,
3712 else => unreachable,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,12 +5556,14 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32
5556 const mod = self.bin_file.options.module.?;5556 const mod = self.bin_file.options.module.?;
5557 const ptr_field_ty = self.typeOfIndex(inst);5557 const ptr_field_ty = self.typeOfIndex(inst);
5558 const ptr_container_ty = self.typeOf(operand);5558 const ptr_container_ty = self.typeOf(operand);
5559 const ptr_container_ty_info = ptr_container_ty.ptrInfo(mod);
5559 const container_ty = ptr_container_ty.childType(mod);5560 const container_ty = ptr_container_ty.childType(mod);
5561
5560 const field_offset: i32 = @intCast(switch (container_ty.containerLayout(mod)) {5562 const field_offset: i32 = @intCast(switch (container_ty.containerLayout(mod)) {
5561 .Auto, .Extern => container_ty.structFieldOffset(index, mod),5563 .Auto, .Extern => container_ty.structFieldOffset(index, mod),
5562 .Packed => if (container_ty.zigTypeTag(mod) == .Struct and5564 .Packed => if (container_ty.zigTypeTag(mod) == .Struct and
5563 ptr_field_ty.ptrInfo(mod).packed_offset.host_size == 0)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 else5567 else
5566 0,5568 0,
5567 });5569 });
src/codegen/c.zig+6-5
...@@ -661,7 +661,7 @@ pub const DeclGen = struct {...@@ -661,7 +661,7 @@ pub const DeclGen = struct {
661 try dg.renderCType(writer, ptr_cty);661 try dg.renderCType(writer, ptr_cty);
662 try writer.writeByte(')');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 .begin => try dg.renderParentPtr(writer, field.base, location),665 .begin => try dg.renderParentPtr(writer, field.base, location),
666 .field => |name| {666 .field => |name| {
667 try writer.writeAll("&(");667 try writer.writeAll("&(");
...@@ -5187,7 +5187,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5187,7 +5187,7 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
5187}5187}
51885188
5189fn fieldLocation(5189fn fieldLocation(
5190 container_ty: Type,5190 container_ptr_ty: Type,
5191 field_ptr_ty: Type,5191 field_ptr_ty: Type,
5192 field_index: u32,5192 field_index: u32,
5193 mod: *Module,5193 mod: *Module,
...@@ -5198,6 +5198,7 @@ fn fieldLocation(...@@ -5198,6 +5198,7 @@ fn fieldLocation(
5198 end: void,5198 end: void,
5199} {5199} {
5200 const ip = &mod.intern_pool;5200 const ip = &mod.intern_pool;
5201 const container_ty = container_ptr_ty.childType(mod);
5201 return switch (container_ty.zigTypeTag(mod)) {5202 return switch (container_ty.zigTypeTag(mod)) {
5202 .Struct => switch (container_ty.containerLayout(mod)) {5203 .Struct => switch (container_ty.containerLayout(mod)) {
5203 .Auto, .Extern => for (field_index..container_ty.structFieldCount(mod)) |next_field_index| {5204 .Auto, .Extern => for (field_index..container_ty.structFieldCount(mod)) |next_field_index| {
...@@ -5211,7 +5212,7 @@ fn fieldLocation(...@@ -5211,7 +5212,7 @@ fn fieldLocation(
5211 .{ .identifier = ip.stringToSlice(container_ty.structFieldName(next_field_index, mod)) } };5212 .{ .identifier = ip.stringToSlice(container_ty.structFieldName(next_field_index, mod)) } };
5212 } else if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin,5213 } else if (container_ty.hasRuntimeBitsIgnoreComptime(mod)) .end else .begin,
5213 .Packed => if (field_ptr_ty.ptrInfo(mod).packed_offset.host_size == 0)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 else5216 else
5216 .begin,5217 .begin,
5217 },5218 },
...@@ -5282,7 +5283,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -5282,7 +5283,7 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue {
5282 try f.renderType(writer, container_ptr_ty);5283 try f.renderType(writer, container_ptr_ty);
5283 try writer.writeByte(')');5284 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)) {
5286 .begin => try f.writeCValue(writer, field_ptr_val, .Initializer),5287 .begin => try f.writeCValue(writer, field_ptr_val, .Initializer),
5287 .field => |field| {5288 .field => |field| {
5288 const u8_ptr_ty = try mod.adjustPtrTypeChild(field_ptr_ty, Type.u8);5289 const u8_ptr_ty = try mod.adjustPtrTypeChild(field_ptr_ty, Type.u8);
...@@ -5339,7 +5340,7 @@ fn fieldPtr(...@@ -5339,7 +5340,7 @@ fn fieldPtr(
5339 try f.renderType(writer, field_ptr_ty);5340 try f.renderType(writer, field_ptr_ty);
5340 try writer.writeByte(')');5341 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)) {
5343 .begin => try f.writeCValue(writer, container_ptr_val, .Initializer),5344 .begin => try f.writeCValue(writer, container_ptr_val, .Initializer),
5344 .field => |field| {5345 .field => |field| {
5345 try writer.writeByte('&');5346 try writer.writeByte('&');
src/codegen/llvm.zig+11-1
...@@ -8797,6 +8797,15 @@ pub const FuncGen = struct {...@@ -8797,6 +8797,15 @@ pub const FuncGen = struct {
87978797
8798 const val_is_undef = if (try self.air.value(bin_op.rhs, mod)) |val| val.isUndefDeep(mod) else false;8798 const val_is_undef = if (try self.air.value(bin_op.rhs, mod)) |val| val.isUndefDeep(mod) else false;
8799 if (val_is_undef) {8799 if (val_is_undef) {
8800 const ptr_info = ptr_ty.ptrInfo(mod);
8801 const needs_bitmask = (ptr_info.packed_offset.host_size != 0);
8802 if (needs_bitmask) {
8803 // TODO: only some bits are to be undef, we cannot write with a simple memset.
8804 // meanwhile, ignore the write rather than stomping over valid bits.
8805 // https://github.com/ziglang/zig/issues/15337
8806 return .none;
8807 }
8808
8800 // Even if safety is disabled, we still emit a memset to undefined since it conveys8809 // Even if safety is disabled, we still emit a memset to undefined since it conveys
8801 // extra information to LLVM. However, safety makes the difference between using8810 // extra information to LLVM. However, safety makes the difference between using
8802 // 0xaa or actual undefined for the fill byte.8811 // 0xaa or actual undefined for the fill byte.
...@@ -10646,6 +10655,7 @@ pub const FuncGen = struct {...@@ -10646,6 +10655,7 @@ pub const FuncGen = struct {
10646 .Packed => {10655 .Packed => {
10647 const result_ty = self.typeOfIndex(inst);10656 const result_ty = self.typeOfIndex(inst);
10648 const result_ty_info = result_ty.ptrInfo(mod);10657 const result_ty_info = result_ty.ptrInfo(mod);
10658 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod);
1064910659
10650 if (result_ty_info.packed_offset.host_size != 0) {10660 if (result_ty_info.packed_offset.host_size != 0) {
10651 // From LLVM's perspective, a pointer to a packed struct and a pointer10661 // From LLVM's perspective, a pointer to a packed struct and a pointer
...@@ -10657,7 +10667,7 @@ pub const FuncGen = struct {...@@ -10657,7 +10667,7 @@ pub const FuncGen = struct {
1065710667
10658 // We have a pointer to a packed struct field that happens to be byte-aligned.10668 // We have a pointer to a packed struct field that happens to be byte-aligned.
10659 // Offset our operand pointer by the correct number of bytes.10669 // Offset our operand pointer by the correct number of bytes.
10660 const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, mod);10670 const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, mod) + @divExact(struct_ptr_ty_info.packed_offset.bit_offset, 8);
10661 if (byte_offset == 0) return struct_ptr;10671 if (byte_offset == 0) return struct_ptr;
10662 const usize_ty = try o.lowerType(Type.usize);10672 const usize_ty = try o.lowerType(Type.usize);
10663 const llvm_index = try o.builder.intValue(usize_ty, byte_offset);10673 const llvm_index = try o.builder.intValue(usize_ty, byte_offset);
test/behavior/packed-struct.zig+94-1
...@@ -528,6 +528,100 @@ test "nested packed struct field access test" {...@@ -528,6 +528,100 @@ test "nested packed struct field access test" {
528 try std.testing.expect(arg.g.i == 8);528 try std.testing.expect(arg.g.i == 8);
529}529}
530530
531test "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
559test "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
531test "runtime init of unnamed packed struct type" {625test "runtime init of unnamed packed struct type" {
532 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;626 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
533 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;627 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
...@@ -632,7 +726,6 @@ test "pointer to container level packed struct field" {...@@ -632,7 +726,6 @@ test "pointer to container level packed struct field" {
632test "store undefined to packed result location" {726test "store undefined to packed result location" {
633 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;727 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
634 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;728 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
635 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
636 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;729 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
637 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;730 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
638731