diff --git a/compiler/rustc_target/src/abi/call/sparc64.rs b/compiler/rustc_target/src/abi/call/sparc64.rs
index 39d80c4c7e7..822db6d05bb 100644
--- a/compiler/rustc_target/src/abi/call/sparc64.rs
+++ b/compiler/rustc_target/src/abi/call/sparc64.rs
@@ -1,29 +1,135 @@
 // FIXME: This needs an audit for correctness and completeness.
 
 use crate::abi::call::{
-    ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, RegKind, Uniform,
+    ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Reg, Uniform,
 };
-use crate::abi::{self, HasDataLayout, Size, TyAbiInterface};
+use crate::abi::{self, HasDataLayout, Scalar, Size, TyAbiInterface, TyAndLayout};
 
-fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
+#[derive(Clone, Debug)]
+pub struct Sdata {
+    pub prefix: [Option<Reg>; 8],
+    pub prefix_index: usize,
+    pub last_offset: Size,
+    pub has_float: bool,
+    pub arg_attribute: ArgAttribute,
+}
+
+fn arg_scalar<C>(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata
+where
+    C: HasDataLayout,
+{
+    let dl = cx.data_layout();
+
+    if scalar.value != abi::F32 && scalar.value != abi::F64 {
+        return data;
+    }
+
+    data.has_float = true;
+
+    if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset {
+        if data.prefix_index == data.prefix.len() {
+            return data;
+        }
+        data.prefix[data.prefix_index] = Some(Reg::i32());
+        data.prefix_index += 1;
+        data.last_offset = data.last_offset + Reg::i32().size;
+    }
+
+    for _ in 0..((offset - data.last_offset).bits() / 64)
+        .min((data.prefix.len() - data.prefix_index) as u64)
+    {
+        data.prefix[data.prefix_index] = Some(Reg::i64());
+        data.prefix_index += 1;
+        data.last_offset = data.last_offset + Reg::i64().size;
+    }
+
+    if data.last_offset < offset {
+        if data.prefix_index == data.prefix.len() {
+            return data;
+        }
+        data.prefix[data.prefix_index] = Some(Reg::i32());
+        data.prefix_index += 1;
+        data.last_offset = data.last_offset + Reg::i32().size;
+    }
+
+    if data.prefix_index == data.prefix.len() {
+        return data;
+    }
+
+    if scalar.value == abi::F32 {
+        data.arg_attribute = ArgAttribute::InReg;
+        data.prefix[data.prefix_index] = Some(Reg::f32());
+        data.last_offset = offset + Reg::f32().size;
+    } else {
+        data.prefix[data.prefix_index] = Some(Reg::f64());
+        data.last_offset = offset + Reg::f64().size;
+    }
+    data.prefix_index += 1;
+    return data;
+}
+
+fn arg_scalar_pair<C>(
+    cx: &C,
+    scalar1: &Scalar,
+    scalar2: &Scalar,
+    mut offset: Size,
+    mut data: Sdata,
+) -> Sdata
+where
+    C: HasDataLayout,
+{
+    data = arg_scalar(cx, &scalar1, offset, data);
+    if scalar1.value == abi::F32 {
+        offset += Reg::f32().size;
+    } else if scalar2.value == abi::F64 {
+        offset += Reg::f64().size;
+    } else if let abi::Int(i, _signed) = scalar1.value {
+        offset += i.size();
+    } else if scalar1.value == abi::Pointer {
+        offset = offset + Reg::i64().size;
+    }
+
+    if (offset.raw % 4) != 0 && (scalar2.value == abi::F32 || scalar2.value == abi::F64) {
+        offset.raw += 4 - (offset.raw % 4);
+    }
+    data = arg_scalar(cx, &scalar2, offset, data);
+    return data;
+}
+
+fn parse_structure<'a, Ty, C>(
+    cx: &C,
+    layout: TyAndLayout<'a, Ty>,
+    mut data: Sdata,
+    mut offset: Size,
+) -> Sdata
 where
     Ty: TyAbiInterface<'a, C> + Copy,
     C: HasDataLayout,
 {
-    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
-        // Ensure we have at most eight uniquely addressable members.
-        if arg.layout.size > unit.size.checked_mul(8, cx).unwrap() {
-            return None;
+    if let abi::FieldsShape::Union(_) = layout.fields {
+        return data;
+    }
+
+    match layout.abi {
+        abi::Abi::Scalar(scalar) => {
+            data = arg_scalar(cx, &scalar, offset, data);
         }
+        abi::Abi::Aggregate { .. } => {
+            for i in 0..layout.fields.count().clone() {
+                if offset < layout.fields.offset(i) {
+                    offset = layout.fields.offset(i);
+                }
+                data = parse_structure(cx, layout.field(cx, i).clone(), data.clone(), offset);
+            }
+        }
+        _ => {
+            if let abi::Abi::ScalarPair(scalar1, scalar2) = &layout.abi {
+                data = arg_scalar_pair(cx, scalar1, scalar2, offset, data);
+            }
+        }
+    }
 
-        let valid_unit = match unit.kind {
-            RegKind::Integer => false,
-            RegKind::Float => false,
-            RegKind::Vector => arg.layout.size.bits() == 128,
-        };
-
-        valid_unit.then_some(Uniform { unit, total: arg.layout.size })
-    })
+    return data;
 }
 
 fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
@@ -36,102 +142,78 @@ where
         return;
     }
 
-    // This doesn't intentionally handle structures with floats which needs
-    // special care below.
-    if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
-        arg.cast_to(uniform);
-        return;
-    }
-
-    if let abi::FieldsShape::Arbitrary { .. } = arg.layout.fields {
-        let dl = cx.data_layout();
-        let size = arg.layout.size;
-        let mut prefix = [None; 8];
-        let mut prefix_index = 0;
-        let mut last_offset = Size::ZERO;
-        let mut has_float = false;
-        let mut arg_attribute = ArgAttribute::default();
-
-        for i in 0..arg.layout.fields.count() {
-            let field = arg.layout.field(cx, i);
-            let offset = arg.layout.fields.offset(i);
-
-            if let abi::Abi::Scalar(scalar) = &field.abi {
-                if scalar.value == abi::F32 || scalar.value == abi::F64 {
-                    has_float = true;
-
-                    if !last_offset.is_aligned(dl.f64_align.abi) && last_offset < offset {
-                        if prefix_index == prefix.len() {
-                            break;
-                        }
-                        prefix[prefix_index] = Some(Reg::i32());
-                        prefix_index += 1;
-                        last_offset = last_offset + Reg::i32().size;
-                    }
-
-                    for _ in 0..((offset - last_offset).bits() / 64)
-                        .min((prefix.len() - prefix_index) as u64)
-                    {
-                        prefix[prefix_index] = Some(Reg::i64());
-                        prefix_index += 1;
-                        last_offset = last_offset + Reg::i64().size;
-                    }
-
-                    if last_offset < offset {
-                        if prefix_index == prefix.len() {
-                            break;
-                        }
-                        prefix[prefix_index] = Some(Reg::i32());
-                        prefix_index += 1;
-                        last_offset = last_offset + Reg::i32().size;
-                    }
-
-                    if prefix_index == prefix.len() {
-                        break;
-                    }
-
-                    if scalar.value == abi::F32 {
-                        arg_attribute = ArgAttribute::InReg;
-                        prefix[prefix_index] = Some(Reg::f32());
-                        last_offset = offset + Reg::f32().size;
-                    } else {
-                        prefix[prefix_index] = Some(Reg::f64());
-                        last_offset = offset + Reg::f64().size;
-                    }
-                    prefix_index += 1;
-                }
-            }
-        }
-
-        if has_float && arg.layout.size <= in_registers_max {
-            let mut rest_size = size - last_offset;
-
-            if (rest_size.raw % 8) != 0 && prefix_index < prefix.len() {
-                prefix[prefix_index] = Some(Reg::i32());
-                rest_size = rest_size - Reg::i32().size;
-            }
-
-            arg.cast_to(CastTarget {
-                prefix,
-                rest: Uniform { unit: Reg::i64(), total: rest_size },
-                attrs: ArgAttributes {
-                    regular: arg_attribute,
-                    arg_ext: ArgExtension::None,
-                    pointee_size: Size::ZERO,
-                    pointee_align: None,
-                },
-            });
-            return;
-        }
-    }
-
     let total = arg.layout.size;
     if total > in_registers_max {
         arg.make_indirect();
         return;
     }
 
-    arg.cast_to(Uniform { unit: Reg::i64(), total });
+    match arg.layout.fields {
+        abi::FieldsShape::Primitive => unreachable!(),
+        abi::FieldsShape::Array { .. } => {
+            // Arrays are passed indirectly
+            arg.make_indirect();
+            return;
+        }
+        abi::FieldsShape::Union(_) => {
+            // Unions and are always treated as a series of 64-bit integer chunks
+        }
+        abi::FieldsShape::Arbitrary { .. } => {
+            // Stuctures with floating point numbers need special care.
+
+            let mut data = parse_structure(
+                cx,
+                arg.layout.clone(),
+                Sdata {
+                    prefix: [None; 8],
+                    prefix_index: 0,
+                    last_offset: Size::ZERO,
+                    has_float: false,
+                    arg_attribute: ArgAttribute::default(),
+                },
+                Size { raw: 0 },
+            );
+
+            if data.has_float {
+                // Structure { float, int, int } doesn't like to be handled like
+                // { float, long int }. Other way around it doesn't mind.
+                if data.last_offset < arg.layout.size
+                    && (data.last_offset.raw % 8) != 0
+                    && data.prefix_index < data.prefix.len()
+                {
+                    data.prefix[data.prefix_index] = Some(Reg::i32());
+                    data.prefix_index += 1;
+                    data.last_offset += Reg::i32().size;
+                }
+
+                let mut rest_size = arg.layout.size - data.last_offset;
+                if (rest_size.raw % 8) != 0 && data.prefix_index < data.prefix.len() {
+                    data.prefix[data.prefix_index] = Some(Reg::i32());
+                    rest_size = rest_size - Reg::i32().size;
+                }
+
+                arg.cast_to(CastTarget {
+                    prefix: data.prefix,
+                    rest: Uniform {
+                        unit: Reg::i64(),
+                        total: rest_size,
+                    },
+                    attrs: ArgAttributes {
+                        regular: data.arg_attribute,
+                        arg_ext: ArgExtension::None,
+                        pointee_size: Size::ZERO,
+                        pointee_align: None,
+                    },
+                });
+                return;
+            }
+        }
+    }
+
+    arg.cast_to(Uniform {
+        unit: Reg::i64(),
+        total,
+    });
 }
 
 pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)