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
29 .prefix
30 .iter()
31 .any(|r| r.is_some_and(|x| #[allow(non_exhaustive_omitted_patterns)] match x.kind {
RegKind::Vector { .. } => true,
_ => false,
}matches!(x.kind, RegKind::Vector { .. })))
32 || #[allow(non_exhaustive_omitted_patterns)] match cast.rest.unit.kind {
RegKind::Vector { .. } => true,
_ => false,
}matches!(cast.rest.unit.kind, RegKind::Vector { .. }) =>
33 {
34 UsesVectorRegisters::FixedVector
35 }
36 PassMode::Direct(..) | PassMode::Pair(..)
37 if #[allow(non_exhaustive_omitted_patterns)] match repr {
BackendRepr::SimdVector { .. } => true,
_ => false,
}matches!(repr, BackendRepr::SimdVector { .. }) =>
38 {
39 UsesVectorRegisters::FixedVector
40 }
41 PassMode::Direct(..) | PassMode::Pair(..)
42 if #[allow(non_exhaustive_omitted_patterns)] match repr {
BackendRepr::SimdScalableVector { .. } => true,
_ => false,
}matches!(repr, BackendRepr::SimdScalableVector { .. }) =>
43 {
44 UsesVectorRegisters::ScalableVector
45 }
46 _ => UsesVectorRegisters::No,
47 }
48}
49
50fn do_check_simd_vector_abi<'tcx>(
55 tcx: TyCtxt<'tcx>,
56 abi: &FnAbi<'tcx, Ty<'tcx>>,
57 def_id: DefId,
58 is_call: bool,
59 loc: impl Fn() -> (Span, HirId),
60) {
61 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
62 let have_feature = |feat: Symbol| {
63 let target_feats = tcx.sess.unstable_target_features.contains(&feat);
64 let fn_feats = codegen_attrs.target_features.iter().any(|x| x.name == feat);
65 target_feats || fn_feats
66 };
67 for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {
68 let size = arg_abi.layout.size;
69 match passes_vectors_by_value(&arg_abi.mode, &arg_abi.layout.backend_repr) {
70 UsesVectorRegisters::FixedVector => {
71 let feature_def = tcx.sess.target.features_for_correct_fixed_length_vector_abi();
72 let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {
74 Some((_, feature)) => feature,
75 None => {
76 let (span, _hir_id) = loc();
77 tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType {
78 span,
79 ty: arg_abi.layout.ty,
80 is_call,
81 });
82 continue;
83 }
84 };
85 if !feature.is_empty() && !have_feature(Symbol::intern(feature)) {
86 let (span, _hir_id) = loc();
87 tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
88 span,
89 required_feature: feature,
90 ty: arg_abi.layout.ty,
91 is_call,
92 is_scalable: false,
93 });
94 }
95 }
96 UsesVectorRegisters::ScalableVector => {
97 let Some(required_feature) =
98 tcx.sess.target.features_for_correct_scalable_vector_abi()
99 else {
100 continue;
101 };
102 if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) {
103 let (span, _) = loc();
104 tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
105 span,
106 required_feature,
107 ty: arg_abi.layout.ty,
108 is_call,
109 is_scalable: true,
110 });
111 }
112 }
113 UsesVectorRegisters::No => {
114 continue;
115 }
116 }
117 }
118 if abi.conv == CanonAbi::X86(X86Call::Vectorcall) && !have_feature(sym::sse2) {
120 let (span, _hir_id) = loc();
121 tcx.dcx().emit_err(errors::AbiRequiredTargetFeature {
122 span,
123 required_feature: "sse2",
124 abi: "vectorcall",
125 is_call,
126 });
127 }
128}
129
130fn do_check_unsized_params<'tcx>(
135 tcx: TyCtxt<'tcx>,
136 fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
137 is_call: bool,
138 loc: impl Fn() -> (Span, HirId),
139) {
140 if fn_abi.conv.is_rustic_abi() {
142 return;
143 }
144
145 for arg_abi in fn_abi.args.iter() {
146 if !arg_abi.layout.layout.is_sized() {
147 let (span, _hir_id) = loc();
148 tcx.dcx().emit_err(errors::AbiErrorUnsupportedUnsizedParameter {
149 span,
150 ty: arg_abi.layout.ty,
151 is_call,
152 });
153 }
154 }
155}
156
157fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
162 let typing_env = ty::TypingEnv::fully_monomorphized();
163 let ty = instance.ty(tcx, typing_env);
164 if ty.is_fn() && ty.fn_sig(tcx).abi() == ExternAbi::Unadjusted {
165 return;
168 }
169 let Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
170 else {
171 tcx.dcx().delayed_bug("ABI computation failure should lead to compilation failure");
174 return;
175 };
176 let loc = || {
183 let def_id = instance.def_id();
184 (
185 tcx.def_span(def_id),
186 def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID),
187 )
188 };
189 do_check_unsized_params(tcx, abi, false, loc);
190 do_check_simd_vector_abi(tcx, abi, instance.def_id(), false, loc);
191}
192
193fn check_call_site_abi<'tcx>(
198 tcx: TyCtxt<'tcx>,
199 callee: Ty<'tcx>,
200 caller: InstanceKind<'tcx>,
201 loc: impl Fn() -> (Span, HirId) + Copy,
202) {
203 let extern_abi = callee.fn_sig(tcx).abi();
204 if extern_abi.is_rustic_abi() || extern_abi == ExternAbi::Unadjusted {
205 return;
210 }
211 let typing_env = ty::TypingEnv::fully_monomorphized();
212 let callee_abi = match *callee.kind() {
213 ty::FnPtr(..) => {
214 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((callee.fn_sig(tcx), ty::List::empty())))
215 }
216 ty::FnDef(def_id, args) => {
217 if tcx.intrinsic(def_id).is_some() {
219 return;
220 }
221 let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
222 tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
223 }
224 _ => {
225 { ::core::panicking::panic_fmt(format_args!("Invalid function call")); };panic!("Invalid function call");
226 }
227 };
228
229 let Ok(callee_abi) = callee_abi else {
230 return;
232 };
233 do_check_unsized_params(tcx, callee_abi, true, loc);
234 do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), true, loc);
235}
236
237fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
238 for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
240 let terminator = body.basic_blocks[bb].terminator();
241 match terminator.kind {
242 mir::TerminatorKind::Call { ref func, ref fn_span, .. }
243 | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => {
244 let callee_ty = func.ty(body, tcx);
245 let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions(
246 tcx,
247 ty::TypingEnv::fully_monomorphized(),
248 ty::EarlyBinder::bind(callee_ty),
249 );
250 check_call_site_abi(tcx, callee_ty, body.source.instance, || {
251 let loc = Location {
252 block: bb,
253 statement_index: body.basic_blocks[bb].statements.len(),
254 };
255 (
256 *fn_span,
257 body.source_info(loc)
258 .scope
259 .lint_root(&body.source_scopes)
260 .unwrap_or(CRATE_HIR_ID),
261 )
262 });
263 }
264 _ => {}
265 }
266 }
267}
268
269pub(crate) fn check_feature_dependent_abi<'tcx>(
270 tcx: TyCtxt<'tcx>,
271 instance: Instance<'tcx>,
272 body: &'tcx mir::Body<'tcx>,
273) {
274 check_instance_abi(tcx, instance);
275 check_callees_abi(tcx, instance, body);
276}