Skip to content

Commit

Permalink
codegen/llvm: fix wrong field offset in packed structs
Browse files Browse the repository at this point in the history
A field smaller that the abisize is still be accessed with a packed_offset decorated ptr, even when aligned to a byte boundary.
The test was incorrect and the offset was applied once to the pointer (as if it would be used as an undecorated ptr),
and again when dereferencing it (by the packed_offset decoration)

resolves ziglang#16615
and maybe ziglang#16621 ?
  • Loading branch information
xxxbxxx committed Aug 8, 2023
1 parent 9c01755 commit c1bc6fc
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
5 changes: 4 additions & 1 deletion src/arch/wasm/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2968,7 +2968,10 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue

const field_offset = switch (parent_ty.zigTypeTag(mod)) {
.Struct => switch (parent_ty.containerLayout(mod)) {
.Packed => parent_ty.packedStructFieldByteOffset(@as(usize, @intCast(field.index)), mod),
.Packed => if (parent_ty.packedStructFieldByteAligned(@intCast(field.index), mod))
parent_ty.packedStructFieldByteOffset(@as(usize, @intCast(field.index)), mod)
else
0,
else => parent_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod),
},
.Union => switch (parent_ty.containerLayout(mod)) {
Expand Down
11 changes: 4 additions & 7 deletions src/codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -692,13 +692,10 @@ fn lowerParentPtr(
mod,
)),
.Packed => if (mod.typeToStruct(base_type.toType())) |struct_obj|
math.divExact(u16, struct_obj.packedFieldBitOffset(
mod,
@intCast(field.index),
), 8) catch |err| switch (err) {
error.UnexpectedRemainder => 0,
error.DivisionByZero => unreachable,
}
if (base_type.toType().packedStructFieldByteAligned(@intCast(field.index), mod))
@divExact(struct_obj.packedFieldBitOffset(mod, @intCast(field.index)), 8)
else
0
else
0,
},
Expand Down
2 changes: 1 addition & 1 deletion src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3862,7 +3862,7 @@ pub const Object = struct {
.opt_payload,
.elem,
.field,
=> try o.lowerParentPtr(val, ty.ptrInfo(mod).packed_offset.bit_offset % 8 == 0),
=> try o.lowerParentPtr(val, ptr_ty.isPackedStructFieldPtrByteAligned(mod)),
.comptime_field => unreachable,
};
switch (ptr.len) {
Expand Down
39 changes: 39 additions & 0 deletions src/type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3123,6 +3123,45 @@ pub const Type = struct {
};
}

// value can be accessed through a normal ptr
pub fn isPackedStructFieldPtrByteAligned(ptr_ty: Type, mod: *Module) bool {
const ptr_info = ptr_ty.ptrInfo(mod);
if (ptr_info.packed_offset.host_size == 0) return true;
//const target = mod.getTarget();
const elem_ty = ptr_ty.childType(mod);
const elem_abi_size = elem_ty.abiSize(mod);
const elem_size_bits = elem_ty.bitSize(mod);
const bit_offset = ptr_info.packed_offset.bit_offset;
return bit_offset % 8 == 0
// and target.cpu.arch.endian() == .Little ?
and (elem_abi_size * 8 == elem_size_bits);
}

pub fn packedStructFieldByteAligned(ty: Type, field_index: usize, mod: *Module) bool {
const struct_type = mod.intern_pool.indexToKey(ty.toIntern()).struct_type;
const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
assert(struct_obj.layout == .Packed);
comptime assert(Type.packed_struct_layout_version == 2);

var bit_offset: u16 = undefined;
var elem_abi_size: u64 = 0;
var elem_size_bits: u16 = undefined;
var running_bits: u16 = 0;
for (struct_obj.fields.values(), 0..) |f, i| {
if (!f.ty.hasRuntimeBits(mod)) continue;

const field_bits = @as(u16, @intCast(f.ty.bitSize(mod)));
if (i == field_index) {
bit_offset = running_bits;
elem_size_bits = field_bits;
elem_abi_size = f.ty.abiSize(mod);
}
running_bits += field_bits;
}

return (bit_offset % 8 == 0 and elem_abi_size * 8 == elem_size_bits);
}

pub fn packedStructFieldByteOffset(ty: Type, field_index: usize, mod: *Module) u32 {
const struct_type = mod.intern_pool.indexToKey(ty.toIntern()).struct_type;
const struct_obj = mod.structPtrUnwrap(struct_type.index).?;
Expand Down

0 comments on commit c1bc6fc

Please sign in to comment.