@@ -10,7 +10,7 @@ use crate::abi::{
10
10
TyAndLayout ,
11
11
} ;
12
12
use crate :: spec:: abi:: Abi as SpecAbi ;
13
- use crate :: spec:: { self , HasTargetSpec , HasWasmCAbiOpt , HasX86AbiOpt , WasmCAbi } ;
13
+ use crate :: spec:: { self , HasTargetSpec , HasWasmCAbiOpt , HasX86AbiOpt , RustAbi , WasmCAbi } ;
14
14
15
15
mod aarch64;
16
16
mod amdgpu;
@@ -736,14 +736,30 @@ impl<'a, Ty> FnAbi<'a, Ty> {
736
736
C : HasDataLayout + HasTargetSpec ,
737
737
{
738
738
let spec = cx. target_spec ( ) ;
739
- match & spec. arch [ .. ] {
739
+ match & * spec. arch {
740
740
"x86" => x86:: compute_rust_abi_info ( cx, self , abi) ,
741
741
"riscv32" | "riscv64" => riscv:: compute_rust_abi_info ( cx, self , abi) ,
742
742
"loongarch64" => loongarch:: compute_rust_abi_info ( cx, self , abi) ,
743
743
"aarch64" => aarch64:: compute_rust_abi_info ( cx, self ) ,
744
744
_ => { }
745
745
} ;
746
746
747
+ // Decides whether we can pass the given SIMD argument via `PassMode::Direct`.
748
+ // May only return `true` if the target will always pass those arguments the same way,
749
+ // no matter what the user does with `-Ctarget-feature`! In other words, whatever
750
+ // target features are required to pass a SIMD value in registers must be listed in
751
+ // the `abi_required_features` for the current target and ABI.
752
+ let can_pass_simd_directly = |arg : & ArgAbi < ' _ , Ty > | match & * spec. arch {
753
+ // On x86, if we have SSE2 (which we have by default for x86_64), we can always pass up
754
+ // to 128-bit-sized vectors.
755
+ "x86" if spec. rust_abi == Some ( RustAbi :: X86Sse2 ) => arg. layout . size . bits ( ) <= 128 ,
756
+ "x86_64" if spec. rust_abi != Some ( RustAbi :: X86Softfloat ) => {
757
+ arg. layout . size . bits ( ) <= 128
758
+ }
759
+ // So far, we haven't implemented this logic for any other target.
760
+ _ => false ,
761
+ } ;
762
+
747
763
for ( arg_idx, arg) in self
748
764
. args
749
765
. iter_mut ( )
@@ -755,7 +771,10 @@ impl<'a, Ty> FnAbi<'a, Ty> {
755
771
continue ;
756
772
}
757
773
758
- if arg_idx. is_none ( ) && arg. layout . size > Pointer ( AddressSpace :: DATA ) . size ( cx) * 2 {
774
+ if arg_idx. is_none ( )
775
+ && arg. layout . size > Pointer ( AddressSpace :: DATA ) . size ( cx) * 2
776
+ && !matches ! ( arg. layout. backend_repr, BackendRepr :: Vector { .. } )
777
+ {
759
778
// Return values larger than 2 registers using a return area
760
779
// pointer. LLVM and Cranelift disagree about how to return
761
780
// values that don't fit in the registers designated for return
@@ -794,53 +813,57 @@ impl<'a, Ty> FnAbi<'a, Ty> {
794
813
// rustc_target already ensure any return value which doesn't
795
814
// fit in the available amount of return registers is passed in
796
815
// the right way for the current target.
816
+ // The adjustment is also not necessary nor desired for types with
817
+ // a vector representation; those are handled below.
797
818
arg. make_indirect ( ) ;
798
819
continue ;
799
820
}
800
821
801
822
match arg. layout . backend_repr {
802
- BackendRepr :: Memory { .. } => { }
803
-
804
- // This is a fun case! The gist of what this is doing is
805
- // that we want callers and callees to always agree on the
806
- // ABI of how they pass SIMD arguments. If we were to *not*
807
- // make these arguments indirect then they'd be immediates
808
- // in LLVM, which means that they'd used whatever the
809
- // appropriate ABI is for the callee and the caller. That
810
- // means, for example, if the caller doesn't have AVX
811
- // enabled but the callee does, then passing an AVX argument
812
- // across this boundary would cause corrupt data to show up.
813
- //
814
- // This problem is fixed by unconditionally passing SIMD
815
- // arguments through memory between callers and callees
816
- // which should get them all to agree on ABI regardless of
817
- // target feature sets. Some more information about this
818
- // issue can be found in #44367.
819
- //
820
- // Note that the intrinsic ABI is exempt here as
821
- // that's how we connect up to LLVM and it's unstable
822
- // anyway, we control all calls to it in libstd.
823
- BackendRepr :: Vector { .. }
824
- if abi != SpecAbi :: RustIntrinsic && spec. simd_types_indirect =>
825
- {
826
- arg. make_indirect ( ) ;
827
- continue ;
823
+ BackendRepr :: Memory { .. } => {
824
+ // Compute `Aggregate` ABI.
825
+
826
+ let is_indirect_not_on_stack =
827
+ matches ! ( arg. mode, PassMode :: Indirect { on_stack: false , .. } ) ;
828
+ assert ! ( is_indirect_not_on_stack) ;
829
+
830
+ let size = arg. layout . size ;
831
+ if arg. layout . is_sized ( ) && size <= Pointer ( AddressSpace :: DATA ) . size ( cx) {
832
+ // We want to pass small aggregates as immediates, but using
833
+ // an LLVM aggregate type for this leads to bad optimizations,
834
+ // so we pick an appropriately sized integer type instead.
835
+ arg. cast_to ( Reg { kind : RegKind :: Integer , size } ) ;
836
+ }
828
837
}
829
838
830
- _ => continue ,
831
- }
832
- // Compute `Aggregate` ABI.
833
-
834
- let is_indirect_not_on_stack =
835
- matches ! ( arg. mode, PassMode :: Indirect { on_stack: false , .. } ) ;
836
- assert ! ( is_indirect_not_on_stack) ;
837
-
838
- let size = arg. layout . size ;
839
- if !arg. layout . is_unsized ( ) && size <= Pointer ( AddressSpace :: DATA ) . size ( cx) {
840
- // We want to pass small aggregates as immediates, but using
841
- // an LLVM aggregate type for this leads to bad optimizations,
842
- // so we pick an appropriately sized integer type instead.
843
- arg. cast_to ( Reg { kind : RegKind :: Integer , size } ) ;
839
+ BackendRepr :: Vector { .. } => {
840
+ // This is a fun case! The gist of what this is doing is
841
+ // that we want callers and callees to always agree on the
842
+ // ABI of how they pass SIMD arguments. If we were to *not*
843
+ // make these arguments indirect then they'd be immediates
844
+ // in LLVM, which means that they'd used whatever the
845
+ // appropriate ABI is for the callee and the caller. That
846
+ // means, for example, if the caller doesn't have AVX
847
+ // enabled but the callee does, then passing an AVX argument
848
+ // across this boundary would cause corrupt data to show up.
849
+ //
850
+ // This problem is fixed by unconditionally passing SIMD
851
+ // arguments through memory between callers and callees
852
+ // which should get them all to agree on ABI regardless of
853
+ // target feature sets. Some more information about this
854
+ // issue can be found in #44367.
855
+ //
856
+ // Note that the intrinsic ABI is exempt here as tjpse are not
857
+ // real functions anyway, and LLVM expects certain types.
858
+ if abi != SpecAbi :: RustIntrinsic
859
+ && spec. simd_types_indirect
860
+ && !can_pass_simd_directly ( arg)
861
+ {
862
+ arg. make_indirect ( ) ;
863
+ }
864
+ }
865
+
866
+ _ => { }
844
867
}
845
868
}
846
869
}
0 commit comments