Skip to main content

rustc_const_eval/interpret/intrinsics/
atomic.rs

1use rustc_middle::mir::BinOp;
2use rustc_middle::ty::consts::ConstExt;
3use rustc_middle::{mir, ty};
4use rustc_span::{Symbol, span_bug, sym};
5use tracing::trace;
6
7use super::{
8    AtomicRmwOp, Immediate, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar, interp_ok,
9};
10
11impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
12    /// Returns `true` if emulation happened.
13    /// Here we implement the intrinsics that are common to all CTFE instances; individual machines can add their own
14    /// intrinsic handling.
15    pub fn eval_atomic_intrinsic(
16        &mut self,
17        intrinsic_name: Symbol,
18        generic_args: ty::GenericArgsRef<'tcx>,
19        args: &[OpTy<'tcx, M::Provenance>],
20        dest: &PlaceTy<'tcx, M::Provenance>,
21        ret: Option<mir::BasicBlock>,
22    ) -> InterpResult<'tcx, bool> {
23        let get_ord_at = |i: usize| {
24            let ordering = generic_args.const_at(i).to_value();
25            ordering.to_branch()[0].to_value().to_leaf().to_atomic_ordering()
26        };
27
28        match intrinsic_name {
29            sym::atomic_load => {
30                let ord = get_ord_at(1);
31                let _volatile = generic_args.const_at(2).to_value(); // makes no difference for us
32                let [ptr] = args else { ::rustc_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("invalid `atomic_load` call"), Location::caller())span_bug!(self.cur_span(), "invalid `atomic_load` call") };
33
34                let place = self.deref_pointer(ptr)?;
35                let val = M::atomic_load(self, &place, ord)?;
36                self.write_scalar(val, dest)?;
37            }
38            sym::atomic_store => {
39                let ord = get_ord_at(1);
40                let _volatile = generic_args.const_at(2).to_value(); // makes no difference for us
41                let [ptr, val] = args else {
42                    ::rustc_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("invalid `atomic_store` call"), Location::caller())span_bug!(self.cur_span(), "invalid `atomic_store` call")
43                };
44
45                let place = self.deref_pointer(ptr)?;
46                let val = self.read_immediate(val)?;
47                M::atomic_store(self, &place, &val, ord)?;
48            }
49            sym::atomic_or
50            | sym::atomic_xor
51            | sym::atomic_and
52            | sym::atomic_nand
53            | sym::atomic_xadd
54            | sym::atomic_xsub
55            | sym::atomic_min
56            | sym::atomic_umin
57            | sym::atomic_max
58            | sym::atomic_umax
59            | sym::atomic_xchg => {
60                let num_ty_generics = match intrinsic_name {
61                    sym::atomic_min
62                    | sym::atomic_umin
63                    | sym::atomic_max
64                    | sym::atomic_umax
65                    | sym::atomic_xchg => 1,
66                    _ => 2,
67                };
68                let ord = get_ord_at(num_ty_generics);
69                let [ptr, operand] = args else {
70                    ::rustc_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("invalid `{0}` call", intrinsic_name), Location::caller())span_bug!(self.cur_span(), "invalid `{intrinsic_name}` call")
71                };
72
73                let place = self.deref_pointer(ptr)?;
74                let operand = self.read_immediate(operand)?;
75
76                let op = match intrinsic_name {
77                    sym::atomic_or => AtomicRmwOp::MirOp { op: BinOp::BitOr, neg: false },
78                    sym::atomic_xor => AtomicRmwOp::MirOp { op: BinOp::BitXor, neg: false },
79                    sym::atomic_and => AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: false },
80                    sym::atomic_nand => AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: true },
81                    sym::atomic_xadd => AtomicRmwOp::MirOp { op: BinOp::Add, neg: false },
82                    sym::atomic_xsub => AtomicRmwOp::MirOp { op: BinOp::Sub, neg: false },
83                    sym::atomic_min => AtomicRmwOp::Min,
84                    sym::atomic_umin => AtomicRmwOp::Min,
85                    sym::atomic_max => AtomicRmwOp::Max,
86                    sym::atomic_umax => AtomicRmwOp::Max,
87                    sym::atomic_xchg => AtomicRmwOp::Swap,
88                    _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
89                };
90
91                let res = M::atomic_rmw(self, &place, op, &operand, ord)?;
92                self.write_scalar(res, dest)?;
93            }
94            sym::atomic_cxchg | sym::atomic_cxchgweak => {
95                let success_ord = get_ord_at(1);
96                let failure_ord = get_ord_at(2);
97                let [ptr, expected_old, new] = args else {
98                    ::rustc_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("invalid `{0}` call", intrinsic_name), Location::caller())span_bug!(self.cur_span(), "invalid `{intrinsic_name}` call")
99                };
100
101                let place = self.deref_pointer(ptr)?;
102                let expected_old = self.read_immediate(expected_old)?;
103                let new = self.read_immediate(new)?;
104
105                let (actual_old, success) = M::atomic_compare_exchange(
106                    self,
107                    &place,
108                    &expected_old,
109                    &new,
110                    /* can_fail_spuriously */ intrinsic_name == sym::atomic_cxchgweak,
111                    success_ord,
112                    failure_ord,
113                )?;
114                let res = Immediate::ScalarPair(actual_old, Scalar::from_bool(success));
115                self.write_immediate(res, dest)?;
116            }
117            sym::atomic_fence | sym::atomic_singlethreadfence => {
118                let ord = get_ord_at(0);
119                let [] = args else {
120                    ::rustc_span::macros::bug_impl(Some(self.cur_span()),
    format_args!("invalid `{0}` call", intrinsic_name), Location::caller())span_bug!(self.cur_span(), "invalid `{intrinsic_name}` call")
121                };
122
123                M::atomic_fence(self, ord, intrinsic_name == sym::atomic_singlethreadfence)?;
124            }
125
126            // Unsupported intrinsic: skip the return_to_block below.
127            _ => return interp_ok(false),
128        }
129
130        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/f7575a9da8e4a4fca3b5668d5a2ea7476db44b3f/compiler/rustc_const_eval/src/interpret/intrinsics/atomic.rs:130",
                        "rustc_const_eval::interpret::intrinsics::atomic",
                        ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/f7575a9da8e4a4fca3b5668d5a2ea7476db44b3f/compiler/rustc_const_eval/src/interpret/intrinsics/atomic.rs"),
                        ::tracing_core::__macro_support::Option::Some(130u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_const_eval::interpret::intrinsics::atomic"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("{0:?}",
                                                    self.dump_place(&dest.clone().into())) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};trace!("{:?}", self.dump_place(&dest.clone().into()));
131        self.return_to_block(ret)?;
132        interp_ok(true)
133    }
134}