[llvm-branch-commits] [llvm] [InstCombine] Canonicalize atomicrmw sub of a constant to add (PR #232626)
Harrison Hao via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Oct 16 00:04:25 PDT 2026
https://github.com/harrisonGPU created https://github.com/llvm/llvm-project/pull/232626
Rewrite `atomicrmw sub ptr, C` as `atomicrmw add ptr, -C`. This
matches the canonical form InstCombine already uses for plain `sub`.
In wrapping arithmetic $old - C = old + (-C)$, so both the stored
value and the returned old value stay the same, including when C is
`INT_MIN`. Ordering, syncscope and metadata are kept.
```llvm
; before
%old = atomicrmw sub ptr %p, i32 1 syncscope("agent") monotonic
; after
%old = atomicrmw add ptr %p, i32 -1 syncscope("agent") monotonic
```
This benefits AMDGPU, for example: https://godbolt.org/z/39xsYro3o
---
<sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
>From a494c7b7c8f0f643033bda1e1ae760df2f100224 Mon Sep 17 00:00:00 2001
From: Harrison Hao <tsworld1314 at gmail.com>
Date: Fri, 16 Oct 2026 14:35:13 +0800
Subject: [PATCH] [InstCombine] Canonicalize atomicrmw sub of a constant to add
Rewrite `atomicrmw sub ptr, C` as `atomicrmw add ptr, -C`. This
matches the canonical form InstCombine already uses for plain `sub`.
In wrapping arithmetic $old - C = old + (-C)$, so both the stored
value and the returned old value stay the same, including when C is
`INT_MIN`. Ordering, syncscope and metadata are kept.
```llvm
; before
%old = atomicrmw sub ptr %p, i32 1 syncscope("agent") monotonic
; after
%old = atomicrmw add ptr %p, i32 -1 syncscope("agent") monotonic
```
This benefits AMDGPU, for example: https://godbolt.org/z/39xsYro3o
---
llvm/lib/Transforms/InstCombine/InstCombineAtomicRMW.cpp | 7 +++++++
llvm/test/Transforms/InstCombine/atomicrmw-add-neg.ll | 4 ++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAtomicRMW.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAtomicRMW.cpp
index 4d6b0684abe4da..f9eab910fc56ae 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAtomicRMW.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAtomicRMW.cpp
@@ -134,6 +134,13 @@ Instruction *InstCombinerImpl::visitAtomicRMWInst(AtomicRMWInst &RMWI) {
: AtomicRMWInst::Add);
return replaceOperand(RMWI, 1, X);
}
+
+ // atomicrmw sub(ptr, C) -> atomicrmw add(ptr, -C)
+ const APInt *C;
+ if (Op == AtomicRMWInst::Sub && match(Val, m_APInt(C)) && !C->isZero()) {
+ RMWI.setOperation(AtomicRMWInst::Add);
+ return replaceOperand(RMWI, 1, ConstantInt::get(RMWI.getType(), -*C));
+ }
}
if (!isIdempotentRMW(RMWI))
diff --git a/llvm/test/Transforms/InstCombine/atomicrmw-add-neg.ll b/llvm/test/Transforms/InstCombine/atomicrmw-add-neg.ll
index 05f38e749910d6..48212c9df7a8d8 100644
--- a/llvm/test/Transforms/InstCombine/atomicrmw-add-neg.ll
+++ b/llvm/test/Transforms/InstCombine/atomicrmw-add-neg.ll
@@ -97,7 +97,7 @@ define i64 @fn_add_of_two_vars(ptr %p, i64 %a, i64 %b) {
define i32 @fn_sub_const(ptr %a) {
; CHECK-LABEL: define i32 @fn_sub_const(
; CHECK-SAME: ptr [[A:%.*]]) {
-; CHECK-NEXT: [[OLD:%.*]] = atomicrmw sub ptr [[A]], i32 5 syncscope("agent") acq_rel, align 4
+; CHECK-NEXT: [[OLD:%.*]] = atomicrmw add ptr [[A]], i32 -5 syncscope("agent") acq_rel, align 4
; CHECK-NEXT: ret i32 [[OLD]]
;
%old = atomicrmw sub ptr %a, i32 5 syncscope("agent") acq_rel
@@ -107,7 +107,7 @@ define i32 @fn_sub_const(ptr %a) {
define i64 @fn_sub_const_int_min(ptr %a) {
; CHECK-LABEL: define i64 @fn_sub_const_int_min(
; CHECK-SAME: ptr [[A:%.*]]) {
-; CHECK-NEXT: [[OLD:%.*]] = atomicrmw sub ptr [[A]], i64 -9223372036854775808 monotonic, align 8
+; CHECK-NEXT: [[OLD:%.*]] = atomicrmw add ptr [[A]], i64 -9223372036854775808 monotonic, align 8
; CHECK-NEXT: ret i64 [[OLD]]
;
%old = atomicrmw sub ptr %a, i64 -9223372036854775808 monotonic
More information about the llvm-branch-commits
mailing list