rustc_monomorphize/mono_checks/
abi_check.rs1use rustc_abi::{BackendRepr, RegKind};
4use rustc_hir::{CRATE_HIR_ID, HirId};
5use rustc_middle::mir::{self, Location, traversal};
6use rustc_middle::ty::layout::LayoutCx;
7use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt, TypingEnv};
8use rustc_session::lint::builtin::{ABI_UNSUPPORTED_VECTOR_TYPES, WASM_C_ABI};
9use rustc_span::def_id::DefId;
10use rustc_span::{DUMMY_SP, Span, Symbol, sym};
11use rustc_target::callconv::{ArgAbi, Conv, FnAbi, PassMode};
12use rustc_target::spec::{HasWasmCAbiOpt, WasmCAbi};
13
14use crate::errors;
15
16fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {
17 match mode {
18 PassMode::Ignore | PassMode::Indirect { .. } => false,
19 PassMode::Cast { pad_i32: _, cast } => {
20 cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector))
21 || cast.rest.unit.kind == RegKind::Vector
22 }
23 PassMode::Direct(..) | PassMode::Pair(..) => matches!(repr, BackendRepr::SimdVector { .. }),
24 }
25}
26
27fn do_check_simd_vector_abi<'tcx>(
32 tcx: TyCtxt<'tcx>,
33 abi: &FnAbi<'tcx, Ty<'tcx>>,
34 def_id: DefId,
35 is_call: bool,
36 loc: impl Fn() -> (Span, HirId),
37) {
38 let feature_def = tcx.sess.target.features_for_correct_vector_abi();
41 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
42 let have_feature = |feat: Symbol| {
43 tcx.sess.unstable_target_features.contains(&feat)
44 || codegen_attrs.target_features.iter().any(|x| x.name == feat)
45 };
46 for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {
47 let size = arg_abi.layout.size;
48 if uses_vector_registers(&arg_abi.mode, &arg_abi.layout.backend_repr) {
49 let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {
51 Some((_, feature)) => feature,
52 None => {
53 let (span, hir_id) = loc();
54 tcx.emit_node_span_lint(
55 ABI_UNSUPPORTED_VECTOR_TYPES,
56 hir_id,
57 span,
58 errors::AbiErrorUnsupportedVectorType {
59 span,
60 ty: arg_abi.layout.ty,
61 is_call,
62 },
63 );
64 continue;
65 }
66 };
67 if !have_feature(Symbol::intern(feature)) {
68 let (span, hir_id) = loc();
70 tcx.emit_node_span_lint(
71 ABI_UNSUPPORTED_VECTOR_TYPES,
72 hir_id,
73 span,
74 errors::AbiErrorDisabledVectorType {
75 span,
76 required_feature: feature,
77 ty: arg_abi.layout.ty,
78 is_call,
79 },
80 );
81 }
82 }
83 }
84 if abi.conv == Conv::X86VectorCall && !have_feature(sym::sse2) {
86 let (span, _hir_id) = loc();
87 tcx.dcx().emit_err(errors::AbiRequiredTargetFeature {
88 span,
89 required_feature: "sse2",
90 abi: "vectorcall",
91 is_call,
92 });
93 }
94}
95
96fn wasm_abi_safe<'tcx>(tcx: TyCtxt<'tcx>, arg: &ArgAbi<'tcx, Ty<'tcx>>) -> bool {
98 if matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
99 return true;
100 }
101
102 if uses_vector_registers(&arg.mode, &arg.layout.backend_repr) {
105 return true;
106 }
107
108 if arg.layout.is_aggregate() {
110 let cx = LayoutCx::new(tcx, TypingEnv::fully_monomorphized());
111 if let Some(unit) = arg.layout.homogeneous_aggregate(&cx).ok().and_then(|ha| ha.unit()) {
112 let size = arg.layout.size;
113 if unit.size == size {
115 return true;
116 }
117 }
118 }
119
120 if arg.layout.is_zst() {
122 return true;
123 }
124
125 false
126}
127
128fn do_check_wasm_abi<'tcx>(
131 tcx: TyCtxt<'tcx>,
132 abi: &FnAbi<'tcx, Ty<'tcx>>,
133 is_call: bool,
134 loc: impl Fn() -> (Span, HirId),
135) {
136 if !(tcx.sess.target.arch == "wasm32"
139 && tcx.sess.target.os == "unknown"
140 && tcx.wasm_c_abi_opt() == WasmCAbi::Legacy { with_lint: true }
141 && abi.conv == Conv::C)
142 {
143 return;
144 }
145 for arg_abi in abi.args.iter() {
147 if wasm_abi_safe(tcx, arg_abi) {
148 continue;
149 }
150 let (span, hir_id) = loc();
151 tcx.emit_node_span_lint(
152 WASM_C_ABI,
153 hir_id,
154 span,
155 errors::WasmCAbiTransition { ty: arg_abi.layout.ty, is_call },
156 );
157 break;
159 }
160}
161
162fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
165 let typing_env = ty::TypingEnv::fully_monomorphized();
166 let Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
167 else {
168 return;
171 };
172 let loc = || {
173 let def_id = instance.def_id();
174 (
175 tcx.def_span(def_id),
176 def_id.as_local().map(|did| tcx.local_def_id_to_hir_id(did)).unwrap_or(CRATE_HIR_ID),
177 )
178 };
179 do_check_simd_vector_abi(tcx, abi, instance.def_id(), false, loc);
180 do_check_wasm_abi(tcx, abi, false, loc);
181}
182
183fn check_call_site_abi<'tcx>(
186 tcx: TyCtxt<'tcx>,
187 callee: Ty<'tcx>,
188 caller: InstanceKind<'tcx>,
189 loc: impl Fn() -> (Span, HirId) + Copy,
190) {
191 if callee.fn_sig(tcx).abi().is_rustic_abi() {
192 return;
194 }
195 let typing_env = ty::TypingEnv::fully_monomorphized();
196 let callee_abi = match *callee.kind() {
197 ty::FnPtr(..) => {
198 tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((callee.fn_sig(tcx), ty::List::empty())))
199 }
200 ty::FnDef(def_id, args) => {
201 if tcx.intrinsic(def_id).is_some() {
203 return;
204 }
205 let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
206 tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
207 }
208 _ => {
209 panic!("Invalid function call");
210 }
211 };
212
213 let Ok(callee_abi) = callee_abi else {
214 return;
216 };
217 do_check_simd_vector_abi(tcx, callee_abi, caller.def_id(), true, loc);
218 do_check_wasm_abi(tcx, callee_abi, true, loc);
219}
220
221fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
222 for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
224 let terminator = body.basic_blocks[bb].terminator();
225 match terminator.kind {
226 mir::TerminatorKind::Call { ref func, ref fn_span, .. }
227 | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => {
228 let callee_ty = func.ty(body, tcx);
229 let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions(
230 tcx,
231 ty::TypingEnv::fully_monomorphized(),
232 ty::EarlyBinder::bind(callee_ty),
233 );
234 check_call_site_abi(tcx, callee_ty, body.source.instance, || {
235 let loc = Location {
236 block: bb,
237 statement_index: body.basic_blocks[bb].statements.len(),
238 };
239 (
240 *fn_span,
241 body.source_info(loc)
242 .scope
243 .lint_root(&body.source_scopes)
244 .unwrap_or(CRATE_HIR_ID),
245 )
246 });
247 }
248 _ => {}
249 }
250 }
251}
252
253pub(crate) fn check_feature_dependent_abi<'tcx>(
254 tcx: TyCtxt<'tcx>,
255 instance: Instance<'tcx>,
256 body: &'tcx mir::Body<'tcx>,
257) {
258 check_instance_abi(tcx, instance);
259 check_callees_abi(tcx, instance, body);
260}