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"
1489251edeSMichael Kruse #include "llvm/IR/Dominators.h"
1589251edeSMichael Kruse #include "llvm/IR/Module.h"
1689251edeSMichael Kruse
1789251edeSMichael Kruse using namespace llvm;
1889251edeSMichael Kruse using namespace polly;
1989251edeSMichael Kruse
createCallSpawnThreads(Value * SubFn,Value * SubFnParam,Value * LB,Value * UB,Value * Stride)2089251edeSMichael Kruse void ParallelLoopGeneratorGOMP::createCallSpawnThreads(Value *SubFn,
2189251edeSMichael Kruse Value *SubFnParam,
2289251edeSMichael Kruse Value *LB, Value *UB,
2389251edeSMichael Kruse Value *Stride) {
2489251edeSMichael Kruse const std::string Name = "GOMP_parallel_loop_runtime_start";
2589251edeSMichael Kruse
2689251edeSMichael Kruse Function *F = M->getFunction(Name);
2789251edeSMichael Kruse
2889251edeSMichael Kruse // If F is not available, declare it.
2989251edeSMichael Kruse if (!F) {
3089251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
3189251edeSMichael Kruse
3289251edeSMichael Kruse Type *Params[] = {PointerType::getUnqual(FunctionType::get(
3389251edeSMichael Kruse Builder.getVoidTy(), Builder.getInt8PtrTy(), false)),
3489251edeSMichael Kruse Builder.getInt8PtrTy(),
3589251edeSMichael Kruse Builder.getInt32Ty(),
3689251edeSMichael Kruse LongType,
3789251edeSMichael Kruse LongType,
3889251edeSMichael Kruse LongType};
3989251edeSMichael Kruse
4089251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), Params, false);
4189251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M);
4289251edeSMichael Kruse }
4389251edeSMichael Kruse
4489251edeSMichael Kruse Value *Args[] = {SubFn, SubFnParam, Builder.getInt32(PollyNumThreads),
4589251edeSMichael Kruse LB, UB, Stride};
4689251edeSMichael Kruse
47*fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, Args);
48*fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated);
4989251edeSMichael Kruse }
5089251edeSMichael Kruse
deployParallelExecution(Function * SubFn,Value * SubFnParam,Value * LB,Value * UB,Value * Stride)513e5d671cSEli Friedman void ParallelLoopGeneratorGOMP::deployParallelExecution(Function *SubFn,
5289251edeSMichael Kruse Value *SubFnParam,
5389251edeSMichael Kruse Value *LB, Value *UB,
5489251edeSMichael Kruse Value *Stride) {
5589251edeSMichael Kruse // Tell the runtime we start a parallel loop
5689251edeSMichael Kruse createCallSpawnThreads(SubFn, SubFnParam, LB, UB, Stride);
57*fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(SubFn, SubFnParam);
58*fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated);
5989251edeSMichael Kruse createCallJoinThreads();
6089251edeSMichael Kruse }
6189251edeSMichael Kruse
prepareSubFnDefinition(Function * F) const6289251edeSMichael Kruse Function *ParallelLoopGeneratorGOMP::prepareSubFnDefinition(Function *F) const {
6389251edeSMichael Kruse FunctionType *FT =
6489251edeSMichael Kruse FunctionType::get(Builder.getVoidTy(), {Builder.getInt8PtrTy()}, false);
6589251edeSMichael Kruse Function *SubFn = Function::Create(FT, Function::InternalLinkage,
6689251edeSMichael Kruse F->getName() + "_polly_subfn", M);
6789251edeSMichael Kruse // Name the function's arguments
6889251edeSMichael Kruse SubFn->arg_begin()->setName("polly.par.userContext");
6989251edeSMichael Kruse return SubFn;
7089251edeSMichael Kruse }
7189251edeSMichael Kruse
7289251edeSMichael Kruse // Create a subfunction of the following (preliminary) structure:
7389251edeSMichael Kruse //
7489251edeSMichael Kruse // PrevBB
7589251edeSMichael Kruse // |
7689251edeSMichael Kruse // v
7789251edeSMichael Kruse // HeaderBB
7889251edeSMichael Kruse // | _____
7989251edeSMichael Kruse // v v |
8089251edeSMichael Kruse // CheckNextBB PreHeaderBB
8189251edeSMichael Kruse // |\ |
8289251edeSMichael Kruse // | \______/
8389251edeSMichael Kruse // |
8489251edeSMichael Kruse // v
8589251edeSMichael Kruse // ExitBB
8689251edeSMichael Kruse //
8789251edeSMichael Kruse // HeaderBB will hold allocations and loading of variables.
8889251edeSMichael Kruse // CheckNextBB will check for more work.
8989251edeSMichael Kruse // If there is more work to do: go to PreHeaderBB, otherwise go to ExitBB.
9089251edeSMichael Kruse // PreHeaderBB loads the new boundaries (& will lead to the loop body later on).
9189251edeSMichael Kruse // ExitBB marks the end of the parallel execution.
9289251edeSMichael Kruse std::tuple<Value *, Function *>
createSubFn(Value * Stride,AllocaInst * StructData,SetVector<Value * > Data,ValueMapT & Map)9389251edeSMichael Kruse ParallelLoopGeneratorGOMP::createSubFn(Value *Stride, AllocaInst *StructData,
9489251edeSMichael Kruse SetVector<Value *> Data,
9589251edeSMichael Kruse ValueMapT &Map) {
9689251edeSMichael Kruse if (PollyScheduling != OMPGeneralSchedulingType::Runtime) {
9789251edeSMichael Kruse // User tried to influence the scheduling type (currently not supported)
9889251edeSMichael Kruse errs() << "warning: Polly's GNU OpenMP backend solely "
9989251edeSMichael Kruse "supports the scheduling type 'runtime'.\n";
10089251edeSMichael Kruse }
10189251edeSMichael Kruse
10289251edeSMichael Kruse if (PollyChunkSize != 0) {
10389251edeSMichael Kruse // User tried to influence the chunk size (currently not supported)
10489251edeSMichael Kruse errs() << "warning: Polly's GNU OpenMP backend solely "
10589251edeSMichael Kruse "supports the default chunk size.\n";
10689251edeSMichael Kruse }
10789251edeSMichael Kruse
10889251edeSMichael Kruse Function *SubFn = createSubFnDefinition();
10989251edeSMichael Kruse LLVMContext &Context = SubFn->getContext();
11089251edeSMichael Kruse
11189251edeSMichael Kruse // Store the previous basic block.
11289251edeSMichael Kruse BasicBlock *PrevBB = Builder.GetInsertBlock();
11389251edeSMichael Kruse
11489251edeSMichael Kruse // Create basic blocks.
11589251edeSMichael Kruse BasicBlock *HeaderBB = BasicBlock::Create(Context, "polly.par.setup", SubFn);
11689251edeSMichael Kruse BasicBlock *ExitBB = BasicBlock::Create(Context, "polly.par.exit", SubFn);
11789251edeSMichael Kruse BasicBlock *CheckNextBB =
11889251edeSMichael Kruse BasicBlock::Create(Context, "polly.par.checkNext", SubFn);
11989251edeSMichael Kruse BasicBlock *PreHeaderBB =
12089251edeSMichael Kruse BasicBlock::Create(Context, "polly.par.loadIVBounds", SubFn);
12189251edeSMichael Kruse
12289251edeSMichael Kruse DT.addNewBlock(HeaderBB, PrevBB);
12389251edeSMichael Kruse DT.addNewBlock(ExitBB, HeaderBB);
12489251edeSMichael Kruse DT.addNewBlock(CheckNextBB, HeaderBB);
12589251edeSMichael Kruse DT.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");
13189251edeSMichael Kruse Value *UserContext = Builder.CreateBitCast(
13289251edeSMichael Kruse &*SubFn->arg_begin(), StructData->getType(), "polly.par.userContext");
13389251edeSMichael Kruse
13489251edeSMichael Kruse extractValuesFromStruct(Data, StructData->getAllocatedType(), UserContext,
13589251edeSMichael Kruse Map);
13689251edeSMichael Kruse Builder.CreateBr(CheckNextBB);
13789251edeSMichael Kruse
13889251edeSMichael Kruse // Add code to check if another set of iterations will be executed.
13989251edeSMichael Kruse Builder.SetInsertPoint(CheckNextBB);
14089251edeSMichael Kruse Value *Next = createCallGetWorkItem(LBPtr, UBPtr);
14189251edeSMichael Kruse Value *HasNextSchedule = Builder.CreateTrunc(
14289251edeSMichael Kruse Next, Builder.getInt1Ty(), "polly.par.hasNextScheduleBlock");
14389251edeSMichael Kruse Builder.CreateCondBr(HasNextSchedule, PreHeaderBB, ExitBB);
14489251edeSMichael Kruse
14589251edeSMichael Kruse // Add code to load the iv bounds for this set of iterations.
14689251edeSMichael Kruse Builder.SetInsertPoint(PreHeaderBB);
14746354bacSNikita Popov Value *LB = Builder.CreateLoad(LongType, LBPtr, "polly.par.LB");
14846354bacSNikita Popov Value *UB = Builder.CreateLoad(LongType, UBPtr, "polly.par.UB");
14989251edeSMichael Kruse
15089251edeSMichael Kruse // Subtract one as the upper bound provided by OpenMP is a < comparison
15189251edeSMichael Kruse // whereas the codegenForSequential function creates a <= comparison.
15289251edeSMichael Kruse UB = Builder.CreateSub(UB, ConstantInt::get(LongType, 1),
15389251edeSMichael Kruse "polly.par.UBAdjusted");
15489251edeSMichael Kruse
15589251edeSMichael Kruse Builder.CreateBr(CheckNextBB);
15689251edeSMichael Kruse Builder.SetInsertPoint(&*--Builder.GetInsertPoint());
15789251edeSMichael Kruse BasicBlock *AfterBB;
15889251edeSMichael Kruse Value *IV =
15989251edeSMichael Kruse createLoop(LB, UB, Stride, Builder, LI, DT, AfterBB, ICmpInst::ICMP_SLE,
16089251edeSMichael Kruse nullptr, true, /* UseGuard */ false);
16189251edeSMichael Kruse
16289251edeSMichael Kruse BasicBlock::iterator LoopBody = Builder.GetInsertPoint();
16389251edeSMichael Kruse
16489251edeSMichael Kruse // Add code to terminate this subfunction.
16589251edeSMichael Kruse Builder.SetInsertPoint(ExitBB);
16689251edeSMichael Kruse createCallCleanupThread();
16789251edeSMichael Kruse Builder.CreateRetVoid();
16889251edeSMichael Kruse
16989251edeSMichael Kruse Builder.SetInsertPoint(&*LoopBody);
17089251edeSMichael Kruse
17189251edeSMichael Kruse return std::make_tuple(IV, SubFn);
17289251edeSMichael Kruse }
17389251edeSMichael Kruse
createCallGetWorkItem(Value * LBPtr,Value * UBPtr)17489251edeSMichael Kruse Value *ParallelLoopGeneratorGOMP::createCallGetWorkItem(Value *LBPtr,
17589251edeSMichael Kruse Value *UBPtr) {
17689251edeSMichael Kruse const std::string Name = "GOMP_loop_runtime_next";
17789251edeSMichael Kruse
17889251edeSMichael Kruse Function *F = M->getFunction(Name);
17989251edeSMichael Kruse
18089251edeSMichael Kruse // If F is not available, declare it.
18189251edeSMichael Kruse if (!F) {
18289251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
18389251edeSMichael Kruse Type *Params[] = {LongType->getPointerTo(), LongType->getPointerTo()};
18489251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getInt8Ty(), Params, false);
18589251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M);
18689251edeSMichael Kruse }
18789251edeSMichael Kruse
18889251edeSMichael Kruse Value *Args[] = {LBPtr, UBPtr};
189*fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, Args);
190*fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated);
191*fe0e5b3eSMichael Kruse Value *Return = Builder.CreateICmpNE(
192*fe0e5b3eSMichael Kruse Call, Builder.CreateZExt(Builder.getFalse(), Call->getType()));
19389251edeSMichael Kruse return Return;
19489251edeSMichael Kruse }
19589251edeSMichael Kruse
createCallJoinThreads()19689251edeSMichael Kruse void ParallelLoopGeneratorGOMP::createCallJoinThreads() {
19789251edeSMichael Kruse const std::string Name = "GOMP_parallel_end";
19889251edeSMichael Kruse
19989251edeSMichael Kruse Function *F = M->getFunction(Name);
20089251edeSMichael Kruse
20189251edeSMichael Kruse // If F is not available, declare it.
20289251edeSMichael Kruse if (!F) {
20389251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
20489251edeSMichael Kruse
20589251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false);
20689251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M);
20789251edeSMichael Kruse }
20889251edeSMichael Kruse
209*fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, {});
210*fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated);
21189251edeSMichael Kruse }
21289251edeSMichael Kruse
createCallCleanupThread()21389251edeSMichael Kruse void ParallelLoopGeneratorGOMP::createCallCleanupThread() {
21489251edeSMichael Kruse const std::string Name = "GOMP_loop_end_nowait";
21589251edeSMichael Kruse
21689251edeSMichael Kruse Function *F = M->getFunction(Name);
21789251edeSMichael Kruse
21889251edeSMichael Kruse // If F is not available, declare it.
21989251edeSMichael Kruse if (!F) {
22089251edeSMichael Kruse GlobalValue::LinkageTypes Linkage = Function::ExternalLinkage;
22189251edeSMichael Kruse
22289251edeSMichael Kruse FunctionType *Ty = FunctionType::get(Builder.getVoidTy(), false);
22389251edeSMichael Kruse F = Function::Create(Ty, Linkage, Name, M);
22489251edeSMichael Kruse }
22589251edeSMichael Kruse
226*fe0e5b3eSMichael Kruse CallInst *Call = Builder.CreateCall(F, {});
227*fe0e5b3eSMichael Kruse Call->setDebugLoc(DLGenerated);
22889251edeSMichael Kruse }
229