llvm generates UNPREDICTABLE cmp-T2 opcode for cmpxchg thumb
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
I tried this code:
#![no_main]
#![no_std]
use core::sync::atomic::{AtomicUsize, Ordering};
#[repr(usize)]
enum BoundToThreadStage {
Unbound = 0,
Binding = 1,
Bound = 2,
}
static BOUND_TO_THREAD: AtomicUsize =
AtomicUsize::new(BoundToThreadStage::Unbound as usize);
pub fn try_begin_binding() -> bool {
BOUND_TO_THREAD
.compare_exchange(
BoundToThreadStage::Unbound as usize, // expected
BoundToThreadStage::Binding as usize, // new
Ordering::Relaxed, // success
Ordering::Relaxed, // failure
)
.is_ok()
}
pub fn finish_binding() {
BOUND_TO_THREAD.store(BoundToThreadStage::Bound as usize, Ordering::Relaxed);
}
#[panic_handler]
pub unsafe fn panic_fmt(_pi: &core::panic::PanicInfo) -> ! {
loop{};
}
#[used]
static USED: fn()->bool = try_begin_binding;
Compiled like this:
rustc \
--target thumbv8m.main-none-eabi \
-C opt-level=0 -C debuginfo=0 -C panic=abort \
--edition 2024 \
-o wrong-cmp-t2 \
wrong-cmp-t2.rs && arm-none-eabi-objdump -D wrong-cmp-t2 > wrong-cmp-t2.S
This code generates the llvm-ir instruction cmpxchg. I think this is compiled to
20340: e853 1f00 ldrex r1, [r3]
20344: 4501 cmp r1, r0
20346: d103 bne.n 20350 <_atomic_compare_exchange+0xf4>
20348: e843 c200 strex r2, ip, [r3]
I expected to see this happen: cmp should be encoded as 0x4281
20344: 4281 cmp r1, r0
Instead, this happened:
20344: 4501 cmp r1, r0
This opcode is wrong as the documentation for cmp T2 states that Rm and Rn cannot be both from r0-r7.
https://developer.arm.com/documentation/ddi0553/latest/
I think this issue was never reported before as thumb code is mostly compiled using size optimization e.g. -Os.
Meta
rustc --version --verbose:
rustc 1.95.0 (59807616e 2026-04-14)
binary: rustc
commit-hash: 59807616e1fa2540724bfbac14d7976d7e4a3860
commit-date: 2026-04-14
host: x86_64-unknown-linux-gnu
release: 1.95.0
LLVM version: 22.1.2
Same error in nightly version 2026-04-21
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No repository file or test is named in the report. Start by reproducing the provided rustc command for thumbv8m.main-none-eabi and trace the LLVM cmpxchg path into Thumb assembly; done means cmp uses a legal encoding such as 0x4281 rather than the unpredictable 0x4501 opcode, with a regression test covering the case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100