Auto merge of #132173 - veluca93:abi_checks, r= · rust-lang/rust@95e2c91 (original) (raw)
``
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.
`
``
3
`+
use rustc_hir::CRATE_HIR_ID;
`
``
4
`+
use rustc_middle::query::Providers;
`
``
5
`+
use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt};
`
``
6
`+
use rustc_session::lint::builtin::ABI_UNSUPPORTED_VECTOR_TYPES;
`
``
7
`+
use rustc_span::def_id::DefId;
`
``
8
`+
use rustc_span::{DUMMY_SP, Span, Symbol};
`
``
9
`+
use rustc_target::abi:🤙:{FnAbi, PassMode};
`
``
10
`+
use rustc_target::abi::{Abi, RegKind};
`
``
11
+
``
12
`+
use crate::errors::{AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef};
`
``
13
+
``
14
`+
fn uses_vector_registers(mode: &PassMode, abi: &Abi) -> bool {
`
``
15
`+
match mode {
`
``
16
`+
PassMode::Ignore | PassMode::Indirect { .. } => false,
`
``
17
`+
PassMode::Cast { pad_i32: _, cast } => {
`
``
18
`+
cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector))
`
``
19
`+
|| cast.rest.unit.kind == RegKind::Vector
`
``
20
`+
}
`
``
21
`+
PassMode::Direct(..) | PassMode::Pair(..) => matches!(abi, Abi::Vector { .. }),
`
``
22
`+
}
`
``
23
`+
}
`
``
24
+
``
25
`+
fn do_check_abi<'tcx>(
`
``
26
`+
tcx: TyCtxt<'tcx>,
`
``
27
`+
abi: &FnAbi<'tcx, Ty<'tcx>>,
`
``
28
`+
target_feature_def: DefId,
`
``
29
`+
mut emit_err: impl FnMut(&'static str),
`
``
30
`+
) {
`
``
31
`+
let Some(feature_def) = tcx.sess.target.features_for_correct_vector_abi() else {
`
``
32
`+
return;
`
``
33
`+
};
`
``
34
`+
let codegen_attrs = tcx.codegen_fn_attrs(target_feature_def);
`
``
35
`+
for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {
`
``
36
`+
let size = arg_abi.layout.size;
`
``
37
`+
if uses_vector_registers(&arg_abi.mode, &arg_abi.layout.abi) {
`
``
38
`+
// Find the first feature that provides at least this vector size.
`
``
39
`+
let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {
`
``
40
`+
Some((_, feature)) => feature,
`
``
41
`+
None => {
`
``
42
`+
emit_err("");
`
``
43
`+
continue;
`
``
44
`+
}
`
``
45
`+
};
`
``
46
`+
let feature_sym = Symbol::intern(feature);
`
``
47
`+
if !tcx.sess.unstable_target_features.contains(&feature_sym)
`
``
48
`+
&& !codegen_attrs.target_features.iter().any(|x| x.name == feature_sym)
`
``
49
`+
{
`
``
50
`+
emit_err(feature);
`
``
51
`+
}
`
``
52
`+
}
`
``
53
`+
}
`
``
54
`+
}
`
``
55
+
``
56
`+
/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments
`
``
57
`+
/// or return values for which the corresponding target feature is not enabled.
`
``
58
`+
fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
`
``
59
`+
let param_env = ParamEnv::reveal_all();
`
``
60
`+
let Ok(abi) = tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) else {
`
``
61
`+
// An error will be reported during codegen if we cannot determine the ABI of this
`
``
62
`+
// function.
`
``
63
`+
return;
`
``
64
`+
};
`
``
65
`+
do_check_abi(tcx, abi, instance.def_id(), |required_feature| {
`
``
66
`+
let span = tcx.def_span(instance.def_id());
`
``
67
`+
tcx.emit_node_span_lint(
`
``
68
`+
ABI_UNSUPPORTED_VECTOR_TYPES,
`
``
69
`+
CRATE_HIR_ID,
`
``
70
`+
span,
`
``
71
`+
AbiErrorDisabledVectorTypeDef { span, required_feature },
`
``
72
`+
);
`
``
73
`+
})
`
``
74
`+
}
`
``
75
+
``
76
`+
fn call_site_abi_missing_features<'tcx>(
`
``
77
`+
tcx: TyCtxt<'tcx>,
`
``
78
`+
(ty, caller): (Ty<'tcx>, InstanceKind<'tcx>),
`
``
79
`+
) -> Vec {
`
``
80
`+
let mut answer = vec![];
`
``
81
`+
let param_env = ParamEnv::reveal_all();
`
``
82
`+
let callee_abi = match *ty.kind() {
`
``
83
`+
ty::FnPtr(..) => tcx.fn_abi_of_fn_ptr(param_env.and((ty.fn_sig(tcx), ty::List::empty()))),
`
``
84
`+
ty::FnDef(def_id, args) => {
`
``
85
`+
// Intrinsics are handled separately by the compiler.
`
``
86
`+
if tcx.intrinsic(def_id).is_some() {
`
``
87
`+
return vec![];
`
``
88
`+
}
`
``
89
`+
let instance = ty::Instance::expect_resolve(tcx, param_env, def_id, args, DUMMY_SP);
`
``
90
`+
tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty())))
`
``
91
`+
}
`
``
92
`+
_ => {
`
``
93
`+
panic!("Invalid function call");
`
``
94
`+
}
`
``
95
`+
};
`
``
96
+
``
97
`+
let Ok(callee_abi) = callee_abi else {
`
``
98
`+
// ABI failed to compute; this will not get through codegen.
`
``
99
`+
return vec![];
`
``
100
`+
};
`
``
101
`+
do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| {
`
``
102
`+
answer.push(required_feature.to_string());
`
``
103
`+
});
`
``
104
`+
answer
`
``
105
`+
}
`
``
106
+
``
107
`+
/// Checks that a call expression does not try to pass a vector-passed argument which requires a
`
``
108
`+
/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch.
`
``
109
`+
pub(super) fn check_call_site_abi<'tcx>(
`
``
110
`+
tcx: TyCtxt<'tcx>,
`
``
111
`+
ty: Ty<'tcx>,
`
``
112
`+
span: Span,
`
``
113
`+
caller: InstanceKind<'tcx>,
`
``
114
`+
) {
`
``
115
`+
for required_feature in tcx.call_site_abi_missing_features((ty, caller)) {
`
``
116
`+
tcx.emit_node_span_lint(
`
``
117
`+
ABI_UNSUPPORTED_VECTOR_TYPES,
`
``
118
`+
CRATE_HIR_ID,
`
``
119
`+
span,
`
``
120
`+
AbiErrorDisabledVectorTypeCall { span, required_feature },
`
``
121
`+
);
`
``
122
`+
}
`
``
123
`+
}
`
``
124
+
``
125
`+
pub(super) fn provide(providers: &mut Providers) {
`
``
126
`+
*providers = Providers { check_instance_abi, call_site_abi_missing_features, ..*providers }
`
``
127
`+
}
`