miri/intrinsics/
simd.rs

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