//===- 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/OMPConstants.h" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/DIBuilder.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.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", "/src", llvm::None, Optional("/src/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 M; Function *F; BasicBlock *BB; DebugLoc DL; }; // Returns the value stored in the given allocation. Returns null if the given // value is not a result of an allocation, if no value is stored or if there is // more than one store. static Value *findStoredValue(Value *AllocaValue) { Instruction *Alloca = dyn_cast(AllocaValue); if (!Alloca) return nullptr; StoreInst *Store = nullptr; for (Use &U : Alloca->uses()) { if (auto *CandidateStore = dyn_cast(U.getUser())) { EXPECT_EQ(Store, nullptr); Store = CandidateStore; } } if (!Store) return nullptr; return Store->getValueOperand(); } 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(&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(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(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(&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(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(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(BB->getTerminator())); ASSERT_EQ(BB->getTerminator()->getNumSuccessors(), 2U); BB = BB->getTerminator()->getSuccessor(0); EXPECT_EQ(BB->size(), 4U); CallInst *GTID = dyn_cast(&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(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(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(&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(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(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(&BB->front()); CallInst *Barrier = dyn_cast(GTID->getNextNode()); EXPECT_EQ(GTID->getDebugLoc(), DL); EXPECT_EQ(Barrier->getDebugLoc(), DL); EXPECT_TRUE(isa(Barrier->getOperand(0))); if (!isa(Barrier->getOperand(0))) return; GlobalVariable *Ident = cast(Barrier->getOperand(0)); EXPECT_TRUE(Ident->hasInitializer()); if (!Ident->hasInitializer()) return; Constant *Initializer = Ident->getInitializer(); EXPECT_TRUE( isa(Initializer->getOperand(4)->stripPointerCasts())); GlobalVariable *SrcStrGlob = cast(Initializer->getOperand(4)->stripPointerCasts()); if (!SrcStrGlob) return; EXPECT_TRUE(isa(SrcStrGlob->getInitializer())); ConstantDataArray *SrcSrc = dyn_cast(SrcStrGlob->getInitializer()); if (!SrcSrc) return; EXPECT_EQ(SrcSrc->getAsCString(), ";/src/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 &Orig, Value &Inner, Value *&ReplacementValue) -> InsertPointTy { ++NumPrivatizedVars; if (!isa(Orig)) { EXPECT_EQ(&Orig, F->arg_begin()); ReplacementValue = &Inner; return CodeGenIP; } // Since the original value is an allocation, it has a pointer type and // therefore no additional wrapping should happen. EXPECT_EQ(&Orig, &Inner); // Trivial copy (=firstprivate). Builder.restoreIP(AllocaIP); Type *VTy = Inner.getType()->getPointerElementType(); Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload"); ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy"); Builder.restoreIP(CodeGenIP); Builder.CreateStore(V, ReplacementValue); return CodeGenIP; }; auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; }; IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(), F->getEntryBlock().getFirstInsertionPt()); IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel(Loc, AllocaIP, 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(Usr)); CallInst *ForkCI = dyn_cast(Usr->user_back()); ASSERT_NE(ForkCI, nullptr); EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call"); EXPECT_EQ(ForkCI->getNumArgOperands(), 4U); EXPECT_TRUE(isa(ForkCI->getArgOperand(0))); EXPECT_EQ(ForkCI->getArgOperand(1), ConstantInt::get(Type::getInt32Ty(Ctx), 1U)); EXPECT_EQ(ForkCI->getArgOperand(2), Usr); EXPECT_EQ(findStoredValue(ForkCI->getArgOperand(3)), F->arg_begin()); } TEST_F(OpenMPIRBuilderTest, ParallelNested) { using InsertPointTy = OpenMPIRBuilder::InsertPointTy; OpenMPIRBuilder OMPBuilder(*M); OMPBuilder.initialize(); F->setName("func"); IRBuilder<> Builder(BB); OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); unsigned NumInnerBodiesGenerated = 0; unsigned NumOuterBodiesGenerated = 0; unsigned NumFinalizationPoints = 0; auto InnerBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, BasicBlock &ContinuationIP) { ++NumInnerBodiesGenerated; }; auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Orig, Value &Inner, Value *&ReplacementValue) -> InsertPointTy { // Trivial copy (=firstprivate). Builder.restoreIP(AllocaIP); Type *VTy = Inner.getType()->getPointerElementType(); Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload"); ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy"); Builder.restoreIP(CodeGenIP); Builder.CreateStore(V, ReplacementValue); return CodeGenIP; }; auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; }; auto OuterBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, BasicBlock &ContinuationIP) { ++NumOuterBodiesGenerated; Builder.restoreIP(CodeGenIP); BasicBlock *CGBB = CodeGenIP.getBlock(); BasicBlock *NewBB = SplitBlock(CGBB, &*CodeGenIP.getPoint()); CGBB->getTerminator()->eraseFromParent(); ; IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel( InsertPointTy(CGBB, CGBB->end()), AllocaIP, InnerBodyGenCB, PrivCB, FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false); Builder.restoreIP(AfterIP); Builder.CreateBr(NewBB); }; IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(), F->getEntryBlock().getFirstInsertionPt()); IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel(Loc, AllocaIP, OuterBodyGenCB, PrivCB, FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false); EXPECT_EQ(NumInnerBodiesGenerated, 1U); EXPECT_EQ(NumOuterBodiesGenerated, 1U); EXPECT_EQ(NumFinalizationPoints, 2U); Builder.restoreIP(AfterIP); Builder.CreateRetVoid(); OMPBuilder.finalize(); EXPECT_EQ(M->size(), 5U); for (Function &OutlinedFn : *M) { if (F == &OutlinedFn || OutlinedFn.isDeclaration()) continue; 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(), 2U); EXPECT_EQ(OutlinedFn.getNumUses(), 1U); User *Usr = OutlinedFn.user_back(); ASSERT_TRUE(isa(Usr)); CallInst *ForkCI = dyn_cast(Usr->user_back()); ASSERT_NE(ForkCI, nullptr); EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call"); EXPECT_EQ(ForkCI->getNumArgOperands(), 3U); EXPECT_TRUE(isa(ForkCI->getArgOperand(0))); EXPECT_EQ(ForkCI->getArgOperand(1), ConstantInt::get(Type::getInt32Ty(Ctx), 0U)); EXPECT_EQ(ForkCI->getArgOperand(2), Usr); } } TEST_F(OpenMPIRBuilderTest, ParallelNested2Inner) { using InsertPointTy = OpenMPIRBuilder::InsertPointTy; OpenMPIRBuilder OMPBuilder(*M); OMPBuilder.initialize(); F->setName("func"); IRBuilder<> Builder(BB); OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); unsigned NumInnerBodiesGenerated = 0; unsigned NumOuterBodiesGenerated = 0; unsigned NumFinalizationPoints = 0; auto InnerBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, BasicBlock &ContinuationIP) { ++NumInnerBodiesGenerated; }; auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &Orig, Value &Inner, Value *&ReplacementValue) -> InsertPointTy { // Trivial copy (=firstprivate). Builder.restoreIP(AllocaIP); Type *VTy = Inner.getType()->getPointerElementType(); Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload"); ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy"); Builder.restoreIP(CodeGenIP); Builder.CreateStore(V, ReplacementValue); return CodeGenIP; }; auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; }; auto OuterBodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, BasicBlock &ContinuationIP) { ++NumOuterBodiesGenerated; Builder.restoreIP(CodeGenIP); BasicBlock *CGBB = CodeGenIP.getBlock(); BasicBlock *NewBB1 = SplitBlock(CGBB, &*CodeGenIP.getPoint()); BasicBlock *NewBB2 = SplitBlock(NewBB1, &*NewBB1->getFirstInsertionPt()); CGBB->getTerminator()->eraseFromParent(); ; NewBB1->getTerminator()->eraseFromParent(); ; IRBuilder<>::InsertPoint AfterIP1 = OMPBuilder.createParallel( InsertPointTy(CGBB, CGBB->end()), AllocaIP, InnerBodyGenCB, PrivCB, FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false); Builder.restoreIP(AfterIP1); Builder.CreateBr(NewBB1); IRBuilder<>::InsertPoint AfterIP2 = OMPBuilder.createParallel( InsertPointTy(NewBB1, NewBB1->end()), AllocaIP, InnerBodyGenCB, PrivCB, FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false); Builder.restoreIP(AfterIP2); Builder.CreateBr(NewBB2); }; IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(), F->getEntryBlock().getFirstInsertionPt()); IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel(Loc, AllocaIP, OuterBodyGenCB, PrivCB, FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false); EXPECT_EQ(NumInnerBodiesGenerated, 2U); EXPECT_EQ(NumOuterBodiesGenerated, 1U); EXPECT_EQ(NumFinalizationPoints, 3U); Builder.restoreIP(AfterIP); Builder.CreateRetVoid(); OMPBuilder.finalize(); EXPECT_EQ(M->size(), 6U); for (Function &OutlinedFn : *M) { if (F == &OutlinedFn || OutlinedFn.isDeclaration()) continue; 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(), 2U); unsigned NumAllocas = 0; for (Instruction &I : instructions(OutlinedFn)) NumAllocas += isa(I); EXPECT_EQ(NumAllocas, 1U); EXPECT_EQ(OutlinedFn.getNumUses(), 1U); User *Usr = OutlinedFn.user_back(); ASSERT_TRUE(isa(Usr)); CallInst *ForkCI = dyn_cast(Usr->user_back()); ASSERT_NE(ForkCI, nullptr); EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call"); EXPECT_EQ(ForkCI->getNumArgOperands(), 3U); EXPECT_TRUE(isa(ForkCI->getArgOperand(0))); EXPECT_EQ(ForkCI->getArgOperand(1), ConstantInt::get(Type::getInt32Ty(Ctx), 0U)); EXPECT_EQ(ForkCI->getArgOperand(2), Usr); } } 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 &Orig, Value &Inner, Value *&ReplacementValue) -> InsertPointTy { ++NumPrivatizedVars; if (!isa(Orig)) { EXPECT_EQ(&Orig, F->arg_begin()); ReplacementValue = &Inner; return CodeGenIP; } // Since the original value is an allocation, it has a pointer type and // therefore no additional wrapping should happen. EXPECT_EQ(&Orig, &Inner); // Trivial copy (=firstprivate). Builder.restoreIP(AllocaIP); Type *VTy = Inner.getType()->getPointerElementType(); Value *V = Builder.CreateLoad(VTy, &Inner, Orig.getName() + ".reload"); ReplacementValue = Builder.CreateAlloca(VTy, 0, Orig.getName() + ".copy"); Builder.restoreIP(CodeGenIP); Builder.CreateStore(V, ReplacementValue); return CodeGenIP; }; auto FiniCB = [&](InsertPointTy CodeGenIP) { ++NumFinalizationPoints; // No destructors. }; IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(), F->getEntryBlock().getFirstInsertionPt()); IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel(Loc, AllocaIP, 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(Usr)) { ASSERT_EQ(DirectCI, nullptr); DirectCI = cast(Usr); } else { ASSERT_TRUE(isa(Usr)); ASSERT_EQ(Usr->getNumUses(), 1U); ASSERT_TRUE(isa(Usr->user_back())); ForkCI = cast(Usr->user_back()); } } EXPECT_EQ(ForkCI->getCalledFunction()->getName(), "__kmpc_fork_call"); EXPECT_EQ(ForkCI->getNumArgOperands(), 4U); EXPECT_TRUE(isa(ForkCI->getArgOperand(0))); EXPECT_EQ(ForkCI->getArgOperand(1), ConstantInt::get(Type::getInt32Ty(Ctx), 1)); Value *StoredForkArg = findStoredValue(ForkCI->getArgOperand(3)); EXPECT_EQ(StoredForkArg, F->arg_begin()); EXPECT_EQ(DirectCI->getCalledFunction(), OutlinedFn); EXPECT_EQ(DirectCI->getNumArgOperands(), 3U); EXPECT_TRUE(isa(DirectCI->getArgOperand(0))); EXPECT_TRUE(isa(DirectCI->getArgOperand(1))); Value *StoredDirectArg = findStoredValue(DirectCI->getArgOperand(2)); EXPECT_EQ(StoredDirectArg, 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(CBFn->user_back())); ASSERT_EQ(CBFn->user_back()->getNumUses(), 1U); CheckedBarrier = cast(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(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(CBFn->user_back())); ASSERT_EQ(CBFn->user_back()->getNumUses(), 0U); }; auto PrivCB = [&](InsertPointTy, InsertPointTy, Value &V, Value &, 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 AllocaIP(&F->getEntryBlock(), F->getEntryBlock().getFirstInsertionPt()); IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel(Loc, AllocaIP, 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(Usr); ASSERT_EQ(CI->getCalledFunction(), FakeDestructor); ASSERT_TRUE(isa(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(ExitBB->front())) { ASSERT_TRUE(isa(ExitBB->front())); ASSERT_EQ(cast(ExitBB->front()).getNumSuccessors(), 1U); ASSERT_TRUE(isa( cast(ExitBB->front()).getSuccessor(0)->front())); } } } TEST_F(OpenMPIRBuilderTest, ParallelForwardAsPointers) { OpenMPIRBuilder OMPBuilder(*M); OMPBuilder.initialize(); F->setName("func"); IRBuilder<> Builder(BB); OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); using InsertPointTy = OpenMPIRBuilder::InsertPointTy; Type *I32Ty = Type::getInt32Ty(M->getContext()); Type *I32PtrTy = Type::getInt32PtrTy(M->getContext()); Type *StructTy = StructType::get(I32Ty, I32PtrTy); Type *StructPtrTy = StructTy->getPointerTo(); Type *VoidTy = Type::getVoidTy(M->getContext()); FunctionCallee RetI32Func = M->getOrInsertFunction("ret_i32", I32Ty); FunctionCallee TakeI32Func = M->getOrInsertFunction("take_i32", VoidTy, I32Ty); FunctionCallee RetI32PtrFunc = M->getOrInsertFunction("ret_i32ptr", I32PtrTy); FunctionCallee TakeI32PtrFunc = M->getOrInsertFunction("take_i32ptr", VoidTy, I32PtrTy); FunctionCallee RetStructFunc = M->getOrInsertFunction("ret_struct", StructTy); FunctionCallee TakeStructFunc = M->getOrInsertFunction("take_struct", VoidTy, StructTy); FunctionCallee RetStructPtrFunc = M->getOrInsertFunction("ret_structptr", StructPtrTy); FunctionCallee TakeStructPtrFunc = M->getOrInsertFunction("take_structPtr", VoidTy, StructPtrTy); Value *I32Val = Builder.CreateCall(RetI32Func); Value *I32PtrVal = Builder.CreateCall(RetI32PtrFunc); Value *StructVal = Builder.CreateCall(RetStructFunc); Value *StructPtrVal = Builder.CreateCall(RetStructPtrFunc); Instruction *Internal; auto BodyGenCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, BasicBlock &ContinuationBB) { IRBuilder<>::InsertPointGuard Guard(Builder); Builder.restoreIP(CodeGenIP); Internal = Builder.CreateCall(TakeI32Func, I32Val); Builder.CreateCall(TakeI32PtrFunc, I32PtrVal); Builder.CreateCall(TakeStructFunc, StructVal); Builder.CreateCall(TakeStructPtrFunc, StructPtrVal); }; auto PrivCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, Value &, Value &Inner, Value *&ReplacementValue) { ReplacementValue = &Inner; return CodeGenIP; }; auto FiniCB = [](InsertPointTy) {}; IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(), F->getEntryBlock().getFirstInsertionPt()); IRBuilder<>::InsertPoint AfterIP = OMPBuilder.createParallel(Loc, AllocaIP, BodyGenCB, PrivCB, FiniCB, nullptr, nullptr, OMP_PROC_BIND_default, false); Builder.restoreIP(AfterIP); Builder.CreateRetVoid(); OMPBuilder.finalize(); EXPECT_FALSE(verifyModule(*M, &errs())); Function *OutlinedFn = Internal->getFunction(); Type *Arg2Type = OutlinedFn->getArg(2)->getType(); EXPECT_TRUE(Arg2Type->isPointerTy()); EXPECT_EQ(Arg2Type->getPointerElementType(), I32Ty); // Arguments that need to be passed through pointers and reloaded will get // used earlier in the functions and therefore will appear first in the // argument list after outlining. Type *Arg3Type = OutlinedFn->getArg(3)->getType(); EXPECT_TRUE(Arg3Type->isPointerTy()); EXPECT_EQ(Arg3Type->getPointerElementType(), StructTy); Type *Arg4Type = OutlinedFn->getArg(4)->getType(); EXPECT_EQ(Arg4Type, I32PtrTy); Type *Arg5Type = OutlinedFn->getArg(5)->getType(); EXPECT_EQ(Arg5Type, StructPtrTy); } TEST_F(OpenMPIRBuilderTest, CanonicalLoopSimple) { using InsertPointTy = OpenMPIRBuilder::InsertPointTy; OpenMPIRBuilder OMPBuilder(*M); OMPBuilder.initialize(); IRBuilder<> Builder(BB); OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); Value *TripCount = F->getArg(0); unsigned NumBodiesGenerated = 0; auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, llvm::Value *LC) { NumBodiesGenerated += 1; Builder.restoreIP(CodeGenIP); Value *Cmp = Builder.CreateICmpEQ(LC, TripCount); Instruction *ThenTerm, *ElseTerm; SplitBlockAndInsertIfThenElse(Cmp, CodeGenIP.getBlock()->getTerminator(), &ThenTerm, &ElseTerm); }; CanonicalLoopInfo *Loop = OMPBuilder.createCanonicalLoop(Loc, LoopBodyGenCB, TripCount); Builder.restoreIP(Loop->getAfterIP()); ReturnInst *RetInst = Builder.CreateRetVoid(); OMPBuilder.finalize(); Loop->assertOK(); EXPECT_FALSE(verifyModule(*M, &errs())); EXPECT_EQ(NumBodiesGenerated, 1U); // Verify control flow structure (in addition to Loop->assertOK()). EXPECT_EQ(Loop->getPreheader()->getSinglePredecessor(), &F->getEntryBlock()); EXPECT_EQ(Loop->getAfter(), Builder.GetInsertBlock()); Instruction *IndVar = Loop->getIndVar(); EXPECT_TRUE(isa(IndVar)); EXPECT_EQ(IndVar->getType(), TripCount->getType()); EXPECT_EQ(IndVar->getParent(), Loop->getHeader()); EXPECT_EQ(Loop->getTripCount(), TripCount); BasicBlock *Body = Loop->getBody(); Instruction *CmpInst = &Body->getInstList().front(); EXPECT_TRUE(isa(CmpInst)); EXPECT_EQ(CmpInst->getOperand(0), IndVar); BasicBlock *LatchPred = Loop->getLatch()->getSinglePredecessor(); EXPECT_TRUE(llvm::all_of(successors(Body), [=](BasicBlock *SuccBB) { return SuccBB->getSingleSuccessor() == LatchPred; })); EXPECT_EQ(&Loop->getAfter()->front(), RetInst); } TEST_F(OpenMPIRBuilderTest, CanonicalLoopBounds) { using InsertPointTy = OpenMPIRBuilder::InsertPointTy; OpenMPIRBuilder OMPBuilder(*M); OMPBuilder.initialize(); IRBuilder<> Builder(BB); // Check the trip count is computed correctly. We generate the canonical loop // but rely on the IRBuilder's constant folder to compute the final result // since all inputs are constant. To verify overflow situations, limit the // trip count / loop counter widths to 16 bits. auto EvalTripCount = [&](int64_t Start, int64_t Stop, int64_t Step, bool IsSigned, bool InclusiveStop) -> int64_t { OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); Type *LCTy = Type::getInt16Ty(Ctx); Value *StartVal = ConstantInt::get(LCTy, Start); Value *StopVal = ConstantInt::get(LCTy, Stop); Value *StepVal = ConstantInt::get(LCTy, Step); auto LoopBodyGenCB = [&](InsertPointTy CodeGenIP, llvm::Value *LC) {}; CanonicalLoopInfo *Loop = OMPBuilder.createCanonicalLoop(Loc, LoopBodyGenCB, StartVal, StopVal, StepVal, IsSigned, InclusiveStop); Loop->assertOK(); Builder.restoreIP(Loop->getAfterIP()); Value *TripCount = Loop->getTripCount(); return cast(TripCount)->getValue().getZExtValue(); }; ASSERT_EQ(EvalTripCount(0, 0, 1, false, false), 0); ASSERT_EQ(EvalTripCount(0, 1, 2, false, false), 1); ASSERT_EQ(EvalTripCount(0, 42, 1, false, false), 42); ASSERT_EQ(EvalTripCount(0, 42, 2, false, false), 21); ASSERT_EQ(EvalTripCount(21, 42, 1, false, false), 21); ASSERT_EQ(EvalTripCount(0, 5, 5, false, false), 1); ASSERT_EQ(EvalTripCount(0, 9, 5, false, false), 2); ASSERT_EQ(EvalTripCount(0, 11, 5, false, false), 3); ASSERT_EQ(EvalTripCount(0, 0xFFFF, 1, false, false), 0xFFFF); ASSERT_EQ(EvalTripCount(0xFFFF, 0, 1, false, false), 0); ASSERT_EQ(EvalTripCount(0xFFFE, 0xFFFF, 1, false, false), 1); ASSERT_EQ(EvalTripCount(0, 0xFFFF, 0x100, false, false), 0x100); ASSERT_EQ(EvalTripCount(0, 0xFFFF, 0xFFFF, false, false), 1); ASSERT_EQ(EvalTripCount(0, 6, 5, false, false), 2); ASSERT_EQ(EvalTripCount(0, 0xFFFF, 0xFFFE, false, false), 2); ASSERT_EQ(EvalTripCount(0, 0, 1, false, true), 1); ASSERT_EQ(EvalTripCount(0, 0, 0xFFFF, false, true), 1); ASSERT_EQ(EvalTripCount(0, 0xFFFE, 1, false, true), 0xFFFF); ASSERT_EQ(EvalTripCount(0, 0xFFFE, 2, false, true), 0x8000); ASSERT_EQ(EvalTripCount(0, 0, -1, true, false), 0); ASSERT_EQ(EvalTripCount(0, 1, -1, true, true), 0); ASSERT_EQ(EvalTripCount(20, 5, -5, true, false), 3); ASSERT_EQ(EvalTripCount(20, 5, -5, true, true), 4); ASSERT_EQ(EvalTripCount(-4, -2, 2, true, false), 1); ASSERT_EQ(EvalTripCount(-4, -3, 2, true, false), 1); ASSERT_EQ(EvalTripCount(-4, -2, 2, true, true), 2); ASSERT_EQ(EvalTripCount(INT16_MIN, 0, 1, true, false), 0x8000); ASSERT_EQ(EvalTripCount(INT16_MIN, 0, 1, true, true), 0x8001); ASSERT_EQ(EvalTripCount(INT16_MIN, 0x7FFF, 1, true, false), 0xFFFF); ASSERT_EQ(EvalTripCount(INT16_MIN + 1, 0x7FFF, 1, true, true), 0xFFFF); ASSERT_EQ(EvalTripCount(INT16_MIN, 0, 0x7FFF, true, false), 2); ASSERT_EQ(EvalTripCount(0x7FFF, 0, -1, true, false), 0x7FFF); ASSERT_EQ(EvalTripCount(0, INT16_MIN, -1, true, false), 0x8000); ASSERT_EQ(EvalTripCount(0, INT16_MIN, -16, true, false), 0x800); ASSERT_EQ(EvalTripCount(0x7FFF, INT16_MIN, -1, true, false), 0xFFFF); ASSERT_EQ(EvalTripCount(0x7FFF, 1, INT16_MIN, true, false), 1); ASSERT_EQ(EvalTripCount(0x7FFF, -1, INT16_MIN, true, true), 2); // Finalize the function and verify it. Builder.CreateRetVoid(); OMPBuilder.finalize(); EXPECT_FALSE(verifyModule(*M, &errs())); } 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(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 *MasterEntryCI = cast(CondInst->getOperand(0)); EXPECT_EQ(MasterEntryCI->getNumArgOperands(), 2U); EXPECT_EQ(MasterEntryCI->getCalledFunction()->getName(), "__kmpc_master"); EXPECT_TRUE(isa(MasterEntryCI->getArgOperand(0))); CallInst *MasterEndCI = nullptr; for (auto &FI : *ThenBB) { Instruction *cur = &FI; if (isa(cur)) { MasterEndCI = cast(cur); if (MasterEndCI->getCalledFunction()->getName() == "__kmpc_end_master") break; MasterEndCI = nullptr; } } EXPECT_NE(MasterEndCI, nullptr); EXPECT_EQ(MasterEndCI->getNumArgOperands(), 2U); EXPECT_TRUE(isa(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(cur)) { CriticalEntryCI = cast(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(CriticalEntryCI->getArgOperand(0))); CallInst *CriticalEndCI = nullptr; for (auto &FI : *EntryBB) { Instruction *cur = &FI; if (isa(cur)) { CriticalEndCI = cast(cur); if (CriticalEndCI->getCalledFunction()->getName() == "__kmpc_end_critical") break; CriticalEndCI = nullptr; } } EXPECT_NE(CriticalEndCI, nullptr); EXPECT_EQ(CriticalEndCI->getNumArgOperands(), 3U); EXPECT_TRUE(isa(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(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(EntryBr->getCondition()); EXPECT_NE(CMP, nullptr); EXPECT_NE(NotMasterBB, nullptr); EXPECT_NE(CopyinEnd, nullptr); BranchInst* NotMasterBr = dyn_cast_or_null(NotMasterBB->getTerminator()); EXPECT_NE(NotMasterBr, nullptr); EXPECT_FALSE(NotMasterBr->isConditional()); 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