1use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, Semantics, SingleS};
2use rustc_apfloat::{self, Float, FloatConvert, Round};
3use rustc_middle::mir;
4use rustc_middle::ty::{self, FloatTy};
5
6use self::math::{HostFloatOperation, HostUnaryFloatOp, IeeeExt, host_unary_float_op};
7use super::check_intrinsic_arg_count;
8use crate::*;
9
10fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
11 this: &mut MiriInterpCx<'tcx>,
12 args: &[OpTy<'tcx>],
13 dest: &MPlaceTy<'tcx>,
14) -> InterpResult<'tcx> {
15 let [f] = check_intrinsic_arg_count(args)?;
16 math::sqrt_op::<F>(this, f, dest)
17}
18
19fn is_host_unary_float_op(intrinsic_name: &str) -> Option<(FloatTy, HostUnaryFloatOp)> {
21 let (op, ty) = intrinsic_name.rsplit_once('f')?;
22
23 let float_ty = match ty {
24 "16" => FloatTy::F16,
25 "32" => FloatTy::F32,
26 "64" => FloatTy::F64,
27 "128" => FloatTy::F128,
28 _ => return None,
29 };
30
31 let host_float_op = match op {
32 "sin" => HostUnaryFloatOp::Sin,
33 "cos" => HostUnaryFloatOp::Cos,
34 "exp" => HostUnaryFloatOp::Exp,
35 "exp2" => HostUnaryFloatOp::Exp2,
36 "log" => HostUnaryFloatOp::Log,
37 "log10" => HostUnaryFloatOp::Log10,
38 "log2" => HostUnaryFloatOp::Log2,
39 _ => return None,
40 };
41
42 Some((float_ty, host_float_op))
43}
44
45fn pow_intrinsic<'tcx, S: Semantics>(
46 this: &mut MiriInterpCx<'tcx>,
47 args: &[OpTy<'tcx>],
48 dest: &MPlaceTy<'tcx>,
49) -> InterpResult<'tcx, ()>
50where
51 IeeeFloat<S>: HostFloatOperation + IeeeExt + Float + Into<Scalar>,
52{
53 let [f1, f2] = check_intrinsic_arg_count(args)?;
54 let f1: IeeeFloat<S> = this.read_scalar(f1)?.to_float()?;
55 let f2: IeeeFloat<S> = this.read_scalar(f2)?.to_float()?;
56
57 let res = math::fixed_float_value(this, "pow", &[f1, f2]).unwrap_or_else(|| {
58 let res = f1.host_powf(f2);
60
61 math::apply_random_float_error_ulp(this, res, 4)
64 });
65 let res = this.adjust_nan(res, &[f1, f2]);
66 this.write_scalar(res, dest)?;
67 interp_ok(())
68}
69fn powi_intrinsic<'tcx, S: Semantics>(
70 this: &mut MiriInterpCx<'tcx>,
71 args: &[OpTy<'tcx>],
72 dest: &MPlaceTy<'tcx>,
73) -> InterpResult<'tcx, ()>
74where
75 IeeeFloat<S>: HostFloatOperation + IeeeExt + Float + Into<Scalar>,
76{
77 let [f, i] = check_intrinsic_arg_count(args)?;
78 let f: IeeeFloat<S> = this.read_scalar(f)?.to_float()?;
79 let i = this.read_scalar(i)?.to_i32()?;
80
81 let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
82 let res = f.host_powi(i);
84
85 math::apply_random_float_error_ulp(this, res, 4)
88 });
89 let res = this.adjust_nan(res, &[f]);
90 this.write_scalar(res, dest)?;
91 interp_ok(())
92}
93
94impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
95pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
96 fn emulate_math_intrinsic(
97 &mut self,
98 intrinsic_name: &str,
99 _generic_args: ty::GenericArgsRef<'tcx>,
100 args: &[OpTy<'tcx>],
101 dest: &MPlaceTy<'tcx>,
102 ) -> InterpResult<'tcx, EmulateItemResult> {
103 let this = self.eval_context_mut();
104
105 match intrinsic_name {
106 "sqrtf16" => sqrt::<rustc_apfloat::ieee::Half>(this, args, dest)?,
108 "sqrtf32" => sqrt::<rustc_apfloat::ieee::Single>(this, args, dest)?,
109 "sqrtf64" => sqrt::<rustc_apfloat::ieee::Double>(this, args, dest)?,
110 "sqrtf128" => sqrt::<rustc_apfloat::ieee::Quad>(this, args, dest)?,
111
112 #[rustfmt::skip]
113 | "fadd_fast"
114 | "fsub_fast"
115 | "fmul_fast"
116 | "fdiv_fast"
117 | "frem_fast"
118 => {
119 let [a, b] = check_intrinsic_arg_count(args)?;
120 let a = this.read_immediate(a)?;
121 let b = this.read_immediate(b)?;
122 let op = match intrinsic_name {
123 "fadd_fast" => mir::BinOp::Add,
124 "fsub_fast" => mir::BinOp::Sub,
125 "fmul_fast" => mir::BinOp::Mul,
126 "fdiv_fast" => mir::BinOp::Div,
127 "frem_fast" => mir::BinOp::Rem,
128 _ => bug!(),
129 };
130 let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
131 let ty::Float(fty) = x.layout.ty.kind() else {
132 bug!("float_finite: non-float input type {}", x.layout.ty)
133 };
134 interp_ok(match fty {
135 FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
136 FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
137 FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
138 FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
139 })
140 };
141 match (float_finite(&a)?, float_finite(&b)?) {
142 (false, false) => throw_ub_format!(
143 "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
144 ),
145 (false, _) => throw_ub_format!(
146 "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
147 ),
148 (_, false) => throw_ub_format!(
149 "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
150 ),
151 _ => {}
152 }
153 let res = this.binary_op(op, &a, &b)?;
154 if !float_finite(&res)? {
157 throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
158 }
159 let res = math::apply_random_float_error_to_imm(this, res, 4)?;
162 this.write_immediate(*res, dest)?;
163 }
164
165 "float_to_int_unchecked" => {
166 let [val] = check_intrinsic_arg_count(args)?;
167 let val = this.read_immediate(val)?;
168
169 let res = this
170 .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
171 .ok_or_else(|| {
172 err_ub_format!(
173 "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
174 dest.layout.ty
175 )
176 })?;
177
178 this.write_immediate(*res, dest)?;
179 }
180
181 _ if let Some((float_ty, op)) = is_host_unary_float_op(intrinsic_name) => {
183 let [f] = check_intrinsic_arg_count(args)?;
184 match float_ty {
185 FloatTy::F16 => host_unary_float_op::<HalfS>(this, f, op, dest)?,
186 FloatTy::F32 => host_unary_float_op::<SingleS>(this, f, op, dest)?,
187 FloatTy::F64 => host_unary_float_op::<DoubleS>(this, f, op, dest)?,
188 FloatTy::F128 => todo!("f128"), };
190 }
191
192 "powf16" => pow_intrinsic::<HalfS>(this, args, dest)?,
193 "powf32" => pow_intrinsic::<SingleS>(this, args, dest)?,
194 "powf64" => pow_intrinsic::<DoubleS>(this, args, dest)?,
195 "powf128" => todo!("f128"), "powif16" => powi_intrinsic::<HalfS>(this, args, dest)?,
198 "powif32" => powi_intrinsic::<SingleS>(this, args, dest)?,
199 "powif64" => powi_intrinsic::<DoubleS>(this, args, dest)?,
200 "powif128" => todo!("f128"), _ => return interp_ok(EmulateItemResult::NotSupported),
203 }
204
205 interp_ok(EmulateItemResult::NeedsReturn)
206 }
207}