diff --git a/c2rust-transpile/src/translator/atomics.rs b/c2rust-transpile/src/translator/atomics.rs index 5c21c90184..b71986e28b 100644 --- a/c2rust-transpile/src/translator/atomics.rs +++ b/c2rust-transpile/src/translator/atomics.rs @@ -19,7 +19,7 @@ fn order_suffix(order: Ordering) -> &'static str { } } -fn order_ty_name(order: Ordering) -> &'static str { +pub(crate) fn order_ty_name(order: Ordering) -> &'static str { use Ordering::*; match order { SeqCst => "SeqCst", @@ -104,7 +104,7 @@ impl<'c> Translation<'c> { } } - fn convert_memordering(&self, expr: CExprId) -> Option { + pub(crate) fn convert_memordering(&self, expr: CExprId) -> Option { let memorder = &self.ast_context.index_unwrap_parens(expr); let i = match memorder.kind { CExprKind::Literal(_, CLiteral::Integer(i, _)) => Some(i), diff --git a/c2rust-transpile/src/translator/builtins.rs b/c2rust-transpile/src/translator/builtins.rs index 763bb79416..a4e6d776a3 100644 --- a/c2rust-transpile/src/translator/builtins.rs +++ b/c2rust-transpile/src/translator/builtins.rs @@ -4,11 +4,12 @@ use super::*; use crate::format_translation_err; -use crate::translator::atomics::CAtomicBinOp; +use crate::translator::atomics::{order_ty_name, CAtomicBinOp}; use crate::translator::simd::simd_fn_from_builtin_fn; use c2rust_rust_tools::RustEdition::Edition2024; use log::warn; use std::sync::atomic::Ordering::Acquire; +use std::sync::atomic::Ordering::Relaxed; use std::sync::atomic::Ordering::Release; use std::sync::atomic::Ordering::SeqCst; @@ -525,6 +526,43 @@ impl<'c> Translation<'c> { ) } + // `__atomic_thread_fence` is a full fence (`atomic_fence`); + // `__atomic_signal_fence` only fences the compiler + // (`compiler_fence`). The order picks the intrinsic at + // compile time, so we only support a constant one. A relaxed fence + // is a no-op (and Rust has no relaxed fence intrinsic), so we drop it. + "__atomic_thread_fence" | "__atomic_signal_fence" => { + let order = self.convert_memordering(args[0]).ok_or_else(|| { + format_translation_err!( + self.ast_context.display_loc(src_loc), + "non-constant memory order argument to {} is not supported", + builtin_name + ) + })?; + let call_expr = if order == Relaxed { + mk().tuple_expr(vec![]) + } else if builtin_name == "__atomic_thread_fence" { + mk().call_expr(self.atomic_intrinsic_expr("fence", &[order]), vec![]) + } else { + let ordering = mk().abs_path_expr(vec![ + "core", + "sync", + "atomic", + "Ordering", + order_ty_name(order), + ]); + mk().call_expr( + mk().abs_path_expr(vec!["core", "sync", "atomic", "compiler_fence"]), + vec![ordering], + ) + }; + self.convert_side_effects_expr( + ctx, + WithStmts::new_val(call_expr), + "Builtin is not supposed to be used", + ) + } + "__sync_lock_test_and_set_1" | "__sync_lock_test_and_set_2" | "__sync_lock_test_and_set_4" diff --git a/c2rust-transpile/tests/snapshots.rs b/c2rust-transpile/tests/snapshots.rs index df95f3719f..2d9ab9ed75 100644 --- a/c2rust-transpile/tests/snapshots.rs +++ b/c2rust-transpile/tests/snapshots.rs @@ -376,6 +376,11 @@ fn test_factorial() { transpile("factorial.c").run(); } +#[test] +fn test_fences() { + transpile("fences.c").run(); +} + #[test] fn test_fn_attrs() { transpile("fn_attrs.c").run(); diff --git a/c2rust-transpile/tests/snapshots/fences.c b/c2rust-transpile/tests/snapshots/fences.c new file mode 100644 index 0000000000..f5ee0b2006 --- /dev/null +++ b/c2rust-transpile/tests/snapshots/fences.c @@ -0,0 +1,13 @@ +void fences(void) { + __atomic_thread_fence(__ATOMIC_RELAXED); + __atomic_thread_fence(__ATOMIC_ACQUIRE); + __atomic_thread_fence(__ATOMIC_RELEASE); + __atomic_thread_fence(__ATOMIC_ACQ_REL); + __atomic_thread_fence(__ATOMIC_SEQ_CST); + + __atomic_signal_fence(__ATOMIC_RELAXED); + __atomic_signal_fence(__ATOMIC_ACQUIRE); + __atomic_signal_fence(__ATOMIC_RELEASE); + __atomic_signal_fence(__ATOMIC_ACQ_REL); + __atomic_signal_fence(__ATOMIC_SEQ_CST); +} diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@fences.c.2021.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@fences.c.2021.clang15.snap new file mode 100644 index 0000000000..7a5136573e --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@fences.c.2021.clang15.snap @@ -0,0 +1,32 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/fences.2021.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused_assignments, + unused_mut +)] +#![feature(core_intrinsics)] +#[no_mangle] +pub unsafe extern "C" fn fences() { + (); + ::core::intrinsics::atomic_fence_acquire(); + ::core::intrinsics::atomic_fence_release(); + ::core::intrinsics::atomic_fence_acqrel(); + ::core::intrinsics::atomic_fence_seqcst(); + (); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Acquire); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Release); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::AcqRel); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::SeqCst); +} +pub const __ATOMIC_RELAXED: ::core::ffi::c_int = 0 as ::core::ffi::c_int; +pub const __ATOMIC_ACQUIRE: ::core::ffi::c_int = 2 as ::core::ffi::c_int; +pub const __ATOMIC_RELEASE: ::core::ffi::c_int = 3 as ::core::ffi::c_int; +pub const __ATOMIC_ACQ_REL: ::core::ffi::c_int = 4 as ::core::ffi::c_int; +pub const __ATOMIC_SEQ_CST: ::core::ffi::c_int = 5 as ::core::ffi::c_int; diff --git a/c2rust-transpile/tests/snapshots/snapshots__transpile@fences.c.2024.clang15.snap b/c2rust-transpile/tests/snapshots/snapshots__transpile@fences.c.2024.clang15.snap new file mode 100644 index 0000000000..c620bb5f8f --- /dev/null +++ b/c2rust-transpile/tests/snapshots/snapshots__transpile@fences.c.2024.clang15.snap @@ -0,0 +1,33 @@ +--- +source: c2rust-transpile/tests/snapshots.rs +expression: cat tests/snapshots/fences.2024.clang15.rs +--- +#![allow( + clippy::missing_safety_doc, + dead_code, + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unsafe_op_in_unsafe_fn, + unused_assignments, + unused_mut +)] +#![feature(core_intrinsics)] +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fences() { + (); + ::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::Acquire }>(); + ::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::Release }>(); + ::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::AcqRel }>(); + ::core::intrinsics::atomic_fence::<{ ::core::intrinsics::AtomicOrdering::SeqCst }>(); + (); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Acquire); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::Release); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::AcqRel); + ::core::sync::atomic::compiler_fence(::core::sync::atomic::Ordering::SeqCst); +} +pub const __ATOMIC_RELAXED: ::core::ffi::c_int = 0 as ::core::ffi::c_int; +pub const __ATOMIC_ACQUIRE: ::core::ffi::c_int = 2 as ::core::ffi::c_int; +pub const __ATOMIC_RELEASE: ::core::ffi::c_int = 3 as ::core::ffi::c_int; +pub const __ATOMIC_ACQ_REL: ::core::ffi::c_int = 4 as ::core::ffi::c_int; +pub const __ATOMIC_SEQ_CST: ::core::ffi::c_int = 5 as ::core::ffi::c_int; diff --git a/tests/unit/builtins/src/atomics.c b/tests/unit/builtins/src/atomics.c index fe514a30e8..4c5f04a0ed 100644 --- a/tests/unit/builtins/src/atomics.c +++ b/tests/unit/builtins/src/atomics.c @@ -66,3 +66,25 @@ void new_atomics(const unsigned buffer_size, int buffer[const]) __atomic_store_n(&x, 0, __ATOMIC_RELAXED); buffer[i++] = x; } + +void fences(const unsigned buffer_size, int buffer[const]) +{ + int i = 0, x = 34; + // Full memory fences (`__atomic_thread_fence`) and compiler-only fences + // (`__atomic_signal_fence`), across every memory order. A relaxed fence is + // a no-op. Fences have no observable effect here; this just exercises that + // each one transpiles and compiles. + __atomic_thread_fence(__ATOMIC_RELAXED); buffer[i++] = ++x; + __atomic_thread_fence(__ATOMIC_ACQUIRE); buffer[i++] = ++x; + __atomic_thread_fence(__ATOMIC_RELEASE); buffer[i++] = ++x; + __atomic_thread_fence(__ATOMIC_ACQ_REL); buffer[i++] = ++x; + __atomic_thread_fence(__ATOMIC_SEQ_CST); buffer[i++] = ++x; + + __atomic_signal_fence(__ATOMIC_RELAXED); buffer[i++] = ++x; + __atomic_signal_fence(__ATOMIC_ACQUIRE); buffer[i++] = ++x; + __atomic_signal_fence(__ATOMIC_RELEASE); buffer[i++] = ++x; + __atomic_signal_fence(__ATOMIC_ACQ_REL); buffer[i++] = ++x; + __atomic_signal_fence(__ATOMIC_SEQ_CST); buffer[i++] = ++x; + + __sync_synchronize(); buffer[i++] = ++x; +} diff --git a/tests/unit/builtins/src/test_builtins.rs b/tests/unit/builtins/src/test_builtins.rs index afbc298ec7..788ad78fc1 100644 --- a/tests/unit/builtins/src/test_builtins.rs +++ b/tests/unit/builtins/src/test_builtins.rs @@ -1,7 +1,7 @@ //! feature_core_intrinsics, feature_raw_ref_op use crate::alloca::rust_alloca_hello; -use crate::atomics::{rust_atomics_entry, rust_new_atomics}; +use crate::atomics::{rust_atomics_entry, rust_fences, rust_new_atomics}; use crate::math::{rust_ffs, rust_ffsl, rust_ffsll, rust_isfinite, rust_isinf_sign, rust_isnan}; use crate::mem_x_fns::{rust_assume_aligned, rust_mem_x}; use crate::overflow::rust_overflow_builtins; @@ -12,6 +12,7 @@ unsafe extern "C" { fn alloca_hello() -> c_int; fn atomics_entry(_: c_uint, _: *mut c_int); fn new_atomics(_: c_uint, _: *mut c_int); + fn fences(_: c_uint, _: *mut c_int); fn mem_x(_: *const c_char, _: *mut c_char); fn ffs(_: c_int) -> c_int; fn ffsl(_: c_long) -> c_int; @@ -79,6 +80,21 @@ pub fn test_overflow_builtins() { } } +#[test] +pub fn test_fences() { + let mut buffer = [0; BUFFER_SIZE]; + let mut rust_buffer = [0; BUFFER_SIZE]; + + unsafe { + fences(BUFFER_SIZE as u32, buffer.as_mut_ptr()); + rust_fences(BUFFER_SIZE as u32, rust_buffer.as_mut_ptr()); + } + + for index in 0..BUFFER_SIZE { + assert_eq!(buffer[index], rust_buffer[index]); + } +} + #[test] pub fn test_mem_fns() { let const_string = "I am ten!\0";