LLVM 24.0.0git
AMDGPULowerIntrinsics.cpp
Go to the documentation of this file.
1//===-- AMDGPULowerIntrinsics.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// Lower intrinsics that would otherwise require separate handling in both
10// SelectionDAG and GlobalISel.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AMDGPU.h"
15#include "AMDGPUTargetMachine.h"
16#include "GCNSubtarget.h"
17#include "llvm/IR/IRBuilder.h"
19#include "llvm/IR/IntrinsicsAMDGPU.h"
22
23#define DEBUG_TYPE "amdgpu-lower-intrinsics"
24
25using namespace llvm;
26
27namespace {
28
29class AMDGPULowerIntrinsicsImpl {
30public:
31 Module &M;
32 const AMDGPUTargetMachine &TM;
33
34 AMDGPULowerIntrinsicsImpl(Module &M, const AMDGPUTargetMachine &TM)
35 : M(M), TM(TM) {}
36
37 bool run();
38
39private:
40 bool visitBarrier(IntrinsicInst &I);
41 bool visitPtrSBufferLoad(IntrinsicInst &I);
42 bool visitMonitorSleep(IntrinsicInst &I);
43};
44
45class AMDGPULowerIntrinsicsLegacy : public ModulePass {
46public:
47 static char ID;
48
49 AMDGPULowerIntrinsicsLegacy() : ModulePass(ID) {}
50
51 bool runOnModule(Module &M) override;
52
53 void getAnalysisUsage(AnalysisUsage &AU) const override {
55 }
56};
57
58template <class T> static void forEachCall(Function &Intrin, T Callback) {
59 for (User *U : make_early_inc_range(Intrin.users())) {
60 if (auto *CI = dyn_cast<IntrinsicInst>(U))
61 Callback(CI);
62 }
63}
64
65} // anonymous namespace
66
67bool AMDGPULowerIntrinsicsImpl::run() {
68 bool Changed = false;
69
70 for (Function &F : M) {
71 switch (F.getIntrinsicID()) {
72 default:
73 continue;
74 case Intrinsic::amdgcn_s_barrier:
75 case Intrinsic::amdgcn_s_barrier_signal:
76 case Intrinsic::amdgcn_s_barrier_signal_isfirst:
77 case Intrinsic::amdgcn_s_barrier_wait:
78 case Intrinsic::amdgcn_s_cluster_barrier:
79 forEachCall(F, [&](IntrinsicInst *II) { Changed |= visitBarrier(*II); });
80 break;
81 case Intrinsic::amdgcn_ptr_s_buffer_load:
83 F, [&](IntrinsicInst *II) { Changed |= visitPtrSBufferLoad(*II); });
84 break;
85 case Intrinsic::amdgcn_s_monitor_sleep:
87 F, [&](IntrinsicInst *II) { Changed |= visitMonitorSleep(*II); });
88 break;
89 }
90 }
91
92 return Changed;
93}
94
95// Optimize barriers and lower s_(cluster_)barrier to a sequence of split
96// barrier intrinsics.
97bool AMDGPULowerIntrinsicsImpl::visitBarrier(IntrinsicInst &I) {
98 assert(I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier ||
99 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal ||
100 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal_isfirst ||
101 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_wait ||
102 I.getIntrinsicID() == Intrinsic::amdgcn_s_cluster_barrier);
103
104 const GCNSubtarget &ST = TM.getSubtarget<GCNSubtarget>(*I.getFunction());
105 bool IsSingleWaveWG = false;
106
107 if (TM.getOptLevel() > CodeGenOptLevel::None) {
108 unsigned WGMaxSize = ST.getFlatWorkGroupSizes(*I.getFunction()).second;
109 IsSingleWaveWG = WGMaxSize <= ST.getWavefrontSize();
110 }
111
112 IRBuilder<> B(&I);
113
114 // Lower the s_cluster_barrier intrinsic first. There is no corresponding
115 // hardware instruction in any subtarget.
116 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_cluster_barrier) {
117 // The default cluster barrier expects one signal per workgroup. So we need
118 // a workgroup barrier first.
119 if (IsSingleWaveWG) {
120 B.CreateIntrinsicWithoutFolding(B.getVoidTy(),
121 Intrinsic::amdgcn_wave_barrier, {})
122 ->copyMetadata(I);
123 } else {
124 Value *BarrierID_32 = B.getInt32(AMDGPU::Barrier::WORKGROUP);
125 Value *BarrierID_16 = B.getInt16(AMDGPU::Barrier::WORKGROUP);
126 CallInst *IsFirst = B.CreateIntrinsicWithoutFolding(
127 B.getInt1Ty(), Intrinsic::amdgcn_s_barrier_signal_isfirst,
128 {BarrierID_32});
129 IsFirst->copyMetadata(I);
130 B.CreateIntrinsicWithoutFolding(
131 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_wait, {BarrierID_16})
132 ->copyMetadata(I);
133
134 Instruction *ThenTerm =
135 SplitBlockAndInsertIfThen(IsFirst, I.getIterator(), false);
136 B.SetInsertPoint(ThenTerm);
137 }
138
139 // Now we can signal the cluster barrier from a single wave and wait for the
140 // barrier in all waves.
141 Value *BarrierID_32 = B.getInt32(AMDGPU::Barrier::CLUSTER);
142 Value *BarrierID_16 = B.getInt16(AMDGPU::Barrier::CLUSTER);
143 B.CreateIntrinsicWithoutFolding(
144 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_signal, {BarrierID_32})
145 ->copyMetadata(I);
146
147 B.SetInsertPoint(&I);
148 B.CreateIntrinsicWithoutFolding(
149 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_wait, {BarrierID_16})
150 ->copyMetadata(I);
151
152 I.eraseFromParent();
153 return true;
154 }
155
156 bool IsWorkgroupScope = false;
157
158 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_wait ||
159 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal ||
160 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal_isfirst) {
161 int BarrierID = cast<ConstantInt>(I.getArgOperand(0))->getSExtValue();
162 if (BarrierID == AMDGPU::Barrier::TRAP ||
163 BarrierID == AMDGPU::Barrier::WORKGROUP ||
166 IsWorkgroupScope = true;
167 } else {
168 assert(I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier);
169 IsWorkgroupScope = true;
170 }
171
172 if (IsWorkgroupScope && IsSingleWaveWG) {
173 // Down-grade waits, remove split signals.
174 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier ||
175 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_wait) {
176 B.CreateIntrinsicWithoutFolding(B.getVoidTy(),
177 Intrinsic::amdgcn_wave_barrier, {})
178 ->copyMetadata(I);
179 } else if (I.getIntrinsicID() ==
180 Intrinsic::amdgcn_s_barrier_signal_isfirst) {
181 // If we're the only wave of the workgroup, we're always first.
182 I.replaceAllUsesWith(B.getInt1(true));
183 }
184 I.eraseFromParent();
185 return true;
186 }
187
188 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier &&
189 ST.hasSplitBarriers()) {
190 // Lower to split barriers.
191 Value *BarrierID_32 = B.getInt32(AMDGPU::Barrier::WORKGROUP);
192 Value *BarrierID_16 = B.getInt16(AMDGPU::Barrier::WORKGROUP);
193 B.CreateIntrinsicWithoutFolding(
194 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_signal, {BarrierID_32})
195 ->copyMetadata(I);
196 B.CreateIntrinsicWithoutFolding(
197 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_wait, {BarrierID_16})
198 ->copyMetadata(I);
199 I.eraseFromParent();
200 return true;
201 }
202
203 return false;
204}
205
206bool AMDGPULowerIntrinsicsImpl::visitPtrSBufferLoad(IntrinsicInst &I) {
207 assert(I.getIntrinsicID() == Intrinsic::amdgcn_ptr_s_buffer_load);
208
209 if (I.hasMetadata(LLVMContext::MD_invariant_load))
210 return false;
211
212 I.setMetadata(LLVMContext::MD_invariant_load,
213 MDNode::get(I.getContext(), {}));
214 return true;
215}
216
217bool AMDGPULowerIntrinsicsImpl::visitMonitorSleep(IntrinsicInst &I) {
218 assert(I.getIntrinsicID() == Intrinsic::amdgcn_s_monitor_sleep);
219
220 const GCNSubtarget &ST = TM.getSubtarget<GCNSubtarget>(*I.getFunction());
221 if (!ST.hasNoSleepForever())
222 return false;
223
224 int Sleep = cast<ConstantInt>(I.getArgOperand(0))->getSExtValue();
225 if (!(Sleep & 0x8000))
226 return false;
227
228 IRBuilder<> B(&I);
229 Value *NewSleep = B.getInt16(0x2000); // Maximum
230 I.setArgOperand(0, NewSleep);
231
232 return true;
233}
234
237 AMDGPULowerIntrinsicsImpl Impl(M, TM);
238 if (!Impl.run())
239 return PreservedAnalyses::all();
241}
242
243bool AMDGPULowerIntrinsicsLegacy::runOnModule(Module &M) {
244 auto &TPC = getAnalysis<TargetPassConfig>();
245 const AMDGPUTargetMachine &TM = TPC.getTM<AMDGPUTargetMachine>();
246
247 AMDGPULowerIntrinsicsImpl Impl(M, TM);
248 return Impl.run();
249}
250
251#define PASS_DESC "AMDGPU lower intrinsics"
252INITIALIZE_PASS_BEGIN(AMDGPULowerIntrinsicsLegacy, DEBUG_TYPE, PASS_DESC, false,
253 false)
255INITIALIZE_PASS_END(AMDGPULowerIntrinsicsLegacy, DEBUG_TYPE, PASS_DESC, false,
256 false)
257
258char AMDGPULowerIntrinsicsLegacy::ID = 0;
259
261 return new AMDGPULowerIntrinsicsLegacy;
262}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
The AMDGPU TargetMachine interface definition for hw codegen targets.
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
AMD GCN specific subclass of TargetSubtarget.
#define DEBUG_TYPE
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
#define T
uint64_t IntrinsicInst * II
ModuleAnalysisManager MAM
#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
static bool forEachCall(Function &Intrin, T Callback)
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
LLVM_ABI void copyMetadata(const Instruction &SrcInst, ArrayRef< unsigned > WL=ArrayRef< unsigned >())
Copy metadata from SrcInst to this instruction.
A wrapper class for inspecting calls to intrinsic functions.
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1567
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:67
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
CodeGenOptLevel getOptLevel() const
Returns the optimization level: None, Less, Default, or Aggressive.
const STC & getSubtarget(const Function &F) const
This method returns a pointer to the specified type of TargetSubtargetInfo.
Target-Independent Code Generator Pass Configuration Options.
iterator_range< user_iterator > users()
Definition Value.h:426
Changed
friend class Instruction
Iterator for Instructions in a `BasicBlock.
Definition BasicBlock.h:73
This is an optimization pass for GlobalISel generic memory operations.
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:633
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
ModulePass * createAMDGPULowerIntrinsicsLegacyPass()
IRBuilder(LLVMContext &, FolderTy, InserterTy, MDNode *, ArrayRef< OperandBundleDef >) -> IRBuilder< FolderTy, InserterTy >
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
LLVM_ABI Instruction * SplitBlockAndInsertIfThen(Value *Cond, BasicBlock::iterator SplitBefore, bool Unreachable, MDNode *BranchWeights=nullptr, DomTreeUpdater *DTU=nullptr, LoopInfo *LI=nullptr, BasicBlock *ThenBlock=nullptr)
Split the containing block at the specified instruction - everything before SplitBefore stays in the ...
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)