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    |   8 ++
 gcc/rust/backend/rust-intrinsic-handlers.cc   |  73 ++++++++++++
 gcc/rust/backend/rust-intrinsic-handlers.h    |   3 +
 gcc/rust/util/rust-intrinsic-values.h         |   6 +
 .../rust/execute/torture/atomic_exchange.rs   | 112 ++++++++++++++++++
 5 files changed, 202 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..1342772ce4e6 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -52,6 +52,14 @@ 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 (__ATOMIC_SEQ_CST)},
+     {IValue::ATOMIC_EXCHANGE_RELEASE,
+      handlers::atomic_exchange (__ATOMIC_RELEASE)},
+     {IValue::ATOMIC_EXCHANGE_RELAXED,
+      handlers::atomic_exchange (__ATOMIC_RELAXED)},
+     {IValue::ATOMIC_EXCHANGE_UNORDERED,
+      handlers::atomic_exchange (__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 8cbcb9ca4cef..f8e6bb8b977e 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.cc
+++ b/gcc/rust/backend/rust-intrinsic-handlers.cc
@@ -748,6 +748,71 @@ atomic_load (Context *ctx, TyTy::FnType *fntype, int 
ordering)
   return fndecl;
 }
 
+tree
+atomic_exchange (Context *ctx, TyTy::FnType *fntype, int ordering)
+{
+  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_unsigned_long_tree (ordering);
+
+  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
@@ -1069,6 +1134,14 @@ atomic_load (int ordering)
   };
 }
 
+HandlerBuilder
+atomic_exchange (int ordering)
+{
+  return [ordering] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::atomic_exchange (ctx, fntype, ordering);
+  };
+}
+
 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..ac26749dfdb9 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.h
+++ b/gcc/rust/backend/rust-intrinsic-handlers.h
@@ -37,6 +37,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, int ordering);
 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 +80,8 @@ HandlerBuilder atomic_store (int ordering);
 
 HandlerBuilder atomic_load (int ordering);
 
+HandlerBuilder atomic_exchange (int ordering);
+
 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..eaa51928bacf 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_exchange_seqcst";
+  static constexpr auto &ATOMIC_EXCHANGE_RELEASE = "atomic_exchange_release";
+  static constexpr auto &ATOMIC_EXCHANGE_RELAXED = "atomic_exchange_relaxed";
+  static constexpr auto &ATOMIC_EXCHANGE_UNORDERED
+    = "atomic_exchange_unordered";
+
   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..569504f6a30c
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/atomic_exchange.rs
@@ -0,0 +1,112 @@
+#![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_exchange_seqcst<T : Copy> (dst : *mut T, val : T) -> T;
+pub fn atomic_exchange_release<T : Copy> (dst : *mut T, val : T) -> T;
+pub fn atomic_exchange_relaxed<T : Copy> (dst : *mut T, val : T) -> T;
+pub fn atomic_exchange_unordered<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;
+
+  unsafe
+  {
+    let mut ret = atomic_exchange_seqcst (&mut dst, 1);
+    one = dst;
+
+    if ret
+      != init { return ret; }
+
+    ret = atomic_exchange_release (&mut dst, 2);
+    two = dst;
+
+    if ret
+      != one { return ret; }
+
+    ret = atomic_exchange_relaxed (&mut dst, 3);
+    three = dst;
+
+    if ret
+      != two { return ret; }
+
+    ret = atomic_exchange_unordered (&mut dst, 4);
+    four = dst;
+
+    if ret
+      != three { return ret; }
+  }
+
+  (four + three + two + one) - 10
+}
-- 
2.54.0

Reply via email to