rustc_monomorphize/mono_checks/
abi_check.rs

1//! This module ensures that if a function's ABI requires a particular target feature,
2//! that target feature is enabled both on the callee and all callers.
3use rustc_abi::{BackendRepr, RegKind};
4use rustc_hir::CRATE_HIR_ID;
5use rustc_middle::mir::{self, traversal};
6use rustc_middle::ty::inherent::*;
7use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt};
8use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES;
9use rustc_span::def_id::DefId;
10use rustc_span::{DUMMY_SP, Span, Symbol};
11use rustc_target::callconv::{FnAbi, PassMode};
12
13use crate::errors::{
14    AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef,
15    AbiErrorUnsupportedVectorTypeCall, AbiErrorUnsupportedVectorTypeDef,
16};
17
18fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {
19    match mode {
20        PassMode::Ignore | PassMode::Indirect { .. } => false,
21        PassMode::Cast { pad_i32: _, cast } => {
22            cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector))
23                || cast.rest.unit.kind == RegKind::Vector
24        }
25        PassMode::Direct(..) | PassMode::Pair(..) => matches!(repr, BackendRepr::Vector { .. }),
26    }
27}
28
29/// Checks whether a certain function ABI is compatible with the target features currently enabled
30/// for a certain function.
31/// If not, `emit_err` is called, with `Some(feature)` if a certain feature should be enabled and
32/// with `None` if no feature is known that would make the ABI compatible.
33fn do_check_abi<'tcx>(
34    tcx: TyCtxt<'tcx>,
35    abi: &FnAbi<'tcx, Ty<'tcx>>,
36    target_feature_def: DefId,
37    mut emit_err: impl FnMut(Option<&'static str>),
38) {
39    let feature_def = tcx.sess.target.features_for_correct_vector_abi();
40    let codegen_attrs = tcx.codegen_fn_attrs(target_feature_def);
41    for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {
42        let size = arg_abi.layout.size;
43        if uses_vector_registers(&arg_abi.mode, &arg_abi.layout.backend_repr) {
44            // Find the first feature that provides at least this vector size.
45            let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {
46                Some((_, feature)) => feature,
47                None => {
48                    emit_err(None);
49                    continue;
50                }
51            };
52            let feature_sym = Symbol::intern(feature);
53            if !tcx.sess.unstable_target_features.contains(&feature_sym)
54                && !codegen_attrs.target_features.iter().any(|x| x.name == feature_sym)
55            {
56                emit_err(Some(&feature));
57            }
58        }
59    }
60}
61
62/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments
63/// or return values for which the corresponding target feature is not enabled.
64fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
65    let typing_env = ty::TypingEnv::fully_monomorphized();
66    let Ok(abi) = tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
67    else {
68        // An error will be reported during codegen if we cannot determine the ABI of this
69        // function.
70        return;
71    };
72    do_check_abi(tcx, abi, instance.def_id(), |required_feature| {
73        let span = tcx.def_span(instance.def_id());
74        if let Some(required_feature) = required_feature {
75            tcx.emit_node_span_lint(
76                ABI_UNSUPPORTED_VECTOR_TYPES,
77                CRATE_HIR_ID,
78                span,
79                AbiErrorDisabledVectorTypeDef { span, required_feature },
80            );
81        } else {
82            tcx.emit_node_span_lint(
83                ABI_UNSUPPORTED_VECTOR_TYPES,
84                CRATE_HIR_ID,
85                span,
86                AbiErrorUnsupportedVectorTypeDef { span },
87            );
88        }
89    })
90}
91
92/// Checks that a call expression does not try to pass a vector-passed argument which requires a
93/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch.
94fn check_call_site_abi<'tcx>(
95    tcx: TyCtxt<'tcx>,
96    callee: Ty<'tcx>,
97    span: Span,
98    caller: InstanceKind<'tcx>,
99) {
100    if callee.fn_sig(tcx).abi().is_rust() {
101        // "Rust" ABI never passes arguments in vector registers.
102        return;
103    }
104    let typing_env = ty::TypingEnv::fully_monomorphized();
105    let callee_abi = match *callee.kind() {
106        ty::FnPtr(..) => {
107            tcx.fn_abi_of_fn_ptr(typing_env.as_query_input((callee.fn_sig(tcx), ty::List::empty())))
108        }
109        ty::FnDef(def_id, args) => {
110            // Intrinsics are handled separately by the compiler.
111            if tcx.intrinsic(def_id).is_some() {
112                return;
113            }
114            let instance = ty::Instance::expect_resolve(tcx, typing_env, def_id, args, DUMMY_SP);
115            tcx.fn_abi_of_instance(typing_env.as_query_input((instance, ty::List::empty())))
116        }
117        _ => {
118            panic!("Invalid function call");
119        }
120    };
121
122    let Ok(callee_abi) = callee_abi else {
123        // ABI failed to compute; this will not get through codegen.
124        return;
125    };
126    do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| {
127        if let Some(required_feature) = required_feature {
128            tcx.emit_node_span_lint(
129                ABI_UNSUPPORTED_VECTOR_TYPES,
130                CRATE_HIR_ID,
131                span,
132                AbiErrorDisabledVectorTypeCall { span, required_feature },
133            );
134        } else {
135            tcx.emit_node_span_lint(
136                ABI_UNSUPPORTED_VECTOR_TYPES,
137                CRATE_HIR_ID,
138                span,
139                AbiErrorUnsupportedVectorTypeCall { span },
140            );
141        }
142    });
143}
144
145fn check_callees_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, body: &mir::Body<'tcx>) {
146    // Check all function call terminators.
147    for (bb, _data) in traversal::mono_reachable(body, tcx, instance) {
148        let terminator = body.basic_blocks[bb].terminator();
149        match terminator.kind {
150            mir::TerminatorKind::Call { ref func, ref fn_span, .. }
151            | mir::TerminatorKind::TailCall { ref func, ref fn_span, .. } => {
152                let callee_ty = func.ty(body, tcx);
153                let callee_ty = instance.instantiate_mir_and_normalize_erasing_regions(
154                    tcx,
155                    ty::TypingEnv::fully_monomorphized(),
156                    ty::EarlyBinder::bind(callee_ty),
157                );
158                check_call_site_abi(tcx, callee_ty, *fn_span, body.source.instance);
159            }
160            _ => {}
161        }
162    }
163}
164
165pub(crate) fn check_feature_dependent_abi<'tcx>(
166    tcx: TyCtxt<'tcx>,
167    instance: Instance<'tcx>,
168    body: &'tcx mir::Body<'tcx>,
169) {
170    check_instance_abi(tcx, instance);
171    check_callees_abi(tcx, instance, body);
172}