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 {...@@ -991,6 +991,7 @@ pub const Struct = struct {
991 is_comptime: bool,991 is_comptime: bool,
992992
993 /// Returns the field alignment. If the struct is packed, returns 0.993 /// Returns the field alignment. If the struct is packed, returns 0.
994 /// Keep implementation in sync with `Sema.structFieldAlignment`.
994 pub fn alignment(995 pub fn alignment(
995 field: Field,996 field: Field,
996 mod: *Module,997 mod: *Module,
src/Sema.zig+31-3
...@@ -25842,6 +25842,9 @@ fn structFieldPtrByIndex(...@@ -25842,6 +25842,9 @@ fn structFieldPtrByIndex(
2584225842
25843 const target = mod.getTarget();25843 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
25845 if (struct_obj.layout == .Packed) {25848 if (struct_obj.layout == .Packed) {
25846 comptime assert(Type.packed_struct_layout_version == 2);25849 comptime assert(Type.packed_struct_layout_version == 2);
2584725850
...@@ -25863,8 +25866,6 @@ fn structFieldPtrByIndex(...@@ -25863,8 +25866,6 @@ fn structFieldPtrByIndex(
25863 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;25866 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;
25864 }25867 }
2586525868
25866 const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse
25867 struct_ptr_ty_info.child.toType().abiAlignment(mod);
25868 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);25869 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);
2586925870
25870 // If the field happens to be byte-aligned, simplify the pointer type.25871 // If the field happens to be byte-aligned, simplify the pointer type.
...@@ -25888,8 +25889,13 @@ fn structFieldPtrByIndex(...@@ -25888,8 +25889,13 @@ fn structFieldPtrByIndex(
25888 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };25889 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };
25889 }25890 }
25890 }25891 }
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);
25891 } else {25895 } 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));
25893 }25899 }
2589425900
25895 const ptr_field_ty = try mod.ptrType(ptr_ty_data);25901 const ptr_field_ty = try mod.ptrType(ptr_ty_data);
...@@ -35952,6 +35958,28 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 {...@@ -35952,6 +35958,28 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 {
35952 field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty)));35958 field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty)));
35953}35959}
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
35955/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.35983/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
35956pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {35984pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
35957 const mod = sema.mod;35985 const mod = sema.mod;
test/behavior/struct.zig+77
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const native_endian = builtin.target.cpu.arch.endian();3const native_endian = builtin.target.cpu.arch.endian();
4const assert = std.debug.assert;
4const expect = std.testing.expect;5const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;6const expectEqual = std.testing.expectEqual;
6const expectEqualSlices = std.testing.expectEqualSlices;7const expectEqualSlices = std.testing.expectEqualSlices;
...@@ -1637,3 +1638,79 @@ test "instantiate struct with comptime field" {...@@ -1637,3 +1638,79 @@ test "instantiate struct with comptime field" {
1637 comptime std.debug.assert(things.foo == 1);1638 comptime std.debug.assert(things.foo == 1);
1638 }1639 }
1639}1640}
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}