Why `compare_exchange_weak` generates loop on RISC-V?

Let's take a look at this snippet:

pub fn cmpx(a0: &AtomicU32, a1: u32, a2: u32) -> bool {
    a0.compare_exchange_weak(a1, a2, AcqRel, Relaxed).is_ok()
}

We get the following RISC-V assembly for it:

cmpx:
.LBB0_1:
        lr.w.aq a3, (a0)
        bne     a3, a1, .LBB0_3
        sc.w.rl a4, a2, (a0)
        bnez    a4, .LBB0_1
.LBB0_3:
        xor     a1, a1, a3
        seqz    a0, a1
        ret

compare_exchange_weak explicitly allows spurious failures. Its raison d'être is to allow for efficient codegen on targets with LL/SC instructions. But in the snippet we see that the compiler has generated retry loop on failed conditional stores (the bnez a4, .LBB0_1 instruction), which goes against my expectations. In other words, we get the same assembly regardless whether compare_exchange_weak or compare_exchange was used!

Is there a reason for this behavior or is it just a sub-optimal codegen by LLVM?

either this, or some arcane llvm codegen options are missing.

for the record, the compare_exchange_weak() is lowering to a single llvm operation:

; Function Attrs: mustprogress nofree norecurse nounwind nonlazybind willreturn memory(argmem: readwrite) uwtable
define noundef zeroext i1 @cmpx(ptr nocapture noundef nonnull align 4 %a0, i32 noundef %a1, i32 noundef %a2) unnamed_addr #0 {
start:
  %0 = cmpxchg weak ptr %a0, i32 %a1, i32 %a2 acq_rel monotonic, align 4
  %1 = extractvalue { i32, i1 } %0, 1
  ret i1 %1
}

The cmpxchg weak part looks like a pretty explicit way to convey our intent to LLVM. I guess I will create an LLVM issue for this.

UPD: LLVM issue: `cmpxchg weak` generates re-try loop on RISC-V · Issue #141488 · llvm/llvm-project · GitHub