Skip to content

Commit 41d7012

Browse files
committed
Auto merge of rust-lang#3180 - eduardosm:check-intrinsics-target-feature, r=RalfJung
Check that target features required by LLVM intrinsics are enabled Fixes rust-lang/miri#3178
2 parents 4926115 + 09358a0 commit 41d7012

File tree

11 files changed

+101
-19
lines changed

11 files changed

+101
-19
lines changed

src/tools/miri/src/helpers.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10631063
_ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
10641064
}
10651065
}
1066+
1067+
/// Checks that target feature `target_feature` is enabled.
1068+
///
1069+
/// If not enabled, emits an UB error that states that the feature is
1070+
/// required by `intrinsic`.
1071+
fn expect_target_feature_for_intrinsic(
1072+
&self,
1073+
intrinsic: Symbol,
1074+
target_feature: &str,
1075+
) -> InterpResult<'tcx, ()> {
1076+
let this = self.eval_context_ref();
1077+
if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
1078+
throw_ub_format!(
1079+
"attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
1080+
);
1081+
}
1082+
Ok(())
1083+
}
10661084
}
10671085

10681086
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {

src/tools/miri/src/shims/foreign_items.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1012,9 +1012,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10121012
"llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => {
10131013
let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
10141014
let arg = this.read_scalar(arg)?.to_i32()?;
1015+
// Note that different arguments might have different target feature requirements.
10151016
match arg {
10161017
// YIELD
10171018
1 => {
1019+
this.expect_target_feature_for_intrinsic(link_name, "v6")?;
10181020
this.yield_active_thread();
10191021
}
10201022
_ => {

src/tools/miri/src/shims/x86/aesni.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
1818
dest: &PlaceTy<'tcx, Provenance>,
1919
) -> InterpResult<'tcx, EmulateForeignItemResult> {
2020
let this = self.eval_context_mut();
21+
this.expect_target_feature_for_intrinsic(link_name, "aes")?;
2122
// Prefix should have already been checked.
2223
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.aesni.").unwrap();
2324

src/tools/miri/src/shims/x86/mod.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,11 @@ enum FloatBinOp {
164164
impl FloatBinOp {
165165
/// Convert from the `imm` argument used to specify the comparison
166166
/// operation in intrinsics such as `llvm.x86.sse.cmp.ss`.
167-
fn cmp_from_imm(imm: i8, intrinsic: &str) -> InterpResult<'_, Self> {
167+
fn cmp_from_imm<'tcx>(
168+
this: &crate::MiriInterpCx<'_, 'tcx>,
169+
imm: i8,
170+
intrinsic: Symbol,
171+
) -> InterpResult<'tcx, Self> {
168172
// Only bits 0..=4 are used, remaining should be zero.
169173
if imm & !0b1_1111 != 0 {
170174
throw_unsup_format!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
@@ -174,7 +178,7 @@ impl FloatBinOp {
174178
// Bits 0..=2 specifies the operation.
175179
// `gt` indicates the result to be returned when the LHS is strictly
176180
// greater than the RHS, and so on.
177-
let (gt, lt, eq, unord) = match imm & 0b111 {
181+
let (gt, lt, eq, mut unord) = match imm & 0b111 {
178182
// Equal
179183
0x0 => (false, false, true, false),
180184
// Less-than
@@ -194,7 +198,10 @@ impl FloatBinOp {
194198
_ => unreachable!(),
195199
};
196200
// When bit 3 is 1 (only possible in AVX), unord is toggled.
197-
let unord = unord ^ (imm & 0b1000 != 0);
201+
if imm & 0b1000 != 0 {
202+
this.expect_target_feature_for_intrinsic(intrinsic, "avx")?;
203+
unord = !unord;
204+
}
198205
Ok(Self::Cmp { gt, lt, eq, unord })
199206
}
200207
}

src/tools/miri/src/shims/x86/sse.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
2121
dest: &PlaceTy<'tcx, Provenance>,
2222
) -> InterpResult<'tcx, EmulateForeignItemResult> {
2323
let this = self.eval_context_mut();
24+
this.expect_target_feature_for_intrinsic(link_name, "sse")?;
2425
// Prefix should have already been checked.
2526
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse.").unwrap();
2627
// All these intrinsics operate on 128-bit (f32x4) SIMD vectors unless stated otherwise.
@@ -107,10 +108,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
107108
let [left, right, imm] =
108109
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
109110

110-
let which = FloatBinOp::cmp_from_imm(
111-
this.read_scalar(imm)?.to_i8()?,
112-
"llvm.x86.sse.cmp.ss",
113-
)?;
111+
let which =
112+
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
114113

115114
bin_op_simd_float_first::<Single>(this, which, left, right, dest)?;
116115
}
@@ -126,10 +125,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
126125
let [left, right, imm] =
127126
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
128127

129-
let which = FloatBinOp::cmp_from_imm(
130-
this.read_scalar(imm)?.to_i8()?,
131-
"llvm.x86.sse.cmp.ps",
132-
)?;
128+
let which =
129+
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
133130

134131
bin_op_simd_float_all::<Single>(this, which, left, right, dest)?;
135132
}

src/tools/miri/src/shims/x86/sse2.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
2020
dest: &PlaceTy<'tcx, Provenance>,
2121
) -> InterpResult<'tcx, EmulateForeignItemResult> {
2222
let this = self.eval_context_mut();
23+
this.expect_target_feature_for_intrinsic(link_name, "sse2")?;
2324
// Prefix should have already been checked.
2425
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse2.").unwrap();
2526

@@ -473,10 +474,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
473474
let [left, right, imm] =
474475
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
475476

476-
let which = FloatBinOp::cmp_from_imm(
477-
this.read_scalar(imm)?.to_i8()?,
478-
"llvm.x86.sse2.cmp.sd",
479-
)?;
477+
let which =
478+
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
480479

481480
bin_op_simd_float_first::<Double>(this, which, left, right, dest)?;
482481
}
@@ -492,10 +491,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
492491
let [left, right, imm] =
493492
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
494493

495-
let which = FloatBinOp::cmp_from_imm(
496-
this.read_scalar(imm)?.to_i8()?,
497-
"llvm.x86.sse2.cmp.pd",
498-
)?;
494+
let which =
495+
FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
499496

500497
bin_op_simd_float_all::<Double>(this, which, left, right, dest)?;
501498
}

src/tools/miri/src/shims/x86/sse3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
1818
dest: &PlaceTy<'tcx, Provenance>,
1919
) -> InterpResult<'tcx, EmulateForeignItemResult> {
2020
let this = self.eval_context_mut();
21+
this.expect_target_feature_for_intrinsic(link_name, "sse3")?;
2122
// Prefix should have already been checked.
2223
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse3.").unwrap();
2324

src/tools/miri/src/shims/x86/sse41.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
1818
dest: &PlaceTy<'tcx, Provenance>,
1919
) -> InterpResult<'tcx, EmulateForeignItemResult> {
2020
let this = self.eval_context_mut();
21+
this.expect_target_feature_for_intrinsic(link_name, "sse4.1")?;
2122
// Prefix should have already been checked.
2223
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse41.").unwrap();
2324

src/tools/miri/src/shims/x86/ssse3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
1818
dest: &PlaceTy<'tcx, Provenance>,
1919
) -> InterpResult<'tcx, EmulateForeignItemResult> {
2020
let this = self.eval_context_mut();
21+
this.expect_target_feature_for_intrinsic(link_name, "ssse3")?;
2122
// Prefix should have already been checked.
2223
let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.ssse3.").unwrap();
2324

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Ignore everything except x86 and x86_64
2+
// Any new targets that are added to CI should be ignored here.
3+
// We cannot use `cfg`-based tricks here since the output would be
4+
// different for non-x86 targets.
5+
//@ignore-target-aarch64
6+
//@ignore-target-arm
7+
//@ignore-target-avr
8+
//@ignore-target-s390x
9+
//@ignore-target-thumbv7em
10+
//@ignore-target-wasm32
11+
// Explicitly disable SSE4.1 because it is enabled by default on macOS
12+
//@compile-flags: -C target-feature=-sse4.1
13+
14+
#![feature(link_llvm_intrinsics, simd_ffi)]
15+
16+
#[cfg(target_arch = "x86")]
17+
use std::arch::x86::*;
18+
#[cfg(target_arch = "x86_64")]
19+
use std::arch::x86_64::*;
20+
21+
fn main() {
22+
assert!(is_x86_feature_detected!("sse"));
23+
assert!(!is_x86_feature_detected!("sse4.1"));
24+
25+
unsafe {
26+
// Pass, since SSE is enabled
27+
addss(_mm_setzero_ps(), _mm_setzero_ps());
28+
29+
// Fail, since SSE4.1 is not enabled
30+
dpps(_mm_setzero_ps(), _mm_setzero_ps(), 0);
31+
//~^ ERROR: Undefined Behavior: attempted to call intrinsic `llvm.x86.sse41.dpps` that requires missing target feature sse4.1
32+
}
33+
}
34+
35+
#[allow(improper_ctypes)]
36+
extern "C" {
37+
#[link_name = "llvm.x86.sse.add.ss"]
38+
fn addss(a: __m128, b: __m128) -> __m128;
39+
40+
#[link_name = "llvm.x86.sse41.dpps"]
41+
fn dpps(a: __m128, b: __m128, imm8: u8) -> __m128;
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: Undefined Behavior: attempted to call intrinsic `llvm.x86.sse41.dpps` that requires missing target feature sse4.1
2+
--> $DIR/intrinsic_target_feature.rs:LL:CC
3+
|
4+
LL | dpps(_mm_setzero_ps(), _mm_setzero_ps(), 0);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to call intrinsic `llvm.x86.sse41.dpps` that requires missing target feature sse4.1
6+
|
7+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9+
= note: BACKTRACE:
10+
= note: inside `main` at $DIR/intrinsic_target_feature.rs:LL:CC
11+
12+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
13+
14+
error: aborting due to previous error
15+

0 commit comments

Comments
 (0)