1use rustc_middle::mir::BinOp;
2use rustc_middle::ty::AtomicOrdering;
3use rustc_middle::{mir, ty};
4
5use self::helpers::check_intrinsic_arg_count;
6use crate::*;
7
8pub enum AtomicOp {
9 MirOp(mir::BinOp, bool),
12 Max,
13 Min,
14}
15
16impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
17pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
18 fn emulate_atomic_intrinsic(
21 &mut self,
22 intrinsic_name: &str,
23 generic_args: ty::GenericArgsRef<'tcx>,
24 args: &[OpTy<'tcx>],
25 dest: &MPlaceTy<'tcx>,
26 ) -> InterpResult<'tcx, EmulateItemResult> {
27 let this = self.eval_context_mut();
28
29 let get_ord_at = |i: usize| {
30 let ordering = generic_args.const_at(i).to_value();
31 ordering.valtree.unwrap_branch()[0].unwrap_leaf().to_atomic_ordering()
32 };
33
34 fn read_ord(ord: AtomicOrdering) -> AtomicReadOrd {
35 match ord {
36 AtomicOrdering::SeqCst => AtomicReadOrd::SeqCst,
37 AtomicOrdering::Acquire => AtomicReadOrd::Acquire,
38 AtomicOrdering::Relaxed => AtomicReadOrd::Relaxed,
39 _ => panic!("invalid read ordering `{ord:?}`"),
40 }
41 }
42
43 fn write_ord(ord: AtomicOrdering) -> AtomicWriteOrd {
44 match ord {
45 AtomicOrdering::SeqCst => AtomicWriteOrd::SeqCst,
46 AtomicOrdering::Release => AtomicWriteOrd::Release,
47 AtomicOrdering::Relaxed => AtomicWriteOrd::Relaxed,
48 _ => panic!("invalid write ordering `{ord:?}`"),
49 }
50 }
51
52 fn rw_ord(ord: AtomicOrdering) -> AtomicRwOrd {
53 match ord {
54 AtomicOrdering::SeqCst => AtomicRwOrd::SeqCst,
55 AtomicOrdering::AcqRel => AtomicRwOrd::AcqRel,
56 AtomicOrdering::Acquire => AtomicRwOrd::Acquire,
57 AtomicOrdering::Release => AtomicRwOrd::Release,
58 AtomicOrdering::Relaxed => AtomicRwOrd::Relaxed,
59 }
60 }
61
62 fn fence_ord(ord: AtomicOrdering) -> AtomicFenceOrd {
63 match ord {
64 AtomicOrdering::SeqCst => AtomicFenceOrd::SeqCst,
65 AtomicOrdering::AcqRel => AtomicFenceOrd::AcqRel,
66 AtomicOrdering::Acquire => AtomicFenceOrd::Acquire,
67 AtomicOrdering::Release => AtomicFenceOrd::Release,
68 _ => panic!("invalid fence ordering `{ord:?}`"),
69 }
70 }
71
72 match intrinsic_name {
73 "load" => {
74 let ord = get_ord_at(1);
75 this.atomic_load(args, dest, read_ord(ord))?;
76 }
77
78 "store" => {
79 let ord = get_ord_at(1);
80 this.atomic_store(args, write_ord(ord))?
81 }
82
83 "fence" => {
84 let ord = get_ord_at(0);
85 this.atomic_fence_intrinsic(args, fence_ord(ord))?
86 }
87 "singlethreadfence" => {
88 let ord = get_ord_at(0);
89 this.compiler_fence_intrinsic(args, fence_ord(ord))?;
90 }
91
92 "xchg" => {
93 let ord = get_ord_at(1);
94 this.atomic_exchange(args, dest, rw_ord(ord))?;
95 }
96 "cxchg" => {
97 let ord1 = get_ord_at(1);
98 let ord2 = get_ord_at(2);
99 this.atomic_compare_exchange(args, dest, rw_ord(ord1), read_ord(ord2))?;
100 }
101 "cxchgweak" => {
102 let ord1 = get_ord_at(1);
103 let ord2 = get_ord_at(2);
104 this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1), read_ord(ord2))?;
105 }
106
107 "or" => {
108 let ord = get_ord_at(1);
109 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?;
110 }
111 "xor" => {
112 let ord = get_ord_at(1);
113 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?;
114 }
115 "and" => {
116 let ord = get_ord_at(1);
117 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?;
118 }
119 "nand" => {
120 let ord = get_ord_at(1);
121 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?;
122 }
123 "xadd" => {
124 let ord = get_ord_at(1);
125 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?;
126 }
127 "xsub" => {
128 let ord = get_ord_at(1);
129 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?;
130 }
131 "min" => {
132 let ord = get_ord_at(1);
133 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
136 this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
137 }
138 "umin" => {
139 let ord = get_ord_at(1);
140 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
143 this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
144 }
145 "max" => {
146 let ord = get_ord_at(1);
147 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
150 this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
151 }
152 "umax" => {
153 let ord = get_ord_at(1);
154 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
157 this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
158 }
159
160 _ => return interp_ok(EmulateItemResult::NotSupported),
161 }
162 interp_ok(EmulateItemResult::NeedsReturn)
163 }
164}
165
166impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {}
167trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
168 fn atomic_load(
169 &mut self,
170 args: &[OpTy<'tcx>],
171 dest: &MPlaceTy<'tcx>,
172 atomic: AtomicReadOrd,
173 ) -> InterpResult<'tcx> {
174 let this = self.eval_context_mut();
175
176 let [place] = check_intrinsic_arg_count(args)?;
177 let place = this.deref_pointer(place)?;
178
179 let val = this.read_scalar_atomic(&place, atomic)?;
181 this.write_scalar(val, dest)?;
183 interp_ok(())
184 }
185
186 fn atomic_store(&mut self, args: &[OpTy<'tcx>], atomic: AtomicWriteOrd) -> InterpResult<'tcx> {
187 let this = self.eval_context_mut();
188
189 let [place, val] = check_intrinsic_arg_count(args)?;
190 let place = this.deref_pointer(place)?;
191
192 let val = this.read_scalar(val)?;
194 this.write_scalar_atomic(val, &place, atomic)?;
196 interp_ok(())
197 }
198
199 fn compiler_fence_intrinsic(
200 &mut self,
201 args: &[OpTy<'tcx>],
202 atomic: AtomicFenceOrd,
203 ) -> InterpResult<'tcx> {
204 let [] = check_intrinsic_arg_count(args)?;
205 let _ = atomic;
206 interp_ok(())
208 }
209
210 fn atomic_fence_intrinsic(
211 &mut self,
212 args: &[OpTy<'tcx>],
213 atomic: AtomicFenceOrd,
214 ) -> InterpResult<'tcx> {
215 let this = self.eval_context_mut();
216 let [] = check_intrinsic_arg_count(args)?;
217 this.atomic_fence(atomic)?;
218 interp_ok(())
219 }
220
221 fn atomic_rmw_op(
222 &mut self,
223 args: &[OpTy<'tcx>],
224 dest: &MPlaceTy<'tcx>,
225 atomic_op: AtomicOp,
226 atomic: AtomicRwOrd,
227 ) -> InterpResult<'tcx> {
228 let this = self.eval_context_mut();
229
230 let [place, rhs] = check_intrinsic_arg_count(args)?;
231 let place = this.deref_pointer(place)?;
232 let rhs = this.read_immediate(rhs)?;
233
234 if !place.layout.ty.is_integral() && !place.layout.ty.is_raw_ptr() {
235 span_bug!(
236 this.cur_span(),
237 "atomic arithmetic operations only work on integer and raw pointer types",
238 );
239 }
240 if rhs.layout.ty != place.layout.ty {
241 span_bug!(this.cur_span(), "atomic arithmetic operation type mismatch");
242 }
243
244 let old = match atomic_op {
245 AtomicOp::Min =>
246 this.atomic_min_max_scalar(&place, rhs, true, atomic)?,
247 AtomicOp::Max =>
248 this.atomic_min_max_scalar(&place, rhs, false, atomic)?,
249 AtomicOp::MirOp(op, not) =>
250 this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?,
251 };
252 this.write_immediate(*old, dest)?; interp_ok(())
254 }
255
256 fn atomic_exchange(
257 &mut self,
258 args: &[OpTy<'tcx>],
259 dest: &MPlaceTy<'tcx>,
260 atomic: AtomicRwOrd,
261 ) -> InterpResult<'tcx> {
262 let this = self.eval_context_mut();
263
264 let [place, new] = check_intrinsic_arg_count(args)?;
265 let place = this.deref_pointer(place)?;
266 let new = this.read_scalar(new)?;
267
268 let old = this.atomic_exchange_scalar(&place, new, atomic)?;
269 this.write_scalar(old, dest)?; interp_ok(())
271 }
272
273 fn atomic_compare_exchange_impl(
274 &mut self,
275 args: &[OpTy<'tcx>],
276 dest: &MPlaceTy<'tcx>,
277 success: AtomicRwOrd,
278 fail: AtomicReadOrd,
279 can_fail_spuriously: bool,
280 ) -> InterpResult<'tcx> {
281 let this = self.eval_context_mut();
282
283 let [place, expect_old, new] = check_intrinsic_arg_count(args)?;
284 let place = this.deref_pointer(place)?;
285 let expect_old = this.read_immediate(expect_old)?; let new = this.read_scalar(new)?;
287
288 let old = this.atomic_compare_exchange_scalar(
289 &place,
290 &expect_old,
291 new,
292 success,
293 fail,
294 can_fail_spuriously,
295 )?;
296
297 this.write_immediate(old, dest)?;
299 interp_ok(())
300 }
301
302 fn atomic_compare_exchange(
303 &mut self,
304 args: &[OpTy<'tcx>],
305 dest: &MPlaceTy<'tcx>,
306 success: AtomicRwOrd,
307 fail: AtomicReadOrd,
308 ) -> InterpResult<'tcx> {
309 self.atomic_compare_exchange_impl(args, dest, success, fail, false)
310 }
311
312 fn atomic_compare_exchange_weak(
313 &mut self,
314 args: &[OpTy<'tcx>],
315 dest: &MPlaceTy<'tcx>,
316 success: AtomicRwOrd,
317 fail: AtomicReadOrd,
318 ) -> InterpResult<'tcx> {
319 self.atomic_compare_exchange_impl(args, dest, success, fail, true)
320 }
321}