| //===- llvm/unittest/IR/OpenMPIRBuilderTest.cpp - OpenMPIRBuilder tests ---===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" |
| #include "llvm/IR/BasicBlock.h" |
| #include "llvm/IR/DIBuilder.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Frontend/OpenMP/OMPConstants.h" |
| #include "llvm/IR/Verifier.h" |
| #include "llvm/Transforms/Utils/BasicBlockUtils.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| using namespace omp; |
| |
| namespace { |
| |
| class OpenMPIRBuilderTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| M.reset(new Module("MyModule", Ctx)); |
| FunctionType *FTy = |
| FunctionType::get(Type::getVoidTy(Ctx), {Type::getInt32Ty(Ctx)}, |
| /*isVarArg=*/false); |
| F = Function::Create(FTy, Function::ExternalLinkage, "", M.get()); |
| BB = BasicBlock::Create(Ctx, "", F); |
| |
| DIBuilder DIB(*M); |
| auto File = DIB.createFile("test.dbg", "/"); |
| auto CU = |
| DIB.createCompileUnit(dwarf::DW_LANG_C, File, "llvm-C", true, "", 0); |
| auto Type = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None)); |
| auto SP = DIB.createFunction( |
| CU, "foo", "", File, 1, Type, 1, DINode::FlagZero, |
| DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized); |
| F->setSubprogram(SP); |
| auto Scope = DIB.createLexicalBlockFile(SP, File, 0); |
| DIB.finalize(); |
| DL = DebugLoc::get(3, 7, Scope); |
| } |
| |
| void TearDown() override { |
| BB = nullptr; |
| M.reset(); |
| } |
| |
| LLVMContext Ctx; |
| std::unique_ptr<Module> M; |
| Function *F; |
| BasicBlock *BB; |
| DebugLoc DL; |
| }; |
| |
| TEST_F(OpenMPIRBuilderTest, CreateBarrier) { |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| |
| IRBuilder<> Builder(BB); |
| |
| OMPBuilder.CreateBarrier({IRBuilder<>::InsertPoint()}, OMPD_for); |
| EXPECT_TRUE(M->global_empty()); |
| EXPECT_EQ(M->size(), 1U); |
| EXPECT_EQ(F->size(), 1U); |
| EXPECT_EQ(BB->size(), 0U); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()}); |
| OMPBuilder.CreateBarrier(Loc, OMPD_for); |
| EXPECT_FALSE(M->global_empty()); |
| EXPECT_EQ(M->size(), 3U); |
| EXPECT_EQ(F->size(), 1U); |
| EXPECT_EQ(BB->size(), 2U); |
| |
| CallInst *GTID = dyn_cast<CallInst>(&BB->front()); |
| EXPECT_NE(GTID, nullptr); |
| EXPECT_EQ(GTID->getNumArgOperands(), 1U); |
| EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num"); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory()); |
| |
| CallInst *Barrier = dyn_cast<CallInst>(GTID->getNextNode()); |
| EXPECT_NE(Barrier, nullptr); |
| EXPECT_EQ(Barrier->getNumArgOperands(), 2U); |
| EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_barrier"); |
| EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory()); |
| |
| EXPECT_EQ(cast<CallInst>(Barrier)->getArgOperand(1), GTID); |
| |
| Builder.CreateUnreachable(); |
| EXPECT_FALSE(verifyModule(*M, &errs())); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, CreateCancel) { |
| using InsertPointTy = OpenMPIRBuilder::InsertPointTy; |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| |
| BasicBlock *CBB = BasicBlock::Create(Ctx, "", F); |
| new UnreachableInst(Ctx, CBB); |
| auto FiniCB = [&](InsertPointTy IP) { |
| ASSERT_NE(IP.getBlock(), nullptr); |
| ASSERT_EQ(IP.getBlock()->end(), IP.getPoint()); |
| BranchInst::Create(CBB, IP.getBlock()); |
| }; |
| OMPBuilder.pushFinalizationCB({FiniCB, OMPD_parallel, true}); |
| |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()}); |
| auto NewIP = OMPBuilder.CreateCancel(Loc, nullptr, OMPD_parallel); |
| Builder.restoreIP(NewIP); |
| EXPECT_FALSE(M->global_empty()); |
| EXPECT_EQ(M->size(), 3U); |
| EXPECT_EQ(F->size(), 4U); |
| EXPECT_EQ(BB->size(), 4U); |
| |
| CallInst *GTID = dyn_cast<CallInst>(&BB->front()); |
| EXPECT_NE(GTID, nullptr); |
| EXPECT_EQ(GTID->getNumArgOperands(), 1U); |
| EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num"); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory()); |
| |
| CallInst *Cancel = dyn_cast<CallInst>(GTID->getNextNode()); |
| EXPECT_NE(Cancel, nullptr); |
| EXPECT_EQ(Cancel->getNumArgOperands(), 3U); |
| EXPECT_EQ(Cancel->getCalledFunction()->getName(), "__kmpc_cancel"); |
| EXPECT_FALSE(Cancel->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(Cancel->getCalledFunction()->doesNotFreeMemory()); |
| EXPECT_EQ(Cancel->getNumUses(), 1U); |
| Instruction *CancelBBTI = Cancel->getParent()->getTerminator(); |
| EXPECT_EQ(CancelBBTI->getNumSuccessors(), 2U); |
| EXPECT_EQ(CancelBBTI->getSuccessor(0), NewIP.getBlock()); |
| EXPECT_EQ(CancelBBTI->getSuccessor(1)->size(), 1U); |
| EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getNumSuccessors(), |
| 1U); |
| EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getSuccessor(0), |
| CBB); |
| |
| EXPECT_EQ(cast<CallInst>(Cancel)->getArgOperand(1), GTID); |
| |
| OMPBuilder.popFinalizationCB(); |
| |
| Builder.CreateUnreachable(); |
| EXPECT_FALSE(verifyModule(*M, &errs())); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, CreateCancelIfCond) { |
| using InsertPointTy = OpenMPIRBuilder::InsertPointTy; |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| |
| BasicBlock *CBB = BasicBlock::Create(Ctx, "", F); |
| new UnreachableInst(Ctx, CBB); |
| auto FiniCB = [&](InsertPointTy IP) { |
| ASSERT_NE(IP.getBlock(), nullptr); |
| ASSERT_EQ(IP.getBlock()->end(), IP.getPoint()); |
| BranchInst::Create(CBB, IP.getBlock()); |
| }; |
| OMPBuilder.pushFinalizationCB({FiniCB, OMPD_parallel, true}); |
| |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()}); |
| auto NewIP = OMPBuilder.CreateCancel(Loc, Builder.getTrue(), OMPD_parallel); |
| Builder.restoreIP(NewIP); |
| EXPECT_FALSE(M->global_empty()); |
| EXPECT_EQ(M->size(), 3U); |
| EXPECT_EQ(F->size(), 7U); |
| EXPECT_EQ(BB->size(), 1U); |
| ASSERT_TRUE(isa<BranchInst>(BB->getTerminator())); |
| ASSERT_EQ(BB->getTerminator()->getNumSuccessors(), 2U); |
| BB = BB->getTerminator()->getSuccessor(0); |
| EXPECT_EQ(BB->size(), 4U); |
| |
| |
| CallInst *GTID = dyn_cast<CallInst>(&BB->front()); |
| EXPECT_NE(GTID, nullptr); |
| EXPECT_EQ(GTID->getNumArgOperands(), 1U); |
| EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num"); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory()); |
| |
| CallInst *Cancel = dyn_cast<CallInst>(GTID->getNextNode()); |
| EXPECT_NE(Cancel, nullptr); |
| EXPECT_EQ(Cancel->getNumArgOperands(), 3U); |
| EXPECT_EQ(Cancel->getCalledFunction()->getName(), "__kmpc_cancel"); |
| EXPECT_FALSE(Cancel->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(Cancel->getCalledFunction()->doesNotFreeMemory()); |
| EXPECT_EQ(Cancel->getNumUses(), 1U); |
| Instruction *CancelBBTI = Cancel->getParent()->getTerminator(); |
| EXPECT_EQ(CancelBBTI->getNumSuccessors(), 2U); |
| EXPECT_EQ(CancelBBTI->getSuccessor(0)->size(), 1U); |
| EXPECT_EQ(CancelBBTI->getSuccessor(0)->getUniqueSuccessor(), NewIP.getBlock()); |
| EXPECT_EQ(CancelBBTI->getSuccessor(1)->size(), 1U); |
| EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getNumSuccessors(), |
| 1U); |
| EXPECT_EQ(CancelBBTI->getSuccessor(1)->getTerminator()->getSuccessor(0), |
| CBB); |
| |
| EXPECT_EQ(cast<CallInst>(Cancel)->getArgOperand(1), GTID); |
| |
| OMPBuilder.popFinalizationCB(); |
| |
| Builder.CreateUnreachable(); |
| EXPECT_FALSE(verifyModule(*M, &errs())); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, CreateCancelBarrier) { |
| using InsertPointTy = OpenMPIRBuilder::InsertPointTy; |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| |
| BasicBlock *CBB = BasicBlock::Create(Ctx, "", F); |
| new UnreachableInst(Ctx, CBB); |
| auto FiniCB = [&](InsertPointTy IP) { |
| ASSERT_NE(IP.getBlock(), nullptr); |
| ASSERT_EQ(IP.getBlock()->end(), IP.getPoint()); |
| BranchInst::Create(CBB, IP.getBlock()); |
| }; |
| OMPBuilder.pushFinalizationCB({FiniCB, OMPD_parallel, true}); |
| |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP()}); |
| auto NewIP = OMPBuilder.CreateBarrier(Loc, OMPD_for); |
| Builder.restoreIP(NewIP); |
| EXPECT_FALSE(M->global_empty()); |
| EXPECT_EQ(M->size(), 3U); |
| EXPECT_EQ(F->size(), 4U); |
| EXPECT_EQ(BB->size(), 4U); |
| |
| CallInst *GTID = dyn_cast<CallInst>(&BB->front()); |
| EXPECT_NE(GTID, nullptr); |
| EXPECT_EQ(GTID->getNumArgOperands(), 1U); |
| EXPECT_EQ(GTID->getCalledFunction()->getName(), "__kmpc_global_thread_num"); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(GTID->getCalledFunction()->doesNotFreeMemory()); |
| |
| CallInst *Barrier = dyn_cast<CallInst>(GTID->getNextNode()); |
| EXPECT_NE(Barrier, nullptr); |
| EXPECT_EQ(Barrier->getNumArgOperands(), 2U); |
| EXPECT_EQ(Barrier->getCalledFunction()->getName(), "__kmpc_cancel_barrier"); |
| EXPECT_FALSE(Barrier->getCalledFunction()->doesNotAccessMemory()); |
| EXPECT_FALSE(Barrier->getCalledFunction()->doesNotFreeMemory()); |
| EXPECT_EQ(Barrier->getNumUses(), 1U); |
| Instruction *BarrierBBTI = Barrier->getParent()->getTerminator(); |
| EXPECT_EQ(BarrierBBTI->getNumSuccessors(), 2U); |
| EXPECT_EQ(BarrierBBTI->getSuccessor(0), NewIP.getBlock()); |
| EXPECT_EQ(BarrierBBTI->getSuccessor(1)->size(), 1U); |
| EXPECT_EQ(BarrierBBTI->getSuccessor(1)->getTerminator()->getNumSuccessors(), |
| 1U); |
| EXPECT_EQ(BarrierBBTI->getSuccessor(1)->getTerminator()->getSuccessor(0), |
| CBB); |
| |
| EXPECT_EQ(cast<CallInst>(Barrier)->getArgOperand(1), GTID); |
| |
| OMPBuilder.popFinalizationCB(); |
| |
| Builder.CreateUnreachable(); |
| EXPECT_FALSE(verifyModule(*M, &errs())); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, DbgLoc) { |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| F->setName("func"); |
| |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); |
| OMPBuilder.CreateBarrier(Loc, OMPD_for); |
| CallInst *GTID = dyn_cast<CallInst>(&BB->front()); |
| CallInst *Barrier = dyn_cast<CallInst>(GTID->getNextNode()); |
| EXPECT_EQ(GTID->getDebugLoc(), DL); |
| EXPECT_EQ(Barrier->getDebugLoc(), DL); |
| EXPECT_TRUE(isa<GlobalVariable>(Barrier->getOperand(0))); |
| if (!isa<GlobalVariable>(Barrier->getOperand(0))) |
| return; |
| GlobalVariable *Ident = cast<GlobalVariable>(Barrier->getOperand(0)); |
| EXPECT_TRUE(Ident->hasInitializer()); |
| if (!Ident->hasInitializer()) |
| return; |
| Constant *Initializer = Ident->getInitializer(); |
| EXPECT_TRUE( |
| isa<GlobalVariable>(Initializer->getOperand(4)->stripPointerCasts())); |
| GlobalVariable *SrcStrGlob = |
| cast<GlobalVariable>(Initializer->getOperand(4)->stripPointerCasts()); |
| if (!SrcStrGlob) |
| return; |
| EXPECT_TRUE(isa<ConstantDataArray>(SrcStrGlob->getInitializer())); |
| ConstantDataArray *SrcSrc = |
| dyn_cast<ConstantDataArray>(SrcStrGlob->getInitializer()); |
| if (!SrcSrc) |
| return; |
| EXPECT_EQ(SrcSrc->getAsCString(), ";test.dbg;foo;3;7;;"); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, ParallelSimple) { |
| using InsertPointTy = OpenMPIRBuilder::InsertPointTy; |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| F->setName("func"); |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); |
| |
| AllocaInst *PrivAI = nullptr; |
| |
| unsigned NumBodiesGenerated = 0; |
| unsigned NumPrivatizedVars = 0; |
| unsigned NumFinalizationPoints = 0; |
| |
| auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, |
| BasicBlock &ContinuationIP) { |
| ++NumBodiesGenerated; |
| |
| Builder.restoreIP(AllocaIP); |
| PrivAI = Builder.CreateAlloca(F->arg_begin()->getType()); |
| Builder.CreateStore(F->arg_begin(), PrivAI); |
| |
| Builder.restoreIP(CodeGenIP); |
| Value *PrivLoad = Builder.CreateLoad(PrivAI, "local.use"); |
| Value *Cmp = Builder.CreateICmpNE(F->arg_begin(), PrivLoad); |
| Instruction *ThenTerm, *ElseTerm; |
| SplitBlockAndInsertIfThenElse(Cmp, CodeGenIP.getBlock()->getTerminator(), |
| &ThenTerm, &ElseTerm); |
| |
| Builder.SetInsertPoint(ThenTerm); |
| Builder.CreateBr(&ContinuationIP); |
| ThenTerm->eraseFromParent(); |
| }; |
| |
| auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, |
| Value &VPtr, Value *&ReplacementValue) -> InsertPointTy { |
| ++NumPrivatizedVars; |
| |
| if (!isa<AllocaInst>(VPtr)) { |
| EXPECT_EQ(&VPtr, F->arg_begin()); |
| ReplacementValue = &VPtr; |
| return CodeGenIP; |
| } |
| |
| // Trivial copy (=firstprivate). |
| Builder.restoreIP(AllocaIP); |
| Type *VTy = VPtr.getType()->getPointerElementType(); |
| Value *V = Builder.CreateLoad(VTy, &VPtr, VPtr.getName() + ".reload"); |
| ReplacementValue = Builder.CreateAlloca(VTy, 0, VPtr.getName() + ".copy"); |
| Builder.restoreIP(CodeGenIP); |
| Builder.CreateStore(V, ReplacementValue); |
| return CodeGenIP; |
| }; |
| |
| auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; }; |
| |
| IRBuilder<>::InsertPoint AfterIP = |
| OMPBuilder.CreateParallel(Loc, BodyGenCB, PrivCB, FiniCB, nullptr, |
| nullptr, OMP_PROC_BIND_default, false); |
| EXPECT_EQ(NumBodiesGenerated, 1U); |
| EXPECT_EQ(NumPrivatizedVars, 1U); |
| EXPECT_EQ(NumFinalizationPoints, 1U); |
| |
| Builder.restoreIP(AfterIP); |
| Builder.CreateRetVoid(); |
| |
| OMPBuilder.finalize(); |
| |
| EXPECT_NE(PrivAI, nullptr); |
| Function *OutlinedFn = PrivAI->getFunction(); |
| EXPECT_NE(F, OutlinedFn); |
| EXPECT_FALSE(verifyModule(*M, &errs())); |
| EXPECT_TRUE(OutlinedFn->hasFnAttribute(Attribute::NoUnwind)); |
| EXPECT_TRUE(OutlinedFn->hasFnAttribute(Attribute::NoRecurse)); |
| EXPECT_TRUE(OutlinedFn->hasParamAttribute(0, Attribute::NoAlias)); |
| EXPECT_TRUE(OutlinedFn->hasParamAttribute(1, Attribute::NoAlias)); |
| |
| EXPECT_TRUE(OutlinedFn->hasInternalLinkage()); |
| EXPECT_EQ(OutlinedFn->arg_size(), 3U); |
| |
| EXPECT_EQ(&OutlinedFn->getEntryBlock(), PrivAI->getParent()); |
| EXPECT_EQ(OutlinedFn->getNumUses(), 1U); |
| User *Usr = OutlinedFn->user_back(); |
| ASSERT_TRUE(isa<ConstantExpr>(Usr)); |
| CallInst *ForkCI = dyn_cast<CallInst>(Usr->user_back()); |
| ASSERT_NE(ForkCI, nullptr); |
| |
| EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call"); |
| EXPECT_EQ(ForkCI->getNumArgOperands(), 4U); |
| EXPECT_TRUE(isa<GlobalVariable>(ForkCI->getArgOperand(0))); |
| EXPECT_EQ(ForkCI->getArgOperand(1), |
| ConstantInt::get(Type::getInt32Ty(Ctx), 1U)); |
| EXPECT_EQ(ForkCI->getArgOperand(2), Usr); |
| EXPECT_EQ(ForkCI->getArgOperand(3), F->arg_begin()); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, ParallelIfCond) { |
| using InsertPointTy = OpenMPIRBuilder::InsertPointTy; |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| F->setName("func"); |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); |
| |
| AllocaInst *PrivAI = nullptr; |
| |
| unsigned NumBodiesGenerated = 0; |
| unsigned NumPrivatizedVars = 0; |
| unsigned NumFinalizationPoints = 0; |
| |
| auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, |
| BasicBlock &ContinuationIP) { |
| ++NumBodiesGenerated; |
| |
| Builder.restoreIP(AllocaIP); |
| PrivAI = Builder.CreateAlloca(F->arg_begin()->getType()); |
| Builder.CreateStore(F->arg_begin(), PrivAI); |
| |
| Builder.restoreIP(CodeGenIP); |
| Value *PrivLoad = Builder.CreateLoad(PrivAI, "local.use"); |
| Value *Cmp = Builder.CreateICmpNE(F->arg_begin(), PrivLoad); |
| Instruction *ThenTerm, *ElseTerm; |
| SplitBlockAndInsertIfThenElse(Cmp, CodeGenIP.getBlock()->getTerminator(), |
| &ThenTerm, &ElseTerm); |
| |
| Builder.SetInsertPoint(ThenTerm); |
| Builder.CreateBr(&ContinuationIP); |
| ThenTerm->eraseFromParent(); |
| }; |
| |
| auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, |
| Value &VPtr, Value *&ReplacementValue) -> InsertPointTy { |
| ++NumPrivatizedVars; |
| |
| if (!isa<AllocaInst>(VPtr)) { |
| EXPECT_EQ(&VPtr, F->arg_begin()); |
| ReplacementValue = &VPtr; |
| return CodeGenIP; |
| } |
| |
| // Trivial copy (=firstprivate). |
| Builder.restoreIP(AllocaIP); |
| Type *VTy = VPtr.getType()->getPointerElementType(); |
| Value *V = Builder.CreateLoad(VTy, &VPtr, VPtr.getName() + ".reload"); |
| ReplacementValue = Builder.CreateAlloca(VTy, 0, VPtr.getName() + ".copy"); |
| Builder.restoreIP(CodeGenIP); |
| Builder.CreateStore(V, ReplacementValue); |
| return CodeGenIP; |
| }; |
| |
| auto FiniCB = [&](InsertPointTy CodeGenIP) { |
| ++NumFinalizationPoints; |
| // No destructors. |
| }; |
| |
| IRBuilder<>::InsertPoint AfterIP = OMPBuilder.CreateParallel( |
| Loc, BodyGenCB, PrivCB, FiniCB, Builder.CreateIsNotNull(F->arg_begin()), |
| nullptr, OMP_PROC_BIND_default, false); |
| |
| EXPECT_EQ(NumBodiesGenerated, 1U); |
| EXPECT_EQ(NumPrivatizedVars, 1U); |
| EXPECT_EQ(NumFinalizationPoints, 1U); |
| |
| Builder.restoreIP(AfterIP); |
| Builder.CreateRetVoid(); |
| OMPBuilder.finalize(); |
| |
| EXPECT_NE(PrivAI, nullptr); |
| Function *OutlinedFn = PrivAI->getFunction(); |
| EXPECT_NE(F, OutlinedFn); |
| EXPECT_FALSE(verifyModule(*M, &errs())); |
| |
| EXPECT_TRUE(OutlinedFn->hasInternalLinkage()); |
| EXPECT_EQ(OutlinedFn->arg_size(), 3U); |
| |
| EXPECT_EQ(&OutlinedFn->getEntryBlock(), PrivAI->getParent()); |
| ASSERT_EQ(OutlinedFn->getNumUses(), 2U); |
| |
| CallInst *DirectCI = nullptr; |
| CallInst *ForkCI = nullptr; |
| for (User *Usr : OutlinedFn->users()) { |
| if (isa<CallInst>(Usr)) { |
| ASSERT_EQ(DirectCI, nullptr); |
| DirectCI = cast<CallInst>(Usr); |
| } else { |
| ASSERT_TRUE(isa<ConstantExpr>(Usr)); |
| ASSERT_EQ(Usr->getNumUses(), 1U); |
| ASSERT_TRUE(isa<CallInst>(Usr->user_back())); |
| ForkCI = cast<CallInst>(Usr->user_back()); |
| } |
| } |
| |
| EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call"); |
| EXPECT_EQ(ForkCI->getNumArgOperands(), 4U); |
| EXPECT_TRUE(isa<GlobalVariable>(ForkCI->getArgOperand(0))); |
| EXPECT_EQ(ForkCI->getArgOperand(1), |
| ConstantInt::get(Type::getInt32Ty(Ctx), 1)); |
| EXPECT_EQ(ForkCI->getArgOperand(3), F->arg_begin()); |
| |
| EXPECT_EQ(DirectCI->getCalledFunction(), OutlinedFn); |
| EXPECT_EQ(DirectCI->getNumArgOperands(), 3U); |
| EXPECT_TRUE(isa<AllocaInst>(DirectCI->getArgOperand(0))); |
| EXPECT_TRUE(isa<AllocaInst>(DirectCI->getArgOperand(1))); |
| EXPECT_EQ(DirectCI->getArgOperand(2), F->arg_begin()); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, ParallelCancelBarrier) { |
| using InsertPointTy = OpenMPIRBuilder::InsertPointTy; |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| F->setName("func"); |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); |
| |
| unsigned NumBodiesGenerated = 0; |
| unsigned NumPrivatizedVars = 0; |
| unsigned NumFinalizationPoints = 0; |
| |
| CallInst *CheckedBarrier = nullptr; |
| auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, |
| BasicBlock &ContinuationIP) { |
| ++NumBodiesGenerated; |
| |
| Builder.restoreIP(CodeGenIP); |
| |
| // Create three barriers, two cancel barriers but only one checked. |
| Function *CBFn, *BFn; |
| |
| Builder.restoreIP( |
| OMPBuilder.CreateBarrier(Builder.saveIP(), OMPD_parallel)); |
| |
| CBFn = M->getFunction("__kmpc_cancel_barrier"); |
| BFn = M->getFunction("__kmpc_barrier"); |
| ASSERT_NE(CBFn, nullptr); |
| ASSERT_EQ(BFn, nullptr); |
| ASSERT_EQ(CBFn->getNumUses(), 1U); |
| ASSERT_TRUE(isa<CallInst>(CBFn->user_back())); |
| ASSERT_EQ(CBFn->user_back()->getNumUses(), 1U); |
| CheckedBarrier = cast<CallInst>(CBFn->user_back()); |
| |
| Builder.restoreIP( |
| OMPBuilder.CreateBarrier(Builder.saveIP(), OMPD_parallel, true)); |
| CBFn = M->getFunction("__kmpc_cancel_barrier"); |
| BFn = M->getFunction("__kmpc_barrier"); |
| ASSERT_NE(CBFn, nullptr); |
| ASSERT_NE(BFn, nullptr); |
| ASSERT_EQ(CBFn->getNumUses(), 1U); |
| ASSERT_EQ(BFn->getNumUses(), 1U); |
| ASSERT_TRUE(isa<CallInst>(BFn->user_back())); |
| ASSERT_EQ(BFn->user_back()->getNumUses(), 0U); |
| |
| Builder.restoreIP(OMPBuilder.CreateBarrier(Builder.saveIP(), OMPD_parallel, |
| false, false)); |
| ASSERT_EQ(CBFn->getNumUses(), 2U); |
| ASSERT_EQ(BFn->getNumUses(), 1U); |
| ASSERT_TRUE(CBFn->user_back() != CheckedBarrier); |
| ASSERT_TRUE(isa<CallInst>(CBFn->user_back())); |
| ASSERT_EQ(CBFn->user_back()->getNumUses(), 0U); |
| }; |
| |
| auto PrivCB = [&](InsertPointTy, InsertPointTy, Value &V, |
| Value *&) -> InsertPointTy { |
| ++NumPrivatizedVars; |
| llvm_unreachable("No privatization callback call expected!"); |
| }; |
| |
| FunctionType *FakeDestructorTy = |
| FunctionType::get(Type::getVoidTy(Ctx), {Type::getInt32Ty(Ctx)}, |
| /*isVarArg=*/false); |
| auto *FakeDestructor = Function::Create( |
| FakeDestructorTy, Function::ExternalLinkage, "fakeDestructor", M.get()); |
| |
| auto FiniCB = [&](InsertPointTy IP) { |
| ++NumFinalizationPoints; |
| Builder.restoreIP(IP); |
| Builder.CreateCall(FakeDestructor, |
| {Builder.getInt32(NumFinalizationPoints)}); |
| }; |
| |
| IRBuilder<>::InsertPoint AfterIP = OMPBuilder.CreateParallel( |
| Loc, BodyGenCB, PrivCB, FiniCB, Builder.CreateIsNotNull(F->arg_begin()), |
| nullptr, OMP_PROC_BIND_default, true); |
| |
| EXPECT_EQ(NumBodiesGenerated, 1U); |
| EXPECT_EQ(NumPrivatizedVars, 0U); |
| EXPECT_EQ(NumFinalizationPoints, 2U); |
| EXPECT_EQ(FakeDestructor->getNumUses(), 2U); |
| |
| Builder.restoreIP(AfterIP); |
| Builder.CreateRetVoid(); |
| OMPBuilder.finalize(); |
| |
| EXPECT_FALSE(verifyModule(*M, &errs())); |
| |
| BasicBlock *ExitBB = nullptr; |
| for (const User *Usr : FakeDestructor->users()) { |
| const CallInst *CI = dyn_cast<CallInst>(Usr); |
| ASSERT_EQ(CI->getCalledFunction(), FakeDestructor); |
| ASSERT_TRUE(isa<BranchInst>(CI->getNextNode())); |
| ASSERT_EQ(CI->getNextNode()->getNumSuccessors(), 1U); |
| if (ExitBB) |
| ASSERT_EQ(CI->getNextNode()->getSuccessor(0), ExitBB); |
| else |
| ExitBB = CI->getNextNode()->getSuccessor(0); |
| ASSERT_EQ(ExitBB->size(), 1U); |
| if (!isa<ReturnInst>(ExitBB->front())) { |
| ASSERT_TRUE(isa<BranchInst>(ExitBB->front())); |
| ASSERT_EQ(cast<BranchInst>(ExitBB->front()).getNumSuccessors(), 1U); |
| ASSERT_TRUE(isa<ReturnInst>( |
| cast<BranchInst>(ExitBB->front()).getSuccessor(0)->front())); |
| } |
| } |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, MasterDirective) { |
| 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.CreateMaster(Builder, BodyGenCB, FiniCB)); |
| Value *EntryBBTI = EntryBB->getTerminator(); |
| EXPECT_NE(EntryBBTI, nullptr); |
| EXPECT_TRUE(isa<BranchInst>(EntryBBTI)); |
| BranchInst *EntryBr = cast<BranchInst>(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<CmpInst>(EntryBr->getCondition()); |
| EXPECT_TRUE(isa<CallInst>(CondInst->getOperand(0))); |
| |
| CallInst *MasterEntryCI = cast<CallInst>(CondInst->getOperand(0)); |
| EXPECT_EQ(MasterEntryCI->getNumArgOperands(), 2U); |
| EXPECT_EQ(MasterEntryCI->getCalledFunction()->getName(), "__kmpc_master"); |
| EXPECT_TRUE(isa<GlobalVariable>(MasterEntryCI->getArgOperand(0))); |
| |
| CallInst *MasterEndCI = nullptr; |
| for (auto &FI : *ThenBB) { |
| Instruction *cur = &FI; |
| if (isa<CallInst>(cur)) { |
| MasterEndCI = cast<CallInst>(cur); |
| if (MasterEndCI->getCalledFunction()->getName() == "__kmpc_end_master") |
| break; |
| MasterEndCI = nullptr; |
| } |
| } |
| EXPECT_NE(MasterEndCI, nullptr); |
| EXPECT_EQ(MasterEndCI->getNumArgOperands(), 2U); |
| EXPECT_TRUE(isa<GlobalVariable>(MasterEndCI->getArgOperand(0))); |
| EXPECT_EQ(MasterEndCI->getArgOperand(1), MasterEntryCI->getArgOperand(1)); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, CriticalDirective) { |
| using InsertPointTy = OpenMPIRBuilder::InsertPointTy; |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| F->setName("func"); |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); |
| |
| AllocaInst *PrivAI = Builder.CreateAlloca(F->arg_begin()->getType()); |
| |
| BasicBlock *EntryBB = nullptr; |
| |
| auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, |
| BasicBlock &FiniBB) { |
| // collect some info for checks later |
| EntryBB = FiniBB.getUniquePredecessor(); |
| |
| // actual start for bodyCB |
| llvm::BasicBlock *CodeGenIPBB = CodeGenIP.getBlock(); |
| llvm::Instruction *CodeGenIPInst = &*CodeGenIP.getPoint(); |
| EXPECT_EQ(CodeGenIPBB->getTerminator(), CodeGenIPInst); |
| EXPECT_EQ(EntryBB, CodeGenIPBB); |
| |
| // body begin |
| Builder.restoreIP(CodeGenIP); |
| Builder.CreateStore(F->arg_begin(), PrivAI); |
| 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.CreateCritical(Builder, BodyGenCB, FiniCB, |
| "testCRT", nullptr)); |
| |
| Value *EntryBBTI = EntryBB->getTerminator(); |
| EXPECT_EQ(EntryBBTI, nullptr); |
| |
| CallInst *CriticalEntryCI = nullptr; |
| for (auto &EI : *EntryBB) { |
| Instruction *cur = &EI; |
| if (isa<CallInst>(cur)) { |
| CriticalEntryCI = cast<CallInst>(cur); |
| if (CriticalEntryCI->getCalledFunction()->getName() == "__kmpc_critical") |
| break; |
| CriticalEntryCI = nullptr; |
| } |
| } |
| EXPECT_NE(CriticalEntryCI, nullptr); |
| EXPECT_EQ(CriticalEntryCI->getNumArgOperands(), 3U); |
| EXPECT_EQ(CriticalEntryCI->getCalledFunction()->getName(), "__kmpc_critical"); |
| EXPECT_TRUE(isa<GlobalVariable>(CriticalEntryCI->getArgOperand(0))); |
| |
| CallInst *CriticalEndCI = nullptr; |
| for (auto &FI : *EntryBB) { |
| Instruction *cur = &FI; |
| if (isa<CallInst>(cur)) { |
| CriticalEndCI = cast<CallInst>(cur); |
| if (CriticalEndCI->getCalledFunction()->getName() == |
| "__kmpc_end_critical") |
| break; |
| CriticalEndCI = nullptr; |
| } |
| } |
| EXPECT_NE(CriticalEndCI, nullptr); |
| EXPECT_EQ(CriticalEndCI->getNumArgOperands(), 3U); |
| EXPECT_TRUE(isa<GlobalVariable>(CriticalEndCI->getArgOperand(0))); |
| EXPECT_EQ(CriticalEndCI->getArgOperand(1), CriticalEntryCI->getArgOperand(1)); |
| PointerType *CriticalNamePtrTy = |
| PointerType::getUnqual(ArrayType::get(Type::getInt32Ty(Ctx), 8)); |
| EXPECT_EQ(CriticalEndCI->getArgOperand(2), CriticalEntryCI->getArgOperand(2)); |
| EXPECT_EQ(CriticalEndCI->getArgOperand(2)->getType(), CriticalNamePtrTy); |
| } |
| |
| TEST_F(OpenMPIRBuilderTest, CopyinBlocks) { |
| OpenMPIRBuilder OMPBuilder(*M); |
| OMPBuilder.initialize(); |
| F->setName("func"); |
| IRBuilder<> Builder(BB); |
| |
| OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); |
| |
| IntegerType* Int32 = Type::getInt32Ty(M->getContext()); |
| AllocaInst* MasterAddress = Builder.CreateAlloca(Int32->getPointerTo()); |
| AllocaInst* PrivAddress = Builder.CreateAlloca(Int32->getPointerTo()); |
| |
| BasicBlock *EntryBB = BB; |
| |
| OMPBuilder.CreateCopyinClauseBlocks(Builder.saveIP(), MasterAddress, PrivAddress, Int32, /*BranchtoEnd*/true); |
| |
| BranchInst* EntryBr = dyn_cast_or_null<BranchInst>(EntryBB->getTerminator()); |
| |
| EXPECT_NE(EntryBr, nullptr); |
| EXPECT_TRUE(EntryBr->isConditional()); |
| |
| BasicBlock* NotMasterBB = EntryBr->getSuccessor(0); |
| BasicBlock* CopyinEnd = EntryBr->getSuccessor(1); |
| CmpInst* CMP = dyn_cast_or_null<CmpInst>(EntryBr->getCondition()); |
| |
| EXPECT_NE(CMP, nullptr); |
| EXPECT_NE(NotMasterBB, nullptr); |
| EXPECT_NE(CopyinEnd, nullptr); |
| |
| BranchInst* NotMasterBr = dyn_cast_or_null<BranchInst>(NotMasterBB->getTerminator()); |
| EXPECT_NE(NotMasterBr, nullptr); |
| EXPECT_FALSE(NotMasterBr->isConditional()); |
| EXPECT_EQ(CopyinEnd,NotMasterBr->getSuccessor(0)); |
| } |
| |
| } // namespace |