authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-02 00:20:41+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-02 13:43:41+01:00
log15ff891f044102ba6515cb07f40805452420ea24
tree05e00c580057290dfcfccda0a8483668322aa11f
parent521bd2e94a3b32382b2d1de1e6185149032b49db

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`

3 files changed, 60 insertions(+), 4 deletions(-)

src/arch/x86_64/CodeGen.zig+3-3
......@@ -1801,13 +1801,12 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
18011801 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
18021802 const struct_field_ty = struct_ty.structFieldType(index);
18031803 const struct_field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*));
1804 const offset_to_field = struct_size - struct_field_offset - struct_field_size;
18051804
18061805 const dst_mcv: MCValue = result: {
18071806 switch (mcv) {
18081807 .stack_offset => {
18091808 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
1810 .immediate = offset_to_field,
1809 .immediate = struct_field_offset,
18111810 });
18121811 self.register_manager.freezeRegs(&.{offset_reg});
18131812 defer self.register_manager.unfreezeRegs(&.{offset_reg});
......@@ -1817,12 +1816,13 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
18171816 break :result dst_mcv;
18181817 },
18191818 .ptr_stack_offset => |off| {
1819 const offset_to_field = struct_size - struct_field_offset - struct_field_size;
18201820 const ptr_stack_offset = off + @intCast(i32, offset_to_field);
18211821 break :result MCValue{ .ptr_stack_offset = ptr_stack_offset };
18221822 },
18231823 .register => |reg| {
18241824 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
1825 .immediate = offset_to_field,
1825 .immediate = struct_field_offset,
18261826 });
18271827 self.register_manager.freezeRegs(&.{offset_reg});
18281828 defer self.register_manager.unfreezeRegs(&.{offset_reg});
src/codegen.zig+24-1
......@@ -373,11 +373,24 @@ pub fn generateSymbol(
373373 },
374374 .Struct => {
375375 // TODO debug info
376 // TODO padding of struct members
376 const struct_obj = typed_value.ty.castTag(.@"struct").?.data;
377 if (struct_obj.layout == .Packed) {
378 return Result{
379 .fail = try ErrorMsg.create(
380 bin_file.allocator,
381 src_loc,
382 "TODO implement generateSymbol for packed struct",
383 .{},
384 ),
385 };
386 }
387
388 const struct_begin = code.items.len;
377389 const field_vals = typed_value.val.castTag(.@"struct").?.data;
378390 for (field_vals) |field_val, index| {
379391 const field_ty = typed_value.ty.structFieldType(index);
380392 if (!field_ty.hasRuntimeBits()) continue;
393
381394 switch (try generateSymbol(bin_file, src_loc, .{
382395 .ty = field_ty,
383396 .val = field_val,
......@@ -388,6 +401,16 @@ pub fn generateSymbol(
388401 },
389402 .fail => |em| return Result{ .fail = em },
390403 }
404 const unpadded_field_end = code.items.len - struct_begin;
405
406 // Pad struct members if required
407 const target = bin_file.options.target;
408 const padded_field_end = typed_value.ty.structFieldOffset(index + 1, target);
409 const padding = try math.cast(usize, padded_field_end - unpadded_field_end);
410
411 if (padding > 0) {
412 try code.writer().writeByteNTimes(0, padding);
413 }
391414 }
392415
393416 return Result{ .appended = {} };
test/behavior/struct.zig+33
......@@ -18,6 +18,39 @@ test "top level fields" {
1818 try expect(@as(i32, 1235) == instance.top_level_field);
1919}
2020
21const StructWithFields = struct {
22 a: u8,
23 b: u32,
24 c: u64,
25 d: u32,
26
27 fn first(self: *const StructWithFields) u8 {
28 return self.a;
29 }
30
31 fn second(self: *const StructWithFields) u32 {
32 return self.b;
33 }
34
35 fn third(self: *const StructWithFields) u64 {
36 return self.c;
37 }
38
39 fn fourth(self: *const StructWithFields) u32 {
40 return self.d;
41 }
42};
43
44test "non-packed struct has fields padded out to the required alignment" {
45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
46
47 const foo = StructWithFields{ .a = 5, .b = 1, .c = 10, .d = 2 };
48 try expect(foo.first() == 5);
49 try expect(foo.second() == 1);
50 try expect(foo.third() == 10);
51 try expect(foo.fourth() == 2);
52}
53
2154const StructWithNoFields = struct {
2255 fn add(a: i32, b: i32) i32 {
2356 return a + b;