Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7587ff3

Browse files
committedAug 1, 2024
Auto merge of rust-lang#127731 - veluca93:abi_checks, r=<try>
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. r? RalfJung
2 parents 70591dc + e8302b3 commit 7587ff3

15 files changed

+253
-0
lines changed
 

‎Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4363,6 +4363,7 @@ dependencies = [
43634363
name = "rustc_monomorphize"
43644364
version = "0.0.0"
43654365
dependencies = [
4366+
"rustc_abi",
43664367
"rustc_data_structures",
43674368
"rustc_errors",
43684369
"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 std::path::PathBuf;
@@ -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_site_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,111 @@
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::call::{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+
}

‎compiler/rustc_monomorphize/src/errors.rs

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

‎tests/assembly/simd-bitmask.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ ignore-test
12
//@ revisions: x86 x86-avx2 x86-avx512 aarch64
23
//@ [x86] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
34
//@ [x86] needs-llvm-components: x86

‎tests/assembly/simd-intrinsic-gather.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ ignore-test
12
//@ revisions: x86-avx512
23
//@ [x86-avx512] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
34
//@ [x86-avx512] compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512dq

‎tests/assembly/simd-intrinsic-mask-load.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ ignore-test
12
//@ revisions: x86-avx2 x86-avx512
23
//@ [x86-avx2] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
34
//@ [x86-avx2] compile-flags: -C target-feature=+avx2

‎tests/assembly/simd-intrinsic-mask-reduce.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ ignore-test
12
// verify that simd mask reductions do not introduce additional bit shift operations
23
//@ revisions: x86 aarch64
34
//@ [x86] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel

‎tests/assembly/simd-intrinsic-mask-store.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ ignore-test
12
//@ revisions: x86-avx2 x86-avx512
23
//@ [x86-avx2] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
34
//@ [x86-avx2] compile-flags: -C target-feature=+avx2

‎tests/assembly/simd-intrinsic-scatter.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ ignore-test
12
//@ revisions: x86-avx512
23
//@ [x86-avx512] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
34
//@ [x86-avx512] compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bw,+avx512dq

‎tests/assembly/simd-intrinsic-select.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//@ ignore-test
12
//@ revisions: x86-avx2 x86-avx512 aarch64
23
//@ [x86-avx2] compile-flags: --target=x86_64-unknown-linux-gnu -C llvm-args=-x86-asm-syntax=intel
34
//@ [x86-avx2] compile-flags: -C target-feature=+avx2

‎tests/ui/simd-abi-checks.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//@ only-x86_64
2+
//@ build-fail
3+
4+
#![feature(avx512_target_feature)]
5+
#![feature(portable_simd)]
6+
#![allow(improper_ctypes_definitions)]
7+
8+
use std::arch::x86_64::*;
9+
10+
unsafe extern "C" fn f(_: __m256) {
11+
//~^ ABI error: this function definition uses a avx vector type, which is not enabled
12+
todo!()
13+
}
14+
15+
unsafe extern "C" fn g() -> __m256 {
16+
//~^ ABI error: this function definition uses a avx vector type, which is not enabled
17+
todo!()
18+
}
19+
20+
#[target_feature(enable = "avx")]
21+
unsafe extern "C" fn favx(_: __m256) {
22+
todo!()
23+
}
24+
25+
#[target_feature(enable = "avx")]
26+
unsafe extern "C" fn gavx() -> __m256 {
27+
todo!()
28+
}
29+
30+
fn as_f64x8(d: __m512d) -> std::simd::f64x8 {
31+
unsafe { std::mem::transmute(d) }
32+
}
33+
34+
unsafe fn test() {
35+
let arg = std::mem::transmute([0.0f64; 8]);
36+
as_f64x8(arg);
37+
}
38+
39+
fn main() {
40+
unsafe {
41+
f(g());
42+
//~^ ERROR ABI error: this function call uses a avx vector type, which is not enabled in the caller
43+
//~| ERROR ABI error: this function call uses a avx vector type, which is not enabled in the caller
44+
}
45+
46+
unsafe {
47+
favx(gavx());
48+
//~^ ERROR ABI error: this function call uses a avx vector type, which is not enabled in the caller
49+
//~| ERROR ABI error: this function call uses a avx vector type, which is not enabled in the caller
50+
}
51+
52+
unsafe {
53+
test();
54+
}
55+
}

‎tests/ui/simd-abi-checks.stderr

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
error: ABI error: this function call uses a avx vector type, which is not enabled in the caller
2+
--> $DIR/simd-abi-checks.rs:41:11
3+
|
4+
LL | f(g());
5+
| ^^^
6+
|
7+
= help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")])
8+
9+
error: ABI error: this function call uses a avx vector type, which is not enabled in the caller
10+
--> $DIR/simd-abi-checks.rs:41:9
11+
|
12+
LL | f(g());
13+
| ^^^^^^
14+
|
15+
= help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")])
16+
17+
error: ABI error: this function call uses a avx vector type, which is not enabled in the caller
18+
--> $DIR/simd-abi-checks.rs:47:14
19+
|
20+
LL | favx(gavx());
21+
| ^^^^^^
22+
|
23+
= help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")])
24+
25+
error: ABI error: this function call uses a avx vector type, which is not enabled in the caller
26+
--> $DIR/simd-abi-checks.rs:47:9
27+
|
28+
LL | favx(gavx());
29+
| ^^^^^^^^^^^^
30+
|
31+
= help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")])
32+
33+
error: ABI error: this function definition uses a avx vector type, which is not enabled
34+
--> $DIR/simd-abi-checks.rs:15:1
35+
|
36+
LL | unsafe extern "C" fn g() -> __m256 {
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38+
|
39+
= help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")])
40+
41+
error: ABI error: this function definition uses a avx vector type, which is not enabled
42+
--> $DIR/simd-abi-checks.rs:10:1
43+
|
44+
LL | unsafe extern "C" fn f(_: __m256) {
45+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
46+
|
47+
= help: consider enabling it globally (-C target-feature=+avx) or locally (#[target_feature(enable="avx")])
48+
49+
error: aborting due to 6 previous errors
50+

0 commit comments

Comments
 (0)
Failed to load comments.