Skip to content

Commit 3d34ebc

Browse files
committed
Port the Miri implementations of SIMD intrinsics to rustc_const_eval
1 parent 6f34f4e commit 3d34ebc

File tree

5 files changed

+900
-822
lines changed

5 files changed

+900
-822
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)?;
@@ -1081,4 +1057,66 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
10811057
self.write_scalar(res, dest)?;
10821058
interp_ok(())
10831059
}
1060+
1061+
/// Converts `src` from floating point to integer type `dest_ty`
1062+
/// after rounding with mode `round`.
1063+
/// Returns `None` if `f` is NaN or out of range.
1064+
pub fn float_to_int_checked(
1065+
&self,
1066+
src: &ImmTy<'tcx, M::Provenance>,
1067+
cast_to: TyAndLayout<'tcx>,
1068+
round: rustc_apfloat::Round,
1069+
) -> InterpResult<'tcx, Option<ImmTy<'tcx, M::Provenance>>> {
1070+
fn float_to_int_inner<'tcx, F: rustc_apfloat::Float, M: Machine<'tcx>>(
1071+
ecx: &InterpCx<'tcx, M>,
1072+
src: F,
1073+
cast_to: TyAndLayout<'tcx>,
1074+
round: rustc_apfloat::Round,
1075+
) -> (Scalar<M::Provenance>, rustc_apfloat::Status) {
1076+
let int_size = cast_to.layout.size;
1077+
match cast_to.ty.kind() {
1078+
// Unsigned
1079+
ty::Uint(_) => {
1080+
let res = src.to_u128_r(int_size.bits_usize(), round, &mut false);
1081+
(Scalar::from_uint(res.value, int_size), res.status)
1082+
}
1083+
// Signed
1084+
ty::Int(_) => {
1085+
let res = src.to_i128_r(int_size.bits_usize(), round, &mut false);
1086+
(Scalar::from_int(res.value, int_size), res.status)
1087+
}
1088+
// Nothing else
1089+
_ => span_bug!(
1090+
ecx.cur_span(),
1091+
"attempted float-to-int conversion with non-int output type {}",
1092+
cast_to.ty,
1093+
),
1094+
}
1095+
}
1096+
1097+
let ty::Float(fty) = src.layout.ty.kind() else {
1098+
bug!("float_to_int_checked: non-float input type {}", src.layout.ty)
1099+
};
1100+
1101+
let (val, status) = match fty {
1102+
FloatTy::F16 => float_to_int_inner(self, src.to_scalar().to_f16()?, cast_to, round),
1103+
FloatTy::F32 => float_to_int_inner(self, src.to_scalar().to_f32()?, cast_to, round),
1104+
FloatTy::F64 => float_to_int_inner(self, src.to_scalar().to_f64()?, cast_to, round),
1105+
FloatTy::F128 => float_to_int_inner(self, src.to_scalar().to_f128()?, cast_to, round),
1106+
};
1107+
1108+
if status.intersects(
1109+
rustc_apfloat::Status::INVALID_OP
1110+
| rustc_apfloat::Status::OVERFLOW
1111+
| rustc_apfloat::Status::UNDERFLOW,
1112+
) {
1113+
// Floating point value is NaN (flagged with INVALID_OP) or outside the range
1114+
// of values of the integer type (flagged with OVERFLOW or UNDERFLOW).
1115+
interp_ok(None)
1116+
} else {
1117+
// Floating point value can be represented by the integer type after rounding.
1118+
// The INEXACT flag is ignored on purpose to allow rounding.
1119+
interp_ok(Some(ImmTy::from_scalar(val, cast_to)))
1120+
}
1121+
}
10841122
}

0 commit comments

Comments
 (0)