From: Yap Zhi Heng <[email protected]>

Similar to rustc_codegen_gcc and libstdc++, we just generate a compare-exchange
loop for these functions as there is no equivalent implemented in gcc backend.

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

gcc/rust/ChangeLog:

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

Signed-off-by: Yap Zhi Heng <[email protected]>

Tested on x86_64-pc-linux-gnu.

---
 gcc/rust/backend/rust-compile-intrinsic.cc    |  60 +++++
 gcc/rust/backend/rust-intrinsic-handlers.cc   |  96 ++++++++
 gcc/rust/backend/rust-intrinsic-handlers.h    |  11 +
 .../rust-hir-type-check-intrinsic.cc          |  41 ++++
 gcc/rust/util/rust-intrinsic-values.h         |  21 ++
 gcc/testsuite/rust/compile/atomic_minmax.rs   |  78 ++++++
 .../rust/execute/torture/atomic_minmax.rs     | 226 ++++++++++++++++++
 7 files changed, 533 insertions(+)
 create mode 100644 gcc/testsuite/rust/compile/atomic_minmax.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/atomic_minmax.rs

diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc 
b/gcc/rust/backend/rust-compile-intrinsic.cc
index 151bda26f40..5ebcec6609f 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -120,6 +120,66 @@ static const std::map<std::string, Intrinsic::CompileFn> 
intrinsics = {
   {IValue::ATOMIC_CXCHGWEAK_RELAXED,
    handlers::atomic_compare_exchange (true, __ATOMIC_RELAXED,
                                      __ATOMIC_RELAXED)},
+  {IValue::ATOMIC_MAX,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMax,
+                           __ATOMIC_SEQ_CST)},
+  {IValue::ATOMIC_MAX_ACQ,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMax,
+                           __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_MAX_REL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMax,
+                           __ATOMIC_RELEASE)},
+  {IValue::ATOMIC_MAX_ACQREL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMax,
+                           __ATOMIC_ACQ_REL)},
+  {IValue::ATOMIC_MAX_RELAXED,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMax,
+                           __ATOMIC_RELAXED)},
+  {IValue::ATOMIC_UMAX,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMax,
+                           __ATOMIC_SEQ_CST)},
+  {IValue::ATOMIC_UMAX_ACQ,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMax,
+                           __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_UMAX_REL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMax,
+                           __ATOMIC_RELEASE)},
+  {IValue::ATOMIC_UMAX_ACQREL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMax,
+                           __ATOMIC_ACQ_REL)},
+  {IValue::ATOMIC_UMAX_RELAXED,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMax,
+                           __ATOMIC_RELAXED)},
+  {IValue::ATOMIC_MIN,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMin,
+                           __ATOMIC_SEQ_CST)},
+  {IValue::ATOMIC_MIN_ACQ,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMin,
+                           __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_MIN_REL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMin,
+                           __ATOMIC_RELEASE)},
+  {IValue::ATOMIC_MIN_ACQREL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMin,
+                           __ATOMIC_ACQ_REL)},
+  {IValue::ATOMIC_MIN_RELAXED,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::SignedMin,
+                           __ATOMIC_RELAXED)},
+  {IValue::ATOMIC_UMIN,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMin,
+                           __ATOMIC_SEQ_CST)},
+  {IValue::ATOMIC_UMIN_ACQ,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMin,
+                           __ATOMIC_ACQUIRE)},
+  {IValue::ATOMIC_UMIN_REL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMin,
+                           __ATOMIC_RELEASE)},
+  {IValue::ATOMIC_UMIN_ACQREL,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMin,
+                           __ATOMIC_ACQ_REL)},
+  {IValue::ATOMIC_UMIN_RELAXED,
+   handlers::atomic_minmax (handlers::AtomicMinMaxKind::UnsignedMin,
+                           __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 1beb7a2e97b..84eb4cbbb8a 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.cc
+++ b/gcc/rust/backend/rust-intrinsic-handlers.cc
@@ -706,6 +706,94 @@ atomic_compare_exchange (IntrinsicCtx &ctx, bool weak, int 
success_ordering,
   ctx.add_statement (return_statement);
 }
 
+// Compiles atomic min/max with signature (*mut T, T) -> T
+//
+// no gcc backend built-in equivalent, so we just do a compare-and-swap loop
+// similar to rustc_codegen_gcc & libstdc++.
+//
+// This is equivalent to the following code:
+//
+// T old_val = __atomic_load_N (dst, failure_order);
+// do
+//   {
+//     T desired = min_or_max (old_val, value);
+//     if (__atomic_compare_exchange_N (dst, &old_val, desired, true,
+//                                     order, failure_order))
+//       break;
+//   } while (true);
+// return old_val;
+void
+atomic_minmax (IntrinsicCtx &ctx, AtomicMinMaxKind kind, int ordering)
+{
+  // Most intrinsic functions are pure but not the atomic ones
+  TREE_READONLY (ctx.fn) = 0;
+  TREE_SIDE_EFFECTS (ctx.fn) = 1;
+
+  auto load_decl = resolve_atomic_builtin (ctx, "atomic_load");
+  if (!load_decl || load_decl == error_mark_node)
+    return;
+  auto exchange_decl = resolve_atomic_builtin (ctx, "atomic_compare_exchange");
+  if (!exchange_decl || exchange_decl == error_mark_node)
+    return;
+
+  auto dst = Backend::var_expression (ctx.param_vars[0], UNDEF_LOCATION);
+  TREE_READONLY (dst) = 0;
+  auto value = Backend::var_expression (ctx.param_vars[1], UNDEF_LOCATION);
+  auto value_type = ctx.param_types[1];
+
+  // release uses relaxed & acq_req uses acquire for loads on fail exchanges
+  int failure_ordering = ordering;
+  if (ordering == __ATOMIC_RELEASE)
+    failure_ordering = __ATOMIC_RELAXED;
+  else if (ordering == __ATOMIC_ACQ_REL)
+    failure_ordering = __ATOMIC_ACQUIRE;
+
+  auto failure_memorder = make_unsigned_long_tree (failure_ordering);
+  auto load_addr = build_fold_addr_expr_loc (UNDEF_LOCATION, load_decl);
+  auto load_call = Backend::call_expression (load_addr, {dst, 
failure_memorder},
+                                            nullptr, UNDEF_LOCATION);
+  TREE_READONLY (load_call) = 0;
+  TREE_SIDE_EFFECTS (load_call) = 1;
+
+  tree tmp_stmt = NULL_TREE;
+  Bvariable *expected_var
+    = Backend::temporary_variable (ctx.fn, NULL_TREE, value_type, load_call,
+                                  true, UNDEF_LOCATION, &tmp_stmt);
+  ctx.add_statement (tmp_stmt);
+  auto expected = expected_var->get_tree (UNDEF_LOCATION);
+
+  bool is_unsigned = kind == AtomicMinMaxKind::UnsignedMax
+                    || kind == AtomicMinMaxKind::UnsignedMin;
+  auto comparison_type = is_unsigned ? unsigned_type_for (value_type)
+                                    : signed_type_for (value_type);
+
+  // handle signedness irrespective of what the original type of the var is,
+  // in case users do things like using atomic_umin on signed types and expect
+  // it to work correctly
+  bool is_min = (kind == AtomicMinMaxKind::SignedMin
+                || kind == AtomicMinMaxKind::UnsignedMin);
+  auto desired = build2 (is_min ? MIN_EXPR : MAX_EXPR, comparison_type,
+                        fold_convert (comparison_type, expected),
+                        fold_convert (comparison_type, value));
+  desired = fold_convert (value_type, desired);
+
+  auto exchange_addr = build_fold_addr_expr_loc (UNDEF_LOCATION, 
exchange_decl);
+  auto expected_addr = build_fold_addr_expr_loc (UNDEF_LOCATION, expected);
+  auto exchange_call
+    = Backend::call_expression (exchange_addr,
+                               {dst, expected_addr, desired, boolean_true_node,
+                                make_unsigned_long_tree (ordering),
+                                failure_memorder},
+                               nullptr, UNDEF_LOCATION);
+  TREE_READONLY (exchange_call) = 0;
+  TREE_SIDE_EFFECTS (exchange_call) = 1;
+
+  auto exit = Backend::exit_expression (exchange_call, UNDEF_LOCATION);
+  ctx.add_statement (Backend::loop_expression (exit, UNDEF_LOCATION));
+  ctx.add_statement (
+    Backend::return_statement (ctx.fn, expected, UNDEF_LOCATION));
+}
+
 // Shared inner implementation for ctlz and ctlz_nonzero.
 //
 // nonzero=false → ctlz: ctlz(0) is well-defined in Rust and must return
@@ -1195,6 +1283,14 @@ atomic_compare_exchange (bool weak, int 
success_ordering, int failure_ordering)
   };
 }
 
+Intrinsic::CompileFn
+atomic_minmax (AtomicMinMaxKind kind, int ordering)
+{
+  return [kind, ordering] (IntrinsicCtx &ctx) {
+    return inner::atomic_minmax (ctx, kind, 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 a1f3aa03fd5..685612b2f12 100644
--- a/gcc/rust/backend/rust-intrinsic-handlers.h
+++ b/gcc/rust/backend/rust-intrinsic-handlers.h
@@ -130,6 +130,14 @@ enum class TyAssert
 
 namespace handlers {
 
+enum class AtomicMinMaxKind
+{
+  SignedMax,
+  SignedMin,
+  UnsignedMax,
+  UnsignedMin,
+};
+
 namespace inner {
 void wrapping_op (IntrinsicCtx &ctx, tree_code op);
 
@@ -139,6 +147,7 @@ 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);
+void atomic_minmax (IntrinsicCtx &ctx, AtomicMinMaxKind kind, int ordering);
 inline void copy (IntrinsicCtx &ctx, bool overlaps);
 inline void expect (IntrinsicCtx &ctx, bool likely);
 void try_handler (IntrinsicCtx &ctx, bool is_new_api);
@@ -203,6 +212,8 @@ 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 atomic_minmax (handlers::AtomicMinMaxKind kind,
+                                   int 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 eccbe882a92..8a632fae8e3 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_[u]min/max<T>(dst: *mut T, val: T) -> T;
+    {IValue::ATOMIC_MIN,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MIN_ACQ,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MIN_REL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MIN_ACQREL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MIN_RELAXED,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MAX,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MAX_ACQ,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MAX_REL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MAX_ACQREL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_MAX_RELAXED,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMIN,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMIN_ACQ,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMIN_REL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMIN_ACQREL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMIN_RELAXED,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMAX,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMAX_ACQ,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMAX_REL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMAX_ACQREL,
+     {1, {IRT::MutPtrFirstGeneric, IRT::FirstGeneric}, IRT::FirstGeneric}},
+    {IValue::ATOMIC_UMAX_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,
diff --git a/gcc/rust/util/rust-intrinsic-values.h 
b/gcc/rust/util/rust-intrinsic-values.h
index 06d3469e58b..37f61f8cb3c 100644
--- a/gcc/rust/util/rust-intrinsic-values.h
+++ b/gcc/rust/util/rust-intrinsic-values.h
@@ -151,6 +151,27 @@ public:
   static constexpr auto &ATOMIC_XOR_ACQREL = "atomic_xor_acqrel";
   static constexpr auto &ATOMIC_XOR_RELAXED = "atomic_xor_relaxed";
 
+  static constexpr auto &ATOMIC_MIN = "atomic_min";
+  static constexpr auto &ATOMIC_MIN_ACQ = "atomic_min_acq";
+  static constexpr auto &ATOMIC_MIN_REL = "atomic_min_rel";
+  static constexpr auto &ATOMIC_MIN_ACQREL = "atomic_min_acqrel";
+  static constexpr auto &ATOMIC_MIN_RELAXED = "atomic_min_relaxed";
+  static constexpr auto &ATOMIC_MAX = "atomic_max";
+  static constexpr auto &ATOMIC_MAX_ACQ = "atomic_max_acq";
+  static constexpr auto &ATOMIC_MAX_REL = "atomic_max_rel";
+  static constexpr auto &ATOMIC_MAX_ACQREL = "atomic_max_acqrel";
+  static constexpr auto &ATOMIC_MAX_RELAXED = "atomic_max_relaxed";
+  static constexpr auto &ATOMIC_UMIN = "atomic_umin";
+  static constexpr auto &ATOMIC_UMIN_ACQ = "atomic_umin_acq";
+  static constexpr auto &ATOMIC_UMIN_REL = "atomic_umin_rel";
+  static constexpr auto &ATOMIC_UMIN_ACQREL = "atomic_umin_acqrel";
+  static constexpr auto &ATOMIC_UMIN_RELAXED = "atomic_umin_relaxed";
+  static constexpr auto &ATOMIC_UMAX = "atomic_umax";
+  static constexpr auto &ATOMIC_UMAX_ACQ = "atomic_umax_acq";
+  static constexpr auto &ATOMIC_UMAX_REL = "atomic_umax_rel";
+  static constexpr auto &ATOMIC_UMAX_ACQREL = "atomic_umax_acqrel";
+  static constexpr auto &ATOMIC_UMAX_RELAXED = "atomic_umax_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";
diff --git a/gcc/testsuite/rust/compile/atomic_minmax.rs 
b/gcc/testsuite/rust/compile/atomic_minmax.rs
new file mode 100644
index 00000000000..4ae47a37728
--- /dev/null
+++ b/gcc/testsuite/rust/compile/atomic_minmax.rs
@@ -0,0 +1,78 @@
+#![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" {
+    fn atomic_min<T>(dst: *mut T, val: T) -> T;
+    fn atomic_umax<T>(dst: *mut T, val: T) -> T;
+}
+
+fn main() {
+    let mut flag = true;
+    let mut value = 1.0f32;
+    unsafe {
+        atomic_min(&mut flag, false); // { dg-error "atomic operation 
intrinsic can only be used with basic integer types .got .bool.." }
+        atomic_umax(&mut value, 2.0f32); // { dg-error "atomic operation 
intrinsic can only be used with basic integer types .got .f32.." }
+    }
+}
diff --git a/gcc/testsuite/rust/execute/torture/atomic_minmax.rs 
b/gcc/testsuite/rust/execute/torture/atomic_minmax.rs
new file mode 100644
index 00000000000..0135a6fe794
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/atomic_minmax.rs
@@ -0,0 +1,226 @@
+#![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_min<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_min_acq<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_min_rel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_min_acqrel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_min_relaxed<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_max<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_max_acq<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_max_rel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_max_acqrel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_max_relaxed<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umin<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umin_acq<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umin_rel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umin_acqrel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umin_relaxed<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umax<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umax_acq<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umax_rel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umax_acqrel<T: Copy>(dst: *mut T, val: T) -> T;
+    pub fn atomic_umax_relaxed<T: Copy>(dst: *mut T, val: T) -> T;
+}
+
+fn main() -> u32 {
+    unsafe {
+        let mut src = 9i8;
+
+        let old = atomic_min(&mut src, -7);
+        if old != 9 || src != -7 {
+            return 1;
+        }
+
+        let old = atomic_min_acq(&mut src, 9);
+        if old != -7 || src != -7 {
+            return 1;
+        }
+
+        let old = atomic_min_rel(&mut src, -9);
+        if old != -7 || src != -9 {
+            return 1;
+        }
+
+        let old = atomic_min_acqrel(&mut src, -9);
+        if old != -9 || src != -9 {
+            return 1;
+        }
+
+        let old = atomic_min_relaxed(&mut src, -10);
+        if old != -9 || src != -10 {
+            return 1;
+        }
+
+        let mut src = -7i8;
+
+        let old = atomic_max(&mut src, 9);
+        if old != -7 || src != 9 {
+            return 1;
+        }
+
+        let old = atomic_max_acq(&mut src, -7);
+        if old != 9 || src != 9 {
+            return 1;
+        }
+
+        let old = atomic_max_rel(&mut src, 10);
+        if old != 9 || src != 10 {
+            return 1;
+        }
+
+        let old = atomic_max_acqrel(&mut src, 10);
+        if old != 10 || src != 10 {
+            return 1;
+        }
+
+        let old = atomic_max_relaxed(&mut src, 11);
+        if old != 10 || src != 11 {
+            return 1;
+        }
+
+        let mut src = 249u8;
+
+        let old = atomic_umin(&mut src, 9);
+        if old != 249 || src != 9 {
+            return 1;
+        }
+
+        let old = atomic_umin_acq(&mut src, 249);
+        if old != 9 || src != 9 {
+            return 1;
+        }
+
+        let old = atomic_umin_rel(&mut src, 7);
+        if old != 9 || src != 7 {
+            return 1;
+        }
+
+        let old = atomic_umin_acqrel(&mut src, 7);
+        if old != 7 || src != 7 {
+            return 1;
+        }
+
+        let old = atomic_umin_relaxed(&mut src, 6);
+        if old != 7 || src != 6 {
+            return 1;
+        }
+
+        let mut src = 9u8;
+
+        let old = atomic_umax(&mut src, 249);
+        if old != 9 || src != 249 {
+            return 1;
+        }
+
+        let old = atomic_umax_acq(&mut src, 9);
+        if old != 249 || src != 249 {
+            return 1;
+        }
+
+        let old = atomic_umax_rel(&mut src, 250);
+        if old != 249 || src != 250 {
+            return 1;
+        }
+
+        let old = atomic_umax_acqrel(&mut src, 250);
+        if old != 250 || src != 250 {
+            return 1;
+        }
+
+        let old = atomic_umax_relaxed(&mut src, 251);
+        if old != 250 || src != 251 {
+            return 1;
+        }
+
+        // funny non-matching signedness tests below
+        let mut src = 128u8;
+
+        let old = atomic_min(&mut src, 7);
+        if old != 128 || src != 128 {
+            return 1;
+        }
+
+        let old = atomic_max(&mut src, 7);
+        if old != 128 || src != 7 {
+            return 1;
+        }
+
+        let mut src = 7i8;
+
+        let old = atomic_umax(&mut src, -128);
+        if old != 7 || src != -128 {
+            return 1;
+        }
+
+        let old = atomic_umin(&mut src, 7);
+        if old != -128 || src != 7 {
+            return 1;
+        }
+    }
+
+    0
+}
-- 
2.55.0

Reply via email to