authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-06-25 23:06:53-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-06-25 23:06:53-07:00
log40cf3f7ae5fbfb84b7af6b27e6296ee858b209ef
treee0c720d3fb38e61049a7411d7d176033e1616f69
parentd98147414d084bc41b00ba9c0be8c7b82ad4e76c
parentb66865d0597124af02fed027a2d72c00b976a17f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15995 from mlugg/fix/union-field-ptr-align

Sema: copy pointer alignment to union field pointers

5 files changed, 231 insertions(+), 9 deletions(-)

lib/std/pdb.zig+2-2
...@@ -776,8 +776,8 @@ pub const Pdb = struct {...@@ -776,8 +776,8 @@ pub const Pdb = struct {
776 } else 0;776 } else 0;
777777
778 const found_line_index = start_line_index + line_entry_idx * @sizeOf(LineNumberEntry);778 const found_line_index = start_line_index + line_entry_idx * @sizeOf(LineNumberEntry);
779 const line_num_entry = @as(*align(1) LineNumberEntry, @ptrCast(&subsect_info[found_line_index]));779 const line_num_entry: *align(1) LineNumberEntry = @ptrCast(&subsect_info[found_line_index]);
780 const flags = @as(*LineNumberEntry.Flags, @ptrCast(&line_num_entry.Flags));780 const flags: *align(1) LineNumberEntry.Flags = @ptrCast(&line_num_entry.Flags);
781781
782 return debug.LineInfo{782 return debug.LineInfo{
783 .file_name = source_file_name,783 .file_name = source_file_name,
src/Module.zig+1
...@@ -992,6 +992,7 @@ pub const Struct = struct {...@@ -992,6 +992,7 @@ pub const Struct = struct {
992 is_comptime: bool,992 is_comptime: bool,
993993
994 /// Returns the field alignment. If the struct is packed, returns 0.994 /// Returns the field alignment. If the struct is packed, returns 0.
995 /// Keep implementation in sync with `Sema.structFieldAlignment`.
995 pub fn alignment(996 pub fn alignment(
996 field: Field,997 field: Field,
997 mod: *Module,998 mod: *Module,
src/Sema.zig+42-7
...@@ -25871,6 +25871,9 @@ fn structFieldPtrByIndex(...@@ -25871,6 +25871,9 @@ fn structFieldPtrByIndex(
2587125871
25872 const target = mod.getTarget();25872 const target = mod.getTarget();
2587325873
25874 const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse
25875 try sema.typeAbiAlignment(struct_ptr_ty_info.child.toType());
25876
25874 if (struct_obj.layout == .Packed) {25877 if (struct_obj.layout == .Packed) {
25875 comptime assert(Type.packed_struct_layout_version == 2);25878 comptime assert(Type.packed_struct_layout_version == 2);
2587625879
...@@ -25892,8 +25895,6 @@ fn structFieldPtrByIndex(...@@ -25892,8 +25895,6 @@ fn structFieldPtrByIndex(
25892 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;25895 ptr_ty_data.packed_offset.bit_offset += struct_ptr_ty_info.packed_offset.bit_offset;
25893 }25896 }
2589425897
25895 const parent_align = struct_ptr_ty_info.flags.alignment.toByteUnitsOptional() orelse
25896 struct_ptr_ty_info.child.toType().abiAlignment(mod);
25897 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);25898 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);
2589825899
25899 // If the field happens to be byte-aligned, simplify the pointer type.25900 // If the field happens to be byte-aligned, simplify the pointer type.
...@@ -25917,8 +25918,13 @@ fn structFieldPtrByIndex(...@@ -25917,8 +25918,13 @@ fn structFieldPtrByIndex(
25917 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };25918 ptr_ty_data.packed_offset = .{ .host_size = 0, .bit_offset = 0 };
25918 }25919 }
25919 }25920 }
25921 } else if (struct_obj.layout == .Extern and field_index == 0) {
25922 // This is the first field in memory, so can inherit the struct alignment
25923 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(parent_align);
25920 } else {25924 } else {
25921 ptr_ty_data.flags.alignment = field.abi_align;25925 // Our alignment is capped at the field alignment
25926 const field_align = try sema.structFieldAlignment(field, struct_obj.layout);
25927 ptr_ty_data.flags.alignment = Alignment.fromByteUnits(@min(field_align, parent_align));
25922 }25928 }
2592325929
25924 const ptr_field_ty = try mod.ptrType(ptr_ty_data);25930 const ptr_field_ty = try mod.ptrType(ptr_ty_data);
...@@ -26089,6 +26095,7 @@ fn unionFieldPtr(...@@ -26089,6 +26095,7 @@ fn unionFieldPtr(
26089 assert(unresolved_union_ty.zigTypeTag(mod) == .Union);26095 assert(unresolved_union_ty.zigTypeTag(mod) == .Union);
2609026096
26091 const union_ptr_ty = sema.typeOf(union_ptr);26097 const union_ptr_ty = sema.typeOf(union_ptr);
26098 const union_ptr_info = union_ptr_ty.ptrInfo(mod);
26092 const union_ty = try sema.resolveTypeFields(unresolved_union_ty);26099 const union_ty = try sema.resolveTypeFields(unresolved_union_ty);
26093 const union_obj = mod.typeToUnion(union_ty).?;26100 const union_obj = mod.typeToUnion(union_ty).?;
26094 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);26101 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
...@@ -26096,10 +26103,16 @@ fn unionFieldPtr(...@@ -26096,10 +26103,16 @@ fn unionFieldPtr(
26096 const ptr_field_ty = try mod.ptrType(.{26103 const ptr_field_ty = try mod.ptrType(.{
26097 .child = field.ty.toIntern(),26104 .child = field.ty.toIntern(),
26098 .flags = .{26105 .flags = .{
26099 .is_const = !union_ptr_ty.ptrIsMutable(mod),26106 .is_const = union_ptr_info.flags.is_const,
26100 .is_volatile = union_ptr_ty.isVolatilePtr(mod),26107 .is_volatile = union_ptr_info.flags.is_volatile,
26101 .address_space = union_ptr_ty.ptrAddressSpace(mod),26108 .address_space = union_ptr_info.flags.address_space,
26102 },26109 .alignment = if (union_obj.layout == .Auto) blk: {
26110 const union_align = union_ptr_info.flags.alignment.toByteUnitsOptional() orelse try sema.typeAbiAlignment(union_ty);
26111 const field_align = try sema.unionFieldAlignment(field);
26112 break :blk InternPool.Alignment.fromByteUnits(@min(union_align, field_align));
26113 } else union_ptr_info.flags.alignment,
26114 },
26115 .packed_offset = union_ptr_info.packed_offset,
26103 });26116 });
26104 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));26117 const enum_field_index = @as(u32, @intCast(union_obj.tag_ty.enumFieldIndex(field_name, mod).?));
2610526118
...@@ -35974,6 +35987,28 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 {...@@ -35974,6 +35987,28 @@ fn unionFieldAlignment(sema: *Sema, field: Module.Union.Field) !u32 {
35974 field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty)));35987 field.abi_align.toByteUnitsOptional() orelse try sema.typeAbiAlignment(field.ty)));
35975}35988}
3597635989
35990/// Keep implementation in sync with `Module.Struct.Field.alignment`.
35991fn structFieldAlignment(sema: *Sema, field: Module.Struct.Field, layout: std.builtin.Type.ContainerLayout) !u32 {
35992 const mod = sema.mod;
35993 if (field.abi_align.toByteUnitsOptional()) |a| {
35994 assert(layout != .Packed);
35995 return @as(u32, @intCast(a));
35996 }
35997 switch (layout) {
35998 .Packed => return 0,
35999 .Auto => if (mod.getTarget().ofmt != .c) {
36000 return sema.typeAbiAlignment(field.ty);
36001 },
36002 .Extern => {},
36003 }
36004 // extern
36005 const ty_abi_align = try sema.typeAbiAlignment(field.ty);
36006 if (field.ty.isAbiInt(mod) and field.ty.intInfo(mod).bits >= 128) {
36007 return @max(ty_abi_align, 16);
36008 }
36009 return ty_abi_align;
36010}
36011
35977/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.36012/// Synchronize logic with `Type.isFnOrHasRuntimeBits`.
35978pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {36013pub fn fnHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
35979 const mod = sema.mod;36014 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}
test/behavior/union.zig+109
...@@ -1583,3 +1583,112 @@ test "coerce enum literal to union in result loc" {...@@ -1583,3 +1583,112 @@ test "coerce enum literal to union in result loc" {
1583 try U.doTest(true);1583 try U.doTest(true);
1584 try comptime U.doTest(true);1584 try comptime U.doTest(true);
1585}1585}
1586
1587test "defined-layout union field pointer has correct alignment" {
1588 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1589 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1590 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1591 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1592 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1593 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1594 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1595
1596 const S = struct {
1597 fn doTheTest(comptime U: type) !void {
1598 var a: U = .{ .x = 123 };
1599 var b: U align(1) = .{ .x = 456 };
1600 var c: U align(64) = .{ .x = 789 };
1601
1602 const ap = &a.x;
1603 const bp = &b.x;
1604 const cp = &c.x;
1605
1606 comptime assert(@TypeOf(ap) == *u32);
1607 comptime assert(@TypeOf(bp) == *align(1) u32);
1608 comptime assert(@TypeOf(cp) == *align(64) u32);
1609
1610 try expectEqual(@as(u32, 123), ap.*);
1611 try expectEqual(@as(u32, 456), bp.*);
1612 try expectEqual(@as(u32, 789), cp.*);
1613 }
1614 };
1615
1616 const U1 = extern union { x: u32 };
1617 const U2 = packed union { x: u32 };
1618
1619 try S.doTheTest(U1);
1620 try S.doTheTest(U2);
1621 try comptime S.doTheTest(U1);
1622 try comptime S.doTheTest(U2);
1623}
1624
1625test "undefined-layout union field pointer has correct alignment" {
1626 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1627 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1628 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1629 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1630 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1631 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1632 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1633
1634 const S = struct {
1635 fn doTheTest(comptime U: type) !void {
1636 var a: U = .{ .x = 123 };
1637 var b: U align(1) = .{ .x = 456 };
1638 var c: U align(64) = .{ .x = 789 };
1639
1640 const ap = &a.x;
1641 const bp = &b.x;
1642 const cp = &c.x;
1643
1644 comptime assert(@TypeOf(ap) == *u32);
1645 comptime assert(@TypeOf(bp) == *align(1) u32);
1646 comptime assert(@TypeOf(cp) == *u32); // undefined layout so does not inherit larger aligns
1647
1648 try expectEqual(@as(u32, 123), ap.*);
1649 try expectEqual(@as(u32, 456), bp.*);
1650 try expectEqual(@as(u32, 789), cp.*);
1651 }
1652 };
1653
1654 const U1 = union { x: u32 };
1655 const U2 = union(enum) { x: u32 };
1656
1657 try S.doTheTest(U1);
1658 try S.doTheTest(U2);
1659 try comptime S.doTheTest(U1);
1660 try comptime S.doTheTest(U2);
1661}
1662
1663test "packed union field pointer has correct alignment" {
1664 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1665 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1666 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1667 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1668 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1669 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1670 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1671
1672 const U = packed union { x: u20 };
1673 const S = packed struct(u24) { a: u2, u: U, b: u2 };
1674
1675 var a: S = undefined;
1676 var b: S align(1) = undefined;
1677 var c: S align(64) = undefined;
1678
1679 const ap = &a.u.x;
1680 const bp = &b.u.x;
1681 const cp = &c.u.x;
1682
1683 comptime assert(@TypeOf(ap) == *align(4:2:3) u20);
1684 comptime assert(@TypeOf(bp) == *align(1:2:3) u20);
1685 comptime assert(@TypeOf(cp) == *align(64:2:3) u20);
1686
1687 a.u = .{ .x = 123 };
1688 b.u = .{ .x = 456 };
1689 c.u = .{ .x = 789 };
1690
1691 try expectEqual(@as(u20, 123), ap.*);
1692 try expectEqual(@as(u20, 456), bp.*);
1693 try expectEqual(@as(u20, 789), cp.*);
1694}