[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