1#![warn(clippy::arithmetic_side_effects)]
2
3mod atomic;
4mod simd;
5
6use rand::Rng;
7use rustc_abi::Size;
8use rustc_apfloat::{self, Float, Round};
9use rustc_middle::mir;
10use rustc_middle::ty::{self, FloatTy};
11use rustc_span::{Symbol, sym};
12
13use self::atomic::EvalContextExt as _;
14use self::helpers::{ToHost, ToSoft};
15use self::simd::EvalContextExt as _;
16use crate::*;
17
18fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>(
20 args: &'a [OpTy<'tcx>],
21) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
22where
23 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
24{
25 if let Ok(ops) = args.try_into() {
26 return interp_ok(ops);
27 }
28 throw_ub_format!(
29 "incorrect number of arguments for intrinsic: got {}, expected {}",
30 args.len(),
31 N
32 )
33}
34
35impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
36pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
37 fn call_intrinsic(
38 &mut self,
39 instance: ty::Instance<'tcx>,
40 args: &[OpTy<'tcx>],
41 dest: &PlaceTy<'tcx>,
42 ret: Option<mir::BasicBlock>,
43 unwind: mir::UnwindAction,
44 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
45 let this = self.eval_context_mut();
46
47 if this.machine.force_intrinsic_fallback
49 && !this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden
50 {
51 return interp_ok(Some(ty::Instance {
52 def: ty::InstanceKind::Item(instance.def_id()),
53 args: instance.args,
54 }));
55 }
56
57 if this.eval_intrinsic(instance, args, dest, ret)? {
59 return interp_ok(None);
60 }
61 let intrinsic_name = this.tcx.item_name(instance.def_id());
62 let intrinsic_name = intrinsic_name.as_str();
63
64 let dest = this.force_allocation(dest)?;
66
67 match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, &dest, ret)? {
68 EmulateItemResult::NotSupported => {
69 if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
71 throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`")
72 }
73 let intrinsic_fallback_is_spec = Symbol::intern("intrinsic_fallback_is_spec");
74 if this
75 .tcx
76 .get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_is_spec])
77 .next()
78 .is_none()
79 {
80 throw_unsup_format!(
81 "Miri can only use intrinsic fallback bodies that exactly reflect the specification: they fully check for UB and are as non-deterministic as possible. After verifying that `{intrinsic_name}` does so, add the `#[miri::intrinsic_fallback_is_spec]` attribute to it; also ping @rust-lang/miri when you do that"
82 );
83 }
84 interp_ok(Some(ty::Instance {
85 def: ty::InstanceKind::Item(instance.def_id()),
86 args: instance.args,
87 }))
88 }
89 EmulateItemResult::NeedsReturn => {
90 trace!("{:?}", this.dump_place(&dest.clone().into()));
91 this.return_to_block(ret)?;
92 interp_ok(None)
93 }
94 EmulateItemResult::NeedsUnwind => {
95 this.unwind_to_block(unwind)?;
97 interp_ok(None)
98 }
99 EmulateItemResult::AlreadyJumped => interp_ok(None),
100 }
101 }
102
103 fn emulate_intrinsic_by_name(
106 &mut self,
107 intrinsic_name: &str,
108 generic_args: ty::GenericArgsRef<'tcx>,
109 args: &[OpTy<'tcx>],
110 dest: &MPlaceTy<'tcx>,
111 ret: Option<mir::BasicBlock>,
112 ) -> InterpResult<'tcx, EmulateItemResult> {
113 let this = self.eval_context_mut();
114
115 if let Some(name) = intrinsic_name.strip_prefix("atomic_") {
116 return this.emulate_atomic_intrinsic(name, generic_args, args, dest);
117 }
118 if let Some(name) = intrinsic_name.strip_prefix("simd_") {
119 return this.emulate_simd_intrinsic(name, generic_args, args, dest);
120 }
121
122 match intrinsic_name {
123 "abort" => {
125 throw_machine_stop!(TerminationInfo::Abort(
126 "the program aborted execution".to_owned()
127 ));
128 }
129 "catch_unwind" => {
130 let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
131 this.handle_catch_unwind(try_fn, data, catch_fn, dest, ret)?;
132 return interp_ok(EmulateItemResult::AlreadyJumped);
134 }
135
136 "volatile_load" => {
138 let [place] = check_intrinsic_arg_count(args)?;
139 let place = this.deref_pointer(place)?;
140 this.copy_op(&place, dest)?;
141 }
142 "volatile_store" => {
143 let [place, dest] = check_intrinsic_arg_count(args)?;
144 let place = this.deref_pointer(place)?;
145 this.copy_op(dest, &place)?;
146 }
147
148 "volatile_set_memory" => {
149 let [ptr, val_byte, count] = check_intrinsic_arg_count(args)?;
150 this.write_bytes_intrinsic(ptr, val_byte, count, "volatile_set_memory")?;
151 }
152
153 "ptr_mask" => {
155 let [ptr, mask] = check_intrinsic_arg_count(args)?;
156
157 let ptr = this.read_pointer(ptr)?;
158 let mask = this.read_target_usize(mask)?;
159
160 let masked_addr = Size::from_bytes(ptr.addr().bytes() & mask);
161
162 this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?;
163 }
164
165 "is_val_statically_known" => {
171 let [_arg] = check_intrinsic_arg_count(args)?;
172 let branch: bool = this.machine.rng.get_mut().random();
176 this.write_scalar(Scalar::from_bool(branch), dest)?;
177 }
178
179 "sqrtf32" => {
180 let [f] = check_intrinsic_arg_count(args)?;
181 let f = this.read_scalar(f)?.to_f32()?;
182 let res = math::sqrt(f);
184 let res = this.adjust_nan(res, &[f]);
185 this.write_scalar(res, dest)?;
186 }
187 "sqrtf64" => {
188 let [f] = check_intrinsic_arg_count(args)?;
189 let f = this.read_scalar(f)?.to_f64()?;
190 let res = math::sqrt(f);
192 let res = this.adjust_nan(res, &[f]);
193 this.write_scalar(res, dest)?;
194 }
195
196 #[rustfmt::skip]
197 | "sinf32"
198 | "cosf32"
199 | "expf32"
200 | "exp2f32"
201 | "logf32"
202 | "log10f32"
203 | "log2f32"
204 => {
205 let [f] = check_intrinsic_arg_count(args)?;
206 let f = this.read_scalar(f)?.to_f32()?;
207
208 let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
209 let host = f.to_host();
212 let res = match intrinsic_name {
213 "sinf32" => host.sin(),
214 "cosf32" => host.cos(),
215 "expf32" => host.exp(),
216 "exp2f32" => host.exp2(),
217 "logf32" => host.ln(),
218 "log10f32" => host.log10(),
219 "log2f32" => host.log2(),
220 _ => bug!(),
221 };
222 let res = res.to_soft();
223
224 let res = math::apply_random_float_error_ulp(
227 this,
228 res,
229 4,
230 );
231
232 math::clamp_float_value(intrinsic_name, res)
235 });
236 let res = this.adjust_nan(res, &[f]);
237 this.write_scalar(res, dest)?;
238 }
239
240 #[rustfmt::skip]
241 | "sinf64"
242 | "cosf64"
243 | "expf64"
244 | "exp2f64"
245 | "logf64"
246 | "log10f64"
247 | "log2f64"
248 => {
249 let [f] = check_intrinsic_arg_count(args)?;
250 let f = this.read_scalar(f)?.to_f64()?;
251
252 let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
253 let host = f.to_host();
256 let res = match intrinsic_name {
257 "sinf64" => host.sin(),
258 "cosf64" => host.cos(),
259 "expf64" => host.exp(),
260 "exp2f64" => host.exp2(),
261 "logf64" => host.ln(),
262 "log10f64" => host.log10(),
263 "log2f64" => host.log2(),
264 _ => bug!(),
265 };
266 let res = res.to_soft();
267
268 let res = math::apply_random_float_error_ulp(
271 this,
272 res,
273 4,
274 );
275
276 math::clamp_float_value(intrinsic_name, res)
279 });
280 let res = this.adjust_nan(res, &[f]);
281 this.write_scalar(res, dest)?;
282 }
283
284 "fmaf32" => {
285 let [a, b, c] = check_intrinsic_arg_count(args)?;
286 let a = this.read_scalar(a)?.to_f32()?;
287 let b = this.read_scalar(b)?.to_f32()?;
288 let c = this.read_scalar(c)?.to_f32()?;
289 let res = a.mul_add(b, c).value;
290 let res = this.adjust_nan(res, &[a, b, c]);
291 this.write_scalar(res, dest)?;
292 }
293 "fmaf64" => {
294 let [a, b, c] = check_intrinsic_arg_count(args)?;
295 let a = this.read_scalar(a)?.to_f64()?;
296 let b = this.read_scalar(b)?.to_f64()?;
297 let c = this.read_scalar(c)?.to_f64()?;
298 let res = a.mul_add(b, c).value;
299 let res = this.adjust_nan(res, &[a, b, c]);
300 this.write_scalar(res, dest)?;
301 }
302
303 "fmuladdf32" => {
304 let [a, b, c] = check_intrinsic_arg_count(args)?;
305 let a = this.read_scalar(a)?.to_f32()?;
306 let b = this.read_scalar(b)?.to_f32()?;
307 let c = this.read_scalar(c)?.to_f32()?;
308 let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
309 let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
310 let res = this.adjust_nan(res, &[a, b, c]);
311 this.write_scalar(res, dest)?;
312 }
313 "fmuladdf64" => {
314 let [a, b, c] = check_intrinsic_arg_count(args)?;
315 let a = this.read_scalar(a)?.to_f64()?;
316 let b = this.read_scalar(b)?.to_f64()?;
317 let c = this.read_scalar(c)?.to_f64()?;
318 let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
319 let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
320 let res = this.adjust_nan(res, &[a, b, c]);
321 this.write_scalar(res, dest)?;
322 }
323
324 "powf32" => {
325 let [f1, f2] = check_intrinsic_arg_count(args)?;
326 let f1 = this.read_scalar(f1)?.to_f32()?;
327 let f2 = this.read_scalar(f2)?.to_f32()?;
328
329 let res =
330 math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
331 let res = f1.to_host().powf(f2.to_host()).to_soft();
333
334 math::apply_random_float_error_ulp(this, res, 4)
337 });
338 let res = this.adjust_nan(res, &[f1, f2]);
339 this.write_scalar(res, dest)?;
340 }
341 "powf64" => {
342 let [f1, f2] = check_intrinsic_arg_count(args)?;
343 let f1 = this.read_scalar(f1)?.to_f64()?;
344 let f2 = this.read_scalar(f2)?.to_f64()?;
345
346 let res =
347 math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
348 let res = f1.to_host().powf(f2.to_host()).to_soft();
350
351 math::apply_random_float_error_ulp(this, res, 4)
354 });
355 let res = this.adjust_nan(res, &[f1, f2]);
356 this.write_scalar(res, dest)?;
357 }
358
359 "powif32" => {
360 let [f, i] = check_intrinsic_arg_count(args)?;
361 let f = this.read_scalar(f)?.to_f32()?;
362 let i = this.read_scalar(i)?.to_i32()?;
363
364 let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
365 let res = f.to_host().powi(i).to_soft();
367
368 math::apply_random_float_error_ulp(this, res, 4)
371 });
372 let res = this.adjust_nan(res, &[f]);
373 this.write_scalar(res, dest)?;
374 }
375 "powif64" => {
376 let [f, i] = check_intrinsic_arg_count(args)?;
377 let f = this.read_scalar(f)?.to_f64()?;
378 let i = this.read_scalar(i)?.to_i32()?;
379
380 let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
381 let res = f.to_host().powi(i).to_soft();
383
384 math::apply_random_float_error_ulp(this, res, 4)
387 });
388 let res = this.adjust_nan(res, &[f]);
389 this.write_scalar(res, dest)?;
390 }
391
392 #[rustfmt::skip]
393 | "fadd_fast"
394 | "fsub_fast"
395 | "fmul_fast"
396 | "fdiv_fast"
397 | "frem_fast"
398 => {
399 let [a, b] = check_intrinsic_arg_count(args)?;
400 let a = this.read_immediate(a)?;
401 let b = this.read_immediate(b)?;
402 let op = match intrinsic_name {
403 "fadd_fast" => mir::BinOp::Add,
404 "fsub_fast" => mir::BinOp::Sub,
405 "fmul_fast" => mir::BinOp::Mul,
406 "fdiv_fast" => mir::BinOp::Div,
407 "frem_fast" => mir::BinOp::Rem,
408 _ => bug!(),
409 };
410 let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
411 let ty::Float(fty) = x.layout.ty.kind() else {
412 bug!("float_finite: non-float input type {}", x.layout.ty)
413 };
414 interp_ok(match fty {
415 FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
416 FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
417 FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
418 FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
419 })
420 };
421 match (float_finite(&a)?, float_finite(&b)?) {
422 (false, false) => throw_ub_format!(
423 "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
424 ),
425 (false, _) => throw_ub_format!(
426 "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
427 ),
428 (_, false) => throw_ub_format!(
429 "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
430 ),
431 _ => {}
432 }
433 let res = this.binary_op(op, &a, &b)?;
434 if !float_finite(&res)? {
437 throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
438 }
439 let res = math::apply_random_float_error_to_imm(this, res, 4)?;
442 this.write_immediate(*res, dest)?;
443 }
444
445 "float_to_int_unchecked" => {
446 let [val] = check_intrinsic_arg_count(args)?;
447 let val = this.read_immediate(val)?;
448
449 let res = this
450 .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
451 .ok_or_else(|| {
452 err_ub_format!(
453 "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
454 dest.layout.ty
455 )
456 })?;
457
458 this.write_immediate(*res, dest)?;
459 }
460
461 "breakpoint" => {
463 let [] = check_intrinsic_arg_count(args)?;
464 throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap")))
466 }
467
468 "assert_inhabited" | "assert_zero_valid" | "assert_mem_uninitialized_valid" => {
469 }
471
472 _ => return interp_ok(EmulateItemResult::NotSupported),
473 }
474
475 interp_ok(EmulateItemResult::NeedsReturn)
476 }
477}