From: Elle Rhumsaa <[email protected]>

Adds the `atomic_exchange` intrinsics for every memory ordering.

gcc/
        * rust/backend/rust-compile-intrinsic.cc: compile impl
        * rust/backend/rust-intrinsic-handlers.h: handler decls
        * rust/backend/rust-intrinsic-handlers.cc: handler impls
        * rust/util/rust-intrinsic-values.h: value defs

gcc/teststuite/
        * rust/execute/torture/atomic_exchange.rs: basic tests
---
 gcc/rust/backend/rust-compile-intrinsic.cc    |  10 ++
 gcc/rust/backend/rust-compile-intrinsic.h     |   1 +
 gcc/rust/backend/rust-intrinsic-handlers.cc   |  79 ++++++++++++
 gcc/rust/backend/rust-intrinsic-handlers.h    |   4 +
 gcc/rust/util/rust-intrinsic-values.h         |   6 +
 .../rust/execute/torture/atomic_exchange.rs   | 116 ++++++++++++++++++
 6 files changed, 216 insertions(+)
 create mode 100644 gcc/testsuite/rust/execute/torture/atomic_exchange.rs

diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc 
b/gcc/rust/backend/rust-compile-intrinsic.cc
index 1456f88df985..a733f83e63d0 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -52,6 +52,16 @@ static const std::map<std::string, handlers::HandlerBuilder> 
generic_intrinsics
      {IValue::ATOMIC_LOAD_ACQUIRE, handlers::atomic_load (__ATOMIC_ACQUIRE)},
      {IValue::ATOMIC_LOAD_RELAXED, handlers::atomic_load (__ATOMIC_RELAXED)},
      {IValue::ATOMIC_LOAD_UNORDERED, handlers::atomic_load (__ATOMIC_RELAXED)},
+     {IValue::ATOMIC_EXCHANGE_SEQCST,
+      handlers::atomic_exchange (MEMMODEL_SEQ_CST)},
+     {IValue::ATOMIC_EXCHANGE_ACQUIRE,
+      handlers::atomic_exchange (MEMMODEL_ACQUIRE)},
+     {IValue::ATOMIC_EXCHANGE_RELEASE,
+      handlers::atomic_exchange (MEMMODEL_RELEASE)},
+     {IValue::ATOMIC_EXCHANGE_ACQREL,
+      handlers::atomic_exchange (MEMMODEL_ACQ_REL)},
+     {IValue::ATOMIC_EXCHANGE_RELAXED,
+      handlers::atomic_exchange (MEMMODEL_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-compile-intrinsic.h 
b/gcc/rust/backend/rust-compile-intrinsic.h
index dceb0864fd49..7b5e7e1cb9ff 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.h
+++ b/gcc/rust/backend/rust-compile-intrinsic.h
@@ -19,6 +19,7 @@
 
 #include "rust-compile-context.h"
 #include "langhooks.h"
+#include "memmodel.h"
 
 namespace Rust {
 namespace Compile {
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.cc 
b/gcc/rust/backend/rust-intrinsic-handlers.cc
index dab2ca861c77..d9aa95b79a8b 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.cc
+++ b/gcc/rust/backend/rust-intrinsic-handlers.cc
@@ -45,6 +45,12 @@ make_unsigned_long_tree (unsigned long value)
   return build_int_cst (integer_type_node, value);
 }
 
+static tree
+make_memmodel_tree (memmodel value)
+{
+  return build_int_cst (integer_type_node, value);
+}
+
 static bool
 is_basic_integer_type (TyTy::BaseType *type)
 {
@@ -749,6 +755,71 @@ atomic_load (Context *ctx, TyTy::FnType *fntype, int 
ordering)
   return fndecl;
 }
 
+tree
+atomic_exchange (Context *ctx, TyTy::FnType *fntype, memmodel model)
+{
+  rust_assert (fntype->get_params ().size () == 2);
+  rust_assert (fntype->get_num_substitutions () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // Most intrinsic functions are pure but not the atomic ones
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  std::vector<tree> types;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
+
+  auto ok = Backend::function_set_parameters (fndecl, param_vars);
+  rust_assert (ok);
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  TREE_READONLY (dst) = 0;
+
+  auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
+  auto memorder = make_memmodel_tree (model);
+
+  auto monomorphized_type
+    = fntype->get_substs ()[0].get_param_ty ()->resolve ();
+
+  auto builtin_name
+    = build_atomic_builtin_name ("atomic_exchange_", fntype->get_locus (),
+                                monomorphized_type);
+  if (builtin_name.empty ())
+    return error_mark_node;
+
+  tree atomic_exchange_raw = nullptr;
+  BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
+                                                &atomic_exchange_raw);
+  rust_assert (atomic_exchange_raw);
+
+  auto atomic_exchange
+    = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_exchange_raw);
+
+  auto exchange_call
+    = Backend::call_expression (atomic_exchange, {dst, value, memorder},
+                               nullptr, UNDEF_LOCATION);
+
+  auto return_statement
+    = Backend::return_statement (fndecl, exchange_call, UNDEF_LOCATION);
+
+  TREE_READONLY (exchange_call) = 0;
+  TREE_SIDE_EFFECTS (exchange_call) = 1;
+
+  ctx->add_statement (return_statement);
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
 // Shared inner implementation for ctlz and ctlz_nonzero.
 //
 // nonzero=false → ctlz: ctlz(0) is well-defined in Rust and must return
@@ -1070,6 +1141,14 @@ atomic_load (int ordering)
   };
 }
 
+HandlerBuilder
+atomic_exchange (memmodel model)
+{
+  return [model] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::atomic_exchange (ctx, fntype, model);
+  };
+}
+
 const HandlerBuilder
 unchecked_op (tree_code op)
 {
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.h 
b/gcc/rust/backend/rust-intrinsic-handlers.h
index f4c36b3fe62d..96c62f0a1cae 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.h
+++ b/gcc/rust/backend/rust-intrinsic-handlers.h
@@ -20,6 +20,7 @@
 #define RUST_INTRINSIC_HANDLERS_H
 
 #include "rust-compile-context.h"
+#include "memmodel.h"
 
 namespace Rust {
 namespace Compile {
@@ -37,6 +38,7 @@ tree wrapping_op (Context *ctx, TyTy::FnType *fntype, 
tree_code op);
 
 tree atomic_store (Context *ctx, TyTy::FnType *fntype, int ordering);
 tree atomic_load (Context *ctx, TyTy::FnType *fntype, int ordering);
+tree atomic_exchange (Context *ctx, TyTy::FnType *fntype, memmodel model);
 inline tree copy (Context *ctx, TyTy::FnType *fntype, bool overlaps);
 inline tree expect (Context *ctx, TyTy::FnType *fntype, bool likely);
 tree try_handler (Context *ctx, TyTy::FnType *fntype, bool is_new_api);
@@ -79,6 +81,8 @@ HandlerBuilder atomic_store (int ordering);
 
 HandlerBuilder atomic_load (int ordering);
 
+HandlerBuilder atomic_exchange (memmodel model);
+
 const HandlerBuilder unchecked_op (tree_code op);
 
 const HandlerBuilder copy (bool overlaps);
diff --git a/gcc/rust/util/rust-intrinsic-values.h 
b/gcc/rust/util/rust-intrinsic-values.h
index c868c851d237..45f8286874c7 100644
--- a/gcc/rust/util/rust-intrinsic-values.h
+++ b/gcc/rust/util/rust-intrinsic-values.h
@@ -46,6 +46,12 @@ public:
   static constexpr auto &ATOMIC_LOAD_RELAXED = "atomic_load_relaxed";
   static constexpr auto &ATOMIC_LOAD_UNORDERED = "atomic_load_unordered";
 
+  static constexpr auto &ATOMIC_EXCHANGE_SEQCST = "atomic_xchg";
+  static constexpr auto &ATOMIC_EXCHANGE_RELEASE = "atomic_xchg_rel";
+  static constexpr auto &ATOMIC_EXCHANGE_ACQUIRE = "atomic_xchg_acq";
+  static constexpr auto &ATOMIC_EXCHANGE_ACQREL = "atomic_xchg_acqrel";
+  static constexpr auto &ATOMIC_EXCHANGE_RELAXED = "atomic_xchg_relaxed";
+
   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_exchange.rs 
b/gcc/testsuite/rust/execute/torture/atomic_exchange.rs
new file mode 100644
index 000000000000..b645ef17fe74
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/atomic_exchange.rs
@@ -0,0 +1,116 @@
+#![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_xchg<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_xchg_acq<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_xchg_rel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_xchg_acqrel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_xchg_relaxed<T: Copy>(dst: *mut T, val: T) -> T;
+}
+
+fn main() -> u32 {
+    let mut dst = 15u32;
+    let init = dst;
+    let one;
+    let two;
+    let three;
+    let four;
+    let five;
+
+    unsafe {
+        let mut ret = atomic_xchg(&mut dst, 1);
+        one = dst;
+
+        if ret != init {
+            return ret;
+        }
+
+        ret = atomic_xchg_acq(&mut dst, 2);
+        two = dst;
+
+        if ret != one {
+            return ret;
+        }
+
+        ret = atomic_xchg_rel(&mut dst, 3);
+        three = dst;
+
+        if ret != two {
+            return ret;
+        }
+
+        ret = atomic_xchg_acqrel(&mut dst, 4);
+        four = dst;
+
+        if ret != three {
+            return ret;
+        }
+
+        ret = atomic_xchg_relaxed(&mut dst, 5);
+        five = dst;
+
+        if ret != four {
+            return ret;
+        }
+    }
+
+    (five + four + three + two + one) - init
+}
-- 
2.54.0

Reply via email to