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