@@ -7,7 +7,7 @@ use rustc_abi::{
7
7
} ;
8
8
use rustc_macros:: HashStable_Generic ;
9
9
10
- use crate :: spec:: { HasTargetSpec , HasWasmCAbiOpt , HasX86AbiOpt , WasmCAbi } ;
10
+ use crate :: spec:: { HasTargetSpec , HasWasmCAbiOpt , HasX86AbiOpt , RustcAbi , WasmCAbi } ;
11
11
12
12
mod aarch64;
13
13
mod amdgpu;
@@ -386,6 +386,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
386
386
/// Pass this argument directly instead. Should NOT be used!
387
387
/// Only exists because of past ABI mistakes that will take time to fix
388
388
/// (see <https://github.com/rust-lang/rust/issues/115666>).
389
+ #[ track_caller]
389
390
pub fn make_direct_deprecated ( & mut self ) {
390
391
match self . mode {
391
392
PassMode :: Indirect { .. } => {
@@ -398,6 +399,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
398
399
399
400
/// Pass this argument indirectly, by passing a (thin or wide) pointer to the argument instead.
400
401
/// This is valid for both sized and unsized arguments.
402
+ #[ track_caller]
401
403
pub fn make_indirect ( & mut self ) {
402
404
match self . mode {
403
405
PassMode :: Direct ( _) | PassMode :: Pair ( _, _) => {
@@ -412,6 +414,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
412
414
413
415
/// Same as `make_indirect`, but for arguments that are ignored. Only needed for ABIs that pass
414
416
/// ZSTs indirectly.
417
+ #[ track_caller]
415
418
pub fn make_indirect_from_ignore ( & mut self ) {
416
419
match self . mode {
417
420
PassMode :: Ignore => {
@@ -717,27 +720,46 @@ impl<'a, Ty> FnAbi<'a, Ty> {
717
720
C : HasDataLayout + HasTargetSpec ,
718
721
{
719
722
let spec = cx. target_spec ( ) ;
720
- match & spec. arch [ .. ] {
723
+ match & * spec. arch {
721
724
"x86" => x86:: compute_rust_abi_info ( cx, self , abi) ,
722
725
"riscv32" | "riscv64" => riscv:: compute_rust_abi_info ( cx, self , abi) ,
723
726
"loongarch64" => loongarch:: compute_rust_abi_info ( cx, self , abi) ,
724
727
"aarch64" => aarch64:: compute_rust_abi_info ( cx, self ) ,
725
728
_ => { }
726
729
} ;
727
730
731
+ // Decides whether we can pass the given SIMD argument via `PassMode::Direct`.
732
+ // May only return `true` if the target will always pass those arguments the same way,
733
+ // no matter what the user does with `-Ctarget-feature`! In other words, whatever
734
+ // target features are required to pass a SIMD value in registers must be listed in
735
+ // the `abi_required_features` for the current target and ABI.
736
+ let can_pass_simd_directly = |arg : & ArgAbi < ' _ , Ty > | match & * spec. arch {
737
+ // On x86, if we have SSE2 (which we have by default for x86_64), we can always pass up
738
+ // to 128-bit-sized vectors.
739
+ "x86" if spec. rustc_abi == Some ( RustcAbi :: X86Sse2 ) => arg. layout . size . bits ( ) <= 128 ,
740
+ "x86_64" if spec. rustc_abi != Some ( RustcAbi :: X86Softfloat ) => {
741
+ arg. layout . size . bits ( ) <= 128
742
+ }
743
+ // So far, we haven't implemented this logic for any other target.
744
+ _ => false ,
745
+ } ;
746
+
728
747
for ( arg_idx, arg) in self
729
748
. args
730
749
. iter_mut ( )
731
750
. enumerate ( )
732
751
. map ( |( idx, arg) | ( Some ( idx) , arg) )
733
752
. chain ( iter:: once ( ( None , & mut self . ret ) ) )
734
753
{
735
- if arg. is_ignore ( ) {
754
+ // If the logic above already picked a specific type to cast the argument to, leave that
755
+ // in place.
756
+ if matches ! ( arg. mode, PassMode :: Ignore | PassMode :: Cast { .. } ) {
736
757
continue ;
737
758
}
738
759
739
760
if arg_idx. is_none ( )
740
761
&& arg. layout . size > Primitive :: Pointer ( AddressSpace :: DATA ) . size ( cx) * 2
762
+ && !matches ! ( arg. layout. backend_repr, BackendRepr :: Vector { .. } )
741
763
{
742
764
// Return values larger than 2 registers using a return area
743
765
// pointer. LLVM and Cranelift disagree about how to return
@@ -747,7 +769,8 @@ impl<'a, Ty> FnAbi<'a, Ty> {
747
769
// return value independently and decide to pass it in a
748
770
// register or not, which would result in the return value
749
771
// being passed partially in registers and partially through a
750
- // return area pointer.
772
+ // return area pointer. For large IR-level values such as `i128`,
773
+ // cranelift will even split up the value into smaller chunks.
751
774
//
752
775
// While Cranelift may need to be fixed as the LLVM behavior is
753
776
// generally more correct with respect to the surface language,
@@ -777,53 +800,60 @@ impl<'a, Ty> FnAbi<'a, Ty> {
777
800
// rustc_target already ensure any return value which doesn't
778
801
// fit in the available amount of return registers is passed in
779
802
// the right way for the current target.
803
+ //
804
+ // The adjustment is not necessary nor desired for types with a vector
805
+ // representation; those are handled below.
780
806
arg. make_indirect ( ) ;
781
807
continue ;
782
808
}
783
809
784
810
match arg. layout . backend_repr {
785
- BackendRepr :: Memory { .. } => { }
786
-
787
- // This is a fun case! The gist of what this is doing is
788
- // that we want callers and callees to always agree on the
789
- // ABI of how they pass SIMD arguments. If we were to *not*
790
- // make these arguments indirect then they'd be immediates
791
- // in LLVM, which means that they'd used whatever the
792
- // appropriate ABI is for the callee and the caller. That
793
- // means, for example, if the caller doesn't have AVX
794
- // enabled but the callee does, then passing an AVX argument
795
- // across this boundary would cause corrupt data to show up.
796
- //
797
- // This problem is fixed by unconditionally passing SIMD
798
- // arguments through memory between callers and callees
799
- // which should get them all to agree on ABI regardless of
800
- // target feature sets. Some more information about this
801
- // issue can be found in #44367.
802
- //
803
- // Note that the intrinsic ABI is exempt here as
804
- // that's how we connect up to LLVM and it's unstable
805
- // anyway, we control all calls to it in libstd.
806
- BackendRepr :: Vector { .. }
807
- if abi != ExternAbi :: RustIntrinsic && spec. simd_types_indirect =>
808
- {
809
- arg. make_indirect ( ) ;
810
- continue ;
811
+ BackendRepr :: Memory { .. } => {
812
+ // Compute `Aggregate` ABI.
813
+
814
+ let is_indirect_not_on_stack =
815
+ matches ! ( arg. mode, PassMode :: Indirect { on_stack: false , .. } ) ;
816
+ assert ! ( is_indirect_not_on_stack) ;
817
+
818
+ let size = arg. layout . size ;
819
+ if arg. layout . is_sized ( )
820
+ && size <= Primitive :: Pointer ( AddressSpace :: DATA ) . size ( cx)
821
+ {
822
+ // We want to pass small aggregates as immediates, but using
823
+ // an LLVM aggregate type for this leads to bad optimizations,
824
+ // so we pick an appropriately sized integer type instead.
825
+ arg. cast_to ( Reg { kind : RegKind :: Integer , size } ) ;
826
+ }
811
827
}
812
828
813
- _ => continue ,
814
- }
815
- // Compute `Aggregate` ABI.
816
-
817
- let is_indirect_not_on_stack =
818
- matches ! ( arg. mode, PassMode :: Indirect { on_stack: false , .. } ) ;
819
- assert ! ( is_indirect_not_on_stack) ;
820
-
821
- let size = arg. layout . size ;
822
- if !arg. layout . is_unsized ( ) && size <= Primitive :: Pointer ( AddressSpace :: DATA ) . size ( cx) {
823
- // We want to pass small aggregates as immediates, but using
824
- // an LLVM aggregate type for this leads to bad optimizations,
825
- // so we pick an appropriately sized integer type instead.
826
- arg. cast_to ( Reg { kind : RegKind :: Integer , size } ) ;
829
+ BackendRepr :: Vector { .. } => {
830
+ // This is a fun case! The gist of what this is doing is
831
+ // that we want callers and callees to always agree on the
832
+ // ABI of how they pass SIMD arguments. If we were to *not*
833
+ // make these arguments indirect then they'd be immediates
834
+ // in LLVM, which means that they'd used whatever the
835
+ // appropriate ABI is for the callee and the caller. That
836
+ // means, for example, if the caller doesn't have AVX
837
+ // enabled but the callee does, then passing an AVX argument
838
+ // across this boundary would cause corrupt data to show up.
839
+ //
840
+ // This problem is fixed by unconditionally passing SIMD
841
+ // arguments through memory between callers and callees
842
+ // which should get them all to agree on ABI regardless of
843
+ // target feature sets. Some more information about this
844
+ // issue can be found in #44367.
845
+ //
846
+ // Note that the intrinsic ABI is exempt here as those are not
847
+ // real functions anyway, and the backend expects very specific types.
848
+ if abi != ExternAbi :: RustIntrinsic
849
+ && spec. simd_types_indirect
850
+ && !can_pass_simd_directly ( arg)
851
+ {
852
+ arg. make_indirect ( ) ;
853
+ }
854
+ }
855
+
856
+ _ => { }
827
857
}
828
858
}
829
859
}
0 commit comments