Skip to content

Commit ebc85de

Browse files
committed
Emit error when calling/declaring functions with unavailable vectors.
On some architectures, vector types may have a different ABI when relevant target features are enabled. As discussed in rust-lang/lang-team#235, this turns out to very easily lead to unsound code. This commit makes it an error to declare or call functions using those vector types in a context in which the corresponding target features are disabled, if using an ABI for which the difference is relevant.
1 parent 62c068f commit ebc85de

File tree

6 files changed

+126
-0
lines changed

6 files changed

+126
-0
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4458,6 +4458,7 @@ dependencies = [
44584458
name = "rustc_monomorphize"
44594459
version = "0.0.0"
44604460
dependencies = [
4461+
"rustc_abi",
44614462
"rustc_data_structures",
44624463
"rustc_errors",
44634464
"rustc_fluent_macro",

compiler/rustc_monomorphize/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
# tidy-alphabetical-start
8+
rustc_abi = { path = "../rustc_abi" }
89
rustc_data_structures = { path = "../rustc_data_structures" }
910
rustc_errors = { path = "../rustc_errors" }
1011
rustc_fluent_macro = { path = "../rustc_fluent_macro" }

compiler/rustc_monomorphize/messages.ftl

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
monomorphize_abi_error_disabled_vector_type_call =
2+
ABI error: this function call uses a {$required_feature} vector type, which is not enabled in the caller
3+
.help = consider enabling it globally (-C target-feature=+{$required_feature}) or locally (#[target_feature(enable="{$required_feature}")])
4+
monomorphize_abi_error_disabled_vector_type_def =
5+
ABI error: this function definition uses a {$required_feature} vector type, which is not enabled
6+
.help = consider enabling it globally (-C target-feature=+{$required_feature}) or locally (#[target_feature(enable="{$required_feature}")])
7+
18
monomorphize_couldnt_dump_mono_stats =
29
unexpected error occurred while dumping monomorphization stats: {$error}
310

compiler/rustc_monomorphize/src/collector.rs

+3
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
//! this is not implemented however: a mono item will be produced
206206
//! regardless of whether it is actually needed or not.
207207
208+
mod abi_check;
208209
mod move_check;
209210

210211
use rustc_data_structures::sync::{par_for_each_in, LRef, MTLock};
@@ -762,6 +763,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
762763
self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty));
763764
let callee_ty = self.monomorphize(callee_ty);
764765
self.check_fn_args_move_size(callee_ty, args, *fn_span, location);
766+
abi_check::check_call_abi(tcx, callee_ty, *fn_span, self.body.source.instance);
765767
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.used_items)
766768
}
767769
mir::TerminatorKind::Drop { ref place, .. } => {
@@ -1199,6 +1201,7 @@ fn collect_items_of_instance<'tcx>(
11991201
mentioned_items: &mut MonoItems<'tcx>,
12001202
mode: CollectionMode,
12011203
) {
1204+
abi_check::check_instance_abi(tcx, instance);
12021205
let body = tcx.instance_mir(instance.def);
12031206
// Naively, in "used" collection mode, all functions get added to *both* `used_items` and
12041207
// `mentioned_items`. Mentioned items processing will then notice that they have already been
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
use rustc_abi::Abi;
2+
use rustc_middle::ty::{self, Instance, InstanceKind, Ty, TyCtxt};
3+
use rustc_span::{def_id::DefId, Span, Symbol};
4+
use rustc_target::abi::call::FnAbi;
5+
6+
use crate::errors::{AbiErrorDisabledVectorTypeCall, AbiErrorDisabledVectorTypeDef};
7+
8+
fn do_check_abi<'tcx>(
9+
tcx: TyCtxt<'tcx>,
10+
abi: &FnAbi<'tcx, Ty<'tcx>>,
11+
target_feature_def: DefId,
12+
emit_err: impl Fn(&'static str),
13+
) {
14+
// This check is a no-op on non-x86, at least for now.
15+
if tcx.sess.target.arch != "x86" && tcx.sess.target.arch != "x86_64" {
16+
return;
17+
}
18+
let codegen_attrs = tcx.codegen_fn_attrs(target_feature_def);
19+
for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {
20+
let size = arg_abi.layout.size;
21+
if matches!(arg_abi.layout.abi, Abi::Vector { .. }) {
22+
let required_feature = match size.bits() {
23+
x if x <= 128 => "sse",
24+
x if x <= 256 => "avx",
25+
x if x <= 512 => "avx512f",
26+
_ => {
27+
panic!("Unknown vector size for x86: {}; arg = {:?}", size.bits(), arg_abi)
28+
}
29+
};
30+
let required_feature_sym = Symbol::intern(required_feature);
31+
if !tcx.sess.unstable_target_features.contains(&required_feature_sym)
32+
&& !codegen_attrs.target_features.contains(&required_feature_sym)
33+
{
34+
emit_err(required_feature);
35+
}
36+
}
37+
}
38+
}
39+
40+
/// Checks that the ABI of a given instance of a function does not contain vector-passed arguments
41+
/// or return values for which the corresponding target feature is not enabled.
42+
pub fn check_instance_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
43+
let InstanceKind::Item(item_def) = instance.def else {
44+
return;
45+
};
46+
47+
let param_env = tcx.param_env(item_def);
48+
let Ok(abi) = tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty()))) else {
49+
// an error will be reported somewhere else if we cannot determine the ABI of this
50+
// function.
51+
return;
52+
};
53+
do_check_abi(tcx, abi, item_def, |required_feature| {
54+
tcx.dcx().emit_err(AbiErrorDisabledVectorTypeDef {
55+
span: tcx.def_span(item_def),
56+
required_feature,
57+
});
58+
})
59+
}
60+
61+
/// Checks that a call expression does not try to pass a vector-passed argument which requires a
62+
/// target feature that the caller does not have, as doing so causes UB because of ABI mismatch.
63+
pub fn check_call_abi<'tcx>(
64+
tcx: TyCtxt<'tcx>,
65+
ty: Ty<'tcx>,
66+
span: Span,
67+
caller: InstanceKind<'tcx>,
68+
) {
69+
let InstanceKind::Item(caller_def) = caller else {
70+
return;
71+
};
72+
let param_env = tcx.param_env(caller_def);
73+
let callee_abi = match *ty.kind() {
74+
ty::FnPtr(sig) => tcx.fn_abi_of_fn_ptr(param_env.and((sig, ty::List::empty()))),
75+
ty::FnDef(def_id, args) => {
76+
// Intrinsics are handled separately by the compiler.
77+
if tcx.intrinsic(def_id).is_some() {
78+
return;
79+
}
80+
let Ok(Some(instance)) = ty::Instance::try_resolve(tcx, param_env, def_id, args) else {
81+
return;
82+
};
83+
tcx.fn_abi_of_instance(param_env.and((instance, ty::List::empty())))
84+
}
85+
_ => {
86+
panic!("Invalid function call");
87+
}
88+
};
89+
90+
let Ok(callee_abi) = callee_abi else {
91+
return;
92+
};
93+
do_check_abi(tcx, callee_abi, caller_def, |required_feature| {
94+
tcx.dcx().emit_err(AbiErrorDisabledVectorTypeCall { span, required_feature });
95+
})
96+
}

compiler/rustc_monomorphize/src/errors.rs

+18
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,21 @@ pub struct StartNotFound;
9191
pub struct UnknownCguCollectionMode<'a> {
9292
pub mode: &'a str,
9393
}
94+
95+
#[derive(Diagnostic)]
96+
#[diag(monomorphize_abi_error_disabled_vector_type_def)]
97+
#[help]
98+
pub struct AbiErrorDisabledVectorTypeDef<'a> {
99+
#[primary_span]
100+
pub span: Span,
101+
pub required_feature: &'a str,
102+
}
103+
104+
#[derive(Diagnostic)]
105+
#[diag(monomorphize_abi_error_disabled_vector_type_call)]
106+
#[help]
107+
pub struct AbiErrorDisabledVectorTypeCall<'a> {
108+
#[primary_span]
109+
pub span: Span,
110+
pub required_feature: &'a str,
111+
}

0 commit comments

Comments
 (0)