|
1 |
| -use std::fmt; |
2 | 1 | use std::str::FromStr;
|
| 2 | +use std::{fmt, iter}; |
3 | 3 |
|
4 | 4 | pub use rustc_abi::{Reg, RegKind};
|
5 | 5 | use rustc_macros::HashStable_Generic;
|
6 | 6 | use rustc_span::Symbol;
|
7 | 7 |
|
8 |
| -use crate::abi::{self, Abi, Align, HasDataLayout, Size, TyAbiInterface, TyAndLayout}; |
| 8 | +use crate::abi::{ |
| 9 | + self, Abi, AddressSpace, Align, HasDataLayout, Pointer, Size, TyAbiInterface, TyAndLayout, |
| 10 | +}; |
| 11 | +use crate::spec::abi::Abi as SpecAbi; |
9 | 12 | use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi};
|
10 | 13 |
|
11 | 14 | mod aarch64;
|
@@ -720,6 +723,118 @@ impl<'a, Ty> FnAbi<'a, Ty> {
|
720 | 723 |
|
721 | 724 | Ok(())
|
722 | 725 | }
|
| 726 | + |
| 727 | + pub fn adjust_for_rust_abi<C>(&mut self, cx: &C, abi: SpecAbi) |
| 728 | + where |
| 729 | + Ty: TyAbiInterface<'a, C> + Copy, |
| 730 | + C: HasDataLayout + HasTargetSpec, |
| 731 | + { |
| 732 | + let spec = cx.target_spec(); |
| 733 | + match &spec.arch[..] { |
| 734 | + "x86" => x86::compute_rust_abi_info(cx, self, abi), |
| 735 | + "riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self, abi), |
| 736 | + "loongarch64" => loongarch::compute_rust_abi_info(cx, self, abi), |
| 737 | + _ => {} |
| 738 | + }; |
| 739 | + |
| 740 | + for (arg_idx, arg) in self |
| 741 | + .args |
| 742 | + .iter_mut() |
| 743 | + .enumerate() |
| 744 | + .map(|(idx, arg)| (Some(idx), arg)) |
| 745 | + .chain(iter::once((None, &mut self.ret))) |
| 746 | + { |
| 747 | + if arg.is_ignore() { |
| 748 | + continue; |
| 749 | + } |
| 750 | + |
| 751 | + if arg_idx.is_none() && arg.layout.size > Pointer(AddressSpace::DATA).size(cx) * 2 { |
| 752 | + // Return values larger than 2 registers using a return area |
| 753 | + // pointer. LLVM and Cranelift disagree about how to return |
| 754 | + // values that don't fit in the registers designated for return |
| 755 | + // values. LLVM will force the entire return value to be passed |
| 756 | + // by return area pointer, while Cranelift will look at each IR level |
| 757 | + // return value independently and decide to pass it in a |
| 758 | + // register or not, which would result in the return value |
| 759 | + // being passed partially in registers and partially through a |
| 760 | + // return area pointer. |
| 761 | + // |
| 762 | + // While Cranelift may need to be fixed as the LLVM behavior is |
| 763 | + // generally more correct with respect to the surface language, |
| 764 | + // forcing this behavior in rustc itself makes it easier for |
| 765 | + // other backends to conform to the Rust ABI and for the C ABI |
| 766 | + // rustc already handles this behavior anyway. |
| 767 | + // |
| 768 | + // In addition LLVM's decision to pass the return value in |
| 769 | + // registers or using a return area pointer depends on how |
| 770 | + // exactly the return type is lowered to an LLVM IR type. For |
| 771 | + // example `Option<u128>` can be lowered as `{ i128, i128 }` |
| 772 | + // in which case the x86_64 backend would use a return area |
| 773 | + // pointer, or it could be passed as `{ i32, i128 }` in which |
| 774 | + // case the x86_64 backend would pass it in registers by taking |
| 775 | + // advantage of an LLVM ABI extension that allows using 3 |
| 776 | + // registers for the x86_64 sysv call conv rather than the |
| 777 | + // officially specified 2 registers. |
| 778 | + // |
| 779 | + // FIXME: Technically we should look at the amount of available |
| 780 | + // return registers rather than guessing that there are 2 |
| 781 | + // registers for return values. In practice only a couple of |
| 782 | + // architectures have less than 2 return registers. None of |
| 783 | + // which supported by Cranelift. |
| 784 | + // |
| 785 | + // NOTE: This adjustment is only necessary for the Rust ABI as |
| 786 | + // for other ABI's the calling convention implementations in |
| 787 | + // rustc_target already ensure any return value which doesn't |
| 788 | + // fit in the available amount of return registers is passed in |
| 789 | + // the right way for the current target. |
| 790 | + arg.make_indirect(); |
| 791 | + continue; |
| 792 | + } |
| 793 | + |
| 794 | + match arg.layout.abi { |
| 795 | + Abi::Aggregate { .. } => {} |
| 796 | + |
| 797 | + // This is a fun case! The gist of what this is doing is |
| 798 | + // that we want callers and callees to always agree on the |
| 799 | + // ABI of how they pass SIMD arguments. If we were to *not* |
| 800 | + // make these arguments indirect then they'd be immediates |
| 801 | + // in LLVM, which means that they'd used whatever the |
| 802 | + // appropriate ABI is for the callee and the caller. That |
| 803 | + // means, for example, if the caller doesn't have AVX |
| 804 | + // enabled but the callee does, then passing an AVX argument |
| 805 | + // across this boundary would cause corrupt data to show up. |
| 806 | + // |
| 807 | + // This problem is fixed by unconditionally passing SIMD |
| 808 | + // arguments through memory between callers and callees |
| 809 | + // which should get them all to agree on ABI regardless of |
| 810 | + // target feature sets. Some more information about this |
| 811 | + // issue can be found in #44367. |
| 812 | + // |
| 813 | + // Note that the intrinsic ABI is exempt here as |
| 814 | + // that's how we connect up to LLVM and it's unstable |
| 815 | + // anyway, we control all calls to it in libstd. |
| 816 | + Abi::Vector { .. } if abi != SpecAbi::RustIntrinsic && spec.simd_types_indirect => { |
| 817 | + arg.make_indirect(); |
| 818 | + continue; |
| 819 | + } |
| 820 | + |
| 821 | + _ => continue, |
| 822 | + } |
| 823 | + // Compute `Aggregate` ABI. |
| 824 | + |
| 825 | + let is_indirect_not_on_stack = |
| 826 | + matches!(arg.mode, PassMode::Indirect { on_stack: false, .. }); |
| 827 | + assert!(is_indirect_not_on_stack); |
| 828 | + |
| 829 | + let size = arg.layout.size; |
| 830 | + if !arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx) { |
| 831 | + // We want to pass small aggregates as immediates, but using |
| 832 | + // an LLVM aggregate type for this leads to bad optimizations, |
| 833 | + // so we pick an appropriately sized integer type instead. |
| 834 | + arg.cast_to(Reg { kind: RegKind::Integer, size }); |
| 835 | + } |
| 836 | + } |
| 837 | + } |
723 | 838 | }
|
724 | 839 |
|
725 | 840 | impl FromStr for Conv {
|
|
0 commit comments