1 From 9ce0fe02fd6cda5fb29fbb0d5037a1798a810b8a Mon Sep 17 00:00:00 2001
2 From: Alexey Sotkin <alexey.sotkin@intel.com>
3 Date: Thu, 21 Feb 2019 17:14:36 +0300
4 Subject: [PATCH 1/3] Update LowerOpenCL pass to handle new blocks
5 represntation in LLVM IR
8 lib/SPIRV/SPIRVLowerOCLBlocks.cpp | 413 ++++++++----------------------
9 test/global_block.ll | 71 ++---
10 test/literal-struct.ll | 31 ++-
11 test/transcoding/block_w_struct_return.ll | 47 ++--
12 test/transcoding/enqueue_kernel.ll | 237 ++++++++++-------
13 5 files changed, 317 insertions(+), 482 deletions(-)
15 diff --git a/lib/SPIRV/SPIRVLowerOCLBlocks.cpp b/lib/SPIRV/SPIRVLowerOCLBlocks.cpp
16 index 50e1838..b42a4ec 100644
17 --- a/lib/SPIRV/SPIRVLowerOCLBlocks.cpp
18 +++ b/lib/SPIRV/SPIRVLowerOCLBlocks.cpp
20 -//===- SPIRVLowerOCLBlocks.cpp - OCL Utilities ----------------------------===//
\r
22 -// The LLVM/SPIRV Translator
\r
24 -// This file is distributed under the University of Illinois Open Source
\r
25 -// License. See LICENSE.TXT for details.
\r
27 -// Copyright (c) 2018 Intel Corporation. All rights reserved.
\r
29 -// Permission is hereby granted, free of charge, to any person obtaining a
\r
30 -// copy of this software and associated documentation files (the "Software"),
\r
31 -// to deal with the Software without restriction, including without limitation
\r
32 -// the rights to use, copy, modify, merge, publish, distribute, sublicense,
\r
33 -// and/or sell copies of the Software, and to permit persons to whom the
\r
34 -// Software is furnished to do so, subject to the following conditions:
\r
36 -// Redistributions of source code must retain the above copyright notice,
\r
37 -// this list of conditions and the following disclaimers.
\r
38 -// Redistributions in binary form must reproduce the above copyright notice,
\r
39 -// this list of conditions and the following disclaimers in the documentation
\r
40 -// and/or other materials provided with the distribution.
\r
41 -// Neither the names of Intel Corporation, nor the names of its
\r
42 -// contributors may be used to endorse or promote products derived from this
\r
43 -// Software without specific prior written permission.
\r
44 -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
45 -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
46 -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
47 -// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
48 -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
49 -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
\r
52 -//===----------------------------------------------------------------------===//
\r
54 -// SPIR-V specification doesn't allow function pointers, so SPIR-V translator
\r
55 -// is designed to fail if a value with function type (except calls) is occured.
\r
56 -// Currently there is only two cases, when function pointers are generating in
\r
57 -// LLVM IR in OpenCL - block calls and device side enqueue built-in calls.
\r
59 -// In both cases values with function type used as intermediate representation
\r
60 -// for block literal structure.
\r
62 -// This pass is designed to find such cases and simplify them to avoid any
\r
63 -// function pointer types occurrences in LLVM IR in 4 steps.
\r
65 -// 1. Find all function pointer allocas, like
\r
66 -// %block = alloca void () *
\r
68 -// Then find a single store to that alloca:
\r
69 -// %blockLit = alloca <{ i32, i32, ...}>, align 4
\r
70 -// %0 = bitcast <{ i32, i32, ... }>* %blockLit to void ()*
\r
71 -// > store void ()* %0, void ()** %block, align 4
\r
73 -// And replace the alloca users by new instructions which used stored value
\r
74 -// %blockLit itself instead of function pointer alloca %block.
\r
76 -// 2. Find consecutive casts from block literal type to i8 addrspace(4)*
\r
77 -// used function pointers as an intermediate type:
\r
78 -// %0 = bitcast <{ i32, i32 }> %block to void() *
\r
79 -// %1 = addrspacecast void() * %0 to i8 addrspace(4)*
\r
80 -// And simplify them:
\r
81 -// %2 = addrspacecast <{ i32, i32 }> %block to i8 addrspace(4)*
\r
83 -// 3. Find all unused instructions with function pointer type occured after
\r
84 -// pp.1-2 and remove them.
\r
86 -// 4. Find unused globals with function pointer type, like
\r
87 -// @block = constant void ()*
\r
88 -// bitcast ({ i32, i32 }* @__block_literal_global to void ()*
\r
90 -// And remove them.
\r
92 -//===----------------------------------------------------------------------===//
\r
93 -#define DEBUG_TYPE "spv-lower-ocl-blocks"
\r
95 -#include "OCLUtil.h"
\r
96 -#include "SPIRVInternal.h"
\r
98 -#include "llvm/ADT/SetVector.h"
\r
99 -#include "llvm/Analysis/ValueTracking.h"
\r
100 -#include "llvm/IR/GlobalVariable.h"
\r
101 -#include "llvm/IR/InstIterator.h"
\r
102 -#include "llvm/IR/Module.h"
\r
103 -#include "llvm/Pass.h"
\r
104 -#include "llvm/PassSupport.h"
\r
105 -#include "llvm/Support/Casting.h"
\r
107 -using namespace llvm;
\r
112 -removeUnusedFunctionPtrInst(Instruction *I,
\r
113 - SmallSetVector<Instruction *, 16> &FuncPtrInsts) {
\r
114 - for (unsigned OpIdx = 0, Ops = I->getNumOperands(); OpIdx != Ops; ++OpIdx) {
\r
115 - Instruction *OpI = dyn_cast<Instruction>(I->getOperand(OpIdx));
\r
116 - I->setOperand(OpIdx, nullptr);
\r
117 - if (OpI && OpI != I && OpI->user_empty())
\r
118 - FuncPtrInsts.insert(OpI);
\r
120 - I->eraseFromParent();
\r
123 -static bool isFuncPtrAlloca(const AllocaInst *AI) {
\r
124 - auto *ET = dyn_cast<PointerType>(AI->getAllocatedType());
\r
125 - return ET && ET->getElementType()->isFunctionTy();
\r
128 -static bool hasFuncPtrType(const Value *V) {
\r
129 - auto *PT = dyn_cast<PointerType>(V->getType());
\r
130 - return PT && PT->getElementType()->isFunctionTy();
\r
133 -static bool isFuncPtrInst(const Instruction *I) {
\r
134 - if (auto *AI = dyn_cast<AllocaInst>(I))
\r
135 - return isFuncPtrAlloca(AI);
\r
137 - for (auto &Op : I->operands()) {
\r
138 - if (auto *AI = dyn_cast<AllocaInst>(Op))
\r
139 - return isFuncPtrAlloca(AI);
\r
141 - auto *OpI = dyn_cast<Instruction>(&Op);
\r
142 - if (OpI && OpI != I && hasFuncPtrType(OpI))
\r
148 -static StoreInst *findSingleStore(AllocaInst *AI) {
\r
149 - StoreInst *Store = nullptr;
\r
150 - for (auto *U : AI->users()) {
\r
151 - if (!isa<StoreInst>(U))
\r
152 - continue; // not a store
\r
154 - return nullptr; // there are more than one stores
\r
155 - Store = dyn_cast<StoreInst>(U);
\r
160 -static void fixFunctionPtrAllocaUsers(AllocaInst *AI) {
\r
161 - // Find and remove a single store to alloca
\r
162 - auto *SingleStore = findSingleStore(AI);
\r
163 - assert(SingleStore && "More than one store to the function pointer alloca");
\r
164 - auto *StoredVal = SingleStore->getValueOperand();
\r
165 - SingleStore->eraseFromParent();
\r
167 - // Find loads from the alloca and replace thier users
\r
168 - for (auto *U : AI->users()) {
\r
169 - auto *LI = dyn_cast<LoadInst>(U);
\r
173 - for (auto *U : LI->users()) {
\r
174 - auto *UInst = cast<Instruction>(U);
\r
175 - auto *Cast = CastInst::CreatePointerBitCastOrAddrSpaceCast(
\r
176 - StoredVal, UInst->getType(), "", UInst);
\r
177 - UInst->replaceAllUsesWith(Cast);
\r
182 -static int getBlockLiteralIdx(const Function &F) {
\r
183 - StringRef FName = F.getName();
\r
184 - if (isEnqueueKernelBI(FName))
\r
185 - return FName.contains("events") ? 7 : 4;
\r
186 - if (isKernelQueryBI(FName))
\r
187 - return FName.contains("for_ndrange") ? 2 : 1;
\r
188 - if (FName.startswith("__") && FName.contains("_block_invoke"))
\r
189 - return F.hasStructRetAttr() ? 1 : 0;
\r
191 - return -1; // No block literal argument
\r
194 -static bool hasBlockLiteralArg(const Function &F) {
\r
195 - return getBlockLiteralIdx(F) != -1;
\r
198 -static bool simplifyFunctionPtrCasts(Function &F) {
\r
199 - bool Changed = false;
\r
200 - int BlockLiteralIdx = getBlockLiteralIdx(F);
\r
201 - for (auto *U : F.users()) {
\r
202 - auto *Call = dyn_cast<CallInst>(U);
\r
205 - if (Call->getFunction()->getName() == F.getName().str() + "_kernel")
\r
206 - continue; // Skip block invoke function calls inside block invoke kernels
\r
208 - const DataLayout &DL = F.getParent()->getDataLayout();
\r
209 - auto *BlockLiteral = Call->getOperand(BlockLiteralIdx);
\r
210 - auto *BlockLiteralVal = GetUnderlyingObject(BlockLiteral, DL);
\r
211 - if (isa<GlobalVariable>(BlockLiteralVal))
\r
212 - continue; // nothing to do with globals
\r
214 - auto *BlockLiteralAlloca = cast<AllocaInst>(BlockLiteralVal);
\r
215 - assert(!BlockLiteralAlloca->getAllocatedType()->isFunctionTy() &&
\r
216 - "Function type shouldn't be there");
\r
218 - auto *NewBlockLiteral = CastInst::CreatePointerBitCastOrAddrSpaceCast(
\r
219 - BlockLiteralAlloca, BlockLiteral->getType(), "", Call);
\r
220 - BlockLiteral->replaceAllUsesWith(NewBlockLiteral);
\r
227 -findFunctionPtrAllocas(Module &M,
\r
228 - SmallVectorImpl<AllocaInst *> &FuncPtrAllocas) {
\r
229 - for (auto &F : M) {
\r
230 - if (F.isDeclaration())
\r
232 - for (auto &I : instructions(F)) {
\r
233 - auto *AI = dyn_cast<AllocaInst>(&I);
\r
234 - if (!AI || !isFuncPtrAlloca(AI))
\r
236 - FuncPtrAllocas.push_back(AI);
\r
242 -findUnusedFunctionPtrInsts(Module &M,
\r
243 - SmallSetVector<Instruction *, 16> &FuncPtrInsts) {
\r
244 - for (auto &F : M) {
\r
245 - if (F.isDeclaration())
\r
247 - for (auto &I : instructions(F))
\r
248 - if (I.user_empty() && isFuncPtrInst(&I))
\r
249 - FuncPtrInsts.insert(&I);
\r
254 -findUnusedFunctionPtrGlbs(Module &M,
\r
255 - SmallVectorImpl<GlobalVariable *> &FuncPtrGlbs) {
\r
256 - for (auto &GV : M.globals()) {
\r
257 - if (!GV.user_empty())
\r
259 - auto *GVType = dyn_cast<PointerType>(GV.getType()->getElementType());
\r
260 - if (GVType && GVType->getElementType()->isFunctionTy())
\r
261 - FuncPtrGlbs.push_back(&GV);
\r
265 -class SPIRVLowerOCLBlocks : public ModulePass {
\r
268 - SPIRVLowerOCLBlocks() : ModulePass(ID) {}
\r
270 - bool runOnModule(Module &M) {
\r
271 - bool Changed = false;
\r
273 - // 1. Find function pointer allocas and fix their users
\r
274 - SmallVector<AllocaInst *, 16> FuncPtrAllocas;
\r
275 - findFunctionPtrAllocas(M, FuncPtrAllocas);
\r
277 - Changed |= !FuncPtrAllocas.empty();
\r
278 - for (auto *AI : FuncPtrAllocas)
\r
279 - fixFunctionPtrAllocaUsers(AI);
\r
281 - // 2. Simplify consecutive casts which use function pointer types
\r
282 - for (auto &F : M)
\r
283 - if (hasBlockLiteralArg(F))
\r
284 - Changed |= simplifyFunctionPtrCasts(F);
\r
286 - // 3. Cleanup unused instructions with function pointer type
\r
287 - // which are occured after pp. 1-2
\r
288 - SmallSetVector<Instruction *, 16> FuncPtrInsts;
\r
289 - findUnusedFunctionPtrInsts(M, FuncPtrInsts);
\r
291 - Changed |= !FuncPtrInsts.empty();
\r
292 - while (!FuncPtrInsts.empty()) {
\r
293 - Instruction *I = FuncPtrInsts.pop_back_val();
\r
294 - removeUnusedFunctionPtrInst(I, FuncPtrInsts);
\r
297 - // 4. Find and remove unused global variables with function pointer type
\r
298 - SmallVector<GlobalVariable *, 16> FuncPtrGlbs;
\r
299 - findUnusedFunctionPtrGlbs(M, FuncPtrGlbs);
\r
301 - Changed |= !FuncPtrGlbs.empty();
\r
302 - for (auto *GV : FuncPtrGlbs)
\r
303 - GV->eraseFromParent();
\r
309 -}; // class SPIRVLowerOCLBlocks
\r
311 -char SPIRVLowerOCLBlocks::ID = 0;
\r
316 - SPIRVLowerOCLBlocks, "spv-lower-ocl-blocks",
\r
317 - "Remove function pointers occured in case of using OpenCL blocks", false,
\r
320 -llvm::ModulePass *llvm::createSPIRVLowerOCLBlocks() {
\r
321 - return new SPIRVLowerOCLBlocks();
\r
323 +//===- SPIRVLowerOCLBlocks.cpp - OCL Utilities ----------------------------===//
325 +// The LLVM/SPIRV Translator
327 +// This file is distributed under the University of Illinois Open Source
328 +// License. See LICENSE.TXT for details.
330 +// Copyright (c) 2018 Intel Corporation. All rights reserved.
332 +// Permission is hereby granted, free of charge, to any person obtaining a
333 +// copy of this software and associated documentation files (the "Software"),
334 +// to deal with the Software without restriction, including without limitation
335 +// the rights to use, copy, modify, merge, publish, distribute, sublicense,
336 +// and/or sell copies of the Software, and to permit persons to whom the
337 +// Software is furnished to do so, subject to the following conditions:
339 +// Redistributions of source code must retain the above copyright notice,
340 +// this list of conditions and the following disclaimers.
341 +// Redistributions in binary form must reproduce the above copyright notice,
342 +// this list of conditions and the following disclaimers in the documentation
343 +// and/or other materials provided with the distribution.
344 +// Neither the names of Intel Corporation, nor the names of its
345 +// contributors may be used to endorse or promote products derived from this
346 +// Software without specific prior written permission.
347 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
348 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
349 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
350 +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
351 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
352 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
355 +//===----------------------------------------------------------------------===//
357 +// SPIR-V specification doesn't allow function pointers, so SPIR-V translator
358 +// is designed to fail if a value with function type (except calls) is occured.
359 +// Currently there is only two cases, when function pointers are generating in
360 +// LLVM IR in OpenCL - block calls and device side enqueue built-in calls.
362 +// In both cases values with function type used as intermediate representation
363 +// for block literal structure.
365 +// In LLVM IR produced by clang, blocks are represented with the following
367 +// %struct.__opencl_block_literal_generic = type { i32, i32, i8 addrspace(4)* }
368 +// Pointers to block invoke functions are stored in the third field. Clang
369 +// replaces inderect function calls in all cases except if block is passed as a
370 +// function argument. Note that it is somewhat unclear if the OpenCL C spec
371 +// should allow passing blocks as function argumernts. This pass is not supposed
372 +// to work correctly with such functions.
373 +// Clang though has to store function pointers to this structure. Purpose of
374 +// this pass is to replace store of function pointers(not allowed in SPIR-V)
375 +// with null pointers.
377 +//===----------------------------------------------------------------------===//
378 +#define DEBUG_TYPE "spv-lower-ocl-blocks"
380 +#include "SPIRVInternal.h"
382 +#include "llvm/IR/Module.h"
383 +#include "llvm/Pass.h"
384 +#include "llvm/Support/Regex.h"
386 +using namespace llvm;
390 +static bool isBlockInvoke(Function &F) {
391 + static Regex BlockInvokeRegex("_block_invoke_?[0-9]*$");
392 + return BlockInvokeRegex.match(F.getName());
395 +class SPIRVLowerOCLBlocks : public ModulePass {
398 + SPIRVLowerOCLBlocks() : ModulePass(ID) {}
400 + bool runOnModule(Module &M) {
401 + bool Changed = false;
402 + for (Function &F : M) {
403 + if (!isBlockInvoke(F))
405 + for (User *U : F.users()) {
406 + if (!isa<Constant>(U))
408 + Constant *Null = Constant::getNullValue(U->getType());
410 + U->replaceAllUsesWith(Null);
421 +char SPIRVLowerOCLBlocks::ID = 0;
426 + SPIRVLowerOCLBlocks, "spv-lower-ocl-blocks",
427 + "Remove function pointers occured in case of using OpenCL blocks", false,
430 +llvm::ModulePass *llvm::createSPIRVLowerOCLBlocks() {
431 + return new SPIRVLowerOCLBlocks();