Skip to content

Commit 669198c

Browse files
authored
transpile: fix atomic intrinsics in edition 2024 (#1658)
Most of these commits are refactoring things to be in a shape where adding edition 2024 support is simple. Then the last commit, 07c2635, actually adds edition 2024 support.
2 parents 35b689c + e8192bb commit 669198c

6 files changed

Lines changed: 190 additions & 120 deletions

File tree

c2rust-transpile/src/translator/atomics.rs

Lines changed: 128 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,101 @@
11
use crate::format_translation_err;
2+
use itertools::repeat_n;
3+
use std::sync::atomic::Ordering;
24

35
use super::*;
4-
use std::sync::atomic::Ordering;
6+
7+
fn order_suffix(order: Ordering) -> &'static str {
8+
use Ordering::*;
9+
match order {
10+
SeqCst => "seqcst",
11+
AcqRel => "acqrel",
12+
Acquire => "acquire",
13+
Release => "release",
14+
Relaxed => "relaxed",
15+
_ => unreachable!(
16+
"new variants added to `{}`",
17+
std::any::type_name::<Ordering>()
18+
),
19+
}
20+
}
21+
22+
fn order_ty_name(order: Ordering) -> &'static str {
23+
use Ordering::*;
24+
match order {
25+
SeqCst => "SeqCst",
26+
AcqRel => "AcqRel",
27+
Acquire => "Acquire",
28+
Release => "Release",
29+
Relaxed => "Relaxed",
30+
_ => unreachable!(
31+
"new variants added to `{}`",
32+
std::any::type_name::<Ordering>()
33+
),
34+
}
35+
}
536

637
impl<'c> Translation<'c> {
38+
fn atomic_intrinsic_expr_edition_2021(
39+
&self,
40+
base_name: &str,
41+
orders: &[Ordering],
42+
) -> Box<Expr> {
43+
let prefix = ["atomic", base_name];
44+
let suffix = orders.iter().map(|&order| order_suffix(order));
45+
let intrinsic_name = prefix.iter().copied().chain(suffix).join("_");
46+
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name])
47+
}
48+
49+
fn atomic_intrinsic_expr_edition_2024(
50+
&self,
51+
base_name: &str,
52+
orders: &[Ordering],
53+
) -> Box<Expr> {
54+
let num_ty_params = match base_name {
55+
"fence" => 0,
56+
"load" | "store" | "xchg" | "cxchg" | "cxchgweak" => 1,
57+
"xadd" | "xsub" | "or" | "xor" | "nand" | "and" => 2,
58+
_ => unimplemented!("unknown atomic intrinsic: {base_name}"),
59+
};
60+
61+
let args = repeat_n("_".to_owned(), num_ty_params)
62+
.chain(orders.iter().map(|&order| {
63+
let order = order_ty_name(order);
64+
format!("{{ ::core::intrinsics::AtomicOrdering::{order} }}")
65+
}))
66+
.join(", ");
67+
let path = format!("::core::intrinsics::atomic_{base_name}::<{args}>");
68+
// `mk()`/`Builder` doesn't seem to support const generic expressions,
69+
// so re-parsing with `syn` is simpler.
70+
let expr = syn::parse_str::<Expr>(&path)
71+
.unwrap_or_else(|_| panic!("failed to parse intrinsic path: {path}"));
72+
Box::new(expr)
73+
}
74+
75+
pub fn atomic_intrinsic_expr(&self, base_name: &str, orders: &[Ordering]) -> Box<Expr> {
76+
assert!(matches!(
77+
orders,
78+
&[_ /* order */] | &[_ /* order_succ */, _ /* order_fail */]
79+
));
80+
81+
self.use_feature("core_intrinsics");
82+
if self.tcfg.edition < Edition2024 {
83+
self.atomic_intrinsic_expr_edition_2021(base_name, orders)
84+
} else {
85+
self.atomic_intrinsic_expr_edition_2024(base_name, orders)
86+
}
87+
}
88+
89+
fn atomic_intrinsic_cxchg_expr(
90+
&self,
91+
weak: bool,
92+
order_succ: Ordering,
93+
order_fail: Ordering,
94+
) -> Box<Expr> {
95+
let base = if weak { "cxchgweak" } else { "cxchg" };
96+
self.atomic_intrinsic_expr(base, &[order_succ, order_fail])
97+
}
98+
799
fn convert_constant_bool(&self, expr: CExprId) -> Option<bool> {
8100
let val = self.ast_context.unwrap_cast_expr(expr);
9101
match self.ast_context.index(val).kind {
@@ -70,28 +162,11 @@ impl<'c> Translation<'c> {
70162
})
71163
}
72164

73-
fn order_name(order: Ordering) -> &'static str {
74-
use Ordering::*;
75-
match order {
76-
SeqCst => "seqcst",
77-
AcqRel => "acqrel",
78-
Acquire => "acquire",
79-
Release => "release",
80-
Relaxed => "relaxed",
81-
_ => unreachable!(
82-
"new variants added to `{}`",
83-
std::any::type_name::<Ordering>()
84-
),
85-
}
86-
}
87-
88165
match name {
89166
"__atomic_load" | "__atomic_load_n" | "__c11_atomic_load" => ptr.and_then(|ptr| {
90-
let intrinsic_name = format!("atomic_load_{}", order_name(static_order(order)));
167+
let order = static_order(order);
91168

92-
self.use_feature("core_intrinsics");
93-
94-
let atomic_load = mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
169+
let atomic_load = self.atomic_intrinsic_expr("load", &[order]);
95170
let call = mk().call_expr(atomic_load, vec![ptr]);
96171
if name == "__atomic_load" {
97172
let ret = val1.expect("__atomic_load should have a ret argument");
@@ -116,16 +191,11 @@ impl<'c> Translation<'c> {
116191
}),
117192

118193
"__atomic_store" | "__atomic_store_n" | "__c11_atomic_store" => {
194+
let order = static_order(order);
119195
let val = val1.expect("__atomic_store must have a val argument");
120196
ptr.and_then(|ptr| {
121197
val.and_then(|val| {
122-
let intrinsic_name =
123-
format!("atomic_store_{}", order_name(static_order(order)));
124-
125-
self.use_feature("core_intrinsics");
126-
127-
let atomic_store =
128-
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
198+
let atomic_store = self.atomic_intrinsic_expr("store", &[order]);
129199
let val = if name == "__atomic_store" {
130200
mk().unary_expr(UnOp::Deref(Default::default()), val)
131201
} else {
@@ -160,16 +230,11 @@ impl<'c> Translation<'c> {
160230
}
161231

162232
"__atomic_exchange" | "__atomic_exchange_n" | "__c11_atomic_exchange" => {
233+
let order = static_order(order);
163234
let val = val1.expect("__atomic_store must have a val argument");
164235
ptr.and_then(|ptr| {
165236
val.and_then(|val| {
166-
let intrinsic_name =
167-
format!("atomic_xchg_{}", order_name(static_order(order)));
168-
169-
self.use_feature("core_intrinsics");
170-
171-
let fn_path =
172-
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
237+
let fn_path = self.atomic_intrinsic_expr("xchg", &[order]);
173238
let val = if name == "__atomic_exchange" {
174239
mk().unary_expr(UnOp::Deref(Default::default()), val)
175240
} else {
@@ -218,14 +283,15 @@ impl<'c> Translation<'c> {
218283
_ => weak,
219284
};
220285

286+
let order = static_order(order);
287+
let order_fail = static_order(order_fail);
288+
let weak = static_order(weak);
289+
221290
ptr.and_then(|ptr| {
222291
expected.and_then(|expected| {
223292
desired.and_then(|desired| {
224-
let weak = static_order(weak);
225-
let order = static_order(order);
226-
let order_fail = static_order(order_fail);
227293
use Ordering::*;
228-
let intrinsic_name = match (order, order_fail) {
294+
let (order, order_fail) = match (order, order_fail) {
229295
(_, Release | AcqRel) => None,
230296
(SeqCst, SeqCst | Acquire | Relaxed)
231297
| (AcqRel, Acquire | Relaxed)
@@ -237,12 +303,6 @@ impl<'c> Translation<'c> {
237303

238304
(_, _) => unreachable!("Did we not handle a case above??"),
239305
}
240-
.map(|(order, order_fail)| {
241-
let weak = if weak { "weak" } else { "" };
242-
let order = order_name(order);
243-
let order_fail = order_name(order_fail);
244-
format!("atomic_cxchg{weak}_{order}_{order_fail}")
245-
})
246306
.ok_or_else(|| {
247307
format_translation_err!(
248308
self.ast_context
@@ -251,7 +311,6 @@ impl<'c> Translation<'c> {
251311
)
252312
})?;
253313

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

264323
let atomic_cxchg =
265-
mk().abs_path_expr(vec!["core", "intrinsics", &intrinsic_name]);
324+
self.atomic_intrinsic_cxchg_expr(weak, order, order_fail);
266325
let call =
267326
mk().call_expr(atomic_cxchg, vec![ptr, expected.clone(), desired]);
268327
let res_name = self.renamer.borrow_mut().fresh();
@@ -305,28 +364,27 @@ impl<'c> Translation<'c> {
305364
| "__c11_atomic_fetch_or"
306365
| "__c11_atomic_fetch_nand" => {
307366
let intrinsic_name = if name.contains("_add") {
308-
"atomic_xadd"
367+
"xadd"
309368
} else if name.contains("_sub") {
310-
"atomic_xsub"
369+
"xsub"
311370
} else if name.contains("_or") {
312-
"atomic_or"
371+
"or"
313372
} else if name.contains("_xor") {
314-
"atomic_xor"
373+
"xor"
315374
} else if name.contains("_nand") {
316-
"atomic_nand"
375+
"nand"
317376
} else {
318-
"atomic_and"
377+
"and"
319378
};
320379

321-
let intrinsic_suffix = order_name(static_order(order));
322-
let intrinsic_name = format!("{intrinsic_name}_{intrinsic_suffix}");
380+
let order = static_order(order);
323381

324382
let fetch_first =
325383
name.starts_with("__atomic_fetch") || name.starts_with("__c11_atomic_fetch");
326384
let val = val1.expect("__atomic arithmetic operations must have a val argument");
327385
ptr.and_then(|ptr| {
328386
val.and_then(|val| {
329-
self.convert_atomic_op(ctx, &intrinsic_name, ptr, val, fetch_first)
387+
self.convert_atomic_op(ctx, intrinsic_name, order, ptr, val, fetch_first)
330388
})
331389
})
332390
}
@@ -338,16 +396,16 @@ impl<'c> Translation<'c> {
338396
pub(crate) fn convert_atomic_cxchg(
339397
&self,
340398
ctx: ExprContext,
341-
intrinsic_name: &str,
399+
weak: bool,
400+
order_succ: Ordering,
401+
order_fail: Ordering,
342402
dst: Box<Expr>,
343403
old_val: Box<Expr>,
344404
src_val: Box<Expr>,
345405
returns_val: bool,
346406
) -> TranslationResult<WithStmts<Box<Expr>>> {
347-
self.use_feature("core_intrinsics");
348-
349407
// Emit `atomic_cxchg(a0, a1, a2).idx`
350-
let atomic_cxchg = mk().abs_path_expr(vec!["core", "intrinsics", intrinsic_name]);
408+
let atomic_cxchg = self.atomic_intrinsic_cxchg_expr(weak, order_succ, order_fail);
351409
let call = mk().call_expr(atomic_cxchg, vec![dst, old_val, src_val]);
352410
let field_idx = if returns_val { 0 } else { 1 };
353411
let call_expr = mk().anon_field_expr(call, field_idx);
@@ -361,15 +419,14 @@ impl<'c> Translation<'c> {
361419
pub(crate) fn convert_atomic_op(
362420
&self,
363421
ctx: ExprContext,
364-
func_name: &str,
422+
base_name: &str,
423+
order: Ordering,
365424
dst: Box<Expr>,
366425
src: Box<Expr>,
367426
fetch_first: bool,
368427
) -> TranslationResult<WithStmts<Box<Expr>>> {
369-
self.use_feature("core_intrinsics");
370-
371428
// Emit `atomic_func(a0, a1) (op a1)?`
372-
let atomic_func = mk().abs_path_expr(vec!["core", "intrinsics", func_name]);
429+
let atomic_func = self.atomic_intrinsic_expr(base_name, &[order]);
373430

374431
if fetch_first {
375432
let call_expr = mk().call_expr(atomic_func, vec![dst, src]);
@@ -379,20 +436,14 @@ impl<'c> Translation<'c> {
379436
"Builtin is not supposed to be used",
380437
)
381438
} else {
382-
let (binary_op, is_nand) = if func_name.starts_with("atomic_xadd") {
383-
(BinOp::Add(Default::default()), false)
384-
} else if func_name.starts_with("atomic_xsub") {
385-
(BinOp::Sub(Default::default()), false)
386-
} else if func_name.starts_with("atomic_or") {
387-
(BinOp::BitOr(Default::default()), false)
388-
} else if func_name.starts_with("atomic_xor") {
389-
(BinOp::BitXor(Default::default()), false)
390-
} else if func_name.starts_with("atomic_nand") {
391-
(BinOp::BitAnd(Default::default()), true)
392-
} else if func_name.starts_with("atomic_and") {
393-
(BinOp::BitAnd(Default::default()), false)
394-
} else {
395-
panic!("Unexpected atomic intrinsic name: {}", func_name)
439+
let (binary_op, is_nand) = match base_name {
440+
"xadd" => (BinOp::Add(Default::default()), false),
441+
"xsub" => (BinOp::Sub(Default::default()), false),
442+
"or" => (BinOp::BitOr(Default::default()), false),
443+
"xor" => (BinOp::BitXor(Default::default()), false),
444+
"nand" => (BinOp::BitAnd(Default::default()), true),
445+
"and" => (BinOp::BitAnd(Default::default()), false),
446+
_ => panic!("Unexpected atomic intrinsic base name: {base_name}"),
396447
};
397448

398449
// Since the value of `arg1` is used twice, we need to copy

0 commit comments

Comments
 (0)