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
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 Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
164 else {
165 tcx.dcx().delayed_bug("ABI computation failure should lead to compilation failure");
168 return;
169 };
170 let loc = || {
177 let def_id = instance.def_id();
178 (
179 tcx.def_span(def_id),
180 def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID),
181 )
182 };
183 do_check_unsized_params(tcx, abi, false, loc);
184 do_check_simd_vector_abi(tcx, abi, instance.def_id(), false, loc);
185}
186
187fn check_call_site_abi<'tcx>(
192 tcx: TyCtxt<'tcx>,
193 callee: Ty<'tcx>,
194 caller: InstanceKind<'tcx>,
195 loc: impl Fn() -> (Span, HirId) + Copy,
196) {
197 if callee.fn_sig(tcx).abi().is_rustic_abi() {
198 return;
201 }
202 let typing_env = ty::TypingEnv::fully_monomorphized();
203 let callee_abi = match *callee.kind() {
204 ty::FnPtr(..) => {
205 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((callee.fn_sig(tcx), ty::List::empty())))
206 }
207 ty::FnDef(def_id, args) => {
208 if tcx.intrinsic(def_id).is_some() {
210 return;
211 }
212 let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
213 tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
214 }
215 _ => {
216 { ::core::panicking::panic_fmt(format_args!("Invalid function call")); };panic!("Invalid function call");
217 }
218 };
219
220 let Ok(callee_abi) = callee_abi else {
221 return;
223 };
224 do_check_unsized_params(tcx, callee_abi, true, loc);
225 do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), true, loc);
226}
227
228fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
229 for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
231 let terminator = body.basic_blocks[bb].terminator();
232 match terminator.kind {
233 mir::TerminatorKind::Call { ref func, ref fn_span, .. }
234 | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => {
235 let callee_ty = func.ty(body, tcx);
236 let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions(
237 tcx,
238 ty::TypingEnv::fully_monomorphized(),
239 ty::EarlyBinder::bind(callee_ty),
240 );
241 check_call_site_abi(tcx, callee_ty, body.source.instance, || {
242 let loc = Location {
243 block: bb,
244 statement_index: body.basic_blocks[bb].statements.len(),
245 };
246 (
247 *fn_span,
248 body.source_info(loc)
249 .scope
250 .lint_root(&body.source_scopes)
251 .unwrap_or(CRATE_HIR_ID),
252 )
253 });
254 }
255 _ => {}
256 }
257 }
258}
259
260pub(crate) fn check_feature_dependent_abi<'tcx>(
261 tcx: TyCtxt<'tcx>,
262 instance: Instance<'tcx>,
263 body: &'tcx mir::Body<'tcx>,
264) {
265 check_instance_abi(tcx, instance);
266 check_callees_abi(tcx, instance, body);
267}