Skip to content

Commit f1d8fdb

Browse files
committed
Make SIMD intrinsics available in const-contexts
Port the Miri implementations to `rustc_const_eval`
1 parent 4cd91ef commit f1d8fdb

File tree

7 files changed

+955
-877
lines changed

7 files changed

+955
-877
lines changed

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 71 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
33
//! and miri.
44
5+
mod simd;
6+
57
use std::assert_matches::assert_matches;
68

79
use rustc_abi::{FieldIdx, HasDataLayout, Size};
810
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
911
use rustc_middle::mir::interpret::{CTFE_ALLOC_SALT, read_target_uint, write_target_uint};
1012
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
1113
use rustc_middle::ty::layout::TyAndLayout;
12-
use rustc_middle::ty::{Ty, TyCtxt};
13-
use rustc_middle::{bug, ty};
14+
use rustc_middle::ty::{FloatTy, Ty, TyCtxt};
15+
use rustc_middle::{bug, span_bug, ty};
1416
use rustc_span::{Symbol, sym};
1517
use tracing::trace;
1618

@@ -121,6 +123,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
121123
) -> InterpResult<'tcx, bool> {
122124
let instance_args = instance.args;
123125
let intrinsic_name = self.tcx.item_name(instance.def_id());
126+
127+
if intrinsic_name.as_str().starts_with("simd_") {
128+
return self.eval_simd_intrinsic(intrinsic_name, instance_args, args, dest, ret);
129+
}
130+
124131
let tcx = self.tcx.tcx;
125132

126133
match intrinsic_name {
@@ -454,37 +461,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
454461
self.exact_div(&val, &size, dest)?;
455462
}
456463

457-
sym::simd_insert => {
458-
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
459-
let elem = &args[2];
460-
let (input, input_len) = self.project_to_simd(&args[0])?;
461-
let (dest, dest_len) = self.project_to_simd(dest)?;
462-
assert_eq!(input_len, dest_len, "Return vector length must match input length");
463-
// Bounds are not checked by typeck so we have to do it ourselves.
464-
if index >= input_len {
465-
throw_ub_format!(
466-
"`simd_insert` index {index} is out-of-bounds of vector with length {input_len}"
467-
);
468-
}
469-
470-
for i in 0..dest_len {
471-
let place = self.project_index(&dest, i)?;
472-
let value =
473-
if i == index { elem.clone() } else { self.project_index(&input, i)? };
474-
self.copy_op(&value, &place)?;
475-
}
476-
}
477-
sym::simd_extract => {
478-
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
479-
let (input, input_len) = self.project_to_simd(&args[0])?;
480-
// Bounds are not checked by typeck so we have to do it ourselves.
481-
if index >= input_len {
482-
throw_ub_format!(
483-
"`simd_extract` index {index} is out-of-bounds of vector with length {input_len}"
484-
);
485-
}
486-
self.copy_op(&self.project_index(&input, index)?, dest)?;
487-
}
488464
sym::black_box => {
489465
// These just return their argument
490466
self.copy_op(&args[0], dest)?;
@@ -1035,4 +1011,66 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
10351011
self.write_scalar(res, dest)?;
10361012
interp_ok(())
10371013
}
1014+
1015+
/// Converts `src` from floating point to integer type `dest_ty`
1016+
/// after rounding with mode `round`.
1017+
/// Returns `None` if `f` is NaN or out of range.
1018+
pub fn float_to_int_checked(
1019+
&self,
1020+
src: &ImmTy<'tcx, M::Provenance>,
1021+
cast_to: TyAndLayout<'tcx>,
1022+
round: rustc_apfloat::Round,
1023+
) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> {
1024+
fn float_to_int_inner<'tcx, F: rustc_apfloat::Float, M: Machine<'tcx>>(
1025+
ecx: &InterpCx<'tcx, M>,
1026+
src: F,
1027+
cast_to: TyAndLayout<'tcx>,
1028+
round: rustc_apfloat::Round,
1029+
) -> (Scalar<M::Provenance>, rustc_apfloat::Status) {
1030+
let int_size = cast_to.layout.size;
1031+
match cast_to.ty.kind() {
1032+
// Unsigned
1033+
ty::Uint(_) => {
1034+
let res = src.to_u128_r(int_size.bits_usize(), round, &mut false);
1035+
(Scalar::from_uint(res.value, int_size), res.status)
1036+
}
1037+
// Signed
1038+
ty::Int(_) => {
1039+
let res = src.to_i128_r(int_size.bits_usize(), round, &mut false);
1040+
(Scalar::from_int(res.value, int_size), res.status)
1041+
}
1042+
// Nothing else
1043+
_ => span_bug!(
1044+
ecx.cur_span(),
1045+
"attempted float-to-int conversion with non-int output type {}",
1046+
cast_to.ty,
1047+
),
1048+
}
1049+
}
1050+
1051+
let ty::Float(fty) = src.layout.ty.kind() else {
1052+
bug!("float_to_int_checked: non-float input type {}", src.layout.ty)
1053+
};
1054+
1055+
let (val, status) = match fty {
1056+
FloatTy::F16 => float_to_int_inner(self, src.to_scalar().to_f16()?, cast_to, round),
1057+
FloatTy::F32 => float_to_int_inner(self, src.to_scalar().to_f32()?, cast_to, round),
1058+
FloatTy::F64 => float_to_int_inner(self, src.to_scalar().to_f64()?, cast_to, round),
1059+
FloatTy::F128 => float_to_int_inner(self, src.to_scalar().to_f128()?, cast_to, round),
1060+
};
1061+
1062+
if status.intersects(
1063+
rustc_apfloat::Status::INVALID_OP
1064+
| rustc_apfloat::Status::OVERFLOW
1065+
| rustc_apfloat::Status::UNDERFLOW,
1066+
) {
1067+
// Floating point value is NaN (flagged with INVALID_OP) or outside the range
1068+
// of values of the integer type (flagged with OVERFLOW or UNDERFLOW).
1069+
interp_ok(None)
1070+
} else {
1071+
// Floating point value can be represented by the integer type after rounding.
1072+
// The INEXACT flag is ignored on purpose to allow rounding.
1073+
interp_ok(Some(ImmTy::from_scalar(val, cast_to)))
1074+
}
1075+
}
10381076
}

0 commit comments

Comments
 (0)