From: Yap Zhi Heng <[email protected]>

gcc/rust/ChangeLog:

        * util/rust-intrinsic-values.h (Intrinsics): Add new entries for atomic 
compare-
        exchange function names.
        * backend/rust-intrinsic-handlers.h (inner::atomic_compare_exchange): 
New function
        for compiling atomic commpare-exchange intrinsics.
        (atomic_compare_exchange): New function to handle atomic_cxchg and its 
variants.
        * backend/rust-intrinsic-handlers.cc (inner::atomic_compare_exchange): 
Implement.
        (atomic_compare_exchange): Implement.
        * backend/rust-compile-intrinsic.cc (generic_intrinsics): Add entries 
for the new atomic
        compare-exchange intrinsics.
        * typecheck/rust-hir-type-check-intrinsic.cc (intrinsic_rules): 
Likewise, but for type
        checking.

        Assisted-by: GPT-6.1 Sol (for testcases)

Signed-off-by: Yap Zhi Heng <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/5acde068bc7c04a978f5582e85fee69101bbf096

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4993

 gcc/rust/backend/rust-compile-intrinsic.cc    |  30 +++
 gcc/rust/backend/rust-intrinsic-handlers.cc   |  73 ++++++
 gcc/rust/backend/rust-intrinsic-handlers.h    |   4 +
 .../rust-hir-type-check-intrinsic.cc          |  41 +++
 gcc/rust/util/rust-intrinsic-values.h         |  24 ++
 .../torture/atomic_compare_exchange.rs        | 241 ++++++++++++++++++
 6 files changed, 413 insertions(+)
 create mode 100644 
gcc/testsuite/rust/execute/torture/atomic_compare_exchange.rs

diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc 
b/gcc/rust/backend/rust-compile-intrinsic.cc
index dab7f67d0..151bda26f 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -90,6 +90,36 @@ static const std::map<std::string, Intrinsic::CompileFn> 
intrinsics = {
   {IValue::ATOMIC_XOR_REL, handlers::atomic_xor (__ATOMIC_RELEASE)},
   {IValue::ATOMIC_XOR_ACQREL, handlers::atomic_xor (__ATOMIC_ACQ_REL)},
   {IValue::ATOMIC_XOR_RELAXED, handlers::atomic_xor (__ATOMIC_RELAXED)},
+  {IValue::ATOMIC_CXCHG,
+   handlers::atomic_compare_exchange (false, __ATOMIC_SEQ_CST,
+                                     __ATOMIC_SEQ_CST)},
+  {IValue::ATOMIC_CXCHG_ACQ,
+   handlers::atomic_compare_exchange (false, __ATOMIC_ACQUIRE,
+                                     __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_CXCHG_REL,
+   handlers::atomic_compare_exchange (false, __ATOMIC_RELEASE,
+                                     __ATOMIC_RELAXED)},
+  {IValue::ATOMIC_CXCHG_ACQREL,
+   handlers::atomic_compare_exchange (false, __ATOMIC_ACQ_REL,
+                                     __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_CXCHG_RELAXED,
+   handlers::atomic_compare_exchange (false, __ATOMIC_RELAXED,
+                                     __ATOMIC_RELAXED)},
+  {IValue::ATOMIC_CXCHGWEAK,
+   handlers::atomic_compare_exchange (true, __ATOMIC_SEQ_CST,
+                                     __ATOMIC_SEQ_CST)},
+  {IValue::ATOMIC_CXCHGWEAK_ACQ,
+   handlers::atomic_compare_exchange (true, __ATOMIC_ACQUIRE,
+                                     __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_CXCHGWEAK_REL,
+   handlers::atomic_compare_exchange (true, __ATOMIC_RELEASE,
+                                     __ATOMIC_RELAXED)},
+  {IValue::ATOMIC_CXCHGWEAK_ACQREL,
+   handlers::atomic_compare_exchange (true, __ATOMIC_ACQ_REL,
+                                     __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_CXCHGWEAK_RELAXED,
+   handlers::atomic_compare_exchange (true, __ATOMIC_RELAXED,
+                                     __ATOMIC_RELAXED)},
   {IValue::UNCHECKED_ADD, handlers::unchecked_op (PLUS_EXPR)},
   {IValue::UNCHECKED_SUB, handlers::unchecked_op (MINUS_EXPR)},
   {IValue::UNCHECKED_MUL, handlers::unchecked_op (MULT_EXPR)},
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.cc 
b/gcc/rust/backend/rust-intrinsic-handlers.cc
index b15e4a539..1beb7a2e9 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.cc
+++ b/gcc/rust/backend/rust-intrinsic-handlers.cc
@@ -641,6 +641,71 @@ atomic_binary_op (IntrinsicCtx &ctx, const std::string 
&op_name, int ordering)
   ctx.add_statement (return_statement);
 }
 
+// Compiles atomic compare exchange with signature (*mut T, T, T) -> (T, bool).
+void
+atomic_compare_exchange (IntrinsicCtx &ctx, bool weak, int success_ordering,
+                        int failure_ordering)
+{
+  // Most intrinsic functions are pure but not the atomic ones
+  TREE_READONLY (ctx.fn) = 0;
+  TREE_SIDE_EFFECTS (ctx.fn) = 1;
+
+  // The intrinsic block is already open, so emit the temporary declarations
+  // directly into it.
+  tree tmp_stmt = NULL_TREE;
+  Bvariable *expected_var
+    = Backend::temporary_variable (ctx.fn, NULL_TREE, ctx.param_types[1],
+                                  NULL_TREE, true, UNDEF_LOCATION, &tmp_stmt);
+  ctx.add_statement (tmp_stmt);
+  Bvariable *success_var
+    = Backend::temporary_variable (ctx.fn, NULL_TREE, boolean_type_node,
+                                  NULL_TREE, false, UNDEF_LOCATION, &tmp_stmt);
+  ctx.add_statement (tmp_stmt);
+
+  auto dst = Backend::var_expression (ctx.param_vars[0], UNDEF_LOCATION);
+  TREE_READONLY (dst) = 0;
+
+  auto expected = expected_var->get_tree (UNDEF_LOCATION);
+  auto success = success_var->get_tree (UNDEF_LOCATION);
+
+  auto old_value = Backend::var_expression (ctx.param_vars[1], UNDEF_LOCATION);
+  auto new_value = Backend::var_expression (ctx.param_vars[2], UNDEF_LOCATION);
+
+  auto builtin_decl = resolve_atomic_builtin (ctx, "atomic_compare_exchange");
+
+  if (!builtin_decl || builtin_decl == error_mark_node)
+    return;
+
+  ctx.add_statement (
+    Backend::assignment_statement (expected, old_value, UNDEF_LOCATION));
+
+  auto expected_addr = build_fold_addr_expr_loc (UNDEF_LOCATION, expected);
+  auto builtin_addr = build_fold_addr_expr_loc (UNDEF_LOCATION, builtin_decl);
+  auto weak_flag = build_int_cst (boolean_type_node, weak);
+  auto success_memorder = make_unsigned_long_tree (success_ordering);
+  auto failure_memorder = make_unsigned_long_tree (failure_ordering);
+
+  auto exchange_call
+    = Backend::call_expression (builtin_addr,
+                               {dst, expected_addr, new_value, weak_flag,
+                                success_memorder, failure_memorder},
+                               nullptr, UNDEF_LOCATION);
+  TREE_READONLY (exchange_call) = 0;
+  TREE_SIDE_EFFECTS (exchange_call) = 1;
+
+  auto assignment_statement
+    = Backend::assignment_statement (success, exchange_call, UNDEF_LOCATION);
+  ctx.add_statement (assignment_statement);
+
+  auto tuple_type = TREE_TYPE (DECL_RESULT (ctx.fn));
+  auto result
+    = Backend::constructor_expression (tuple_type, false, {expected, success},
+                                      -1, UNDEF_LOCATION);
+  auto return_statement
+    = Backend::return_statement (ctx.fn, result, UNDEF_LOCATION);
+  ctx.add_statement (return_statement);
+}
+
 // Shared inner implementation for ctlz and ctlz_nonzero.
 //
 // nonzero=false → ctlz: ctlz(0) is well-defined in Rust and must return
@@ -1121,6 +1186,14 @@ atomic_xor (int ordering)
     return inner::atomic_binary_op (ctx, "fetch_xor", ordering);
   };
 }
+Intrinsic::CompileFn
+atomic_compare_exchange (bool weak, int success_ordering, int failure_ordering)
+{
+  return [weak, success_ordering, failure_ordering] (IntrinsicCtx &ctx) {
+    return inner::atomic_compare_exchange (ctx, weak, success_ordering,
+                                          failure_ordering);
+  };
+}
 
 Intrinsic::CompileFn
 unchecked_op (tree_code op)
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.h 
b/gcc/rust/backend/rust-intrinsic-handlers.h
index 3bd1d12db..a1f3aa03f 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.h
+++ b/gcc/rust/backend/rust-intrinsic-handlers.h
@@ -137,6 +137,8 @@ void atomic_store (IntrinsicCtx &ctx, int ordering);
 void atomic_load (IntrinsicCtx &ctx, int ordering);
 void atomic_binary_op (IntrinsicCtx &ctx, const std::string &op_name,
                       int ordering);
+void atomic_compare_exchange (IntrinsicCtx &ctx, bool weak,
+                             int success_ordering, int failure_ordering);
 inline void copy (IntrinsicCtx &ctx, bool overlaps);
 inline void expect (IntrinsicCtx &ctx, bool likely);
 void try_handler (IntrinsicCtx &ctx, bool is_new_api);
@@ -199,6 +201,8 @@ Intrinsic::CompileFn atomic_and (int ordering);
 Intrinsic::CompileFn atomic_nand (int ordering);
 Intrinsic::CompileFn atomic_or (int ordering);
 Intrinsic::CompileFn atomic_xor (int ordering);
+Intrinsic::CompileFn atomic_compare_exchange (bool weak, int success_ordering,
+                                             int failure_ordering);
 
 Intrinsic::CompileFn unchecked_op (tree_code op);
 
diff --git a/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc 
b/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc
index 6e94d6f5f..eccbe882a 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-intrinsic.cc
@@ -277,6 +277,47 @@ const std::unordered_map<std::string, IntrinsicRules>
     // pub unsafe fn atomic_xor_relaxed<T: Copy>(dst: *mut T, val: T) -> T;
     {IValue::ATOMIC_XOR_RELAXED,
      {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    // fn atomic_cxchg[weak]*<T>(dst: *mut T, old: T, new: T) -> (T, bool);
+    {IValue::ATOMIC_CXCHG,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHG_ACQ,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHG_REL,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHG_ACQREL,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHG_RELAXED,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHGWEAK,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHGWEAK_ACQ,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHGWEAK_REL,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHGWEAK_ACQREL,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
+    {IValue::ATOMIC_CXCHGWEAK_RELAXED,
+     {1,
+      {IRT::MutPtrFirstGeneric, IRT::FirstGeneric, IRT::FirstGeneric},
+      IRT::TupleFirstGenericAndBool}},
     // pub fn unchecked_add<T: Copy>(x: T, y: T) -> T;
     {IValue::UNCHECKED_ADD,
      {1, {IRT::FirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
diff --git a/gcc/rust/util/rust-intrinsic-values.h 
b/gcc/rust/util/rust-intrinsic-values.h
index e184859a0..06d3469e5 100644
--- a/gcc/rust/util/rust-intrinsic-values.h
+++ b/gcc/rust/util/rust-intrinsic-values.h
@@ -151,6 +151,30 @@ public:
   static constexpr auto &ATOMIC_XOR_ACQREL = "atomic_xor_acqrel";
   static constexpr auto &ATOMIC_XOR_RELAXED = "atomic_xor_relaxed";
 
+  static constexpr auto &ATOMIC_CXCHG = "atomic_cxchg";
+  static constexpr auto &ATOMIC_CXCHG_ACQ = "atomic_cxchg_acq";
+  static constexpr auto &ATOMIC_CXCHG_REL = "atomic_cxchg_rel";
+  static constexpr auto &ATOMIC_CXCHG_ACQREL = "atomic_cxchg_acqrel";
+  static constexpr auto &ATOMIC_CXCHG_RELAXED = "atomic_cxchg_relaxed";
+  static constexpr auto &ATOMIC_CXCHG_FAILRELAXED = "atomic_cxchg_failrelaxed";
+  static constexpr auto &ATOMIC_CXCHG_FAILACQ = "atomic_cxchg_failacq";
+  static constexpr auto &ATOMIC_CXCHG_ACQ_FAILRELAXED
+    = "atomic_cxchg_acq_failrelaxed";
+  static constexpr auto &ATOMIC_CXCHG_ACQREL_FAILRELAXED
+    = "atomic_cxchg_acqrel_failrelaxed";
+  static constexpr auto &ATOMIC_CXCHGWEAK = "atomic_cxchgweak";
+  static constexpr auto &ATOMIC_CXCHGWEAK_ACQ = "atomic_cxchgweak_acq";
+  static constexpr auto &ATOMIC_CXCHGWEAK_REL = "atomic_cxchgweak_rel";
+  static constexpr auto &ATOMIC_CXCHGWEAK_ACQREL = "atomic_cxchgweak_acqrel";
+  static constexpr auto &ATOMIC_CXCHGWEAK_RELAXED = "atomic_cxchgweak_relaxed";
+  static constexpr auto &ATOMIC_CXCHGWEAK_FAILRELAXED
+    = "atomic_cxchgweak_failrelaxed";
+  static constexpr auto &ATOMIC_CXCHGWEAK_FAILACQ = "atomic_cxchgweak_failacq";
+  static constexpr auto &ATOMIC_CXCHGWEAK_ACQ_FAILRELAXED
+    = "atomic_cxchgweak_acq_failrelaxed";
+  static constexpr auto &ATOMIC_CXCHGWEAK_ACQREL_FAILRELAXED
+    = "atomic_cxchgweak_acqrel_failrelaxed";
+
   static constexpr auto &UNCHECKED_ADD = "unchecked_add";
   static constexpr auto &UNCHECKED_SUB = "unchecked_sub";
   static constexpr auto &UNCHECKED_MUL = "unchecked_mul";
diff --git a/gcc/testsuite/rust/execute/torture/atomic_compare_exchange.rs 
b/gcc/testsuite/rust/execute/torture/atomic_compare_exchange.rs
new file mode 100644
index 000000000..2fa8e6098
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/atomic_compare_exchange.rs
@@ -0,0 +1,241 @@
+#![feature(no_core)]
+#![no_core]
+
+#![feature(intrinsics)]
+
+#![feature(lang_items)]
+#[lang = "sized"]
+pub trait Sized {}
+
+#[lang = "clone"]
+pub trait Clone: Sized {
+    fn clone(&self) -> Self;
+
+    fn clone_from(&mut self, source: &Self) {
+        *self = source.clone()
+    }
+}
+
+mod impls {
+    use super::Clone;
+
+    macro_rules! impl_clone {
+        ($($t:ty)*) => {
+            $(
+                impl Clone for $t {
+                    fn clone(&self) -> Self {
+                        *self
+                    }
+                }
+            )*
+        }
+    }
+
+    impl_clone! {
+        usize u8 u16 u32 u64 // u128
+        isize i8 i16 i32 i64 // i128
+        f32 f64
+        bool char
+    }
+}
+
+#[lang = "copy"]
+pub trait Copy: Clone {
+    // Empty.
+}
+
+mod copy_impls {
+    use super::Copy;
+
+    macro_rules! impl_copy {
+        ($($t:ty)*) => {
+            $(
+                impl Copy for $t {}
+            )*
+        }
+    }
+
+    impl_copy! {
+        usize u8 u16 u32 u64 // u128
+        isize i8 i16 i32 i64 // i128
+        f32 f64
+        bool char
+    }
+}
+
+extern "rust-intrinsic" {
+    pub fn atomic_cxchg<T: Copy>(dst: *mut T, old: T, new: T) -> (T, bool);
+    pub fn atomic_cxchg_acq<T: Copy>(dst: *mut T, old: T, new: T) -> (T, bool);
+    pub fn atomic_cxchg_rel<T: Copy>(dst: *mut T, old: T, new: T) -> (T, bool);
+    pub fn atomic_cxchg_acqrel<T: Copy>(dst: *mut T, old: T, new: T) -> (T, 
bool);
+    pub fn atomic_cxchg_relaxed<T: Copy>(dst: *mut T, old: T, new: T) -> (T, 
bool);
+    pub fn atomic_cxchgweak<T: Copy>(dst: *mut T, old: T, new: T) -> (T, bool);
+    pub fn atomic_cxchgweak_acq<T: Copy>(dst: *mut T, old: T, new: T) -> (T, 
bool);
+    pub fn atomic_cxchgweak_rel<T: Copy>(dst: *mut T, old: T, new: T) -> (T, 
bool);
+    pub fn atomic_cxchgweak_acqrel<T: Copy>(dst: *mut T, old: T, new: T) -> 
(T, bool);
+    pub fn atomic_cxchgweak_relaxed<T: Copy>(dst: *mut T, old: T, new: T) -> 
(T, bool);
+}
+
+fn main() -> u32 {
+    unsafe {
+        let mut src = -7i8;
+
+        let result = atomic_cxchg(&mut src, -7, 9);
+        if result.0 != -7 || !result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchg(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchgweak(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        // Retry weak compare-exchange when it fails spuriously.
+        loop {
+            let result = atomic_cxchgweak(&mut src, 9, -7);
+            if result.0 != 9 {
+                return 1;
+            }
+            if result.1 {
+                if src != -7 {
+                    return 1;
+                }
+                break;
+            }
+            if src != 9 {
+                return 1;
+            }
+        }
+
+        let result = atomic_cxchg_acq(&mut src, -7, 9);
+        if result.0 != -7 || !result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchg_acq(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchgweak_acq(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        loop {
+            let result = atomic_cxchgweak_acq(&mut src, 9, -7);
+            if result.0 != 9 {
+                return 1;
+            }
+            if result.1 {
+                if src != -7 {
+                    return 1;
+                }
+                break;
+            }
+            if src != 9 {
+                return 1;
+            }
+        }
+
+        let result = atomic_cxchg_rel(&mut src, -7, 9);
+        if result.0 != -7 || !result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchg_rel(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchgweak_rel(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        loop {
+            let result = atomic_cxchgweak_rel(&mut src, 9, -7);
+            if result.0 != 9 {
+                return 1;
+            }
+            if result.1 {
+                if src != -7 {
+                    return 1;
+                }
+                break;
+            }
+            if src != 9 {
+                return 1;
+            }
+        }
+
+        let result = atomic_cxchg_acqrel(&mut src, -7, 9);
+        if result.0 != -7 || !result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchg_acqrel(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchgweak_acqrel(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        loop {
+            let result = atomic_cxchgweak_acqrel(&mut src, 9, -7);
+            if result.0 != 9 {
+                return 1;
+            }
+            if result.1 {
+                if src != -7 {
+                    return 1;
+                }
+                break;
+            }
+            if src != 9 {
+                return 1;
+            }
+        }
+
+        let result = atomic_cxchg_relaxed(&mut src, -7, 9);
+        if result.0 != -7 || !result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchg_relaxed(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        let result = atomic_cxchgweak_relaxed(&mut src, 3, 11);
+        if result.0 != 9 || result.1 || src != 9 {
+            return 1;
+        }
+
+        loop {
+            let result = atomic_cxchgweak_relaxed(&mut src, 9, -7);
+            if result.0 != 9 {
+                return 1;
+            }
+            if result.1 {
+                if src != -7 {
+                    return 1;
+                }
+                break;
+            }
+            if src != 9 {
+                return 1;
+            }
+        }
+    }
+
+    0
+}
-- 
2.56.0

Reply via email to