diff --git a/lib/compiler_rt/truncdfhf2.zig b/lib/compiler_rt/truncdfhf2.zig index e15a2202a165..10f13d504788 100644 --- a/lib/compiler_rt/truncdfhf2.zig +++ b/lib/compiler_rt/truncdfhf2.zig @@ -6,9 +6,8 @@ pub const panic = common.panic; comptime { if (common.want_aeabi) { @export(&__aeabi_d2h, .{ .name = "__aeabi_d2h", .linkage = common.linkage, .visibility = common.visibility }); - } else { - @export(&__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = common.linkage, .visibility = common.visibility }); } + @export(&__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = common.linkage, .visibility = common.visibility }); } pub fn __truncdfhf2(a: f64) callconv(.C) common.F16T(f64) { diff --git a/lib/std/Target.zig b/lib/std/Target.zig index d215e29fc88e..60f75e25fad0 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -765,12 +765,13 @@ pub const Abi = enum { pub inline fn floatAbi(abi: Abi) FloatAbi { return switch (abi) { - .gnueabihf, - .eabihf, - .musleabihf, - => .hard, - .ohos => .soft, - else => .soft, + .eabi, + .gnueabi, + .musleabi, + .gnusf, + .ohos, + => .soft, + else => .hard, }; } }; @@ -1645,7 +1646,7 @@ pub const FloatAbi = enum { soft, }; -pub inline fn getFloatAbi(target: Target) FloatAbi { +pub inline fn floatAbi(target: Target) FloatAbi { return target.abi.floatAbi(); } diff --git a/lib/std/math/gamma.zig b/lib/std/math/gamma.zig index ce37db496215..5ac041e195df 100644 --- a/lib/std/math/gamma.zig +++ b/lib/std/math/gamma.zig @@ -3,6 +3,7 @@ // // https://git.musl-libc.org/cgit/musl/tree/src/math/tgamma.c +const builtin = @import("builtin"); const std = @import("../std.zig"); /// Returns the gamma function of x, @@ -262,6 +263,8 @@ test gamma { } test "gamma.special" { + if (builtin.cpu.arch.isArmOrThumb() and builtin.target.floatAbi() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 + inline for (&.{ f32, f64 }) |T| { try expect(std.math.isNan(gamma(T, -std.math.nan(T)))); try expect(std.math.isNan(gamma(T, std.math.nan(T)))); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 4de1e2065671..046bd3854e45 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -384,6 +384,12 @@ pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { query.cpu_features_add, query.cpu_features_sub, ); + + // https://github.com/llvm/llvm-project/issues/105978 + if (result.cpu.arch.isArmOrThumb() and result.floatAbi() == .soft) { + result.cpu.features.removeFeature(@intFromEnum(Target.arm.Feature.vfp2)); + } + return result; } diff --git a/lib/std/zig/target.zig b/lib/std/zig/target.zig index d0d68acd70fd..b60586da8519 100644 --- a/lib/std/zig/target.zig +++ b/lib/std/zig/target.zig @@ -46,17 +46,20 @@ pub const available_libcs = [_]ArchOsAbi{ .{ .arch = .mips64, .os = .linux, .abi = .musl }, .{ .arch = .mipsel, .os = .linux, .abi = .gnueabi }, .{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf }, - .{ .arch = .mipsel, .os = .linux, .abi = .musl }, + .{ .arch = .mipsel, .os = .linux, .abi = .musleabi }, + .{ .arch = .mipsel, .os = .linux, .abi = .musleabihf }, .{ .arch = .mips, .os = .linux, .abi = .gnueabi }, .{ .arch = .mips, .os = .linux, .abi = .gnueabihf }, - .{ .arch = .mips, .os = .linux, .abi = .musl }, + .{ .arch = .mips, .os = .linux, .abi = .musleabi }, + .{ .arch = .mips, .os = .linux, .abi = .musleabihf }, .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 19, .patch = 0 } }, .{ .arch = .powerpc64le, .os = .linux, .abi = .musl }, .{ .arch = .powerpc64, .os = .linux, .abi = .gnu }, .{ .arch = .powerpc64, .os = .linux, .abi = .musl }, .{ .arch = .powerpc, .os = .linux, .abi = .gnueabi }, .{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf }, - .{ .arch = .powerpc, .os = .linux, .abi = .musl }, + .{ .arch = .powerpc, .os = .linux, .abi = .musleabi }, + .{ .arch = .powerpc, .os = .linux, .abi = .musleabihf }, .{ .arch = .riscv32, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 33, .patch = 0 } }, .{ .arch = .riscv32, .os = .linux, .abi = .musl }, .{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } }, diff --git a/src/Compilation.zig b/src/Compilation.zig index 54fa1208569f..bd93b2061b48 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5484,6 +5484,9 @@ pub fn addCCArgs( const is_enabled = target.cpu.features.isEnabled(index); if (feature.llvm_name) |llvm_name| { + // We communicate float ABI to Clang through the dedicated options further down. + if (std.mem.eql(u8, llvm_name, "soft-float")) continue; + argv.appendSliceAssumeCapacity(&[_][]const u8{ "-Xclang", "-target-feature", "-Xclang" }); const plus_or_minus = "-+"[@intFromBool(is_enabled)]; const arg = try std.fmt.allocPrint(arena, "{c}{s}", .{ plus_or_minus, llvm_name }); @@ -5705,10 +5708,6 @@ pub fn addCCArgs( if (target.cpu.model.llvm_name) |llvm_name| { try argv.append(try std.fmt.allocPrint(arena, "-march={s}", .{llvm_name})); } - - if (std.Target.mips.featureSetHas(target.cpu.features, .soft_float)) { - try argv.append("-msoft-float"); - } }, else => { // TODO @@ -5751,6 +5750,28 @@ pub fn addCCArgs( try argv.append(try std.fmt.allocPrint(arena, "-mabi={s}", .{mabi})); } + // We might want to support -mfloat-abi=softfp for Arm and CSKY here in the future. + if (target_util.clangSupportsFloatAbiArg(target)) { + const fabi = @tagName(target.floatAbi()); + + try argv.append(switch (target.cpu.arch) { + // For whatever reason, Clang doesn't support `-mfloat-abi` for s390x. + .s390x => try std.fmt.allocPrint(arena, "-m{s}-float", .{fabi}), + else => try std.fmt.allocPrint(arena, "-mfloat-abi={s}", .{fabi}), + }); + } + + if (target_util.clangSupportsNoImplicitFloatArg(target) and target.floatAbi() == .soft) { + try argv.append("-mno-implicit-float"); + } + + // https://github.com/llvm/llvm-project/issues/105972 + if (target.cpu.arch.isPowerPC() and target.floatAbi() == .soft) { + try argv.append("-D__NO_FPRS__"); + try argv.append("-D_SOFT_FLOAT"); + try argv.append("-D_SOFT_DOUBLE"); + } + if (out_dep_path) |p| { try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a46d875b342b..5ba80e3c4e34 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1284,8 +1284,7 @@ pub const Object = struct { .large => .Large, }; - // TODO handle float ABI better- it should depend on the ABI portion of std.Target - const float_abi: llvm.ABIType = .Default; + const float_abi: llvm.ABIType = if (comp.root_mod.resolved_target.result.floatAbi() == .hard) .Hard else .Soft; var target_machine = llvm.TargetMachine.create( target, @@ -3109,6 +3108,30 @@ pub const Object = struct { .value = .empty, } }, &o.builder); } + if (target.floatAbi() == .soft) { + // `use-soft-float` means "use software routines for floating point computations". In + // other words, it configures how LLVM lowers basic float instructions like `fcmp`, + // `fadd`, etc. The float calling convention is configured on `TargetMachine` and is + // mostly an orthogonal concept, although obviously we do need hardware float operations + // to actually be able to pass float values in float registers. + // + // Ideally, we would support something akin to the `-mfloat-abi=softfp` option that GCC + // and Clang support for Arm32 and CSKY. We don't currently expose such an option in + // Zig, and using CPU features as the source of truth for this makes for a miserable + // user experience since people expect e.g. `arm-linux-gnueabi` to mean full soft float + // unless the compiler has explicitly been told otherwise. (And note that our baseline + // CPU models almost all include FPU features!) + // + // Revisit this at some point. + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("use-soft-float"), + .value = try o.builder.string("true"), + } }, &o.builder); + + // This prevents LLVM from using FPU/SIMD code for things like `memcpy`. As for the + // above, this should be revisited if `softfp` support is added. + try attributes.addFnAttr(.noimplicitfloat, &o.builder); + } } fn resolveGlobalUav( @@ -12400,6 +12423,11 @@ fn backendSupportsF16(target: std.Target) bool { .mips64el, .s390x, => false, + .arm, + .armeb, + .thumb, + .thumbeb, + => target.floatAbi() == .soft or std.Target.arm.featureSetHas(target.cpu.features, .fp_armv8), .aarch64, .aarch64_be, => std.Target.aarch64.featureSetHas(target.cpu.features, .fp_armv8), @@ -12422,6 +12450,11 @@ fn backendSupportsF128(target: std.Target) bool { .powerpc64, .powerpc64le, => target.os.tag != .aix, + .arm, + .armeb, + .thumb, + .thumbeb, + => target.floatAbi() == .soft or std.Target.arm.featureSetHas(target.cpu.features, .fp_armv8), .aarch64, .aarch64_be, => std.Target.aarch64.featureSetHas(target.cpu.features, .fp_armv8), diff --git a/src/musl.zig b/src/musl.zig index acf590203349..5ddbcb66522c 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -387,6 +387,7 @@ fn addCcArgs( "-fno-builtin", "-fexcess-precision=standard", "-frounding-math", + "-ffp-contract=off", "-fno-strict-aliasing", "-Wa,--noexecstack", "-D_XOPEN_SOURCE=700", diff --git a/src/target.zig b/src/target.zig index 221c2029baf9..59c151bd6e64 100644 --- a/src/target.zig +++ b/src/target.zig @@ -339,6 +339,48 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool { }; } +pub fn clangSupportsFloatAbiArg(target: std.Target) bool { + return switch (target.cpu.arch) { + .arm, + .armeb, + .thumb, + .thumbeb, + .csky, + .mips, + .mipsel, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .s390x, + .sparc, + .sparc64, + => true, + // We use the target triple for LoongArch. + .loongarch32, .loongarch64 => false, + else => false, + }; +} + +pub fn clangSupportsNoImplicitFloatArg(target: std.Target) bool { + return switch (target.cpu.arch) { + .aarch64, + .aarch64_be, + .arm, + .armeb, + .thumb, + .thumbeb, + .riscv32, + .riscv64, + .x86, + .x86_64, + => true, + else => false, + }; +} + pub fn needUnwindTables(target: std.Target) bool { return target.os.tag == .windows or target.isDarwin() or std.debug.Dwarf.abi.supportsUnwinding(target); } diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index f9f2579b0aca..d799c9133119 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -126,6 +126,7 @@ test "cmp f16" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.cpu.arch.isArmOrThumb() and builtin.target.floatAbi() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 try testCmp(f16); try comptime testCmp(f16); @@ -134,6 +135,7 @@ test "cmp f16" { test "cmp f32/f64" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; + if (builtin.cpu.arch.isArmOrThumb() and builtin.target.floatAbi() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 try testCmp(f32); try comptime testCmp(f32); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 7837ffb74b4b..c658ea3081df 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -1597,6 +1597,7 @@ test "NaN comparison" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.cpu.arch.isArmOrThumb() and builtin.target.floatAbi() == .soft) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/21234 try testNanEqNan(f16); try testNanEqNan(f32); diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index cee9f168f193..9baba8485085 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1442,7 +1442,14 @@ test "store vector with memset" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_llvm) { + // LLVM 16 ERROR: "Converting bits to bytes lost precision" + // https://github.com/ziglang/zig/issues/16177 switch (builtin.target.cpu.arch) { + .arm, + .armeb, + .thumb, + .thumbeb, + => if (builtin.target.floatAbi() == .soft) return error.SkipZigTest, .wasm32, .mips, .mipsel, @@ -1451,11 +1458,7 @@ test "store vector with memset" { .riscv64, .powerpc, .powerpc64, - => { - // LLVM 16 ERROR: "Converting bits to bytes lost precision" - // https://github.com/ziglang/zig/issues/16177 - return error.SkipZigTest; - }, + => return error.SkipZigTest, else => {}, } } diff --git a/test/tests.zig b/test/tests.zig index 7d5be7bbf74c..f07f67c2e1c6 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -291,23 +291,49 @@ const test_targets = blk: { }, .{ - .target = std.Target.Query.parse(.{ - .arch_os_abi = "arm-linux-none", - .cpu_features = "generic+v8a", - }) catch unreachable, + .target = .{ + .cpu_arch = .arm, + .os_tag = .linux, + .abi = .eabi, + }, + }, + .{ + .target = .{ + .cpu_arch = .arm, + .os_tag = .linux, + .abi = .eabihf, + }, + }, + .{ + .target = .{ + .cpu_arch = .arm, + .os_tag = .linux, + .abi = .musleabi, + }, + .link_libc = true, + }, + .{ + .target = .{ + .cpu_arch = .arm, + .os_tag = .linux, + .abi = .musleabihf, + }, + .link_libc = true, }, .{ - .target = std.Target.Query.parse(.{ - .arch_os_abi = "arm-linux-musleabihf", - .cpu_features = "generic+v8a", - }) catch unreachable, + .target = .{ + .cpu_arch = .arm, + .os_tag = .linux, + .abi = .gnueabi, + }, .link_libc = true, }, .{ - .target = std.Target.Query.parse(.{ - .arch_os_abi = "arm-linux-gnueabihf", - .cpu_features = "generic+v8a", - }) catch unreachable, + .target = .{ + .cpu_arch = .arm, + .os_tag = .linux, + .abi = .gnueabihf, + }, .link_libc = true, }, @@ -315,7 +341,7 @@ const test_targets = blk: { .target = .{ .cpu_arch = .mips, .os_tag = .linux, - .abi = .none, + .abi = .eabi, }, .slow_backend = true, }, @@ -323,7 +349,33 @@ const test_targets = blk: { .target = .{ .cpu_arch = .mips, .os_tag = .linux, - .abi = .musl, + .abi = .eabihf, + }, + .slow_backend = true, + }, + .{ + .target = .{ + .cpu_arch = .mips, + .os_tag = .linux, + .abi = .musleabi, + }, + .link_libc = true, + .slow_backend = true, + }, + .{ + .target = .{ + .cpu_arch = .mips, + .os_tag = .linux, + .abi = .musleabihf, + }, + .link_libc = true, + .slow_backend = true, + }, + .{ + .target = .{ + .cpu_arch = .mips, + .os_tag = .linux, + .abi = .gnueabi, }, .link_libc = true, .slow_backend = true, @@ -342,7 +394,7 @@ const test_targets = blk: { .target = .{ .cpu_arch = .mipsel, .os_tag = .linux, - .abi = .none, + .abi = .eabi, }, .slow_backend = true, }, @@ -350,7 +402,33 @@ const test_targets = blk: { .target = .{ .cpu_arch = .mipsel, .os_tag = .linux, - .abi = .musl, + .abi = .eabihf, + }, + .slow_backend = true, + }, + .{ + .target = .{ + .cpu_arch = .mipsel, + .os_tag = .linux, + .abi = .musleabi, + }, + .link_libc = true, + .slow_backend = true, + }, + .{ + .target = .{ + .cpu_arch = .mipsel, + .os_tag = .linux, + .abi = .musleabihf, + }, + .link_libc = true, + .slow_backend = true, + }, + .{ + .target = .{ + .cpu_arch = .mipsel, + .os_tag = .linux, + .abi = .gnueabi, }, .link_libc = true, .slow_backend = true, @@ -417,14 +495,29 @@ const test_targets = blk: { .target = .{ .cpu_arch = .powerpc, .os_tag = .linux, - .abi = .none, + .abi = .eabi, }, }, .{ .target = .{ .cpu_arch = .powerpc, .os_tag = .linux, - .abi = .musl, + .abi = .eabihf, + }, + }, + .{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .musleabi, + }, + .link_libc = true, + }, + .{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .musleabihf, }, .link_libc = true, }, @@ -661,7 +754,7 @@ const c_abi_targets = [_]CAbiTarget{ .target = .{ .cpu_arch = .mips, .os_tag = .linux, - .abi = .musl, + .abi = .musleabihf, }, }, .{ @@ -682,7 +775,7 @@ const c_abi_targets = [_]CAbiTarget{ .target = .{ .cpu_arch = .powerpc, .os_tag = .linux, - .abi = .musl, + .abi = .musleabihf, }, }, .{