[llvm] [SCEV] Allow reuse of provably-disjoint `or` in canReuseInstruction (PR #216355)
Nick Riasanovsky via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 14 09:53:37 PDT 2026
https://github.com/njriasan created https://github.com/llvm/llvm-project/pull/216355
canReuseInstruction() unconditionally rejects any `or disjoint` found in the operand graph. `disjoint` is a poison-generating flag, so the generic machinery would otherwise just drop it on reuse -- but dropping it changes the instruction's meaning, since SCEV models `or disjoint x, y` as `x + y` and a plain `or` is only an add when the operands share no bits.
Relax the bail-out to the cases where disjointness cannot be re-established independently of the flag. haveNoCommonBitsSet() is queried with UseInstrInfo=false so the proof cannot rest on flags or metadata that DropPoisonGeneratingInsts is about to strip. DC is left null, so no position-dependent branch facts enter the proof either, and the reused instruction is never moved by any of the callers.
This lets SLSR strength-reduce index arithmetic in cases where InstCombine has canonicalized an `add` into an `or disjoint`.
This arose from an internal Triton use case I can't share where the increased register usages resulted in a 33% latency regression (now resolved).
Assisted-by: Codex
>From 1c60b7b4373c0ca98a6fb7a5e4b4481c0266ceec Mon Sep 17 00:00:00 2001
From: Nick Riasanovsky <njriasan at meta.com>
Date: Fri, 14 Aug 2026 09:08:30 -0700
Subject: [PATCH] [SCEV] Allow reuse of provably-disjoint `or` in
canReuseInstruction
canReuseInstruction() unconditionally rejects any `or disjoint` found in
the operand graph. `disjoint` is a poison-generating flag, so the generic
machinery would otherwise just drop it on reuse -- but dropping it changes
the instruction's meaning, since SCEV models `or disjoint x, y` as `x + y`
and a plain `or` is only an add when the operands share no bits.
Relax the bail-out to the cases where disjointness cannot be re-established
independently of the flag. haveNoCommonBitsSet() is queried with
UseInstrInfo=false so the proof cannot rest on flags or metadata that
DropPoisonGeneratingInsts is about to strip. DC is left null, so no
position-dependent branch facts enter the proof either, and the reused
instruction is never moved by any of the callers.
This lets SLSR strength-reduce index arithmetic in cases where InstCombine
has canonicalized an `add` into an `or disjoint`.
Assisted-by: Codex
---
llvm/lib/Analysis/ScalarEvolution.cpp | 13 +++++---
.../StraightLineStrengthReduce/slsr-mul.ll | 33 +++++++++++++++++++
2 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 27a1a20bcdf79..c481bab6fb3ec 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -4315,11 +4315,16 @@ bool ScalarEvolution::canReuseInstruction(
if (!I)
return false;
- // Disjoint or instructions are interpreted as adds by SCEV. However, we
- // can't replace an arbitrary add with disjoint or, even if we drop the
- // flag. We would need to convert the or into an add.
+ // Disjoint or instructions are interpreted as adds by SCEV. Reusing one is
+ // only safe if the operands are independently known not to overlap; after
+ // dropping the flag, an arbitrary or would not represent the add. Exclude
+ // instruction annotations from the proof because successful reuse may drop
+ // them below.
if (auto *PDI = dyn_cast<PossiblyDisjointInst>(I))
- if (PDI->isDisjoint())
+ if (PDI->isDisjoint() &&
+ !haveNoCommonBitsSet(PDI->getOperand(0), PDI->getOperand(1),
+ SimplifyQuery(getDataLayout(), &DT, &AC, I,
+ /*UseInstrInfo=*/false)))
return false;
// FIXME: Ignore vscale, even though it technically could be poison. Do this
diff --git a/llvm/test/Transforms/StraightLineStrengthReduce/slsr-mul.ll b/llvm/test/Transforms/StraightLineStrengthReduce/slsr-mul.ll
index 485c666a8779b..355242fbaa377 100644
--- a/llvm/test/Transforms/StraightLineStrengthReduce/slsr-mul.ll
+++ b/llvm/test/Transforms/StraightLineStrengthReduce/slsr-mul.ll
@@ -68,6 +68,39 @@ define void @or(i32 %a, i32 %s) {
ret void
}
+define void @disjoint_or_known_disjoint(i32 %a, i32 %s) {
+; CHECK-LABEL: @disjoint_or_known_disjoint(
+ %b = shl i32 %a, 4
+ %b1 = or disjoint i32 %b, 1
+; CHECK: %b1 = or i32 %b, 1
+ %mul1 = mul i32 %b1, %s
+; CHECK: %mul1 = mul i32 %b1, %s
+ call void @foo(i32 %mul1)
+
+ %b2 = add i32 %b, 2
+ %mul2 = mul i32 %b2, %s
+; CHECK-NOT: %mul2 = mul
+; CHECK: %mul2 = add i32 %mul1, %s
+ call void @foo(i32 %mul2)
+
+ ret void
+}
+
+define void @disjoint_or_may_overlap(i32 %b, i32 %s) {
+; CHECK-LABEL: @disjoint_or_may_overlap(
+ %b1 = or disjoint i32 %b, 1
+ %mul1 = mul i32 %b1, %s
+; CHECK: %mul1 = mul i32 %b1, %s
+ call void @foo(i32 %mul1)
+
+ %b2 = add i32 %b, 2
+ %mul2 = mul i32 %b2, %s
+; CHECK: %mul2 = mul i32 %b2, %s
+ call void @foo(i32 %mul2)
+
+ ret void
+}
+
; foo(a * b)
; foo((a + 1) * b)
; foo(a * (b + 1))
More information about the llvm-commits
mailing list