diff --git a/src/helpers.rs b/src/helpers.rs index 965cd534d1..ec2201e872 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1063,6 +1063,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"), } } + + /// Checks that target feature `target_feature` is enabled. + /// + /// If not enabled, emits an UB error that states that the feature is + /// required by `intrinsic`. + fn expect_target_feature_for_intrinsic( + &self, + intrinsic: Symbol, + target_feature: Symbol, + ) -> InterpResult<'tcx, ()> { + let this = self.eval_context_ref(); + if !this.tcx.sess.unstable_target_features.contains(&target_feature) { + throw_ub_format!( + "attempted to call intrinsic `{intrinsic}` that requires missing {target_feature} target feature" + ); + } + Ok(()) + } } impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> { diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 2d5df30374..9675947032 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -1008,9 +1008,11 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => { let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?; let arg = this.read_scalar(arg)?.to_i32()?; + // Note that different arguments might have different target feature requirements. match arg { // YIELD 1 => { + this.expect_target_feature_for_intrinsic(link_name, Symbol::intern("v6"))?; this.yield_active_thread(); } _ => { diff --git a/src/shims/x86/aesni.rs b/src/shims/x86/aesni.rs index aef930595b..15162cca8d 100644 --- a/src/shims/x86/aesni.rs +++ b/src/shims/x86/aesni.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, Symbol::intern("aes"))?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.aesni.").unwrap(); diff --git a/src/shims/x86/mod.rs b/src/shims/x86/mod.rs index 2a2171134d..aa2c680983 100644 --- a/src/shims/x86/mod.rs +++ b/src/shims/x86/mod.rs @@ -1,5 +1,5 @@ use rustc_middle::mir; -use rustc_span::Symbol; +use rustc_span::{sym, Symbol}; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; @@ -164,7 +164,11 @@ enum FloatBinOp { impl FloatBinOp { /// Convert from the `imm` argument used to specify the comparison /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`. - fn cmp_from_imm(imm: i8, intrinsic: &str) -> InterpResult<'_, Self> { + fn cmp_from_imm<'tcx>( + this: &crate::MiriInterpCx<'_, 'tcx>, + imm: i8, + intrinsic: Symbol, + ) -> InterpResult<'tcx, Self> { // Only bits 0..=4 are used, remaining should be zero. if imm & !0b1_1111 != 0 { throw_unsup_format!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}"); @@ -174,7 +178,7 @@ impl FloatBinOp { // Bits 0..=2 specifies the operation. // `gt` indicates the result to be returned when the LHS is strictly // greater than the RHS, and so on. - let (gt, lt, eq, unord) = match imm & 0b111 { + let (gt, lt, eq, mut unord) = match imm & 0b111 { // Equal 0x0 => (false, false, true, false), // Less-than @@ -194,7 +198,10 @@ impl FloatBinOp { _ => unreachable!(), }; // When bit 3 is 1 (only possible in AVX), unord is toggled. - let unord = unord ^ (imm & 0b1000 != 0); + if imm & 0b1000 != 0 { + this.expect_target_feature_for_intrinsic(intrinsic, sym::avx)?; + unord = !unord; + } Ok(Self::Cmp { gt, lt, eq, unord }) } } diff --git a/src/shims/x86/sse.rs b/src/shims/x86/sse.rs index e15023c3c2..c7d0a2fddd 100644 --- a/src/shims/x86/sse.rs +++ b/src/shims/x86/sse.rs @@ -1,6 +1,6 @@ use rustc_apfloat::{ieee::Single, Float as _}; use rustc_middle::mir; -use rustc_span::Symbol; +use rustc_span::{sym, Symbol}; use rustc_target::spec::abi::Abi; use rand::Rng as _; @@ -21,6 +21,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, sym::sse)?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse.").unwrap(); // All these intrinsics operate on 128-bit (f32x4) SIMD vectors unless stated otherwise. @@ -107,10 +108,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::cmp_from_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse.cmp.ss", - )?; + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_first::(this, which, left, right, dest)?; } @@ -126,10 +125,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::cmp_from_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse.cmp.ps", - )?; + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_all::(this, which, left, right, dest)?; } diff --git a/src/shims/x86/sse2.rs b/src/shims/x86/sse2.rs index 55520771cf..c8ada503ec 100644 --- a/src/shims/x86/sse2.rs +++ b/src/shims/x86/sse2.rs @@ -20,6 +20,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, Symbol::intern("sse2"))?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse2.").unwrap(); @@ -473,10 +474,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::cmp_from_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse2.cmp.sd", - )?; + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_first::(this, which, left, right, dest)?; } @@ -492,10 +491,8 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: let [left, right, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - let which = FloatBinOp::cmp_from_imm( - this.read_scalar(imm)?.to_i8()?, - "llvm.x86.sse2.cmp.pd", - )?; + let which = + FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; bin_op_simd_float_all::(this, which, left, right, dest)?; } diff --git a/src/shims/x86/sse3.rs b/src/shims/x86/sse3.rs index 270da36f0e..06a56b5e73 100644 --- a/src/shims/x86/sse3.rs +++ b/src/shims/x86/sse3.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, Symbol::intern("sse3"))?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse3.").unwrap(); diff --git a/src/shims/x86/sse41.rs b/src/shims/x86/sse41.rs index 523f3bfc26..82f9946ce3 100644 --- a/src/shims/x86/sse41.rs +++ b/src/shims/x86/sse41.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, Symbol::intern("sse4.1"))?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sse41.").unwrap(); diff --git a/src/shims/x86/ssse3.rs b/src/shims/x86/ssse3.rs index dbc2b947b3..688d0e2606 100644 --- a/src/shims/x86/ssse3.rs +++ b/src/shims/x86/ssse3.rs @@ -18,6 +18,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: dest: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, EmulateForeignItemResult> { let this = self.eval_context_mut(); + this.expect_target_feature_for_intrinsic(link_name, Symbol::intern("ssse3"))?; // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.ssse3.").unwrap();