about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-02-18 14:21:29 +0100
committerRalf Jung <post@ralfj.de>2025-02-19 08:41:19 +0100
commit73b6482ead710656c14fbb86011aa0e7d65461fd (patch)
tree252ae690b81cadbab5ea0d92b1f16f7ba9752425
parent608e228ca9a1e57336ca5c16e5722a8ac8284d8d (diff)
downloadrust-73b6482ead710656c14fbb86011aa0e7d65461fd.tar.gz
rust-73b6482ead710656c14fbb86011aa0e7d65461fd.zip
x86_win64 ABI: do not use xmm0 with softfloat ABI
-rw-r--r--compiler/rustc_target/src/callconv/x86_win64.rs16
-rw-r--r--tests/assembly/x86_64-windows-i128-abi.rs26
-rw-r--r--tests/codegen/i128-x86-callconv.rs38
3 files changed, 59 insertions, 21 deletions
diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs
index 23ef2cf8284..2ef5127de04 100644
--- a/compiler/rustc_target/src/callconv/x86_win64.rs
+++ b/compiler/rustc_target/src/callconv/x86_win64.rs
@@ -1,11 +1,11 @@
 use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size};
 
 use crate::callconv::{ArgAbi, FnAbi, Reg};
-use crate::spec::HasTargetSpec;
+use crate::spec::{HasTargetSpec, RustcAbi};
 
 // Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing
 
-pub(crate) fn compute_abi_info<Ty>(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) {
+pub(crate) fn compute_abi_info<Ty>(cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) {
     let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| {
         match a.layout.backend_repr {
             BackendRepr::Uninhabited | BackendRepr::Memory { sized: false } => {}
@@ -24,10 +24,14 @@ pub(crate) fn compute_abi_info<Ty>(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<
             }
             BackendRepr::Scalar(scalar) => {
                 if is_ret && matches!(scalar.primitive(), Primitive::Int(Integer::I128, _)) {
-                    // `i128` is returned in xmm0 by Clang and GCC
-                    // FIXME(#134288): This may change for the `-msvc` targets in the future.
-                    let reg = Reg { kind: RegKind::Vector, size: Size::from_bits(128) };
-                    a.cast_to(reg);
+                    if cx.target_spec().rustc_abi == Some(RustcAbi::X86Softfloat) {
+                        // Use the native `i128` LLVM type for the softfloat ABI -- in other words, adjust nothing.
+                    } else {
+                        // `i128` is returned in xmm0 by Clang and GCC
+                        // FIXME(#134288): This may change for the `-msvc` targets in the future.
+                        let reg = Reg { kind: RegKind::Vector, size: Size::from_bits(128) };
+                        a.cast_to(reg);
+                    }
                 } else if a.layout.size.bytes() > 8
                     && !matches!(scalar.primitive(), Primitive::Float(Float::F128))
                 {
diff --git a/tests/assembly/x86_64-windows-i128-abi.rs b/tests/assembly/x86_64-windows-i128-abi.rs
new file mode 100644
index 00000000000..d2aefb7daa6
--- /dev/null
+++ b/tests/assembly/x86_64-windows-i128-abi.rs
@@ -0,0 +1,26 @@
+//@ assembly-output: emit-asm
+//@ add-core-stubs
+//@ revisions: msvc softfloat
+//@ compile-flags: -Copt-level=3
+//@[msvc] compile-flags: --target x86_64-pc-windows-msvc
+//@[msvc] needs-llvm-components: x86
+//@[softfloat] compile-flags: --target x86_64-unknown-uefi
+//@[softfloat] needs-llvm-components: x86
+
+#![feature(no_core)]
+#![no_core]
+#![crate_type = "lib"]
+
+extern crate minicore;
+use minicore::*;
+
+// CHECK-LABEL: ret_i128
+// Hardfloat targets return via xmm0, softfloat targets via rax and rdx.
+// msvc: movaps {{.*}}, %xmm0
+// softfloat: movq (%[[INPUT:.*]]), %rax
+// softfloat-NEXT: movq 8(%[[INPUT]]), %rdx
+// CHECK-NEXT: retq
+#[no_mangle]
+pub extern "C" fn ret_i128(x: &i128) -> i128 {
+    *x
+}
diff --git a/tests/codegen/i128-x86-callconv.rs b/tests/codegen/i128-x86-callconv.rs
index 9a9c9002fc0..41c30c09c1a 100644
--- a/tests/codegen/i128-x86-callconv.rs
+++ b/tests/codegen/i128-x86-callconv.rs
@@ -4,13 +4,18 @@
 //@ compile-flags: -Copt-level=1
 
 //@ add-core-stubs
-//@ revisions: MSVC MINGW
+//@ revisions: MSVC MINGW softfloat
 //@ [MSVC] needs-llvm-components: x86
-//@ [MINGW] needs-llvm-components: x86
 //@ [MSVC] compile-flags: --target x86_64-pc-windows-msvc
-//@ [MINGW] compile-flags: --target x86_64-pc-windows-gnu
+// Use `WIN` as a common prefix for MSVC and MINGW but *not* the softfloat test.
 //@ [MSVC] filecheck-flags: --check-prefix=WIN
+//@ [MINGW] needs-llvm-components: x86
+//@ [MINGW] compile-flags: --target x86_64-pc-windows-gnu
 //@ [MINGW] filecheck-flags: --check-prefix=WIN
+// The `x86_64-unknown-uefi` target also uses the Windows calling convention,
+// but does not have SSE registers available.
+//@ [softfloat] needs-llvm-components: x86
+//@ [softfloat] compile-flags: --target x86_64-unknown-uefi
 
 #![crate_type = "lib"]
 #![no_std]
@@ -28,24 +33,26 @@ extern "C" {
 pub extern "C" fn pass(_arg0: u32, arg1: i128) {
     // CHECK-LABEL: @pass(
     // i128 is passed indirectly on Windows. It should load the pointer to the stack and pass
-    // a pointer to that allocation.
-    // WIN-SAME: %_arg0, ptr{{.*}} %arg1)
-    // WIN: [[PASS:%[_0-9]+]] = alloca [16 x i8], align 16
-    // WIN: [[LOADED:%[_0-9]+]] = load i128, ptr %arg1
-    // WIN: store i128 [[LOADED]], ptr [[PASS]]
-    // WIN: call void @extern_call
+    // a pointer to that allocation. The softfloat ABI works the same.
+    // CHECK-SAME: %_arg0, ptr{{.*}} %arg1)
+    // CHECK: [[PASS:%[_0-9]+]] = alloca [16 x i8], align 16
+    // CHECK: [[LOADED:%[_0-9]+]] = load i128, ptr %arg1
+    // CHECK: store i128 [[LOADED]], ptr [[PASS]]
+    // CHECK: call void @extern_call
     unsafe { extern_call(arg1) };
 }
 
 // Check that we produce the correct return ABI
 #[no_mangle]
 pub extern "C" fn ret(_arg0: u32, arg1: i128) -> i128 {
-    // CHECK-LABEL: @ret(
+    // WIN-LABEL: @ret(
     // i128 is returned in xmm0 on Windows
     // FIXME(#134288): This may change for the `-msvc` targets in the future.
     // WIN-SAME: i32{{.*}} %_arg0, ptr{{.*}} %arg1)
     // WIN: [[LOADED:%[_0-9]+]] = load <16 x i8>, ptr %arg1
     // WIN-NEXT: ret <16 x i8> [[LOADED]]
+    // The softfloat ABI returns this indirectly.
+    // softfloat-LABEL: i128 @ret(i32{{.*}} %_arg0, ptr{{.*}} %arg1)
     arg1
 }
 
@@ -57,6 +64,7 @@ pub extern "C" fn forward(dst: *mut i128) {
     // WIN: [[RETURNED:%[_0-9]+]] = tail call <16 x i8> @extern_ret()
     // WIN: store <16 x i8> [[RETURNED]], ptr %dst
     // WIN: ret void
+    // softfloat: [[RETURNED:%[_0-9]+]] = tail call {{.*}}i128 @extern_ret()
     unsafe { *dst = extern_ret() };
 }
 
@@ -70,10 +78,10 @@ struct RetAggregate {
 pub extern "C" fn ret_aggregate(_arg0: u32, arg1: i128) -> RetAggregate {
     // CHECK-LABEL: @ret_aggregate(
     // Aggregates should also be returned indirectly
-    // WIN-SAME: ptr{{.*}}sret([32 x i8]){{.*}}[[RET:%[_0-9]+]], i32{{.*}}%_arg0, ptr{{.*}}%arg1)
-    // WIN: [[LOADED:%[_0-9]+]] = load i128, ptr %arg1
-    // WIN: [[GEP:%[_0-9]+]] = getelementptr{{.*}}, ptr [[RET]]
-    // WIN: store i128 [[LOADED]], ptr [[GEP]]
-    // WIN: ret void
+    // CHECK-SAME: ptr{{.*}}sret([32 x i8]){{.*}}[[RET:%[_0-9]+]], i32{{.*}}%_arg0, ptr{{.*}}%arg1)
+    // CHECK: [[LOADED:%[_0-9]+]] = load i128, ptr %arg1
+    // CHECK: [[GEP:%[_0-9]+]] = getelementptr{{.*}}, ptr [[RET]]
+    // CHECK: store i128 [[LOADED]], ptr [[GEP]]
+    // CHECK: ret void
     RetAggregate { a: 1, b: arg1 }
 }