diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h b/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h --- a/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h +++ b/llvm/include/llvm/Frontend/OpenMP/OMPConstants.h @@ -76,6 +76,11 @@ LLVM_MARK_AS_BITMASK_ENUM(0x7FFFFFFF) }; +enum class SchedType { +#define OMP_SCHED_TYPE(Enum, Str, Value) Enum = Value, +#include "llvm/Frontend/OpenMP/OMPKinds.def" +}; + #define OMP_IDENT_FLAG(Enum, ...) constexpr auto Enum = omp::IdentFlag::Enum; #include "llvm/Frontend/OpenMP/OMPKinds.def" 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 @@ -363,12 +363,29 @@ /// \param CriticalName name of the lock used by the critical directive /// \param HintInst Hint Instruction for hint clause associated with critical /// - /// \returns The insertion position *after* the master. + /// \returns The insertion position *after* the critical. InsertPointTy CreateCritical(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, FinalizeCallbackTy FiniCB, StringRef CriticalName, Value *HintInst); + /// Generator for '#omp sections' + /// + /// \param Loc The insert and source location description. + /// \param AllocaIP The insertion points to be used for alloca instructions. + /// \param SectionCBs Callbacks that will generate body of each section. + /// \param PrivCB Callback to copy a given variable (think copy constructor). + /// \param FiniCB Callback to finalize variable copies. + /// \param IsCancellable Flag to indicate a cancellable parallel region. + /// + /// \returns The insertion position *after* the sections. + InsertPointTy CreateSections(const LocationDescription &Loc, + InsertPointTy AllocaIP, + ArrayRef SectionCBs, + PrivatizeCallbackTy PrivCB, + FinalizeCallbackTy FiniCB, bool IsCancellable, + bool IsNowait); + /// Generate conditional branch and relevant BasicBlocks through which private /// threads copy the 'copyin' variables from Master copy to threadprivate /// copies. diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def --- a/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def +++ b/llvm/include/llvm/Frontend/OpenMP/OMPKinds.def @@ -1012,6 +1012,52 @@ ///} +/// OMP Scheduler Type Kinds +/// +/// See: +/// openmp/runtime/src/kmp.h - enum sched_type +///{ + +#ifndef OMP_SCHED_TYPE +#define OMP_SCHED_TYPE(Enum, Str, Value) +#endif + +#define __OMP_SCHED_TYPE(Name, Value) \ + OMP_SCHED_TYPE(OMP_SCHED_TYPE_##Name, #Name, Value) + +/// Lower bound for default (unordered) versions. +__OMP_SCHED_TYPE(SCH_LOWER, 32) +__OMP_SCHED_TYPE(SCH_STATIC_CHUNKED, 33) +__OMP_SCHED_TYPE(SCH_STATIC, 34) +__OMP_SCHED_TYPE(SCH_DYNAMIC_CHUNKED, 35) +__OMP_SCHED_TYPE(SCH_GUIDED_CHUNKED, 36) +__OMP_SCHED_TYPE(SCH_RUNTIME, 37) +__OMP_SCHED_TYPE(SCH_AUTO, 38) +/// static with chunk adjustment (e.g., simd) +__OMP_SCHED_TYPE(SCH_STATIC_BALANCED_CHUNKED, 45) +/// Lower bound for 'ordered' versions. +__OMP_SCHED_TYPE(ORD_LOWER, 64) +__OMP_SCHED_TYPE(ORD_STATIC_CHUNKED, 65) +__OMP_SCHED_TYPE(ORD_STATIC, 66) +__OMP_SCHED_TYPE(ORD_DYNAMIC_CHUNKED, 67) +__OMP_SCHED_TYPE(ORD_GUIDED_CHUNKED, 68) +__OMP_SCHED_TYPE(ORD_RUNTIME, 69) +__OMP_SCHED_TYPE(ORD_AUTO, 70) +__OMP_SCHED_TYPE(SCH_DEFAULT, 34) // = SCH_STATIC +/// dist_schedule types +__OMP_SCHED_TYPE(DIST_SCH_STATIC_CHUNKED, 91) +__OMP_SCHED_TYPE(DIST_SCH_STATIC, 92) +/// Support for OpenMP 4.5 monotonic and nonmonotonic schedule modifiers. +/// Set if the monotonic schedule modifier was present. +__OMP_SCHED_TYPE(SCH_MODIFIER_MONOTONIC, 1 << 29) +/// Set if the nonmonotonic schedule modifier was present. +__OMP_SCHED_TYPE(SCH_MODIFIER_NONMONOTONIC, 1 << 30) + +#undef __OMP_SCHED_TYPE +#undef OMP_SCHED_TYPE + +///} + /// KMP cancel kind /// ///{ 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 @@ -780,6 +780,158 @@ emitTaskyieldImpl(Loc); } +// TODO: Handle privatisation callbacks +OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::CreateSections( + const LocationDescription &Loc, InsertPointTy AllocaIP, + ArrayRef SectionCBs, PrivatizeCallbackTy PrivCB, + FinalizeCallbackTy FiniCB, bool IsCancellable, bool IsNowait) { + if (!updateToLocation(Loc)) + return Loc.IP; + Constant *SrcLocStr = getOrCreateSrcLocStr(Loc); + Value *Ident = getOrCreateIdent(SrcLocStr); + Value *ThreadID = getOrCreateThreadID(Ident); + + BasicBlock *InsertBB = Builder.GetInsertBlock(); + Function *CurFn = InsertBB->getParent(); + + FinalizationStack.push_back( + {FiniCB, OMPD_sections, /*IsCancellable*/ IsCancellable}); + + InsertPointTy CurIP = Builder.saveIP(); + Builder.restoreIP(AllocaIP); + // allocate helper vars for for loop + Value *LB = Builder.CreateAlloca(Int32, nullptr, "omp.sections.lb"); + Value *UB = Builder.CreateAlloca(Int32, nullptr, "omp.sections.ub"); + Value *ST = Builder.CreateAlloca(Int32, nullptr, "omp.sections.st"); + Value *IL = Builder.CreateAlloca(Int32, nullptr, "omp.sections.il"); + Value *IV = Builder.CreateAlloca(Int32, nullptr, "omp.sections.iv"); + + Builder.restoreIP(CurIP); + // initialize helper vars for for loop + ConstantInt *GlobalUBVal = Builder.getInt32(SectionCBs.size() - 1); + Builder.CreateStore(Builder.getInt32(0), LB); + Builder.CreateStore(GlobalUBVal, UB); + Builder.CreateStore(Builder.getInt32(1), ST); + Builder.CreateStore(Builder.getInt32(0), IL); + + // emit kmpc_for + // The Control Flow Graph is described as follows: + // loop condition (omp.sections.inner.for.cond) <-------------------- + // | (false) | + // |--------> loop exit ---> end | + // (true)| | + // v | + // loop body | + // | | + // switch-case construct, one case for each section (see below) | + // | | + // loop increment | + // | | + // ----------------------------------------------------------- + // TODO: Change the following to call the IR Builder function to emit kmpc_for + // after for loop is lowered. + Value *ForEntryArgs[] = { + Ident, + ThreadID, + Builder.getInt32(static_cast( + SchedType::OMP_SCHED_TYPE_SCH_STATIC)), // Schedule type + IL, // &isLastIter + LB, // &LB + UB, // &UB + ST, // &Stride + Builder.getInt32(1), // Incr + Builder.getInt32(1) // Chunk + }; + Function *EntryRTLFn = + getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_for_static_init_4); + Builder.CreateCall(EntryRTLFn, ForEntryArgs); + + Instruction *LBRef = Builder.CreateLoad(LB); + Builder.CreateStore(LBRef, IV); + + // create new basic blocks for emitting the for loop + auto *ForBodyBB = + BasicBlock::Create(M.getContext(), "omp.sections.inner.for.body", CurFn); + auto *ForExitBB = + BasicBlock::Create(M.getContext(), "omp.sections.inner.for.exit", CurFn); + auto *ForIncBB = + BasicBlock::Create(M.getContext(), "omp.sections.inner.for.inc", CurFn); + + // split InsertBB to create the basic block for emitting for-loop-condition + auto *UI = new UnreachableInst(Builder.getContext(), InsertBB); + BasicBlock *ForCondBB = + InsertBB->splitBasicBlock(UI, "omp.sections.inner.for.cond"); + UI->eraseFromParent(); + Builder.SetInsertPoint(ForCondBB); + Instruction *IVRef = Builder.CreateLoad(IV); + Instruction *UBRef = Builder.CreateLoad(UB); + Value *cmpRef = + Builder.CreateICmpSLE(IVRef, UBRef, "omp.sections.inner.for.cond.cmp"); + Builder.CreateCondBr(cmpRef, ForBodyBB, ForExitBB); + + Builder.SetInsertPoint(ForBodyBB); + // creating switch statement inside for body. (this is also the finalization + // basic block) + auto *SwitchExitBB = BasicBlock::Create( + M.getContext(), "omp.sections.inner.for.body.switch.exit", CurFn); + + // end switch statement inside for body + Builder.SetInsertPoint(SwitchExitBB); + Builder.CreateBr(ForIncBB); + + // for Body + Builder.SetInsertPoint(ForBodyBB); + SwitchInst *SwitchStmt = + Builder.CreateSwitch(Builder.CreateLoad(IV), SwitchExitBB); + // each section in emitted as a switch case + // Iterate through all sections and emit a switch construct: + // switch (IV) { + // case 0: + // ; + // break; + // ... + // case - 1: + // - 1]>; + // break; + // } + // omp.sections.inner.for.body.switch.exit: + unsigned CaseNumber = 0; + for (auto SectionCB : SectionCBs) { + auto *CaseBB = BasicBlock::Create( + M.getContext(), "omp.sections.inner.for.body.switch.case", CurFn); + SwitchStmt->addCase(Builder.getInt32(CaseNumber), CaseBB); + Builder.SetInsertPoint(CaseBB); + SectionCB(InsertPointTy(), Builder.saveIP(), *SwitchExitBB); + CaseNumber++; + } + // end for body + + FinalizationInfo Fi = FinalizationStack.pop_back_val(); + assert(Fi.DK == OMPD_sections && + "Unexpected Directive for Finalization call!"); + Fi.FiniCB(InsertPointTy(SwitchExitBB, SwitchExitBB->getFirstInsertionPt())); + + // for inc + Builder.SetInsertPoint(ForIncBB); + Instruction *IVRef2 = Builder.CreateLoad(IV); + Value *Inc = Builder.CreateNSWAdd(IVRef2, Builder.getInt32(1), "inc"); + Builder.CreateStore(Inc, IV); + Builder.CreateBr(ForCondBB); + Builder.SetInsertPoint(ForExitBB); + + Value *ForExitArgs[] = {Ident, ThreadID}; + Function *ExitRTLFn = + getOrCreateRuntimeFunctionPtr(OMPRTL___kmpc_for_static_fini); + Builder.CreateCall(ExitRTLFn, ForExitArgs); + + if (!IsNowait) + CreateBarrier(Builder, OMPD_sections, true, IsCancellable); + + // TODO: Handle PrivCB + + return Builder.saveIP(); +} + OpenMPIRBuilder::InsertPointTy OpenMPIRBuilder::CreateMaster(const LocationDescription &Loc, BodyGenCallbackTy BodyGenCB, 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 @@ -1101,4 +1101,66 @@ EXPECT_EQ(SingleEndCI->getArgOperand(1), SingleEntryCI->getArgOperand(1)); } +TEST_F(OpenMPIRBuilderTest, CreateSections) { + using InsertPointTy = OpenMPIRBuilder::InsertPointTy; + using BodyGenCallbackTy = + llvm::function_ref; + OpenMPIRBuilder OMPBuilder(*M); + OMPBuilder.initialize(); + F->setName("func"); + IRBuilder<> Builder(BB); + + OpenMPIRBuilder::LocationDescription Loc({Builder.saveIP(), DL}); + llvm::SmallVector SectionCBVector; + + AllocaInst *PrivAI = nullptr; + + unsigned NumBodiesGenerated = 0; + unsigned NumFiniCBCalls = 0; + PrivAI = Builder.CreateAlloca(F->arg_begin()->getType()); + + auto FiniCB = [&](InsertPointTy IP) { + ++NumFiniCBCalls; + Builder.restoreIP(IP); + Builder.CreateStore(F->arg_begin(), PrivAI); + Builder.CreateLoad(PrivAI, "local.use.fini"); + }; + + auto SectionCB = [&](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, + BasicBlock &FiniBB) { + ++NumBodiesGenerated; + Builder.restoreIP(CodeGenIP); + Builder.CreateStore(F->arg_begin(), PrivAI); + Builder.CreateLoad(PrivAI, "local.use.section"); + Builder.CreateBr(&FiniBB); + }; + auto PrivCB = [](InsertPointTy AllocaIP, InsertPointTy CodeGenIP, + llvm::Value &Val, llvm::Value *&ReplVal) { + // TODO: Privatization not implemented yet + return CodeGenIP; + }; + + SectionCBVector.push_back(SectionCB); + SectionCBVector.push_back(SectionCB); + ArrayRef SectionCBs = makeArrayRef(SectionCBVector); + + IRBuilder<>::InsertPoint AllocaIP(&F->getEntryBlock(), + F->getEntryBlock().getFirstInsertionPt()); + + Builder.restoreIP(OMPBuilder.CreateSections(Loc, AllocaIP, SectionCBs, PrivCB, + FiniCB, false, false)); + Builder.CreateRetVoid(); // Required at the end of the function + + EXPECT_NE(PrivAI, nullptr); + Function *OutlinedFn = PrivAI->getFunction(); + EXPECT_EQ(F, OutlinedFn); + EXPECT_FALSE(verifyModule(*M, &errs())); + EXPECT_EQ(OutlinedFn->arg_size(), 1U); + EXPECT_EQ(OutlinedFn->getBasicBlockList().size(), 8U); + + ASSERT_EQ(NumBodiesGenerated, 2U); + ASSERT_EQ(NumFiniCBCalls, 1U); +} + } // namespace