Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 128 additions & 77 deletions c2rust-transpile/src/translator/atomics.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,101 @@
use crate::format_translation_err;
use itertools::repeat_n;
use std::sync::atomic::Ordering;

use super::*;
use std::sync::atomic::Ordering;

fn order_suffix(order: Ordering) -> &'static str {
use Ordering::*;
match order {
SeqCst => "seqcst",
AcqRel => "acqrel",
Acquire => "acquire",
Release => "release",
Relaxed => "relaxed",
_ => unreachable!(
"new variants added to `{}`",
std::any::type_name::<Ordering>()
),
}
}

fn order_ty_name(order: Ordering) -> &'static str {
use Ordering::*;
match order {
SeqCst => "SeqCst",
AcqRel => "AcqRel",
Acquire => "Acquire",
Release => "Release",
Relaxed => "Relaxed",
_ => unreachable!(
"new variants added to `{}`",
std::any::type_name::<Ordering>()
),
}
}

impl<'c> Translation<'c> {
fn atomic_intrinsic_expr_edition_2021(
&self,
base_name: &str,
orders: &[Ordering],
Comment thread
kkysen marked this conversation as resolved.
) -> Box<Expr> {
let prefix = ["atomic", base_name];
let suffix = orders.iter().map(|&order| order_suffix(order));
let intrinsic_name = prefix.iter().copied().chain(suffix).join("_");
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name])
}

fn atomic_intrinsic_expr_edition_2024(
&self,
base_name: &str,
orders: &[Ordering],
) -> Box<Expr> {
let num_ty_params = match base_name {
"fence" => 0,
"load" | "store" | "xchg" | "cxchg" | "cxchgweak" => 1,
"xadd" | "xsub" | "or" | "xor" | "nand" | "and" => 2,
_ => unimplemented!("unknown atomic intrinsic: {base_name}"),
};

let args = repeat_n("_".to_owned(), num_ty_params)
.chain(orders.iter().map(|&order| {
let order = order_ty_name(order);
format!("{{ ::core::intrinsics::AtomicOrdering::{order} }}")
}))
.join(", ");
let path = format!("::core::intrinsics::atomic_{base_name}::<{args}>");
// `mk()`/`Builder` doesn't seem to support const generic expressions,
// so re-parsing with `syn` is simpler.
let expr = syn::parse_str::<Expr>(&path)
.unwrap_or_else(|_| panic!("failed to parse intrinsic path: {path}"));
Box::new(expr)
}

pub fn atomic_intrinsic_expr(&self, base_name: &str, orders: &[Ordering]) -> Box<Expr> {
assert!(matches!(
orders,
&[_ /* order */] | &[_ /* order_succ */, _ /* order_fail */]
));

self.use_feature("core_intrinsics");
if self.tcfg.edition < Edition2024 {
self.atomic_intrinsic_expr_edition_2021(base_name, orders)
} else {
self.atomic_intrinsic_expr_edition_2024(base_name, orders)
}
}

fn atomic_intrinsic_cxchg_expr(
&self,
weak: bool,
order_succ: Ordering,
order_fail: Ordering,
) -> Box<Expr> {
let base = if weak { "cxchgweak" } else { "cxchg" };
self.atomic_intrinsic_expr(base, &[order_succ, order_fail])
}

fn convert_constant_bool(&self, expr: CExprId) -> Option<bool> {
let val = self.ast_context.unwrap_cast_expr(expr);
match self.ast_context.index(val).kind {
Expand Down Expand Up @@ -70,28 +162,11 @@ impl<'c> Translation<'c> {
})
}

fn order_name(order: Ordering) -> &'static str {
use Ordering::*;
match order {
SeqCst => "seqcst",
AcqRel => "acqrel",
Acquire => "acquire",
Release => "release",
Relaxed => "relaxed",
_ => unreachable!(
"new variants added to `{}`",
std::any::type_name::<Ordering>()
),
}
}

match name {
"__atomic_load" | "__atomic_load_n" | "__c11_atomic_load" => ptr.and_then(|ptr| {
let intrinsic_name = format!("atomic_load_{}", order_name(static_order(order)));
let order = static_order(order);

self.use_feature("core_intrinsics");

let atomic_load = mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
let atomic_load = self.atomic_intrinsic_expr("load", &[order]);
let call = mk().call_expr(atomic_load, vec![ptr]);
if name == "__atomic_load" {
let ret = val1.expect("__atomic_load should have a ret argument");
Expand All @@ -116,16 +191,11 @@ impl<'c> Translation<'c> {
}),

"__atomic_store" | "__atomic_store_n" | "__c11_atomic_store" => {
let order = static_order(order);
let val = val1.expect("__atomic_store must have a val argument");
ptr.and_then(|ptr| {
val.and_then(|val| {
let intrinsic_name =
format!("atomic_store_{}", order_name(static_order(order)));

self.use_feature("core_intrinsics");

let atomic_store =
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
let atomic_store = self.atomic_intrinsic_expr("store", &[order]);
let val = if name == "__atomic_store" {
mk().unary_expr(UnOp::Deref(Default::default()), val)
} else {
Expand Down Expand Up @@ -160,16 +230,11 @@ impl<'c> Translation<'c> {
}

"__atomic_exchange" | "__atomic_exchange_n" | "__c11_atomic_exchange" => {
let order = static_order(order);
let val = val1.expect("__atomic_store must have a val argument");
ptr.and_then(|ptr| {
val.and_then(|val| {
let intrinsic_name =
format!("atomic_xchg_{}", order_name(static_order(order)));

self.use_feature("core_intrinsics");

let fn_path =
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
let fn_path = self.atomic_intrinsic_expr("xchg", &[order]);
let val = if name == "__atomic_exchange" {
mk().unary_expr(UnOp::Deref(Default::default()), val)
} else {
Expand Down Expand Up @@ -218,14 +283,15 @@ impl<'c> Translation<'c> {
_ => weak,
};

let order = static_order(order);
let order_fail = static_order(order_fail);
let weak = static_order(weak);

ptr.and_then(|ptr| {
expected.and_then(|expected| {
desired.and_then(|desired| {
let weak = static_order(weak);
let order = static_order(order);
let order_fail = static_order(order_fail);
use Ordering::*;
let intrinsic_name = match (order, order_fail) {
let (order, order_fail) = match (order, order_fail) {
(_, Release | AcqRel) => None,
(SeqCst, SeqCst | Acquire | Relaxed)
| (AcqRel, Acquire | Relaxed)
Expand All @@ -237,12 +303,6 @@ impl<'c> Translation<'c> {

(_, _) => unreachable!("Did we not handle a case above??"),
}
.map(|(order, order_fail)| {
let weak = if weak { "weak" } else { "" };
let order = order_name(order);
let order_fail = order_name(order_fail);
format!("atomic_cxchg{weak}_{order}_{order_fail}")
})
.ok_or_else(|| {
format_translation_err!(
self.ast_context
Expand All @@ -251,7 +311,6 @@ impl<'c> Translation<'c> {
)
})?;

self.use_feature("core_intrinsics");
let expected =
mk().unary_expr(UnOp::Deref(Default::default()), expected);
let desired = match name {
Expand All @@ -262,7 +321,7 @@ impl<'c> Translation<'c> {
};

let atomic_cxchg =
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
self.atomic_intrinsic_cxchg_expr(weak, order, order_fail);
let call =
mk().call_expr(atomic_cxchg, vec![ptr, expected.clone(), desired]);
let res_name = self.renamer.borrow_mut().fresh();
Expand Down Expand Up @@ -305,28 +364,27 @@ impl<'c> Translation<'c> {
| "__c11_atomic_fetch_or"
| "__c11_atomic_fetch_nand" => {
let intrinsic_name = if name.contains("_add") {
"atomic_xadd"
"xadd"
} else if name.contains("_sub") {
"atomic_xsub"
"xsub"
} else if name.contains("_or") {
"atomic_or"
"or"
} else if name.contains("_xor") {
"atomic_xor"
"xor"
} else if name.contains("_nand") {
"atomic_nand"
"nand"
} else {
"atomic_and"
"and"
};

let intrinsic_suffix = order_name(static_order(order));
let intrinsic_name = format!("{intrinsic_name}_{intrinsic_suffix}");
let order = static_order(order);

let fetch_first =
name.starts_with("__atomic_fetch") || name.starts_with("__c11_atomic_fetch");
let val = val1.expect("__atomic arithmetic operations must have a val argument");
ptr.and_then(|ptr| {
val.and_then(|val| {
self.convert_atomic_op(ctx, &intrinsic_name, ptr, val, fetch_first)
self.convert_atomic_op(ctx, intrinsic_name, order, ptr, val, fetch_first)
})
})
}
Expand All @@ -338,16 +396,16 @@ impl<'c> Translation<'c> {
pub(crate) fn convert_atomic_cxchg(
&self,
ctx: ExprContext,
intrinsic_name: &str,
weak: bool,
order_succ: Ordering,
order_fail: Ordering,
dst: Box<Expr>,
old_val: Box<Expr>,
src_val: Box<Expr>,
returns_val: bool,
) -> TranslationResult<WithStmts<Box<Expr>>> {
self.use_feature("core_intrinsics");

// Emit `atomic_cxchg(a0, a1, a2).idx`
Comment thread
kkysen marked this conversation as resolved.
let atomic_cxchg = mk().abs_path_expr(vec!["core", "intrinsics", intrinsic_name]);
let atomic_cxchg = self.atomic_intrinsic_cxchg_expr(weak, order_succ, order_fail);
let call = mk().call_expr(atomic_cxchg, vec![dst, old_val, src_val]);
let field_idx = if returns_val { 0 } else { 1 };
let call_expr = mk().anon_field_expr(call, field_idx);
Expand All @@ -361,15 +419,14 @@ impl<'c> Translation<'c> {
pub(crate) fn convert_atomic_op(
&self,
ctx: ExprContext,
func_name: &str,
base_name: &str,
order: Ordering,
dst: Box<Expr>,
src: Box<Expr>,
fetch_first: bool,
) -> TranslationResult<WithStmts<Box<Expr>>> {
self.use_feature("core_intrinsics");

// Emit `atomic_func(a0, a1) (op a1)?`
let atomic_func = mk().abs_path_expr(vec!["core", "intrinsics", func_name]);
let atomic_func = self.atomic_intrinsic_expr(base_name, &[order]);

if fetch_first {
let call_expr = mk().call_expr(atomic_func, vec![dst, src]);
Expand All @@ -379,20 +436,14 @@ impl<'c> Translation<'c> {
"Builtin is not supposed to be used",
)
} else {
let (binary_op, is_nand) = if func_name.starts_with("atomic_xadd") {
(BinOp::Add(Default::default()), false)
} else if func_name.starts_with("atomic_xsub") {
(BinOp::Sub(Default::default()), false)
} else if func_name.starts_with("atomic_or") {
(BinOp::BitOr(Default::default()), false)
} else if func_name.starts_with("atomic_xor") {
(BinOp::BitXor(Default::default()), false)
} else if func_name.starts_with("atomic_nand") {
(BinOp::BitAnd(Default::default()), true)
} else if func_name.starts_with("atomic_and") {
(BinOp::BitAnd(Default::default()), false)
} else {
panic!("Unexpected atomic intrinsic name: {}", func_name)
let (binary_op, is_nand) = match base_name {
"xadd" => (BinOp::Add(Default::default()), false),
"xsub" => (BinOp::Sub(Default::default()), false),
"or" => (BinOp::BitOr(Default::default()), false),
"xor" => (BinOp::BitXor(Default::default()), false),
"nand" => (BinOp::BitAnd(Default::default()), true),
"and" => (BinOp::BitAnd(Default::default()), false),
_ => panic!("Unexpected atomic intrinsic base name: {base_name}"),
};

// Since the value of `arg1` is used twice, we need to copy
Expand Down
Loading
Loading