[llvm] Revert "[AMDGPU] Combine redundant ballot intrinsic calls (#218357)" (PR #218962)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 02:08:24 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-amdgpu

Author: Anshil Gandhi (gandhi56)

<details>
<summary>Changes</summary>

This reverts commit 2be211bb2ab35461520530f37e3faf382dc644d5.


---

Patch is 26.55 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/218962.diff


2 Files Affected:

- (modified) llvm/lib/Target/AMDGPU/AMDGPUUniformIntrinsicCombine.cpp (+5-170) 
- (removed) llvm/test/CodeGen/AMDGPU/redundant-ballot-reads.ll (-448) 


``````````diff
diff --git a/llvm/lib/Target/AMDGPU/AMDGPUUniformIntrinsicCombine.cpp b/llvm/lib/Target/AMDGPU/AMDGPUUniformIntrinsicCombine.cpp
index 00d3f5a1d2dee..6b68c25825d7b 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUUniformIntrinsicCombine.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUUniformIntrinsicCombine.cpp
@@ -16,26 +16,16 @@
 /// uniformity. And every instruction that's downstream and cares about dynamic
 /// uniformity must be convergent (and isel will introduce v_readfirstlane for
 /// them if their operands can't be proven statically uniform).
-///
-/// The pass additionally performs a convergence-aware CSE of
-/// llvm.amdgcn.ballot. Because ballot is convergent, the generic CSE passes
-/// refuse to merge two calls that live in different basic blocks: the result
-/// implicitly depends on the set of currently active lanes (exec). Here we can
-/// do better, because uniformity analysis lets us prove that exec is unchanged
-/// between two identical calls, in which case the later one is redundant.
 //===----------------------------------------------------------------------===//
 
 #include "AMDGPU.h"
 #include "GCNSubtarget.h"
-#include "llvm/ADT/DepthFirstIterator.h"
-#include "llvm/ADT/MapVector.h"
 #include "llvm/Analysis/DomTreeUpdater.h"
 #include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/ScalarEvolution.h"
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/UniformityAnalysis.h"
 #include "llvm/CodeGen/TargetPassConfig.h"
-#include "llvm/IR/Dominators.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstIterator.h"
 #include "llvm/IR/InstVisitor.h"
@@ -145,154 +135,8 @@ static bool optimizeUniformIntrinsic(IntrinsicInst &II,
   return false;
 }
 
-/// Maximum number of basic blocks inspected while proving that exec is
-/// invariant between two ballots. Keeps the walk below linear-per-pair in
-/// pathological CFGs.
-static constexpr unsigned MaxExecInvarianceBlocks = 100;
-
-/// Returns true if \p I may change exec, i.e. the set of lanes that are active
-/// when the following instructions execute.
-static bool isExecModifyingInst(const Instruction &I) {
-  const auto *CB = dyn_cast<CallBase>(&I);
-  if (!CB)
-    return false;
-
-  switch (CB->getIntrinsicID()) {
-  case Intrinsic::amdgcn_kill:
-  case Intrinsic::amdgcn_wqm_demote:
-  case Intrinsic::amdgcn_init_exec:
-  case Intrinsic::amdgcn_init_exec_from_input:
-  case Intrinsic::amdgcn_init_whole_wave:
-  // The WQM/WWM family makes the exec state at a given point depend on
-  // WQM/Exact decisions that SIWholeQuadMode only makes much later, so treat
-  // any occurrence of it as opaque.
-  case Intrinsic::amdgcn_wqm:
-  case Intrinsic::amdgcn_softwqm:
-  case Intrinsic::amdgcn_strict_wqm:
-  case Intrinsic::amdgcn_wwm:
-  case Intrinsic::amdgcn_strict_wwm:
-  case Intrinsic::amdgcn_set_inactive:
-  case Intrinsic::amdgcn_set_inactive_chain_arg:
-    return true;
-  case Intrinsic::not_intrinsic:
-    // A callee may itself execute llvm.amdgcn.kill.
-    return true;
-  default:
-    return false;
-  }
-}
-
-/// Returns true if exec is provably the same at \p A and at \p B, given that
-/// \p A dominates \p B. That holds when no path from \p A to \p B crosses a
-/// divergent terminator or an instruction that writes exec.
-static bool isExecInvariantBetween(const Instruction *A, const Instruction *B,
-                                   const UniformityInfo &UI) {
-  const BasicBlock *ABB = A->getParent();
-  const BasicBlock *BBB = B->getParent();
-
-  auto HasExecModifier = [](BasicBlock::const_iterator Begin,
-                            BasicBlock::const_iterator End) {
-    return any_of(make_range(Begin, End), isExecModifyingInst);
-  };
-
-  // Straight-line case: only the instructions in between can matter. Note that
-  // if this block is part of a cycle then A re-executes before B does, so the
-  // two still pair up within an iteration.
-  if (ABB == BBB)
-    return !HasExecModifier(std::next(A->getIterator()), B->getIterator());
-
-  // Everything in BBB ahead of B runs between A and B.
-  if (HasExecModifier(BBB->begin(), B->getIterator()))
-    return false;
-
-  // Collect every block on some path ABB ->* BBB that does not re-enter ABB.
-  // Since A dominates B, every such path starts at ABB, and every block found
-  // this way is dominated by ABB. Stopping the walk at ABB is correct: if a
-  // path did revisit ABB, then a later dynamic instance of A would be the one
-  // reaching B, and the path from that instance does not revisit ABB.
-  SmallPtrSet<const BasicBlock *, 8> Region;
-  SmallVector<const BasicBlock *, 8> Worklist;
-  Region.insert(ABB);
-  for (const BasicBlock *Pred : predecessors(BBB)) {
-    if (Region.insert(Pred).second)
-      Worklist.push_back(Pred);
-  }
-
-  while (!Worklist.empty()) {
-    const BasicBlock *Cur = Worklist.pop_back_val();
-    if (Cur == ABB)
-      continue;
-    for (const BasicBlock *Pred : predecessors(Cur))
-      if (Region.insert(Pred).second)
-        Worklist.push_back(Pred);
-  }
-
-  if (Region.size() > MaxExecInvarianceBlocks)
-    return false;
-
-  // Note that BBB lands in the region only if it can reach itself without
-  // passing through ABB, i.e. B sits in a cycle that A is outside of. In that
-  // case B re-executes and its whole block, terminator included, is checked
-  // below; otherwise BBB is absent and its terminator, which runs after B, is
-  // correctly left out.
-  for (const BasicBlock *Blk : Region) {
-    if (!UI.isUniformTerminator(Blk->getTerminator()))
-      return false;
-
-    // Instructions ahead of A never run between the last A and B.
-    BasicBlock::const_iterator Begin =
-        Blk == ABB ? std::next(A->getIterator()) : Blk->begin();
-    if (HasExecModifier(Begin, Blk->end()))
-      return false;
-  }
-  return true;
-}
-
-/// Removes ballot calls that are made redundant by an earlier identical call
-/// which is guaranteed to have executed with the same exec mask.
-static bool combineRedundantBallots(Function &F, UniformityInfo &UI,
-                                    const DominatorTree &DT) {
-  // Bucket the ballots by (result type, condition); only calls landing in the
-  // same bucket can possibly be identical. Visiting the dominator tree in
-  // pre-order means a dominating call always precedes the calls it dominates.
-  SmallMapVector<std::pair<Type *, Value *>, SmallVector<CallInst *, 4>, 4>
-      Buckets;
-  for (const DomTreeNode *N : depth_first(DT.getRootNode())) {
-    for (Instruction &I : *N->getBlock()) {
-      auto *CI = dyn_cast<CallInst>(&I);
-      if (CI && CI->getIntrinsicID() == Intrinsic::amdgcn_ballot)
-        Buckets[{CI->getType(), CI->getArgOperand(0)}].push_back(CI);
-    }
-  }
-
-  bool Changed = false;
-  for (auto &[Key, Ballots] : Buckets) {
-    for (unsigned I = 1, E = Ballots.size(); I < E; ++I) {
-      CallInst *B = Ballots[I];
-      for (unsigned J = 0; J < I; ++J) {
-        CallInst *A = Ballots[J];
-        // Null entries are calls that have already been erased.
-        if (!A || !A->isIdenticalToWhenDefined(B) || !DT.dominates(A, B) ||
-            !isExecInvariantBetween(A, B, UI))
-          continue;
-
-        LLVM_DEBUG(dbgs() << "Replacing redundant ballot " << *B << " with "
-                          << *A << '\n');
-        UI.forgetValue(B);
-        B->replaceAllUsesWith(A);
-        B->eraseFromParent();
-        Ballots[I] = nullptr;
-        Changed = true;
-        break;
-      }
-    }
-  }
-  return Changed;
-}
-
 /// Iterates over intrinsic calls in the Function to optimize.
-static bool runUniformIntrinsicCombine(Function &F, UniformityInfo &UI,
-                                       const DominatorTree &DT) {
+static bool runUniformIntrinsicCombine(Function &F, const UniformityInfo &UI) {
   bool IsChanged = false;
   ValueMap<const Value *, bool> Tracker;
 
@@ -302,22 +146,17 @@ static bool runUniformIntrinsicCombine(Function &F, UniformityInfo &UI,
       continue;
     IsChanged |= optimizeUniformIntrinsic(*II, UI, Tracker);
   }
-
-  IsChanged |= combineRedundantBallots(F, UI, DT);
-
   return IsChanged;
 }
 
 PreservedAnalyses
 AMDGPUUniformIntrinsicCombinePass::run(Function &F,
                                        FunctionAnalysisManager &AM) {
-  auto &UI = AM.getResult<UniformityInfoAnalysis>(F);
-  const auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
-  if (!runUniformIntrinsicCombine(F, UI, DT))
+  const auto &UI = AM.getResult<UniformityInfoAnalysis>(F);
+  if (!runUniformIntrinsicCombine(F, UI))
     return PreservedAnalyses::all();
 
   PreservedAnalyses PA;
-  PA.preserveSet<CFGAnalyses>();
   PA.preserve<UniformityInfoAnalysis>();
   return PA;
 }
@@ -333,7 +172,6 @@ class AMDGPUUniformIntrinsicCombineLegacy : public FunctionPass {
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesCFG();
     AU.addRequired<UniformityInfoWrapperPass>();
-    AU.addRequired<DominatorTreeWrapperPass>();
     AU.addRequired<TargetPassConfig>();
   }
 };
@@ -346,17 +184,14 @@ char &llvm::AMDGPUUniformIntrinsicCombineLegacyPassID =
 bool AMDGPUUniformIntrinsicCombineLegacy::runOnFunction(Function &F) {
   if (skipFunction(F))
     return false;
-  UniformityInfo &UI =
+  const UniformityInfo &UI =
       getAnalysis<UniformityInfoWrapperPass>().getUniformityInfo();
-  const DominatorTree &DT =
-      getAnalysis<DominatorTreeWrapperPass>().getDomTree();
-  return runUniformIntrinsicCombine(F, UI, DT);
+  return runUniformIntrinsicCombine(F, UI);
 }
 
 INITIALIZE_PASS_BEGIN(AMDGPUUniformIntrinsicCombineLegacy, DEBUG_TYPE,
                       "AMDGPU Uniform Intrinsic Combine", false, false)
 INITIALIZE_PASS_DEPENDENCY(UniformityInfoWrapperPass)
-INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
 INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
 INITIALIZE_PASS_END(AMDGPUUniformIntrinsicCombineLegacy, DEBUG_TYPE,
                     "AMDGPU Uniform Intrinsic Combine", false, false)
diff --git a/llvm/test/CodeGen/AMDGPU/redundant-ballot-reads.ll b/llvm/test/CodeGen/AMDGPU/redundant-ballot-reads.ll
deleted file mode 100644
index 59ae73d65eb66..0000000000000
--- a/llvm/test/CodeGen/AMDGPU/redundant-ballot-reads.ll
+++ /dev/null
@@ -1,448 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -mtriple=amdgpu11-amd-amdhsa -passes=amdgpu-uniform-intrinsic-combine -S < %s | FileCheck %s
-
-define amdgpu_kernel void @same_block(ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @same_block(
-; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    [[R:%.*]] = add i64 [[A]], [[A]]
-; CHECK-NEXT:    store i64 [[R]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    ret void
-;
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond = icmp ult i32 %tid, 8
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %r = add i64 %a, %b
-  store i64 %r, ptr addrspace(1) %out
-  ret void
-}
-
-define amdgpu_kernel void @cross_block(ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @cross_block(
-; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    br label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[R:%.*]] = add i64 [[A]], [[A]]
-; CHECK-NEXT:    store i64 [[R]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    ret void
-;
-entry:
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond = icmp ult i32 %tid, 8
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  br label %exit
-
-exit:
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %r = add i64 %a, %b
-  store i64 %r, ptr addrspace(1) %out
-  ret void
-}
-
-; A uniform branch keeps every lane together, so exec at %merge equals exec at
-; %entry.
-define amdgpu_kernel void @uniform_diamond(i1 %uc, ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @uniform_diamond(
-; CHECK-SAME: i1 [[UC:%.*]], ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    br i1 [[UC]], label %[[THEN:.*]], label %[[ELSE:.*]]
-; CHECK:       [[THEN]]:
-; CHECK-NEXT:    br label %[[MERGE:.*]]
-; CHECK:       [[ELSE]]:
-; CHECK-NEXT:    br label %[[MERGE]]
-; CHECK:       [[MERGE]]:
-; CHECK-NEXT:    [[R:%.*]] = add i64 [[A]], [[A]]
-; CHECK-NEXT:    store i64 [[R]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    ret void
-;
-entry:
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond = icmp ult i32 %tid, 8
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  br i1 %uc, label %then, label %else
-
-then:
-  br label %merge
-
-else:
-  br label %merge
-
-merge:
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %r = add i64 %a, %b
-  store i64 %r, ptr addrspace(1) %out
-  ret void
-}
-
-; The loop is uniform, so every lane iterates in lockstep and the ballot in the
-; body always sees the exec mask it had on entry.
-define amdgpu_kernel void @uniform_loop(i32 %n, ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @uniform_loop(
-; CHECK-SAME: i32 [[N:%.*]], ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[R:%.*]] = add i64 [[A]], [[A]]
-; CHECK-NEXT:    store i64 [[R]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    [[NEXT]] = add i32 [[IV]], 1
-; CHECK-NEXT:    [[EC:%.*]] = icmp eq i32 [[NEXT]], [[N]]
-; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    ret void
-;
-entry:
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond = icmp ult i32 %tid, 8
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  br label %loop
-
-loop:
-  %iv = phi i32 [ 0, %entry ], [ %next, %loop ]
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %r = add i64 %a, %b
-  store i64 %r, ptr addrspace(1) %out
-  %next = add i32 %iv, 1
-  %ec = icmp eq i32 %next, %n
-  br i1 %ec, label %exit, label %loop
-
-exit:
-  ret void
-}
-
-; All three collapse onto the first.
-define amdgpu_kernel void @chain(ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @chain(
-; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    br label %[[MID:.*]]
-; CHECK:       [[MID]]:
-; CHECK-NEXT:    br label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[R0:%.*]] = add i64 [[A]], [[A]]
-; CHECK-NEXT:    [[R1:%.*]] = add i64 [[R0]], [[A]]
-; CHECK-NEXT:    store i64 [[R1]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    ret void
-;
-entry:
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond = icmp ult i32 %tid, 8
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  br label %mid
-
-mid:
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  br label %exit
-
-exit:
-  %c = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %r0 = add i64 %a, %b
-  %r1 = add i64 %r0, %c
-  store i64 %r1, ptr addrspace(1) %out
-  ret void
-}
-
-; Negative test: lanes reconverge at %merge, but a divergent branch is
-; not something this pass reasons about, so both calls stay.
-define amdgpu_kernel void @divergent_branch(ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @divergent_branch(
-; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    [[DC:%.*]] = icmp ult i32 [[TID]], 16
-; CHECK-NEXT:    br i1 [[DC]], label %[[THEN:.*]], label %[[ELSE:.*]]
-; CHECK:       [[THEN]]:
-; CHECK-NEXT:    br label %[[MERGE:.*]]
-; CHECK:       [[ELSE]]:
-; CHECK-NEXT:    br label %[[MERGE]]
-; CHECK:       [[MERGE]]:
-; CHECK-NEXT:    [[B:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    [[R:%.*]] = add i64 [[A]], [[B]]
-; CHECK-NEXT:    store i64 [[R]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    ret void
-;
-entry:
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond = icmp ult i32 %tid, 8
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %dc = icmp ult i32 %tid, 16
-  br i1 %dc, label %then, label %else
-
-then:
-  br label %merge
-
-else:
-  br label %merge
-
-merge:
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %r = add i64 %a, %b
-  store i64 %r, ptr addrspace(1) %out
-  ret void
-}
-
-; Negative test: The divergent latch does not dominate %loop, so an
-; idom-chain walk would miss it. Lanes drop out of the loop one at
-; a time, so the ballot in %loop sees a different exec mask on later
-; iterations.
-define amdgpu_kernel void @divergent_latch(ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @divergent_latch(
-; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[NEXT:%.*]], %[[LATCH:.*]] ]
-; CHECK-NEXT:    [[B:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND]])
-; CHECK-NEXT:    [[R:%.*]] = add i64 [[A]], [[B]]
-; CHECK-NEXT:    store i64 [[R]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    br label %[[LATCH]]
-; CHECK:       [[LATCH]]:
-; CHECK-NEXT:    [[NEXT]] = add i32 [[IV]], 1
-; CHECK-NEXT:    [[EC:%.*]] = icmp ult i32 [[NEXT]], [[TID]]
-; CHECK-NEXT:    br i1 [[EC]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    ret void
-;
-entry:
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond = icmp ult i32 %tid, 8
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  br label %loop
-
-loop:
-  %iv = phi i32 [ 0, %entry ], [ %next, %latch ]
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond)
-  %r = add i64 %a, %b
-  store i64 %r, ptr addrspace(1) %out
-  br label %latch
-
-latch:
-  %next = add i32 %iv, 1
-  %ec = icmp ult i32 %next, %tid
-  br i1 %ec, label %loop, label %exit
-
-exit:
-  ret void
-}
-
-define amdgpu_kernel void @different_arg(ptr addrspace(1) %out) {
-; CHECK-LABEL: define amdgpu_kernel void @different_arg(
-; CHECK-SAME: ptr addrspace(1) [[OUT:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TID:%.*]] = call i32 @llvm.amdgcn.workitem.id.x()
-; CHECK-NEXT:    [[COND0:%.*]] = icmp ult i32 [[TID]], 8
-; CHECK-NEXT:    [[COND1:%.*]] = icmp ult i32 [[TID]], 16
-; CHECK-NEXT:    [[A:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND0]])
-; CHECK-NEXT:    br label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[B:%.*]] = call i64 @llvm.amdgcn.ballot.i64(i1 [[COND1]])
-; CHECK-NEXT:    [[R:%.*]] = add i64 [[A]], [[B]]
-; CHECK-NEXT:    store i64 [[R]], ptr addrspace(1) [[OUT]], align 8
-; CHECK-NEXT:    ret void
-;
-entry:
-  %tid = call i32 @llvm.amdgcn.workitem.id.x()
-  %cond0 = icmp ult i32 %tid, 8
-  %cond1 = icmp ult i32 %tid, 16
-  %a = call i64 @llvm.amdgcn.ballot.i64(i1 %cond0)
-  br label %exit
-
-exit:
-  %b = call i64 @llvm.amdgcn.ballot.i64(i1 %cond1)
-  %r = add i64 %a, %b
-  store i64 %r, ptr addrspace(1) %out
-  ret void
-}
-
-define amdgpu_kernel void @different_type(ptr addrspace(1) %out) {
-; CHE...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/218962


More information about the llvm-commits mailing list