1#![warn(clippy::arithmetic_side_effects)]
2
3mod atomic;
4mod simd;
5
6use std::ops::Neg;
7
8use rand::Rng;
9use rustc_abi::Size;
10use rustc_apfloat::ieee::{IeeeFloat, Semantics};
11use rustc_apfloat::{self, Float, Round};
12use rustc_middle::mir;
13use rustc_middle::ty::{self, FloatTy, ScalarInt};
14use rustc_span::{Symbol, sym};
15
16use self::atomic::EvalContextExt as _;
17use self::helpers::{ToHost, ToSoft};
18use self::simd::EvalContextExt as _;
19use crate::math::{IeeeExt, apply_random_float_error_ulp};
20use crate::*;
21
22fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>(
24 args: &'a [OpTy<'tcx>],
25) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
26where
27 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
28{
29 if let Ok(ops) = args.try_into() {
30 return interp_ok(ops);
31 }
32 throw_ub_format!(
33 "incorrect number of arguments for intrinsic: got {}, expected {}",
34 args.len(),
35 N
36 )
37}
38
39impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
40pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
41 fn call_intrinsic(
42 &mut self,
43 instance: ty::Instance<'tcx>,
44 args: &[OpTy<'tcx>],
45 dest: &PlaceTy<'tcx>,
46 ret: Option<mir::BasicBlock>,
47 unwind: mir::UnwindAction,
48 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
49 let this = self.eval_context_mut();
50
51 if this.machine.force_intrinsic_fallback
53 && !this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden
54 {
55 return interp_ok(Some(ty::Instance {
56 def: ty::InstanceKind::Item(instance.def_id()),
57 args: instance.args,
58 }));
59 }
60
61 if this.eval_intrinsic(instance, args, dest, ret)? {
63 return interp_ok(None);
64 }
65 let intrinsic_name = this.tcx.item_name(instance.def_id());
66 let intrinsic_name = intrinsic_name.as_str();
67
68 let dest = this.force_allocation(dest)?;
70
71 match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, &dest, ret)? {
72 EmulateItemResult::NotSupported => {
73 if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
75 throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`")
76 }
77 let intrinsic_fallback_is_spec = Symbol::intern("intrinsic_fallback_is_spec");
78 if this
79 .tcx
80 .get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_is_spec])
81 .next()
82 .is_none()
83 {
84 throw_unsup_format!(
85 "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"
86 );
87 }
88 interp_ok(Some(ty::Instance {
89 def: ty::InstanceKind::Item(instance.def_id()),
90 args: instance.args,
91 }))
92 }
93 EmulateItemResult::NeedsReturn => {
94 trace!("{:?}", this.dump_place(&dest.clone().into()));
95 this.return_to_block(ret)?;
96 interp_ok(None)
97 }
98 EmulateItemResult::NeedsUnwind => {
99 this.unwind_to_block(unwind)?;
101 interp_ok(None)
102 }
103 EmulateItemResult::AlreadyJumped => interp_ok(None),
104 }
105 }
106
107 fn emulate_intrinsic_by_name(
110 &mut self,
111 intrinsic_name: &str,
112 generic_args: ty::GenericArgsRef<'tcx>,
113 args: &[OpTy<'tcx>],
114 dest: &MPlaceTy<'tcx>,
115 ret: Option<mir::BasicBlock>,
116 ) -> InterpResult<'tcx, EmulateItemResult> {
117 let this = self.eval_context_mut();
118
119 if let Some(name) = intrinsic_name.strip_prefix("atomic_") {
120 return this.emulate_atomic_intrinsic(name, generic_args, args, dest);
121 }
122 if let Some(name) = intrinsic_name.strip_prefix("simd_") {
123 return this.emulate_simd_intrinsic(name, generic_args, args, dest);
124 }
125
126 match intrinsic_name {
127 "abort" => {
129 throw_machine_stop!(TerminationInfo::Abort(
130 "the program aborted execution".to_owned()
131 ));
132 }
133 "catch_unwind" => {
134 let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
135 this.handle_catch_unwind(try_fn, data, catch_fn, dest, ret)?;
136 return interp_ok(EmulateItemResult::AlreadyJumped);
138 }
139
140 "volatile_load" => {
142 let [place] = check_intrinsic_arg_count(args)?;
143 let place = this.deref_pointer(place)?;
144 this.copy_op(&place, dest)?;
145 }
146 "volatile_store" => {
147 let [place, dest] = check_intrinsic_arg_count(args)?;
148 let place = this.deref_pointer(place)?;
149 this.copy_op(dest, &place)?;
150 }
151
152 "volatile_set_memory" => {
153 let [ptr, val_byte, count] = check_intrinsic_arg_count(args)?;
154 this.write_bytes_intrinsic(ptr, val_byte, count, "volatile_set_memory")?;
155 }
156
157 "ptr_mask" => {
159 let [ptr, mask] = check_intrinsic_arg_count(args)?;
160
161 let ptr = this.read_pointer(ptr)?;
162 let mask = this.read_target_usize(mask)?;
163
164 let masked_addr = Size::from_bytes(ptr.addr().bytes() & mask);
165
166 this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?;
167 }
168
169 "is_val_statically_known" => {
175 let [_arg] = check_intrinsic_arg_count(args)?;
176 let branch: bool = this.machine.rng.get_mut().random();
180 this.write_scalar(Scalar::from_bool(branch), dest)?;
181 }
182
183 "sqrtf32" => {
184 let [f] = check_intrinsic_arg_count(args)?;
185 let f = this.read_scalar(f)?.to_f32()?;
186 let res = math::sqrt(f);
188 let res = this.adjust_nan(res, &[f]);
189 this.write_scalar(res, dest)?;
190 }
191 "sqrtf64" => {
192 let [f] = check_intrinsic_arg_count(args)?;
193 let f = this.read_scalar(f)?.to_f64()?;
194 let res = math::sqrt(f);
196 let res = this.adjust_nan(res, &[f]);
197 this.write_scalar(res, dest)?;
198 }
199
200 #[rustfmt::skip]
201 | "sinf32"
202 | "cosf32"
203 | "expf32"
204 | "exp2f32"
205 | "logf32"
206 | "log10f32"
207 | "log2f32"
208 => {
209 let [f] = check_intrinsic_arg_count(args)?;
210 let f = this.read_scalar(f)?.to_f32()?;
211
212 let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
213 let host = f.to_host();
216 let res = match intrinsic_name {
217 "sinf32" => host.sin(),
218 "cosf32" => host.cos(),
219 "expf32" => host.exp(),
220 "exp2f32" => host.exp2(),
221 "logf32" => host.ln(),
222 "log10f32" => host.log10(),
223 "log2f32" => host.log2(),
224 _ => bug!(),
225 };
226 let res = res.to_soft();
227
228 let res = apply_random_float_error_ulp(
231 this,
232 res,
233 2, );
235
236 clamp_float_value(intrinsic_name, res)
239 });
240 let res = this.adjust_nan(res, &[f]);
241 this.write_scalar(res, dest)?;
242 }
243
244 #[rustfmt::skip]
245 | "sinf64"
246 | "cosf64"
247 | "expf64"
248 | "exp2f64"
249 | "logf64"
250 | "log10f64"
251 | "log2f64"
252 => {
253 let [f] = check_intrinsic_arg_count(args)?;
254 let f = this.read_scalar(f)?.to_f64()?;
255
256 let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
257 let host = f.to_host();
260 let res = match intrinsic_name {
261 "sinf64" => host.sin(),
262 "cosf64" => host.cos(),
263 "expf64" => host.exp(),
264 "exp2f64" => host.exp2(),
265 "logf64" => host.ln(),
266 "log10f64" => host.log10(),
267 "log2f64" => host.log2(),
268 _ => bug!(),
269 };
270 let res = res.to_soft();
271
272 let res = apply_random_float_error_ulp(
275 this,
276 res,
277 2, );
279
280 clamp_float_value(intrinsic_name, res)
283 });
284 let res = this.adjust_nan(res, &[f]);
285 this.write_scalar(res, dest)?;
286 }
287
288 "fmaf32" => {
289 let [a, b, c] = check_intrinsic_arg_count(args)?;
290 let a = this.read_scalar(a)?.to_f32()?;
291 let b = this.read_scalar(b)?.to_f32()?;
292 let c = this.read_scalar(c)?.to_f32()?;
293 let res = a.mul_add(b, c).value;
294 let res = this.adjust_nan(res, &[a, b, c]);
295 this.write_scalar(res, dest)?;
296 }
297 "fmaf64" => {
298 let [a, b, c] = check_intrinsic_arg_count(args)?;
299 let a = this.read_scalar(a)?.to_f64()?;
300 let b = this.read_scalar(b)?.to_f64()?;
301 let c = this.read_scalar(c)?.to_f64()?;
302 let res = a.mul_add(b, c).value;
303 let res = this.adjust_nan(res, &[a, b, c]);
304 this.write_scalar(res, dest)?;
305 }
306
307 "fmuladdf32" => {
308 let [a, b, c] = check_intrinsic_arg_count(args)?;
309 let a = this.read_scalar(a)?.to_f32()?;
310 let b = this.read_scalar(b)?.to_f32()?;
311 let c = this.read_scalar(c)?.to_f32()?;
312 let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
313 let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
314 let res = this.adjust_nan(res, &[a, b, c]);
315 this.write_scalar(res, dest)?;
316 }
317 "fmuladdf64" => {
318 let [a, b, c] = check_intrinsic_arg_count(args)?;
319 let a = this.read_scalar(a)?.to_f64()?;
320 let b = this.read_scalar(b)?.to_f64()?;
321 let c = this.read_scalar(c)?.to_f64()?;
322 let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
323 let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
324 let res = this.adjust_nan(res, &[a, b, c]);
325 this.write_scalar(res, dest)?;
326 }
327
328 "powf32" => {
329 let [f1, f2] = check_intrinsic_arg_count(args)?;
330 let f1 = this.read_scalar(f1)?.to_f32()?;
331 let f2 = this.read_scalar(f2)?.to_f32()?;
332
333 let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
334 let res = f1.to_host().powf(f2.to_host()).to_soft();
336
337 apply_random_float_error_ulp(
340 this, res, 2, )
342 });
343 let res = this.adjust_nan(res, &[f1, f2]);
344 this.write_scalar(res, dest)?;
345 }
346 "powf64" => {
347 let [f1, f2] = check_intrinsic_arg_count(args)?;
348 let f1 = this.read_scalar(f1)?.to_f64()?;
349 let f2 = this.read_scalar(f2)?.to_f64()?;
350
351 let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
352 let res = f1.to_host().powf(f2.to_host()).to_soft();
354
355 apply_random_float_error_ulp(
358 this, res, 2, )
360 });
361 let res = this.adjust_nan(res, &[f1, f2]);
362 this.write_scalar(res, dest)?;
363 }
364
365 "powif32" => {
366 let [f, i] = check_intrinsic_arg_count(args)?;
367 let f = this.read_scalar(f)?.to_f32()?;
368 let i = this.read_scalar(i)?.to_i32()?;
369
370 let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
371 let res = f.to_host().powi(i).to_soft();
373
374 apply_random_float_error_ulp(
377 this, res, 2, )
379 });
380 let res = this.adjust_nan(res, &[f]);
381 this.write_scalar(res, dest)?;
382 }
383 "powif64" => {
384 let [f, i] = check_intrinsic_arg_count(args)?;
385 let f = this.read_scalar(f)?.to_f64()?;
386 let i = this.read_scalar(i)?.to_i32()?;
387
388 let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
389 let res = f.to_host().powi(i).to_soft();
391
392 apply_random_float_error_ulp(
395 this, res, 2, )
397 });
398 let res = this.adjust_nan(res, &[f]);
399 this.write_scalar(res, dest)?;
400 }
401
402 #[rustfmt::skip]
403 | "fadd_fast"
404 | "fsub_fast"
405 | "fmul_fast"
406 | "fdiv_fast"
407 | "frem_fast"
408 => {
409 let [a, b] = check_intrinsic_arg_count(args)?;
410 let a = this.read_immediate(a)?;
411 let b = this.read_immediate(b)?;
412 let op = match intrinsic_name {
413 "fadd_fast" => mir::BinOp::Add,
414 "fsub_fast" => mir::BinOp::Sub,
415 "fmul_fast" => mir::BinOp::Mul,
416 "fdiv_fast" => mir::BinOp::Div,
417 "frem_fast" => mir::BinOp::Rem,
418 _ => bug!(),
419 };
420 let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
421 let ty::Float(fty) = x.layout.ty.kind() else {
422 bug!("float_finite: non-float input type {}", x.layout.ty)
423 };
424 interp_ok(match fty {
425 FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
426 FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
427 FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
428 FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
429 })
430 };
431 match (float_finite(&a)?, float_finite(&b)?) {
432 (false, false) => throw_ub_format!(
433 "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
434 ),
435 (false, _) => throw_ub_format!(
436 "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
437 ),
438 (_, false) => throw_ub_format!(
439 "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
440 ),
441 _ => {}
442 }
443 let res = this.binary_op(op, &a, &b)?;
444 if !float_finite(&res)? {
447 throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
448 }
449 let res = apply_random_float_error_to_imm(this, res, 2 )?;
452 this.write_immediate(*res, dest)?;
453 }
454
455 "float_to_int_unchecked" => {
456 let [val] = check_intrinsic_arg_count(args)?;
457 let val = this.read_immediate(val)?;
458
459 let res = this
460 .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
461 .ok_or_else(|| {
462 err_ub_format!(
463 "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
464 dest.layout.ty
465 )
466 })?;
467
468 this.write_immediate(*res, dest)?;
469 }
470
471 "breakpoint" => {
473 let [] = check_intrinsic_arg_count(args)?;
474 throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap")))
476 }
477
478 "assert_inhabited" | "assert_zero_valid" | "assert_mem_uninitialized_valid" => {
479 }
481
482 _ => return interp_ok(EmulateItemResult::NotSupported),
483 }
484
485 interp_ok(EmulateItemResult::NeedsReturn)
486 }
487}
488
489fn apply_random_float_error_to_imm<'tcx>(
494 ecx: &mut MiriInterpCx<'tcx>,
495 val: ImmTy<'tcx>,
496 ulp_exponent: u32,
497) -> InterpResult<'tcx, ImmTy<'tcx>> {
498 let scalar = val.to_scalar_int()?;
499 let res: ScalarInt = match val.layout.ty.kind() {
500 ty::Float(FloatTy::F16) =>
501 apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(),
502 ty::Float(FloatTy::F32) =>
503 apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(),
504 ty::Float(FloatTy::F64) =>
505 apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(),
506 ty::Float(FloatTy::F128) =>
507 apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(),
508 _ => bug!("intrinsic called with non-float input type"),
509 };
510
511 interp_ok(ImmTy::from_scalar_int(res, val.layout))
512}
513
514fn fixed_float_value<S: Semantics>(
544 ecx: &mut MiriInterpCx<'_>,
545 intrinsic_name: &str,
546 args: &[IeeeFloat<S>],
547) -> Option<IeeeFloat<S>> {
548 let one = IeeeFloat::<S>::one();
549 Some(match (intrinsic_name, args) {
550 ("cosf32" | "cosf64", [input]) if input.is_zero() => one,
552
553 ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one,
555
556 ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one,
558
559 ("powf32" | "powf64", [base, exp]) if *base == one => {
561 let rng = ecx.machine.rng.get_mut();
562 let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random();
564 if return_nan { ecx.generate_nan(args) } else { one }
566 }
567
568 ("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
570 let rng = ecx.machine.rng.get_mut();
571 let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random();
573 if return_nan { ecx.generate_nan(args) } else { one }
575 }
576
577 _ => return None,
580 })
581}
582
583fn fixed_powi_float_value<S: Semantics>(
586 ecx: &mut MiriInterpCx<'_>,
587 base: IeeeFloat<S>,
588 exp: i32,
589) -> Option<IeeeFloat<S>> {
590 Some(match exp {
591 0 => {
592 let one = IeeeFloat::<S>::one();
593 let rng = ecx.machine.rng.get_mut();
594 let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling();
595 if return_nan { ecx.generate_nan(&[base]) } else { one }
599 }
600
601 _ => return None,
602 })
603}
604
605fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> {
608 match intrinsic_name {
609 "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
611 val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()),
612 "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
614 IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO),
615 _ => val,
616 }
617}