From b7799ef322103c8e449c45494c29fb4a8c9867df Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 29 Apr 2024 17:40:49 -0700 Subject: [PATCH] std.Target.maxIntAlignment: move to compiler implementation This should not be a public API, and the x86 backend does not support the value 16. --- lib/compiler/aro/aro/Type.zig | 2 +- lib/std/Target.zig | 91 -------------------------- src/codegen/c/Type.zig | 10 +-- src/codegen/llvm.zig | 2 +- src/link/C.zig | 4 +- src/type.zig | 119 ++++++++++++++++++++++++++++++---- test/behavior/align.zig | 28 +++++++- 7 files changed, 145 insertions(+), 111 deletions(-) diff --git a/lib/compiler/aro/aro/Type.zig b/lib/compiler/aro/aro/Type.zig index 76c9a545da..3588d46e7c 100644 --- a/lib/compiler/aro/aro/Type.zig +++ b/lib/compiler/aro/aro/Type.zig @@ -1116,7 +1116,7 @@ pub fn alignof(ty: Type, comp: *const Compilation) u29 { .bit_int => @min( std.math.ceilPowerOfTwoPromote(u16, (ty.data.int.bits + 7) / 8), - comp.target.maxIntAlignment(), + 16, // comp.target.maxIntAlignment(), please use your own logic for this value as it is implementation-defined ), .float => comp.target.c_type_alignment(.float), diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 3bdb511de8..fa69ca0c46 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -1862,97 +1862,6 @@ pub fn standardDynamicLinkerPath(target: Target) DynamicLinker { return DynamicLinker.standard(target.cpu, target.os.tag, target.abi); } -pub fn maxIntAlignment(target: Target) u16 { - return switch (target.cpu.arch) { - .avr => 1, - .msp430 => 2, - .xcore => 4, - - .arm, - .armeb, - .thumb, - .thumbeb, - .hexagon, - .mips, - .mipsel, - .powerpc, - .powerpcle, - .r600, - .amdgcn, - .riscv32, - .sparc, - .sparcel, - .s390x, - .lanai, - .wasm32, - .wasm64, - => 8, - - .x86 => if (target.ofmt == .c) 16 else return switch (target.os.tag) { - .windows, .uefi => 8, - else => 4, - }, - - // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 - // is a relevant number in three cases: - // 1. Different machine code instruction when loading into SIMD register. - // 2. The C ABI wants 16 for extern structs. - // 3. 16-byte cmpxchg needs 16-byte alignment. - // Same logic for powerpc64, mips64, sparc64. - .powerpc64, - .powerpc64le, - .mips64, - .mips64el, - .sparc64, - => return switch (target.ofmt) { - .c => 16, - else => 8, - }, - - // Even LLVMABIAlignmentOfType(i128) agrees on these targets. - .x86_64, - .aarch64, - .aarch64_be, - .aarch64_32, - .riscv64, - .bpfel, - .bpfeb, - .nvptx, - .nvptx64, - => 16, - - // Below this comment are unverified but based on the fact that C requires - // int128_t to be 16 bytes aligned, it's a safe default. - .spu_2, - .csky, - .arc, - .m68k, - .tce, - .tcele, - .le32, - .amdil, - .hsail, - .spir, - .kalimba, - .renderscript32, - .spirv, - .spirv32, - .shave, - .le64, - .amdil64, - .hsail64, - .spir64, - .renderscript64, - .ve, - .spirv64, - .dxil, - .loongarch32, - .loongarch64, - .xtensa, - => 16, - }; -} - pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 { switch (abi) { .gnux32, .muslx32, .gnuabin32, .gnuilp32 => return 32, diff --git a/src/codegen/c/Type.zig b/src/codegen/c/Type.zig index e6a9dc69a3..d38cd5d400 100644 --- a/src/codegen/c/Type.zig +++ b/src/codegen/c/Type.zig @@ -1312,10 +1312,10 @@ pub const Pool = struct { }, else => { const target = &mod.resolved_target.result; - const abi_align = Type.intAbiAlignment(int_info.bits, target.*); + const abi_align = Type.intAbiAlignment(int_info.bits, target.*, false); const abi_align_bytes = abi_align.toByteUnits().?; const array_ctype = try pool.getArray(allocator, .{ - .len = @divExact(Type.intAbiSize(int_info.bits, target.*), abi_align_bytes), + .len = @divExact(Type.intAbiSize(int_info.bits, target.*, false), abi_align_bytes), .elem_ctype = try pool.fromIntInfo(allocator, .{ .signedness = .unsigned, .bits = @intCast(abi_align_bytes * 8), @@ -1443,7 +1443,7 @@ pub const Pool = struct { .name = .{ .index = .len }, .ctype = CType.usize, .alignas = AlignAs.fromAbiAlignment( - Type.intAbiAlignment(target.ptrBitWidth(), target.*), + Type.intAbiAlignment(target.ptrBitWidth(), target.*, false), ), }, }; @@ -1545,7 +1545,7 @@ pub const Pool = struct { .name = .{ .index = .len }, .ctype = CType.usize, .alignas = AlignAs.fromAbiAlignment( - Type.intAbiAlignment(target.ptrBitWidth(), target.*), + Type.intAbiAlignment(target.ptrBitWidth(), target.*, false), ), }, }; @@ -1665,7 +1665,7 @@ pub const Pool = struct { .name = .{ .index = .@"error" }, .ctype = error_set_ctype, .alignas = AlignAs.fromAbiAlignment( - Type.intAbiAlignment(error_set_bits, target.*), + Type.intAbiAlignment(error_set_bits, target.*, false), ), }, .{ diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 458c187fec..877b2365c8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -609,7 +609,7 @@ const DataLayoutBuilder = struct { switch (kind) { .integer => { if (self.target.ptrBitWidth() <= 16 and size >= 128) return; - abi = @min(abi, self.target.maxIntAlignment() * 8); + abi = @min(abi, Type.maxIntAlignment(self.target, true) * 8); switch (self.target.cpu.arch) { .aarch64, .aarch64_be, diff --git a/src/link/C.zig b/src/link/C.zig index 63faf827b0..07814c9e71 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -383,7 +383,9 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { .msvc => try writer.writeAll("#define ZIG_TARGET_ABI_MSVC\n"), else => {}, } - try writer.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{target.maxIntAlignment()}); + try writer.print("#define ZIG_TARGET_MAX_INT_ALIGNMENT {d}\n", .{ + Type.maxIntAlignment(target, false), + }); return defines; } diff --git a/src/type.zig b/src/type.zig index 5fc6e962c4..9fe95f99f7 100644 --- a/src/type.zig +++ b/src/type.zig @@ -883,6 +883,7 @@ pub const Type = struct { strat: AbiAlignmentAdvancedStrat, ) Module.CompileError!AbiAlignmentAdvanced { const target = mod.getTarget(); + const use_llvm = mod.comp.config.use_llvm; const ip = &mod.intern_pool; const opt_sema = switch (strat) { @@ -895,7 +896,7 @@ pub const Type = struct { else => switch (ip.indexToKey(ty.toIntern())) { .int_type => |int_type| { if (int_type.bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; - return .{ .scalar = intAbiAlignment(int_type.bits, target) }; + return .{ .scalar = intAbiAlignment(int_type.bits, target, use_llvm) }; }, .ptr_type, .anyframe_type => { return .{ .scalar = ptrAbiAlignment(target) }; @@ -941,7 +942,7 @@ pub const Type = struct { .error_set_type, .inferred_error_set_type => { const bits = mod.errorSetBits(); if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; - return .{ .scalar = intAbiAlignment(bits, target) }; + return .{ .scalar = intAbiAlignment(bits, target, use_llvm) }; }, // represents machine code; not a pointer @@ -962,7 +963,7 @@ pub const Type = struct { .usize, .isize, - => return .{ .scalar = intAbiAlignment(target.ptrBitWidth(), target) }, + => return .{ .scalar = intAbiAlignment(target.ptrBitWidth(), target, use_llvm) }, .export_options, .extern_options, @@ -1001,7 +1002,7 @@ pub const Type = struct { .anyerror, .adhoc_inferred_error_set => { const bits = mod.errorSetBits(); if (bits == 0) return AbiAlignmentAdvanced{ .scalar = .@"1" }; - return .{ .scalar = intAbiAlignment(bits, target) }; + return .{ .scalar = intAbiAlignment(bits, target, use_llvm) }; }, .void, @@ -1216,6 +1217,7 @@ pub const Type = struct { strat: AbiAlignmentAdvancedStrat, ) Module.CompileError!AbiSizeAdvanced { const target = mod.getTarget(); + const use_llvm = mod.comp.config.use_llvm; const ip = &mod.intern_pool; switch (ty.toIntern()) { @@ -1224,7 +1226,7 @@ pub const Type = struct { else => switch (ip.indexToKey(ty.toIntern())) { .int_type => |int_type| { if (int_type.bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; - return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target) }; + return AbiSizeAdvanced{ .scalar = intAbiSize(int_type.bits, target, use_llvm) }; }, .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .Slice => return .{ .scalar = @divExact(target.ptrBitWidth(), 8) * 2 }, @@ -1286,7 +1288,7 @@ pub const Type = struct { .error_set_type, .inferred_error_set_type => { const bits = mod.errorSetBits(); if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; - return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target) }; + return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target, use_llvm) }; }, .error_union_type => |error_union_type| { @@ -1384,7 +1386,7 @@ pub const Type = struct { .anyerror, .adhoc_inferred_error_set => { const bits = mod.errorSetBits(); if (bits == 0) return AbiSizeAdvanced{ .scalar = 0 }; - return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target) }; + return AbiSizeAdvanced{ .scalar = intAbiSize(bits, target, use_llvm) }; }, .prefetch_options => unreachable, // missing call to resolveTypeFields @@ -1533,17 +1535,112 @@ pub const Type = struct { return Alignment.fromNonzeroByteUnits(@divExact(target.ptrBitWidth(), 8)); } - pub fn intAbiSize(bits: u16, target: Target) u64 { - return intAbiAlignment(bits, target).forward(@as(u16, @intCast((@as(u17, bits) + 7) / 8))); + pub fn intAbiSize(bits: u16, target: Target, use_llvm: bool) u64 { + return intAbiAlignment(bits, target, use_llvm).forward(@as(u16, @intCast((@as(u17, bits) + 7) / 8))); } - pub fn intAbiAlignment(bits: u16, target: Target) Alignment { + pub fn intAbiAlignment(bits: u16, target: Target, use_llvm: bool) Alignment { return Alignment.fromByteUnits(@min( std.math.ceilPowerOfTwoPromote(u16, @as(u16, @intCast((@as(u17, bits) + 7) / 8))), - target.maxIntAlignment(), + maxIntAlignment(target, use_llvm), )); } + pub fn maxIntAlignment(target: std.Target, use_llvm: bool) u16 { + return switch (target.cpu.arch) { + .avr => 1, + .msp430 => 2, + .xcore => 4, + + .arm, + .armeb, + .thumb, + .thumbeb, + .hexagon, + .mips, + .mipsel, + .powerpc, + .powerpcle, + .r600, + .amdgcn, + .riscv32, + .sparc, + .sparcel, + .s390x, + .lanai, + .wasm32, + .wasm64, + => 8, + + .x86 => if (target.ofmt == .c) 16 else return switch (target.os.tag) { + .windows, .uefi => 8, + else => 4, + }, + + // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 + // is a relevant number in three cases: + // 1. Different machine code instruction when loading into SIMD register. + // 2. The C ABI wants 16 for extern structs. + // 3. 16-byte cmpxchg needs 16-byte alignment. + // Same logic for powerpc64, mips64, sparc64. + .powerpc64, + .powerpc64le, + .mips64, + .mips64el, + .sparc64, + => switch (target.ofmt) { + .c => 16, + else => 8, + }, + + .x86_64 => switch (target_util.zigBackend(target, use_llvm)) { + .stage2_x86_64 => 8, + else => 16, + }, + + // Even LLVMABIAlignmentOfType(i128) agrees on these targets. + .aarch64, + .aarch64_be, + .aarch64_32, + .riscv64, + .bpfel, + .bpfeb, + .nvptx, + .nvptx64, + => 16, + + // Below this comment are unverified but based on the fact that C requires + // int128_t to be 16 bytes aligned, it's a safe default. + .spu_2, + .csky, + .arc, + .m68k, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .kalimba, + .renderscript32, + .spirv, + .spirv32, + .shave, + .le64, + .amdil64, + .hsail64, + .spir64, + .renderscript64, + .ve, + .spirv64, + .dxil, + .loongarch32, + .loongarch64, + .xtensa, + => 16, + }; + } + pub fn bitSize(ty: Type, mod: *Module) u64 { return bitSizeAdvanced(ty, mod, null) catch unreachable; } diff --git a/test/behavior/align.zig b/test/behavior/align.zig index f8ccf2f306..53c7455dbf 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -184,6 +184,33 @@ test "alignment and size of structs with 128-bit fields" { }, }, + .x86_64 => switch (builtin.zig_backend) { + .stage2_x86_64 => .{ + .a_align = 8, + .a_size = 16, + + .b_align = 16, + .b_size = 32, + + .u128_align = 8, + .u128_size = 16, + .u129_align = 8, + .u129_size = 24, + }, + else => .{ + .a_align = 16, + .a_size = 16, + + .b_align = 16, + .b_size = 32, + + .u128_align = 16, + .u128_size = 16, + .u129_align = 16, + .u129_size = 32, + }, + }, + .aarch64, .aarch64_be, .aarch64_32, @@ -192,7 +219,6 @@ test "alignment and size of structs with 128-bit fields" { .bpfeb, .nvptx, .nvptx64, - .x86_64, => .{ .a_align = 16, .a_size = 16,