From 405705cb76914072b9a91ac29f7cf0bf67b255f4 Mon Sep 17 00:00:00 2001 From: Xavier Bouchoux Date: Tue, 3 Oct 2023 05:34:19 +0000 Subject: [PATCH] codegen: fix byte-aligned field offsets in unaligned nested packed structs --- src/arch/wasm/CodeGen.zig | 3 ++- src/codegen/llvm.zig | 3 ++- src/type.zig | 6 ------ test/behavior/packed-struct.zig | 34 +++++++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 1fc28a8287..3d3054c618 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3828,7 +3828,8 @@ fn structFieldPtr( if (result_ty.ptrInfo(mod).packed_offset.host_size != 0) { break :offset @as(u32, 0); } - break :offset struct_ty.packedStructFieldByteOffset(index, mod) + @divExact(struct_ptr_ty_info.packed_offset.bit_offset, 8); + const struct_type = mod.typeToStruct(struct_ty).?; + break :offset @divExact(mod.structPackedFieldBitOffset(struct_type, index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); }, .Union => 0, else => unreachable, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 753e02a857..236584f44f 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -10140,6 +10140,7 @@ pub const FuncGen = struct { const result_ty = self.typeOfIndex(inst); const result_ty_info = result_ty.ptrInfo(mod); const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); + const struct_type = mod.typeToStruct(struct_ty).?; if (result_ty_info.packed_offset.host_size != 0) { // From LLVM's perspective, a pointer to a packed struct and a pointer @@ -10151,7 +10152,7 @@ pub const FuncGen = struct { // We have a pointer to a packed struct field that happens to be byte-aligned. // Offset our operand pointer by the correct number of bytes. - const byte_offset = struct_ty.packedStructFieldByteOffset(field_index, mod) + @divExact(struct_ptr_ty_info.packed_offset.bit_offset, 8); + const byte_offset = @divExact(mod.structPackedFieldBitOffset(struct_type, field_index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); if (byte_offset == 0) return struct_ptr; const usize_ty = try o.lowerType(Type.usize); const llvm_index = try o.builder.intValue(usize_ty, byte_offset); diff --git a/src/type.zig b/src/type.zig index a7b760a482..78514fe3d5 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3028,12 +3028,6 @@ pub const Type = struct { }; } - pub fn packedStructFieldByteOffset(ty: Type, field_index: u32, mod: *Module) u32 { - const ip = &mod.intern_pool; - const struct_type = ip.indexToKey(ty.toIntern()).struct_type; - return @divExact(mod.structPackedFieldBitOffset(struct_type, field_index), 8); - } - pub const FieldOffset = struct { field: usize, offset: u64, diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 08df5471e1..796c202ed6 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -622,6 +622,8 @@ test "@intFromPtr on a packed struct field unaligned and nested" { } test "packed struct fields modification" { + // Originally reported at https://github.com/ziglang/zig/issues/16615 + const Small = packed struct { val: u8 = 0, lo: u4 = 0, @@ -989,6 +991,7 @@ test "bitcast back and forth" { } test "field access of packed struct smaller than its abi size inside struct initialized with rls" { + // Originally reported at https://github.com/ziglang/zig/issues/14200 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .arm) return error.SkipZigTest; const S = struct { ps: packed struct { x: i2, y: i2 }, @@ -1003,3 +1006,34 @@ test "field access of packed struct smaller than its abi size inside struct init try expect(@as(i2, 0) == s.ps.x); try expect(@as(i2, 1) == s.ps.y); } + +test "modify nested packed struct aligned field" { + // Originally reported at https://github.com/ziglang/zig/issues/14632 + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; + + const Options = packed struct { + foo: bool = false, + bar: bool = false, + pretty_print: packed struct { + enabled: bool = false, + num_spaces: u4 = 4, + space_char: enum { space, tab } = .space, + indent: u8 = 0, + } = .{}, + baz: bool = false, + }; + + var opts = Options{}; + opts.pretty_print.indent += 1; + try std.testing.expectEqual(@as(u17, 0b00000000100100000), @bitCast(opts)); + try std.testing.expect(!opts.foo); + try std.testing.expect(!opts.bar); + try std.testing.expect(!opts.pretty_print.enabled); + try std.testing.expectEqual(@as(u4, 4), opts.pretty_print.num_spaces); + try std.testing.expectEqual(@as(u8, 1), opts.pretty_print.indent); + try std.testing.expect(!opts.baz); +}