Skip to main content

miri/intrinsics/
simd.rs

1use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS};
2use rustc_middle::ty;
3use rustc_middle::ty::FloatTy;
4
5use super::check_intrinsic_arg_count;
6use crate::math::{HostUnaryFloatOp, host_unary_float_op};
7use crate::*;
8
9impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
10pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11    /// Calls the simd intrinsic `intrinsic`; the `simd_` prefix has already been removed.
12    /// Returns `Ok(true)` if the intrinsic was handled.
13    fn emulate_simd_intrinsic(
14        &mut self,
15        intrinsic_name: &str,
16        args: &[OpTy<'tcx>],
17        dest: &MPlaceTy<'tcx>,
18    ) -> InterpResult<'tcx, EmulateItemResult> {
19        let this = self.eval_context_mut();
20        match intrinsic_name {
21            // Operations we can do with soft-floats.
22            "fsqrt" => {
23                let [op] = check_intrinsic_arg_count(args)?;
24                let (op, op_len) = this.project_to_simd(op)?;
25                let (dest, dest_len) = this.project_to_simd(dest)?;
26
27                assert_eq!(dest_len, op_len);
28
29                for i in 0..dest_len {
30                    let op = this.project_index(&op, i)?;
31                    let dest = this.project_index(&dest, i)?;
32                    let ty::Float(float_ty) = op.layout.ty.kind() else {
33                        span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name)
34                    };
35                    match float_ty {
36                        FloatTy::F16 => math::sqrt_op::<IeeeFloat<HalfS>>(this, &op, &dest)?,
37                        FloatTy::F32 => math::sqrt_op::<IeeeFloat<SingleS>>(this, &op, &dest)?,
38                        FloatTy::F64 => math::sqrt_op::<IeeeFloat<DoubleS>>(this, &op, &dest)?,
39                        FloatTy::F128 => math::sqrt_op::<IeeeFloat<QuadS>>(this, &op, &dest)?,
40                    };
41                }
42            }
43
44            // Operations that need host floats.
45            "fsin" | "fcos" | "fexp" | "fexp2" | "flog" | "flog2" | "flog10" => {
46                let [op] = check_intrinsic_arg_count(args)?;
47                let (op, op_len) = this.project_to_simd(op)?;
48                let (dest, dest_len) = this.project_to_simd(dest)?;
49
50                assert_eq!(dest_len, op_len);
51
52                let host_op = match intrinsic_name {
53                    "fsin" => HostUnaryFloatOp::Sin,
54                    "fcos" => HostUnaryFloatOp::Cos,
55                    "fexp" => HostUnaryFloatOp::Exp,
56                    "fexp2" => HostUnaryFloatOp::Exp2,
57                    "flog" => HostUnaryFloatOp::Log,
58                    "flog2" => HostUnaryFloatOp::Log2,
59                    "flog10" => HostUnaryFloatOp::Log10,
60                    _ => bug!(),
61                };
62
63                for i in 0..dest_len {
64                    let op = this.project_index(&op, i)?;
65                    let dest = this.project_index(&dest, i)?;
66                    let ty::Float(float_ty) = op.layout.ty.kind() else {
67                        span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name)
68                    };
69                    // Using host floats except for sqrt (but it's fine, these operations do not
70                    // have guaranteed precision).
71                    match float_ty {
72                        FloatTy::F16 => host_unary_float_op::<HalfS>(this, &op, host_op, &dest)?,
73                        FloatTy::F32 => host_unary_float_op::<SingleS>(this, &op, host_op, &dest)?,
74                        FloatTy::F64 => host_unary_float_op::<DoubleS>(this, &op, host_op, &dest)?,
75                        FloatTy::F128 => unimplemented!("f128"), // FIXME(f128)
76                    }
77                }
78            }
79            "expose_provenance" => {
80                let [op] = check_intrinsic_arg_count(args)?;
81                let (op, op_len) = this.project_to_simd(op)?;
82                let (dest, dest_len) = this.project_to_simd(dest)?;
83
84                assert_eq!(dest_len, op_len);
85
86                for i in 0..dest_len {
87                    let op = this.read_immediate(&this.project_index(&op, i)?)?;
88                    let dest = this.project_index(&dest, i)?;
89
90                    let val = match (op.layout.ty.kind(), dest.layout.ty.kind()) {
91                        // Ptr/Int casts
92                        (ty::RawPtr(..), ty::Int(_) | ty::Uint(_)) =>
93                            this.pointer_expose_provenance_cast(&op, dest.layout)?,
94                        // Error otherwise
95                        _ =>
96                            throw_unsup_format!(
97                                "Unsupported `simd_expose_provenance` from element type {from_ty} to {to_ty}",
98                                from_ty = op.layout.ty,
99                                to_ty = dest.layout.ty,
100                            ),
101                    };
102                    this.write_immediate(*val, &dest)?;
103                }
104            }
105
106            _ => return interp_ok(EmulateItemResult::NotSupported),
107        }
108        interp_ok(EmulateItemResult::NeedsReturn)
109    }
110}