Auto merge of #127731 - veluca93:abi_checks, r= · rust-lang/rust@7587ff3 (original) (raw)
``
1
`+
use rustc_abi::Abi;
`
``
2
`+
use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt};
`
``
3
`+
use rustc_span::{def_id::DefId, Span, Symbol};
`
``
4
`+
use rustc_target::abi:🤙:{FnAbi, PassMode};
`
``
5
+
``
6
`+
use crate::errors::{AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef};
`
``
7
+
``
8
`+
const SSE_FEATURES: &'static [&'static str] = &["sse", "sse2", "ssse3", "sse3", "sse4.1", "sse4.2"];
`
``
9
`+
const AVX_FEATURES: &'static [&'static str] = &["avx", "avx2", "f16c", "fma"];
`
``
10
`+
const AVX512_FEATURES: &'static [&'static str] = &[
`
``
11
`+
"avx512f",
`
``
12
`+
"avx512bw",
`
``
13
`+
"avx512cd",
`
``
14
`+
"avx512er",
`
``
15
`+
"avx512pf",
`
``
16
`+
"avx512vl",
`
``
17
`+
"avx512dq",
`
``
18
`+
"avx512ifma",
`
``
19
`+
"avx512vbmi",
`
``
20
`+
"avx512vnni",
`
``
21
`+
"avx512bitalg",
`
``
22
`+
"avx512vpopcntdq",
`
``
23
`+
"avx512bf16",
`
``
24
`+
"avx512vbmi2",
`
``
25
`+
];
`
``
26
+
``
27
`+
fn do_check_abi<'tcx>(
`
``
28
`+
tcx: TyCtxt<'tcx>,
`
``
29
`+
abi: &FnAbi<'tcx, Ty<'tcx>>,
`
``
30
`+
target_feature_def: DefId,
`
``
31
`+
emit_err: impl Fn(&'static str),
`
``
32
`+
) {
`
``
33
`+
// FIXME: add support for other architectures
`
``
34
`+
if tcx.sess.target.arch != "x86" && tcx.sess.target.arch != "x86_64" {
`
``
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 matches!(arg_abi.layout.abi, Abi::Vector { .. })
`
``
41
`+
&& matches!(arg_abi.mode, PassMode::Direct(_))
`
``
42
`+
{
`
``
43
`+
let features: &[_] = match size.bits() {
`
``
44
`+
x if x <= 128 => &[SSE_FEATURES, AVX_FEATURES, AVX512_FEATURES],
`
``
45
`+
x if x <= 256 => &[AVX_FEATURES, AVX512_FEATURES],
`
``
46
`+
x if x <= 512 => &[AVX512_FEATURES],
`
``
47
`+
_ => {
`
``
48
`+
panic!("Unknown vector size for x86: {}; arg = {:?}", size.bits(), arg_abi)
`
``
49
`+
}
`
``
50
`+
};
`
``
51
`+
let required_feature = features.iter().map(|x| x.iter()).flatten().next().unwrap();
`
``
52
`+
if !features.iter().map(|x| x.iter()).flatten().any(|feature| {
`
``
53
`+
let required_feature_sym = Symbol::intern(feature);
`
``
54
`+
tcx.sess.unstable_target_features.contains(&required_feature_sym)
`
``
55
`+
|| codegen_attrs.target_features.contains(&required_feature_sym)
`
``
56
`+
}) {
`
``
57
`+
emit_err(required_feature);
`
``
58
`+
}
`
``
59
`+
}
`
``
60
`+
}
`
``
61
`+
}
`
``
62
+
``
63
`+
/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments
`
``
64
`+
/// or return values for which the corresponding target feature is not enabled.
`
``
65
`+
pub fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
`
``
66
`+
let param_env = ParamEnv::reveal_all();
`
``
67
`+
let Ok(abi) = tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) 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
`+
tcx.dcx().emit_err(AbiErrorDisabledVectorTypeDef {
`
``
74
`+
span: tcx.def_span(instance.def_id()),
`
``
75
`+
required_feature,
`
``
76
`+
});
`
``
77
`+
})
`
``
78
`+
}
`
``
79
+
``
80
`+
/// Checks that a call expression does not try to pass a vector-passed argument which requires a
`
``
81
`+
/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch.
`
``
82
`+
pub fn check_call_site_abi<'tcx>(
`
``
83
`+
tcx: TyCtxt<'tcx>,
`
``
84
`+
ty: Ty<'tcx>,
`
``
85
`+
span: Span,
`
``
86
`+
caller: InstanceKind<'tcx>,
`
``
87
`+
) {
`
``
88
`+
let param_env = ParamEnv::reveal_all();
`
``
89
`+
let callee_abi = match *ty.kind() {
`
``
90
`+
ty::FnPtr(sig) => tcx.fn_abi_of_fn_ptr(param_env.and((sig, ty::List::empty()))),
`
``
91
`+
ty::FnDef(def_id, args) => {
`
``
92
`+
// Intrinsics are handled separately by the compiler.
`
``
93
`+
if tcx.intrinsic(def_id).is_some() {
`
``
94
`+
return;
`
``
95
`+
}
`
``
96
`+
let instance = ty::Instance::expect_resolve(tcx, param_env, def_id, args, span);
`
``
97
`+
tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty())))
`
``
98
`+
}
`
``
99
`+
_ => {
`
``
100
`+
panic!("Invalid function call");
`
``
101
`+
}
`
``
102
`+
};
`
``
103
+
``
104
`+
let Ok(callee_abi) = callee_abi else {
`
``
105
`+
// ABI failed to compute; this will not get through codegen.
`
``
106
`+
return;
`
``
107
`+
};
`
``
108
`+
do_check_abi(tcx, callee_abi, caller.def_id(), |required_feature| {
`
``
109
`+
tcx.dcx().emit_err(AbiErrorDisabledVectorTypeCall { span, required_feature });
`
``
110
`+
})
`
``
111
`+
}
`