diff options
| author | Siddharth Bhat <siddu.druid@gmail.com> | 2017-06-26 13:12:06 +0000 |
|---|---|---|
| committer | Siddharth Bhat <siddu.druid@gmail.com> | 2017-06-26 13:12:06 +0000 |
| commit | f291c8d510d28ec3464d764c81bd46690edeb9e9 (patch) | |
| tree | 6880edfc02cb5c19c91d613e40c9a776abe72b2b /polly/lib/CodeGen/PPCGCodeGeneration.cpp | |
| parent | 256070d85c65edddd6d7601b317e9768267321fb (diff) | |
[PPCGCodeGeneration] Allow intrinsics within kernels.
- In D33414, if any function call was found within a kernel, we would bail out.
- This is an over-approximation. This patch changes this by allowing the
`llvm.sqrt.*` family of intrinsics.
- This introduces an additional step when creating a separate llvm::Module
for a kernel (GPUModule). We now copy function declarations from the
original module to new module.
- We also populate IslNodeBuilder::ValueMap so it replaces the function
references to the old module to the ones in the new module
(GPUModule).
Differential Revision: https://reviews.llvm.org/D34145
llvm-svn: 306284
Diffstat (limited to 'polly/lib/CodeGen/PPCGCodeGeneration.cpp')
| -rw-r--r-- | polly/lib/CodeGen/PPCGCodeGeneration.cpp | 145 |
1 files changed, 125 insertions, 20 deletions
diff --git a/polly/lib/CodeGen/PPCGCodeGeneration.cpp b/polly/lib/CodeGen/PPCGCodeGeneration.cpp index 02b401615032..bc07eecc628f 100644 --- a/polly/lib/CodeGen/PPCGCodeGeneration.cpp +++ b/polly/lib/CodeGen/PPCGCodeGeneration.cpp @@ -255,8 +255,12 @@ private: /// /// @param Kernel The kernel to scan for llvm::Values /// - /// @returns A set of values referenced by the kernel. - SetVector<Value *> getReferencesInKernel(ppcg_kernel *Kernel); + /// @returns A pair, whose first element contains the set of values + /// referenced by the kernel, and whose second element contains the + /// set of functions referenced by the kernel. All functions in the + /// second set satisfy isValidFunctionInKernel. + std::pair<SetVector<Value *>, SetVector<Function *>> + getReferencesInKernel(ppcg_kernel *Kernel); /// Compute the sizes of the execution grid for a given kernel. /// @@ -365,8 +369,11 @@ private: /// /// @param Kernel The kernel to generate code for. /// @param SubtreeValues The set of llvm::Values referenced by this kernel. + /// @param SubtreeFunctions The set of llvm::Functions referenced by this + /// kernel. void createKernelFunction(ppcg_kernel *Kernel, - SetVector<Value *> &SubtreeValues); + SetVector<Value *> &SubtreeValues, + SetVector<Function *> &SubtreeFunctions); /// Create the declaration of a kernel function. /// @@ -389,6 +396,25 @@ private: /// @param The kernel to generate the intrinsic functions for. void insertKernelIntrinsics(ppcg_kernel *Kernel); + /// Setup the creation of functions referenced by the GPU kernel. + /// + /// 1. Create new function declarations in GPUModule which are the same as + /// SubtreeFunctions. + /// + /// 2. Populate IslNodeBuilder::ValueMap with mappings from + /// old functions (that come from the original module) to new functions + /// (that are created within GPUModule). That way, we generate references + /// to the correct function (in GPUModule) in BlockGenerator. + /// + /// @see IslNodeBuilder::ValueMap + /// @see BlockGenerator::GlobalMap + /// @see BlockGenerator::getNewValue + /// @see GPUNodeBuilder::getReferencesInKernel. + /// + /// @param SubtreeFunctions The set of llvm::Functions referenced by + /// this kernel. + void setupKernelSubtreeFunctions(SetVector<Function *> SubtreeFunctions); + /// Create a global-to-shared or shared-to-global copy statement. /// /// @param CopyStmt The copy statement to generate code for @@ -1109,7 +1135,40 @@ isl_bool collectReferencesInGPUStmt(__isl_keep isl_ast_node *Node, void *User) { return isl_bool_true; } -SetVector<Value *> GPUNodeBuilder::getReferencesInKernel(ppcg_kernel *Kernel) { +/// Check if F is a function that we can code-generate in a GPU kernel. +static bool isValidFunctionInKernel(llvm::Function *F) { + assert(F && "F is an invalid pointer"); + // We string compare against the name of the function to allow + // all variants of the intrinsic "llvm.sqrt.*" + return F->isIntrinsic() && F->getName().startswith("llvm.sqrt"); +} + +/// Do not take `Function` as a subtree value. +/// +/// We try to take the reference of all subtree values and pass them along +/// to the kernel from the host. Taking an address of any function and +/// trying to pass along is nonsensical. Only allow `Value`s that are not +/// `Function`s. +static bool isValidSubtreeValue(llvm::Value *V) { return !isa<Function>(V); } + +/// Return `Function`s from `RawSubtreeValues`. +static SetVector<Function *> +getFunctionsFromRawSubtreeValues(SetVector<Value *> RawSubtreeValues) { + SetVector<Function *> SubtreeFunctions; + for (Value *It : RawSubtreeValues) { + Function *F = dyn_cast<Function>(It); + if (F) { + assert(isValidFunctionInKernel(F) && "Code should have bailed out by " + "this point if an invalid function " + "were present in a kernel."); + SubtreeFunctions.insert(F); + } + } + return SubtreeFunctions; +} + +std::pair<SetVector<Value *>, SetVector<Function *>> +GPUNodeBuilder::getReferencesInKernel(ppcg_kernel *Kernel) { SetVector<Value *> SubtreeValues; SetVector<const SCEV *> SCEVs; SetVector<const Loop *> Loops; @@ -1146,7 +1205,19 @@ SetVector<Value *> GPUNodeBuilder::getReferencesInKernel(ppcg_kernel *Kernel) { isl_id_free(Id); } - return SubtreeValues; + // Note: { ValidSubtreeValues, ValidSubtreeFunctions } partitions + // SubtreeValues. This is important, because we should not lose any + // SubtreeValues in the process of constructing the + // "ValidSubtree{Values, Functions} sets. Nor should the set + // ValidSubtree{Values, Functions} have any common element. + auto ValidSubtreeValuesIt = + make_filter_range(SubtreeValues, isValidSubtreeValue); + SetVector<Value *> ValidSubtreeValues(ValidSubtreeValuesIt.begin(), + ValidSubtreeValuesIt.end()); + SetVector<Function *> ValidSubtreeFunctions( + getFunctionsFromRawSubtreeValues(SubtreeValues)); + + return std::make_pair(ValidSubtreeValues, ValidSubtreeFunctions); } void GPUNodeBuilder::clearDominators(Function *F) { @@ -1353,6 +1424,21 @@ GPUNodeBuilder::createLaunchParameters(ppcg_kernel *Kernel, Function *F, Launch + "_params_i8ptr", Location); } +void GPUNodeBuilder::setupKernelSubtreeFunctions( + SetVector<Function *> SubtreeFunctions) { + for (auto Fn : SubtreeFunctions) { + const std::string ClonedFnName = Fn->getName(); + Function *Clone = GPUModule->getFunction(ClonedFnName); + if (!Clone) + Clone = + Function::Create(Fn->getFunctionType(), GlobalValue::ExternalLinkage, + ClonedFnName, GPUModule.get()); + assert(Clone && "Expected cloned function to be initialized."); + assert(ValueMap.find(Fn) == ValueMap.end() && + "Fn already present in ValueMap"); + ValueMap[Fn] = Clone; + } +} void GPUNodeBuilder::createKernel(__isl_take isl_ast_node *KernelStmt) { isl_id *Id = isl_ast_node_get_annotation(KernelStmt); ppcg_kernel *Kernel = (ppcg_kernel *)isl_id_get_user(Id); @@ -1369,7 +1455,9 @@ void GPUNodeBuilder::createKernel(__isl_take isl_ast_node *KernelStmt) { Value *BlockDimX, *BlockDimY, *BlockDimZ; std::tie(BlockDimX, BlockDimY, BlockDimZ) = getBlockSizes(Kernel); - SetVector<Value *> SubtreeValues = getReferencesInKernel(Kernel); + SetVector<Value *> SubtreeValues; + SetVector<Function *> SubtreeFunctions; + std::tie(SubtreeValues, SubtreeFunctions) = getReferencesInKernel(Kernel); assert(Kernel->tree && "Device AST of kernel node is empty"); @@ -1393,7 +1481,8 @@ void GPUNodeBuilder::createKernel(__isl_take isl_ast_node *KernelStmt) { SubtreeValues.insert(V); } - createKernelFunction(Kernel, SubtreeValues); + createKernelFunction(Kernel, SubtreeValues, SubtreeFunctions); + setupKernelSubtreeFunctions(SubtreeFunctions); create(isl_ast_node_copy(Kernel->tree)); @@ -1721,8 +1810,9 @@ void GPUNodeBuilder::createKernelVariables(ppcg_kernel *Kernel, Function *FN) { } } -void GPUNodeBuilder::createKernelFunction(ppcg_kernel *Kernel, - SetVector<Value *> &SubtreeValues) { +void GPUNodeBuilder::createKernelFunction( + ppcg_kernel *Kernel, SetVector<Value *> &SubtreeValues, + SetVector<Function *> &SubtreeFunctions) { std::string Identifier = "kernel_" + std::to_string(Kernel->id); GPUModule.reset(new Module(Identifier, Builder.getContext())); @@ -2611,9 +2701,18 @@ public: return isl_ast_expr_ge(Iterations, MinComputeExpr); } - /// Check whether the Block contains any Function value. - bool ContainsFnPtrValInBlock(const BasicBlock *BB) { - for (const Instruction &Inst : *BB) + /// Check if the basic block contains a function we cannot codegen for GPU + /// kernels. + /// + /// If this basic block does something with a `Function` other than calling + /// a function that we support in a kernel, return true. + bool containsInvalidKernelFunctionInBllock(const BasicBlock *BB) { + for (const Instruction &Inst : *BB) { + const CallInst *Call = dyn_cast<CallInst>(&Inst); + if (Call && isValidFunctionInKernel(Call->getCalledFunction())) { + continue; + } + for (Value *SrcVal : Inst.operands()) { PointerType *p = dyn_cast<PointerType>(SrcVal->getType()); if (!p) @@ -2621,20 +2720,21 @@ public: if (isa<FunctionType>(p->getElementType())) return true; } + } return false; } - /// Return whether the Scop S has functions. - bool ContainsFnPtr(const Scop &S) { + /// Return whether the Scop S uses functions in a way that we do not support. + bool containsInvalidKernelFunction(const Scop &S) { for (auto &Stmt : S) { if (Stmt.isBlockStmt()) { - if (ContainsFnPtrValInBlock(Stmt.getBasicBlock())) + if (containsInvalidKernelFunctionInBllock(Stmt.getBasicBlock())) return true; } else { assert(Stmt.isRegionStmt() && "Stmt was neither block nor region statement"); for (const BasicBlock *BB : Stmt.getRegion()->blocks()) - if (ContainsFnPtrValInBlock(BB)) + if (containsInvalidKernelFunctionInBllock(BB)) return true; } } @@ -2708,13 +2808,18 @@ public: DL = &S->getRegion().getEntry()->getModule()->getDataLayout(); RI = &getAnalysis<RegionInfoPass>().getRegionInfo(); - // We currently do not support functions inside kernels, as code - // generation will need to offload function calls to the kernel. - // This may lead to a kernel trying to call a function on the host. + // We currently do not support functions other than intrinsics inside + // kernels, as code generation will need to offload function calls to the + // kernel. This may lead to a kernel trying to call a function on the host. // This also allows us to prevent codegen from trying to take the // address of an intrinsic function to send to the kernel. - if (ContainsFnPtr(CurrentScop)) + if (containsInvalidKernelFunction(CurrentScop)) { + DEBUG( + dbgs() + << "Scop contains function which cannot be materialised in a GPU " + "kernel. Bailing out.\n";); return false; + } auto PPCGScop = createPPCGScop(); auto PPCGProg = createPPCGProg(PPCGScop); |
