LLVM 24.0.0git
AMDGPUUniformIntrinsicCombine.cpp
Go to the documentation of this file.
1//===-- AMDGPUUniformIntrinsicCombine.cpp ---------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9/// \file
10/// This pass simplifies certain intrinsic calls when the arguments are uniform.
11/// It's true that this pass has transforms that can lead to a situation where
12/// some instruction whose operand was previously recognized as statically
13/// uniform is later on no longer recognized as statically uniform. However, the
14/// semantics of how programs execute don't (and must not, for this precise
15/// reason) care about static uniformity, they only ever care about dynamic
16/// uniformity. And every instruction that's downstream and cares about dynamic
17/// uniformity must be convergent (and isel will introduce v_readfirstlane for
18/// them if their operands can't be proven statically uniform).
19//===----------------------------------------------------------------------===//
20
21#include "AMDGPU.h"
27#include "llvm/IR/IRBuilder.h"
29#include "llvm/IR/IntrinsicsAMDGPU.h"
34
35#define DEBUG_TYPE "amdgpu-uniform-intrinsic-combine"
36
37using namespace llvm;
38using namespace llvm::AMDGPU;
39using namespace llvm::PatternMatch;
40
41/// Optimizes uniform intrinsics calls if their operand can be proven uniform.
43 llvm::Intrinsic::ID IID = II.getIntrinsicID();
44 /// We deliberately do not simplify readfirstlane with a uniform argument, so
45 /// that frontends can use it to force a copy to SGPR and thereby prevent the
46 /// backend from generating unwanted waterfall loops.
47 switch (IID) {
48 case Intrinsic::amdgcn_permlane64:
49 case Intrinsic::amdgcn_readlane: {
50 Value *Src = II.getArgOperand(0);
51 if (UI.isDivergentAtUse(II.getOperandUse(0)))
52 return false;
53 LLVM_DEBUG(dbgs() << "Replacing " << II << " with " << *Src << '\n');
54 II.replaceAllUsesWith(Src);
55 UI.forgetValue(&II);
56 II.eraseFromParent();
57 return true;
58 }
59 case Intrinsic::amdgcn_ballot: {
60 Value *Src = II.getArgOperand(0);
61 if (UI.isDivergentAtUse(II.getOperandUse(0)))
62 return false;
63 LLVM_DEBUG(dbgs() << "Found uniform ballot intrinsic: " << II << '\n');
64
65 bool Changed = false;
66 for (User *U : make_early_inc_range(II.users())) {
67 if (auto *ICmp = dyn_cast<ICmpInst>(U)) {
68 Value *Op0 = ICmp->getOperand(0);
69 Value *Op1 = ICmp->getOperand(1);
70 ICmpInst::Predicate Pred = ICmp->getPredicate();
71 Value *OtherOp = Op0 == &II ? Op1 : Op0;
72
73 if (Pred == ICmpInst::ICMP_EQ && match(OtherOp, m_Zero())) {
74 // Case: (icmp eq %ballot, 0) -> xor %ballot_arg, 1
75 Instruction *NotOp =
76 BinaryOperator::CreateNot(Src, "", ICmp->getIterator());
77 LLVM_DEBUG(dbgs() << "Replacing ICMP_EQ: " << *NotOp << '\n');
78 ICmp->replaceAllUsesWith(NotOp);
79 Changed = true;
80 } else if (Pred == ICmpInst::ICMP_NE && match(OtherOp, m_Zero())) {
81 // Case: (icmp ne %ballot, 0) -> %ballot_arg
82 LLVM_DEBUG(dbgs() << "Replacing ICMP_NE with ballot argument: "
83 << *Src << '\n');
84 ICmp->replaceAllUsesWith(Src);
85 Changed = true;
86 }
87 }
88 }
89 // Erase the intrinsic if it has no remaining uses.
90 if (II.use_empty()) {
91 UI.forgetValue(&II);
92 II.eraseFromParent();
93 Changed = true;
94 }
95 return Changed;
96 }
97 case Intrinsic::amdgcn_wave_shuffle: {
98 Use &Val = II.getOperandUse(0);
99 Use &Idx = II.getOperandUse(1);
100
101 // Like with readlane, if Value is uniform then just propagate it
102 if (!UI.isDivergentAtUse(Val)) {
103 II.replaceAllUsesWith(Val);
104 UI.forgetValue(&II);
105 II.eraseFromParent();
106 return true;
107 }
108
109 // Otherwise, when Index is uniform, this is just a readlane operation
110 if (UI.isDivergentAtUse(Idx))
111 return false;
112
113 // The readlane intrinsic we want to call has the exact same function
114 // signature, so we can quickly modify the instruction in-place
115 Module *Mod = II.getModule();
116 II.setCalledFunction(Intrinsic::getOrInsertDeclaration(
117 Mod, Intrinsic::amdgcn_readlane, II.getType()));
118 return true;
119 }
120 default:
121 return false;
122 }
123 return false;
124}
125
126/// Fold `icmp pred (readfirstlane X), X` when X is uniform at that use.
128 Value *X;
129 IntrinsicInst *RFL;
130 if (!match(&Cmp,
132 m_Value(X)),
133 m_AnyIntrinsic(RFL)),
134 m_Deferred(X))))
135 return false;
136
137 if (UI.isDivergentAtUse(RFL->getArgOperandUse(0)))
138 return false;
139
140 // If readfirstlane's operand is uniform then we know we are comparing two
141 // identical values.
142 bool Holds = ICmpInst::isTrueWhenEqual(Cmp.getPredicate());
143 LLVM_DEBUG(dbgs() << "Uniform readfirstlane operand, replacing " << Cmp
144 << " with " << (Holds ? "true" : "false") << '\n');
145 Cmp.replaceAllUsesWith(ConstantInt::get(Cmp.getType(), Holds));
146 UI.forgetValue(&Cmp);
147 Cmp.eraseFromParent();
148 return true;
149}
150
151/// Iterates over the Function, optimizing uniform intrinsic calls and
152/// comparisons against a readfirstlane of a uniform value.
154 bool IsChanged = false;
156 if (auto *Cmp = dyn_cast<ICmpInst>(&I)) {
157 IsChanged |= optimizeUniformCmpReadFirstLane(*Cmp, UI);
158 continue;
159 }
160 auto *II = dyn_cast<IntrinsicInst>(&I);
161 if (!II)
162 continue;
163 IsChanged |= optimizeUniformIntrinsic(*II, UI);
164 }
165 return IsChanged;
166}
167
179
180namespace {
181class AMDGPUUniformIntrinsicCombineLegacy : public FunctionPass {
182public:
183 static char ID;
184 AMDGPUUniformIntrinsicCombineLegacy() : FunctionPass(ID) {}
185
186private:
187 bool runOnFunction(Function &F) override;
188 void getAnalysisUsage(AnalysisUsage &AU) const override {
189 AU.setPreservesCFG();
192 }
193};
194} // namespace
195
196char AMDGPUUniformIntrinsicCombineLegacy::ID = 0;
198 AMDGPUUniformIntrinsicCombineLegacy::ID;
199
200bool AMDGPUUniformIntrinsicCombineLegacy::runOnFunction(Function &F) {
201 if (skipFunction(F))
202 return false;
203 UniformityInfo &UI =
204 getAnalysis<UniformityInfoWrapperPass>().getUniformityInfo();
205 return runUniformIntrinsicCombine(F, UI);
206}
207
208INITIALIZE_PASS_BEGIN(AMDGPUUniformIntrinsicCombineLegacy, DEBUG_TYPE,
209 "AMDGPU Uniform Intrinsic Combine", false, false)
212INITIALIZE_PASS_END(AMDGPUUniformIntrinsicCombineLegacy, DEBUG_TYPE,
213 "AMDGPU Uniform Intrinsic Combine", false, false)
214
216 return new AMDGPUUniformIntrinsicCombineLegacy();
217}
SDValue Pred
static bool optimizeUniformCmpReadFirstLane(ICmpInst &Cmp, UniformityInfo &UI)
Fold icmp pred (readfirstlane X), X when X is uniform at that use.
static bool runUniformIntrinsicCombine(Function &F, UniformityInfo &UI)
Iterates over the Function, optimizing uniform intrinsic calls and comparisons against a readfirstlan...
static bool optimizeUniformIntrinsic(IntrinsicInst &II, UniformityInfo &UI)
Optimizes uniform intrinsics calls if their operand can be proven uniform.
Expand Atomic instructions
#define X(NUM, ENUM, NAME)
Definition ELF.h:665
static bool runOnFunction(Function &F, bool PostInlining)
#define DEBUG_TYPE
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
uint64_t IntrinsicInst * II
if(auto Err=PB.parsePassPipeline(MPM, Passes)) return wrap(std MPM run * Mod
#define INITIALIZE_PASS_DEPENDENCY(depName)
Definition PassSupport.h:42
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition PassSupport.h:44
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition PassSupport.h:39
#define LLVM_DEBUG(...)
Definition Debug.h:119
Target-Independent Code Generator Pass Configuration Options pass.
LLVM IR instance of the generic uniformity analysis.
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
LLVM_ABI void setPreservesCFG()
This function should be called by the pass, iff they do not:
Definition Pass.cpp:280
static LLVM_ABI BinaryOperator * CreateNot(Value *Op, const Twine &Name="", InsertPosition InsertBefore=nullptr)
Represents analyses that only rely on functions' control flow.
Definition Analysis.h:73
const Use & getArgOperandUse(unsigned i) const
Wrappers for getting the Use of a call argument.
Predicate
This enumeration lists the possible predicates for CmpInst subclasses.
Definition InstrTypes.h:740
@ ICMP_NE
not equal
Definition InstrTypes.h:762
bool isTrueWhenEqual() const
This is just a convenience.
FunctionPass class - This class is used to implement most global optimizations.
Definition Pass.h:312
bool isDivergentAtUse(const UseT &U) const
Whether U is divergent at its use.
void forgetValue(ConstValueRefT V)
Call before erasing V, or a later instruction reusing its address may be misclassified as uniform.
This instruction compares its operands according to the predicate given to the constructor.
A wrapper class for inspecting calls to intrinsic functions.
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses & preserveSet()
Mark an analysis set as preserved.
Definition Analysis.h:151
Target-Independent Code Generator Pass Configuration Options.
Analysis pass which computes UniformityInfo.
Legacy analysis pass which computes a CycleInfo.
A Use represents the edge between a Value definition and its users.
Definition Use.h:35
LLVM Value Representation.
Definition Value.h:75
LLVM_ABI Function * getOrInsertDeclaration(Module *M, ID id, ArrayRef< Type * > OverloadTys={})
Look up the Function declaration of the intrinsic id in the Module M.
match_combine_and< Ty... > m_CombineAnd(const Ty &...Ps)
Combine pattern matchers matching all of Ps patterns.
bool match(Val *V, const Pattern &P)
match_deferred< Value > m_Deferred(Value *const &V)
Like m_Specific(), but works if the specific value to match is determined as part of the same match()...
CmpClass_match< LHS, RHS, ICmpInst, true > m_c_ICmp(CmpPredicate &Pred, const LHS &L, const RHS &R)
Matches an ICmp with a predicate over LHS and RHS in either order.
auto m_Value()
Match an arbitrary value and ignore it.
auto m_AnyIntrinsic()
Matches any intrinsic call and ignore it.
is_zero< true > m_Zero()
Match any null constant or a vector with all elements equal to 0.
auto m_Intrinsic(const Ts &...Ops)
Match intrinsic calls like this: m_Intrinsic<Intrinsic::fabs>(m_Value(X))
This is an optimization pass for GlobalISel generic memory operations.
GenericUniformityInfo< SSAContext > UniformityInfo
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
iterator_range< early_inc_iterator_impl< detail::IterOfRange< RangeT > > > make_early_inc_range(RangeT &&Range)
Make a range that does early increment to allow mutation of the underlying range without disrupting i...
Definition STLExtras.h:649
FunctionPass * createAMDGPUUniformIntrinsicCombineLegacyPass()
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
char & AMDGPUUniformIntrinsicCombineLegacyPassID
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)