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
Fields smaller that the abisize, even when aligned to a byte boundary, are accessed via a packed_offset decorated ptr.
This was not always considered by the code, 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 probaly ziglang#16621
  • Loading branch information
xxxbxxx authored and andrewrk committed Sep 17, 2023
1 parent 6748bb4 commit b316d45
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 14 deletions.
5 changes: 4 additions & 1 deletion src/arch/wasm/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3015,7 +3015,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 @@ -696,13 +696,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
15 changes: 9 additions & 6 deletions src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3702,7 +3702,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),
.comptime_field => unreachable,
};
switch (ptr.len) {
Expand Down Expand Up @@ -4137,14 +4137,14 @@ pub const Object = struct {
return o.lowerDeclRefValue(ptr_ty, decl_index);
}

fn lowerParentPtr(o: *Object, ptr_val: Value, byte_aligned: bool) Allocator.Error!Builder.Constant {
fn lowerParentPtr(o: *Object, ptr_val: Value) Allocator.Error!Builder.Constant {
const mod = o.module;
return switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) {
.decl => |decl| o.lowerParentPtrDecl(decl),
.mut_decl => |mut_decl| o.lowerParentPtrDecl(mut_decl.decl),
.int => |int| try o.lowerIntAsPtr(int),
.eu_payload => |eu_ptr| {
const parent_ptr = try o.lowerParentPtr(eu_ptr.toValue(), true);
const parent_ptr = try o.lowerParentPtr(eu_ptr.toValue());

const eu_ty = mod.intern_pool.typeOf(eu_ptr).toType().childType(mod);
const payload_ty = eu_ty.errorUnionPayload(mod);
Expand All @@ -4161,7 +4161,7 @@ pub const Object = struct {
});
},
.opt_payload => |opt_ptr| {
const parent_ptr = try o.lowerParentPtr(opt_ptr.toValue(), true);
const parent_ptr = try o.lowerParentPtr(opt_ptr.toValue());

const opt_ty = mod.intern_pool.typeOf(opt_ptr).toType().childType(mod);
const payload_ty = opt_ty.optionalChild(mod);
Expand All @@ -4179,15 +4179,15 @@ pub const Object = struct {
},
.comptime_field => unreachable,
.elem => |elem_ptr| {
const parent_ptr = try o.lowerParentPtr(elem_ptr.base.toValue(), true);
const parent_ptr = try o.lowerParentPtr(elem_ptr.base.toValue());
const elem_ty = mod.intern_pool.typeOf(elem_ptr.base).toType().elemType2(mod);

return o.builder.gepConst(.inbounds, try o.lowerType(elem_ty), parent_ptr, null, &.{
try o.builder.intConst(try o.lowerType(Type.usize), elem_ptr.index),
});
},
.field => |field_ptr| {
const parent_ptr = try o.lowerParentPtr(field_ptr.base.toValue(), byte_aligned);
const parent_ptr = try o.lowerParentPtr(field_ptr.base.toValue());
const parent_ty = mod.intern_pool.typeOf(field_ptr.base).toType().childType(mod);

const field_index: u32 = @intCast(field_ptr.index);
Expand All @@ -4213,7 +4213,10 @@ pub const Object = struct {
},
.Struct => {
if (parent_ty.containerLayout(mod) == .Packed) {
const ptr_ty = mod.intern_pool.typeOf(ptr_val.ip_index).toType();
const byte_aligned = ptr_ty.isPackedStructFieldPtrByteAligned(mod);
if (!byte_aligned) return parent_ptr;

const llvm_usize = try o.lowerType(Type.usize);
const base_addr =
try o.builder.castConst(.ptrtoint, parent_ptr, llvm_usize);
Expand Down
36 changes: 36 additions & 0 deletions src/type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3113,6 +3113,42 @@ pub const Type = struct {
};
}

// value can be accessed through a normal ptr (without packed_offset related masking)
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 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 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 b316d45

Please sign in to comment.