@@ -9,7 +9,7 @@ pub use rustc_abi::{Primitive, Reg, RegKind};
99use rustc_macros:: HashStable_Generic ;
1010use rustc_span:: Symbol ;
1111
12- use crate :: spec:: { HasTargetSpec , HasWasmCAbiOpt , HasX86AbiOpt , WasmCAbi } ;
12+ use crate :: spec:: { HasTargetSpec , HasWasmCAbiOpt , HasX86AbiOpt , RustcAbi , WasmCAbi } ;
1313
1414mod aarch64;
1515mod amdgpu;
@@ -388,6 +388,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
388388 /// Pass this argument directly instead. Should NOT be used!
389389 /// Only exists because of past ABI mistakes that will take time to fix
390390 /// (see <https://github.com/rust-lang/rust/issues/115666>).
391+ #[ track_caller]
391392 pub fn make_direct_deprecated ( & mut self ) {
392393 match self . mode {
393394 PassMode :: Indirect { .. } => {
@@ -400,6 +401,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
400401
401402 /// Pass this argument indirectly, by passing a (thin or wide) pointer to the argument instead.
402403 /// This is valid for both sized and unsized arguments.
404+ #[ track_caller]
403405 pub fn make_indirect ( & mut self ) {
404406 match self . mode {
405407 PassMode :: Direct ( _) | PassMode :: Pair ( _, _) => {
@@ -414,6 +416,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
414416
415417 /// Same as `make_indirect`, but for arguments that are ignored. Only needed for ABIs that pass
416418 /// ZSTs indirectly.
419+ #[ track_caller]
417420 pub fn make_indirect_from_ignore ( & mut self ) {
418421 match self . mode {
419422 PassMode :: Ignore => {
@@ -737,27 +740,46 @@ impl<'a, Ty> FnAbi<'a, Ty> {
737740 C : HasDataLayout + HasTargetSpec ,
738741 {
739742 let spec = cx. target_spec ( ) ;
740- match & spec. arch [ .. ] {
743+ match & * spec. arch {
741744 "x86" => x86:: compute_rust_abi_info ( cx, self , abi) ,
742745 "riscv32" | "riscv64" => riscv:: compute_rust_abi_info ( cx, self , abi) ,
743746 "loongarch64" => loongarch:: compute_rust_abi_info ( cx, self , abi) ,
744747 "aarch64" => aarch64:: compute_rust_abi_info ( cx, self ) ,
745748 _ => { }
746749 } ;
747750
751+ // Decides whether we can pass the given SIMD argument via `PassMode::Direct`.
752+ // May only return `true` if the target will always pass those arguments the same way,
753+ // no matter what the user does with `-Ctarget-feature`! In other words, whatever
754+ // target features are required to pass a SIMD value in registers must be listed in
755+ // the `abi_required_features` for the current target and ABI.
756+ let can_pass_simd_directly = |arg : & ArgAbi < ' _ , Ty > | match & * spec. arch {
757+ // On x86, if we have SSE2 (which we have by default for x86_64), we can always pass up
758+ // to 128-bit-sized vectors.
759+ "x86" if spec. rustc_abi == Some ( RustcAbi :: X86Sse2 ) => arg. layout . size . bits ( ) <= 128 ,
760+ "x86_64" if spec. rustc_abi != Some ( RustcAbi :: X86Softfloat ) => {
761+ arg. layout . size . bits ( ) <= 128
762+ }
763+ // So far, we haven't implemented this logic for any other target.
764+ _ => false ,
765+ } ;
766+
748767 for ( arg_idx, arg) in self
749768 . args
750769 . iter_mut ( )
751770 . enumerate ( )
752771 . map ( |( idx, arg) | ( Some ( idx) , arg) )
753772 . chain ( iter:: once ( ( None , & mut self . ret ) ) )
754773 {
755- if arg. is_ignore ( ) {
774+ // If the logic above already picked a specific type to cast the argument to, leave that
775+ // in place.
776+ if matches ! ( arg. mode, PassMode :: Ignore | PassMode :: Cast { .. } ) {
756777 continue ;
757778 }
758779
759780 if arg_idx. is_none ( )
760781 && arg. layout . size > Primitive :: Pointer ( AddressSpace :: DATA ) . size ( cx) * 2
782+ && !matches ! ( arg. layout. backend_repr, BackendRepr :: Vector { .. } )
761783 {
762784 // Return values larger than 2 registers using a return area
763785 // pointer. LLVM and Cranelift disagree about how to return
@@ -767,7 +789,8 @@ impl<'a, Ty> FnAbi<'a, Ty> {
767789 // return value independently and decide to pass it in a
768790 // register or not, which would result in the return value
769791 // being passed partially in registers and partially through a
770- // return area pointer.
792+ // return area pointer. For large IR-level values such as `i128`,
793+ // cranelift will even split up the value into smaller chunks.
771794 //
772795 // While Cranelift may need to be fixed as the LLVM behavior is
773796 // generally more correct with respect to the surface language,
@@ -797,53 +820,60 @@ impl<'a, Ty> FnAbi<'a, Ty> {
797820 // rustc_target already ensure any return value which doesn't
798821 // fit in the available amount of return registers is passed in
799822 // the right way for the current target.
823+ //
824+ // The adjustment is not necessary nor desired for types with a vector
825+ // representation; those are handled below.
800826 arg. make_indirect ( ) ;
801827 continue ;
802828 }
803829
804830 match arg. layout . backend_repr {
805- BackendRepr :: Memory { .. } => { }
806-
807- // This is a fun case! The gist of what this is doing is
808- // that we want callers and callees to always agree on the
809- // ABI of how they pass SIMD arguments. If we were to *not*
810- // make these arguments indirect then they'd be immediates
811- // in LLVM, which means that they'd used whatever the
812- // appropriate ABI is for the callee and the caller. That
813- // means, for example, if the caller doesn't have AVX
814- // enabled but the callee does, then passing an AVX argument
815- // across this boundary would cause corrupt data to show up.
816- //
817- // This problem is fixed by unconditionally passing SIMD
818- // arguments through memory between callers and callees
819- // which should get them all to agree on ABI regardless of
820- // target feature sets. Some more information about this
821- // issue can be found in #44367.
822- //
823- // Note that the intrinsic ABI is exempt here as
824- // that's how we connect up to LLVM and it's unstable
825- // anyway, we control all calls to it in libstd.
826- BackendRepr :: Vector { .. }
827- if abi != ExternAbi :: RustIntrinsic && spec. simd_types_indirect =>
828- {
829- arg. make_indirect ( ) ;
830- continue ;
831+ BackendRepr :: Memory { .. } => {
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_sized ( )
840+ && size <= Primitive :: Pointer ( AddressSpace :: DATA ) . size ( cx)
841+ {
842+ // We want to pass small aggregates as immediates, but using
843+ // an LLVM aggregate type for this leads to bad optimizations,
844+ // so we pick an appropriately sized integer type instead.
845+ arg. cast_to ( Reg { kind : RegKind :: Integer , size } ) ;
846+ }
831847 }
832848
833- _ => continue ,
834- }
835- // Compute `Aggregate` ABI.
836-
837- let is_indirect_not_on_stack =
838- matches ! ( arg. mode, PassMode :: Indirect { on_stack: false , .. } ) ;
839- assert ! ( is_indirect_not_on_stack) ;
840-
841- let size = arg. layout . size ;
842- if !arg. layout . is_unsized ( ) && size <= Primitive :: Pointer ( AddressSpace :: DATA ) . size ( cx) {
843- // We want to pass small aggregates as immediates, but using
844- // an LLVM aggregate type for this leads to bad optimizations,
845- // so we pick an appropriately sized integer type instead.
846- arg. cast_to ( Reg { kind : RegKind :: Integer , size } ) ;
849+ BackendRepr :: Vector { .. } => {
850+ // This is a fun case! The gist of what this is doing is
851+ // that we want callers and callees to always agree on the
852+ // ABI of how they pass SIMD arguments. If we were to *not*
853+ // make these arguments indirect then they'd be immediates
854+ // in LLVM, which means that they'd used whatever the
855+ // appropriate ABI is for the callee and the caller. That
856+ // means, for example, if the caller doesn't have AVX
857+ // enabled but the callee does, then passing an AVX argument
858+ // across this boundary would cause corrupt data to show up.
859+ //
860+ // This problem is fixed by unconditionally passing SIMD
861+ // arguments through memory between callers and callees
862+ // which should get them all to agree on ABI regardless of
863+ // target feature sets. Some more information about this
864+ // issue can be found in #44367.
865+ //
866+ // Note that the intrinsic ABI is exempt here as those are not
867+ // real functions anyway, and the backend expects very specific types.
868+ if abi != ExternAbi :: RustIntrinsic
869+ && spec. simd_types_indirect
870+ && !can_pass_simd_directly ( arg)
871+ {
872+ arg. make_indirect ( ) ;
873+ }
874+ }
875+
876+ _ => { }
847877 }
848878 }
849879 }
0 commit comments