diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h --- a/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMPIRBuilder.h @@ -318,6 +318,32 @@ StringMap, BumpPtrAllocator> InternalVars; public: + /// Generator for __kmpc_copyprivate + /// + /// \param Loc The source location description. + /// \param BufSize Number of elements in the buffer. + /// \param CpyBuf List of pointers to data to be copied. + /// \param CpyFn function to call for copying data. + /// \param DidIt flag variable; 1 for 'single' thread, 0 otherwise. + /// + /// \return The insertion position *after* the CopyPrivate call. + + InsertPointTy CreateCopyPrivate(const LocationDescription &Loc, + llvm::Value *BufSize, llvm::Value *CpyBuf, + llvm::Value *CpyFn, llvm::Value *DidIt); + + /// Generator for '#omp single' + /// + /// \param Loc The source location description. + /// \param BodyGenCB Callback that will generate the region code. + /// \param FiniCB Callback to finalize variable copies. + /// \param DidIt Local variable used as a flag to indicate 'single' thread + /// + /// \returns The insertion position *after* the single call. + InsertPointTy CreateSingle(const LocationDescription &Loc, + BodyGenCallbackTy BodyGenCB, + FinalizeCallbackTy FiniCB, llvm::Value *DidIt); + /// Generator for '#omp master' /// /// \param Loc The insert and source location description. diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp --- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp +++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp @@ -804,6 +804,62 @@ /*Conditional*/ true, /*hasFinalize*/ true); } +OpenMPIRBuilder::InsertPointTy +OpenMPIRBuilder::CreateCopyPrivate(const LocationDescription &Loc, + llvm::Value *BufSize, llvm::Value *CpyBuf, + llvm::Value *CpyFn, llvm::Value *DidIt) { + if (!updateToLocation(Loc)) + return Loc.IP; + + Constant *SrcLocStr = getOrCreateSrcLocStr(Loc); + Value *Ident = getOrCreateIdent(SrcLocStr); + Value *ThreadId = getOrCreateThreadID(Ident); + + llvm::Value *DidItLD = Builder.CreateLoad(DidIt); + + Value *Args[] = {Ident, ThreadId, BufSize, CpyBuf, CpyFn, DidItLD}; + + Function *Fn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_copyprivate); + Builder.CreateCall(Fn, Args); + + return Builder.saveIP(); +} + +OpenMPIRBuilder::InsertPointTy +OpenMPIRBuilder::CreateSingle(const LocationDescription &Loc, + BodyGenCallbackTy BodyGenCB, + FinalizeCallbackTy FiniCB, llvm::Value *DidIt) { + + if (!updateToLocation(Loc)) + return Loc.IP; + + // If needed (i.e. not null), initialize `DidIt` with 0 + if (DidIt) { + Builder.CreateStore(Builder.getInt32(0), DidIt); + } + + Directive OMPD = Directive::OMPD_single; + Constant *SrcLocStr = getOrCreateSrcLocStr(Loc); + Value *Ident = getOrCreateIdent(SrcLocStr); + Value *ThreadId = getOrCreateThreadID(Ident); + Value *Args[] = {Ident, ThreadId}; + + Function *EntryRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_single); + Instruction *EntryCall = Builder.CreateCall(EntryRTLFn, Args); + + Function *ExitRTLFn = getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_end_single); + Instruction *ExitCall = Builder.CreateCall(ExitRTLFn, Args); + + // generates the following: + // if (__kmpc_single()) { + // .... single region ... + // __kmpc_end_single + // } + + return EmitOMPInlinedRegion(OMPD, EntryCall, ExitCall, BodyGenCB, FiniCB, + /*Conditional*/ true, /*hasFinalize*/ true); +} + OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::CreateCritical( const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst) { diff --git a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp --- a/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp +++ b/llvm/unittests/Frontend/OpenMPIRBuilderTest.cpp @@ -1021,4 +1021,84 @@ EXPECT_EQ(CopyinEnd,NotMasterBr->getSuccessor(0)); } +TEST_F(OpenMPIRBuilderTest, SingleDirective) { + using InsertPointTy = OpenMPIRBuilder::InsertPointTy; + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.initialize(); + F->setName("func"); + IRBuilder<> Builder(BB); + + OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); + + AllocaInst *PrivAI = nullptr; + + BasicBlock *EntryBB = nullptr; + BasicBlock *ExitBB = nullptr; + BasicBlock *ThenBB = nullptr; + + auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, + BasicBlock &FiniBB) { + if (AllocaIP.isSet()) + Builder.restoreIP(AllocaIP); + else + Builder.SetInsertPoint(&*(F->getEntryBlock().getFirstInsertionPt())); + PrivAI = Builder.CreateAlloca(F->arg_begin()->getType()); + Builder.CreateStore(F->arg_begin(), PrivAI); + + llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock(); + llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint(); + EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst); + + Builder.restoreIP(CodeGenIP); + + // collect some info for checks later + ExitBB = FiniBB.getUniqueSuccessor(); + ThenBB = Builder.GetInsertBlock(); + EntryBB = ThenBB->getUniquePredecessor(); + + // simple instructions for body + Value *PrivLoad = Builder.CreateLoad(PrivAI, "local.use"); + Builder.CreateICmpNE(F->arg_begin(), PrivLoad); + }; + + auto FiniCB = [&](InsertPointTy IP) { + BasicBlock *IPBB = IP.getBlock(); + EXPECT_NE(IPBB->end(), IP.getPoint()); + }; + + Builder.restoreIP( + OMPBuilder.CreateSingle(Builder, BodyGenCB, FiniCB, /*DidIt*/ nullptr)); + Value *EntryBBTI = EntryBB->getTerminator(); + EXPECT_NE(EntryBBTI, nullptr); + EXPECT_TRUE(isa(EntryBBTI)); + BranchInst *EntryBr = cast(EntryBB->getTerminator()); + EXPECT_TRUE(EntryBr->isConditional()); + EXPECT_EQ(EntryBr->getSuccessor(0), ThenBB); + EXPECT_EQ(ThenBB->getUniqueSuccessor(), ExitBB); + EXPECT_EQ(EntryBr->getSuccessor(1), ExitBB); + + CmpInst *CondInst = cast(EntryBr->getCondition()); + EXPECT_TRUE(isa(CondInst->getOperand(0))); + + CallInst *SingleEntryCI = cast(CondInst->getOperand(0)); + EXPECT_EQ(SingleEntryCI->getNumArgOperands(), 2U); + EXPECT_EQ(SingleEntryCI->getCalledFunction()->getName(), "__kmpc_single"); + EXPECT_TRUE(isa(SingleEntryCI->getArgOperand(0))); + + CallInst *SingleEndCI = nullptr; + for (auto &FI : *ThenBB) { + Instruction *cur = &FI; + if (isa(cur)) { + SingleEndCI = cast(cur); + if (SingleEndCI->getCalledFunction()->getName() == "__kmpc_end_single") + break; + SingleEndCI = nullptr; + } + } + EXPECT_NE(SingleEndCI, nullptr); + EXPECT_EQ(SingleEndCI->getNumArgOperands(), 2U); + EXPECT_TRUE(isa(SingleEndCI->getArgOperand(0))); + EXPECT_EQ(SingleEndCI->getArgOperand(1), SingleEntryCI->getArgOperand(1)); +} + } // namespace