+++ /dev/null
-From 9ce0fe02fd6cda5fb29fbb0d5037a1798a810b8a Mon Sep 17 00:00:00 2001
-From: Alexey Sotkin <alexey.sotkin@intel.com>
-Date: Thu, 21 Feb 2019 17:14:36 +0300
-Subject: [PATCH 1/3] Update LowerOpenCL pass to handle new blocks
- represntation in LLVM IR
-
----
- lib/SPIRV/SPIRVLowerOCLBlocks.cpp | 413 ++++++++----------------------
- test/global_block.ll | 71 ++---
- test/literal-struct.ll | 31 ++-
- test/transcoding/block_w_struct_return.ll | 47 ++--
- test/transcoding/enqueue_kernel.ll | 237 ++++++++++-------
- 5 files changed, 317 insertions(+), 482 deletions(-)
-
-diff --git a/lib/SPIRV/SPIRVLowerOCLBlocks.cpp b/lib/SPIRV/SPIRVLowerOCLBlocks.cpp
-index 50e1838..b42a4ec 100644
---- a/lib/SPIRV/SPIRVLowerOCLBlocks.cpp
-+++ b/lib/SPIRV/SPIRVLowerOCLBlocks.cpp
-@@ -1,303 +1,110 @@
--//===- SPIRVLowerOCLBlocks.cpp - OCL Utilities ----------------------------===//\r
--//\r
--// The LLVM/SPIRV Translator\r
--//\r
--// This file is distributed under the University of Illinois Open Source\r
--// License. See LICENSE.TXT for details.\r
--//\r
--// Copyright (c) 2018 Intel Corporation. All rights reserved.\r
--//\r
--// Permission is hereby granted, free of charge, to any person obtaining a\r
--// copy of this software and associated documentation files (the "Software"),\r
--// to deal with the Software without restriction, including without limitation\r
--// the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
--// and/or sell copies of the Software, and to permit persons to whom the\r
--// Software is furnished to do so, subject to the following conditions:\r
--//\r
--// Redistributions of source code must retain the above copyright notice,\r
--// this list of conditions and the following disclaimers.\r
--// Redistributions in binary form must reproduce the above copyright notice,\r
--// this list of conditions and the following disclaimers in the documentation\r
--// and/or other materials provided with the distribution.\r
--// Neither the names of Intel Corporation, nor the names of its\r
--// contributors may be used to endorse or promote products derived from this\r
--// Software without specific prior written permission.\r
--// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
--// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
--// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
--// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
--// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
--// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH\r
--// THE SOFTWARE.\r
--//\r
--//===----------------------------------------------------------------------===//\r
--//\r
--// SPIR-V specification doesn't allow function pointers, so SPIR-V translator\r
--// is designed to fail if a value with function type (except calls) is occured.\r
--// Currently there is only two cases, when function pointers are generating in\r
--// LLVM IR in OpenCL - block calls and device side enqueue built-in calls.\r
--//\r
--// In both cases values with function type used as intermediate representation\r
--// for block literal structure.\r
--//\r
--// This pass is designed to find such cases and simplify them to avoid any\r
--// function pointer types occurrences in LLVM IR in 4 steps.\r
--//\r
--// 1. Find all function pointer allocas, like\r
--// %block = alloca void () *\r
--//\r
--// Then find a single store to that alloca:\r
--// %blockLit = alloca <{ i32, i32, ...}>, align 4\r
--// %0 = bitcast <{ i32, i32, ... }>* %blockLit to void ()*\r
--// > store void ()* %0, void ()** %block, align 4\r
--//\r
--// And replace the alloca users by new instructions which used stored value\r
--// %blockLit itself instead of function pointer alloca %block.\r
--//\r
--// 2. Find consecutive casts from block literal type to i8 addrspace(4)*\r
--// used function pointers as an intermediate type:\r
--// %0 = bitcast <{ i32, i32 }> %block to void() *\r
--// %1 = addrspacecast void() * %0 to i8 addrspace(4)*\r
--// And simplify them:\r
--// %2 = addrspacecast <{ i32, i32 }> %block to i8 addrspace(4)*\r
--//\r
--// 3. Find all unused instructions with function pointer type occured after\r
--// pp.1-2 and remove them.\r
--//\r
--// 4. Find unused globals with function pointer type, like\r
--// @block = constant void ()*\r
--// bitcast ({ i32, i32 }* @__block_literal_global to void ()*\r
--//\r
--// And remove them.\r
--//\r
--//===----------------------------------------------------------------------===//\r
--#define DEBUG_TYPE "spv-lower-ocl-blocks"\r
--\r
--#include "OCLUtil.h"\r
--#include "SPIRVInternal.h"\r
--\r
--#include "llvm/ADT/SetVector.h"\r
--#include "llvm/Analysis/ValueTracking.h"\r
--#include "llvm/IR/GlobalVariable.h"\r
--#include "llvm/IR/InstIterator.h"\r
--#include "llvm/IR/Module.h"\r
--#include "llvm/Pass.h"\r
--#include "llvm/PassSupport.h"\r
--#include "llvm/Support/Casting.h"\r
--\r
--using namespace llvm;\r
--\r
--namespace {\r
--\r
--static void\r
--removeUnusedFunctionPtrInst(Instruction *I,\r
-- SmallSetVector<Instruction *, 16> &FuncPtrInsts) {\r
-- for (unsigned OpIdx = 0, Ops = I->getNumOperands(); OpIdx != Ops; ++OpIdx) {\r
-- Instruction *OpI = dyn_cast<Instruction>(I->getOperand(OpIdx));\r
-- I->setOperand(OpIdx, nullptr);\r
-- if (OpI && OpI != I && OpI->user_empty())\r
-- FuncPtrInsts.insert(OpI);\r
-- }\r
-- I->eraseFromParent();\r
--}\r
--\r
--static bool isFuncPtrAlloca(const AllocaInst *AI) {\r
-- auto *ET = dyn_cast<PointerType>(AI->getAllocatedType());\r
-- return ET && ET->getElementType()->isFunctionTy();\r
--}\r
--\r
--static bool hasFuncPtrType(const Value *V) {\r
-- auto *PT = dyn_cast<PointerType>(V->getType());\r
-- return PT && PT->getElementType()->isFunctionTy();\r
--}\r
--\r
--static bool isFuncPtrInst(const Instruction *I) {\r
-- if (auto *AI = dyn_cast<AllocaInst>(I))\r
-- return isFuncPtrAlloca(AI);\r
--\r
-- for (auto &Op : I->operands()) {\r
-- if (auto *AI = dyn_cast<AllocaInst>(Op))\r
-- return isFuncPtrAlloca(AI);\r
--\r
-- auto *OpI = dyn_cast<Instruction>(&Op);\r
-- if (OpI && OpI != I && hasFuncPtrType(OpI))\r
-- return true;\r
-- }\r
-- return false;\r
--}\r
--\r
--static StoreInst *findSingleStore(AllocaInst *AI) {\r
-- StoreInst *Store = nullptr;\r
-- for (auto *U : AI->users()) {\r
-- if (!isa<StoreInst>(U))\r
-- continue; // not a store\r
-- if (Store)\r
-- return nullptr; // there are more than one stores\r
-- Store = dyn_cast<StoreInst>(U);\r
-- }\r
-- return Store;\r
--}\r
--\r
--static void fixFunctionPtrAllocaUsers(AllocaInst *AI) {\r
-- // Find and remove a single store to alloca\r
-- auto *SingleStore = findSingleStore(AI);\r
-- assert(SingleStore && "More than one store to the function pointer alloca");\r
-- auto *StoredVal = SingleStore->getValueOperand();\r
-- SingleStore->eraseFromParent();\r
--\r
-- // Find loads from the alloca and replace thier users\r
-- for (auto *U : AI->users()) {\r
-- auto *LI = dyn_cast<LoadInst>(U);\r
-- if (!LI)\r
-- continue;\r
--\r
-- for (auto *U : LI->users()) {\r
-- auto *UInst = cast<Instruction>(U);\r
-- auto *Cast = CastInst::CreatePointerBitCastOrAddrSpaceCast(\r
-- StoredVal, UInst->getType(), "", UInst);\r
-- UInst->replaceAllUsesWith(Cast);\r
-- }\r
-- }\r
--}\r
--\r
--static int getBlockLiteralIdx(const Function &F) {\r
-- StringRef FName = F.getName();\r
-- if (isEnqueueKernelBI(FName))\r
-- return FName.contains("events") ? 7 : 4;\r
-- if (isKernelQueryBI(FName))\r
-- return FName.contains("for_ndrange") ? 2 : 1;\r
-- if (FName.startswith("__") && FName.contains("_block_invoke"))\r
-- return F.hasStructRetAttr() ? 1 : 0;\r
--\r
-- return -1; // No block literal argument\r
--}\r
--\r
--static bool hasBlockLiteralArg(const Function &F) {\r
-- return getBlockLiteralIdx(F) != -1;\r
--}\r
--\r
--static bool simplifyFunctionPtrCasts(Function &F) {\r
-- bool Changed = false;\r
-- int BlockLiteralIdx = getBlockLiteralIdx(F);\r
-- for (auto *U : F.users()) {\r
-- auto *Call = dyn_cast<CallInst>(U);\r
-- if (!Call)\r
-- continue;\r
-- if (Call->getFunction()->getName() == F.getName().str() + "_kernel")\r
-- continue; // Skip block invoke function calls inside block invoke kernels\r
--\r
-- const DataLayout &DL = F.getParent()->getDataLayout();\r
-- auto *BlockLiteral = Call->getOperand(BlockLiteralIdx);\r
-- auto *BlockLiteralVal = GetUnderlyingObject(BlockLiteral, DL);\r
-- if (isa<GlobalVariable>(BlockLiteralVal))\r
-- continue; // nothing to do with globals\r
--\r
-- auto *BlockLiteralAlloca = cast<AllocaInst>(BlockLiteralVal);\r
-- assert(!BlockLiteralAlloca->getAllocatedType()->isFunctionTy() &&\r
-- "Function type shouldn't be there");\r
--\r
-- auto *NewBlockLiteral = CastInst::CreatePointerBitCastOrAddrSpaceCast(\r
-- BlockLiteralAlloca, BlockLiteral->getType(), "", Call);\r
-- BlockLiteral->replaceAllUsesWith(NewBlockLiteral);\r
-- Changed |= true;\r
-- }\r
-- return Changed;\r
--}\r
--\r
--static void\r
--findFunctionPtrAllocas(Module &M,\r
-- SmallVectorImpl<AllocaInst *> &FuncPtrAllocas) {\r
-- for (auto &F : M) {\r
-- if (F.isDeclaration())\r
-- continue;\r
-- for (auto &I : instructions(F)) {\r
-- auto *AI = dyn_cast<AllocaInst>(&I);\r
-- if (!AI || !isFuncPtrAlloca(AI))\r
-- continue;\r
-- FuncPtrAllocas.push_back(AI);\r
-- }\r
-- }\r
--}\r
--\r
--static void\r
--findUnusedFunctionPtrInsts(Module &M,\r
-- SmallSetVector<Instruction *, 16> &FuncPtrInsts) {\r
-- for (auto &F : M) {\r
-- if (F.isDeclaration())\r
-- continue;\r
-- for (auto &I : instructions(F))\r
-- if (I.user_empty() && isFuncPtrInst(&I))\r
-- FuncPtrInsts.insert(&I);\r
-- }\r
--}\r
--\r
--static void\r
--findUnusedFunctionPtrGlbs(Module &M,\r
-- SmallVectorImpl<GlobalVariable *> &FuncPtrGlbs) {\r
-- for (auto &GV : M.globals()) {\r
-- if (!GV.user_empty())\r
-- continue;\r
-- auto *GVType = dyn_cast<PointerType>(GV.getType()->getElementType());\r
-- if (GVType && GVType->getElementType()->isFunctionTy())\r
-- FuncPtrGlbs.push_back(&GV);\r
-- }\r
--}\r
--\r
--class SPIRVLowerOCLBlocks : public ModulePass {\r
--\r
--public:\r
-- SPIRVLowerOCLBlocks() : ModulePass(ID) {}\r
--\r
-- bool runOnModule(Module &M) {\r
-- bool Changed = false;\r
--\r
-- // 1. Find function pointer allocas and fix their users\r
-- SmallVector<AllocaInst *, 16> FuncPtrAllocas;\r
-- findFunctionPtrAllocas(M, FuncPtrAllocas);\r
--\r
-- Changed |= !FuncPtrAllocas.empty();\r
-- for (auto *AI : FuncPtrAllocas)\r
-- fixFunctionPtrAllocaUsers(AI);\r
--\r
-- // 2. Simplify consecutive casts which use function pointer types\r
-- for (auto &F : M)\r
-- if (hasBlockLiteralArg(F))\r
-- Changed |= simplifyFunctionPtrCasts(F);\r
--\r
-- // 3. Cleanup unused instructions with function pointer type\r
-- // which are occured after pp. 1-2\r
-- SmallSetVector<Instruction *, 16> FuncPtrInsts;\r
-- findUnusedFunctionPtrInsts(M, FuncPtrInsts);\r
--\r
-- Changed |= !FuncPtrInsts.empty();\r
-- while (!FuncPtrInsts.empty()) {\r
-- Instruction *I = FuncPtrInsts.pop_back_val();\r
-- removeUnusedFunctionPtrInst(I, FuncPtrInsts);\r
-- }\r
--\r
-- // 4. Find and remove unused global variables with function pointer type\r
-- SmallVector<GlobalVariable *, 16> FuncPtrGlbs;\r
-- findUnusedFunctionPtrGlbs(M, FuncPtrGlbs);\r
--\r
-- Changed |= !FuncPtrGlbs.empty();\r
-- for (auto *GV : FuncPtrGlbs)\r
-- GV->eraseFromParent();\r
--\r
-- return Changed;\r
-- }\r
--\r
-- static char ID;\r
--}; // class SPIRVLowerOCLBlocks\r
--\r
--char SPIRVLowerOCLBlocks::ID = 0;\r
--\r
--} // namespace\r
--\r
--INITIALIZE_PASS(\r
-- SPIRVLowerOCLBlocks, "spv-lower-ocl-blocks",\r
-- "Remove function pointers occured in case of using OpenCL blocks", false,\r
-- false)\r
--\r
--llvm::ModulePass *llvm::createSPIRVLowerOCLBlocks() {\r
-- return new SPIRVLowerOCLBlocks();\r
--}\r
-+//===- SPIRVLowerOCLBlocks.cpp - OCL Utilities ----------------------------===//
-+//
-+// The LLVM/SPIRV Translator
-+//
-+// This file is distributed under the University of Illinois Open Source
-+// License. See LICENSE.TXT for details.
-+//
-+// Copyright (c) 2018 Intel Corporation. All rights reserved.
-+//
-+// Permission is hereby granted, free of charge, to any person obtaining a
-+// copy of this software and associated documentation files (the "Software"),
-+// to deal with the Software without restriction, including without limitation
-+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+// and/or sell copies of the Software, and to permit persons to whom the
-+// Software is furnished to do so, subject to the following conditions:
-+//
-+// Redistributions of source code must retain the above copyright notice,
-+// this list of conditions and the following disclaimers.
-+// Redistributions in binary form must reproduce the above copyright notice,
-+// this list of conditions and the following disclaimers in the documentation
-+// and/or other materials provided with the distribution.
-+// Neither the names of Intel Corporation, nor the names of its
-+// contributors may be used to endorse or promote products derived from this
-+// Software without specific prior written permission.
-+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-+// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
-+// THE SOFTWARE.
-+//
-+//===----------------------------------------------------------------------===//
-+//
-+// SPIR-V specification doesn't allow function pointers, so SPIR-V translator
-+// is designed to fail if a value with function type (except calls) is occured.
-+// Currently there is only two cases, when function pointers are generating in
-+// LLVM IR in OpenCL - block calls and device side enqueue built-in calls.
-+//
-+// In both cases values with function type used as intermediate representation
-+// for block literal structure.
-+//
-+// In LLVM IR produced by clang, blocks are represented with the following
-+// structure:
-+// %struct.__opencl_block_literal_generic = type { i32, i32, i8 addrspace(4)* }
-+// Pointers to block invoke functions are stored in the third field. Clang
-+// replaces inderect function calls in all cases except if block is passed as a
-+// function argument. Note that it is somewhat unclear if the OpenCL C spec
-+// should allow passing blocks as function argumernts. This pass is not supposed
-+// to work correctly with such functions.
-+// Clang though has to store function pointers to this structure. Purpose of
-+// this pass is to replace store of function pointers(not allowed in SPIR-V)
-+// with null pointers.
-+//
-+//===----------------------------------------------------------------------===//
-+#define DEBUG_TYPE "spv-lower-ocl-blocks"
-+
-+#include "SPIRVInternal.h"
-+
-+#include "llvm/IR/Module.h"
-+#include "llvm/Pass.h"
-+#include "llvm/Support/Regex.h"
-+
-+using namespace llvm;
-+
-+namespace {
-+
-+static bool isBlockInvoke(Function &F) {
-+ static Regex BlockInvokeRegex("_block_invoke_?[0-9]*$");
-+ return BlockInvokeRegex.match(F.getName());
-+}
-+
-+class SPIRVLowerOCLBlocks : public ModulePass {
-+
-+public:
-+ SPIRVLowerOCLBlocks() : ModulePass(ID) {}
-+
-+ bool runOnModule(Module &M) {
-+ bool Changed = false;
-+ for (Function &F : M) {
-+ if (!isBlockInvoke(F))
-+ continue;
-+ for (User *U : F.users()) {
-+ if (!isa<Constant>(U))
-+ continue;
-+ Constant *Null = Constant::getNullValue(U->getType());
-+ if (U != Null) {
-+ U->replaceAllUsesWith(Null);
-+ Changed = true;
-+ }
-+ }
-+ }
-+ return Changed;
-+ }
-+
-+ static char ID;
-+};
-+
-+char SPIRVLowerOCLBlocks::ID = 0;
-+
-+} // namespace
-+
-+INITIALIZE_PASS(
-+ SPIRVLowerOCLBlocks, "spv-lower-ocl-blocks",
-+ "Remove function pointers occured in case of using OpenCL blocks", false,
-+ false)
-+
-+llvm::ModulePass *llvm::createSPIRVLowerOCLBlocks() {
-+ return new SPIRVLowerOCLBlocks();
-+}
-