189251edeSMichael Kruse //===------ LoopGeneratorsGOMP.cpp - IR helper to create loops ------------===// 289251edeSMichael Kruse // 389251edeSMichael Kruse // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 489251edeSMichael Kruse // See https://llvm.org/LICENSE.txt for license information. 589251edeSMichael Kruse // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 689251edeSMichael Kruse // 789251edeSMichael Kruse //===----------------------------------------------------------------------===// 889251edeSMichael Kruse // 989251edeSMichael Kruse // This file contains functions to create parallel loops as LLVM-IR. 1089251edeSMichael Kruse // 1189251edeSMichael Kruse //===----------------------------------------------------------------------===// 1289251edeSMichael Kruse 1389251edeSMichael Kruse #include "polly/CodeGen/LoopGeneratorsGOMP.h" 1422c77f23SMichael Kruse #include "llvm/Analysis/LoopInfo.h" 1589251edeSMichael Kruse #include "llvm/IR/Dominators.h" 1689251edeSMichael Kruse #include "llvm/IR/Module.h" 1789251edeSMichael Kruse 1889251edeSMichael Kruse using namespace llvm; 1989251edeSMichael Kruse using namespace polly; 2089251edeSMichael Kruse 2189251edeSMichael Kruse void ParallelLoopGeneratorGOMP::createCallSpawnThreads(Value *SubFn, 2289251edeSMichael Kruse Value *SubFnParam, 2389251edeSMichael Kruse Value *LB, Value *UB, 2489251edeSMichael Kruse Value *Stride) { 2589251edeSMichael Kruse const std::string Name = "GOMP_parallel_loop_runtime_start"; 2689251edeSMichael Kruse 2789251edeSMichael Kruse Function *F = M->getFunction(Name); 2889251edeSMichael Kruse 2989251edeSMichael Kruse // If F is not available, declare it. 3089251edeSMichael Kruse if (!F) { 3189251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 3289251edeSMichael Kruse 3389251edeSMichael Kruse Type *Params[] = {PointerType::getUnqual(FunctionType::get( 34a3ef8589SFangrui Song Builder.getVoidTy(), Builder.getPtrTy(), false)), 35a3ef8589SFangrui Song Builder.getPtrTy(), 3689251edeSMichael Kruse Builder.getInt32Ty(), 3789251edeSMichael Kruse LongType, 3889251edeSMichael Kruse LongType, 3989251edeSMichael Kruse LongType}; 4089251edeSMichael Kruse 4189251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false); 4289251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M); 4389251edeSMichael Kruse } 4489251edeSMichael Kruse 4589251edeSMichael Kruse Value *Args[] = {SubFn, SubFnParam, Builder.getInt32(PollyNumThreads), 4689251edeSMichael Kruse LB, UB, Stride}; 4789251edeSMichael Kruse 48fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, Args); 49fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated); 5089251edeSMichael Kruse } 5189251edeSMichael Kruse 523e5d671cSEli Friedman void ParallelLoopGeneratorGOMP::deployParallelExecution(Function *SubFn, 5389251edeSMichael Kruse Value *SubFnParam, 5489251edeSMichael Kruse Value *LB, Value *UB, 5589251edeSMichael Kruse Value *Stride) { 5689251edeSMichael Kruse // Tell the runtime we start a parallel loop 5789251edeSMichael Kruse createCallSpawnThreads(SubFn, SubFnParam, LB, UB, Stride); 58fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(SubFn, SubFnParam); 59fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated); 6089251edeSMichael Kruse createCallJoinThreads(); 6189251edeSMichael Kruse } 6289251edeSMichael Kruse 6389251edeSMichael Kruse Function *ParallelLoopGeneratorGOMP::prepareSubFnDefinition(Function *F) const { 6489251edeSMichael Kruse FunctionType *FT = 65a3ef8589SFangrui Song FunctionType::get(Builder.getVoidTy(), {Builder.getPtrTy()}, false); 6689251edeSMichael Kruse Function *SubFn = Function::Create(FT, Function::InternalLinkage, 6789251edeSMichael Kruse F->getName() + "_polly_subfn", M); 6889251edeSMichael Kruse // Name the function's arguments 6989251edeSMichael Kruse SubFn->arg_begin()->setName("polly.par.userContext"); 7089251edeSMichael Kruse return SubFn; 7189251edeSMichael Kruse } 7289251edeSMichael Kruse 7389251edeSMichael Kruse // Create a subfunction of the following (preliminary) structure: 7489251edeSMichael Kruse // 7589251edeSMichael Kruse // PrevBB 7689251edeSMichael Kruse // | 7789251edeSMichael Kruse // v 7889251edeSMichael Kruse // HeaderBB 7989251edeSMichael Kruse // | _____ 8089251edeSMichael Kruse // v v | 8189251edeSMichael Kruse // CheckNextBB PreHeaderBB 8289251edeSMichael Kruse // |\ | 8389251edeSMichael Kruse // | \______/ 8489251edeSMichael Kruse // | 8589251edeSMichael Kruse // v 8689251edeSMichael Kruse // ExitBB 8789251edeSMichael Kruse // 8889251edeSMichael Kruse // HeaderBB will hold allocations and loading of variables. 8989251edeSMichael Kruse // CheckNextBB will check for more work. 9089251edeSMichael Kruse // If there is more work to do: go to PreHeaderBB, otherwise go to ExitBB. 9189251edeSMichael Kruse // PreHeaderBB loads the new boundaries (& will lead to the loop body later on). 9289251edeSMichael Kruse // ExitBB marks the end of the parallel execution. 9389251edeSMichael Kruse std::tuple<Value *, Function *> 9489251edeSMichael Kruse ParallelLoopGeneratorGOMP::createSubFn(Value *Stride, AllocaInst *StructData, 9589251edeSMichael Kruse SetVector<Value *> Data, 9689251edeSMichael Kruse ValueMapT &Map) { 9789251edeSMichael Kruse if (PollyScheduling != OMPGeneralSchedulingType::Runtime) { 9889251edeSMichael Kruse // User tried to influence the scheduling type (currently not supported) 9989251edeSMichael Kruse errs() << "warning: Polly's GNU OpenMP backend solely " 10089251edeSMichael Kruse "supports the scheduling type 'runtime'.\n"; 10189251edeSMichael Kruse } 10289251edeSMichael Kruse 10389251edeSMichael Kruse if (PollyChunkSize != 0) { 10489251edeSMichael Kruse // User tried to influence the chunk size (currently not supported) 10589251edeSMichael Kruse errs() << "warning: Polly's GNU OpenMP backend solely " 10689251edeSMichael Kruse "supports the default chunk size.\n"; 10789251edeSMichael Kruse } 10889251edeSMichael Kruse 10989251edeSMichael Kruse Function *SubFn = createSubFnDefinition(); 11089251edeSMichael Kruse LLVMContext &Context = SubFn->getContext(); 11189251edeSMichael Kruse 11289251edeSMichael Kruse // Create basic blocks. 11389251edeSMichael Kruse BasicBlock *HeaderBB = BasicBlock::Create(Context, "polly.par.setup", SubFn); 11422c77f23SMichael Kruse SubFnDT = std::make_unique<DominatorTree>(*SubFn); 11522c77f23SMichael Kruse SubFnLI = std::make_unique<LoopInfo>(*SubFnDT); 11622c77f23SMichael Kruse 11789251edeSMichael Kruse BasicBlock *ExitBB = BasicBlock::Create(Context, "polly.par.exit", SubFn); 11889251edeSMichael Kruse BasicBlock *CheckNextBB = 11989251edeSMichael Kruse BasicBlock::Create(Context, "polly.par.checkNext", SubFn); 12089251edeSMichael Kruse BasicBlock *PreHeaderBB = 12189251edeSMichael Kruse BasicBlock::Create(Context, "polly.par.loadIVBounds", SubFn); 12289251edeSMichael Kruse 12322c77f23SMichael Kruse SubFnDT->addNewBlock(ExitBB, HeaderBB); 12422c77f23SMichael Kruse SubFnDT->addNewBlock(CheckNextBB, HeaderBB); 12522c77f23SMichael Kruse SubFnDT->addNewBlock(PreHeaderBB, HeaderBB); 12689251edeSMichael Kruse 12789251edeSMichael Kruse // Fill up basic block HeaderBB. 12889251edeSMichael Kruse Builder.SetInsertPoint(HeaderBB); 12989251edeSMichael Kruse Value *LBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.LBPtr"); 13089251edeSMichael Kruse Value *UBPtr = Builder.CreateAlloca(LongType, nullptr, "polly.par.UBPtr"); 13118680a36SNikita Popov Value *UserContext = &*SubFn->arg_begin(); 13289251edeSMichael Kruse 13389251edeSMichael Kruse extractValuesFromStruct(Data, StructData->getAllocatedType(), UserContext, 13489251edeSMichael Kruse Map); 13589251edeSMichael Kruse Builder.CreateBr(CheckNextBB); 13689251edeSMichael Kruse 13789251edeSMichael Kruse // Add code to check if another set of iterations will be executed. 13889251edeSMichael Kruse Builder.SetInsertPoint(CheckNextBB); 13989251edeSMichael Kruse Value *Next = createCallGetWorkItem(LBPtr, UBPtr); 14089251edeSMichael Kruse Value *HasNextSchedule = Builder.CreateTrunc( 14189251edeSMichael Kruse Next, Builder.getInt1Ty(), "polly.par.hasNextScheduleBlock"); 14289251edeSMichael Kruse Builder.CreateCondBr(HasNextSchedule, PreHeaderBB, ExitBB); 14389251edeSMichael Kruse 14489251edeSMichael Kruse // Add code to load the iv bounds for this set of iterations. 14589251edeSMichael Kruse Builder.SetInsertPoint(PreHeaderBB); 14646354bacSNikita Popov Value *LB = Builder.CreateLoad(LongType, LBPtr, "polly.par.LB"); 14746354bacSNikita Popov Value *UB = Builder.CreateLoad(LongType, UBPtr, "polly.par.UB"); 14889251edeSMichael Kruse 14989251edeSMichael Kruse // Subtract one as the upper bound provided by OpenMP is a < comparison 15089251edeSMichael Kruse // whereas the codegenForSequential function creates a <= comparison. 15189251edeSMichael Kruse UB = Builder.CreateSub(UB, ConstantInt::get(LongType, 1), 15289251edeSMichael Kruse "polly.par.UBAdjusted"); 15389251edeSMichael Kruse 15489251edeSMichael Kruse Builder.CreateBr(CheckNextBB); 15589251edeSMichael Kruse Builder.SetInsertPoint(&*--Builder.GetInsertPoint()); 15689251edeSMichael Kruse BasicBlock *AfterBB; 15789251edeSMichael Kruse Value *IV = 15822c77f23SMichael Kruse createLoop(LB, UB, Stride, Builder, *SubFnLI, *SubFnDT, AfterBB, 15922c77f23SMichael Kruse ICmpInst::ICMP_SLE, nullptr, true, /* UseGuard */ false); 16089251edeSMichael Kruse 16189251edeSMichael Kruse BasicBlock::iterator LoopBody = Builder.GetInsertPoint(); 16289251edeSMichael Kruse 16389251edeSMichael Kruse // Add code to terminate this subfunction. 16489251edeSMichael Kruse Builder.SetInsertPoint(ExitBB); 16589251edeSMichael Kruse createCallCleanupThread(); 16689251edeSMichael Kruse Builder.CreateRetVoid(); 16789251edeSMichael Kruse 16889251edeSMichael Kruse Builder.SetInsertPoint(&*LoopBody); 16989251edeSMichael Kruse 17022c77f23SMichael Kruse // FIXME: Call SubFnDT->verify() and SubFnLI->verify() to check that the 17122c77f23SMichael Kruse // DominatorTree/LoopInfo has been created correctly. Alternatively, recreate 17222c77f23SMichael Kruse // from scratch since it is not needed here directly. 17322c77f23SMichael Kruse 17489251edeSMichael Kruse return std::make_tuple(IV, SubFn); 17589251edeSMichael Kruse } 17689251edeSMichael Kruse 17789251edeSMichael Kruse Value *ParallelLoopGeneratorGOMP::createCallGetWorkItem(Value *LBPtr, 17889251edeSMichael Kruse Value *UBPtr) { 17989251edeSMichael Kruse const std::string Name = "GOMP_loop_runtime_next"; 18089251edeSMichael Kruse 18189251edeSMichael Kruse Function *F = M->getFunction(Name); 18289251edeSMichael Kruse 18389251edeSMichael Kruse // If F is not available, declare it. 18489251edeSMichael Kruse if (!F) { 18589251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 186*467a9bdeSYoungsuk Kim Type *Params[] = {Builder.getPtrTy(0), Builder.getPtrTy(0)}; 18789251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getInt8Ty(), Params, false); 18889251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M); 18989251edeSMichael Kruse } 19089251edeSMichael Kruse 19189251edeSMichael Kruse Value *Args[] = {LBPtr, UBPtr}; 192fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, Args); 193fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated); 194fe0e5b3eSMichael Kruse Value *Return = Builder.CreateICmpNE( 195fe0e5b3eSMichael Kruse Call, Builder.CreateZExt(Builder.getFalse(), Call->getType())); 19689251edeSMichael Kruse return Return; 19789251edeSMichael Kruse } 19889251edeSMichael Kruse 19989251edeSMichael Kruse void ParallelLoopGeneratorGOMP::createCallJoinThreads() { 20089251edeSMichael Kruse const std::string Name = "GOMP_parallel_end"; 20189251edeSMichael Kruse 20289251edeSMichael Kruse Function *F = M->getFunction(Name); 20389251edeSMichael Kruse 20489251edeSMichael Kruse // If F is not available, declare it. 20589251edeSMichael Kruse if (!F) { 20689251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 20789251edeSMichael Kruse 20889251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false); 20989251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M); 21089251edeSMichael Kruse } 21189251edeSMichael Kruse 212fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, {}); 213fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated); 21489251edeSMichael Kruse } 21589251edeSMichael Kruse 21689251edeSMichael Kruse void ParallelLoopGeneratorGOMP::createCallCleanupThread() { 21789251edeSMichael Kruse const std::string Name = "GOMP_loop_end_nowait"; 21889251edeSMichael Kruse 21989251edeSMichael Kruse Function *F = M->getFunction(Name); 22089251edeSMichael Kruse 22189251edeSMichael Kruse // If F is not available, declare it. 22289251edeSMichael Kruse if (!F) { 22389251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage; 22489251edeSMichael Kruse 22589251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false); 22689251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M); 22789251edeSMichael Kruse } 22889251edeSMichael Kruse 229fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, {}); 230fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated); 23189251edeSMichael Kruse } 232