1#![warn(clippy::arithmetic_side_effects)]
2
3mod atomic;
4mod math;
5mod simd;
6
7pub use self::atomic::AtomicRmwOp;
8
9#[rustfmt::skip] use rand::Rng;
11use rustc_abi::Size;
12use rustc_middle::{mir, ty};
13use rustc_span::{Symbol, sym};
14
15use self::atomic::EvalContextExt as _;
16use self::math::EvalContextExt as _;
17use self::simd::EvalContextExt as _;
18use crate::*;
19
20fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>(
22 args: &'a [OpTy<'tcx>],
23) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
24where
25 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
26{
27 if let Ok(ops) = args.try_into() {
28 return interp_ok(ops);
29 }
30 throw_ub_format!(
31 "incorrect number of arguments for intrinsic: got {}, expected {}",
32 args.len(),
33 N
34 )
35}
36
37impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
38pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
39 fn call_intrinsic(
40 &mut self,
41 instance: ty::Instance<'tcx>,
42 args: &[OpTy<'tcx>],
43 dest: &PlaceTy<'tcx>,
44 ret: Option<mir::BasicBlock>,
45 unwind: mir::UnwindAction,
46 ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
47 let this = self.eval_context_mut();
48
49 if this.machine.force_intrinsic_fallback
51 && !this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden
52 {
53 return interp_ok(Some(ty::Instance {
54 def: ty::InstanceKind::Item(instance.def_id()),
55 args: instance.args,
56 }));
57 }
58
59 if this.eval_intrinsic(instance, args, dest, ret)? {
61 return interp_ok(None);
62 }
63 let intrinsic_name = this.tcx.item_name(instance.def_id());
64 let intrinsic_name = intrinsic_name.as_str();
65
66 let dest = this.force_allocation(dest)?;
68
69 match this.emulate_intrinsic_by_name(intrinsic_name, instance.args, args, &dest, ret)? {
70 EmulateItemResult::NotSupported => {
71 if this.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
73 throw_unsup_format!("unimplemented intrinsic: `{intrinsic_name}`")
74 }
75 let intrinsic_fallback_is_spec = Symbol::intern("intrinsic_fallback_is_spec");
76 if this
77 .tcx
78 .get_attrs_by_path(instance.def_id(), &[sym::miri, intrinsic_fallback_is_spec])
79 .next()
80 .is_none()
81 {
82 throw_unsup_format!(
83 "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"
84 );
85 }
86 interp_ok(Some(ty::Instance {
87 def: ty::InstanceKind::Item(instance.def_id()),
88 args: instance.args,
89 }))
90 }
91 EmulateItemResult::NeedsReturn => {
92 trace!("{:?}", this.dump_place(&dest.clone().into()));
93 this.return_to_block(ret)?;
94 interp_ok(None)
95 }
96 EmulateItemResult::NeedsUnwind => {
97 this.unwind_to_block(unwind)?;
99 interp_ok(None)
100 }
101 EmulateItemResult::AlreadyJumped => interp_ok(None),
102 }
103 }
104
105 fn emulate_intrinsic_by_name(
108 &mut self,
109 intrinsic_name: &str,
110 generic_args: ty::GenericArgsRef<'tcx>,
111 args: &[OpTy<'tcx>],
112 dest: &MPlaceTy<'tcx>,
113 ret: Option<mir::BasicBlock>,
114 ) -> InterpResult<'tcx, EmulateItemResult> {
115 let this = self.eval_context_mut();
116
117 if let Some(name) = intrinsic_name.strip_prefix("atomic_") {
118 return this.emulate_atomic_intrinsic(name, generic_args, args, dest);
119 }
120 if let Some(name) = intrinsic_name.strip_prefix("simd_") {
121 return this.emulate_simd_intrinsic(name, generic_args, args, dest);
122 }
123
124 match intrinsic_name {
125 "abort" => {
127 throw_machine_stop!(TerminationInfo::Abort(
128 "the program aborted execution".to_owned()
129 ));
130 }
131 "catch_unwind" => {
132 let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
133 this.handle_catch_unwind(try_fn, data, catch_fn, dest, ret)?;
134 return interp_ok(EmulateItemResult::AlreadyJumped);
136 }
137
138 "volatile_load" => {
140 let [place] = check_intrinsic_arg_count(args)?;
141 let place = this.deref_pointer(place)?;
142 this.copy_op(&place, dest)?;
143 }
144 "volatile_store" => {
145 let [place, dest] = check_intrinsic_arg_count(args)?;
146 let place = this.deref_pointer(place)?;
147 this.copy_op(dest, &place)?;
148 }
149
150 "volatile_set_memory" => {
151 let [ptr, val_byte, count] = check_intrinsic_arg_count(args)?;
152 this.write_bytes_intrinsic(ptr, val_byte, count, "volatile_set_memory")?;
153 }
154
155 "ptr_mask" => {
157 let [ptr, mask] = check_intrinsic_arg_count(args)?;
158
159 let ptr = this.read_pointer(ptr)?;
160 let mask = this.read_target_usize(mask)?;
161
162 let masked_addr = Size::from_bytes(ptr.addr().bytes() & mask);
163
164 this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?;
165 }
166
167 "is_val_statically_known" => {
173 let [_arg] = check_intrinsic_arg_count(args)?;
174 let branch: bool = this.machine.rng.get_mut().random();
178 this.write_scalar(Scalar::from_bool(branch), dest)?;
179 }
180
181 "breakpoint" => {
183 let [] = check_intrinsic_arg_count(args)?;
184 throw_machine_stop!(TerminationInfo::Abort(format!("trace/breakpoint trap")))
186 }
187
188 "assert_inhabited" | "assert_zero_valid" | "assert_mem_uninitialized_valid" => {
189 }
191
192 _ => return this.emulate_math_intrinsic(intrinsic_name, generic_args, args, dest),
193 }
194
195 interp_ok(EmulateItemResult::NeedsReturn)
196 }
197}