blob: 942cae6727219b2de0b314cf0737a4fe3cdea8be [file] [log] [blame]
Samuel Antao45bfe4c2016-02-08 15:59:20 +00001//===---- 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"
Alexey Bataevc5b1d322016-03-04 09:22:22 +000016#include "clang/AST/DeclOpenMP.h"
Carlo Bertollic6872252016-04-04 15:55:02 +000017#include "CodeGenFunction.h"
18#include "clang/AST/StmtOpenMP.h"
Samuel Antao45bfe4c2016-02-08 15:59:20 +000019
20using namespace clang;
21using namespace CodeGen;
22
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000023namespace {
24enum OpenMPRTLFunctionNVPTX {
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +000025 /// \brief Call to void __kmpc_kernel_init(kmp_int32 thread_limit);
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000026 OMPRTL_NVPTX__kmpc_kernel_init,
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +000027 /// \brief Call to void __kmpc_kernel_deinit();
28 OMPRTL_NVPTX__kmpc_kernel_deinit,
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000029};
30} // namespace
31
32/// Get the GPU warp size.
33static llvm::Value *getNVPTXWarpSize(CodeGenFunction &CGF) {
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000034 CGBuilderTy &Bld = CGF.Builder;
35 return Bld.CreateCall(
36 llvm::Intrinsic::getDeclaration(
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000037 &CGF.CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_warpsize),
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000038 llvm::None, "nvptx_warp_size");
39}
40
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000041/// Get the id of the current thread on the GPU.
42static llvm::Value *getNVPTXThreadID(CodeGenFunction &CGF) {
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000043 CGBuilderTy &Bld = CGF.Builder;
44 return Bld.CreateCall(
45 llvm::Intrinsic::getDeclaration(
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000046 &CGF.CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_tid_x),
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000047 llvm::None, "nvptx_tid");
48}
49
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000050/// Get the maximum number of threads in a block of the GPU.
51static llvm::Value *getNVPTXNumThreads(CodeGenFunction &CGF) {
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000052 CGBuilderTy &Bld = CGF.Builder;
53 return Bld.CreateCall(
54 llvm::Intrinsic::getDeclaration(
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000055 &CGF.CGM.getModule(), llvm::Intrinsic::nvvm_read_ptx_sreg_ntid_x),
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000056 llvm::None, "nvptx_num_threads");
57}
58
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000059/// Get barrier to synchronize all threads in a block.
60static void getNVPTXCTABarrier(CodeGenFunction &CGF) {
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000061 CGBuilderTy &Bld = CGF.Builder;
62 Bld.CreateCall(llvm::Intrinsic::getDeclaration(
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000063 &CGF.CGM.getModule(), llvm::Intrinsic::nvvm_barrier0));
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000064}
65
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000066/// Synchronize all GPU threads in a block.
67static void syncCTAThreads(CodeGenFunction &CGF) { getNVPTXCTABarrier(CGF); }
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000068
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +000069/// Get the value of the thread_limit clause in the teams directive.
70/// The runtime encodes thread_limit in the launch parameter, always starting
71/// thread_limit+warpSize threads per team.
72static llvm::Value *getThreadLimit(CodeGenFunction &CGF) {
73 CGBuilderTy &Bld = CGF.Builder;
74 return Bld.CreateSub(getNVPTXNumThreads(CGF), getNVPTXWarpSize(CGF),
75 "thread_limit");
76}
77
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000078/// Get the thread id of the OMP master thread.
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000079/// The master thread id is the first thread (lane) of the last warp in the
80/// GPU block. Warp size is assumed to be some power of 2.
81/// Thread id is 0 indexed.
82/// E.g: If NumThreads is 33, master id is 32.
83/// If NumThreads is 64, master id is 32.
84/// If NumThreads is 1024, master id is 992.
Arpith Chacko Jacobccf2f732017-01-03 20:19:56 +000085static llvm::Value *getMasterThreadID(CodeGenFunction &CGF) {
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000086 CGBuilderTy &Bld = CGF.Builder;
87 llvm::Value *NumThreads = getNVPTXNumThreads(CGF);
88
89 // We assume that the warp size is a power of 2.
90 llvm::Value *Mask = Bld.CreateSub(getNVPTXWarpSize(CGF), Bld.getInt32(1));
91
92 return Bld.CreateAnd(Bld.CreateSub(NumThreads, Bld.getInt32(1)),
93 Bld.CreateNot(Mask), "master_tid");
94}
95
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +000096CGOpenMPRuntimeNVPTX::WorkerFunctionState::WorkerFunctionState(
97 CodeGenModule &CGM)
98 : WorkerFn(nullptr), CGFI(nullptr) {
99 createWorkerFunction(CGM);
Vasileios Kalintirise5c09592016-03-22 10:41:20 +0000100}
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000101
102void CGOpenMPRuntimeNVPTX::WorkerFunctionState::createWorkerFunction(
103 CodeGenModule &CGM) {
104 // Create an worker function with no arguments.
105 CGFI = &CGM.getTypes().arrangeNullaryFunction();
106
107 WorkerFn = llvm::Function::Create(
108 CGM.getTypes().GetFunctionType(*CGFI), llvm::GlobalValue::InternalLinkage,
109 /* placeholder */ "_worker", &CGM.getModule());
110 CGM.SetInternalFunctionAttributes(/*D=*/nullptr, WorkerFn, *CGFI);
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000111}
112
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000113void CGOpenMPRuntimeNVPTX::emitGenericKernel(const OMPExecutableDirective &D,
114 StringRef ParentName,
115 llvm::Function *&OutlinedFn,
116 llvm::Constant *&OutlinedFnID,
117 bool IsOffloadEntry,
118 const RegionCodeGenTy &CodeGen) {
119 EntryFunctionState EST;
120 WorkerFunctionState WST(CGM);
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000121
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000122 // Emit target region as a standalone region.
123 class NVPTXPrePostActionTy : public PrePostActionTy {
124 CGOpenMPRuntimeNVPTX &RT;
125 CGOpenMPRuntimeNVPTX::EntryFunctionState &EST;
126 CGOpenMPRuntimeNVPTX::WorkerFunctionState &WST;
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000127
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000128 public:
129 NVPTXPrePostActionTy(CGOpenMPRuntimeNVPTX &RT,
130 CGOpenMPRuntimeNVPTX::EntryFunctionState &EST,
131 CGOpenMPRuntimeNVPTX::WorkerFunctionState &WST)
132 : RT(RT), EST(EST), WST(WST) {}
133 void Enter(CodeGenFunction &CGF) override {
134 RT.emitGenericEntryHeader(CGF, EST, WST);
135 }
136 void Exit(CodeGenFunction &CGF) override {
137 RT.emitGenericEntryFooter(CGF, EST);
138 }
139 } Action(*this, EST, WST);
140 CodeGen.setAction(Action);
141 emitTargetOutlinedFunctionHelper(D, ParentName, OutlinedFn, OutlinedFnID,
142 IsOffloadEntry, CodeGen);
143
144 // Create the worker function
145 emitWorkerFunction(WST);
146
147 // Now change the name of the worker function to correspond to this target
148 // region's entry function.
149 WST.WorkerFn->setName(OutlinedFn->getName() + "_worker");
150}
151
152// Setup NVPTX threads for master-worker OpenMP scheme.
153void CGOpenMPRuntimeNVPTX::emitGenericEntryHeader(CodeGenFunction &CGF,
154 EntryFunctionState &EST,
155 WorkerFunctionState &WST) {
156 CGBuilderTy &Bld = CGF.Builder;
157
158 llvm::BasicBlock *WorkerBB = CGF.createBasicBlock(".worker");
159 llvm::BasicBlock *MasterCheckBB = CGF.createBasicBlock(".mastercheck");
160 llvm::BasicBlock *MasterBB = CGF.createBasicBlock(".master");
161 EST.ExitBB = CGF.createBasicBlock(".exit");
162
163 auto *IsWorker =
164 Bld.CreateICmpULT(getNVPTXThreadID(CGF), getThreadLimit(CGF));
165 Bld.CreateCondBr(IsWorker, WorkerBB, MasterCheckBB);
166
167 CGF.EmitBlock(WorkerBB);
168 CGF.EmitCallOrInvoke(WST.WorkerFn, llvm::None);
169 CGF.EmitBranch(EST.ExitBB);
170
171 CGF.EmitBlock(MasterCheckBB);
172 auto *IsMaster =
173 Bld.CreateICmpEQ(getNVPTXThreadID(CGF), getMasterThreadID(CGF));
174 Bld.CreateCondBr(IsMaster, MasterBB, EST.ExitBB);
175
176 CGF.EmitBlock(MasterBB);
177 // First action in sequential region:
178 // Initialize the state of the OpenMP runtime library on the GPU.
179 llvm::Value *Args[] = {getThreadLimit(CGF)};
180 CGF.EmitRuntimeCall(
181 createNVPTXRuntimeFunction(OMPRTL_NVPTX__kmpc_kernel_init), Args);
182}
183
184void CGOpenMPRuntimeNVPTX::emitGenericEntryFooter(CodeGenFunction &CGF,
185 EntryFunctionState &EST) {
186 if (!EST.ExitBB)
187 EST.ExitBB = CGF.createBasicBlock(".exit");
188
189 llvm::BasicBlock *TerminateBB = CGF.createBasicBlock(".termination.notifier");
190 CGF.EmitBranch(TerminateBB);
191
192 CGF.EmitBlock(TerminateBB);
193 // Signal termination condition.
194 CGF.EmitRuntimeCall(
195 createNVPTXRuntimeFunction(OMPRTL_NVPTX__kmpc_kernel_deinit), None);
196 // Barrier to terminate worker threads.
197 syncCTAThreads(CGF);
198 // Master thread jumps to exit point.
199 CGF.EmitBranch(EST.ExitBB);
200
201 CGF.EmitBlock(EST.ExitBB);
202 EST.ExitBB = nullptr;
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000203}
204
205void CGOpenMPRuntimeNVPTX::emitWorkerFunction(WorkerFunctionState &WST) {
206 auto &Ctx = CGM.getContext();
207
208 CodeGenFunction CGF(CGM, /*suppressNewContext=*/true);
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000209 CGF.disableDebugInfo();
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000210 CGF.StartFunction(GlobalDecl(), Ctx.VoidTy, WST.WorkerFn, *WST.CGFI, {});
211 emitWorkerLoop(CGF, WST);
212 CGF.FinishFunction();
213}
214
215void CGOpenMPRuntimeNVPTX::emitWorkerLoop(CodeGenFunction &CGF,
216 WorkerFunctionState &WST) {
217 //
218 // The workers enter this loop and wait for parallel work from the master.
219 // When the master encounters a parallel region it sets up the work + variable
220 // arguments, and wakes up the workers. The workers first check to see if
221 // they are required for the parallel region, i.e., within the # of requested
222 // parallel threads. The activated workers load the variable arguments and
223 // execute the parallel work.
224 //
225
226 CGBuilderTy &Bld = CGF.Builder;
227
228 llvm::BasicBlock *AwaitBB = CGF.createBasicBlock(".await.work");
229 llvm::BasicBlock *SelectWorkersBB = CGF.createBasicBlock(".select.workers");
230 llvm::BasicBlock *ExecuteBB = CGF.createBasicBlock(".execute.parallel");
231 llvm::BasicBlock *TerminateBB = CGF.createBasicBlock(".terminate.parallel");
232 llvm::BasicBlock *BarrierBB = CGF.createBasicBlock(".barrier.parallel");
233 llvm::BasicBlock *ExitBB = CGF.createBasicBlock(".exit");
234
235 CGF.EmitBranch(AwaitBB);
236
237 // Workers wait for work from master.
238 CGF.EmitBlock(AwaitBB);
239 // Wait for parallel work
240 syncCTAThreads(CGF);
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000241
242 Address WorkFn =
243 CGF.CreateDefaultAlignTempAlloca(CGF.Int8PtrTy, /*Name=*/"work_fn");
244 Address ExecStatus =
245 CGF.CreateDefaultAlignTempAlloca(CGF.Int8Ty, /*Name=*/"exec_status");
246 CGF.InitTempAlloca(ExecStatus, Bld.getInt8(/*C=*/0));
247 CGF.InitTempAlloca(WorkFn, llvm::Constant::getNullValue(CGF.Int8PtrTy));
248
249 // TODO: Call into runtime to get parallel work.
250
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000251 // On termination condition (workid == 0), exit loop.
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000252 llvm::Value *ShouldTerminate =
253 Bld.CreateIsNull(Bld.CreateLoad(WorkFn), "should_terminate");
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000254 Bld.CreateCondBr(ShouldTerminate, ExitBB, SelectWorkersBB);
255
256 // Activate requested workers.
257 CGF.EmitBlock(SelectWorkersBB);
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000258 llvm::Value *IsActive =
259 Bld.CreateIsNotNull(Bld.CreateLoad(ExecStatus), "is_active");
260 Bld.CreateCondBr(IsActive, ExecuteBB, BarrierBB);
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000261
262 // Signal start of parallel region.
263 CGF.EmitBlock(ExecuteBB);
264 // TODO: Add parallel work.
265
266 // Signal end of parallel region.
267 CGF.EmitBlock(TerminateBB);
268 CGF.EmitBranch(BarrierBB);
269
270 // All active and inactive workers wait at a barrier after parallel region.
271 CGF.EmitBlock(BarrierBB);
272 // Barrier after parallel region.
273 syncCTAThreads(CGF);
274 CGF.EmitBranch(AwaitBB);
275
276 // Exit target region.
277 CGF.EmitBlock(ExitBB);
278}
279
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000280/// \brief Returns specified OpenMP runtime function for the current OpenMP
281/// implementation. Specialized for the NVPTX device.
282/// \param Function OpenMP runtime function.
283/// \return Specified function.
284llvm::Constant *
285CGOpenMPRuntimeNVPTX::createNVPTXRuntimeFunction(unsigned Function) {
286 llvm::Constant *RTLFn = nullptr;
287 switch (static_cast<OpenMPRTLFunctionNVPTX>(Function)) {
288 case OMPRTL_NVPTX__kmpc_kernel_init: {
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000289 // Build void __kmpc_kernel_init(kmp_int32 thread_limit);
290 llvm::Type *TypeParams[] = {CGM.Int32Ty};
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000291 llvm::FunctionType *FnTy =
292 llvm::FunctionType::get(CGM.VoidTy, TypeParams, /*isVarArg*/ false);
293 RTLFn = CGM.CreateRuntimeFunction(FnTy, "__kmpc_kernel_init");
294 break;
295 }
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000296 case OMPRTL_NVPTX__kmpc_kernel_deinit: {
297 // Build void __kmpc_kernel_deinit();
298 llvm::FunctionType *FnTy =
299 llvm::FunctionType::get(CGM.VoidTy, {}, /*isVarArg*/ false);
300 RTLFn = CGM.CreateRuntimeFunction(FnTy, "__kmpc_kernel_deinit");
301 break;
302 }
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000303 }
304 return RTLFn;
305}
306
307void CGOpenMPRuntimeNVPTX::createOffloadEntry(llvm::Constant *ID,
308 llvm::Constant *Addr,
309 uint64_t Size) {
310 auto *F = dyn_cast<llvm::Function>(Addr);
311 // TODO: Add support for global variables on the device after declare target
312 // support.
313 if (!F)
314 return;
315 llvm::Module *M = F->getParent();
316 llvm::LLVMContext &Ctx = M->getContext();
317
318 // Get "nvvm.annotations" metadata node
319 llvm::NamedMDNode *MD = M->getOrInsertNamedMetadata("nvvm.annotations");
320
321 llvm::Metadata *MDVals[] = {
322 llvm::ConstantAsMetadata::get(F), llvm::MDString::get(Ctx, "kernel"),
323 llvm::ConstantAsMetadata::get(
324 llvm::ConstantInt::get(llvm::Type::getInt32Ty(Ctx), 1))};
325 // Append metadata to nvvm.annotations
326 MD->addOperand(llvm::MDNode::get(Ctx, MDVals));
327}
328
329void CGOpenMPRuntimeNVPTX::emitTargetOutlinedFunction(
330 const OMPExecutableDirective &D, StringRef ParentName,
331 llvm::Function *&OutlinedFn, llvm::Constant *&OutlinedFnID,
Alexey Bataev14fa1c62016-03-29 05:34:15 +0000332 bool IsOffloadEntry, const RegionCodeGenTy &CodeGen) {
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000333 if (!IsOffloadEntry) // Nothing to do.
334 return;
335
336 assert(!ParentName.empty() && "Invalid target region parent name!");
337
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000338 emitGenericKernel(D, ParentName, OutlinedFn, OutlinedFnID, IsOffloadEntry,
339 CodeGen);
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000340}
341
Samuel Antao45bfe4c2016-02-08 15:59:20 +0000342CGOpenMPRuntimeNVPTX::CGOpenMPRuntimeNVPTX(CodeGenModule &CGM)
Arpith Chacko Jacob406acdb2017-01-05 15:24:05 +0000343 : CGOpenMPRuntime(CGM) {
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000344 if (!CGM.getLangOpts().OpenMPIsDevice)
345 llvm_unreachable("OpenMP NVPTX can only handle device code.");
Arpith Chacko Jacob5c309e42016-03-22 01:48:56 +0000346}
Carlo Bertollic6872252016-04-04 15:55:02 +0000347
348void CGOpenMPRuntimeNVPTX::emitNumTeamsClause(CodeGenFunction &CGF,
349 const Expr *NumTeams,
350 const Expr *ThreadLimit,
351 SourceLocation Loc) {}
352
353llvm::Value *CGOpenMPRuntimeNVPTX::emitParallelOrTeamsOutlinedFunction(
354 const OMPExecutableDirective &D, const VarDecl *ThreadIDVar,
355 OpenMPDirectiveKind InnermostKind, const RegionCodeGenTy &CodeGen) {
356
357 llvm::Function *OutlinedFun = nullptr;
358 if (isa<OMPTeamsDirective>(D)) {
359 llvm::Value *OutlinedFunVal =
360 CGOpenMPRuntime::emitParallelOrTeamsOutlinedFunction(
361 D, ThreadIDVar, InnermostKind, CodeGen);
362 OutlinedFun = cast<llvm::Function>(OutlinedFunVal);
Chandler Carruthfcd33142016-12-23 01:24:49 +0000363 OutlinedFun->removeFnAttr(llvm::Attribute::NoInline);
Carlo Bertollic6872252016-04-04 15:55:02 +0000364 OutlinedFun->addFnAttr(llvm::Attribute::AlwaysInline);
365 } else
366 llvm_unreachable("parallel directive is not yet supported for nvptx "
367 "backend.");
368
369 return OutlinedFun;
370}
371
372void CGOpenMPRuntimeNVPTX::emitTeamsCall(CodeGenFunction &CGF,
373 const OMPExecutableDirective &D,
374 SourceLocation Loc,
375 llvm::Value *OutlinedFn,
376 ArrayRef<llvm::Value *> CapturedVars) {
377 if (!CGF.HaveInsertPoint())
378 return;
379
380 Address ZeroAddr =
381 CGF.CreateTempAlloca(CGF.Int32Ty, CharUnits::fromQuantity(4),
382 /*Name*/ ".zero.addr");
383 CGF.InitTempAlloca(ZeroAddr, CGF.Builder.getInt32(/*C*/ 0));
384 llvm::SmallVector<llvm::Value *, 16> OutlinedFnArgs;
385 OutlinedFnArgs.push_back(ZeroAddr.getPointer());
386 OutlinedFnArgs.push_back(ZeroAddr.getPointer());
387 OutlinedFnArgs.append(CapturedVars.begin(), CapturedVars.end());
388 CGF.EmitCallOrInvoke(OutlinedFn, OutlinedFnArgs);
389}