rustc_monomorphize/mono_checks/
abi_check.rs1use rustc_abi::{BackendRepr, CanonAbi, ExternAbi, RegKind, X86Call};
4use rustc_hir::{CRATE_HIR_ID, HirId};
5use rustc_middle::mir::{self, Location, traversal};
6use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt};
7use rustc_span::def_id::DefId;
8use rustc_span::{DUMMY_SP, Span, Symbol, sym};
9use rustc_target::callconv::{FnAbi, PassMode};
10
11use crate::errors;
12
13enum UsesVectorRegisters {
15 FixedVector,
17 ScalableVector,
19 No,
20}
21
22fn passes_vectors_by_value(mode: &PassMode, repr: &BackendRepr) -> UsesVectorRegisters {
25 match mode {
26 PassMode::Ignore | PassMode::Indirect { .. } => UsesVectorRegisters::No,
27 PassMode::Cast { pad_i32: _, cast }
28 if cast.prefix.iter().any(|x| #[allow(non_exhaustive_omitted_patterns)] match x.kind {
RegKind::Vector { .. } => true,
_ => false,
}matches!(x.kind, RegKind::Vector { .. }))
29 || #[allow(non_exhaustive_omitted_patterns)] match cast.rest.unit.kind {
RegKind::Vector { .. } => true,
_ => false,
}matches!(cast.rest.unit.kind, RegKind::Vector { .. }) =>
30 {
31 UsesVectorRegisters::FixedVector
32 }
33 PassMode::Direct(..) | PassMode::Pair(..)
34 if #[allow(non_exhaustive_omitted_patterns)] match repr {
BackendRepr::SimdVector { .. } => true,
_ => false,
}matches!(repr, BackendRepr::SimdVector { .. }) =>
35 {
36 UsesVectorRegisters::FixedVector
37 }
38 PassMode::Direct(..) | PassMode::Pair(..)
39 if #[allow(non_exhaustive_omitted_patterns)] match repr {
BackendRepr::SimdScalableVector { .. } => true,
_ => false,
}matches!(repr, BackendRepr::SimdScalableVector { .. }) =>
40 {
41 UsesVectorRegisters::ScalableVector
42 }
43 _ => UsesVectorRegisters::No,
44 }
45}
46
47fn do_check_simd_vector_abi<'tcx>(
52 tcx: TyCtxt<'tcx>,
53 abi: &FnAbi<'tcx, Ty<'tcx>>,
54 def_id: DefId,
55 is_call: bool,
56 loc: impl Fn() -> (Span, HirId),
57) {
58 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
59 let have_feature = |feat: Symbol| {
60 let target_feats = tcx.sess.unstable_target_features.contains(&feat);
61 let fn_feats = codegen_attrs.target_features.iter().any(|x| x.name == feat);
62 target_feats || fn_feats
63 };
64 for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {
65 let size = arg_abi.layout.size;
66 match passes_vectors_by_value(&arg_abi.mode, &arg_abi.layout.backend_repr) {
67 UsesVectorRegisters::FixedVector => {
68 let feature_def = tcx.sess.target.features_for_correct_fixed_length_vector_abi();
69 let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {
71 Some((_, feature)) => feature,
72 None => {
73 let (span, _hir_id) = loc();
74 tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType {
75 span,
76 ty: arg_abi.layout.ty,
77 is_call,
78 });
79 continue;
80 }
81 };
82 if !feature.is_empty() && !have_feature(Symbol::intern(feature)) {
83 let (span, _hir_id) = loc();
84 tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
85 span,
86 required_feature: feature,
87 ty: arg_abi.layout.ty,
88 is_call,
89 is_scalable: false,
90 });
91 }
92 }
93 UsesVectorRegisters::ScalableVector => {
94 let Some(required_feature) =
95 tcx.sess.target.features_for_correct_scalable_vector_abi()
96 else {
97 continue;
98 };
99 if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) {
100 let (span, _) = loc();
101 tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
102 span,
103 required_feature,
104 ty: arg_abi.layout.ty,
105 is_call,
106 is_scalable: true,
107 });
108 }
109 }
110 UsesVectorRegisters::No => {
111 continue;
112 }
113 }
114 }
115 if abi.conv == CanonAbi::X86(X86Call::Vectorcall) && !have_feature(sym::sse2) {
117 let (span, _hir_id) = loc();
118 tcx.dcx().emit_err(errors::AbiRequiredTargetFeature {
119 span,
120 required_feature: "sse2",
121 abi: "vectorcall",
122 is_call,
123 });
124 }
125}
126
127fn do_check_unsized_params<'tcx>(
132 tcx: TyCtxt<'tcx>,
133 fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
134 is_call: bool,
135 loc: impl Fn() -> (Span, HirId),
136) {
137 if fn_abi.conv.is_rustic_abi() {
139 return;
140 }
141
142 for arg_abi in fn_abi.args.iter() {
143 if !arg_abi.layout.layout.is_sized() {
144 let (span, _hir_id) = loc();
145 tcx.dcx().emit_err(errors::AbiErrorUnsupportedUnsizedParameter {
146 span,
147 ty: arg_abi.layout.ty,
148 is_call,
149 });
150 }
151 }
152}
153
154fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
159 let typing_env = ty::TypingEnv::fully_monomorphized();
160 let ty = instance.ty(tcx, typing_env);
161 if ty.is_fn() && ty.fn_sig(tcx).abi() == ExternAbi::Unadjusted {
162 return;
165 }
166 let Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
167 else {
168 tcx.dcx().delayed_bug("ABI computation failure should lead to compilation failure");
171 return;
172 };
173 let loc = || {
180 let def_id = instance.def_id();
181 (
182 tcx.def_span(def_id),
183 def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID),
184 )
185 };
186 do_check_unsized_params(tcx, abi, false, loc);
187 do_check_simd_vector_abi(tcx, abi, instance.def_id(), false, loc);
188}
189
190fn check_call_site_abi<'tcx>(
195 tcx: TyCtxt<'tcx>,
196 callee: Ty<'tcx>,
197 caller: InstanceKind<'tcx>,
198 loc: impl Fn() -> (Span, HirId) + Copy,
199) {
200 let extern_abi = callee.fn_sig(tcx).abi();
201 if extern_abi.is_rustic_abi() || extern_abi == ExternAbi::Unadjusted {
202 return;
207 }
208 let typing_env = ty::TypingEnv::fully_monomorphized();
209 let callee_abi = match *callee.kind() {
210 ty::FnPtr(..) => {
211 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((callee.fn_sig(tcx), ty::List::empty())))
212 }
213 ty::FnDef(def_id, args) => {
214 if tcx.intrinsic(def_id).is_some() {
216 return;
217 }
218 let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
219 tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
220 }
221 _ => {
222 { ::core::panicking::panic_fmt(format_args!("Invalid function call")); };panic!("Invalid function call");
223 }
224 };
225
226 let Ok(callee_abi) = callee_abi else {
227 return;
229 };
230 do_check_unsized_params(tcx, callee_abi, true, loc);
231 do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), true, loc);
232}
233
234fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
235 for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
237 let terminator = body.basic_blocks[bb].terminator();
238 match terminator.kind {
239 mir::TerminatorKind::Call { ref func, ref fn_span, .. }
240 | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => {
241 let callee_ty = func.ty(body, tcx);
242 let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions(
243 tcx,
244 ty::TypingEnv::fully_monomorphized(),
245 ty::EarlyBinder::bind(callee_ty),
246 );
247 check_call_site_abi(tcx, callee_ty, body.source.instance, || {
248 let loc = Location {
249 block: bb,
250 statement_index: body.basic_blocks[bb].statements.len(),
251 };
252 (
253 *fn_span,
254 body.source_info(loc)
255 .scope
256 .lint_root(&body.source_scopes)
257 .unwrap_or(CRATE_HIR_ID),
258 )
259 });
260 }
261 _ => {}
262 }
263 }
264}
265
266pub(crate) fn check_feature_dependent_abi<'tcx>(
267 tcx: TyCtxt<'tcx>,
268 instance: Instance<'tcx>,
269 body: &'tcx mir::Body<'tcx>,
270) {
271 check_instance_abi(tcx, instance);
272 check_callees_abi(tcx, instance, body);
273}