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 intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect();
30
31 fn read_ord(ord: &str) -> AtomicReadOrd {
32 match ord {
33 "seqcst" => AtomicReadOrd::SeqCst,
34 "acquire" => AtomicReadOrd::Acquire,
35 "relaxed" => AtomicReadOrd::Relaxed,
36 _ => panic!("invalid read ordering `{ord}`"),
37 }
38 }
39
40 fn read_ord_const_generic(o: AtomicOrdering) -> AtomicReadOrd {
41 match o {
42 AtomicOrdering::SeqCst => AtomicReadOrd::SeqCst,
43 AtomicOrdering::Acquire => AtomicReadOrd::Acquire,
44 AtomicOrdering::Relaxed => AtomicReadOrd::Relaxed,
45 _ => panic!("invalid read ordering `{o:?}`"),
46 }
47 }
48
49 fn write_ord(ord: &str) -> AtomicWriteOrd {
50 match ord {
51 "seqcst" => AtomicWriteOrd::SeqCst,
52 "release" => AtomicWriteOrd::Release,
53 "relaxed" => AtomicWriteOrd::Relaxed,
54 _ => panic!("invalid write ordering `{ord}`"),
55 }
56 }
57
58 fn rw_ord(ord: &str) -> AtomicRwOrd {
59 match ord {
60 "seqcst" => AtomicRwOrd::SeqCst,
61 "acqrel" => AtomicRwOrd::AcqRel,
62 "acquire" => AtomicRwOrd::Acquire,
63 "release" => AtomicRwOrd::Release,
64 "relaxed" => AtomicRwOrd::Relaxed,
65 _ => panic!("invalid read-write ordering `{ord}`"),
66 }
67 }
68
69 fn fence_ord(ord: &str) -> AtomicFenceOrd {
70 match ord {
71 "seqcst" => AtomicFenceOrd::SeqCst,
72 "acqrel" => AtomicFenceOrd::AcqRel,
73 "acquire" => AtomicFenceOrd::Acquire,
74 "release" => AtomicFenceOrd::Release,
75 _ => panic!("invalid fence ordering `{ord}`"),
76 }
77 }
78
79 match &*intrinsic_structure {
80 ["load"] => {
82 let ordering = generic_args.const_at(1).to_value();
83 let ordering =
84 ordering.valtree.unwrap_branch()[0].unwrap_leaf().to_atomic_ordering();
85 this.atomic_load(args, dest, read_ord_const_generic(ordering))?;
86 }
87
88 ["store", ord] => this.atomic_store(args, write_ord(ord))?,
90
91 ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord))?,
92 ["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord))?,
93
94 ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord))?,
95 ["cxchg", ord1, ord2] =>
96 this.atomic_compare_exchange(args, dest, rw_ord(ord1), read_ord(ord2))?,
97 ["cxchgweak", ord1, ord2] =>
98 this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1), read_ord(ord2))?,
99
100 ["or", ord] =>
101 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?,
102 ["xor", ord] =>
103 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?,
104 ["and", ord] =>
105 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?,
106 ["nand", ord] =>
107 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?,
108 ["xadd", ord] =>
109 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?,
110 ["xsub", ord] =>
111 this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?,
112 ["min", ord] => {
113 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
116 this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
117 }
118 ["umin", ord] => {
119 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
122 this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
123 }
124 ["max", ord] => {
125 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
128 this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
129 }
130 ["umax", ord] => {
131 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
134 this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
135 }
136
137 _ => return interp_ok(EmulateItemResult::NotSupported),
138 }
139 interp_ok(EmulateItemResult::NeedsReturn)
140 }
141}
142
143impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {}
144trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
145 fn atomic_load(
146 &mut self,
147 args: &[OpTy<'tcx>],
148 dest: &MPlaceTy<'tcx>,
149 atomic: AtomicReadOrd,
150 ) -> InterpResult<'tcx> {
151 let this = self.eval_context_mut();
152
153 let [place] = check_intrinsic_arg_count(args)?;
154 let place = this.deref_pointer(place)?;
155
156 let val = this.read_scalar_atomic(&place, atomic)?;
158 this.write_scalar(val, dest)?;
160 interp_ok(())
161 }
162
163 fn atomic_store(&mut self, args: &[OpTy<'tcx>], atomic: AtomicWriteOrd) -> InterpResult<'tcx> {
164 let this = self.eval_context_mut();
165
166 let [place, val] = check_intrinsic_arg_count(args)?;
167 let place = this.deref_pointer(place)?;
168
169 let val = this.read_scalar(val)?;
171 this.write_scalar_atomic(val, &place, atomic)?;
173 interp_ok(())
174 }
175
176 fn compiler_fence_intrinsic(
177 &mut self,
178 args: &[OpTy<'tcx>],
179 atomic: AtomicFenceOrd,
180 ) -> InterpResult<'tcx> {
181 let [] = check_intrinsic_arg_count(args)?;
182 let _ = atomic;
183 interp_ok(())
185 }
186
187 fn atomic_fence_intrinsic(
188 &mut self,
189 args: &[OpTy<'tcx>],
190 atomic: AtomicFenceOrd,
191 ) -> InterpResult<'tcx> {
192 let this = self.eval_context_mut();
193 let [] = check_intrinsic_arg_count(args)?;
194 this.atomic_fence(atomic)?;
195 interp_ok(())
196 }
197
198 fn atomic_rmw_op(
199 &mut self,
200 args: &[OpTy<'tcx>],
201 dest: &MPlaceTy<'tcx>,
202 atomic_op: AtomicOp,
203 atomic: AtomicRwOrd,
204 ) -> InterpResult<'tcx> {
205 let this = self.eval_context_mut();
206
207 let [place, rhs] = check_intrinsic_arg_count(args)?;
208 let place = this.deref_pointer(place)?;
209 let rhs = this.read_immediate(rhs)?;
210
211 if !place.layout.ty.is_integral() && !place.layout.ty.is_raw_ptr() {
212 span_bug!(
213 this.cur_span(),
214 "atomic arithmetic operations only work on integer and raw pointer types",
215 );
216 }
217 if rhs.layout.ty != place.layout.ty {
218 span_bug!(this.cur_span(), "atomic arithmetic operation type mismatch");
219 }
220
221 let old = match atomic_op {
222 AtomicOp::Min =>
223 this.atomic_min_max_scalar(&place, rhs, true, atomic)?,
224 AtomicOp::Max =>
225 this.atomic_min_max_scalar(&place, rhs, false, atomic)?,
226 AtomicOp::MirOp(op, not) =>
227 this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?,
228 };
229 this.write_immediate(*old, dest)?; interp_ok(())
231 }
232
233 fn atomic_exchange(
234 &mut self,
235 args: &[OpTy<'tcx>],
236 dest: &MPlaceTy<'tcx>,
237 atomic: AtomicRwOrd,
238 ) -> InterpResult<'tcx> {
239 let this = self.eval_context_mut();
240
241 let [place, new] = check_intrinsic_arg_count(args)?;
242 let place = this.deref_pointer(place)?;
243 let new = this.read_scalar(new)?;
244
245 let old = this.atomic_exchange_scalar(&place, new, atomic)?;
246 this.write_scalar(old, dest)?; interp_ok(())
248 }
249
250 fn atomic_compare_exchange_impl(
251 &mut self,
252 args: &[OpTy<'tcx>],
253 dest: &MPlaceTy<'tcx>,
254 success: AtomicRwOrd,
255 fail: AtomicReadOrd,
256 can_fail_spuriously: bool,
257 ) -> InterpResult<'tcx> {
258 let this = self.eval_context_mut();
259
260 let [place, expect_old, new] = check_intrinsic_arg_count(args)?;
261 let place = this.deref_pointer(place)?;
262 let expect_old = this.read_immediate(expect_old)?; let new = this.read_scalar(new)?;
264
265 let old = this.atomic_compare_exchange_scalar(
266 &place,
267 &expect_old,
268 new,
269 success,
270 fail,
271 can_fail_spuriously,
272 )?;
273
274 this.write_immediate(old, dest)?;
276 interp_ok(())
277 }
278
279 fn atomic_compare_exchange(
280 &mut self,
281 args: &[OpTy<'tcx>],
282 dest: &MPlaceTy<'tcx>,
283 success: AtomicRwOrd,
284 fail: AtomicReadOrd,
285 ) -> InterpResult<'tcx> {
286 self.atomic_compare_exchange_impl(args, dest, success, fail, false)
287 }
288
289 fn atomic_compare_exchange_weak(
290 &mut self,
291 args: &[OpTy<'tcx>],
292 dest: &MPlaceTy<'tcx>,
293 success: AtomicRwOrd,
294 fail: AtomicReadOrd,
295 ) -> InterpResult<'tcx> {
296 self.atomic_compare_exchange_impl(args, dest, success, fail, true)
297 }
298}