rustc_monomorphize/mono_checks/
abi_check.rs1use rustc_abi::{BackendRepr, CanonAbi, 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(|r| r.is_some_and(|x| x.kind == RegKind::Vector))
29 || cast.rest.unit.kind == RegKind::Vector =>
30 {
31 UsesVectorRegisters::FixedVector
32 }
33 PassMode::Direct(..) | PassMode::Pair(..)
34 if matches!(repr, BackendRepr::SimdVector { .. }) =>
35 {
36 UsesVectorRegisters::FixedVector
37 }
38 PassMode::Direct(..) | PassMode::Pair(..)
39 if matches!(repr, BackendRepr::ScalableVector { .. }) =>
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 Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
161 else {
162 tcx.dcx().delayed_bug("ABI computation failure should lead to compilation failure");
165 return;
166 };
167 let loc = || {
174 let def_id = instance.def_id();
175 (
176 tcx.def_span(def_id),
177 def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID),
178 )
179 };
180 do_check_unsized_params(tcx, abi, false, loc);
181 do_check_simd_vector_abi(tcx, abi, instance.def_id(), false, loc);
182}
183
184fn check_call_site_abi<'tcx>(
189 tcx: TyCtxt<'tcx>,
190 callee: Ty<'tcx>,
191 caller: InstanceKind<'tcx>,
192 loc: impl Fn() -> (Span, HirId) + Copy,
193) {
194 if callee.fn_sig(tcx).abi().is_rustic_abi() {
195 return;
198 }
199 let typing_env = ty::TypingEnv::fully_monomorphized();
200 let callee_abi = match *callee.kind() {
201 ty::FnPtr(..) => {
202 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((callee.fn_sig(tcx), ty::List::empty())))
203 }
204 ty::FnDef(def_id, args) => {
205 if tcx.intrinsic(def_id).is_some() {
207 return;
208 }
209 let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
210 tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
211 }
212 _ => {
213 panic!("Invalid function call");
214 }
215 };
216
217 let Ok(callee_abi) = callee_abi else {
218 return;
220 };
221 do_check_unsized_params(tcx, callee_abi, true, loc);
222 do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), true, loc);
223}
224
225fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
226 for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
228 let terminator = body.basic_blocks[bb].terminator();
229 match terminator.kind {
230 mir::TerminatorKind::Call { ref func, ref fn_span, .. }
231 | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => {
232 let callee_ty = func.ty(body, tcx);
233 let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions(
234 tcx,
235 ty::TypingEnv::fully_monomorphized(),
236 ty::EarlyBinder::bind(callee_ty),
237 );
238 check_call_site_abi(tcx, callee_ty, body.source.instance, || {
239 let loc = Location {
240 block: bb,
241 statement_index: body.basic_blocks[bb].statements.len(),
242 };
243 (
244 *fn_span,
245 body.source_info(loc)
246 .scope
247 .lint_root(&body.source_scopes)
248 .unwrap_or(CRATE_HIR_ID),
249 )
250 });
251 }
252 _ => {}
253 }
254 }
255}
256
257pub(crate) fn check_feature_dependent_abi<'tcx>(
258 tcx: TyCtxt<'tcx>,
259 instance: Instance<'tcx>,
260 body: &'tcx mir::Body<'tcx>,
261) {
262 check_instance_abi(tcx, instance);
263 check_callees_abi(tcx, instance, body);
264}