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