authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-16 23:08:33+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-06-25 14:13:52+01:00
log80e493cb3631827909da4daaff001edb8691a83f
tree2a069f4444915c7b6335966230b1d62ae6f9a309
parent2611d97fb07e5cba5657f508e93f01d60a2379bd
signaturelock-open Commit is signed but in an unrecognized format.

Sema: copy pointer alignment to struct field pointers


3 files changed, 109 insertions(+), 3 deletions(-)

src/Module.zig+1
......@@ -991,6 +991,7 @@ pub const Struct = struct {
991991 is_comptime: bool,
992992
993993 /// Returns the field alignment. If the struct is packed, returns 0.
994 /// Keep implementation in sync with `Sema.structFieldAlignment`.
994995 pub fn alignment(
995996 field: Field,
996997 mod: *Module,
src/Sema.zig+31-3
......@@ -25842,6 +25842,9 @@ fn structFieldPtrByIndex(
2584225842
2584325843 const target = mod.getTarget();
2584425844
25845 const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse
25846 try sema.typeAbiAlignment(struct_ptr_ty_info.child.toType());
25847
2584525848 if (struct_obj.layout == .Packed) {
2584625849 comptime assert(Type.packed_struct_layout_version == 2);
2584725850
......@@ -25863,8 +25866,6 @@ fn structFieldPtrByIndex(
2586325866 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;
2586425867 }
2586525868
25866 const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse
25867 struct_ptr_ty_info.child.toType().abiAlignment(mod);
2586825869 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);
2586925870
2587025871 // If the field happens to be byte-aligned, simplify the pointer type.
......@@ -25888,8 +25889,13 @@ fn structFieldPtrByIndex(
2588825889 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };
2588925890 }
2589025891 }
25892 } else if (struct_obj.layout == .Extern and field_index == 0) {
25893 // This is the first field in memory, so can inherit the struct alignment
25894 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);
2589125895 } else {
25892 ptr_ty_data.flags.alignment = field.abi_align;
25896 // Our alignment is capped at the field alignment
25897 const field_align = try sema.structFieldAlignment(field, struct_obj.layout);
25898 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(@min(field_align, parent_align));
2589325899 }
2589425900
2589525901 const ptr_field_ty = try mod.ptrType(ptr_ty_data);
......@@ -35952,6 +35958,28 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 {
3595235958 field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty)));
3595335959}
3595435960
35961/// Keep implementation in sync with `Module.Struct.Field.alignment`.
35962fn structFieldAlignment(sema: *Sema, field: Module.Struct.Field, layout: std.builtin.Type.ContainerLayout) !u32 {
35963 const mod = sema.mod;
35964 if (field.abi_align.toByteUnitsOptional()) |a| {
35965 assert(layout != .Packed);
35966 return @as(u32, @intCast(a));
35967 }
35968 switch (layout) {
35969 .Packed => return 0,
35970 .Auto => if (mod.getTarget().ofmt != .c) {
35971 return sema.typeAbiAlignment(field.ty);
35972 },
35973 .Extern => {},
35974 }
35975 // extern
35976 const ty_abi_align = try sema.typeAbiAlignment(field.ty);
35977 if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) {
35978 return @max(ty_abi_align, 16);
35979 }
35980 return ty_abi_align;
35981}
35982
3595535983/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
3595635984pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
3595735985 const mod = sema.mod;
test/behavior/struct.zig+77
......@@ -1,6 +1,7 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const native_endian = builtin.target.cpu.arch.endian();
4const assert = std.debug.assert;
45const expect = std.testing.expect;
56const expectEqual = std.testing.expectEqual;
67const expectEqualSlices = std.testing.expectEqualSlices;
......@@ -1637,3 +1638,79 @@ test "instantiate struct with comptime field" {
16371638 comptime std.debug.assert(things.foo == 1);
16381639 }
16391640}
1641
1642test "struct field pointer has correct alignment" {
1643 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1644 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1645 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1647 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1648 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1649 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1650
1651 const S = struct {
1652 fn doTheTest() !void {
1653 var a: struct { x: u32 } = .{ .x = 123 };
1654 var b: struct { x: u32 } align(1) = .{ .x = 456 };
1655 var c: struct { x: u32 } align(64) = .{ .x = 789 };
1656
1657 const ap = &a.x;
1658 const bp = &b.x;
1659 const cp = &c.x;
1660
1661 comptime assert(@TypeOf(ap) == *u32);
1662 comptime assert(@TypeOf(bp) == *align(1) u32);
1663 comptime assert(@TypeOf(cp) == *u32); // undefined layout, cannot inherit larger alignment
1664
1665 try expectEqual(@as(u32, 123), ap.*);
1666 try expectEqual(@as(u32, 456), bp.*);
1667 try expectEqual(@as(u32, 789), cp.*);
1668 }
1669 };
1670
1671 try S.doTheTest();
1672 try comptime S.doTheTest();
1673}
1674
1675test "extern struct field pointer has correct alignment" {
1676 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1677 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1678 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1679 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1680 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1681 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1682 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1683
1684 const S = struct {
1685 fn doTheTest() !void {
1686 var a: extern struct { x: u32, y: u32 } = .{ .x = 1, .y = 2 };
1687 var b: extern struct { x: u32, y: u32 } align(1) = .{ .x = 3, .y = 4 };
1688 var c: extern struct { x: u32, y: u32 } align(64) = .{ .x = 5, .y = 6 };
1689
1690 const axp = &a.x;
1691 const bxp = &b.x;
1692 const cxp = &c.x;
1693 const ayp = &a.y;
1694 const byp = &b.y;
1695 const cyp = &c.y;
1696
1697 comptime assert(@TypeOf(axp) == *u32);
1698 comptime assert(@TypeOf(bxp) == *align(1) u32);
1699 comptime assert(@TypeOf(cxp) == *align(64) u32); // first field, inherits larger alignment
1700 comptime assert(@TypeOf(ayp) == *u32);
1701 comptime assert(@TypeOf(byp) == *align(1) u32);
1702 comptime assert(@TypeOf(cyp) == *u32);
1703
1704 try expectEqual(@as(u32, 1), axp.*);
1705 try expectEqual(@as(u32, 3), bxp.*);
1706 try expectEqual(@as(u32, 5), cxp.*);
1707
1708 try expectEqual(@as(u32, 2), ayp.*);
1709 try expectEqual(@as(u32, 4), byp.*);
1710 try expectEqual(@as(u32, 6), cyp.*);
1711 }
1712 };
1713
1714 try S.doTheTest();
1715 try comptime S.doTheTest();
1716}