about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEduard-Mihai Burtescu <edy.burt@gmail.com>2019-07-04 19:31:52 +0300
committerEduard-Mihai Burtescu <edy.burt@gmail.com>2019-07-05 13:24:25 +0300
commit050a71b570e49106e8dd6f6282e322220a5edf6f (patch)
tree61d2f42d3a03bdb42c054ad4508f46d777beca69
parentb43eb4235ac43c822d903ad26ed806f34cc1a14a (diff)
downloadrust-050a71b570e49106e8dd6f6282e322220a5edf6f.tar.gz
rust-050a71b570e49106e8dd6f6282e322220a5edf6f.zip
rustc_target: avoid negative register counts in the SysV x86_64 ABI.
-rw-r--r--src/librustc_target/abi/call/x86_64.rs33
-rw-r--r--src/test/auxiliary/rust_test_helpers.c23
-rw-r--r--src/test/run-pass/abi-sysv64-arg-passing.rs36
-rw-r--r--src/test/run-pass/abi/issues/issue-62350-sysv-neg-reg-counts.rs46
4 files changed, 129 insertions, 9 deletions
diff --git a/src/librustc_target/abi/call/x86_64.rs b/src/librustc_target/abi/call/x86_64.rs
index b68c70224c9..cdec65e5d40 100644
--- a/src/librustc_target/abi/call/x86_64.rs
+++ b/src/librustc_target/abi/call/x86_64.rs
@@ -167,20 +167,23 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
     target
 }
 
+const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
+const MAX_SSE_REGS: usize = 8; // XMM0-7
+
 pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
     where Ty: TyLayoutMethods<'a, C> + Copy,
           C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
 {
-    let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
-    let mut sse_regs = 8; // XMM0-7
+    let mut int_regs = MAX_INT_REGS;
+    let mut sse_regs = MAX_SSE_REGS;
 
     let mut x86_64_ty = |arg: &mut ArgType<'a, Ty>, is_arg: bool| {
         let mut cls_or_mem = classify_arg(cx, arg);
 
-        let mut needed_int = 0;
-        let mut needed_sse = 0;
         if is_arg {
             if let Ok(cls) = cls_or_mem {
+                let mut needed_int = 0;
+                let mut needed_sse = 0;
                 for &c in &cls {
                     match c {
                         Some(Class::Int) => needed_int += 1,
@@ -188,8 +191,20 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
                         _ => {}
                     }
                 }
-                if arg.layout.is_aggregate() && (int_regs < needed_int || sse_regs < needed_sse) {
-                    cls_or_mem = Err(Memory);
+                match (int_regs.checked_sub(needed_int), sse_regs.checked_sub(needed_sse)) {
+                    (Some(left_int), Some(left_sse)) => {
+                        int_regs = left_int;
+                        sse_regs = left_sse;
+                    }
+                    _ => {
+                        // Not enough registers for this argument, so it will be
+                        // passed on the stack, but we only mark aggregates
+                        // explicitly as indirect `byval` arguments, as LLVM will
+                        // automatically put immediates on the stack itself.
+                        if arg.layout.is_aggregate() {
+                            cls_or_mem = Err(Memory);
+                        }
+                    }
                 }
             }
         }
@@ -201,14 +216,14 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
                 } else {
                     // `sret` parameter thus one less integer register available
                     arg.make_indirect();
+                    // NOTE(eddyb) return is handled first, so no registers
+                    // should've been used yet.
+                    assert_eq!(int_regs, MAX_INT_REGS);
                     int_regs -= 1;
                 }
             }
             Ok(ref cls) => {
                 // split into sized chunks passed individually
-                int_regs -= needed_int;
-                sse_regs -= needed_sse;
-
                 if arg.layout.is_aggregate() {
                     let size = arg.layout.size;
                     arg.cast_to(cast_target(cls, size))
diff --git a/src/test/auxiliary/rust_test_helpers.c b/src/test/auxiliary/rust_test_helpers.c
index 7f2afd9c571..b95b0ca1a89 100644
--- a/src/test/auxiliary/rust_test_helpers.c
+++ b/src/test/auxiliary/rust_test_helpers.c
@@ -215,6 +215,29 @@ uint64_t get_c_many_params(void *a, void *b, void *c, void *d, struct quad f) {
     return f.c;
 }
 
+struct quad_floats {
+    float a;
+    float b;
+    float c;
+    float d;
+};
+
+float get_c_exhaust_sysv64_ints(
+    void *a,
+    void *b,
+    void *c,
+    void *d,
+    void *e,
+    void *f,
+    // `f` used the last integer register, so `g` goes on the stack.
+    // It also used to bring the "count of available integer registers" down to
+    // `-1` which broke the next SSE-only aggregate argument (`h`) - see #62350.
+    void *g,
+    struct quad_floats h
+) {
+    return h.c;
+}
+
 // Calculates the average of `(x + y) / n` where x: i64, y: f64. There must be exactly n pairs
 // passed as variadic arguments. There are two versions of this function: the
 // variadic one, and the one that takes a `va_list`.
diff --git a/src/test/run-pass/abi-sysv64-arg-passing.rs b/src/test/run-pass/abi-sysv64-arg-passing.rs
index 0b9f9934fa9..fdf0573b5e3 100644
--- a/src/test/run-pass/abi-sysv64-arg-passing.rs
+++ b/src/test/run-pass/abi-sysv64-arg-passing.rs
@@ -20,6 +20,7 @@
 // extern-return-TwoU64s
 // foreign-fn-with-byval
 // issue-28676
+// issue-62350-sysv-neg-reg-counts
 // struct-return
 
 // ignore-android
@@ -83,6 +84,9 @@ mod tests {
     #[derive(Copy, Clone)]
     pub struct Quad { a: u64, b: u64, c: u64, d: u64 }
 
+    #[derive(Copy, Clone)]
+    pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
+
     #[repr(C)]
     #[derive(Copy, Clone)]
     pub struct Floats { a: f64, b: u8, c: f64 }
@@ -108,6 +112,16 @@ mod tests {
         pub fn get_z(x: S) -> u64;
         pub fn get_c_many_params(_: *const (), _: *const (),
                                  _: *const (), _: *const (), f: Quad) -> u64;
+        pub fn get_c_exhaust_sysv64_ints(
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            h: QuadFloats,
+        ) -> f32;
         pub fn rust_dbg_abi_1(q: Quad) -> Quad;
         pub fn rust_dbg_abi_2(f: Floats) -> Floats;
     }
@@ -263,6 +277,27 @@ mod tests {
         test();
     }
 
+    fn test_62350() {
+        use std::ptr;
+        unsafe {
+            let null = ptr::null();
+            let q = QuadFloats {
+                a: 10.2,
+                b: 20.3,
+                c: 30.4,
+                d: 40.5
+            };
+            assert_eq!(
+                get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
+                q.c,
+            );
+        }
+    }
+
+    pub fn issue_62350() {
+        test_62350();
+    }
+
     fn test1() {
         unsafe {
             let q = Quad { a: 0xaaaa_aaaa_aaaa_aaaa,
@@ -321,6 +356,7 @@ fn main() {
     extern_return_twou64s();
     foreign_fn_with_byval();
     issue_28676();
+    issue_62350();
     struct_return();
 }
 
diff --git a/src/test/run-pass/abi/issues/issue-62350-sysv-neg-reg-counts.rs b/src/test/run-pass/abi/issues/issue-62350-sysv-neg-reg-counts.rs
new file mode 100644
index 00000000000..df819306e4a
--- /dev/null
+++ b/src/test/run-pass/abi/issues/issue-62350-sysv-neg-reg-counts.rs
@@ -0,0 +1,46 @@
+// run-pass
+#![allow(dead_code)]
+#![allow(improper_ctypes)]
+
+// ignore-wasm32-bare no libc to test ffi with
+
+#[derive(Copy, Clone)]
+pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
+
+mod rustrt {
+    use super::QuadFloats;
+
+    #[link(name = "rust_test_helpers", kind = "static")]
+    extern {
+        pub fn get_c_exhaust_sysv64_ints(
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            _: *const (),
+            h: QuadFloats,
+        ) -> f32;
+    }
+}
+
+fn test() {
+    unsafe {
+        let null = std::ptr::null();
+        let q = QuadFloats {
+            a: 10.2,
+            b: 20.3,
+            c: 30.4,
+            d: 40.5
+        };
+        assert_eq!(
+            rustrt::get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
+            q.c,
+        );
+    }
+}
+
+pub fn main() {
+    test();
+}