[llvm] [Reassociate] Use UniformityInfo to group uniform operands together (PR #198507)

Pankaj Dwivedi via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 03:25:47 PDT 2026


https://github.com/PankajDwivedi-25 updated https://github.com/llvm/llvm-project/pull/198507

>From afdecf8101295b097b75df1eef52450c18d8b840 Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Tue, 19 May 2026 16:54:55 +0530
Subject: [PATCH 1/6] [NFC] pre-commit test

---
 llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll    | 114 ++++++++++++++
 .../CodeGen/AMDGPU/reassoc-uniform-e2e.ll     | 141 ++++++++++++++++++
 2 files changed, 255 insertions(+)
 create mode 100644 llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll

diff --git a/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll b/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll
index 8da7c293e4ea2..8ea4ab9ec5ae4 100644
--- a/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll
+++ b/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll
@@ -125,5 +125,119 @@ bb:
   ret void
 }
 
+; Multiply: (x * tid) * y -- backend does NOT reassociate mul to scalar.
+; Both muls use VALU even though x and y are uniform.
+; GCN-LABEL: reassoc_mul_i32:
+; GCN-NOT: s_mul
+; GCN: v_mul_lo_u32
+; GCN: v_mul_lo_u32
+define amdgpu_kernel void @reassoc_mul_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %mul1 = mul i32 %x, %tid
+  %mul2 = mul i32 %mul1, %y
+  store i32 %mul2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; Multiply with uniforms pre-grouped: (x * y) * tid -- produces s_mul + v_mul.
+; GCN-LABEL: reassoc_mul_i32_uniform_grouped:
+; GCN: s_mul_i32 [[MUL1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
+; GCN: v_mul_lo_u32 v{{[0-9]+}}, [[MUL1]], v{{[0-9]+}}
+define amdgpu_kernel void @reassoc_mul_i32_uniform_grouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %mul1 = mul i32 %x, %y
+  %mul2 = mul i32 %mul1, %tid
+  store i32 %mul2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; OR: (x | tid) | y -- backend does NOT reassociate or to scalar.
+; Both ors use VALU.
+; GCN-LABEL: reassoc_or_i32:
+; GCN-NOT: s_or
+; GCN: v_or_b32_e32
+; GCN: v_or_b32_e32
+define amdgpu_kernel void @reassoc_or_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %or1 = or i32 %x, %tid
+  %or2 = or i32 %or1, %y
+  store i32 %or2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; OR with uniforms pre-grouped: (x | y) | tid -- produces s_or + v_or.
+; GCN-LABEL: reassoc_or_i32_uniform_grouped:
+; GCN: s_or_b32 [[OR1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
+; GCN: v_or_b32_e32 v{{[0-9]+}}, [[OR1]], v{{[0-9]+}}
+define amdgpu_kernel void @reassoc_or_i32_uniform_grouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %or1 = or i32 %x, %y
+  %or2 = or i32 %or1, %tid
+  store i32 %or2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; AND: (x & tid) & y -- backend does NOT reassociate and to scalar.
+; Both ands use VALU.
+; GCN-LABEL: reassoc_and_i32:
+; GCN-NOT: s_and
+; GCN: v_and_b32_e32
+; GCN: v_and_b32_e32
+define amdgpu_kernel void @reassoc_and_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %and1 = and i32 %x, %tid
+  %and2 = and i32 %and1, %y
+  store i32 %and2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; AND with uniforms pre-grouped: (x & y) & tid -- produces s_and + v_and.
+; GCN-LABEL: reassoc_and_i32_uniform_grouped:
+; GCN: s_and_b32 [[AND1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
+; GCN: v_and_b32_e32 v{{[0-9]+}}, [[AND1]], v{{[0-9]+}}
+define amdgpu_kernel void @reassoc_and_i32_uniform_grouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %and1 = and i32 %x, %y
+  %and2 = and i32 %and1, %tid
+  store i32 %and2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; XOR: (x ^ tid) ^ y -- backend ALREADY reassociates xor to scalar.
+; The DAG combiner handles this, producing s_xor + v_xor for both variants.
+; GCN-LABEL: reassoc_xor_i32:
+; GCN: s_xor_b32 [[XOR1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
+; GCN: v_xor_b32_e32 v{{[0-9]+}}, [[XOR1]], v{{[0-9]+}}
+define amdgpu_kernel void @reassoc_xor_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %xor1 = xor i32 %x, %tid
+  %xor2 = xor i32 %xor1, %y
+  store i32 %xor2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; Multi-use: intermediate (x * tid) is stored, blocks reassociation.
+; Both muls use VALU.
+; GCN-LABEL: reassoc_mul_i32_multiuse:
+; GCN-NOT: s_mul
+; GCN: v_mul_lo_u32
+; GCN: v_mul_lo_u32
+define amdgpu_kernel void @reassoc_mul_i32_multiuse(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+bb:
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %mul1 = mul i32 %x, %tid
+  %mul2 = mul i32 %mul1, %y
+  store volatile i32 %mul1, ptr addrspace(1) %arg, align 4
+  store volatile i32 %mul2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
 declare i32 @llvm.amdgcn.workitem.id.x()
 declare i32 @llvm.amdgcn.workitem.id.y()
diff --git a/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll b/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll
new file mode 100644
index 0000000000000..f3fad83ae1f79
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll
@@ -0,0 +1,141 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=reassociate -S < %s | \
+; RUN:   llc -mtriple=amdgcn -mcpu=gfx900 | FileCheck %s
+;
+; End-to-end test: Reassociate pass followed by llc codegen.
+; Tests that uniform operands grouped by IR-level reassociation produce
+; scalar ALU instructions in the AMDGPU backend.
+
+; 3-operand: (uniform * divergent) * uniform
+define amdgpu_kernel void @mul_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+; CHECK-LABEL: mul_ungrouped:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; CHECK-NEXT:    v_mov_b32_e32 v1, 0
+; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    v_mul_lo_u32 v0, s2, v0
+; CHECK-NEXT:    v_mul_lo_u32 v0, v0, s3
+; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
+; CHECK-NEXT:    s_endpgm
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %op1 = mul i32 %x, %tid
+  %op2 = mul i32 %op1, %y
+  store i32 %op2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; 3-operand: (uniform | divergent) | uniform
+define amdgpu_kernel void @or_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+; CHECK-LABEL: or_ungrouped:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; CHECK-NEXT:    v_mov_b32_e32 v1, 0
+; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    v_or_b32_e32 v0, s2, v0
+; CHECK-NEXT:    v_or_b32_e32 v0, s3, v0
+; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
+; CHECK-NEXT:    s_endpgm
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %op1 = or i32 %x, %tid
+  %op2 = or i32 %op1, %y
+  store i32 %op2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; 3-operand: (uniform & divergent) & uniform
+define amdgpu_kernel void @and_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+; CHECK-LABEL: and_ungrouped:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; CHECK-NEXT:    v_mov_b32_e32 v1, 0
+; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    v_and_b32_e32 v0, s2, v0
+; CHECK-NEXT:    v_and_b32_e32 v0, s3, v0
+; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
+; CHECK-NEXT:    s_endpgm
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %op1 = and i32 %x, %tid
+  %op2 = and i32 %op1, %y
+  store i32 %op2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; 3-operand: (uniform + divergent) + uniform -- backend already handles add
+define amdgpu_kernel void @add_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+; CHECK-LABEL: add_ungrouped:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; CHECK-NEXT:    v_mov_b32_e32 v1, 0
+; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    s_add_i32 s2, s3, s2
+; CHECK-NEXT:    v_add_u32_e32 v0, s2, v0
+; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
+; CHECK-NEXT:    s_endpgm
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %op1 = add i32 %x, %tid
+  %op2 = add i32 %op1, %y
+  store i32 %op2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; 3-operand: (uniform ^ divergent) ^ uniform -- backend already handles xor
+define amdgpu_kernel void @xor_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
+; CHECK-LABEL: xor_ungrouped:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; CHECK-NEXT:    v_mov_b32_e32 v1, 0
+; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    s_xor_b32 s2, s3, s2
+; CHECK-NEXT:    v_xor_b32_e32 v0, s2, v0
+; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
+; CHECK-NEXT:    s_endpgm
+  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %op1 = xor i32 %x, %tid
+  %op2 = xor i32 %op1, %y
+  store i32 %op2, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; 4-operand: (u1 * d1) * (u2 * d2)
+define amdgpu_kernel void @mul_4op(ptr addrspace(1) %arg, i32 %u1, i32 %u2) {
+; CHECK-LABEL: mul_4op:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; CHECK-NEXT:    v_mul_u32_u24_e32 v0, v1, v0
+; CHECK-NEXT:    v_mov_b32_e32 v1, 0
+; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    v_mul_lo_u32 v0, v0, s2
+; CHECK-NEXT:    v_mul_lo_u32 v0, v0, s3
+; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
+; CHECK-NEXT:    s_endpgm
+  %d1 = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %d2 = tail call i32 @llvm.amdgcn.workitem.id.y()
+  %op1 = mul i32 %u1, %d1
+  %op2 = mul i32 %u2, %d2
+  %op3 = mul i32 %op1, %op2
+  store i32 %op3, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+; 4-operand: (u1 | d1) | (u2 | d2)
+define amdgpu_kernel void @or_4op(ptr addrspace(1) %arg, i32 %u1, i32 %u2) {
+; CHECK-LABEL: or_4op:
+; CHECK:       ; %bb.0:
+; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
+; CHECK-NEXT:    v_mov_b32_e32 v2, 0
+; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    v_or3_b32 v0, v1, v0, s2
+; CHECK-NEXT:    v_or_b32_e32 v0, s3, v0
+; CHECK-NEXT:    global_store_dword v2, v0, s[0:1]
+; CHECK-NEXT:    s_endpgm
+  %d1 = tail call i32 @llvm.amdgcn.workitem.id.x()
+  %d2 = tail call i32 @llvm.amdgcn.workitem.id.y()
+  %op1 = or i32 %u1, %d1
+  %op2 = or i32 %u2, %d2
+  %op3 = or i32 %op1, %op2
+  store i32 %op3, ptr addrspace(1) %arg, align 4
+  ret void
+}
+
+declare i32 @llvm.amdgcn.workitem.id.x()
+declare i32 @llvm.amdgcn.workitem.id.y()

>From bd67a6eb97f06e1b2e3c2fd21e99b0d8df6db2ee Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Tue, 19 May 2026 17:31:01 +0530
Subject: [PATCH 2/6] [Reassociate] Use UniformityInfo to group uniform
 operands together

---
 .../llvm/Transforms/Scalar/Reassociate.h      |  6 ++-
 llvm/lib/Transforms/Scalar/Reassociate.cpp    | 45 ++++++++++++++++++-
 .../CodeGen/AMDGPU/reassoc-uniform-e2e.ll     | 18 ++++----
 3 files changed, 57 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Scalar/Reassociate.h b/llvm/include/llvm/Transforms/Scalar/Reassociate.h
index 267139f30fce6..3ecd8c13f7a41 100644
--- a/llvm/include/llvm/Transforms/Scalar/Reassociate.h
+++ b/llvm/include/llvm/Transforms/Scalar/Reassociate.h
@@ -25,6 +25,7 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/PostOrderIterator.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/Analysis/UniformityAnalysis.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/PassManager.h"
 #include "llvm/IR/ValueHandle.h"
@@ -95,9 +96,12 @@ class ReassociatePass : public OptionalPassInfoMixin<ReassociatePass> {
   DenseMap<std::pair<Value *, Value *>, PairMapValue> PairMap[NumBinaryOps];
 
   bool MadeChange;
+  UniformityInfo *UA = nullptr;
 
 public:
-  LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &);
+  bool SkipUniformityAnalysis = false;
+
+  LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
 
 private:
   void BuildRankMap(Function &F, ReversePostOrderTraversal<Function *> &RPOT);
diff --git a/llvm/lib/Transforms/Scalar/Reassociate.cpp b/llvm/lib/Transforms/Scalar/Reassociate.cpp
index ea6c394740f22..f8205d7b70984 100644
--- a/llvm/lib/Transforms/Scalar/Reassociate.cpp
+++ b/llvm/lib/Transforms/Scalar/Reassociate.cpp
@@ -31,6 +31,7 @@
 #include "llvm/Analysis/BasicAliasAnalysis.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/GlobalsModRef.h"
+#include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/BasicBlock.h"
@@ -2294,6 +2295,33 @@ void ReassociatePass::ReassociateExpression(BinaryOperator *I) {
 
   LLVM_DEBUG(dbgs() << "RAIn:\t"; PrintOps(I, Ops); dbgs() << '\n');
 
+  // On targets that report branch divergence (TTI.hasBranchDivergence()),
+  // boost the rank of divergent operands so they sort towards the root of
+  // the expression tree.  This clusters uniform operands together at the
+  // leaves, forming sub-expressions whose operands are all uniform and that
+  // can be evaluated in the target's uniform-execution domain.
+  //
+  // Example: (uniform1 + divergent) + uniform2
+  //       -> (uniform1 + uniform2) + divergent
+  if (UA && Ops.size() > 2) {
+    constexpr unsigned DivergentRankOffset = 1U << 28;
+    BasicBlock *ParentBB = I->getParent();
+    for (ValueEntry &Entry : Ops) {
+      if (isa<Constant>(Entry.Op))
+        continue;
+      bool Divergent = false;
+      for (const Use &U : Entry.Op->uses()) {
+        Instruction *Usr = dyn_cast<Instruction>(U.getUser());
+        if (Usr && Usr->getParent() == ParentBB) {
+          Divergent = UA->isDivergentAtUse(U);
+          break;
+        }
+      }
+      if (Divergent)
+        Entry.Rank += DivergentRankOffset;
+    }
+  }
+
   // Now that we have linearized the tree to a list and have gathered all of
   // the operands and their ranks, sort the operands by their rank.  Use a
   // stable_sort so that values with equal ranks will have their relative
@@ -2543,7 +2571,17 @@ ReassociatePass::BuildPairMap(ReversePostOrderTraversal<Function *> &RPOT) {
   }
 }
 
-PreservedAnalyses ReassociatePass::run(Function &F, FunctionAnalysisManager &) {
+PreservedAnalyses ReassociatePass::run(Function &F,
+                                       FunctionAnalysisManager &AM) {
+  // On targets with branch divergence, obtain UniformityInfo so we can group
+  // uniform operands together in expression trees.
+  UA = nullptr;
+  if (!SkipUniformityAnalysis) {
+    const TargetTransformInfo &TTI = AM.getResult<TargetIRAnalysis>(F);
+    if (TTI.hasBranchDivergence(&F))
+      UA = &AM.getResult<UniformityInfoAnalysis>(F);
+  }
+
   // Get the functions basic blocks in Reverse Post Order. This order is used by
   // BuildRankMap to pre calculate ranks correctly. It also excludes dead basic
   // blocks (it has been seen that the analysis in this pass could hang when
@@ -2606,15 +2644,17 @@ PreservedAnalyses ReassociatePass::run(Function &F, FunctionAnalysisManager &) {
     }
   }
 
-  // We are done with the rank map and pair map.
+  // We are done with the rank map, pair map, and uniformity info.
   RankMap.clear();
   ValueRankMap.clear();
   for (auto &Entry : PairMap)
     Entry.clear();
+  UA = nullptr;
 
   if (MadeChange) {
     PreservedAnalyses PA;
     PA.preserveSet<CFGAnalyses>();
+    PA.preserve<UniformityInfoAnalysis>();
     return PA;
   }
 
@@ -2637,6 +2677,7 @@ class ReassociateLegacyPass : public FunctionPass {
     if (skipFunction(F))
       return false;
 
+    Impl.SkipUniformityAnalysis = true;
     FunctionAnalysisManager DummyFAM;
     auto PA = Impl.run(F, DummyFAM);
     return !PA.areAllPreserved();
diff --git a/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll b/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll
index f3fad83ae1f79..eb5b0e9ae9353 100644
--- a/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll
+++ b/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll
@@ -13,8 +13,8 @@ define amdgpu_kernel void @mul_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y)
 ; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
 ; CHECK-NEXT:    v_mov_b32_e32 v1, 0
 ; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    s_mul_i32 s2, s3, s2
 ; CHECK-NEXT:    v_mul_lo_u32 v0, s2, v0
-; CHECK-NEXT:    v_mul_lo_u32 v0, v0, s3
 ; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
 ; CHECK-NEXT:    s_endpgm
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
@@ -31,8 +31,8 @@ define amdgpu_kernel void @or_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
 ; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
 ; CHECK-NEXT:    v_mov_b32_e32 v1, 0
 ; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    s_or_b32 s2, s3, s2
 ; CHECK-NEXT:    v_or_b32_e32 v0, s2, v0
-; CHECK-NEXT:    v_or_b32_e32 v0, s3, v0
 ; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
 ; CHECK-NEXT:    s_endpgm
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
@@ -49,8 +49,8 @@ define amdgpu_kernel void @and_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y)
 ; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
 ; CHECK-NEXT:    v_mov_b32_e32 v1, 0
 ; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
+; CHECK-NEXT:    s_and_b32 s2, s3, s2
 ; CHECK-NEXT:    v_and_b32_e32 v0, s2, v0
-; CHECK-NEXT:    v_and_b32_e32 v0, s3, v0
 ; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
 ; CHECK-NEXT:    s_endpgm
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
@@ -101,11 +101,11 @@ define amdgpu_kernel void @mul_4op(ptr addrspace(1) %arg, i32 %u1, i32 %u2) {
 ; CHECK-LABEL: mul_4op:
 ; CHECK:       ; %bb.0:
 ; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    v_mul_u32_u24_e32 v0, v1, v0
-; CHECK-NEXT:    v_mov_b32_e32 v1, 0
 ; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    v_mul_lo_u32 v0, v0, s2
-; CHECK-NEXT:    v_mul_lo_u32 v0, v0, s3
+; CHECK-NEXT:    s_mul_i32 s2, s3, s2
+; CHECK-NEXT:    v_mul_lo_u32 v0, s2, v0
+; CHECK-NEXT:    v_mul_lo_u32 v0, v0, v1
+; CHECK-NEXT:    v_mov_b32_e32 v1, 0
 ; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
 ; CHECK-NEXT:    s_endpgm
   %d1 = tail call i32 @llvm.amdgcn.workitem.id.x()
@@ -124,8 +124,8 @@ define amdgpu_kernel void @or_4op(ptr addrspace(1) %arg, i32 %u1, i32 %u2) {
 ; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
 ; CHECK-NEXT:    v_mov_b32_e32 v2, 0
 ; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    v_or3_b32 v0, v1, v0, s2
-; CHECK-NEXT:    v_or_b32_e32 v0, s3, v0
+; CHECK-NEXT:    s_or_b32 s2, s3, s2
+; CHECK-NEXT:    v_or3_b32 v0, s2, v0, v1
 ; CHECK-NEXT:    global_store_dword v2, v0, s[0:1]
 ; CHECK-NEXT:    s_endpgm
   %d1 = tail call i32 @llvm.amdgcn.workitem.id.x()

>From 166d2e42fd59bd4faef3bd0ce8d95d2d7fa5bb6c Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Thu, 18 Jun 2026 14:02:25 +0530
Subject: [PATCH 3/6] review

---
 llvm/lib/Transforms/Scalar/Reassociate.cpp |  1 -
 llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll | 33 +++++++++++-----------
 2 files changed, 16 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/Reassociate.cpp b/llvm/lib/Transforms/Scalar/Reassociate.cpp
index f8205d7b70984..1efa1a5036495 100644
--- a/llvm/lib/Transforms/Scalar/Reassociate.cpp
+++ b/llvm/lib/Transforms/Scalar/Reassociate.cpp
@@ -2654,7 +2654,6 @@ PreservedAnalyses ReassociatePass::run(Function &F,
   if (MadeChange) {
     PreservedAnalyses PA;
     PA.preserveSet<CFGAnalyses>();
-    PA.preserve<UniformityInfoAnalysis>();
     return PA;
   }
 
diff --git a/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll b/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll
index 8ea4ab9ec5ae4..6bb21166364db 100644
--- a/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll
+++ b/llvm/test/CodeGen/AMDGPU/reassoc-scalar.ll
@@ -127,12 +127,13 @@ bb:
 
 ; Multiply: (x * tid) * y -- backend does NOT reassociate mul to scalar.
 ; Both muls use VALU even though x and y are uniform.
+; The reassociate pass groups the uniform operands (x * y) so this
+; lowers to s_mul + v_mul; see reassoc-uniform-e2e.ll.
 ; GCN-LABEL: reassoc_mul_i32:
 ; GCN-NOT: s_mul
 ; GCN: v_mul_lo_u32
 ; GCN: v_mul_lo_u32
-define amdgpu_kernel void @reassoc_mul_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_mul_i32(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %mul1 = mul i32 %x, %tid
   %mul2 = mul i32 %mul1, %y
@@ -144,8 +145,7 @@ bb:
 ; GCN-LABEL: reassoc_mul_i32_uniform_grouped:
 ; GCN: s_mul_i32 [[MUL1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
 ; GCN: v_mul_lo_u32 v{{[0-9]+}}, [[MUL1]], v{{[0-9]+}}
-define amdgpu_kernel void @reassoc_mul_i32_uniform_grouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_mul_i32_uniform_grouped(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %mul1 = mul i32 %x, %y
   %mul2 = mul i32 %mul1, %tid
@@ -155,12 +155,13 @@ bb:
 
 ; OR: (x | tid) | y -- backend does NOT reassociate or to scalar.
 ; Both ors use VALU.
+; The reassociate pass groups the uniform operands (x | y) so this
+; lowers to s_or + v_or; see reassoc-uniform-e2e.ll.
 ; GCN-LABEL: reassoc_or_i32:
 ; GCN-NOT: s_or
 ; GCN: v_or_b32_e32
 ; GCN: v_or_b32_e32
-define amdgpu_kernel void @reassoc_or_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_or_i32(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %or1 = or i32 %x, %tid
   %or2 = or i32 %or1, %y
@@ -172,8 +173,7 @@ bb:
 ; GCN-LABEL: reassoc_or_i32_uniform_grouped:
 ; GCN: s_or_b32 [[OR1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
 ; GCN: v_or_b32_e32 v{{[0-9]+}}, [[OR1]], v{{[0-9]+}}
-define amdgpu_kernel void @reassoc_or_i32_uniform_grouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_or_i32_uniform_grouped(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %or1 = or i32 %x, %y
   %or2 = or i32 %or1, %tid
@@ -183,12 +183,13 @@ bb:
 
 ; AND: (x & tid) & y -- backend does NOT reassociate and to scalar.
 ; Both ands use VALU.
+; The reassociate pass groups the uniform operands (x & y) so this
+; lowers to s_and + v_and; see reassoc-uniform-e2e.ll.
 ; GCN-LABEL: reassoc_and_i32:
 ; GCN-NOT: s_and
 ; GCN: v_and_b32_e32
 ; GCN: v_and_b32_e32
-define amdgpu_kernel void @reassoc_and_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_and_i32(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %and1 = and i32 %x, %tid
   %and2 = and i32 %and1, %y
@@ -200,8 +201,7 @@ bb:
 ; GCN-LABEL: reassoc_and_i32_uniform_grouped:
 ; GCN: s_and_b32 [[AND1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
 ; GCN: v_and_b32_e32 v{{[0-9]+}}, [[AND1]], v{{[0-9]+}}
-define amdgpu_kernel void @reassoc_and_i32_uniform_grouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_and_i32_uniform_grouped(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %and1 = and i32 %x, %y
   %and2 = and i32 %and1, %tid
@@ -214,8 +214,7 @@ bb:
 ; GCN-LABEL: reassoc_xor_i32:
 ; GCN: s_xor_b32 [[XOR1:s[0-9]+]], s{{[0-9]+}}, s{{[0-9]+}}
 ; GCN: v_xor_b32_e32 v{{[0-9]+}}, [[XOR1]], v{{[0-9]+}}
-define amdgpu_kernel void @reassoc_xor_i32(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_xor_i32(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %xor1 = xor i32 %x, %tid
   %xor2 = xor i32 %xor1, %y
@@ -224,13 +223,13 @@ bb:
 }
 
 ; Multi-use: intermediate (x * tid) is stored, blocks reassociation.
-; Both muls use VALU.
+; Both muls use VALU. The reassociate pass cannot group the uniforms here
+; because the intermediate result is used more than once.
 ; GCN-LABEL: reassoc_mul_i32_multiuse:
 ; GCN-NOT: s_mul
 ; GCN: v_mul_lo_u32
 ; GCN: v_mul_lo_u32
-define amdgpu_kernel void @reassoc_mul_i32_multiuse(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-bb:
+define void @reassoc_mul_i32_multiuse(ptr addrspace(1) inreg %arg, i32 inreg %x, i32 inreg %y) {
   %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
   %mul1 = mul i32 %x, %tid
   %mul2 = mul i32 %mul1, %y

>From f782f5437e7504deea980708d8e156cc8800c912 Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Mon, 22 Jun 2026 14:20:17 +0530
Subject: [PATCH 4/6] review

---
 llvm/include/llvm/Transforms/Scalar/Reassociate.h | 7 ++++++-
 llvm/lib/Transforms/Scalar/Reassociate.cpp        | 5 ++---
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Scalar/Reassociate.h b/llvm/include/llvm/Transforms/Scalar/Reassociate.h
index 3ecd8c13f7a41..a665b38f75777 100644
--- a/llvm/include/llvm/Transforms/Scalar/Reassociate.h
+++ b/llvm/include/llvm/Transforms/Scalar/Reassociate.h
@@ -98,8 +98,13 @@ class ReassociatePass : public OptionalPassInfoMixin<ReassociatePass> {
   bool MadeChange;
   UniformityInfo *UA = nullptr;
 
+  // When set, UniformityInfo is not fetched in run(). This is used by the
+  // legacy pass manager, which cannot provide the required analyses.
+  bool SkipUniformityAnalysis;
+
 public:
-  bool SkipUniformityAnalysis = false;
+  ReassociatePass(bool SkipUniformityAnalysis = false)
+      : SkipUniformityAnalysis(SkipUniformityAnalysis) {}
 
   LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
 
diff --git a/llvm/lib/Transforms/Scalar/Reassociate.cpp b/llvm/lib/Transforms/Scalar/Reassociate.cpp
index 1efa1a5036495..b4034f246fd83 100644
--- a/llvm/lib/Transforms/Scalar/Reassociate.cpp
+++ b/llvm/lib/Transforms/Scalar/Reassociate.cpp
@@ -2575,7 +2575,6 @@ PreservedAnalyses ReassociatePass::run(Function &F,
                                        FunctionAnalysisManager &AM) {
   // On targets with branch divergence, obtain UniformityInfo so we can group
   // uniform operands together in expression trees.
-  UA = nullptr;
   if (!SkipUniformityAnalysis) {
     const TargetTransformInfo &TTI = AM.getResult<TargetIRAnalysis>(F);
     if (TTI.hasBranchDivergence(&F))
@@ -2668,7 +2667,8 @@ class ReassociateLegacyPass : public FunctionPass {
 public:
   static char ID; // Pass identification, replacement for typeid
 
-  ReassociateLegacyPass() : FunctionPass(ID) {
+  ReassociateLegacyPass()
+      : FunctionPass(ID), Impl(/*SkipUniformityAnalysis=*/true) {
     initializeReassociateLegacyPassPass(*PassRegistry::getPassRegistry());
   }
 
@@ -2676,7 +2676,6 @@ class ReassociateLegacyPass : public FunctionPass {
     if (skipFunction(F))
       return false;
 
-    Impl.SkipUniformityAnalysis = true;
     FunctionAnalysisManager DummyFAM;
     auto PA = Impl.run(F, DummyFAM);
     return !PA.areAllPreserved();

>From 02217683a76bfc7dd0cf75079cb3a95ad79a920d Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Wed, 8 Jul 2026 13:48:56 +0530
Subject: [PATCH 5/6] review

---
 .../llvm/Transforms/Scalar/Reassociate.h      | 13 +++---
 llvm/lib/Transforms/Scalar/Reassociate.cpp    | 44 ++++++++++++++-----
 2 files changed, 38 insertions(+), 19 deletions(-)

diff --git a/llvm/include/llvm/Transforms/Scalar/Reassociate.h b/llvm/include/llvm/Transforms/Scalar/Reassociate.h
index a665b38f75777..e87957b0b435e 100644
--- a/llvm/include/llvm/Transforms/Scalar/Reassociate.h
+++ b/llvm/include/llvm/Transforms/Scalar/Reassociate.h
@@ -98,16 +98,15 @@ class ReassociatePass : public OptionalPassInfoMixin<ReassociatePass> {
   bool MadeChange;
   UniformityInfo *UA = nullptr;
 
-  // When set, UniformityInfo is not fetched in run(). This is used by the
-  // legacy pass manager, which cannot provide the required analyses.
-  bool SkipUniformityAnalysis;
-
 public:
-  ReassociatePass(bool SkipUniformityAnalysis = false)
-      : SkipUniformityAnalysis(SkipUniformityAnalysis) {}
-
   LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
 
+  // Runs the reassociation algorithm. \p UI carries uniformity information when
+  // available (e.g. on targets with branch divergence) and may be null. Both
+  // the new and legacy pass managers funnel through here after acquiring the
+  // analysis in their respective ways.
+  LLVM_ABI PreservedAnalyses runImpl(Function &F, UniformityInfo *UI = nullptr);
+
 private:
   void BuildRankMap(Function &F, ReversePostOrderTraversal<Function *> &RPOT);
   unsigned getRank(Value *V);
diff --git a/llvm/lib/Transforms/Scalar/Reassociate.cpp b/llvm/lib/Transforms/Scalar/Reassociate.cpp
index b4034f246fd83..cd23b7c9cc564 100644
--- a/llvm/lib/Transforms/Scalar/Reassociate.cpp
+++ b/llvm/lib/Transforms/Scalar/Reassociate.cpp
@@ -2574,12 +2574,19 @@ ReassociatePass::BuildPairMap(ReversePostOrderTraversal<Function *> &RPOT) {
 PreservedAnalyses ReassociatePass::run(Function &F,
                                        FunctionAnalysisManager &AM) {
   // On targets with branch divergence, obtain UniformityInfo so we can group
-  // uniform operands together in expression trees.
-  if (!SkipUniformityAnalysis) {
-    const TargetTransformInfo &TTI = AM.getResult<TargetIRAnalysis>(F);
-    if (TTI.hasBranchDivergence(&F))
-      UA = &AM.getResult<UniformityInfoAnalysis>(F);
-  }
+  // uniform operands together in expression trees. TargetTransformInfo is only
+  // queried to gate this on hasBranchDivergence(): targets without divergence
+  // (the common case) never compute UniformityInfo and pay no extra cost.
+  UniformityInfo *UI = nullptr;
+  const TargetTransformInfo &TTI = AM.getResult<TargetIRAnalysis>(F);
+  if (TTI.hasBranchDivergence(&F))
+    UI = &AM.getResult<UniformityInfoAnalysis>(F);
+
+  return runImpl(F, UI);
+}
+
+PreservedAnalyses ReassociatePass::runImpl(Function &F, UniformityInfo *UI) {
+  UA = UI;
 
   // Get the functions basic blocks in Reverse Post Order. This order is used by
   // BuildRankMap to pre calculate ranks correctly. It also excludes dead basic
@@ -2667,8 +2674,7 @@ class ReassociateLegacyPass : public FunctionPass {
 public:
   static char ID; // Pass identification, replacement for typeid
 
-  ReassociateLegacyPass()
-      : FunctionPass(ID), Impl(/*SkipUniformityAnalysis=*/true) {
+  ReassociateLegacyPass() : FunctionPass(ID) {
     initializeReassociateLegacyPassPass(*PassRegistry::getPassRegistry());
   }
 
@@ -2676,13 +2682,23 @@ class ReassociateLegacyPass : public FunctionPass {
     if (skipFunction(F))
       return false;
 
-    FunctionAnalysisManager DummyFAM;
-    auto PA = Impl.run(F, DummyFAM);
+    // On targets with branch divergence, provide UniformityInfo so uniform
+    // operands can be grouped together; other targets pass null.
+    const TargetTransformInfo &TTI =
+        getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
+    UniformityInfo *UI =
+        TTI.hasBranchDivergence(&F)
+            ? &getAnalysis<UniformityInfoWrapperPass>().getUniformityInfo()
+            : nullptr;
+
+    PreservedAnalyses PA = Impl.runImpl(F, UI);
     return !PA.areAllPreserved();
   }
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesCFG();
+    AU.addRequired<TargetTransformInfoWrapperPass>();
+    AU.addRequired<UniformityInfoWrapperPass>();
     AU.addPreserved<AAResultsWrapperPass>();
     AU.addPreserved<BasicAAWrapperPass>();
     AU.addPreserved<GlobalsAAWrapperPass>();
@@ -2693,8 +2709,12 @@ class ReassociateLegacyPass : public FunctionPass {
 
 char ReassociateLegacyPass::ID = 0;
 
-INITIALIZE_PASS(ReassociateLegacyPass, "reassociate",
-                "Reassociate expressions", false, false)
+INITIALIZE_PASS_BEGIN(ReassociateLegacyPass, "reassociate",
+                      "Reassociate expressions", false, false)
+INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(UniformityInfoWrapperPass)
+INITIALIZE_PASS_END(ReassociateLegacyPass, "reassociate",
+                    "Reassociate expressions", false, false)
 
 // Public interface to the Reassociate pass
 FunctionPass *llvm::createReassociatePass() {

>From ac18363ca850072026a8849f504dc34b1759ca37 Mon Sep 17 00:00:00 2001
From: padivedi <pankajkumar.divedi at amd.com>
Date: Thu, 16 Jul 2026 15:49:53 +0530
Subject: [PATCH 6/6] review

---
 llvm/lib/Transforms/Scalar/Reassociate.cpp    |  37 ++---
 .../CodeGen/AMDGPU/reassoc-uniform-e2e.ll     | 141 ------------------
 llvm/test/Other/new-pm-defaults.ll            |   1 +
 .../Other/new-pm-thinlto-postlink-defaults.ll |   1 +
 .../new-pm-thinlto-postlink-pgo-defaults.ll   |   1 +
 ...-pm-thinlto-postlink-samplepgo-defaults.ll |   1 +
 .../Other/new-pm-thinlto-prelink-defaults.ll  |   1 +
 .../new-pm-thinlto-prelink-pgo-defaults.ll    |   1 +
 ...w-pm-thinlto-prelink-samplepgo-defaults.ll |   1 +
 9 files changed, 17 insertions(+), 168 deletions(-)
 delete mode 100644 llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll

diff --git a/llvm/lib/Transforms/Scalar/Reassociate.cpp b/llvm/lib/Transforms/Scalar/Reassociate.cpp
index cd23b7c9cc564..03c49f92cfcb1 100644
--- a/llvm/lib/Transforms/Scalar/Reassociate.cpp
+++ b/llvm/lib/Transforms/Scalar/Reassociate.cpp
@@ -31,7 +31,6 @@
 #include "llvm/Analysis/BasicAliasAnalysis.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/GlobalsModRef.h"
-#include "llvm/Analysis/TargetTransformInfo.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/BasicBlock.h"
@@ -2295,11 +2294,9 @@ void ReassociatePass::ReassociateExpression(BinaryOperator *I) {
 
   LLVM_DEBUG(dbgs() << "RAIn:\t"; PrintOps(I, Ops); dbgs() << '\n');
 
-  // On targets that report branch divergence (TTI.hasBranchDivergence()),
-  // boost the rank of divergent operands so they sort towards the root of
-  // the expression tree.  This clusters uniform operands together at the
-  // leaves, forming sub-expressions whose operands are all uniform and that
-  // can be evaluated in the target's uniform-execution domain.
+  // Boost the rank of divergent operands so they sort towards the root of the
+  // expression tree, clustering uniform operands together at the leaves. On
+  // targets without divergence UniformityInfo is empty and this is a no-op.
   //
   // Example: (uniform1 + divergent) + uniform2
   //       -> (uniform1 + uniform2) + divergent
@@ -2573,16 +2570,10 @@ ReassociatePass::BuildPairMap(ReversePostOrderTraversal<Function *> &RPOT) {
 
 PreservedAnalyses ReassociatePass::run(Function &F,
                                        FunctionAnalysisManager &AM) {
-  // On targets with branch divergence, obtain UniformityInfo so we can group
-  // uniform operands together in expression trees. TargetTransformInfo is only
-  // queried to gate this on hasBranchDivergence(): targets without divergence
-  // (the common case) never compute UniformityInfo and pay no extra cost.
-  UniformityInfo *UI = nullptr;
-  const TargetTransformInfo &TTI = AM.getResult<TargetIRAnalysis>(F);
-  if (TTI.hasBranchDivergence(&F))
-    UI = &AM.getResult<UniformityInfoAnalysis>(F);
-
-  return runImpl(F, UI);
+  // UniformityInfo is empty (and cheap) on targets without branch divergence,
+  // so request it unconditionally.
+  UniformityInfo &UI = AM.getResult<UniformityInfoAnalysis>(F);
+  return runImpl(F, &UI);
 }
 
 PreservedAnalyses ReassociatePass::runImpl(Function &F, UniformityInfo *UI) {
@@ -2682,22 +2673,15 @@ class ReassociateLegacyPass : public FunctionPass {
     if (skipFunction(F))
       return false;
 
-    // On targets with branch divergence, provide UniformityInfo so uniform
-    // operands can be grouped together; other targets pass null.
-    const TargetTransformInfo &TTI =
-        getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
-    UniformityInfo *UI =
-        TTI.hasBranchDivergence(&F)
-            ? &getAnalysis<UniformityInfoWrapperPass>().getUniformityInfo()
-            : nullptr;
+    UniformityInfo &UI =
+        getAnalysis<UniformityInfoWrapperPass>().getUniformityInfo();
 
-    PreservedAnalyses PA = Impl.runImpl(F, UI);
+    PreservedAnalyses PA = Impl.runImpl(F, &UI);
     return !PA.areAllPreserved();
   }
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesCFG();
-    AU.addRequired<TargetTransformInfoWrapperPass>();
     AU.addRequired<UniformityInfoWrapperPass>();
     AU.addPreserved<AAResultsWrapperPass>();
     AU.addPreserved<BasicAAWrapperPass>();
@@ -2711,7 +2695,6 @@ char ReassociateLegacyPass::ID = 0;
 
 INITIALIZE_PASS_BEGIN(ReassociateLegacyPass, "reassociate",
                       "Reassociate expressions", false, false)
-INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(UniformityInfoWrapperPass)
 INITIALIZE_PASS_END(ReassociateLegacyPass, "reassociate",
                     "Reassociate expressions", false, false)
diff --git a/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll b/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll
deleted file mode 100644
index eb5b0e9ae9353..0000000000000
--- a/llvm/test/CodeGen/AMDGPU/reassoc-uniform-e2e.ll
+++ /dev/null
@@ -1,141 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: opt -mtriple=amdgcn-amd-amdhsa -passes=reassociate -S < %s | \
-; RUN:   llc -mtriple=amdgcn -mcpu=gfx900 | FileCheck %s
-;
-; End-to-end test: Reassociate pass followed by llc codegen.
-; Tests that uniform operands grouped by IR-level reassociation produce
-; scalar ALU instructions in the AMDGPU backend.
-
-; 3-operand: (uniform * divergent) * uniform
-define amdgpu_kernel void @mul_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-; CHECK-LABEL: mul_ungrouped:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    v_mov_b32_e32 v1, 0
-; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    s_mul_i32 s2, s3, s2
-; CHECK-NEXT:    v_mul_lo_u32 v0, s2, v0
-; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
-; CHECK-NEXT:    s_endpgm
-  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
-  %op1 = mul i32 %x, %tid
-  %op2 = mul i32 %op1, %y
-  store i32 %op2, ptr addrspace(1) %arg, align 4
-  ret void
-}
-
-; 3-operand: (uniform | divergent) | uniform
-define amdgpu_kernel void @or_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-; CHECK-LABEL: or_ungrouped:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    v_mov_b32_e32 v1, 0
-; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    s_or_b32 s2, s3, s2
-; CHECK-NEXT:    v_or_b32_e32 v0, s2, v0
-; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
-; CHECK-NEXT:    s_endpgm
-  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
-  %op1 = or i32 %x, %tid
-  %op2 = or i32 %op1, %y
-  store i32 %op2, ptr addrspace(1) %arg, align 4
-  ret void
-}
-
-; 3-operand: (uniform & divergent) & uniform
-define amdgpu_kernel void @and_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-; CHECK-LABEL: and_ungrouped:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    v_mov_b32_e32 v1, 0
-; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    s_and_b32 s2, s3, s2
-; CHECK-NEXT:    v_and_b32_e32 v0, s2, v0
-; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
-; CHECK-NEXT:    s_endpgm
-  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
-  %op1 = and i32 %x, %tid
-  %op2 = and i32 %op1, %y
-  store i32 %op2, ptr addrspace(1) %arg, align 4
-  ret void
-}
-
-; 3-operand: (uniform + divergent) + uniform -- backend already handles add
-define amdgpu_kernel void @add_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-; CHECK-LABEL: add_ungrouped:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    v_mov_b32_e32 v1, 0
-; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    s_add_i32 s2, s3, s2
-; CHECK-NEXT:    v_add_u32_e32 v0, s2, v0
-; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
-; CHECK-NEXT:    s_endpgm
-  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
-  %op1 = add i32 %x, %tid
-  %op2 = add i32 %op1, %y
-  store i32 %op2, ptr addrspace(1) %arg, align 4
-  ret void
-}
-
-; 3-operand: (uniform ^ divergent) ^ uniform -- backend already handles xor
-define amdgpu_kernel void @xor_ungrouped(ptr addrspace(1) %arg, i32 %x, i32 %y) {
-; CHECK-LABEL: xor_ungrouped:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    v_mov_b32_e32 v1, 0
-; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    s_xor_b32 s2, s3, s2
-; CHECK-NEXT:    v_xor_b32_e32 v0, s2, v0
-; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
-; CHECK-NEXT:    s_endpgm
-  %tid = tail call i32 @llvm.amdgcn.workitem.id.x()
-  %op1 = xor i32 %x, %tid
-  %op2 = xor i32 %op1, %y
-  store i32 %op2, ptr addrspace(1) %arg, align 4
-  ret void
-}
-
-; 4-operand: (u1 * d1) * (u2 * d2)
-define amdgpu_kernel void @mul_4op(ptr addrspace(1) %arg, i32 %u1, i32 %u2) {
-; CHECK-LABEL: mul_4op:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    s_mul_i32 s2, s3, s2
-; CHECK-NEXT:    v_mul_lo_u32 v0, s2, v0
-; CHECK-NEXT:    v_mul_lo_u32 v0, v0, v1
-; CHECK-NEXT:    v_mov_b32_e32 v1, 0
-; CHECK-NEXT:    global_store_dword v1, v0, s[0:1]
-; CHECK-NEXT:    s_endpgm
-  %d1 = tail call i32 @llvm.amdgcn.workitem.id.x()
-  %d2 = tail call i32 @llvm.amdgcn.workitem.id.y()
-  %op1 = mul i32 %u1, %d1
-  %op2 = mul i32 %u2, %d2
-  %op3 = mul i32 %op1, %op2
-  store i32 %op3, ptr addrspace(1) %arg, align 4
-  ret void
-}
-
-; 4-operand: (u1 | d1) | (u2 | d2)
-define amdgpu_kernel void @or_4op(ptr addrspace(1) %arg, i32 %u1, i32 %u2) {
-; CHECK-LABEL: or_4op:
-; CHECK:       ; %bb.0:
-; CHECK-NEXT:    s_load_dwordx4 s[0:3], s[4:5], 0x24
-; CHECK-NEXT:    v_mov_b32_e32 v2, 0
-; CHECK-NEXT:    s_waitcnt lgkmcnt(0)
-; CHECK-NEXT:    s_or_b32 s2, s3, s2
-; CHECK-NEXT:    v_or3_b32 v0, s2, v0, v1
-; CHECK-NEXT:    global_store_dword v2, v0, s[0:1]
-; CHECK-NEXT:    s_endpgm
-  %d1 = tail call i32 @llvm.amdgcn.workitem.id.x()
-  %d2 = tail call i32 @llvm.amdgcn.workitem.id.y()
-  %op1 = or i32 %u1, %d1
-  %op2 = or i32 %u2, %d2
-  %op3 = or i32 %op1, %op2
-  store i32 %op3, ptr addrspace(1) %arg, align 4
-  ret void
-}
-
-declare i32 @llvm.amdgcn.workitem.id.x()
-declare i32 @llvm.amdgcn.workitem.id.y()
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index 3048206add4e2..abc626d3d11f2 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -157,6 +157,7 @@
 ; CHECK-O23-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
+; CHECK-O-NEXT: Running analysis: UniformityInfoAnalysis
 ; CHECK-O23-NEXT: Running pass: ConstraintEliminationPass
 ; CHECK-O23-NEXT: Running analysis: LoopAnalysis
 ; CHECK-O23-NEXT: Running analysis: ScalarEvolutionAnalysis
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
index c16ec4800b6bc..924de489e2d51 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -93,6 +93,7 @@
 ; CHECK-O23-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
+; CHECK-O-NEXT: Running analysis: UniformityInfoAnalysis
 ; CHECK-O23-NEXT: Running pass: ConstraintEliminationPass
 ; CHECK-O23-NEXT: Running analysis: LoopAnalysis
 ; CHECK-O23-NEXT: Running analysis: ScalarEvolutionAnalysis
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
index 15f11914bbde2..b8770fb8a5da2 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -81,6 +81,7 @@
 ; CHECK-O23-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
+; CHECK-O-NEXT: Running analysis: UniformityInfoAnalysis
 ; CHECK-O23-NEXT: Running pass: ConstraintEliminationPass
 ; CHECK-O23-NEXT: Running analysis: ScalarEvolutionAnalysis
 ; CHECK-O-NEXT: Running pass: LoopSimplifyPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
index 8eac5cf56a106..96c83e0eee727 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -88,6 +88,7 @@
 ; CHECK-O23-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
+; CHECK-O-NEXT: Running analysis: UniformityInfoAnalysis
 ; CHECK-O23-NEXT: Running pass: ConstraintEliminationPass
 ; CHECK-O23-NEXT: Running analysis: ScalarEvolutionAnalysis
 ; CHECK-O-NEXT: Running pass: LoopSimplifyPass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
index f4245b66b0429..c6fc9b6714343 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-defaults.ll
@@ -119,6 +119,7 @@
 ; CHECK-O23-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
+; CHECK-O-NEXT: Running analysis: UniformityInfoAnalysis
 ; CHECK-O23-NEXT: Running pass: ConstraintEliminationPass
 ; CHECK-O23-NEXT: Running analysis: LoopAnalysis
 ; CHECK-O23-NEXT: Running analysis: ScalarEvolutionAnalysis
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
index 87acb355fddc7..c033efd950458 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-pgo-defaults.ll
@@ -127,6 +127,7 @@
 ; CHECK-O23-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
+; CHECK-O-NEXT: Running analysis: UniformityInfoAnalysis
 ; CHECK-O23-NEXT: Running pass: ConstraintEliminationPass
 ; CHECK-O23-NEXT: Running analysis: ScalarEvolutionAnalysis
 ; CHECK-O-NEXT: Running pass: LoopSimplifyPass
diff --git a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
index e5c1453d692eb..61e6175265d4c 100644
--- a/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-prelink-samplepgo-defaults.ll
@@ -93,6 +93,7 @@
 ; CHECK-O23-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
+; CHECK-O-NEXT: Running analysis: UniformityInfoAnalysis
 ; CHECK-O23-NEXT: Running pass: ConstraintEliminationPass
 ; CHECK-O23-NEXT: Running analysis: ScalarEvolutionAnalysis
 ; CHECK-O-NEXT: Running pass: LoopSimplifyPass



More information about the llvm-commits mailing list