Skip to content

Commit 723335f

Browse files
authored
Implement __sync builtins for thumbv6-none-eabi (#1050)
This is a PR for thumbv6-none-eabi (bere-metal Armv6k in Thumb mode) which proposed to be added by rust-lang/rust#150138. Armv6k supports atomic instructions, but they are unavailable in Thumb mode unless Thumb-2 instructions available (v6t2). Using Thumb interworking (can be used via `#[instruction_set]`) allows us to use these instructions even from Thumb mode without Thumb-2 instructions, but LLVM does not implement that processing (as of LLVM 21), so this PR implements it in compiler-builtins. The code around `__sync` builtins is basically copied from `arm_linux.rs` which uses kernel_user_helpers for atomic implementation. The atomic implementation is a port of my [atomic-maybe-uninit inline assembly code]. This PR has been tested on QEMU 10.2.0 using patched compiler-builtins and core that applied the changes in this PR and rust-lang/rust#150138 and the [portable-atomic no-std test suite] (can be run with `./tools/no-std.sh thumbv6-none-eabi` on that repo) which tests wrappers around `core::sync::atomic`. (Note that the target-spec used in test sets max-atomic-width to 32 and atomic_cas to true, unlike the current rust-lang/rust#150138.) The original atomic-maybe-uninit implementation has been tested on real Arm hardware. (Note that Armv6k also supports 64-bit atomic instructions, but they are skipped here. This is because there is no corresponding code in `arm_linux.rs` (since the kernel requirements increased in 1.64, it may be possible to implement 64-bit atomics there as well. see also taiki-e/portable-atomic#82), the code becomes more complex than for 32-bit and smaller atomics.) [atomic-maybe-uninit inline assembly code]: https://github.com/taiki-e/atomic-maybe-uninit/blob/HEAD/src/arch/arm.rs [portable-atomic no-std test suite]: https://github.com/taiki-e/portable-atomic/tree/HEAD/tests/no-std-qemu
1 parent 65624df commit 723335f

7 files changed

Lines changed: 416 additions & 138 deletions

File tree

.github/workflows/main.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,24 @@ jobs:
230230
--target etc/thumbv7em-none-eabi-renamed.json \
231231
-Zbuild-std=core
232232
233+
# FIXME: move this target to test job once https://github.com/rust-lang/rust/pull/150138 merged.
234+
build-thumbv6k:
235+
name: Build thumbv6k
236+
runs-on: ubuntu-24.04
237+
timeout-minutes: 10
238+
steps:
239+
- uses: actions/checkout@v4
240+
- name: Install Rust
241+
run: |
242+
rustup update nightly --no-self-update
243+
rustup default nightly
244+
rustup component add rust-src
245+
- uses: Swatinem/rust-cache@v2
246+
- run: |
247+
cargo build -p compiler_builtins -p libm \
248+
--target etc/thumbv6-none-eabi.json \
249+
-Zbuild-std=core
250+
233251
benchmarks:
234252
name: Benchmarks
235253
timeout-minutes: 20
@@ -354,6 +372,7 @@ jobs:
354372
needs:
355373
- benchmarks
356374
- build-custom
375+
- build-thumbv6k
357376
- clippy
358377
- extensive
359378
- miri

compiler-builtins/src/lib.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub mod float;
4545
pub mod int;
4646
pub mod math;
4747
pub mod mem;
48+
pub mod sync;
4849

4950
// `libm` expects its `support` module to be available in the crate root.
5051
use math::libm_math::support;
@@ -58,13 +59,6 @@ pub mod aarch64;
5859
#[cfg(all(target_arch = "aarch64", target_feature = "outline-atomics"))]
5960
pub mod aarch64_outline_atomics;
6061

61-
#[cfg(all(
62-
kernel_user_helpers,
63-
any(target_os = "linux", target_os = "android"),
64-
target_arch = "arm"
65-
))]
66-
pub mod arm_linux;
67-
6862
#[cfg(target_arch = "avr")]
6963
pub mod avr;
7064

Lines changed: 9 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,16 @@ unsafe fn atomic_cmpxchg<T>(ptr: *mut T, oldval: u32, newval: u32) -> u32 {
125125
let (shift, mask) = get_shift_mask(ptr);
126126

127127
loop {
128-
// FIXME(safety): preconditions review needed
128+
// SAFETY: the caller must guarantee that the pointer is valid for read and write
129+
// and aligned to the element size.
129130
let curval_aligned = unsafe { atomic_load_aligned::<T>(aligned_ptr) };
130131
let curval = extract_aligned(curval_aligned, shift, mask);
131132
if curval != oldval {
132133
return curval;
133134
}
134135
let newval_aligned = insert_aligned(curval_aligned, newval, shift, mask);
135-
// FIXME(safety): preconditions review needed
136+
// SAFETY: the caller must guarantee that the pointer is valid for read and write
137+
// and aligned to the element size.
136138
if unsafe { __kuser_cmpxchg(curval_aligned, newval_aligned, aligned_ptr) } {
137139
return oldval;
138140
}
@@ -143,7 +145,8 @@ macro_rules! atomic_rmw {
143145
($name:ident, $ty:ty, $op:expr, $fetch:expr) => {
144146
intrinsics! {
145147
pub unsafe extern "C" fn $name(ptr: *mut $ty, val: $ty) -> $ty {
146-
// FIXME(safety): preconditions review needed
148+
// SAFETY: the caller must guarantee that the pointer is valid for read and write
149+
// and aligned to the element size.
147150
unsafe {
148151
atomic_rmw(
149152
ptr,
@@ -167,140 +170,15 @@ macro_rules! atomic_cmpxchg {
167170
($name:ident, $ty:ty) => {
168171
intrinsics! {
169172
pub unsafe extern "C" fn $name(ptr: *mut $ty, oldval: $ty, newval: $ty) -> $ty {
170-
// FIXME(safety): preconditions review needed
173+
// SAFETY: the caller must guarantee that the pointer is valid for read and write
174+
// and aligned to the element size.
171175
unsafe { atomic_cmpxchg(ptr, oldval as u32, newval as u32) as $ty }
172176
}
173177
}
174178
};
175179
}
176180

177-
atomic_rmw!(@old __sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b));
178-
atomic_rmw!(@old __sync_fetch_and_add_2, u16, |a: u16, b: u16| a
179-
.wrapping_add(b));
180-
atomic_rmw!(@old __sync_fetch_and_add_4, u32, |a: u32, b: u32| a
181-
.wrapping_add(b));
182-
183-
atomic_rmw!(@new __sync_add_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_add(b));
184-
atomic_rmw!(@new __sync_add_and_fetch_2, u16, |a: u16, b: u16| a
185-
.wrapping_add(b));
186-
atomic_rmw!(@new __sync_add_and_fetch_4, u32, |a: u32, b: u32| a
187-
.wrapping_add(b));
188-
189-
atomic_rmw!(@old __sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
190-
atomic_rmw!(@old __sync_fetch_and_sub_2, u16, |a: u16, b: u16| a
191-
.wrapping_sub(b));
192-
atomic_rmw!(@old __sync_fetch_and_sub_4, u32, |a: u32, b: u32| a
193-
.wrapping_sub(b));
194-
195-
atomic_rmw!(@new __sync_sub_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
196-
atomic_rmw!(@new __sync_sub_and_fetch_2, u16, |a: u16, b: u16| a
197-
.wrapping_sub(b));
198-
atomic_rmw!(@new __sync_sub_and_fetch_4, u32, |a: u32, b: u32| a
199-
.wrapping_sub(b));
200-
201-
atomic_rmw!(@old __sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b);
202-
atomic_rmw!(@old __sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b);
203-
atomic_rmw!(@old __sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b);
204-
205-
atomic_rmw!(@new __sync_and_and_fetch_1, u8, |a: u8, b: u8| a & b);
206-
atomic_rmw!(@new __sync_and_and_fetch_2, u16, |a: u16, b: u16| a & b);
207-
atomic_rmw!(@new __sync_and_and_fetch_4, u32, |a: u32, b: u32| a & b);
208-
209-
atomic_rmw!(@old __sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b);
210-
atomic_rmw!(@old __sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b);
211-
atomic_rmw!(@old __sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b);
212-
213-
atomic_rmw!(@new __sync_or_and_fetch_1, u8, |a: u8, b: u8| a | b);
214-
atomic_rmw!(@new __sync_or_and_fetch_2, u16, |a: u16, b: u16| a | b);
215-
atomic_rmw!(@new __sync_or_and_fetch_4, u32, |a: u32, b: u32| a | b);
216-
217-
atomic_rmw!(@old __sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b);
218-
atomic_rmw!(@old __sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b);
219-
atomic_rmw!(@old __sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b);
220-
221-
atomic_rmw!(@new __sync_xor_and_fetch_1, u8, |a: u8, b: u8| a ^ b);
222-
atomic_rmw!(@new __sync_xor_and_fetch_2, u16, |a: u16, b: u16| a ^ b);
223-
atomic_rmw!(@new __sync_xor_and_fetch_4, u32, |a: u32, b: u32| a ^ b);
224-
225-
atomic_rmw!(@old __sync_fetch_and_nand_1, u8, |a: u8, b: u8| !(a & b));
226-
atomic_rmw!(@old __sync_fetch_and_nand_2, u16, |a: u16, b: u16| !(a & b));
227-
atomic_rmw!(@old __sync_fetch_and_nand_4, u32, |a: u32, b: u32| !(a & b));
228-
229-
atomic_rmw!(@new __sync_nand_and_fetch_1, u8, |a: u8, b: u8| !(a & b));
230-
atomic_rmw!(@new __sync_nand_and_fetch_2, u16, |a: u16, b: u16| !(a & b));
231-
atomic_rmw!(@new __sync_nand_and_fetch_4, u32, |a: u32, b: u32| !(a & b));
232-
233-
atomic_rmw!(@old __sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b {
234-
a
235-
} else {
236-
b
237-
});
238-
atomic_rmw!(@old __sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b {
239-
a
240-
} else {
241-
b
242-
});
243-
atomic_rmw!(@old __sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b {
244-
a
245-
} else {
246-
b
247-
});
248-
249-
atomic_rmw!(@old __sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b {
250-
a
251-
} else {
252-
b
253-
});
254-
atomic_rmw!(@old __sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b {
255-
a
256-
} else {
257-
b
258-
});
259-
atomic_rmw!(@old __sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b {
260-
a
261-
} else {
262-
b
263-
});
264-
265-
atomic_rmw!(@old __sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b {
266-
a
267-
} else {
268-
b
269-
});
270-
atomic_rmw!(@old __sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b {
271-
a
272-
} else {
273-
b
274-
});
275-
atomic_rmw!(@old __sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b {
276-
a
277-
} else {
278-
b
279-
});
280-
281-
atomic_rmw!(@old __sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b {
282-
a
283-
} else {
284-
b
285-
});
286-
atomic_rmw!(@old __sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b {
287-
a
288-
} else {
289-
b
290-
});
291-
atomic_rmw!(@old __sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b {
292-
a
293-
} else {
294-
b
295-
});
296-
297-
atomic_rmw!(@old __sync_lock_test_and_set_1, u8, |_: u8, b: u8| b);
298-
atomic_rmw!(@old __sync_lock_test_and_set_2, u16, |_: u16, b: u16| b);
299-
atomic_rmw!(@old __sync_lock_test_and_set_4, u32, |_: u32, b: u32| b);
300-
301-
atomic_cmpxchg!(__sync_val_compare_and_swap_1, u8);
302-
atomic_cmpxchg!(__sync_val_compare_and_swap_2, u16);
303-
atomic_cmpxchg!(__sync_val_compare_and_swap_4, u32);
181+
include!("arm_thumb_shared.rs");
304182

305183
intrinsics! {
306184
pub unsafe extern "C" fn __sync_synchronize() {
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Used by both arm_linux.rs and thumbv6k.rs.
2+
3+
// References:
4+
// - https://llvm.org/docs/Atomics.html#libcalls-sync
5+
// - https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html
6+
// - https://refspecs.linuxfoundation.org/elf/IA64-SysV-psABI.pdf#page=58
7+
8+
atomic_rmw!(@old __sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b));
9+
atomic_rmw!(@old __sync_fetch_and_add_2, u16, |a: u16, b: u16| a
10+
.wrapping_add(b));
11+
atomic_rmw!(@old __sync_fetch_and_add_4, u32, |a: u32, b: u32| a
12+
.wrapping_add(b));
13+
14+
atomic_rmw!(@new __sync_add_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_add(b));
15+
atomic_rmw!(@new __sync_add_and_fetch_2, u16, |a: u16, b: u16| a
16+
.wrapping_add(b));
17+
atomic_rmw!(@new __sync_add_and_fetch_4, u32, |a: u32, b: u32| a
18+
.wrapping_add(b));
19+
20+
atomic_rmw!(@old __sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
21+
atomic_rmw!(@old __sync_fetch_and_sub_2, u16, |a: u16, b: u16| a
22+
.wrapping_sub(b));
23+
atomic_rmw!(@old __sync_fetch_and_sub_4, u32, |a: u32, b: u32| a
24+
.wrapping_sub(b));
25+
26+
atomic_rmw!(@new __sync_sub_and_fetch_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
27+
atomic_rmw!(@new __sync_sub_and_fetch_2, u16, |a: u16, b: u16| a
28+
.wrapping_sub(b));
29+
atomic_rmw!(@new __sync_sub_and_fetch_4, u32, |a: u32, b: u32| a
30+
.wrapping_sub(b));
31+
32+
atomic_rmw!(@old __sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b);
33+
atomic_rmw!(@old __sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b);
34+
atomic_rmw!(@old __sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b);
35+
36+
atomic_rmw!(@new __sync_and_and_fetch_1, u8, |a: u8, b: u8| a & b);
37+
atomic_rmw!(@new __sync_and_and_fetch_2, u16, |a: u16, b: u16| a & b);
38+
atomic_rmw!(@new __sync_and_and_fetch_4, u32, |a: u32, b: u32| a & b);
39+
40+
atomic_rmw!(@old __sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b);
41+
atomic_rmw!(@old __sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b);
42+
atomic_rmw!(@old __sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b);
43+
44+
atomic_rmw!(@new __sync_or_and_fetch_1, u8, |a: u8, b: u8| a | b);
45+
atomic_rmw!(@new __sync_or_and_fetch_2, u16, |a: u16, b: u16| a | b);
46+
atomic_rmw!(@new __sync_or_and_fetch_4, u32, |a: u32, b: u32| a | b);
47+
48+
atomic_rmw!(@old __sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b);
49+
atomic_rmw!(@old __sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b);
50+
atomic_rmw!(@old __sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b);
51+
52+
atomic_rmw!(@new __sync_xor_and_fetch_1, u8, |a: u8, b: u8| a ^ b);
53+
atomic_rmw!(@new __sync_xor_and_fetch_2, u16, |a: u16, b: u16| a ^ b);
54+
atomic_rmw!(@new __sync_xor_and_fetch_4, u32, |a: u32, b: u32| a ^ b);
55+
56+
atomic_rmw!(@old __sync_fetch_and_nand_1, u8, |a: u8, b: u8| !(a & b));
57+
atomic_rmw!(@old __sync_fetch_and_nand_2, u16, |a: u16, b: u16| !(a & b));
58+
atomic_rmw!(@old __sync_fetch_and_nand_4, u32, |a: u32, b: u32| !(a & b));
59+
60+
atomic_rmw!(@new __sync_nand_and_fetch_1, u8, |a: u8, b: u8| !(a & b));
61+
atomic_rmw!(@new __sync_nand_and_fetch_2, u16, |a: u16, b: u16| !(a & b));
62+
atomic_rmw!(@new __sync_nand_and_fetch_4, u32, |a: u32, b: u32| !(a & b));
63+
64+
atomic_rmw!(@old __sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b {
65+
a
66+
} else {
67+
b
68+
});
69+
atomic_rmw!(@old __sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b {
70+
a
71+
} else {
72+
b
73+
});
74+
atomic_rmw!(@old __sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b {
75+
a
76+
} else {
77+
b
78+
});
79+
80+
atomic_rmw!(@old __sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b {
81+
a
82+
} else {
83+
b
84+
});
85+
atomic_rmw!(@old __sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b {
86+
a
87+
} else {
88+
b
89+
});
90+
atomic_rmw!(@old __sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b {
91+
a
92+
} else {
93+
b
94+
});
95+
96+
atomic_rmw!(@old __sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b {
97+
a
98+
} else {
99+
b
100+
});
101+
atomic_rmw!(@old __sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b {
102+
a
103+
} else {
104+
b
105+
});
106+
atomic_rmw!(@old __sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b {
107+
a
108+
} else {
109+
b
110+
});
111+
112+
atomic_rmw!(@old __sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b {
113+
a
114+
} else {
115+
b
116+
});
117+
atomic_rmw!(@old __sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b {
118+
a
119+
} else {
120+
b
121+
});
122+
atomic_rmw!(@old __sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b {
123+
a
124+
} else {
125+
b
126+
});
127+
128+
atomic_rmw!(@old __sync_lock_test_and_set_1, u8, |_: u8, b: u8| b);
129+
atomic_rmw!(@old __sync_lock_test_and_set_2, u16, |_: u16, b: u16| b);
130+
atomic_rmw!(@old __sync_lock_test_and_set_4, u32, |_: u32, b: u32| b);
131+
132+
atomic_cmpxchg!(__sync_val_compare_and_swap_1, u8);
133+
atomic_cmpxchg!(__sync_val_compare_and_swap_2, u16);
134+
atomic_cmpxchg!(__sync_val_compare_and_swap_4, u32);

compiler-builtins/src/sync/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#[cfg(all(
2+
kernel_user_helpers,
3+
any(target_os = "linux", target_os = "android"),
4+
target_arch = "arm"
5+
))]
6+
pub mod arm_linux;
7+
8+
// Armv6k supports atomic instructions, but they are unavailable in Thumb mode
9+
// unless Thumb-2 instructions available (v6t2).
10+
// Using Thumb interworking allows us to use these instructions even from Thumb mode
11+
// without Thumb-2 instructions, but LLVM does not implement that processing (as of LLVM 21),
12+
// so we implement it here at this time.
13+
// (`not(target_feature = "mclass")` is unneeded because v6k is not set on thumbv6m.)
14+
#[cfg(all(
15+
target_arch = "arm",
16+
target_feature = "thumb-mode",
17+
target_feature = "v6k",
18+
not(target_feature = "v6t2"),
19+
))]
20+
pub mod thumbv6k;

0 commit comments

Comments
 (0)