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"
18#include "llvm/IR/IRBuilder.h"
20#include "llvm/IR/IntrinsicsAMDGPU.h"
23
24#define DEBUG_TYPE "amdgpu-lower-intrinsics"
25
26using namespace llvm;
27
28namespace {
29
30class AMDGPULowerIntrinsicsImpl {
31public:
32 Module &M;
33 const AMDGPUTargetMachine &TM;
34
35 AMDGPULowerIntrinsicsImpl(Module &M, const AMDGPUTargetMachine &TM)
36 : M(M), TM(TM) {}
37
38 bool run();
39
40private:
41 bool visitBarrier(IntrinsicInst &I);
42 bool visitPtrSBufferLoad(IntrinsicInst &I);
43 bool visitMonitorSleep(IntrinsicInst &I);
44 bool visitCvtScale(IntrinsicInst &I);
45 bool visitUnscaledWMMA(IntrinsicInst &I);
46};
47
48class AMDGPULowerIntrinsicsLegacy : public ModulePass {
49public:
50 static char ID;
51
52 AMDGPULowerIntrinsicsLegacy() : ModulePass(ID) {}
53
54 bool runOnModule(Module &M) override;
55
56 void getAnalysisUsage(AnalysisUsage &AU) const override {
58 }
59};
60
61template <class T> static void forEachCall(Function &Intrin, T Callback) {
62 for (User *U : make_early_inc_range(Intrin.users())) {
63 if (auto *CI = dyn_cast<IntrinsicInst>(U))
64 Callback(CI);
65 }
66}
67
68} // anonymous namespace
69
70bool AMDGPULowerIntrinsicsImpl::run() {
71 bool Changed = false;
72
73 for (Function &F : M) {
74 switch (F.getIntrinsicID()) {
75 default:
76 continue;
77 case Intrinsic::amdgcn_s_barrier:
78 case Intrinsic::amdgcn_s_barrier_signal:
79 case Intrinsic::amdgcn_s_barrier_signal_isfirst:
80 case Intrinsic::amdgcn_s_barrier_wait:
81 case Intrinsic::amdgcn_s_cluster_barrier:
82 forEachCall(F, [&](IntrinsicInst *II) { Changed |= visitBarrier(*II); });
83 break;
84 case Intrinsic::amdgcn_ptr_s_buffer_load:
86 F, [&](IntrinsicInst *II) { Changed |= visitPtrSBufferLoad(*II); });
87 break;
88 case Intrinsic::amdgcn_s_monitor_sleep:
90 F, [&](IntrinsicInst *II) { Changed |= visitMonitorSleep(*II); });
91 break;
92 case Intrinsic::amdgcn_cvt_scale_pk8_f16_fp8:
93 case Intrinsic::amdgcn_cvt_scale_pk8_bf16_fp8:
94 case Intrinsic::amdgcn_cvt_scale_pk8_f16_bf8:
95 case Intrinsic::amdgcn_cvt_scale_pk8_bf16_bf8:
96 case Intrinsic::amdgcn_cvt_scale_pk8_f16_fp4:
97 case Intrinsic::amdgcn_cvt_scale_pk8_bf16_fp4:
98 case Intrinsic::amdgcn_cvt_scale_pk8_f32_fp8:
99 case Intrinsic::amdgcn_cvt_scale_pk8_f32_bf8:
100 case Intrinsic::amdgcn_cvt_scale_pk8_f32_fp4:
101 case Intrinsic::amdgcn_cvt_scale_pk16_f16_bf6:
102 case Intrinsic::amdgcn_cvt_scale_pk16_bf16_bf6:
103 case Intrinsic::amdgcn_cvt_scale_pk16_f16_fp6:
104 case Intrinsic::amdgcn_cvt_scale_pk16_bf16_fp6:
105 case Intrinsic::amdgcn_cvt_scale_pk16_f32_fp6:
106 case Intrinsic::amdgcn_cvt_scale_pk16_f32_bf6:
107 forEachCall(F, [&](IntrinsicInst *II) { Changed |= visitCvtScale(*II); });
108 break;
109 case Intrinsic::amdgcn_wmma_f32_16x16x128_f8f6f4:
111 F, [&](IntrinsicInst *II) { Changed |= visitUnscaledWMMA(*II); });
112 break;
113 }
114 }
115
116 return Changed;
117}
118
119// Optimize barriers and lower s_(cluster_)barrier to a sequence of split
120// barrier intrinsics.
121bool AMDGPULowerIntrinsicsImpl::visitBarrier(IntrinsicInst &I) {
122 assert(I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier ||
123 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal ||
124 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal_isfirst ||
125 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_wait ||
126 I.getIntrinsicID() == Intrinsic::amdgcn_s_cluster_barrier);
127
128 const GCNSubtarget &ST = TM.getSubtarget<GCNSubtarget>(*I.getFunction());
129 bool IsSingleWaveWG = false;
130
131 if (TM.getOptLevel() > CodeGenOptLevel::None)
132 IsSingleWaveWG = ST.isSingleWavefrontWorkgroup(*I.getFunction());
133
134 IRBuilder<> B(&I);
135
136 // Lower the s_cluster_barrier intrinsic first. There is no corresponding
137 // hardware instruction in any subtarget.
138 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_cluster_barrier) {
139 // The default cluster barrier expects one signal per workgroup. So we need
140 // a workgroup barrier first.
141 if (IsSingleWaveWG) {
142 B.CreateIntrinsicWithoutFolding(B.getVoidTy(),
143 Intrinsic::amdgcn_wave_barrier, {})
144 ->copyMetadata(I);
145 } else {
146 Value *BarrierID_32 = B.getInt32(AMDGPU::Barrier::WORKGROUP);
147 Value *BarrierID_16 = B.getInt16(AMDGPU::Barrier::WORKGROUP);
148 CallInst *IsFirst = B.CreateIntrinsicWithoutFolding(
149 B.getInt1Ty(), Intrinsic::amdgcn_s_barrier_signal_isfirst,
150 {BarrierID_32});
151 IsFirst->copyMetadata(I);
152 B.CreateIntrinsicWithoutFolding(
153 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_wait, {BarrierID_16})
154 ->copyMetadata(I);
155
156 Instruction *ThenTerm =
157 SplitBlockAndInsertIfThen(IsFirst, I.getIterator(), false);
158 B.SetInsertPoint(ThenTerm);
159 }
160
161 // Now we can signal the cluster barrier from a single wave and wait for the
162 // barrier in all waves.
163 Value *BarrierID_32 = B.getInt32(AMDGPU::Barrier::CLUSTER);
164 Value *BarrierID_16 = B.getInt16(AMDGPU::Barrier::CLUSTER);
165 B.CreateIntrinsicWithoutFolding(
166 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_signal, {BarrierID_32})
167 ->copyMetadata(I);
168
169 B.SetInsertPoint(&I);
170 B.CreateIntrinsicWithoutFolding(
171 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_wait, {BarrierID_16})
172 ->copyMetadata(I);
173
174 I.eraseFromParent();
175 return true;
176 }
177
178 bool IsWorkgroupScope = false;
179
180 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_wait ||
181 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal ||
182 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal_isfirst) {
183 int BarrierID = cast<ConstantInt>(I.getArgOperand(0))->getSExtValue();
184 if (BarrierID == AMDGPU::Barrier::TRAP ||
185 BarrierID == AMDGPU::Barrier::WORKGROUP ||
188 IsWorkgroupScope = true;
189 else if (I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_signal_isfirst &&
190 BarrierID == AMDGPU::Barrier::CLUSTER) {
191 I.getContext().diagnose(
192 DiagnosticInfoUnsupported(*I.getFunction(),
193 "s_barrier_signal_isfirst does not support "
194 "user_cluster_barrier_id (-3)",
195 I.getDebugLoc()));
196 }
197 } else {
198 assert(I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier);
199 IsWorkgroupScope = true;
200 }
201
202 if (IsWorkgroupScope && IsSingleWaveWG) {
203 // Down-grade waits, remove split signals.
204 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier ||
205 I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier_wait) {
206 B.CreateIntrinsicWithoutFolding(B.getVoidTy(),
207 Intrinsic::amdgcn_wave_barrier, {})
208 ->copyMetadata(I);
209 } else if (I.getIntrinsicID() ==
210 Intrinsic::amdgcn_s_barrier_signal_isfirst) {
211 // If we're the only wave of the workgroup, we're always first.
212 I.replaceAllUsesWith(B.getInt1(true));
213 }
214 I.eraseFromParent();
215 return true;
216 }
217
218 if (I.getIntrinsicID() == Intrinsic::amdgcn_s_barrier &&
219 ST.hasSplitBarriers()) {
220 // Lower to split barriers.
221 Value *BarrierID_32 = B.getInt32(AMDGPU::Barrier::WORKGROUP);
222 Value *BarrierID_16 = B.getInt16(AMDGPU::Barrier::WORKGROUP);
223 B.CreateIntrinsicWithoutFolding(
224 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_signal, {BarrierID_32})
225 ->copyMetadata(I);
226 B.CreateIntrinsicWithoutFolding(
227 B.getVoidTy(), Intrinsic::amdgcn_s_barrier_wait, {BarrierID_16})
228 ->copyMetadata(I);
229 I.eraseFromParent();
230 return true;
231 }
232
233 return false;
234}
235
236bool AMDGPULowerIntrinsicsImpl::visitPtrSBufferLoad(IntrinsicInst &I) {
237 assert(I.getIntrinsicID() == Intrinsic::amdgcn_ptr_s_buffer_load);
238
239 if (I.hasMetadata(LLVMContext::MD_invariant_load))
240 return false;
241
242 I.setMetadata(LLVMContext::MD_invariant_load,
243 MDNode::get(I.getContext(), {}));
244 return true;
245}
246
247bool AMDGPULowerIntrinsicsImpl::visitMonitorSleep(IntrinsicInst &I) {
248 assert(I.getIntrinsicID() == Intrinsic::amdgcn_s_monitor_sleep);
249
250 const GCNSubtarget &ST = TM.getSubtarget<GCNSubtarget>(*I.getFunction());
251 if (!ST.hasNoSleepForever())
252 return false;
253
254 int Sleep = cast<ConstantInt>(I.getArgOperand(0))->getSExtValue();
255 if (!(Sleep & 0x8000))
256 return false;
257
258 IRBuilder<> B(&I);
259 Value *NewSleep = B.getInt16(127); // Maximum
260 I.setArgOperand(0, NewSleep);
261
262 return true;
263}
264
265bool AMDGPULowerIntrinsicsImpl::visitCvtScale(IntrinsicInst &I) {
266 int MaxSel = 0;
267 switch (I.getIntrinsicID()) {
268 default:
269 llvm_unreachable("expected cvt_scale_* intrinsic");
270 case Intrinsic::amdgcn_cvt_scale_pk8_f16_fp8:
271 case Intrinsic::amdgcn_cvt_scale_pk8_bf16_fp8:
272 case Intrinsic::amdgcn_cvt_scale_pk8_f16_bf8:
273 case Intrinsic::amdgcn_cvt_scale_pk8_bf16_bf8:
274 case Intrinsic::amdgcn_cvt_scale_pk8_f32_fp8:
275 case Intrinsic::amdgcn_cvt_scale_pk8_f32_bf8:
276 MaxSel = 8;
277 break;
278 case Intrinsic::amdgcn_cvt_scale_pk8_f16_fp4:
279 case Intrinsic::amdgcn_cvt_scale_pk8_bf16_fp4:
280 case Intrinsic::amdgcn_cvt_scale_pk8_f32_fp4:
281 case Intrinsic::amdgcn_cvt_scale_pk16_f16_bf6:
282 case Intrinsic::amdgcn_cvt_scale_pk16_bf16_bf6:
283 case Intrinsic::amdgcn_cvt_scale_pk16_f16_fp6:
284 case Intrinsic::amdgcn_cvt_scale_pk16_bf16_fp6:
285 case Intrinsic::amdgcn_cvt_scale_pk16_f32_fp6:
286 case Intrinsic::amdgcn_cvt_scale_pk16_f32_bf6:
287 MaxSel = 4;
288 break;
289 }
290
291 const GCNSubtarget &ST = TM.getSubtarget<GCNSubtarget>(*I.getFunction());
292 if (ST.hasBlock16ConversionScaleInsts())
293 MaxSel *= 2;
294
295 int ScaleSel = cast<ConstantInt>(I.getArgOperand(2))->getSExtValue();
296 if (ScaleSel < MaxSel)
297 return false;
298
299 I.getContext().diagnose(DiagnosticInfoUnsupported(
300 *I.getFunction(),
301 I.getCalledFunction()->getName() +
302 Twine(" scale_sel maximum supported value is ") + Twine(MaxSel - 1),
303 I.getDebugLoc()));
304
305 return false;
306}
307
308bool AMDGPULowerIntrinsicsImpl::visitUnscaledWMMA(IntrinsicInst &I) {
309 assert(I.getIntrinsicID() == Intrinsic::amdgcn_wmma_f32_16x16x128_f8f6f4);
310
311 const GCNSubtarget &ST = TM.getSubtarget<GCNSubtarget>(*I.getFunction());
312 if (!ST.hasGFX1250_STRICT())
313 return false;
314
315 IRBuilder<> B(&I);
316
317 // Convert unscaled WMMA into a scaled version with scale factors 0.
319 Args.push_back(B.getInt32(0)); // matrix_a_scale
320 Args.push_back(
321 B.getInt32(AMDGPU::WMMA::MATRIX_SCALE_FMT_E8)); // matrix_a_scale_fmt
322 Args.push_back(B.getInt32(0)); // matrix a scale exponential
323 Args.push_back(B.getInt32(0)); // matrix_b_scale
324 Args.push_back(
325 B.getInt32(AMDGPU::WMMA::MATRIX_SCALE_FMT_E8)); // matrix_b_scale_fmt
326 Args.push_back(B.getInt32(0)); // matrix b scale exponential
327 Args.push_back(B.getInt1(0)); // matrix_a_reuse
328 Args.push_back(B.getInt1(0)); // matrix_b_reuse
329
330 CallInst *NewI = B.CreateIntrinsicWithoutFolding(
331 I.getType(), Intrinsic::amdgcn_wmma_scale_f32_16x16x128_f8f6f4, Args);
332 I.replaceAllUsesWith(NewI);
333 NewI->copyMetadata(I);
334 NewI->takeName(&I);
335 I.eraseFromParent();
336
337 return true;
338}
339
342 AMDGPULowerIntrinsicsImpl Impl(M, TM);
343 if (!Impl.run())
344 return PreservedAnalyses::all();
346}
347
348bool AMDGPULowerIntrinsicsLegacy::runOnModule(Module &M) {
349 auto &TPC = getAnalysis<TargetPassConfig>();
350 const AMDGPUTargetMachine &TM = TPC.getTM<AMDGPUTargetMachine>();
351
352 AMDGPULowerIntrinsicsImpl Impl(M, TM);
353 return Impl.run();
354}
355
356#define PASS_DESC "AMDGPU lower intrinsics"
357INITIALIZE_PASS_BEGIN(AMDGPULowerIntrinsicsLegacy, DEBUG_TYPE, PASS_DESC, false,
358 false)
360INITIALIZE_PASS_END(AMDGPULowerIntrinsicsLegacy, DEBUG_TYPE, PASS_DESC, false,
361 false)
362
363char AMDGPULowerIntrinsicsLegacy::ID = 0;
364
366 return new AMDGPULowerIntrinsicsLegacy;
367}
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:1579
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:68
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:428
LLVM_ABI void takeName(Value *V)
Transfer the name from V to this value.
Definition Value.cpp:400
Changed
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
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:649
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
ModulePass * createAMDGPULowerIntrinsicsLegacyPass()
IRBuilder(LLVMContext &, FolderTy, InserterTy) -> IRBuilder< FolderTy, InserterTy >
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
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)