rust-lang / rust-lang/rust

llvm generates UNPREDICTABLE cmp-T2 opcode for cmpxchg thumb

Open
#157,956 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-atomic A-LLVM C-bug O-Arm S-waiting-on-LLVM T-compiler
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/
Image

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.