]> granicus.if.org Git - clang/blob - lib/CodeGen/CGOpenMPRuntimeNVPTX.cpp
[OpenMP] Base support for target directive codegen on NVPTX device.
[clang] / lib / CodeGen / CGOpenMPRuntimeNVPTX.cpp
1 //===---- CGOpenMPRuntimeNVPTX.cpp - Interface to OpenMP NVPTX Runtimes ---===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This provides a class for OpenMP runtime code generation specialized to NVPTX
11 // targets.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "CGOpenMPRuntimeNVPTX.h"
16 #include "clang/AST/DeclOpenMP.h"
17
18 using namespace clang;
19 using namespace CodeGen;
20
21 /// \brief Get the GPU warp size.
22 llvm::Value *CGOpenMPRuntimeNVPTX::getNVPTXWarpSize(CodeGenFunction &CGF) {
23   CGBuilderTy &Bld = CGF.Builder;
24   return Bld.CreateCall(
25       llvm::Intrinsic::getDeclaration(
26           &CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_warpsize),
27       llvm::None, "nvptx_warp_size");
28 }
29
30 /// \brief Get the id of the current thread on the GPU.
31 llvm::Value *CGOpenMPRuntimeNVPTX::getNVPTXThreadID(CodeGenFunction &CGF) {
32   CGBuilderTy &Bld = CGF.Builder;
33   return Bld.CreateCall(
34       llvm::Intrinsic::getDeclaration(
35           &CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_tid_x),
36       llvm::None, "nvptx_tid");
37 }
38
39 // \brief Get the maximum number of threads in a block of the GPU.
40 llvm::Value *CGOpenMPRuntimeNVPTX::getNVPTXNumThreads(CodeGenFunction &CGF) {
41   CGBuilderTy &Bld = CGF.Builder;
42   return Bld.CreateCall(
43       llvm::Intrinsic::getDeclaration(
44           &CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_ntid_x),
45       llvm::None, "nvptx_num_threads");
46 }
47
48 /// \brief Get barrier to synchronize all threads in a block.
49 void CGOpenMPRuntimeNVPTX::getNVPTXCTABarrier(CodeGenFunction &CGF) {
50   CGBuilderTy &Bld = CGF.Builder;
51   Bld.CreateCall(llvm::Intrinsic::getDeclaration(
52       &CGM.getModule(), llvm::Intrinsic::nvvm_barrier0));
53 }
54
55 // \brief Synchronize all GPU threads in a block.
56 void CGOpenMPRuntimeNVPTX::syncCTAThreads(CodeGenFunction &CGF) {
57   getNVPTXCTABarrier(CGF);
58 }
59
60 /// \brief Get the thread id of the OMP master thread.
61 /// The master thread id is the first thread (lane) of the last warp in the
62 /// GPU block.  Warp size is assumed to be some power of 2.
63 /// Thread id is 0 indexed.
64 /// E.g: If NumThreads is 33, master id is 32.
65 ///      If NumThreads is 64, master id is 32.
66 ///      If NumThreads is 1024, master id is 992.
67 llvm::Value *CGOpenMPRuntimeNVPTX::getMasterThreadID(CodeGenFunction &CGF) {
68   CGBuilderTy &Bld = CGF.Builder;
69   llvm::Value *NumThreads = getNVPTXNumThreads(CGF);
70
71   // We assume that the warp size is a power of 2.
72   llvm::Value *Mask = Bld.CreateSub(getNVPTXWarpSize(CGF), Bld.getInt32(1));
73
74   return Bld.CreateAnd(Bld.CreateSub(NumThreads, Bld.getInt32(1)),
75                        Bld.CreateNot(Mask), "master_tid");
76 }
77
78 namespace {
79 enum OpenMPRTLFunctionNVPTX {
80   /// \brief Call to void __kmpc_kernel_init(kmp_int32 omp_handle,
81   /// kmp_int32 thread_limit);
82   OMPRTL_NVPTX__kmpc_kernel_init,
83 };
84
85 // NVPTX Address space
86 enum ADDRESS_SPACE {
87   ADDRESS_SPACE_SHARED = 3,
88 };
89 } // namespace
90
91 CGOpenMPRuntimeNVPTX::WorkerFunctionState::WorkerFunctionState(
92     CodeGenModule &CGM)
93     : WorkerFn(nullptr), CGFI(nullptr) {
94   createWorkerFunction(CGM);
95 };
96
97 void CGOpenMPRuntimeNVPTX::WorkerFunctionState::createWorkerFunction(
98     CodeGenModule &CGM) {
99   // Create an worker function with no arguments.
100   CGFI = &CGM.getTypes().arrangeNullaryFunction();
101
102   WorkerFn = llvm::Function::Create(
103       CGM.getTypes().GetFunctionType(*CGFI), llvm::GlobalValue::InternalLinkage,
104       /* placeholder */ "_worker", &CGM.getModule());
105   CGM.SetInternalFunctionAttributes(/*D=*/nullptr, WorkerFn, *CGFI);
106   WorkerFn->setLinkage(llvm::GlobalValue::InternalLinkage);
107   WorkerFn->addFnAttr(llvm::Attribute::NoInline);
108 }
109
110 void CGOpenMPRuntimeNVPTX::initializeEnvironment() {
111   //
112   // Initialize master-worker control state in shared memory.
113   //
114
115   auto DL = CGM.getDataLayout();
116   ActiveWorkers = new llvm::GlobalVariable(
117       CGM.getModule(), CGM.Int32Ty, /*isConstant=*/false,
118       llvm::GlobalValue::CommonLinkage,
119       llvm::Constant::getNullValue(CGM.Int32Ty), "__omp_num_threads", 0,
120       llvm::GlobalVariable::NotThreadLocal, ADDRESS_SPACE_SHARED);
121   ActiveWorkers->setAlignment(DL.getPrefTypeAlignment(CGM.Int32Ty));
122
123   WorkID = new llvm::GlobalVariable(
124       CGM.getModule(), CGM.Int64Ty, /*isConstant=*/false,
125       llvm::GlobalValue::CommonLinkage,
126       llvm::Constant::getNullValue(CGM.Int64Ty), "__tgt_work_id", 0,
127       llvm::GlobalVariable::NotThreadLocal, ADDRESS_SPACE_SHARED);
128   WorkID->setAlignment(DL.getPrefTypeAlignment(CGM.Int64Ty));
129 }
130
131 void CGOpenMPRuntimeNVPTX::emitWorkerFunction(WorkerFunctionState &WST) {
132   auto &Ctx = CGM.getContext();
133
134   CodeGenFunction CGF(CGM, /*suppressNewContext=*/true);
135   CGF.StartFunction(GlobalDecl(), Ctx.VoidTy, WST.WorkerFn, *WST.CGFI, {});
136   emitWorkerLoop(CGF, WST);
137   CGF.FinishFunction();
138 }
139
140 void CGOpenMPRuntimeNVPTX::emitWorkerLoop(CodeGenFunction &CGF,
141                                           WorkerFunctionState &WST) {
142   //
143   // The workers enter this loop and wait for parallel work from the master.
144   // When the master encounters a parallel region it sets up the work + variable
145   // arguments, and wakes up the workers.  The workers first check to see if
146   // they are required for the parallel region, i.e., within the # of requested
147   // parallel threads.  The activated workers load the variable arguments and
148   // execute the parallel work.
149   //
150
151   CGBuilderTy &Bld = CGF.Builder;
152
153   llvm::BasicBlock *AwaitBB = CGF.createBasicBlock(".await.work");
154   llvm::BasicBlock *SelectWorkersBB = CGF.createBasicBlock(".select.workers");
155   llvm::BasicBlock *ExecuteBB = CGF.createBasicBlock(".execute.parallel");
156   llvm::BasicBlock *TerminateBB = CGF.createBasicBlock(".terminate.parallel");
157   llvm::BasicBlock *BarrierBB = CGF.createBasicBlock(".barrier.parallel");
158   llvm::BasicBlock *ExitBB = CGF.createBasicBlock(".exit");
159
160   CGF.EmitBranch(AwaitBB);
161
162   // Workers wait for work from master.
163   CGF.EmitBlock(AwaitBB);
164   // Wait for parallel work
165   syncCTAThreads(CGF);
166   // On termination condition (workid == 0), exit loop.
167   llvm::Value *ShouldTerminate = Bld.CreateICmpEQ(
168       Bld.CreateAlignedLoad(WorkID, WorkID->getAlignment()),
169       llvm::Constant::getNullValue(WorkID->getType()->getElementType()),
170       "should_terminate");
171   Bld.CreateCondBr(ShouldTerminate, ExitBB, SelectWorkersBB);
172
173   // Activate requested workers.
174   CGF.EmitBlock(SelectWorkersBB);
175   llvm::Value *ThreadID = getNVPTXThreadID(CGF);
176   llvm::Value *ActiveThread = Bld.CreateICmpSLT(
177       ThreadID,
178       Bld.CreateAlignedLoad(ActiveWorkers, ActiveWorkers->getAlignment()),
179       "active_thread");
180   Bld.CreateCondBr(ActiveThread, ExecuteBB, BarrierBB);
181
182   // Signal start of parallel region.
183   CGF.EmitBlock(ExecuteBB);
184   // TODO: Add parallel work.
185
186   // Signal end of parallel region.
187   CGF.EmitBlock(TerminateBB);
188   CGF.EmitBranch(BarrierBB);
189
190   // All active and inactive workers wait at a barrier after parallel region.
191   CGF.EmitBlock(BarrierBB);
192   // Barrier after parallel region.
193   syncCTAThreads(CGF);
194   CGF.EmitBranch(AwaitBB);
195
196   // Exit target region.
197   CGF.EmitBlock(ExitBB);
198 }
199
200 // Setup NVPTX threads for master-worker OpenMP scheme.
201 void CGOpenMPRuntimeNVPTX::emitEntryHeader(CodeGenFunction &CGF,
202                                            EntryFunctionState &EST,
203                                            WorkerFunctionState &WST) {
204   CGBuilderTy &Bld = CGF.Builder;
205
206   // Get the master thread id.
207   llvm::Value *MasterID = getMasterThreadID(CGF);
208   // Current thread's identifier.
209   llvm::Value *ThreadID = getNVPTXThreadID(CGF);
210
211   // Setup BBs in entry function.
212   llvm::BasicBlock *WorkerCheckBB = CGF.createBasicBlock(".check.for.worker");
213   llvm::BasicBlock *WorkerBB = CGF.createBasicBlock(".worker");
214   llvm::BasicBlock *MasterBB = CGF.createBasicBlock(".master");
215   EST.ExitBB = CGF.createBasicBlock(".exit");
216
217   // The head (master thread) marches on while its body of companion threads in
218   // the warp go to sleep.
219   llvm::Value *ShouldDie =
220       Bld.CreateICmpUGT(ThreadID, MasterID, "excess_in_master_warp");
221   Bld.CreateCondBr(ShouldDie, EST.ExitBB, WorkerCheckBB);
222
223   // Select worker threads...
224   CGF.EmitBlock(WorkerCheckBB);
225   llvm::Value *IsWorker = Bld.CreateICmpULT(ThreadID, MasterID, "is_worker");
226   Bld.CreateCondBr(IsWorker, WorkerBB, MasterBB);
227
228   // ... and send to worker loop, awaiting parallel invocation.
229   CGF.EmitBlock(WorkerBB);
230   CGF.EmitCallOrInvoke(WST.WorkerFn, llvm::None);
231   CGF.EmitBranch(EST.ExitBB);
232
233   // Only master thread executes subsequent serial code.
234   CGF.EmitBlock(MasterBB);
235
236   // First action in sequential region:
237   // Initialize the state of the OpenMP runtime library on the GPU.
238   llvm::Value *Args[] = {Bld.getInt32(/*OmpHandle=*/0), getNVPTXThreadID(CGF)};
239   CGF.EmitRuntimeCall(createNVPTXRuntimeFunction(OMPRTL_NVPTX__kmpc_kernel_init),
240                       Args);
241 }
242
243 void CGOpenMPRuntimeNVPTX::emitEntryFooter(CodeGenFunction &CGF,
244                                            EntryFunctionState &EST) {
245   CGBuilderTy &Bld = CGF.Builder;
246   llvm::BasicBlock *TerminateBB = CGF.createBasicBlock(".termination.notifier");
247   CGF.EmitBranch(TerminateBB);
248
249   CGF.EmitBlock(TerminateBB);
250   // Signal termination condition.
251   Bld.CreateAlignedStore(
252       llvm::Constant::getNullValue(WorkID->getType()->getElementType()), WorkID,
253       WorkID->getAlignment());
254   // Barrier to terminate worker threads.
255   syncCTAThreads(CGF);
256   // Master thread jumps to exit point.
257   CGF.EmitBranch(EST.ExitBB);
258
259   CGF.EmitBlock(EST.ExitBB);
260 }
261
262 /// \brief Returns specified OpenMP runtime function for the current OpenMP
263 /// implementation.  Specialized for the NVPTX device.
264 /// \param Function OpenMP runtime function.
265 /// \return Specified function.
266 llvm::Constant *
267 CGOpenMPRuntimeNVPTX::createNVPTXRuntimeFunction(unsigned Function) {
268   llvm::Constant *RTLFn = nullptr;
269   switch (static_cast<OpenMPRTLFunctionNVPTX>(Function)) {
270   case OMPRTL_NVPTX__kmpc_kernel_init: {
271     // Build void __kmpc_kernel_init(kmp_int32 omp_handle,
272     // kmp_int32 thread_limit);
273     llvm::Type *TypeParams[] = {CGM.Int32Ty, CGM.Int32Ty};
274     llvm::FunctionType *FnTy =
275         llvm::FunctionType::get(CGM.VoidTy, TypeParams, /*isVarArg*/ false);
276     RTLFn = CGM.CreateRuntimeFunction(FnTy, "__kmpc_kernel_init");
277     break;
278   }
279   }
280   return RTLFn;
281 }
282
283 void CGOpenMPRuntimeNVPTX::createOffloadEntry(llvm::Constant *ID,
284                                               llvm::Constant *Addr,
285                                               uint64_t Size) {
286   auto *F = dyn_cast<llvm::Function>(Addr);
287   // TODO: Add support for global variables on the device after declare target
288   // support.
289   if (!F)
290     return;
291   llvm::Module *M = F->getParent();
292   llvm::LLVMContext &Ctx = M->getContext();
293
294   // Get "nvvm.annotations" metadata node
295   llvm::NamedMDNode *MD = M->getOrInsertNamedMetadata("nvvm.annotations");
296
297   llvm::Metadata *MDVals[] = {
298       llvm::ConstantAsMetadata::get(F), llvm::MDString::get(Ctx, "kernel"),
299       llvm::ConstantAsMetadata::get(
300           llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 1))};
301   // Append metadata to nvvm.annotations
302   MD->addOperand(llvm::MDNode::get(Ctx, MDVals));
303 }
304
305 void CGOpenMPRuntimeNVPTX::emitTargetOutlinedFunction(
306     const OMPExecutableDirective &D, StringRef ParentName,
307     llvm::Function *&OutlinedFn, llvm::Constant *&OutlinedFnID,
308     bool IsOffloadEntry) {
309   if (!IsOffloadEntry) // Nothing to do.
310     return;
311
312   assert(!ParentName.empty() && "Invalid target region parent name!");
313
314   const CapturedStmt &CS = *cast<CapturedStmt>(D.getAssociatedStmt());
315
316   EntryFunctionState EST;
317   WorkerFunctionState WST(CGM);
318
319   // Emit target region as a standalone region.
320   auto &&CodeGen = [&EST, &WST, &CS, this](CodeGenFunction &CGF) {
321     emitEntryHeader(CGF, EST, WST);
322     CGF.EmitStmt(CS.getCapturedStmt());
323     emitEntryFooter(CGF, EST);
324   };
325   emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,
326                                    IsOffloadEntry, CodeGen);
327
328   // Create the worker function
329   emitWorkerFunction(WST);
330
331   // Now change the name of the worker function to correspond to this target
332   // region's entry function.
333   WST.WorkerFn->setName(OutlinedFn->getName() + "_worker");
334 }
335
336 CGOpenMPRuntimeNVPTX::CGOpenMPRuntimeNVPTX(CodeGenModule &CGM)
337     : CGOpenMPRuntime(CGM), ActiveWorkers(nullptr), WorkID(nullptr) {
338   if (!CGM.getLangOpts().OpenMPIsDevice)
339     llvm_unreachable("OpenMP NVPTX can only handle device code.");
340
341   // Called once per module during initialization.
342   initializeEnvironment();
343 }