mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
stage2: pad out (non-packed) struct fields when lowering to bytes
* pad out (non-packed) struct fields when lowering to bytes to be saved in the binary - prior to this change, fields would be saved at non-aligned addresses leading to wrong accesses * add a matching test case to `behavior/struct.zig` tests * fix offset to field calculation in `struct_field_ptr` on `x86_64`
This commit is contained in:
parent
521bd2e94a
commit
15ff891f04
@ -1801,13 +1801,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
|
||||
const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
|
||||
const struct_field_ty = struct_ty.structFieldType(index);
|
||||
const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
|
||||
const offset_to_field = struct_size - struct_field_offset - struct_field_size;
|
||||
|
||||
const dst_mcv: MCValue = result: {
|
||||
switch (mcv) {
|
||||
.stack_offset => {
|
||||
const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
|
||||
.immediate = offset_to_field,
|
||||
.immediate = struct_field_offset,
|
||||
});
|
||||
self.register_manager.freezeRegs(&.{offset_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{offset_reg});
|
||||
@ -1817,12 +1816,13 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
|
||||
break :result dst_mcv;
|
||||
},
|
||||
.ptr_stack_offset => |off| {
|
||||
const offset_to_field = struct_size - struct_field_offset - struct_field_size;
|
||||
const ptr_stack_offset = off + @intCast(i32, offset_to_field);
|
||||
break :result MCValue{ .ptr_stack_offset = ptr_stack_offset };
|
||||
},
|
||||
.register => |reg| {
|
||||
const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
|
||||
.immediate = offset_to_field,
|
||||
.immediate = struct_field_offset,
|
||||
});
|
||||
self.register_manager.freezeRegs(&.{offset_reg});
|
||||
defer self.register_manager.unfreezeRegs(&.{offset_reg});
|
||||
|
||||
@ -373,11 +373,24 @@ pub fn generateSymbol(
|
||||
},
|
||||
.Struct => {
|
||||
// TODO debug info
|
||||
// TODO padding of struct members
|
||||
const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
|
||||
if (struct_obj.layout == .Packed) {
|
||||
return Result{
|
||||
.fail = try ErrorMsg.create(
|
||||
bin_file.allocator,
|
||||
src_loc,
|
||||
"TODO implement generateSymbol for packed struct",
|
||||
.{},
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
const struct_begin = code.items.len;
|
||||
const field_vals = typed_value.val.castTag(.@"struct").?.data;
|
||||
for (field_vals) |field_val, index| {
|
||||
const field_ty = typed_value.ty.structFieldType(index);
|
||||
if (!field_ty.hasRuntimeBits()) continue;
|
||||
|
||||
switch (try generateSymbol(bin_file, src_loc, .{
|
||||
.ty = field_ty,
|
||||
.val = field_val,
|
||||
@ -388,6 +401,16 @@ pub fn generateSymbol(
|
||||
},
|
||||
.fail => |em| return Result{ .fail = em },
|
||||
}
|
||||
const unpadded_field_end = code.items.len - struct_begin;
|
||||
|
||||
// Pad struct members if required
|
||||
const target = bin_file.options.target;
|
||||
const padded_field_end = typed_value.ty.structFieldOffset(index + 1, target);
|
||||
const padding = try math.cast(usize, padded_field_end - unpadded_field_end);
|
||||
|
||||
if (padding > 0) {
|
||||
try code.writer().writeByteNTimes(0, padding);
|
||||
}
|
||||
}
|
||||
|
||||
return Result{ .appended = {} };
|
||||
|
||||
@ -18,6 +18,39 @@ test "top level fields" {
|
||||
try expect(@as(i32, 1235) == instance.top_level_field);
|
||||
}
|
||||
|
||||
const StructWithFields = struct {
|
||||
a: u8,
|
||||
b: u32,
|
||||
c: u64,
|
||||
d: u32,
|
||||
|
||||
fn first(self: *const StructWithFields) u8 {
|
||||
return self.a;
|
||||
}
|
||||
|
||||
fn second(self: *const StructWithFields) u32 {
|
||||
return self.b;
|
||||
}
|
||||
|
||||
fn third(self: *const StructWithFields) u64 {
|
||||
return self.c;
|
||||
}
|
||||
|
||||
fn fourth(self: *const StructWithFields) u32 {
|
||||
return self.d;
|
||||
}
|
||||
};
|
||||
|
||||
test "non-packed struct has fields padded out to the required alignment" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
|
||||
const foo = StructWithFields{ .a = 5, .b = 1, .c = 10, .d = 2 };
|
||||
try expect(foo.first() == 5);
|
||||
try expect(foo.second() == 1);
|
||||
try expect(foo.third() == 10);
|
||||
try expect(foo.fourth() == 2);
|
||||
}
|
||||
|
||||
const StructWithNoFields = struct {
|
||||
fn add(a: i32, b: i32) i32 {
|
||||
return a + b;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user