Skip to content

Commit cf72d63

Browse files
committed
transpile: Support __atomic_thread_fence/__atomic_signal_fence builtins
Map them to the `atomic_fence`/`atomic_singlethreadfence` intrinsics using the call's memory order. The order must be a constant (a non-constant one fails translation); a relaxed fence is emitted as a no-op since Rust has no relaxed fence intrinsic.
1 parent 0586cb3 commit cf72d63

8 files changed

Lines changed: 163 additions & 4 deletions

File tree

c2rust-transpile/src/translator/atomics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn order_suffix(order: Ordering) -> &'static str {
1919
}
2020
}
2121

22-
fn order_ty_name(order: Ordering) -> &'static str {
22+
pub(crate) fn order_ty_name(order: Ordering) -> &'static str {
2323
use Ordering::*;
2424
match order {
2525
SeqCst => "SeqCst",
@@ -104,7 +104,7 @@ impl<'c> Translation<'c> {
104104
}
105105
}
106106

107-
fn convert_memordering(&self, expr: CExprId) -> Option<Ordering> {
107+
pub(crate) fn convert_memordering(&self, expr: CExprId) -> Option<Ordering> {
108108
let memorder = &self.ast_context.index_unwrap_parens(expr);
109109
let i = match memorder.kind {
110110
CExprKind::Literal(_, CLiteral::Integer(i, _)) => Some(i),

c2rust-transpile/src/translator/builtins.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
use super::*;
55

66
use crate::format_translation_err;
7-
use crate::translator::atomics::CAtomicBinOp;
7+
use crate::translator::atomics::{order_ty_name, CAtomicBinOp};
88
use crate::translator::simd::simd_fn_from_builtin_fn;
99
use c2rust_rust_tools::RustEdition::Edition2024;
1010
use log::warn;
1111
use std::sync::atomic::Ordering::Acquire;
12+
use std::sync::atomic::Ordering::Relaxed;
1213
use std::sync::atomic::Ordering::Release;
1314
use std::sync::atomic::Ordering::SeqCst;
1415

@@ -525,6 +526,43 @@ impl<'c> Translation<'c> {
525526
)
526527
}
527528

529+
// `__atomic_thread_fence` is a full fence (`atomic_fence`);
530+
// `__atomic_signal_fence` only fences the compiler
531+
// (`compiler_fence`). The order picks the intrinsic at
532+
// compile time, so we only support a constant one. A relaxed fence
533+
// is a no-op (and Rust has no relaxed fence intrinsic), so we drop it.
534+
"__atomic_thread_fence" | "__atomic_signal_fence" => {
535+
let order = self.convert_memordering(args[0]).ok_or_else(|| {
536+
format_translation_err!(
537+
self.ast_context.display_loc(src_loc),
538+
"non-constant memory order argument to {} is not supported",
539+
builtin_name
540+
)
541+
})?;
542+
let call_expr = if order == Relaxed {
543+
mk().tuple_expr(vec![])
544+
} else if builtin_name == "__atomic_thread_fence" {
545+
mk().call_expr(self.atomic_intrinsic_expr("fence", &[order]), vec![])
546+
} else {
547+
let ordering = mk().abs_path_expr(vec![
548+
"core",
549+
"sync",
550+
"atomic",
551+
"Ordering",
552+
order_ty_name(order),
553+
]);
554+
mk().call_expr(
555+
mk().abs_path_expr(vec!["core", "sync", "atomic", "compiler_fence"]),
556+
vec![ordering],
557+
)
558+
};
559+
self.convert_side_effects_expr(
560+
ctx,
561+
WithStmts::new_val(call_expr),
562+
"Builtin is not supposed to be used",
563+
)
564+
}
565+
528566
"__sync_lock_test_and_set_1"
529567
| "__sync_lock_test_and_set_2"
530568
| "__sync_lock_test_and_set_4"

c2rust-transpile/tests/snapshots.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,11 @@ fn test_factorial() {
381381
transpile("factorial.c").run();
382382
}
383383

384+
#[test]
385+
fn test_fences() {
386+
transpile("fences.c").run();
387+
}
388+
384389
#[test]
385390
fn test_fn_attrs() {
386391
transpile("fn_attrs.c").run();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void fences(void) {
2+
__atomic_thread_fence(__ATOMIC_RELAXED);
3+
__atomic_thread_fence(__ATOMIC_ACQUIRE);
4+
__atomic_thread_fence(__ATOMIC_RELEASE);
5+
__atomic_thread_fence(__ATOMIC_ACQ_REL);
6+
__atomic_thread_fence(__ATOMIC_SEQ_CST);
7+
8+
__atomic_signal_fence(__ATOMIC_RELAXED);
9+
__atomic_signal_fence(__ATOMIC_ACQUIRE);
10+
__atomic_signal_fence(__ATOMIC_RELEASE);
11+
__atomic_signal_fence(__ATOMIC_ACQ_REL);
12+
__atomic_signal_fence(__ATOMIC_SEQ_CST);
13+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
source: c2rust-transpile/tests/snapshots.rs
3+
expression: cat tests/snapshots/fences.2021.clang15.rs
4+
---
5+
#![allow(
6+
clippy::missing_safety_doc,
7+
dead_code,
8+
non_camel_case_types,
9+
non_snake_case,
10+
non_upper_case_globals,
11+
unused_assignments,
12+
unused_mut
13+
)]
14+
#![feature(core_intrinsics)]
15+
#[no_mangle]
16+
pub unsafe extern "C" fn fences() {
17+
();
18+
::core::intrinsics::atomic_fence_acquire();
19+
::core::intrinsics::atomic_fence_release();
20+
::core::intrinsics::atomic_fence_acqrel();
21+
::core::intrinsics::atomic_fence_seqcst();
22+
();
23+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Acquire);
24+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Release);
25+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::AcqRel);
26+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::SeqCst);
27+
}
28+
pub const __ATOMIC_RELAXED: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
29+
pub const __ATOMIC_ACQUIRE: ::core::ffi::c_int = 2 as ::core::ffi::c_int;
30+
pub const __ATOMIC_RELEASE: ::core::ffi::c_int = 3 as ::core::ffi::c_int;
31+
pub const __ATOMIC_ACQ_REL: ::core::ffi::c_int = 4 as ::core::ffi::c_int;
32+
pub const __ATOMIC_SEQ_CST: ::core::ffi::c_int = 5 as ::core::ffi::c_int;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
source: c2rust-transpile/tests/snapshots.rs
3+
expression: cat tests/snapshots/fences.2024.clang15.rs
4+
---
5+
#![allow(
6+
clippy::missing_safety_doc,
7+
dead_code,
8+
non_camel_case_types,
9+
non_snake_case,
10+
non_upper_case_globals,
11+
unsafe_op_in_unsafe_fn,
12+
unused_assignments,
13+
unused_mut
14+
)]
15+
#![feature(core_intrinsics)]
16+
#[unsafe(no_mangle)]
17+
pub unsafe extern "C" fn fences() {
18+
();
19+
::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::Acquire }>();
20+
::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::Release }>();
21+
::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::AcqRel }>();
22+
::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::SeqCst }>();
23+
();
24+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Acquire);
25+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Release);
26+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::AcqRel);
27+
::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::SeqCst);
28+
}
29+
pub const __ATOMIC_RELAXED: ::core::ffi::c_int = 0 as ::core::ffi::c_int;
30+
pub const __ATOMIC_ACQUIRE: ::core::ffi::c_int = 2 as ::core::ffi::c_int;
31+
pub const __ATOMIC_RELEASE: ::core::ffi::c_int = 3 as ::core::ffi::c_int;
32+
pub const __ATOMIC_ACQ_REL: ::core::ffi::c_int = 4 as ::core::ffi::c_int;
33+
pub const __ATOMIC_SEQ_CST: ::core::ffi::c_int = 5 as ::core::ffi::c_int;

tests/unit/builtins/src/atomics.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,25 @@ void new_atomics(const unsigned buffer_size, int buffer[const])
6666
__atomic_store_n(&x, 0, __ATOMIC_RELAXED);
6767
buffer[i++] = x;
6868
}
69+
70+
void fences(const unsigned buffer_size, int buffer[const])
71+
{
72+
int i = 0, x = 34;
73+
// Full memory fences (`__atomic_thread_fence`) and compiler-only fences
74+
// (`__atomic_signal_fence`), across every memory order. A relaxed fence is
75+
// a no-op. Fences have no observable effect here; this just exercises that
76+
// each one transpiles and compiles.
77+
__atomic_thread_fence(__ATOMIC_RELAXED); buffer[i++] = ++x;
78+
__atomic_thread_fence(__ATOMIC_ACQUIRE); buffer[i++] = ++x;
79+
__atomic_thread_fence(__ATOMIC_RELEASE); buffer[i++] = ++x;
80+
__atomic_thread_fence(__ATOMIC_ACQ_REL); buffer[i++] = ++x;
81+
__atomic_thread_fence(__ATOMIC_SEQ_CST); buffer[i++] = ++x;
82+
83+
__atomic_signal_fence(__ATOMIC_RELAXED); buffer[i++] = ++x;
84+
__atomic_signal_fence(__ATOMIC_ACQUIRE); buffer[i++] = ++x;
85+
__atomic_signal_fence(__ATOMIC_RELEASE); buffer[i++] = ++x;
86+
__atomic_signal_fence(__ATOMIC_ACQ_REL); buffer[i++] = ++x;
87+
__atomic_signal_fence(__ATOMIC_SEQ_CST); buffer[i++] = ++x;
88+
89+
__sync_synchronize(); buffer[i++] = ++x;
90+
}

tests/unit/builtins/src/test_builtins.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! feature_core_intrinsics, feature_raw_ref_op
22
33
use crate::alloca::rust_alloca_hello;
4-
use crate::atomics::{rust_atomics_entry, rust_new_atomics};
4+
use crate::atomics::{rust_atomics_entry, rust_fences, rust_new_atomics};
55
use crate::math::{rust_ffs, rust_ffsl, rust_ffsll, rust_isfinite, rust_isinf_sign, rust_isnan};
66
use crate::mem_x_fns::{rust_assume_aligned, rust_mem_x};
77
use std::ffi::{c_char, c_double, c_int, c_long, c_longlong, c_uint};
@@ -11,6 +11,7 @@ unsafe extern "C" {
1111
fn alloca_hello() -> c_int;
1212
fn atomics_entry(_: c_uint, _: *mut c_int);
1313
fn new_atomics(_: c_uint, _: *mut c_int);
14+
fn fences(_: c_uint, _: *mut c_int);
1415
fn mem_x(_: *const c_char, _: *mut c_char);
1516
fn ffs(_: c_int) -> c_int;
1617
fn ffsl(_: c_long) -> c_int;
@@ -62,6 +63,21 @@ pub fn test_new_atomics() {
6263
}
6364
}
6465

66+
#[test]
67+
pub fn test_fences() {
68+
let mut buffer = [0; BUFFER_SIZE];
69+
let mut rust_buffer = [0; BUFFER_SIZE];
70+
71+
unsafe {
72+
fences(BUFFER_SIZE as u32, buffer.as_mut_ptr());
73+
rust_fences(BUFFER_SIZE as u32, rust_buffer.as_mut_ptr());
74+
}
75+
76+
for index in 0..BUFFER_SIZE {
77+
assert_eq!(buffer[index], rust_buffer[index]);
78+
}
79+
}
80+
6581
#[test]
6682
pub fn test_mem_fns() {
6783
let const_string = "I am ten!\0";

0 commit comments

Comments
 (0)