diff --git a/OCL20ToSPIRV.cpp b/OCL20ToSPIRV.cpp index 1a052e4..8606d60 100644 --- a/OCL20ToSPIRV.cpp +++ b/OCL20ToSPIRV.cpp @@ -1,1457 +1,1457 @@ -//===- OCL20ToSPIRV.cpp - Transform OCL20 to SPIR-V builtins -----*- C++ -*-===// -// -// The LLVM/SPIRV Translator -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. -// -//===----------------------------------------------------------------------===// -// -// This file implements translation of OCL20 builtin functions. -// -//===----------------------------------------------------------------------===// -#define DEBUG_TYPE "cl20tospv" - -#include "SPIRVInternal.h" -#include "OCLUtil.h" -#include "OCLTypeToSPIRV.h" - -#include "llvm/ADT/StringSwitch.h" -#include "llvm/IR/InstVisitor.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Pass.h" -#include "llvm/PassSupport.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" - -#include - -using namespace llvm; -using namespace SPIRV; -using namespace OCLUtil; - -namespace SPIRV { -static size_t -getOCLCpp11AtomicMaxNumOps(StringRef Name) { - return StringSwitch(Name) - .Cases("load", "flag_test_and_set", "flag_clear", 3) - .Cases("store", "exchange", 4) - .StartsWith("compare_exchange", 6) - .StartsWith("fetch", 4) - .Default(0); -} - -class OCL20ToSPIRV: public ModulePass, - public InstVisitor { -public: - OCL20ToSPIRV():ModulePass(ID), M(nullptr), Ctx(nullptr), CLVer(0) { - initializeOCL20ToSPIRVPass(*PassRegistry::getPassRegistry()); - } - virtual bool runOnModule(Module &M); - - void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - } - - virtual void visitCallInst(CallInst &CI); - - /// Transform barrier/work_group_barrier to __spirv_ControlBarrier. - /// barrier(flag) => - /// __spirv_ControlBarrier(workgroup, workgroup, map(flag)) - /// workgroup_barrier(scope, flag) => - /// __spirv_ControlBarrier(workgroup, map(scope), map(flag)) - void visitCallWorkGroupBarrier(CallInst *CI); - - /// Erase useless convert functions. - /// \return true if the call instruction is erased. - bool eraseUselessConvert(CallInst *Call, const std::string &MangledName, - const std::string &DeMangledName); - - /// Transform convert_ to - /// __spirv_{CastOpName}_R{TargeTyName}{_sat}{_rt[p|n|z|e]} - void visitCallConvert(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform async_work_group{_strided}_copy. - /// async_work_group_copy(dst, src, n, event) - /// => async_work_group_strided_copy(dst, src, n, 1, event) - /// async_work_group_strided_copy(dst, src, n, stride, event) - /// => __spirv_AsyncGroupCopy(ScopeWorkGroup, dst, src, n, stride, event) - void visitCallAsyncWorkGroupCopy(CallInst *CI, - const std::string &DemangledName); - - /// Transform OCL builtin function to SPIR-V builtin function. - void transBuiltin(CallInst *CI, OCLBuiltinTransInfo &Info); - - /// Transform OCL work item builtin functions to SPIR-V builtin variables. - void transWorkItemBuiltinsToVariables(); - - /// Transform atomic_work_item_fence/mem_fence to __spirv_MemoryBarrier. - /// func(flag, order, scope) => - /// __spirv_MemoryBarrier(map(scope), map(flag)|map(order)) - void transMemoryBarrier(CallInst *CI, AtomicWorkItemFenceLiterals); - - /// Transform all to __spirv_Op(All|Any). Note that the types mismatch so - // some extra code is emitted to convert between the two. - void visitCallAllAny(spv::Op OC, CallInst *CI); - - /// Transform atomic_* to __spirv_Atomic*. - /// atomic_x(ptr_arg, args, order, scope) => - /// __spirv_AtomicY(ptr_arg, map(order), map(scope), args) - void transAtomicBuiltin(CallInst *CI, OCLBuiltinTransInfo &Info); - - /// Transform atomic_work_item_fence to __spirv_MemoryBarrier. - /// atomic_work_item_fence(flag, order, scope) => - /// __spirv_MemoryBarrier(map(scope), map(flag)|map(order)) - void visitCallAtomicWorkItemFence(CallInst *CI); - - /// Transform atomic_compare_exchange call. - /// In atomic_compare_exchange, the expected value parameter is a pointer. - /// However in SPIR-V it is a value. The transformation adds a load - /// instruction, result of which is passed to atomic_compare_exchange as - /// argument. - /// The transformation adds a store instruction after the call, to update the - /// value in expected with the value pointed to by object. Though, it is not - /// necessary in case they are equal, this approach makes result code simpler. - /// Also ICmp instruction is added, because the call must return result of - /// comparison. - /// \returns the call instruction of atomic_compare_exchange_strong. - CallInst *visitCallAtomicCmpXchg(CallInst *CI, - const std::string &DemangledName); - - /// Transform atomic_init. - /// atomic_init(p, x) => store p, x - void visitCallAtomicInit(CallInst *CI); - - /// Transform legacy OCL 1.x atomic builtins to SPIR-V builtins for extensions - /// cl_khr_int64_base_atomics - /// cl_khr_int64_extended_atomics - /// Do nothing if the called function is not a legacy atomic builtin. - void visitCallAtomicLegacy(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform OCL 2.0 C++11 atomic builtins to SPIR-V builtins. - /// Do nothing if the called function is not a C++11 atomic builtin. - void visitCallAtomicCpp11(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform OCL builtin function to SPIR-V builtin function. - /// Assuming there is a simple name mapping without argument changes. - /// Should be called at last. - void visitCallBuiltinSimple(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform get_image_{width|height|depth|dim}. - /// get_image_xxx(...) => - /// dimension = __spirv_ImageQuerySizeLod_R{ReturnType}(...); - /// return dimension.{x|y|z}; - void visitCallGetImageSize(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform {work|sub}_group_x => - /// __spirv_{OpName} - /// - /// Special handling of work_group_broadcast. - /// work_group_broadcast(a, x, y, z) - /// => - /// __spirv_GroupBroadcast(a, vec3(x, y, z)) - - void visitCallGroupBuiltin(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform mem_fence to __spirv_MemoryBarrier. - /// mem_fence(flag) => __spirv_MemoryBarrier(Workgroup, map(flag)) - void visitCallMemFence(CallInst *CI); - - void visitCallNDRange(CallInst *CI, const std::string &DemangledName); - - /// Transform OCL pipe builtin function to SPIR-V pipe builtin function. - void visitCallPipeBuiltin(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform read_image with sampler arguments. - /// read_image(image, sampler, ...) => - /// sampled_image = __spirv_SampledImage(image, sampler); - /// return __spirv_ImageSampleExplicitLod_R{ReturnType}(sampled_image, ...); - void visitCallReadImageWithSampler(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform read_image with msaa image arguments. - /// Sample argument must be acoded as Image Operand. - void visitCallReadImageMSAA(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform {read|write}_image without sampler arguments. - void visitCallReadWriteImage(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform to_{global|local|private}. - /// - /// T* a = ...; - /// addr T* b = to_addr(a); - /// => - /// i8* x = cast(a); - /// addr i8* y = __spirv_GenericCastToPtr_ToAddr(x); - /// addr T* b = cast(y); - void visitCallToAddr(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform return type of relatinal built-in functions like isnan, isfinite - /// to boolean values. - void visitCallRelational(CallInst *CI, const std::string &DemangledName); - - /// Transform vector load/store functions to SPIR-V extended builtin - /// functions - /// {vload|vstore{a}}{_half}{n}{_rte|_rtz|_rtp|_rtn} => - /// __spirv_ocl_{ExtendedInstructionOpCodeName}__R{ReturnType} - void visitCallVecLoadStore(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transforms get_mem_fence built-in to SPIR-V function and aligns result values with SPIR 1.2. - /// get_mem_fence(ptr) => __spirv_GenericPtrMemSemantics - /// GenericPtrMemSemantics valid values are 0x100, 0x200 and 0x300, where is - /// SPIR 1.2 defines them as 0x1, 0x2 and 0x3, so this function adjusts - /// GenericPtrMemSemantics results to SPIR 1.2 values. - void visitCallGetFence(CallInst *CI, StringRef MangledName, const std::string& DemangledName); - - /// Transforms OpDot instructions with a scalar type to a fmul instruction - void visitCallDot(CallInst *CI); - - /// Fixes for built-in functions with vector+scalar arguments that are - /// translated to the SPIR-V instructions where all arguments must have the - /// same type. - void visitCallScalToVec(CallInst *CI, StringRef MangledName, - const std::string &DemangledName); - - /// Transform get_image_channel_{order|data_type} built-in functions to - /// __spirv_ocl_{ImageQueryOrder|ImageQueryFormat} - void visitCallGetImageChannel(CallInst *CI, StringRef MangledName, - const std::string &DemangledName, - unsigned int Offset); - - void visitDbgInfoIntrinsic(DbgInfoIntrinsic &I){ - I.dropAllReferences(); - I.eraseFromParent(); - } - static char ID; -private: - Module *M; - LLVMContext *Ctx; - unsigned CLVer; /// OpenCL version as major*10+minor - std::set ValuesToDelete; - - ConstantInt *addInt32(int I) { - return getInt32(M, I); - } - ConstantInt *addSizet(uint64_t I) { - return getSizet(M, I); - } - - /// Get vector width from OpenCL vload* function name. - SPIRVWord getVecLoadWidth(const std::string& DemangledName) { - SPIRVWord Width = 0; - if (DemangledName == "vloada_half") - Width = 1; - else { - unsigned Loc = 5; - if (DemangledName.find("vload_half") == 0) - Loc = 10; - else if (DemangledName.find("vloada_half") == 0) - Loc = 11; - - std::stringstream SS(DemangledName.substr(Loc)); - SS >> Width; - } - return Width; - } - - /// Transform OpenCL vload/vstore function name. - void transVecLoadStoreName(std::string& DemangledName, - const std::string &Stem, bool AlwaysN) { - auto HalfStem = Stem + "_half"; - auto HalfStemR = HalfStem + "_r"; - if (!AlwaysN && DemangledName == HalfStem) - return; - if (!AlwaysN && DemangledName.find(HalfStemR) == 0) { - DemangledName = HalfStemR; - return; - } - if (DemangledName.find(HalfStem) == 0) { - auto OldName = DemangledName; - DemangledName = HalfStem + "n"; - if (OldName.find("_r") != std::string::npos) - DemangledName += "_r"; - return; - } - if (DemangledName.find(Stem) == 0) { - DemangledName = Stem + "n"; - return; - } - } - -}; - -char OCL20ToSPIRV::ID = 0; - -bool -OCL20ToSPIRV::runOnModule(Module& Module) { - M = &Module; - Ctx = &M->getContext(); - auto Src = getSPIRVSource(&Module); - if (std::get<0>(Src) != spv::SourceLanguageOpenCL_C) - return false; - - CLVer = std::get<1>(Src); - if (CLVer > kOCLVer::CL20) - return false; - - DEBUG(dbgs() << "Enter OCL20ToSPIRV:\n"); - - transWorkItemBuiltinsToVariables(); - - visit(*M); - - for (auto &I:ValuesToDelete) - if (auto Inst = dyn_cast(I)) - Inst->eraseFromParent(); - for (auto &I:ValuesToDelete) - if (auto GV = dyn_cast(I)) - GV->eraseFromParent(); - - DEBUG(dbgs() << "After OCL20ToSPIRV:\n" << *M); - - std::string Err; - raw_string_ostream ErrorOS(Err); - if (verifyModule(*M, &ErrorOS)){ - DEBUG(errs() << "Fails to verify module: " << ErrorOS.str()); - } - return true; -} - -// The order of handling OCL builtin functions is important. -// Workgroup functions need to be handled before pipe functions since -// there are functions fall into both categories. -void -OCL20ToSPIRV::visitCallInst(CallInst& CI) { - DEBUG(dbgs() << "[visistCallInst] " << CI << '\n'); - auto F = CI.getCalledFunction(); - if (!F) - return; - - auto MangledName = F->getName(); - std::string DemangledName; - if (!oclIsBuiltin(MangledName, &DemangledName)) - return; - DEBUG(dbgs() << "DemangledName: " << DemangledName << '\n'); - if (DemangledName.find(kOCLBuiltinName::NDRangePrefix) == 0) { - visitCallNDRange(&CI, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::All) { - visitCallAllAny(OpAll, &CI); - return; - } - if (DemangledName == kOCLBuiltinName::Any) { - visitCallAllAny(OpAny, &CI); - return; - } - if (DemangledName.find(kOCLBuiltinName::AsyncWorkGroupCopy) == 0 || - DemangledName.find(kOCLBuiltinName::AsyncWorkGroupStridedCopy) == 0) { - visitCallAsyncWorkGroupCopy(&CI, DemangledName); - return; - } - if (DemangledName.find(kOCLBuiltinName::AtomicPrefix) == 0 || - DemangledName.find(kOCLBuiltinName::AtomPrefix) == 0) { - auto PCI = &CI; - if (DemangledName == kOCLBuiltinName::AtomicInit) { - visitCallAtomicInit(PCI); - return; - } - if (DemangledName == kOCLBuiltinName::AtomicWorkItemFence) { - visitCallAtomicWorkItemFence(PCI); - return; - } - if (DemangledName == kOCLBuiltinName::AtomicCmpXchgWeak || - DemangledName == kOCLBuiltinName::AtomicCmpXchgStrong || - DemangledName == kOCLBuiltinName::AtomicCmpXchgWeakExplicit || - DemangledName == kOCLBuiltinName::AtomicCmpXchgStrongExplicit) { - assert(CLVer == kOCLVer::CL20 && "Wrong version of OpenCL"); - PCI = visitCallAtomicCmpXchg(PCI, DemangledName); - } - visitCallAtomicLegacy(PCI, MangledName, DemangledName); - visitCallAtomicCpp11(PCI, MangledName, DemangledName); - return; - } - if (DemangledName.find(kOCLBuiltinName::ConvertPrefix) == 0) { - visitCallConvert(&CI, MangledName, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::GetImageWidth || - DemangledName == kOCLBuiltinName::GetImageHeight || - DemangledName == kOCLBuiltinName::GetImageDepth || - DemangledName == kOCLBuiltinName::GetImageDim || - DemangledName == kOCLBuiltinName::GetImageArraySize) { - visitCallGetImageSize(&CI, MangledName, DemangledName); - return; - } - if ((DemangledName.find(kOCLBuiltinName::WorkGroupPrefix) == 0 && - DemangledName != kOCLBuiltinName::WorkGroupBarrier) || - DemangledName == kOCLBuiltinName::WaitGroupEvent || - DemangledName.find(kOCLBuiltinName::SubGroupPrefix) == 0) { - visitCallGroupBuiltin(&CI, MangledName, DemangledName); - return; - } - if (DemangledName.find(kOCLBuiltinName::Pipe) != std::string::npos) { - visitCallPipeBuiltin(&CI, MangledName, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::MemFence) { - visitCallMemFence(&CI); - return; - } - if (DemangledName.find(kOCLBuiltinName::ReadImage) == 0) { - if (MangledName.find(kMangledName::Sampler) != StringRef::npos) { - visitCallReadImageWithSampler(&CI, MangledName, DemangledName); - return; - } - if (MangledName.find("msaa") != StringRef::npos) { - visitCallReadImageMSAA(&CI, MangledName, DemangledName); - return; - } - } - if (DemangledName.find(kOCLBuiltinName::ReadImage) == 0 || - DemangledName.find(kOCLBuiltinName::WriteImage) == 0) { - visitCallReadWriteImage(&CI, MangledName, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::ToGlobal || - DemangledName == kOCLBuiltinName::ToLocal || - DemangledName == kOCLBuiltinName::ToPrivate) { - visitCallToAddr(&CI, MangledName, DemangledName); - return; - } - if (DemangledName.find(kOCLBuiltinName::VLoadPrefix) == 0 || - DemangledName.find(kOCLBuiltinName::VStorePrefix) == 0) { - visitCallVecLoadStore(&CI, MangledName, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::IsFinite || - DemangledName == kOCLBuiltinName::IsInf || - DemangledName == kOCLBuiltinName::IsNan || - DemangledName == kOCLBuiltinName::IsNormal || - DemangledName == kOCLBuiltinName::Signbit) { - visitCallRelational(&CI, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::WorkGroupBarrier || - DemangledName == kOCLBuiltinName::Barrier) { - visitCallWorkGroupBarrier(&CI); - return; - } - if (DemangledName == kOCLBuiltinName::GetFence) { - visitCallGetFence(&CI, MangledName, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::Dot && - !(CI.getOperand(0)->getType()->isVectorTy())) { - visitCallDot(&CI); - return; - } - if (DemangledName == kOCLBuiltinName::FMin || - DemangledName == kOCLBuiltinName::FMax || - DemangledName == kOCLBuiltinName::Min || - DemangledName == kOCLBuiltinName::Max || - DemangledName == kOCLBuiltinName::Step || - DemangledName == kOCLBuiltinName::SmoothStep || - DemangledName == kOCLBuiltinName::Clamp || - DemangledName == kOCLBuiltinName::Mix) { - visitCallScalToVec(&CI, MangledName, DemangledName); - return; - } - if (DemangledName == kOCLBuiltinName::GetImageChannelDataType) { - visitCallGetImageChannel(&CI, MangledName, DemangledName, - OCLImageChannelDataTypeOffset); - return; - } - if (DemangledName == kOCLBuiltinName::GetImageChannelOrder) { - visitCallGetImageChannel(&CI, MangledName, DemangledName, - OCLImageChannelOrderOffset); - return; - } - visitCallBuiltinSimple(&CI, MangledName, DemangledName); -} - -void -OCL20ToSPIRV::visitCallNDRange(CallInst *CI, - const std::string &DemangledName) { - assert(DemangledName.find(kOCLBuiltinName::NDRangePrefix) == 0); - std::string lenStr = DemangledName.substr(8, 1); - auto Len = atoi(lenStr.c_str()); - assert (Len >= 1 && Len <= 3); - // SPIR-V ndrange structure requires 3 members in the following order: - // global work offset - // global work size - // local work size - // The arguments need to add missing members. - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ - for (size_t I = 1, E = Args.size(); I != E; ++I) - Args[I] = getScalarOrArray(Args[I], Len, CI); - switch (Args.size()) { - case 2: { - // Has global work size. - auto T = Args[1]->getType(); - auto C = getScalarOrArrayConstantInt(CI, T, Len, 0); - Args.push_back(C); - Args.push_back(C); - } - break; - case 3: { - // Has global and local work size. - auto T = Args[1]->getType(); - Args.push_back(getScalarOrArrayConstantInt(CI, T, Len, 0)); - } - break; - case 4: { - // Move offset arg to the end - auto OffsetPos = Args.begin() + 1; - Value* OffsetVal = *OffsetPos; - Args.erase(OffsetPos); - Args.push_back(OffsetVal); - } - break; - default: - assert(0 && "Invalid number of arguments"); - } - // Translate ndrange_ND into differently named SPIR-V decorated functions because - // they have array arugments of different dimension which mangled the same way. - return getSPIRVFuncName(OpBuildNDRange, "_" + lenStr + "D"); - }, &Attrs); -} - -void -OCL20ToSPIRV::visitCallAsyncWorkGroupCopy(CallInst* CI, - const std::string &DemangledName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ - if (DemangledName == OCLUtil::kOCLBuiltinName::AsyncWorkGroupCopy) { - Args.insert(Args.begin()+3, addSizet(1)); - } - Args.insert(Args.begin(), addInt32(ScopeWorkgroup)); - return getSPIRVFuncName(OpGroupAsyncCopy); - }, &Attrs); -} - -CallInst * -OCL20ToSPIRV::visitCallAtomicCmpXchg(CallInst* CI, - const std::string& DemangledName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - Value *Expected = nullptr; - CallInst *NewCI = nullptr; - mutateCallInstOCL(M, CI, [&](CallInst * CI, std::vector &Args, - Type *&RetTy){ - Expected = Args[1]; // temporary save second argument. - Args[1] = new LoadInst(Args[1], "exp", false, CI); - RetTy = Args[2]->getType(); - assert(Args[0]->getType()->getPointerElementType()->isIntegerTy() && - Args[1]->getType()->isIntegerTy() && Args[2]->getType()->isIntegerTy() && - "In SPIR-V 1.0 arguments of OpAtomicCompareExchange must be " - "an integer type scalars"); - return kOCLBuiltinName::AtomicCmpXchgStrong; - }, - [&](CallInst *NCI)->Instruction * { - NewCI = NCI; - Instruction* Store = new StoreInst(NCI, Expected, NCI->getNextNode()); - return new ICmpInst(Store->getNextNode(), CmpInst::ICMP_EQ, NCI, - NCI->getArgOperand(1)); - }, - &Attrs); - return NewCI; -} - -void -OCL20ToSPIRV::visitCallAtomicInit(CallInst* CI) { - auto ST = new StoreInst(CI->getArgOperand(1), CI->getArgOperand(0), CI); - ST->takeName(CI); - CI->dropAllReferences(); - CI->eraseFromParent(); -} - -void -OCL20ToSPIRV::visitCallAllAny(spv::Op OC, CallInst* CI) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - - auto Args = getArguments(CI); - assert(Args.size() == 1); - - auto *ArgTy = Args[0]->getType(); - auto Zero = Constant::getNullValue(Args[0]->getType()); - - auto *Cmp = CmpInst::Create(CmpInst::ICmp, CmpInst::ICMP_SLT, Args[0], Zero, - "cast", CI); - - if (!isa(ArgTy)) { - auto *Cast = CastInst::CreateZExtOrBitCast(Cmp, Type::getInt32Ty(*Ctx), - "", Cmp->getNextNode()); - CI->replaceAllUsesWith(Cast); - CI->eraseFromParent(); - } else { - mutateCallInstSPIRV( - M, CI, - [&](CallInst *, std::vector &Args, Type *&Ret) { - Args[0] = Cmp; - Ret = Type::getInt1Ty(*Ctx); - - return getSPIRVFuncName(OC); - }, - [&](CallInst *CI) -> Instruction * { - return CastInst::CreateZExtOrBitCast(CI, Type::getInt32Ty(*Ctx), "", - CI->getNextNode()); - }, - &Attrs); - } -} - -void -OCL20ToSPIRV::visitCallAtomicWorkItemFence(CallInst* CI) { - transMemoryBarrier(CI, getAtomicWorkItemFenceLiterals(CI)); -} - -void -OCL20ToSPIRV::visitCallMemFence(CallInst* CI) { - transMemoryBarrier(CI, std::make_tuple( - cast(CI->getArgOperand(0))->getZExtValue(), - OCLMO_relaxed, - OCLMS_work_group)); -} - -void OCL20ToSPIRV::transMemoryBarrier(CallInst* CI, - AtomicWorkItemFenceLiterals Lit) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ - Args.resize(2); - Args[0] = addInt32(map(std::get<2>(Lit))); - Args[1] = addInt32(mapOCLMemSemanticToSPIRV(std::get<0>(Lit), - std::get<1>(Lit))); - return getSPIRVFuncName(OpMemoryBarrier); - }, &Attrs); -} - -void -OCL20ToSPIRV::visitCallAtomicLegacy(CallInst* CI, - StringRef MangledName, const std::string& DemangledName) { - StringRef Stem = DemangledName; - if (Stem.startswith("atom_")) - Stem = Stem.drop_front(strlen("atom_")); - else if (Stem.startswith("atomic_")) - Stem = Stem.drop_front(strlen("atomic_")); - else - return; - - std::string Sign; - std::string Postfix; - std::string Prefix; - if (Stem == "add" || - Stem == "sub" || - Stem == "and" || - Stem == "or" || - Stem == "xor" || - Stem == "min" || - Stem == "max") { - if ((Stem == "min" || Stem == "max") && - isMangledTypeUnsigned(MangledName.back())) - Sign = 'u'; - Prefix = "fetch_"; - Postfix = "_explicit"; - } else if (Stem == "xchg") { - Stem = "exchange"; - Postfix = "_explicit"; - } - else if (Stem == "cmpxchg") { - Stem = "compare_exchange_strong"; - Postfix = "_explicit"; - } - else if (Stem == "inc" || - Stem == "dec") { - // do nothing - } else - return; - - OCLBuiltinTransInfo Info; - Info.UniqName = "atomic_" + Prefix + Sign + Stem.str() + Postfix; - std::vector PostOps; - PostOps.push_back(OCLLegacyAtomicMemOrder); - if (Stem.startswith("compare_exchange")) - PostOps.push_back(OCLLegacyAtomicMemOrder); - PostOps.push_back(OCLLegacyAtomicMemScope); - - Info.PostProc = [=](std::vector &Ops){ - for (auto &I:PostOps){ - Ops.push_back(addInt32(I)); - } - }; - transAtomicBuiltin(CI, Info); -} - -void -OCL20ToSPIRV::visitCallAtomicCpp11(CallInst* CI, - StringRef MangledName, const std::string& DemangledName) { - StringRef Stem = DemangledName; - if (Stem.startswith("atomic_")) - Stem = Stem.drop_front(strlen("atomic_")); - else - return; - - std::string NewStem = Stem; - std::vector PostOps; - if (Stem.startswith("store") || - Stem.startswith("load") || - Stem.startswith("exchange") || - Stem.startswith("compare_exchange") || - Stem.startswith("fetch") || - Stem.startswith("flag")) { - if ((Stem.startswith("fetch_min") || - Stem.startswith("fetch_max")) && - containsUnsignedAtomicType(MangledName)) - NewStem.insert(NewStem.begin() + strlen("fetch_"), 'u'); - - if (!Stem.endswith("_explicit")) { - NewStem = NewStem + "_explicit"; - PostOps.push_back(OCLMO_seq_cst); - if (Stem.startswith("compare_exchange")) - PostOps.push_back(OCLMO_seq_cst); - PostOps.push_back(OCLMS_device); - } else { - auto MaxOps = getOCLCpp11AtomicMaxNumOps( - Stem.drop_back(strlen("_explicit"))); - if (CI->getNumArgOperands() < MaxOps) - PostOps.push_back(OCLMS_device); - } - } else if (Stem == "work_item_fence") { - // do nothing - } else - return; - - OCLBuiltinTransInfo Info; - Info.UniqName = std::string("atomic_") + NewStem; - Info.PostProc = [=](std::vector &Ops){ - for (auto &I:PostOps){ - Ops.push_back(addInt32(I)); - } - }; - - transAtomicBuiltin(CI, Info); -} - -void -OCL20ToSPIRV::transAtomicBuiltin(CallInst* CI, - OCLBuiltinTransInfo& Info) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV(M, CI, [=](CallInst * CI, std::vector &Args){ - Info.PostProc(Args); - // Order of args in OCL20: - // object, 0-2 other args, 1-2 order, scope - const size_t NumOrder = getAtomicBuiltinNumMemoryOrderArgs(Info.UniqName); - const size_t ArgsCount = Args.size(); - const size_t ScopeIdx = ArgsCount - 1; - const size_t OrderIdx = ScopeIdx - NumOrder; - Args[ScopeIdx] = mapUInt(M, cast(Args[ScopeIdx]), - [](unsigned I){ - return map(static_cast(I)); - }); - for (size_t I = 0; I < NumOrder; ++I) - Args[OrderIdx + I] = mapUInt(M, cast(Args[OrderIdx + I]), - [](unsigned Ord) { - return mapOCLMemSemanticToSPIRV(0, static_cast(Ord)); - }); - // Order of args in SPIR-V: - // object, scope, 1-2 order, 0-2 other args - std::swap(Args[1], Args[ScopeIdx]); - if(OrderIdx > 2) { - // For atomic_compare_exchange the swap above puts Comparator/Expected - // argument just where it should be, so don't move the last argument then. - int offset = Info.UniqName.find("atomic_compare_exchange") == 0 ? 1 : 0; - std::rotate(Args.begin() + 2, Args.begin() + OrderIdx, - Args.end() - offset); - } - return getSPIRVFuncName(OCLSPIRVBuiltinMap::map(Info.UniqName)); - }, &Attrs); -} - -void -OCL20ToSPIRV::visitCallWorkGroupBarrier(CallInst* CI) { - auto Lit = getWorkGroupBarrierLiterals(CI); - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ - Args.resize(3); - Args[0] = addInt32(map(std::get<2>(Lit))); - Args[1] = addInt32(map(std::get<1>(Lit))); - Args[2] = addInt32(mapOCLMemFenceFlagToSPIRV(std::get<0>(Lit))); - return getSPIRVFuncName(OpControlBarrier); - }, &Attrs); -} - -void OCL20ToSPIRV::visitCallConvert(CallInst* CI, - StringRef MangledName, const std::string& DemangledName) { - if (eraseUselessConvert(CI, MangledName, DemangledName)) - return; - Op OC = OpNop; - auto TargetTy = CI->getType(); - auto SrcTy = CI->getArgOperand(0)->getType(); - if (isa(TargetTy)) - TargetTy = TargetTy->getVectorElementType(); - if (isa(SrcTy)) - SrcTy = SrcTy->getVectorElementType(); - auto IsTargetInt = isa(TargetTy); - - std::string TargetTyName = DemangledName.substr( - strlen(kOCLBuiltinName::ConvertPrefix)); - auto FirstUnderscoreLoc = TargetTyName.find('_'); - if (FirstUnderscoreLoc != std::string::npos) - TargetTyName = TargetTyName.substr(0, FirstUnderscoreLoc); - TargetTyName = std::string("_R") + TargetTyName; - - std::string Sat = DemangledName.find("_sat") != std::string::npos ? - "_sat" : ""; - auto TargetSigned = DemangledName[8] != 'u'; - if (isa(SrcTy)) { - bool Signed = isLastFuncParamSigned(MangledName); - if (IsTargetInt) { - if (!Sat.empty() && TargetSigned != Signed) { - OC = Signed ? OpSatConvertSToU : OpSatConvertUToS; - Sat = ""; - } else - OC = Signed ? OpSConvert : OpUConvert; - } else - OC = Signed ? OpConvertSToF : OpConvertUToF; - } else { - if (IsTargetInt) { - OC = TargetSigned ? OpConvertFToS : OpConvertFToU; - } else - OC = OpFConvert; - } - auto Loc = DemangledName.find("_rt"); - std::string Rounding; - if (Loc != std::string::npos && - !(isa(SrcTy) && IsTargetInt)) { - Rounding = DemangledName.substr(Loc, 4); - } - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ - return getSPIRVFuncName(OC, TargetTyName + Sat + Rounding); - }, &Attrs); -} - -void OCL20ToSPIRV::visitCallGroupBuiltin(CallInst* CI, - StringRef MangledName, const std::string& OrigDemangledName) { - auto F = CI->getCalledFunction(); - std::vector PreOps; - std::string DemangledName = OrigDemangledName; - - if (DemangledName == kOCLBuiltinName::WorkGroupBarrier) - return; - if (DemangledName == kOCLBuiltinName::WaitGroupEvent) { - PreOps.push_back(ScopeWorkgroup); - } else if (DemangledName.find(kOCLBuiltinName::WorkGroupPrefix) == 0) { - DemangledName.erase(0, strlen(kOCLBuiltinName::WorkPrefix)); - PreOps.push_back(ScopeWorkgroup); - } else if (DemangledName.find(kOCLBuiltinName::SubGroupPrefix) == 0) { - DemangledName.erase(0, strlen(kOCLBuiltinName::SubPrefix)); - PreOps.push_back(ScopeSubgroup); - } else - return; - - if (DemangledName != kOCLBuiltinName::WaitGroupEvent) { - StringRef GroupOp = DemangledName; - GroupOp = GroupOp.drop_front(strlen(kSPIRVName::GroupPrefix)); - SPIRSPIRVGroupOperationMap::foreach_conditional([&](const std::string &S, - SPIRVGroupOperationKind G){ - if (!GroupOp.startswith(S)) - return true; // continue - PreOps.push_back(G); - StringRef Op = GroupOp.drop_front(S.size() + 1); - assert(!Op.empty() && "Invalid OpenCL group builtin function"); - char OpTyC = 0; - auto NeedSign = Op == "max" || Op == "min"; - auto OpTy = F->getReturnType(); - if (OpTy->isFloatingPointTy()) - OpTyC = 'f'; - else if (OpTy->isIntegerTy()) { - if (!NeedSign) - OpTyC = 'i'; - else { - if (isLastFuncParamSigned(F->getName())) - OpTyC = 's'; - else - OpTyC = 'u'; - } - } else - llvm_unreachable("Invalid OpenCL group builtin argument type"); - - DemangledName = std::string(kSPIRVName::GroupPrefix) + OpTyC + Op.str(); - return false; // break out of loop - }); - } - - bool IsGroupAllAny = (DemangledName.find("_all") != std::string::npos || - DemangledName.find("_any") != std::string::npos); - - auto Consts = getInt32(M, PreOps); - OCLBuiltinTransInfo Info; - if (IsGroupAllAny) - Info.RetTy = Type::getInt1Ty(*Ctx); - Info.UniqName = DemangledName; - Info.PostProc = [=](std::vector &Ops) { - if (IsGroupAllAny) { - IRBuilder<> IRB(CI); - Ops[0] = - IRB.CreateICmpNE(Ops[0], ConstantInt::get(Type::getInt32Ty(*Ctx), 0)); - } - size_t E = Ops.size(); - if (DemangledName == "group_broadcast" && E > 2) { - assert(E == 3 || E == 4); - makeVector(CI, Ops, std::make_pair(Ops.begin() + 1, Ops.end())); - } - Ops.insert(Ops.begin(), Consts.begin(), Consts.end()); - }; - transBuiltin(CI, Info); -} - -void -OCL20ToSPIRV::transBuiltin(CallInst* CI, - OCLBuiltinTransInfo& Info) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - Op OC = OpNop; - unsigned ExtOp = ~0U; - if (StringRef(Info.UniqName).startswith(kSPIRVName::Prefix)) - return; - if (OCLSPIRVBuiltinMap::find(Info.UniqName, &OC)) - Info.UniqName = getSPIRVFuncName(OC); - else if ((ExtOp = getExtOp(Info.MangledName, Info.UniqName)) != ~0U) - Info.UniqName = getSPIRVExtFuncName(SPIRVEIS_OpenCL, ExtOp); - else - return; - if (!Info.RetTy) - mutateCallInstSPIRV(M, CI, - [=](CallInst *, std::vector &Args) { - Info.PostProc(Args); - return Info.UniqName + Info.Postfix; - }, - &Attrs); - else - mutateCallInstSPIRV( - M, CI, - [=](CallInst *, std::vector &Args, Type *&RetTy) { - Info.PostProc(Args); - RetTy = Info.RetTy; - return Info.UniqName + Info.Postfix; - }, - [=](CallInst *NewCI) -> Instruction * { - if (NewCI->getType()->isIntegerTy() && CI->getType()->isIntegerTy()) - return CastInst::CreateIntegerCast(NewCI, CI->getType(), - Info.isRetSigned, "", CI); - else - return CastInst::CreatePointerBitCastOrAddrSpaceCast( - NewCI, CI->getType(), "", CI); - }, - &Attrs); -} - -void -OCL20ToSPIRV::visitCallPipeBuiltin(CallInst* CI, - StringRef MangledName, const std::string& DemangledName) { - std::string NewName = DemangledName; - // Transform OpenCL read_pipe/write_pipe builtin function names - // with reserve_id argument to reserved_read_pipe/reserved_write_pipe. - if ((DemangledName.find(kOCLBuiltinName::ReadPipe) == 0 || - DemangledName.find(kOCLBuiltinName::WritePipe) == 0) - && CI->getNumArgOperands() > 4) - NewName = std::string(kSPIRVName::ReservedPrefix) + DemangledName; - OCLBuiltinTransInfo Info; - Info.UniqName = NewName; - transBuiltin(CI, Info); -} - -void OCL20ToSPIRV::visitCallReadImageMSAA(CallInst *CI, StringRef MangledName, - const std::string &DemangledName) { - assert(MangledName.find("msaa") != StringRef::npos); - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV( - M, CI, - [=](CallInst *, std::vector &Args) { - Args.insert(Args.begin() + 2, getInt32(M, ImageOperandsSampleMask)); - return getSPIRVFuncName(OpImageRead, - std::string(kSPIRVPostfix::ExtDivider) + - getPostfixForReturnType(CI)); - }, - &Attrs); -} - -void OCL20ToSPIRV::visitCallReadImageWithSampler( - CallInst *CI, StringRef MangledName, const std::string &DemangledName) { - assert (MangledName.find(kMangledName::Sampler) != StringRef::npos); - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - bool isRetScalar = !CI->getType()->isVectorTy(); - mutateCallInstSPIRV( - M, CI, - [=](CallInst *, std::vector &Args, Type *&Ret) { - auto ImageTy = getAnalysis().getAdaptedType(Args[0]); - if (isOCLImageType(ImageTy)) - ImageTy = getSPIRVImageTypeFromOCL(M, ImageTy); - auto SampledImgTy = getSPIRVTypeByChangeBaseTypeName( - M, ImageTy, kSPIRVTypeName::Image, kSPIRVTypeName::SampledImg); - Value *SampledImgArgs[] = {Args[0], Args[1]}; - auto SampledImg = addCallInstSPIRV( - M, getSPIRVFuncName(OpSampledImage), SampledImgTy, SampledImgArgs, - nullptr, CI, kSPIRVName::TempSampledImage); - - Args[0] = SampledImg; - Args.erase(Args.begin() + 1, Args.begin() + 2); - - switch (Args.size()) { - case 2: // no lod - Args.push_back(getInt32(M, ImageOperandsMask::ImageOperandsLodMask)); - Args.push_back(getFloat32(M, 0.f)); - break; - case 3: // explicit lod - Args.insert(Args.begin() + 2, - getInt32(M, ImageOperandsMask::ImageOperandsLodMask)); - break; - case 4: // gradient - Args.insert(Args.begin() + 2, - getInt32(M, ImageOperandsMask::ImageOperandsGradMask)); - break; - default: - assert(0 && "read_image* with unhandled number of args!"); - } - - // SPIR-V intruction always returns 4-element vector - if (isRetScalar) - Ret = VectorType::get(Ret, 4); - return getSPIRVFuncName(OpImageSampleExplicitLod, - std::string(kSPIRVPostfix::ExtDivider) + - getPostfixForReturnType(Ret)); - }, - [&](CallInst *CI) -> Instruction * { - if (isRetScalar) - return ExtractElementInst::Create(CI, getSizet(M, 0), "", - CI->getNextNode()); - return CI; - }, - &Attrs); -} - -void -OCL20ToSPIRV::visitCallGetImageSize(CallInst* CI, - StringRef MangledName, const std::string& DemangledName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - StringRef TyName; - auto IsImg = isOCLImageType(CI->getArgOperand(0)->getType(), &TyName); - assert(IsImg); - SPIRVTypeImageDescriptor Desc = map(TyName.str()); - unsigned Dim = getImageDimension(Desc.Dim) + Desc.Arrayed; - assert(Dim > 0 && "Ivalid image dimention."); - mutateCallInstSPIRV(M, CI, - [&](CallInst *, std::vector &Args, Type *&Ret){ - assert(Args.size() == 1); - Ret = CI->getType()->isIntegerTy(64) ? Type::getInt64Ty(*Ctx) - : Type::getInt32Ty(*Ctx); - if (Dim > 1) - Ret = VectorType::get(Ret, Dim); - if (Desc.Dim == DimBuffer) - return getSPIRVFuncName(OpImageQuerySize, CI->getType()); - else { - Args.push_back(getInt32(M, 0)); - return getSPIRVFuncName(OpImageQuerySizeLod, CI->getType()); - } - }, - [&](CallInst *NCI)->Instruction * { - if (Dim == 1) - return NCI; - if (DemangledName == kOCLBuiltinName::GetImageDim) { - if (Desc.Dim == Dim3D) { - auto ZeroVec = ConstantVector::getSplat(3, - Constant::getNullValue(NCI->getType()->getVectorElementType())); - Constant *Index[] = {getInt32(M, 0), getInt32(M, 1), - getInt32(M, 2), getInt32(M, 3)}; - return new ShuffleVectorInst(NCI, ZeroVec, - ConstantVector::get(Index), "", CI); - - } else if (Desc.Dim == Dim2D && Desc.Arrayed) { - Constant *Index[] = {getInt32(M, 0), getInt32(M, 1)}; - Constant *mask = ConstantVector::get(Index); - return new ShuffleVectorInst(NCI, UndefValue::get(NCI->getType()), - mask, NCI->getName(), CI); - } - return NCI; - } - unsigned I = StringSwitch(DemangledName) - .Case(kOCLBuiltinName::GetImageWidth, 0) - .Case(kOCLBuiltinName::GetImageHeight, 1) - .Case(kOCLBuiltinName::GetImageDepth, 2) - .Case(kOCLBuiltinName::GetImageArraySize, Dim - 1); - return ExtractElementInst::Create(NCI, getUInt32(M, I), "", - NCI->getNextNode()); - }, - &Attrs); -} - -/// Remove trivial conversion functions -bool -OCL20ToSPIRV::eraseUselessConvert(CallInst *CI, - const std::string &MangledName, - const std::string &DemangledName) { - auto TargetTy = CI->getType(); - auto SrcTy = CI->getArgOperand(0)->getType(); - if (isa(TargetTy)) - TargetTy = TargetTy->getVectorElementType(); - if (isa(SrcTy)) - SrcTy = SrcTy->getVectorElementType(); - if (TargetTy == SrcTy) { - if (isa(TargetTy) && - DemangledName.find("_sat") != std::string::npos && - isLastFuncParamSigned(MangledName) != (DemangledName[8] != 'u')) - return false; - CI->getArgOperand(0)->takeName(CI); - SPIRVDBG(dbgs() << "[regularizeOCLConvert] " << *CI << " <- " << - *CI->getArgOperand(0) << '\n'); - CI->replaceAllUsesWith(CI->getArgOperand(0)); - ValuesToDelete.insert(CI); - ValuesToDelete.insert(CI->getCalledFunction()); - return true; - } - return false; -} - -void -OCL20ToSPIRV::visitCallBuiltinSimple(CallInst* CI, - StringRef MangledName, const std::string& DemangledName) { - OCLBuiltinTransInfo Info; - Info.MangledName = MangledName.str(); - Info.UniqName = DemangledName; - transBuiltin(CI, Info); -} - -/// Translates OCL work-item builtin functions to SPIRV builtin variables. -/// Function like get_global_id(i) -> x = load GlobalInvocationId; extract x, i -/// Function like get_work_dim() -> load WorkDim -void OCL20ToSPIRV::transWorkItemBuiltinsToVariables() { - DEBUG(dbgs() << "Enter transWorkItemBuiltinsToVariables\n"); - std::vector WorkList; - for (auto I = M->begin(), E = M->end(); I != E; ++I) { - std::string DemangledName; - if (!oclIsBuiltin(I->getName(), &DemangledName)) - continue; - DEBUG(dbgs() << "Function demangled name: " << DemangledName << '\n'); - std::string BuiltinVarName; - SPIRVBuiltinVariableKind BVKind; - if (!SPIRSPIRVBuiltinVariableMap::find(DemangledName, &BVKind)) - continue; - BuiltinVarName = std::string(kSPIRVName::Prefix) + - SPIRVBuiltInNameMap::map(BVKind); - DEBUG(dbgs() << "builtin variable name: " << BuiltinVarName << '\n'); - bool IsVec = I->getFunctionType()->getNumParams() > 0; - Type *GVType = IsVec ? VectorType::get(I->getReturnType(),3) : - I->getReturnType(); - auto BV = new GlobalVariable(*M, GVType, - true, - GlobalValue::ExternalLinkage, - nullptr, BuiltinVarName, - 0, - GlobalVariable::NotThreadLocal, - SPIRAS_Constant); - std::vector InstList; - for (auto UI = I->user_begin(), UE = I->user_end(); UI != UE; ++UI) { - auto CI = dyn_cast(*UI); - assert(CI && "invalid instruction"); - Value * NewValue = new LoadInst(BV, "", CI); - DEBUG(dbgs() << "Transform: " << *CI << " => " << *NewValue << '\n'); - if (IsVec) { - NewValue = ExtractElementInst::Create(NewValue, - CI->getArgOperand(0), - "", CI); - DEBUG(dbgs() << *NewValue << '\n'); - } - NewValue->takeName(CI); - CI->replaceAllUsesWith(NewValue); - InstList.push_back(CI); - } - for (auto &Inst:InstList) { - Inst->dropAllReferences(); - Inst->removeFromParent(); - } - WorkList.push_back(I); - } - for (auto &I:WorkList) { - I->dropAllReferences(); - I->removeFromParent(); - } -} - -void -OCL20ToSPIRV::visitCallReadWriteImage(CallInst* CI, - StringRef MangledName, const std::string& DemangledName) { - OCLBuiltinTransInfo Info; - if (DemangledName.find(kOCLBuiltinName::ReadImage) == 0) - Info.UniqName = kOCLBuiltinName::ReadImage; - - if (DemangledName.find(kOCLBuiltinName::WriteImage) == 0) - { - Info.UniqName = kOCLBuiltinName::WriteImage; - Info.PostProc = [&](std::vector &Args) { - if (Args.size() == 4) // write with lod - { - auto Lod = Args[2]; - Args.erase(Args.begin() + 2); - Args.push_back(getInt32(M, ImageOperandsMask::ImageOperandsLodMask)); - Args.push_back(Lod); - } - }; - } - - transBuiltin(CI, Info); -} - -void -OCL20ToSPIRV::visitCallToAddr(CallInst* CI, StringRef MangledName, - const std::string &DemangledName) { - auto AddrSpace = static_cast( - CI->getType()->getPointerAddressSpace()); - OCLBuiltinTransInfo Info; - Info.UniqName = DemangledName; - Info.Postfix = std::string(kSPIRVPostfix::Divider) + "To" + - SPIRAddrSpaceCapitalizedNameMap::map(AddrSpace); - auto StorageClass = addInt32(SPIRSPIRVAddrSpaceMap::map(AddrSpace)); - Info.RetTy = getInt8PtrTy(cast(CI->getType())); - Info.PostProc = [=](std::vector &Ops){ - auto P = Ops.back(); - Ops.pop_back(); - Ops.push_back(castToInt8Ptr(P, CI)); - Ops.push_back(StorageClass); - }; - transBuiltin(CI, Info); -} - -void OCL20ToSPIRV::visitCallRelational(CallInst *CI, - const std::string &DemangledName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - Op OC = OpNop; - OCLSPIRVBuiltinMap::find(DemangledName, &OC); - std::string SPIRVName = getSPIRVFuncName(OC); - mutateCallInstSPIRV( - M, CI, - [=](CallInst *, std::vector &Args, Type *&Ret) { - Ret = Type::getInt1Ty(*Ctx); - if (CI->getOperand(0)->getType()->isVectorTy()) - Ret = VectorType::get( - Type::getInt1Ty(*Ctx), - CI->getOperand(0)->getType()->getVectorNumElements()); - return SPIRVName; - }, - [=](CallInst *NewCI) -> Instruction * { - Value *False = nullptr, *True = nullptr; - if (NewCI->getType()->isVectorTy()) { - Type *IntTy = Type::getInt32Ty(*Ctx); - if (cast(NewCI->getOperand(0)->getType()) - ->getElementType() - ->isDoubleTy()) - IntTy = Type::getInt64Ty(*Ctx); - if (cast(NewCI->getOperand(0)->getType()) - ->getElementType() - ->isHalfTy()) - IntTy = Type::getInt16Ty(*Ctx); - Type *VTy = VectorType::get(IntTy, - NewCI->getType()->getVectorNumElements()); - False = Constant::getNullValue(VTy); - True = Constant::getAllOnesValue(VTy); - } else { - False = getInt32(M, 0); - True = getInt32(M, 1); - } - return SelectInst::Create(NewCI, True, False, "", NewCI->getNextNode()); - }, - &Attrs); -} - -void -OCL20ToSPIRV::visitCallVecLoadStore(CallInst* CI, - StringRef MangledName, const std::string& OrigDemangledName) { - std::vector PreOps; - std::string DemangledName = OrigDemangledName; - if (DemangledName.find(kOCLBuiltinName::VLoadPrefix) == 0 && - DemangledName != kOCLBuiltinName::VLoadHalf) { - SPIRVWord Width = getVecLoadWidth(DemangledName); - SPIRVDBG(spvdbgs() << "[visitCallVecLoadStore] DemangledName: " << - DemangledName << " Width: " << Width << '\n'); - PreOps.push_back(Width); - } else if (DemangledName.find(kOCLBuiltinName::RoundingPrefix) - != std::string::npos) { - auto R = SPIRSPIRVFPRoundingModeMap::map(DemangledName.substr( - DemangledName.find(kOCLBuiltinName::RoundingPrefix) + 1, 3)); - PreOps.push_back(R); - } - - if (DemangledName.find(kOCLBuiltinName::VLoadAPrefix) == 0) - transVecLoadStoreName(DemangledName, kOCLBuiltinName::VLoadAPrefix, true); - else - transVecLoadStoreName(DemangledName, kOCLBuiltinName::VLoadPrefix, false); - - if (DemangledName.find(kOCLBuiltinName::VStoreAPrefix) == 0) - transVecLoadStoreName(DemangledName, kOCLBuiltinName::VStoreAPrefix, true); - else - transVecLoadStoreName(DemangledName, kOCLBuiltinName::VStorePrefix, false); - - - auto Consts = getInt32(M, PreOps); - OCLBuiltinTransInfo Info; - Info.MangledName = MangledName; - Info.UniqName = DemangledName; - if (DemangledName.find(kOCLBuiltinName::VLoadPrefix) == 0) - Info.Postfix = std::string(kSPIRVPostfix::ExtDivider) + - getPostfixForReturnType(CI); - Info.PostProc = [=](std::vector &Ops){ - Ops.insert(Ops.end(), Consts.begin(), Consts.end()); - }; - transBuiltin(CI, Info); -} - -void OCL20ToSPIRV::visitCallGetFence(CallInst *CI, StringRef MangledName, - const std::string &DemangledName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - Op OC = OpNop; - OCLSPIRVBuiltinMap::find(DemangledName, &OC); - std::string SPIRVName = getSPIRVFuncName(OC); - mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args, - Type *&Ret) { return SPIRVName; }, - [=](CallInst *NewCI) -> Instruction * { - return BinaryOperator::CreateLShr(NewCI, getInt32(M, 8), "", CI); - }, - &Attrs); -} - -void OCL20ToSPIRV::visitCallDot(CallInst *CI) { - IRBuilder<> Builder(CI); - Value *FMulVal = Builder.CreateFMul(CI->getOperand(0), CI->getOperand(1)); - CI->replaceAllUsesWith(FMulVal); - CI->dropAllReferences(); - CI->removeFromParent(); -} - -void OCL20ToSPIRV::visitCallScalToVec(CallInst *CI, StringRef MangledName, - const std::string &DemangledName) { - // Check if all arguments have the same type - it's simple case. - auto Uniform = true; - auto IsArg0Vector = isa(CI->getOperand(0)->getType()); - for (unsigned I = 1, E = CI->getNumArgOperands(); Uniform && (I != E); ++I) { - Uniform = isa(CI->getOperand(I)->getType()) == IsArg0Vector; - } - if (Uniform) { - visitCallBuiltinSimple(CI, MangledName, DemangledName); - return; - } - - std::vector VecPos; - std::vector ScalarPos; - if (DemangledName == kOCLBuiltinName::FMin || - DemangledName == kOCLBuiltinName::FMax || - DemangledName == kOCLBuiltinName::Min || - DemangledName == kOCLBuiltinName::Max) { - VecPos.push_back(0); - ScalarPos.push_back(1); - } else if (DemangledName == kOCLBuiltinName::Clamp) { - VecPos.push_back(0); - ScalarPos.push_back(1); - ScalarPos.push_back(2); - } else if (DemangledName == kOCLBuiltinName::Mix) { - VecPos.push_back(0); - VecPos.push_back(1); - ScalarPos.push_back(2); - } else if (DemangledName == kOCLBuiltinName::Step) { - VecPos.push_back(1); - ScalarPos.push_back(0); - } else if (DemangledName == kOCLBuiltinName::SmoothStep) { - VecPos.push_back(2); - ScalarPos.push_back(0); - ScalarPos.push_back(1); - } - - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - mutateCallInstSPIRV( - M, CI, - [=](CallInst *, std::vector &Args) { - Args.resize(VecPos.size() + ScalarPos.size()); - for (auto I : VecPos) { - Args[I] = CI->getOperand(I); - } - auto VecArgWidth = - CI->getOperand(VecPos[0])->getType()->getVectorNumElements(); - for (auto I : ScalarPos) { - Instruction *Inst = InsertElementInst::Create( - UndefValue::get(CI->getOperand(VecPos[0])->getType()), - CI->getOperand(I), getInt32(M, 0), "", CI); - Value *NewVec = new ShuffleVectorInst( - Inst, UndefValue::get(CI->getOperand(VecPos[0])->getType()), - ConstantVector::getSplat(VecArgWidth, getInt32(M, 0)), "", CI); - - Args[I] = NewVec; - } - return getSPIRVExtFuncName(SPIRVEIS_OpenCL, - getExtOp(MangledName, DemangledName)); - }, - &Attrs); -} - -void OCL20ToSPIRV::visitCallGetImageChannel(CallInst *CI, StringRef MangledName, - const std::string &DemangledName, - unsigned int Offset) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - Op OC = OpNop; - OCLSPIRVBuiltinMap::find(DemangledName, &OC); - std::string SPIRVName = getSPIRVFuncName(OC); - mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args, - Type *&Ret) { return SPIRVName; }, - [=](CallInst *NewCI) -> Instruction * { - return BinaryOperator::CreateAdd( - NewCI, getInt32(M, Offset), "", CI); - }, - &Attrs); -} -} - -INITIALIZE_PASS_BEGIN(OCL20ToSPIRV, "cl20tospv", "Transform OCL 2.0 to SPIR-V", - false, false) -INITIALIZE_PASS_DEPENDENCY(OCLTypeToSPIRV) -INITIALIZE_PASS_END(OCL20ToSPIRV, "cl20tospv", "Transform OCL 2.0 to SPIR-V", - false, false) - -ModulePass *llvm::createOCL20ToSPIRV() { - return new OCL20ToSPIRV(); -} +//===- OCL20ToSPIRV.cpp - Transform OCL20 to SPIR-V builtins -----*- C++ -*-===// +// +// The LLVM/SPIRV Translator +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. +// +//===----------------------------------------------------------------------===// +// +// This file implements translation of OCL20 builtin functions. +// +//===----------------------------------------------------------------------===// +#define DEBUG_TYPE "cl20tospv" + +#include "SPIRVInternal.h" +#include "OCLUtil.h" +#include "OCLTypeToSPIRV.h" + +#include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/InstVisitor.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Pass.h" +#include "llvm/PassSupport.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace llvm; +using namespace SPIRV; +using namespace OCLUtil; + +namespace SPIRV { +static size_t +getOCLCpp11AtomicMaxNumOps(StringRef Name) { + return StringSwitch(Name) + .Cases("load", "flag_test_and_set", "flag_clear", 3) + .Cases("store", "exchange", 4) + .StartsWith("compare_exchange", 6) + .StartsWith("fetch", 4) + .Default(0); +} + +class OCL20ToSPIRV: public ModulePass, + public InstVisitor { +public: + OCL20ToSPIRV():ModulePass(ID), M(nullptr), Ctx(nullptr), CLVer(0) { + initializeOCL20ToSPIRVPass(*PassRegistry::getPassRegistry()); + } + virtual bool runOnModule(Module &M); + + void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + } + + virtual void visitCallInst(CallInst &CI); + + /// Transform barrier/work_group_barrier to __spirv_ControlBarrier. + /// barrier(flag) => + /// __spirv_ControlBarrier(workgroup, workgroup, map(flag)) + /// workgroup_barrier(scope, flag) => + /// __spirv_ControlBarrier(workgroup, map(scope), map(flag)) + void visitCallWorkGroupBarrier(CallInst *CI); + + /// Erase useless convert functions. + /// \return true if the call instruction is erased. + bool eraseUselessConvert(CallInst *Call, const std::string &MangledName, + const std::string &DeMangledName); + + /// Transform convert_ to + /// __spirv_{CastOpName}_R{TargeTyName}{_sat}{_rt[p|n|z|e]} + void visitCallConvert(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform async_work_group{_strided}_copy. + /// async_work_group_copy(dst, src, n, event) + /// => async_work_group_strided_copy(dst, src, n, 1, event) + /// async_work_group_strided_copy(dst, src, n, stride, event) + /// => __spirv_AsyncGroupCopy(ScopeWorkGroup, dst, src, n, stride, event) + void visitCallAsyncWorkGroupCopy(CallInst *CI, + const std::string &DemangledName); + + /// Transform OCL builtin function to SPIR-V builtin function. + void transBuiltin(CallInst *CI, OCLBuiltinTransInfo &Info); + + /// Transform OCL work item builtin functions to SPIR-V builtin variables. + void transWorkItemBuiltinsToVariables(); + + /// Transform atomic_work_item_fence/mem_fence to __spirv_MemoryBarrier. + /// func(flag, order, scope) => + /// __spirv_MemoryBarrier(map(scope), map(flag)|map(order)) + void transMemoryBarrier(CallInst *CI, AtomicWorkItemFenceLiterals); + + /// Transform all to __spirv_Op(All|Any). Note that the types mismatch so + // some extra code is emitted to convert between the two. + void visitCallAllAny(spv::Op OC, CallInst *CI); + + /// Transform atomic_* to __spirv_Atomic*. + /// atomic_x(ptr_arg, args, order, scope) => + /// __spirv_AtomicY(ptr_arg, map(order), map(scope), args) + void transAtomicBuiltin(CallInst *CI, OCLBuiltinTransInfo &Info); + + /// Transform atomic_work_item_fence to __spirv_MemoryBarrier. + /// atomic_work_item_fence(flag, order, scope) => + /// __spirv_MemoryBarrier(map(scope), map(flag)|map(order)) + void visitCallAtomicWorkItemFence(CallInst *CI); + + /// Transform atomic_compare_exchange call. + /// In atomic_compare_exchange, the expected value parameter is a pointer. + /// However in SPIR-V it is a value. The transformation adds a load + /// instruction, result of which is passed to atomic_compare_exchange as + /// argument. + /// The transformation adds a store instruction after the call, to update the + /// value in expected with the value pointed to by object. Though, it is not + /// necessary in case they are equal, this approach makes result code simpler. + /// Also ICmp instruction is added, because the call must return result of + /// comparison. + /// \returns the call instruction of atomic_compare_exchange_strong. + CallInst *visitCallAtomicCmpXchg(CallInst *CI, + const std::string &DemangledName); + + /// Transform atomic_init. + /// atomic_init(p, x) => store p, x + void visitCallAtomicInit(CallInst *CI); + + /// Transform legacy OCL 1.x atomic builtins to SPIR-V builtins for extensions + /// cl_khr_int64_base_atomics + /// cl_khr_int64_extended_atomics + /// Do nothing if the called function is not a legacy atomic builtin. + void visitCallAtomicLegacy(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform OCL 2.0 C++11 atomic builtins to SPIR-V builtins. + /// Do nothing if the called function is not a C++11 atomic builtin. + void visitCallAtomicCpp11(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform OCL builtin function to SPIR-V builtin function. + /// Assuming there is a simple name mapping without argument changes. + /// Should be called at last. + void visitCallBuiltinSimple(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform get_image_{width|height|depth|dim}. + /// get_image_xxx(...) => + /// dimension = __spirv_ImageQuerySizeLod_R{ReturnType}(...); + /// return dimension.{x|y|z}; + void visitCallGetImageSize(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform {work|sub}_group_x => + /// __spirv_{OpName} + /// + /// Special handling of work_group_broadcast. + /// work_group_broadcast(a, x, y, z) + /// => + /// __spirv_GroupBroadcast(a, vec3(x, y, z)) + + void visitCallGroupBuiltin(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform mem_fence to __spirv_MemoryBarrier. + /// mem_fence(flag) => __spirv_MemoryBarrier(Workgroup, map(flag)) + void visitCallMemFence(CallInst *CI); + + void visitCallNDRange(CallInst *CI, const std::string &DemangledName); + + /// Transform OCL pipe builtin function to SPIR-V pipe builtin function. + void visitCallPipeBuiltin(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform read_image with sampler arguments. + /// read_image(image, sampler, ...) => + /// sampled_image = __spirv_SampledImage(image, sampler); + /// return __spirv_ImageSampleExplicitLod_R{ReturnType}(sampled_image, ...); + void visitCallReadImageWithSampler(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform read_image with msaa image arguments. + /// Sample argument must be acoded as Image Operand. + void visitCallReadImageMSAA(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform {read|write}_image without sampler arguments. + void visitCallReadWriteImage(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform to_{global|local|private}. + /// + /// T* a = ...; + /// addr T* b = to_addr(a); + /// => + /// i8* x = cast(a); + /// addr i8* y = __spirv_GenericCastToPtr_ToAddr(x); + /// addr T* b = cast(y); + void visitCallToAddr(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform return type of relatinal built-in functions like isnan, isfinite + /// to boolean values. + void visitCallRelational(CallInst *CI, const std::string &DemangledName); + + /// Transform vector load/store functions to SPIR-V extended builtin + /// functions + /// {vload|vstore{a}}{_half}{n}{_rte|_rtz|_rtp|_rtn} => + /// __spirv_ocl_{ExtendedInstructionOpCodeName}__R{ReturnType} + void visitCallVecLoadStore(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transforms get_mem_fence built-in to SPIR-V function and aligns result values with SPIR 1.2. + /// get_mem_fence(ptr) => __spirv_GenericPtrMemSemantics + /// GenericPtrMemSemantics valid values are 0x100, 0x200 and 0x300, where is + /// SPIR 1.2 defines them as 0x1, 0x2 and 0x3, so this function adjusts + /// GenericPtrMemSemantics results to SPIR 1.2 values. + void visitCallGetFence(CallInst *CI, StringRef MangledName, const std::string& DemangledName); + + /// Transforms OpDot instructions with a scalar type to a fmul instruction + void visitCallDot(CallInst *CI); + + /// Fixes for built-in functions with vector+scalar arguments that are + /// translated to the SPIR-V instructions where all arguments must have the + /// same type. + void visitCallScalToVec(CallInst *CI, StringRef MangledName, + const std::string &DemangledName); + + /// Transform get_image_channel_{order|data_type} built-in functions to + /// __spirv_ocl_{ImageQueryOrder|ImageQueryFormat} + void visitCallGetImageChannel(CallInst *CI, StringRef MangledName, + const std::string &DemangledName, + unsigned int Offset); + + void visitDbgInfoIntrinsic(DbgInfoIntrinsic &I){ + I.dropAllReferences(); + I.eraseFromParent(); + } + static char ID; +private: + Module *M; + LLVMContext *Ctx; + unsigned CLVer; /// OpenCL version as major*10+minor + std::set ValuesToDelete; + + ConstantInt *addInt32(int I) { + return getInt32(M, I); + } + ConstantInt *addSizet(uint64_t I) { + return getSizet(M, I); + } + + /// Get vector width from OpenCL vload* function name. + SPIRVWord getVecLoadWidth(const std::string& DemangledName) { + SPIRVWord Width = 0; + if (DemangledName == "vloada_half") + Width = 1; + else { + unsigned Loc = 5; + if (DemangledName.find("vload_half") == 0) + Loc = 10; + else if (DemangledName.find("vloada_half") == 0) + Loc = 11; + + std::stringstream SS(DemangledName.substr(Loc)); + SS >> Width; + } + return Width; + } + + /// Transform OpenCL vload/vstore function name. + void transVecLoadStoreName(std::string& DemangledName, + const std::string &Stem, bool AlwaysN) { + auto HalfStem = Stem + "_half"; + auto HalfStemR = HalfStem + "_r"; + if (!AlwaysN && DemangledName == HalfStem) + return; + if (!AlwaysN && DemangledName.find(HalfStemR) == 0) { + DemangledName = HalfStemR; + return; + } + if (DemangledName.find(HalfStem) == 0) { + auto OldName = DemangledName; + DemangledName = HalfStem + "n"; + if (OldName.find("_r") != std::string::npos) + DemangledName += "_r"; + return; + } + if (DemangledName.find(Stem) == 0) { + DemangledName = Stem + "n"; + return; + } + } + +}; + +char OCL20ToSPIRV::ID = 0; + +bool +OCL20ToSPIRV::runOnModule(Module& Module) { + M = &Module; + Ctx = &M->getContext(); + auto Src = getSPIRVSource(&Module); + if (std::get<0>(Src) != spv::SourceLanguageOpenCL_C) + return false; + + CLVer = std::get<1>(Src); + if (CLVer > kOCLVer::CL20) + return false; + + DEBUG(dbgs() << "Enter OCL20ToSPIRV:\n"); + + transWorkItemBuiltinsToVariables(); + + visit(*M); + + for (auto &I:ValuesToDelete) + if (auto Inst = dyn_cast(I)) + Inst->eraseFromParent(); + for (auto &I:ValuesToDelete) + if (auto GV = dyn_cast(I)) + GV->eraseFromParent(); + + DEBUG(dbgs() << "After OCL20ToSPIRV:\n" << *M); + + std::string Err; + raw_string_ostream ErrorOS(Err); + if (verifyModule(*M, &ErrorOS)){ + DEBUG(errs() << "Fails to verify module: " << ErrorOS.str()); + } + return true; +} + +// The order of handling OCL builtin functions is important. +// Workgroup functions need to be handled before pipe functions since +// there are functions fall into both categories. +void +OCL20ToSPIRV::visitCallInst(CallInst& CI) { + DEBUG(dbgs() << "[visistCallInst] " << CI << '\n'); + auto F = CI.getCalledFunction(); + if (!F) + return; + + auto MangledName = F->getName(); + std::string DemangledName; + if (!oclIsBuiltin(MangledName, &DemangledName)) + return; + DEBUG(dbgs() << "DemangledName: " << DemangledName << '\n'); + if (DemangledName.find(kOCLBuiltinName::NDRangePrefix) == 0) { + visitCallNDRange(&CI, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::All) { + visitCallAllAny(OpAll, &CI); + return; + } + if (DemangledName == kOCLBuiltinName::Any) { + visitCallAllAny(OpAny, &CI); + return; + } + if (DemangledName.find(kOCLBuiltinName::AsyncWorkGroupCopy) == 0 || + DemangledName.find(kOCLBuiltinName::AsyncWorkGroupStridedCopy) == 0) { + visitCallAsyncWorkGroupCopy(&CI, DemangledName); + return; + } + if (DemangledName.find(kOCLBuiltinName::AtomicPrefix) == 0 || + DemangledName.find(kOCLBuiltinName::AtomPrefix) == 0) { + auto PCI = &CI; + if (DemangledName == kOCLBuiltinName::AtomicInit) { + visitCallAtomicInit(PCI); + return; + } + if (DemangledName == kOCLBuiltinName::AtomicWorkItemFence) { + visitCallAtomicWorkItemFence(PCI); + return; + } + if (DemangledName == kOCLBuiltinName::AtomicCmpXchgWeak || + DemangledName == kOCLBuiltinName::AtomicCmpXchgStrong || + DemangledName == kOCLBuiltinName::AtomicCmpXchgWeakExplicit || + DemangledName == kOCLBuiltinName::AtomicCmpXchgStrongExplicit) { + assert(CLVer == kOCLVer::CL20 && "Wrong version of OpenCL"); + PCI = visitCallAtomicCmpXchg(PCI, DemangledName); + } + visitCallAtomicLegacy(PCI, MangledName, DemangledName); + visitCallAtomicCpp11(PCI, MangledName, DemangledName); + return; + } + if (DemangledName.find(kOCLBuiltinName::ConvertPrefix) == 0) { + visitCallConvert(&CI, MangledName, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::GetImageWidth || + DemangledName == kOCLBuiltinName::GetImageHeight || + DemangledName == kOCLBuiltinName::GetImageDepth || + DemangledName == kOCLBuiltinName::GetImageDim || + DemangledName == kOCLBuiltinName::GetImageArraySize) { + visitCallGetImageSize(&CI, MangledName, DemangledName); + return; + } + if ((DemangledName.find(kOCLBuiltinName::WorkGroupPrefix) == 0 && + DemangledName != kOCLBuiltinName::WorkGroupBarrier) || + DemangledName == kOCLBuiltinName::WaitGroupEvent || + DemangledName.find(kOCLBuiltinName::SubGroupPrefix) == 0) { + visitCallGroupBuiltin(&CI, MangledName, DemangledName); + return; + } + if (DemangledName.find(kOCLBuiltinName::Pipe) != std::string::npos) { + visitCallPipeBuiltin(&CI, MangledName, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::MemFence) { + visitCallMemFence(&CI); + return; + } + if (DemangledName.find(kOCLBuiltinName::ReadImage) == 0) { + if (MangledName.find(kMangledName::Sampler) != StringRef::npos) { + visitCallReadImageWithSampler(&CI, MangledName, DemangledName); + return; + } + if (MangledName.find("msaa") != StringRef::npos) { + visitCallReadImageMSAA(&CI, MangledName, DemangledName); + return; + } + } + if (DemangledName.find(kOCLBuiltinName::ReadImage) == 0 || + DemangledName.find(kOCLBuiltinName::WriteImage) == 0) { + visitCallReadWriteImage(&CI, MangledName, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::ToGlobal || + DemangledName == kOCLBuiltinName::ToLocal || + DemangledName == kOCLBuiltinName::ToPrivate) { + visitCallToAddr(&CI, MangledName, DemangledName); + return; + } + if (DemangledName.find(kOCLBuiltinName::VLoadPrefix) == 0 || + DemangledName.find(kOCLBuiltinName::VStorePrefix) == 0) { + visitCallVecLoadStore(&CI, MangledName, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::IsFinite || + DemangledName == kOCLBuiltinName::IsInf || + DemangledName == kOCLBuiltinName::IsNan || + DemangledName == kOCLBuiltinName::IsNormal || + DemangledName == kOCLBuiltinName::Signbit) { + visitCallRelational(&CI, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::WorkGroupBarrier || + DemangledName == kOCLBuiltinName::Barrier) { + visitCallWorkGroupBarrier(&CI); + return; + } + if (DemangledName == kOCLBuiltinName::GetFence) { + visitCallGetFence(&CI, MangledName, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::Dot && + !(CI.getOperand(0)->getType()->isVectorTy())) { + visitCallDot(&CI); + return; + } + if (DemangledName == kOCLBuiltinName::FMin || + DemangledName == kOCLBuiltinName::FMax || + DemangledName == kOCLBuiltinName::Min || + DemangledName == kOCLBuiltinName::Max || + DemangledName == kOCLBuiltinName::Step || + DemangledName == kOCLBuiltinName::SmoothStep || + DemangledName == kOCLBuiltinName::Clamp || + DemangledName == kOCLBuiltinName::Mix) { + visitCallScalToVec(&CI, MangledName, DemangledName); + return; + } + if (DemangledName == kOCLBuiltinName::GetImageChannelDataType) { + visitCallGetImageChannel(&CI, MangledName, DemangledName, + OCLImageChannelDataTypeOffset); + return; + } + if (DemangledName == kOCLBuiltinName::GetImageChannelOrder) { + visitCallGetImageChannel(&CI, MangledName, DemangledName, + OCLImageChannelOrderOffset); + return; + } + visitCallBuiltinSimple(&CI, MangledName, DemangledName); +} + +void +OCL20ToSPIRV::visitCallNDRange(CallInst *CI, + const std::string &DemangledName) { + assert(DemangledName.find(kOCLBuiltinName::NDRangePrefix) == 0); + std::string lenStr = DemangledName.substr(8, 1); + auto Len = atoi(lenStr.c_str()); + assert (Len >= 1 && Len <= 3); + // SPIR-V ndrange structure requires 3 members in the following order: + // global work offset + // global work size + // local work size + // The arguments need to add missing members. + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ + for (size_t I = 1, E = Args.size(); I != E; ++I) + Args[I] = getScalarOrArray(Args[I], Len, CI); + switch (Args.size()) { + case 2: { + // Has global work size. + auto T = Args[1]->getType(); + auto C = getScalarOrArrayConstantInt(CI, T, Len, 0); + Args.push_back(C); + Args.push_back(C); + } + break; + case 3: { + // Has global and local work size. + auto T = Args[1]->getType(); + Args.push_back(getScalarOrArrayConstantInt(CI, T, Len, 0)); + } + break; + case 4: { + // Move offset arg to the end + auto OffsetPos = Args.begin() + 1; + Value* OffsetVal = *OffsetPos; + Args.erase(OffsetPos); + Args.push_back(OffsetVal); + } + break; + default: + assert(0 && "Invalid number of arguments"); + } + // Translate ndrange_ND into differently named SPIR-V decorated functions because + // they have array arugments of different dimension which mangled the same way. + return getSPIRVFuncName(OpBuildNDRange, "_" + lenStr + "D"); + }, &Attrs); +} + +void +OCL20ToSPIRV::visitCallAsyncWorkGroupCopy(CallInst* CI, + const std::string &DemangledName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ + if (DemangledName == OCLUtil::kOCLBuiltinName::AsyncWorkGroupCopy) { + Args.insert(Args.begin()+3, addSizet(1)); + } + Args.insert(Args.begin(), addInt32(ScopeWorkgroup)); + return getSPIRVFuncName(OpGroupAsyncCopy); + }, &Attrs); +} + +CallInst * +OCL20ToSPIRV::visitCallAtomicCmpXchg(CallInst* CI, + const std::string& DemangledName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + Value *Expected = nullptr; + CallInst *NewCI = nullptr; + mutateCallInstOCL(M, CI, [&](CallInst * CI, std::vector &Args, + Type *&RetTy){ + Expected = Args[1]; // temporary save second argument. + Args[1] = new LoadInst(Args[1], "exp", false, CI); + RetTy = Args[2]->getType(); + assert(Args[0]->getType()->getPointerElementType()->isIntegerTy() && + Args[1]->getType()->isIntegerTy() && Args[2]->getType()->isIntegerTy() && + "In SPIR-V 1.0 arguments of OpAtomicCompareExchange must be " + "an integer type scalars"); + return kOCLBuiltinName::AtomicCmpXchgStrong; + }, + [&](CallInst *NCI)->Instruction * { + NewCI = NCI; + Instruction* Store = new StoreInst(NCI, Expected, NCI->getNextNode()); + return new ICmpInst(Store->getNextNode(), CmpInst::ICMP_EQ, NCI, + NCI->getArgOperand(1)); + }, + &Attrs); + return NewCI; +} + +void +OCL20ToSPIRV::visitCallAtomicInit(CallInst* CI) { + auto ST = new StoreInst(CI->getArgOperand(1), CI->getArgOperand(0), CI); + ST->takeName(CI); + CI->dropAllReferences(); + CI->eraseFromParent(); +} + +void +OCL20ToSPIRV::visitCallAllAny(spv::Op OC, CallInst* CI) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + + auto Args = getArguments(CI); + assert(Args.size() == 1); + + auto *ArgTy = Args[0]->getType(); + auto Zero = Constant::getNullValue(Args[0]->getType()); + + auto *Cmp = CmpInst::Create(CmpInst::ICmp, CmpInst::ICMP_SLT, Args[0], Zero, + "cast", CI); + + if (!isa(ArgTy)) { + auto *Cast = CastInst::CreateZExtOrBitCast(Cmp, Type::getInt32Ty(*Ctx), + "", Cmp->getNextNode()); + CI->replaceAllUsesWith(Cast); + CI->eraseFromParent(); + } else { + mutateCallInstSPIRV( + M, CI, + [&](CallInst *, std::vector &Args, Type *&Ret) { + Args[0] = Cmp; + Ret = Type::getInt1Ty(*Ctx); + + return getSPIRVFuncName(OC); + }, + [&](CallInst *CI) -> Instruction * { + return CastInst::CreateZExtOrBitCast(CI, Type::getInt32Ty(*Ctx), "", + CI->getNextNode()); + }, + &Attrs); + } +} + +void +OCL20ToSPIRV::visitCallAtomicWorkItemFence(CallInst* CI) { + transMemoryBarrier(CI, getAtomicWorkItemFenceLiterals(CI)); +} + +void +OCL20ToSPIRV::visitCallMemFence(CallInst* CI) { + transMemoryBarrier(CI, std::make_tuple( + cast(CI->getArgOperand(0))->getZExtValue(), + OCLMO_relaxed, + OCLMS_work_group)); +} + +void OCL20ToSPIRV::transMemoryBarrier(CallInst* CI, + AtomicWorkItemFenceLiterals Lit) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ + Args.resize(2); + Args[0] = addInt32(map(std::get<2>(Lit))); + Args[1] = addInt32(mapOCLMemSemanticToSPIRV(std::get<0>(Lit), + std::get<1>(Lit))); + return getSPIRVFuncName(OpMemoryBarrier); + }, &Attrs); +} + +void +OCL20ToSPIRV::visitCallAtomicLegacy(CallInst* CI, + StringRef MangledName, const std::string& DemangledName) { + StringRef Stem = DemangledName; + if (Stem.startswith("atom_")) + Stem = Stem.drop_front(strlen("atom_")); + else if (Stem.startswith("atomic_")) + Stem = Stem.drop_front(strlen("atomic_")); + else + return; + + std::string Sign; + std::string Postfix; + std::string Prefix; + if (Stem == "add" || + Stem == "sub" || + Stem == "and" || + Stem == "or" || + Stem == "xor" || + Stem == "min" || + Stem == "max") { + if ((Stem == "min" || Stem == "max") && + isMangledTypeUnsigned(MangledName.back())) + Sign = 'u'; + Prefix = "fetch_"; + Postfix = "_explicit"; + } else if (Stem == "xchg") { + Stem = "exchange"; + Postfix = "_explicit"; + } + else if (Stem == "cmpxchg") { + Stem = "compare_exchange_strong"; + Postfix = "_explicit"; + } + else if (Stem == "inc" || + Stem == "dec") { + // do nothing + } else + return; + + OCLBuiltinTransInfo Info; + Info.UniqName = "atomic_" + Prefix + Sign + Stem.str() + Postfix; + std::vector PostOps; + PostOps.push_back(OCLLegacyAtomicMemOrder); + if (Stem.startswith("compare_exchange")) + PostOps.push_back(OCLLegacyAtomicMemOrder); + PostOps.push_back(OCLLegacyAtomicMemScope); + + Info.PostProc = [=](std::vector &Ops){ + for (auto &I:PostOps){ + Ops.push_back(addInt32(I)); + } + }; + transAtomicBuiltin(CI, Info); +} + +void +OCL20ToSPIRV::visitCallAtomicCpp11(CallInst* CI, + StringRef MangledName, const std::string& DemangledName) { + StringRef Stem = DemangledName; + if (Stem.startswith("atomic_")) + Stem = Stem.drop_front(strlen("atomic_")); + else + return; + + std::string NewStem = Stem; + std::vector PostOps; + if (Stem.startswith("store") || + Stem.startswith("load") || + Stem.startswith("exchange") || + Stem.startswith("compare_exchange") || + Stem.startswith("fetch") || + Stem.startswith("flag")) { + if ((Stem.startswith("fetch_min") || + Stem.startswith("fetch_max")) && + containsUnsignedAtomicType(MangledName)) + NewStem.insert(NewStem.begin() + strlen("fetch_"), 'u'); + + if (!Stem.endswith("_explicit")) { + NewStem = NewStem + "_explicit"; + PostOps.push_back(OCLMO_seq_cst); + if (Stem.startswith("compare_exchange")) + PostOps.push_back(OCLMO_seq_cst); + PostOps.push_back(OCLMS_device); + } else { + auto MaxOps = getOCLCpp11AtomicMaxNumOps( + Stem.drop_back(strlen("_explicit"))); + if (CI->getNumArgOperands() < MaxOps) + PostOps.push_back(OCLMS_device); + } + } else if (Stem == "work_item_fence") { + // do nothing + } else + return; + + OCLBuiltinTransInfo Info; + Info.UniqName = std::string("atomic_") + NewStem; + Info.PostProc = [=](std::vector &Ops){ + for (auto &I:PostOps){ + Ops.push_back(addInt32(I)); + } + }; + + transAtomicBuiltin(CI, Info); +} + +void +OCL20ToSPIRV::transAtomicBuiltin(CallInst* CI, + OCLBuiltinTransInfo& Info) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV(M, CI, [=](CallInst * CI, std::vector &Args){ + Info.PostProc(Args); + // Order of args in OCL20: + // object, 0-2 other args, 1-2 order, scope + const size_t NumOrder = getAtomicBuiltinNumMemoryOrderArgs(Info.UniqName); + const size_t ArgsCount = Args.size(); + const size_t ScopeIdx = ArgsCount - 1; + const size_t OrderIdx = ScopeIdx - NumOrder; + Args[ScopeIdx] = mapUInt(M, cast(Args[ScopeIdx]), + [](unsigned I){ + return map(static_cast(I)); + }); + for (size_t I = 0; I < NumOrder; ++I) + Args[OrderIdx + I] = mapUInt(M, cast(Args[OrderIdx + I]), + [](unsigned Ord) { + return mapOCLMemSemanticToSPIRV(0, static_cast(Ord)); + }); + // Order of args in SPIR-V: + // object, scope, 1-2 order, 0-2 other args + std::swap(Args[1], Args[ScopeIdx]); + if(OrderIdx > 2) { + // For atomic_compare_exchange the swap above puts Comparator/Expected + // argument just where it should be, so don't move the last argument then. + int offset = Info.UniqName.find("atomic_compare_exchange") == 0 ? 1 : 0; + std::rotate(Args.begin() + 2, Args.begin() + OrderIdx, + Args.end() - offset); + } + return getSPIRVFuncName(OCLSPIRVBuiltinMap::map(Info.UniqName)); + }, &Attrs); +} + +void +OCL20ToSPIRV::visitCallWorkGroupBarrier(CallInst* CI) { + auto Lit = getWorkGroupBarrierLiterals(CI); + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ + Args.resize(3); + Args[0] = addInt32(map(std::get<2>(Lit))); + Args[1] = addInt32(map(std::get<1>(Lit))); + Args[2] = addInt32(mapOCLMemFenceFlagToSPIRV(std::get<0>(Lit))); + return getSPIRVFuncName(OpControlBarrier); + }, &Attrs); +} + +void OCL20ToSPIRV::visitCallConvert(CallInst* CI, + StringRef MangledName, const std::string& DemangledName) { + if (eraseUselessConvert(CI, MangledName, DemangledName)) + return; + Op OC = OpNop; + auto TargetTy = CI->getType(); + auto SrcTy = CI->getArgOperand(0)->getType(); + if (isa(TargetTy)) + TargetTy = TargetTy->getVectorElementType(); + if (isa(SrcTy)) + SrcTy = SrcTy->getVectorElementType(); + auto IsTargetInt = isa(TargetTy); + + std::string TargetTyName = DemangledName.substr( + strlen(kOCLBuiltinName::ConvertPrefix)); + auto FirstUnderscoreLoc = TargetTyName.find('_'); + if (FirstUnderscoreLoc != std::string::npos) + TargetTyName = TargetTyName.substr(0, FirstUnderscoreLoc); + TargetTyName = std::string("_R") + TargetTyName; + + std::string Sat = DemangledName.find("_sat") != std::string::npos ? + "_sat" : ""; + auto TargetSigned = DemangledName[8] != 'u'; + if (isa(SrcTy)) { + bool Signed = isLastFuncParamSigned(MangledName); + if (IsTargetInt) { + if (!Sat.empty() && TargetSigned != Signed) { + OC = Signed ? OpSatConvertSToU : OpSatConvertUToS; + Sat = ""; + } else + OC = Signed ? OpSConvert : OpUConvert; + } else + OC = Signed ? OpConvertSToF : OpConvertUToF; + } else { + if (IsTargetInt) { + OC = TargetSigned ? OpConvertFToS : OpConvertFToU; + } else + OC = OpFConvert; + } + auto Loc = DemangledName.find("_rt"); + std::string Rounding; + if (Loc != std::string::npos && + !(isa(SrcTy) && IsTargetInt)) { + Rounding = DemangledName.substr(Loc, 4); + } + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args){ + return getSPIRVFuncName(OC, TargetTyName + Sat + Rounding); + }, &Attrs); +} + +void OCL20ToSPIRV::visitCallGroupBuiltin(CallInst* CI, + StringRef MangledName, const std::string& OrigDemangledName) { + auto F = CI->getCalledFunction(); + std::vector PreOps; + std::string DemangledName = OrigDemangledName; + + if (DemangledName == kOCLBuiltinName::WorkGroupBarrier) + return; + if (DemangledName == kOCLBuiltinName::WaitGroupEvent) { + PreOps.push_back(ScopeWorkgroup); + } else if (DemangledName.find(kOCLBuiltinName::WorkGroupPrefix) == 0) { + DemangledName.erase(0, strlen(kOCLBuiltinName::WorkPrefix)); + PreOps.push_back(ScopeWorkgroup); + } else if (DemangledName.find(kOCLBuiltinName::SubGroupPrefix) == 0) { + DemangledName.erase(0, strlen(kOCLBuiltinName::SubPrefix)); + PreOps.push_back(ScopeSubgroup); + } else + return; + + if (DemangledName != kOCLBuiltinName::WaitGroupEvent) { + StringRef GroupOp = DemangledName; + GroupOp = GroupOp.drop_front(strlen(kSPIRVName::GroupPrefix)); + SPIRSPIRVGroupOperationMap::foreach_conditional([&](const std::string &S, + SPIRVGroupOperationKind G){ + if (!GroupOp.startswith(S)) + return true; // continue + PreOps.push_back(G); + StringRef Op = GroupOp.drop_front(S.size() + 1); + assert(!Op.empty() && "Invalid OpenCL group builtin function"); + char OpTyC = 0; + auto NeedSign = Op == "max" || Op == "min"; + auto OpTy = F->getReturnType(); + if (OpTy->isFloatingPointTy()) + OpTyC = 'f'; + else if (OpTy->isIntegerTy()) { + if (!NeedSign) + OpTyC = 'i'; + else { + if (isLastFuncParamSigned(F->getName())) + OpTyC = 's'; + else + OpTyC = 'u'; + } + } else + llvm_unreachable("Invalid OpenCL group builtin argument type"); + + DemangledName = std::string(kSPIRVName::GroupPrefix) + OpTyC + Op.str(); + return false; // break out of loop + }); + } + + bool IsGroupAllAny = (DemangledName.find("_all") != std::string::npos || + DemangledName.find("_any") != std::string::npos); + + auto Consts = getInt32(M, PreOps); + OCLBuiltinTransInfo Info; + if (IsGroupAllAny) + Info.RetTy = Type::getInt1Ty(*Ctx); + Info.UniqName = DemangledName; + Info.PostProc = [=](std::vector &Ops) { + if (IsGroupAllAny) { + IRBuilder<> IRB(CI); + Ops[0] = + IRB.CreateICmpNE(Ops[0], ConstantInt::get(Type::getInt32Ty(*Ctx), 0)); + } + size_t E = Ops.size(); + if (DemangledName == "group_broadcast" && E > 2) { + assert(E == 3 || E == 4); + makeVector(CI, Ops, std::make_pair(Ops.begin() + 1, Ops.end())); + } + Ops.insert(Ops.begin(), Consts.begin(), Consts.end()); + }; + transBuiltin(CI, Info); +} + +void +OCL20ToSPIRV::transBuiltin(CallInst* CI, + OCLBuiltinTransInfo& Info) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + Op OC = OpNop; + unsigned ExtOp = ~0U; + if (StringRef(Info.UniqName).startswith(kSPIRVName::Prefix)) + return; + if (OCLSPIRVBuiltinMap::find(Info.UniqName, &OC)) + Info.UniqName = getSPIRVFuncName(OC); + else if ((ExtOp = getExtOp(Info.MangledName, Info.UniqName)) != ~0U) + Info.UniqName = getSPIRVExtFuncName(SPIRVEIS_OpenCL, ExtOp); + else + return; + if (!Info.RetTy) + mutateCallInstSPIRV(M, CI, + [=](CallInst *, std::vector &Args) { + Info.PostProc(Args); + return Info.UniqName + Info.Postfix; + }, + &Attrs); + else + mutateCallInstSPIRV( + M, CI, + [=](CallInst *, std::vector &Args, Type *&RetTy) { + Info.PostProc(Args); + RetTy = Info.RetTy; + return Info.UniqName + Info.Postfix; + }, + [=](CallInst *NewCI) -> Instruction * { + if (NewCI->getType()->isIntegerTy() && CI->getType()->isIntegerTy()) + return CastInst::CreateIntegerCast(NewCI, CI->getType(), + Info.isRetSigned, "", CI); + else + return CastInst::CreatePointerBitCastOrAddrSpaceCast( + NewCI, CI->getType(), "", CI); + }, + &Attrs); +} + +void +OCL20ToSPIRV::visitCallPipeBuiltin(CallInst* CI, + StringRef MangledName, const std::string& DemangledName) { + std::string NewName = DemangledName; + // Transform OpenCL read_pipe/write_pipe builtin function names + // with reserve_id argument to reserved_read_pipe/reserved_write_pipe. + if ((DemangledName.find(kOCLBuiltinName::ReadPipe) == 0 || + DemangledName.find(kOCLBuiltinName::WritePipe) == 0) + && CI->getNumArgOperands() > 4) + NewName = std::string(kSPIRVName::ReservedPrefix) + DemangledName; + OCLBuiltinTransInfo Info; + Info.UniqName = NewName; + transBuiltin(CI, Info); +} + +void OCL20ToSPIRV::visitCallReadImageMSAA(CallInst *CI, StringRef MangledName, + const std::string &DemangledName) { + assert(MangledName.find("msaa") != StringRef::npos); + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV( + M, CI, + [=](CallInst *, std::vector &Args) { + Args.insert(Args.begin() + 2, getInt32(M, ImageOperandsSampleMask)); + return getSPIRVFuncName(OpImageRead, + std::string(kSPIRVPostfix::ExtDivider) + + getPostfixForReturnType(CI)); + }, + &Attrs); +} + +void OCL20ToSPIRV::visitCallReadImageWithSampler( + CallInst *CI, StringRef MangledName, const std::string &DemangledName) { + assert (MangledName.find(kMangledName::Sampler) != StringRef::npos); + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + bool isRetScalar = !CI->getType()->isVectorTy(); + mutateCallInstSPIRV( + M, CI, + [=](CallInst *, std::vector &Args, Type *&Ret) { + auto ImageTy = getAnalysis().getAdaptedType(Args[0]); + if (isOCLImageType(ImageTy)) + ImageTy = getSPIRVImageTypeFromOCL(M, ImageTy); + auto SampledImgTy = getSPIRVTypeByChangeBaseTypeName( + M, ImageTy, kSPIRVTypeName::Image, kSPIRVTypeName::SampledImg); + Value *SampledImgArgs[] = {Args[0], Args[1]}; + auto SampledImg = addCallInstSPIRV( + M, getSPIRVFuncName(OpSampledImage), SampledImgTy, SampledImgArgs, + nullptr, CI, kSPIRVName::TempSampledImage); + + Args[0] = SampledImg; + Args.erase(Args.begin() + 1, Args.begin() + 2); + + switch (Args.size()) { + case 2: // no lod + Args.push_back(getInt32(M, ImageOperandsMask::ImageOperandsLodMask)); + Args.push_back(getFloat32(M, 0.f)); + break; + case 3: // explicit lod + Args.insert(Args.begin() + 2, + getInt32(M, ImageOperandsMask::ImageOperandsLodMask)); + break; + case 4: // gradient + Args.insert(Args.begin() + 2, + getInt32(M, ImageOperandsMask::ImageOperandsGradMask)); + break; + default: + assert(0 && "read_image* with unhandled number of args!"); + } + + // SPIR-V intruction always returns 4-element vector + if (isRetScalar) + Ret = VectorType::get(Ret, 4); + return getSPIRVFuncName(OpImageSampleExplicitLod, + std::string(kSPIRVPostfix::ExtDivider) + + getPostfixForReturnType(Ret)); + }, + [&](CallInst *CI) -> Instruction * { + if (isRetScalar) + return ExtractElementInst::Create(CI, getSizet(M, 0), "", + CI->getNextNode()); + return CI; + }, + &Attrs); +} + +void +OCL20ToSPIRV::visitCallGetImageSize(CallInst* CI, + StringRef MangledName, const std::string& DemangledName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + StringRef TyName; + auto IsImg = isOCLImageType(CI->getArgOperand(0)->getType(), &TyName); + assert(IsImg); + SPIRVTypeImageDescriptor Desc = map(TyName.str()); + unsigned Dim = getImageDimension(Desc.Dim) + Desc.Arrayed; + assert(Dim > 0 && "Ivalid image dimention."); + mutateCallInstSPIRV(M, CI, + [&](CallInst *, std::vector &Args, Type *&Ret){ + assert(Args.size() == 1); + Ret = CI->getType()->isIntegerTy(64) ? Type::getInt64Ty(*Ctx) + : Type::getInt32Ty(*Ctx); + if (Dim > 1) + Ret = VectorType::get(Ret, Dim); + if (Desc.Dim == DimBuffer) + return getSPIRVFuncName(OpImageQuerySize, CI->getType()); + else { + Args.push_back(getInt32(M, 0)); + return getSPIRVFuncName(OpImageQuerySizeLod, CI->getType()); + } + }, + [&](CallInst *NCI)->Instruction * { + if (Dim == 1) + return NCI; + if (DemangledName == kOCLBuiltinName::GetImageDim) { + if (Desc.Dim == Dim3D) { + auto ZeroVec = ConstantVector::getSplat(3, + Constant::getNullValue(NCI->getType()->getVectorElementType())); + Constant *Index[] = {getInt32(M, 0), getInt32(M, 1), + getInt32(M, 2), getInt32(M, 3)}; + return new ShuffleVectorInst(NCI, ZeroVec, + ConstantVector::get(Index), "", CI); + + } else if (Desc.Dim == Dim2D && Desc.Arrayed) { + Constant *Index[] = {getInt32(M, 0), getInt32(M, 1)}; + Constant *mask = ConstantVector::get(Index); + return new ShuffleVectorInst(NCI, UndefValue::get(NCI->getType()), + mask, NCI->getName(), CI); + } + return NCI; + } + unsigned I = StringSwitch(DemangledName) + .Case(kOCLBuiltinName::GetImageWidth, 0) + .Case(kOCLBuiltinName::GetImageHeight, 1) + .Case(kOCLBuiltinName::GetImageDepth, 2) + .Case(kOCLBuiltinName::GetImageArraySize, Dim - 1); + return ExtractElementInst::Create(NCI, getUInt32(M, I), "", + NCI->getNextNode()); + }, + &Attrs); +} + +/// Remove trivial conversion functions +bool +OCL20ToSPIRV::eraseUselessConvert(CallInst *CI, + const std::string &MangledName, + const std::string &DemangledName) { + auto TargetTy = CI->getType(); + auto SrcTy = CI->getArgOperand(0)->getType(); + if (isa(TargetTy)) + TargetTy = TargetTy->getVectorElementType(); + if (isa(SrcTy)) + SrcTy = SrcTy->getVectorElementType(); + if (TargetTy == SrcTy) { + if (isa(TargetTy) && + DemangledName.find("_sat") != std::string::npos && + isLastFuncParamSigned(MangledName) != (DemangledName[8] != 'u')) + return false; + CI->getArgOperand(0)->takeName(CI); + SPIRVDBG(dbgs() << "[regularizeOCLConvert] " << *CI << " <- " << + *CI->getArgOperand(0) << '\n'); + CI->replaceAllUsesWith(CI->getArgOperand(0)); + ValuesToDelete.insert(CI); + ValuesToDelete.insert(CI->getCalledFunction()); + return true; + } + return false; +} + +void +OCL20ToSPIRV::visitCallBuiltinSimple(CallInst* CI, + StringRef MangledName, const std::string& DemangledName) { + OCLBuiltinTransInfo Info; + Info.MangledName = MangledName.str(); + Info.UniqName = DemangledName; + transBuiltin(CI, Info); +} + +/// Translates OCL work-item builtin functions to SPIRV builtin variables. +/// Function like get_global_id(i) -> x = load GlobalInvocationId; extract x, i +/// Function like get_work_dim() -> load WorkDim +void OCL20ToSPIRV::transWorkItemBuiltinsToVariables() { + DEBUG(dbgs() << "Enter transWorkItemBuiltinsToVariables\n"); + std::vector WorkList; + for (auto I = M->begin(), E = M->end(); I != E; ++I) { + std::string DemangledName; + if (!oclIsBuiltin(I->getName(), &DemangledName)) + continue; + DEBUG(dbgs() << "Function demangled name: " << DemangledName << '\n'); + std::string BuiltinVarName; + SPIRVBuiltinVariableKind BVKind; + if (!SPIRSPIRVBuiltinVariableMap::find(DemangledName, &BVKind)) + continue; + BuiltinVarName = std::string(kSPIRVName::Prefix) + + SPIRVBuiltInNameMap::map(BVKind); + DEBUG(dbgs() << "builtin variable name: " << BuiltinVarName << '\n'); + bool IsVec = I->getFunctionType()->getNumParams() > 0; + Type *GVType = IsVec ? VectorType::get(I->getReturnType(),3) : + I->getReturnType(); + auto BV = new GlobalVariable(*M, GVType, + true, + GlobalValue::ExternalLinkage, + nullptr, BuiltinVarName, + 0, + GlobalVariable::NotThreadLocal, + SPIRAS_Constant); + std::vector InstList; + for (auto UI = I->user_begin(), UE = I->user_end(); UI != UE; ++UI) { + auto CI = dyn_cast(*UI); + assert(CI && "invalid instruction"); + Value * NewValue = new LoadInst(BV, "", CI); + DEBUG(dbgs() << "Transform: " << *CI << " => " << *NewValue << '\n'); + if (IsVec) { + NewValue = ExtractElementInst::Create(NewValue, + CI->getArgOperand(0), + "", CI); + DEBUG(dbgs() << *NewValue << '\n'); + } + NewValue->takeName(CI); + CI->replaceAllUsesWith(NewValue); + InstList.push_back(CI); + } + for (auto &Inst:InstList) { + Inst->dropAllReferences(); + Inst->removeFromParent(); + } + WorkList.push_back(&(*I)); + } + for (auto &I:WorkList) { + I->dropAllReferences(); + I->removeFromParent(); + } +} + +void +OCL20ToSPIRV::visitCallReadWriteImage(CallInst* CI, + StringRef MangledName, const std::string& DemangledName) { + OCLBuiltinTransInfo Info; + if (DemangledName.find(kOCLBuiltinName::ReadImage) == 0) + Info.UniqName = kOCLBuiltinName::ReadImage; + + if (DemangledName.find(kOCLBuiltinName::WriteImage) == 0) + { + Info.UniqName = kOCLBuiltinName::WriteImage; + Info.PostProc = [&](std::vector &Args) { + if (Args.size() == 4) // write with lod + { + auto Lod = Args[2]; + Args.erase(Args.begin() + 2); + Args.push_back(getInt32(M, ImageOperandsMask::ImageOperandsLodMask)); + Args.push_back(Lod); + } + }; + } + + transBuiltin(CI, Info); +} + +void +OCL20ToSPIRV::visitCallToAddr(CallInst* CI, StringRef MangledName, + const std::string &DemangledName) { + auto AddrSpace = static_cast( + CI->getType()->getPointerAddressSpace()); + OCLBuiltinTransInfo Info; + Info.UniqName = DemangledName; + Info.Postfix = std::string(kSPIRVPostfix::Divider) + "To" + + SPIRAddrSpaceCapitalizedNameMap::map(AddrSpace); + auto StorageClass = addInt32(SPIRSPIRVAddrSpaceMap::map(AddrSpace)); + Info.RetTy = getInt8PtrTy(cast(CI->getType())); + Info.PostProc = [=](std::vector &Ops){ + auto P = Ops.back(); + Ops.pop_back(); + Ops.push_back(castToInt8Ptr(P, CI)); + Ops.push_back(StorageClass); + }; + transBuiltin(CI, Info); +} + +void OCL20ToSPIRV::visitCallRelational(CallInst *CI, + const std::string &DemangledName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + Op OC = OpNop; + OCLSPIRVBuiltinMap::find(DemangledName, &OC); + std::string SPIRVName = getSPIRVFuncName(OC); + mutateCallInstSPIRV( + M, CI, + [=](CallInst *, std::vector &Args, Type *&Ret) { + Ret = Type::getInt1Ty(*Ctx); + if (CI->getOperand(0)->getType()->isVectorTy()) + Ret = VectorType::get( + Type::getInt1Ty(*Ctx), + CI->getOperand(0)->getType()->getVectorNumElements()); + return SPIRVName; + }, + [=](CallInst *NewCI) -> Instruction * { + Value *False = nullptr, *True = nullptr; + if (NewCI->getType()->isVectorTy()) { + Type *IntTy = Type::getInt32Ty(*Ctx); + if (cast(NewCI->getOperand(0)->getType()) + ->getElementType() + ->isDoubleTy()) + IntTy = Type::getInt64Ty(*Ctx); + if (cast(NewCI->getOperand(0)->getType()) + ->getElementType() + ->isHalfTy()) + IntTy = Type::getInt16Ty(*Ctx); + Type *VTy = VectorType::get(IntTy, + NewCI->getType()->getVectorNumElements()); + False = Constant::getNullValue(VTy); + True = Constant::getAllOnesValue(VTy); + } else { + False = getInt32(M, 0); + True = getInt32(M, 1); + } + return SelectInst::Create(NewCI, True, False, "", NewCI->getNextNode()); + }, + &Attrs); +} + +void +OCL20ToSPIRV::visitCallVecLoadStore(CallInst* CI, + StringRef MangledName, const std::string& OrigDemangledName) { + std::vector PreOps; + std::string DemangledName = OrigDemangledName; + if (DemangledName.find(kOCLBuiltinName::VLoadPrefix) == 0 && + DemangledName != kOCLBuiltinName::VLoadHalf) { + SPIRVWord Width = getVecLoadWidth(DemangledName); + SPIRVDBG(spvdbgs() << "[visitCallVecLoadStore] DemangledName: " << + DemangledName << " Width: " << Width << '\n'); + PreOps.push_back(Width); + } else if (DemangledName.find(kOCLBuiltinName::RoundingPrefix) + != std::string::npos) { + auto R = SPIRSPIRVFPRoundingModeMap::map(DemangledName.substr( + DemangledName.find(kOCLBuiltinName::RoundingPrefix) + 1, 3)); + PreOps.push_back(R); + } + + if (DemangledName.find(kOCLBuiltinName::VLoadAPrefix) == 0) + transVecLoadStoreName(DemangledName, kOCLBuiltinName::VLoadAPrefix, true); + else + transVecLoadStoreName(DemangledName, kOCLBuiltinName::VLoadPrefix, false); + + if (DemangledName.find(kOCLBuiltinName::VStoreAPrefix) == 0) + transVecLoadStoreName(DemangledName, kOCLBuiltinName::VStoreAPrefix, true); + else + transVecLoadStoreName(DemangledName, kOCLBuiltinName::VStorePrefix, false); + + + auto Consts = getInt32(M, PreOps); + OCLBuiltinTransInfo Info; + Info.MangledName = MangledName; + Info.UniqName = DemangledName; + if (DemangledName.find(kOCLBuiltinName::VLoadPrefix) == 0) + Info.Postfix = std::string(kSPIRVPostfix::ExtDivider) + + getPostfixForReturnType(CI); + Info.PostProc = [=](std::vector &Ops){ + Ops.insert(Ops.end(), Consts.begin(), Consts.end()); + }; + transBuiltin(CI, Info); +} + +void OCL20ToSPIRV::visitCallGetFence(CallInst *CI, StringRef MangledName, + const std::string &DemangledName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + Op OC = OpNop; + OCLSPIRVBuiltinMap::find(DemangledName, &OC); + std::string SPIRVName = getSPIRVFuncName(OC); + mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args, + Type *&Ret) { return SPIRVName; }, + [=](CallInst *NewCI) -> Instruction * { + return BinaryOperator::CreateLShr(NewCI, getInt32(M, 8), "", CI); + }, + &Attrs); +} + +void OCL20ToSPIRV::visitCallDot(CallInst *CI) { + IRBuilder<> Builder(CI); + Value *FMulVal = Builder.CreateFMul(CI->getOperand(0), CI->getOperand(1)); + CI->replaceAllUsesWith(FMulVal); + CI->dropAllReferences(); + CI->removeFromParent(); +} + +void OCL20ToSPIRV::visitCallScalToVec(CallInst *CI, StringRef MangledName, + const std::string &DemangledName) { + // Check if all arguments have the same type - it's simple case. + auto Uniform = true; + auto IsArg0Vector = isa(CI->getOperand(0)->getType()); + for (unsigned I = 1, E = CI->getNumArgOperands(); Uniform && (I != E); ++I) { + Uniform = isa(CI->getOperand(I)->getType()) == IsArg0Vector; + } + if (Uniform) { + visitCallBuiltinSimple(CI, MangledName, DemangledName); + return; + } + + std::vector VecPos; + std::vector ScalarPos; + if (DemangledName == kOCLBuiltinName::FMin || + DemangledName == kOCLBuiltinName::FMax || + DemangledName == kOCLBuiltinName::Min || + DemangledName == kOCLBuiltinName::Max) { + VecPos.push_back(0); + ScalarPos.push_back(1); + } else if (DemangledName == kOCLBuiltinName::Clamp) { + VecPos.push_back(0); + ScalarPos.push_back(1); + ScalarPos.push_back(2); + } else if (DemangledName == kOCLBuiltinName::Mix) { + VecPos.push_back(0); + VecPos.push_back(1); + ScalarPos.push_back(2); + } else if (DemangledName == kOCLBuiltinName::Step) { + VecPos.push_back(1); + ScalarPos.push_back(0); + } else if (DemangledName == kOCLBuiltinName::SmoothStep) { + VecPos.push_back(2); + ScalarPos.push_back(0); + ScalarPos.push_back(1); + } + + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + mutateCallInstSPIRV( + M, CI, + [=](CallInst *, std::vector &Args) { + Args.resize(VecPos.size() + ScalarPos.size()); + for (auto I : VecPos) { + Args[I] = CI->getOperand(I); + } + auto VecArgWidth = + CI->getOperand(VecPos[0])->getType()->getVectorNumElements(); + for (auto I : ScalarPos) { + Instruction *Inst = InsertElementInst::Create( + UndefValue::get(CI->getOperand(VecPos[0])->getType()), + CI->getOperand(I), getInt32(M, 0), "", CI); + Value *NewVec = new ShuffleVectorInst( + Inst, UndefValue::get(CI->getOperand(VecPos[0])->getType()), + ConstantVector::getSplat(VecArgWidth, getInt32(M, 0)), "", CI); + + Args[I] = NewVec; + } + return getSPIRVExtFuncName(SPIRVEIS_OpenCL, + getExtOp(MangledName, DemangledName)); + }, + &Attrs); +} + +void OCL20ToSPIRV::visitCallGetImageChannel(CallInst *CI, StringRef MangledName, + const std::string &DemangledName, + unsigned int Offset) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + Op OC = OpNop; + OCLSPIRVBuiltinMap::find(DemangledName, &OC); + std::string SPIRVName = getSPIRVFuncName(OC); + mutateCallInstSPIRV(M, CI, [=](CallInst *, std::vector &Args, + Type *&Ret) { return SPIRVName; }, + [=](CallInst *NewCI) -> Instruction * { + return BinaryOperator::CreateAdd( + NewCI, getInt32(M, Offset), "", CI); + }, + &Attrs); +} +} + +INITIALIZE_PASS_BEGIN(OCL20ToSPIRV, "cl20tospv", "Transform OCL 2.0 to SPIR-V", + false, false) +INITIALIZE_PASS_DEPENDENCY(OCLTypeToSPIRV) +INITIALIZE_PASS_END(OCL20ToSPIRV, "cl20tospv", "Transform OCL 2.0 to SPIR-V", + false, false) + +ModulePass *llvm::createOCL20ToSPIRV() { + return new OCL20ToSPIRV(); +} diff --git a/OCLTypeToSPIRV.cpp b/OCLTypeToSPIRV.cpp index be58ff1..5784197 100644 --- a/OCLTypeToSPIRV.cpp +++ b/OCLTypeToSPIRV.cpp @@ -125,7 +125,7 @@ static unsigned getArgIndex(Function *F, Value *V) { auto A = F->arg_begin(), E = F->arg_end(); for (unsigned I = 0; A != E; ++I, ++A) { - if (A == V) + if (&*A == V) return I; } llvm_unreachable("Not argument of function"); @@ -136,7 +136,7 @@ static Argument* getArg(Function *F, unsigned I) { auto AI = F->arg_begin(); std::advance(AI, I); - return AI; + return &*AI; } /// Create a new function type if \param F has arguments in AdaptedTy, and @@ -291,7 +291,7 @@ OCLTypeToSPIRV::adaptArgumentsByMetadata(Function* F) { auto OCLTyStr = getMDOperandAsString(TypeMD, I); auto NewTy = *PI; if (OCLTyStr == OCL_TYPE_NAME_SAMPLER_T && !NewTy->isStructTy()) { - addAdaptedType(Arg, getSamplerType(M)); + addAdaptedType(&*Arg, getSamplerType(M)); Changed = true; } else if (isPointerToOpaqueStructType(NewTy)) { auto STName = NewTy->getPointerElementType()->getStructName(); @@ -301,7 +301,7 @@ OCLTypeToSPIRV::adaptArgumentsByMetadata(Function* F) { auto AccMD = getArgAccessQualifierMetadata(F); assert(AccMD && "Invalid access qualifier metadata"); auto AccStr = getMDOperandAsString(AccMD, I); - addAdaptedType(Arg, getOrCreateOpaquePtrType(M, + addAdaptedType(&*Arg, getOrCreateOpaquePtrType(M, mapOCLTypeNameToSPIRV(Ty, AccStr))); Changed = true; } diff --git a/SPIRVLowerConstExpr.cpp b/SPIRVLowerConstExpr.cpp index 82796a0..1d9b58f 100644 --- a/SPIRVLowerConstExpr.cpp +++ b/SPIRVLowerConstExpr.cpp @@ -120,7 +120,7 @@ SPIRVLowerConstExpr::visit(Module *M) { auto FBegin = I->begin(); for (auto BI = FBegin, BE = I->end(); BI != BE; ++BI) { for (auto II = BI->begin(), IE = BI->end(); II != IE; ++II) { - WorkList.push_back(II); + WorkList.push_back(&*II); } } while (!WorkList.empty()) { @@ -132,7 +132,7 @@ SPIRVLowerConstExpr::visit(Module *M) { if (auto CE = dyn_cast(Op)) { SPIRVDBG(dbgs() << "[lowerConstantExpressions] " << *CE;) auto ReplInst = CE->getAsInstruction(); - ReplInst->insertBefore(FBegin->begin()); + ReplInst->insertBefore(&*FBegin->begin()); SPIRVDBG(dbgs() << " -> " << *ReplInst << '\n';) WorkList.push_front(ReplInst); std::vector Users; @@ -141,7 +141,7 @@ SPIRVLowerConstExpr::visit(Module *M) { SPIRVDBG(dbgs() << "[lowerConstantExpressions] Use: " << *U << '\n';) if (auto InstUser = dyn_cast(U)) { - if (InstUser->getParent()->getParent() != I) + if (InstUser->getParent()->getParent() != &*I) continue; Users.push_back(InstUser); } diff --git a/SPIRVLowerOCLBlocks.cpp b/SPIRVLowerOCLBlocks.cpp index 24405cd..2cf9aff 100644 --- a/SPIRVLowerOCLBlocks.cpp +++ b/SPIRVLowerOCLBlocks.cpp @@ -1,471 +1,476 @@ -//===- SPIRVLowerOCLBlocks.cpp - Lower OpenCL blocks ------------*- C++ -*-===// -// -// The LLVM/SPIR-V Translator -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. -// -//===----------------------------------------------------------------------===// -/// \file -/// -/// This file implements lowering of OpenCL blocks to functions. -/// -//===----------------------------------------------------------------------===// - -#ifndef OCLLOWERBLOCKS_H_ -#define OCLLOWERBLOCKS_H_ - -#include "SPIRVInternal.h" -#include "OCLUtil.h" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SetVector.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/ADT/Triple.h" -#include "llvm/Analysis/AliasAnalysis.h" -#include "llvm/Analysis/AssumptionCache.h" -#include "llvm/Analysis/CallGraph.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Bitcode/ReaderWriter.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/InstrTypes.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Operator.h" -#include "llvm/Pass.h" -#include "llvm/PassSupport.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/ToolOutputFile.h" -#include "llvm/Transforms/Utils/Cloning.h" - -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "spvblocks" - -using namespace llvm; -using namespace SPIRV; -using namespace OCLUtil; - -namespace SPIRV{ - -/// Lower SPIR2 blocks to function calls. -/// -/// SPIR2 representation of blocks: -/// -/// block = spir_block_bind(bitcast(block_func), context_len, context_align, -/// context) -/// block_func_ptr = bitcast(spir_get_block_invoke(block)) -/// context_ptr = spir_get_block_context(block) -/// ret = block_func_ptr(context_ptr, args) -/// -/// Propagates block_func to each spir_get_block_invoke through def-use chain of -/// spir_block_bind, so that -/// ret = block_func(context, args) -class SPIRVLowerOCLBlocks: public ModulePass { -public: - SPIRVLowerOCLBlocks():ModulePass(ID), M(nullptr){ - initializeSPIRVLowerOCLBlocksPass(*PassRegistry::getPassRegistry()); - } - - virtual void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - AU.addRequired(); - AU.addRequired(); - } - - virtual bool runOnModule(Module &Module) { - M = &Module; - lowerBlockBind(); - lowerGetBlockInvoke(); - lowerGetBlockContext(); - erase(M->getFunction(SPIR_INTRINSIC_GET_BLOCK_INVOKE)); - erase(M->getFunction(SPIR_INTRINSIC_GET_BLOCK_CONTEXT)); - erase(M->getFunction(SPIR_INTRINSIC_BLOCK_BIND)); - DEBUG(dbgs() << "------- After OCLLowerBlocks ------------\n" << - *M << '\n'); - return true; - } - - static char ID; -private: - const static int MaxIter = 1000; - Module *M; - - bool - lowerBlockBind() { - auto F = M->getFunction(SPIR_INTRINSIC_BLOCK_BIND); - if (!F) - return false; - int Iter = MaxIter; - while(lowerBlockBind(F) && Iter > 0){ - Iter--; - DEBUG(dbgs() << "-------------- after iteration " << MaxIter - Iter << - " --------------\n" << *M << '\n'); - } - assert(Iter > 0 && "Too many iterations"); - return true; - } - - bool - eraseUselessFunctions() { - bool changed = false; - for (auto I = M->begin(), E = M->end(); I != E;) { - Function *F = I++; - if (!GlobalValue::isInternalLinkage(F->getLinkage()) && - !F->isDeclaration()) - continue; - - dumpUsers(F, "[eraseUselessFunctions] "); - for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { - auto U = *UI++; - if (auto CE = dyn_cast(U)){ - if (CE->use_empty()) { - CE->dropAllReferences(); - changed = true; - } - } - } - if (F->use_empty()) { - erase(F); - changed = true; - } - } - return changed; - } - - void - lowerGetBlockInvoke() { - if (auto F = M->getFunction(SPIR_INTRINSIC_GET_BLOCK_INVOKE)) { - for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { - auto CI = dyn_cast(*UI++); - assert(CI && "Invalid usage of spir_get_block_invoke"); - lowerGetBlockInvoke(CI); - } - } - } - - void - lowerGetBlockContext() { - if (auto F = M->getFunction(SPIR_INTRINSIC_GET_BLOCK_CONTEXT)) { - for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { - auto CI = dyn_cast(*UI++); - assert(CI && "Invalid usage of spir_get_block_context"); - lowerGetBlockContext(CI); - } - } - } - /// Lower calls of spir_block_bind. - /// Return true if the Module is changed. - bool - lowerBlockBind(Function *BlockBindFunc) { - bool changed = false; - for (auto I = BlockBindFunc->user_begin(), E = BlockBindFunc->user_end(); - I != E;) { - DEBUG(dbgs() << "[lowerBlockBind] " << **I << '\n'); - // Handle spir_block_bind(bitcast(block_func), context_len, - // context_align, context) - auto CallBlkBind = cast(*I++); - Function *InvF = nullptr; - Value *Ctx = nullptr; - Value *CtxLen = nullptr; - Value *CtxAlign = nullptr; - getBlockInvokeFuncAndContext(CallBlkBind, &InvF, &Ctx, &CtxLen, - &CtxAlign); - for (auto II = CallBlkBind->user_begin(), EE = CallBlkBind->user_end(); - II != EE;) { - auto BlkUser = *II++; - SPIRVDBG(dbgs() << " Block user: " << *BlkUser << '\n'); - if (auto Ret = dyn_cast(BlkUser)) { - bool Inlined = false; - changed |= lowerReturnBlock(Ret, CallBlkBind, Inlined); - if (Inlined) - return true; - } else if (auto CI = dyn_cast(BlkUser)){ - auto CallBindF = CI->getCalledFunction(); - auto Name = CallBindF->getName(); - std::string DemangledName; - if (Name == SPIR_INTRINSIC_GET_BLOCK_INVOKE) { - assert(CI->getArgOperand(0) == CallBlkBind); - changed |= lowerGetBlockInvoke(CI, cast(InvF)); - } else if (Name == SPIR_INTRINSIC_GET_BLOCK_CONTEXT) { - assert(CI->getArgOperand(0) == CallBlkBind); - // Handle context_ptr = spir_get_block_context(block) - lowerGetBlockContext(CI, Ctx); - changed = true; +//===- SPIRVLowerOCLBlocks.cpp - Lower OpenCL blocks ------------*- C++ -*-===// +// +// The LLVM/SPIR-V Translator +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file implements lowering of OpenCL blocks to functions. +/// +//===----------------------------------------------------------------------===// + +#ifndef OCLLOWERBLOCKS_H_ +#define OCLLOWERBLOCKS_H_ + +#include "SPIRVInternal.h" +#include "OCLUtil.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/InitializePasses.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/Pass.h" +#include "llvm/PassSupport.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Transforms/Utils/Cloning.h" + +#include +#include +#include +#include +#include +#include + +#define DEBUG_TYPE "spvblocks" + +using namespace llvm; +using namespace SPIRV; +using namespace OCLUtil; + +namespace SPIRV{ + +/// Lower SPIR2 blocks to function calls. +/// +/// SPIR2 representation of blocks: +/// +/// block = spir_block_bind(bitcast(block_func), context_len, context_align, +/// context) +/// block_func_ptr = bitcast(spir_get_block_invoke(block)) +/// context_ptr = spir_get_block_context(block) +/// ret = block_func_ptr(context_ptr, args) +/// +/// Propagates block_func to each spir_get_block_invoke through def-use chain of +/// spir_block_bind, so that +/// ret = block_func(context, args) +class SPIRVLowerOCLBlocks: public ModulePass { +public: + SPIRVLowerOCLBlocks():ModulePass(ID), M(nullptr){ + initializeSPIRVLowerOCLBlocksPass(*PassRegistry::getPassRegistry()); + } + + virtual void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + //AU.addRequired(); + AU.addRequired(); + } + + virtual bool runOnModule(Module &Module) { + M = &Module; + lowerBlockBind(); + lowerGetBlockInvoke(); + lowerGetBlockContext(); + erase(M->getFunction(SPIR_INTRINSIC_GET_BLOCK_INVOKE)); + erase(M->getFunction(SPIR_INTRINSIC_GET_BLOCK_CONTEXT)); + erase(M->getFunction(SPIR_INTRINSIC_BLOCK_BIND)); + DEBUG(dbgs() << "------- After OCLLowerBlocks ------------\n" << + *M << '\n'); + return true; + } + + static char ID; +private: + const static int MaxIter = 1000; + Module *M; + + bool + lowerBlockBind() { + auto F = M->getFunction(SPIR_INTRINSIC_BLOCK_BIND); + if (!F) + return false; + int Iter = MaxIter; + while(lowerBlockBind(F) && Iter > 0){ + Iter--; + DEBUG(dbgs() << "-------------- after iteration " << MaxIter - Iter << + " --------------\n" << *M << '\n'); + } + assert(Iter > 0 && "Too many iterations"); + return true; + } + + bool + eraseUselessFunctions() { + bool changed = false; + for (auto I = M->begin(), E = M->end(); I != E;) { + Function *F = &*I++; + if (!GlobalValue::isInternalLinkage(F->getLinkage()) && + !F->isDeclaration()) + continue; + + dumpUsers(F, "[eraseUselessFunctions] "); + for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { + auto U = *UI++; + if (auto CE = dyn_cast(U)){ + if (CE->use_empty()) { + CE->dropAllReferences(); + changed = true; + } + } + } + if (F->use_empty()) { + erase(F); + changed = true; + } + } + return changed; + } + + void + lowerGetBlockInvoke() { + if (auto F = M->getFunction(SPIR_INTRINSIC_GET_BLOCK_INVOKE)) { + for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { + auto CI = dyn_cast(*UI++); + assert(CI && "Invalid usage of spir_get_block_invoke"); + lowerGetBlockInvoke(CI); + } + } + } + + void + lowerGetBlockContext() { + if (auto F = M->getFunction(SPIR_INTRINSIC_GET_BLOCK_CONTEXT)) { + for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { + auto CI = dyn_cast(*UI++); + assert(CI && "Invalid usage of spir_get_block_context"); + lowerGetBlockContext(CI); + } + } + } + /// Lower calls of spir_block_bind. + /// Return true if the Module is changed. + bool + lowerBlockBind(Function *BlockBindFunc) { + bool changed = false; + for (auto I = BlockBindFunc->user_begin(), E = BlockBindFunc->user_end(); + I != E;) { + DEBUG(dbgs() << "[lowerBlockBind] " << **I << '\n'); + // Handle spir_block_bind(bitcast(block_func), context_len, + // context_align, context) + auto CallBlkBind = cast(*I++); + Function *InvF = nullptr; + Value *Ctx = nullptr; + Value *CtxLen = nullptr; + Value *CtxAlign = nullptr; + getBlockInvokeFuncAndContext(CallBlkBind, &InvF, &Ctx, &CtxLen, + &CtxAlign); + for (auto II = CallBlkBind->user_begin(), EE = CallBlkBind->user_end(); + II != EE;) { + auto BlkUser = *II++; + SPIRVDBG(dbgs() << " Block user: " << *BlkUser << '\n'); + if (auto Ret = dyn_cast(BlkUser)) { + bool Inlined = false; + changed |= lowerReturnBlock(Ret, CallBlkBind, Inlined); + if (Inlined) + return true; + } else if (auto CI = dyn_cast(BlkUser)){ + auto CallBindF = CI->getCalledFunction(); + auto Name = CallBindF->getName(); + std::string DemangledName; + if (Name == SPIR_INTRINSIC_GET_BLOCK_INVOKE) { + assert(CI->getArgOperand(0) == CallBlkBind); + changed |= lowerGetBlockInvoke(CI, cast(InvF)); + } else if (Name == SPIR_INTRINSIC_GET_BLOCK_CONTEXT) { + assert(CI->getArgOperand(0) == CallBlkBind); + // Handle context_ptr = spir_get_block_context(block) + lowerGetBlockContext(CI, Ctx); + changed = true; } else if (oclIsBuiltin(Name, &DemangledName)) { - lowerBlockBuiltin(CI, InvF, Ctx, CtxLen, CtxAlign, DemangledName); - changed = true; - } else - llvm_unreachable("Invalid block user"); - } - } - erase(CallBlkBind); - } - changed |= eraseUselessFunctions(); - return changed; - } - - void - lowerGetBlockContext(CallInst *CallGetBlkCtx, Value *Ctx = nullptr) { - if (!Ctx) - getBlockInvokeFuncAndContext(CallGetBlkCtx->getArgOperand(0), nullptr, - &Ctx); - CallGetBlkCtx->replaceAllUsesWith(Ctx); - DEBUG(dbgs() << " [lowerGetBlockContext] " << *CallGetBlkCtx << " => " << - *Ctx << "\n\n"); - erase(CallGetBlkCtx); - } - - bool - lowerGetBlockInvoke(CallInst *CallGetBlkInvoke, - Function *InvokeF = nullptr) { - bool changed = false; - for (auto UI = CallGetBlkInvoke->user_begin(), - UE = CallGetBlkInvoke->user_end(); - UI != UE;) { - // Handle block_func_ptr = bitcast(spir_get_block_invoke(block)) - auto CallInv = cast(*UI++); - auto Cast = dyn_cast(CallInv); - if (Cast) - CallInv = dyn_cast(*CallInv->user_begin()); - DEBUG(dbgs() << "[lowerGetBlockInvoke] " << *CallInv); - // Handle ret = block_func_ptr(context_ptr, args) - auto CI = cast(CallInv); - auto F = CI->getCalledValue(); - if (InvokeF == nullptr) { - getBlockInvokeFuncAndContext(CallGetBlkInvoke->getArgOperand(0), - &InvokeF, nullptr); - assert(InvokeF); - } - assert(F->getType() == InvokeF->getType()); - CI->replaceUsesOfWith(F, InvokeF); - DEBUG(dbgs() << " => " << *CI << "\n\n"); - erase(Cast); - changed = true; - } - erase(CallGetBlkInvoke); - return changed; - } - - void - lowerBlockBuiltin(CallInst *CI, Function *InvF, Value *Ctx, Value *CtxLen, - Value *CtxAlign, const std::string& DemangledName) { - mutateCallInstSPIRV (M, CI, [=](CallInst *CI, std::vector &Args) { - size_t I = 0; - size_t E = Args.size(); - for (; I != E; ++I) { - if (isPointerToOpaqueStructType(Args[I]->getType(), - SPIR_TYPE_NAME_BLOCK_T)) { - break; - } - } - assert (I < E); - Args[I] = castToVoidFuncPtr(InvF); - if (I + 1 == E) { - Args.push_back(Ctx); - Args.push_back(CtxLen); - Args.push_back(CtxAlign); - } else { - Args.insert(Args.begin() + I + 1, CtxAlign); - Args.insert(Args.begin() + I + 1, CtxLen); - Args.insert(Args.begin() + I + 1, Ctx); - } - if (DemangledName == kOCLBuiltinName::EnqueueKernel) { - // Insert event arguments if there are not. - if (!isa(Args[3]->getType())) { - Args.insert(Args.begin() + 3, getInt32(M, 0)); - Args.insert(Args.begin() + 4, getOCLNullClkEventPtr()); - } - if (!isOCLClkEventPtrType(Args[5]->getType())) - Args.insert(Args.begin() + 5, getOCLNullClkEventPtr()); - } - return getSPIRVFuncName(OCLSPIRVBuiltinMap::map(DemangledName)); - }); - } - /// Transform return of a block. - /// The function returning a block is inlined since the context cannot be - /// passed to another function. - /// Returns true of module is changed. - bool - lowerReturnBlock(ReturnInst *Ret, Value *CallBlkBind, bool &Inlined) { - auto F = Ret->getParent()->getParent(); - auto changed = false; - for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { - auto U = *UI++; - dumpUsers(U); - auto Inst = dyn_cast(U); - if (Inst && Inst->use_empty()) { - erase(Inst); - changed = true; - continue; - } - auto CI = dyn_cast(U); - if(!CI || CI->getCalledFunction() != F) - continue; - - DEBUG(dbgs() << "[lowerReturnBlock] inline " << F->getName() << '\n'); - auto CG = &getAnalysis().getCallGraph(); - auto ACT = &getAnalysis(); - auto AA = &getAnalysis(); - InlineFunctionInfo IFI(CG, M->getDataLayout(), AA, ACT); - InlineFunction(CI, IFI); - Inlined = true; - } - return changed || Inlined; - } - - void - getBlockInvokeFuncAndContext(Value *Blk, Function **PInvF, Value **PCtx, - Value **PCtxLen = nullptr, Value **PCtxAlign = nullptr){ - Function *InvF = nullptr; - Value *Ctx = nullptr; - Value *CtxLen = nullptr; - Value *CtxAlign = nullptr; - if (auto CallBlkBind = dyn_cast(Blk)) { - assert(CallBlkBind->getCalledFunction()->getName() == - SPIR_INTRINSIC_BLOCK_BIND && "Invalid block"); - InvF = dyn_cast( - CallBlkBind->getArgOperand(0)->stripPointerCasts()); - CtxLen = CallBlkBind->getArgOperand(1); - CtxAlign = CallBlkBind->getArgOperand(2); - Ctx = CallBlkBind->getArgOperand(3); - } else if (auto F = dyn_cast(Blk->stripPointerCasts())) { - InvF = F; - Ctx = Constant::getNullValue(IntegerType::getInt8PtrTy(M->getContext())); - } else if (auto Load = dyn_cast(Blk)) { - auto Op = Load->getPointerOperand(); - if (auto GV = dyn_cast(Op)) { - if (GV->isConstant()) { - InvF = cast(GV->getInitializer()->stripPointerCasts()); - Ctx = Constant::getNullValue(IntegerType::getInt8PtrTy(M->getContext())); - } else { - llvm_unreachable("load non-constant block?"); - } - } else { - llvm_unreachable("Loading block from non global?"); - } - } else { - llvm_unreachable("Invalid block"); - } - DEBUG(dbgs() << " Block invocation func: " << InvF->getName() << '\n' << - " Block context: " << *Ctx << '\n'); - assert(InvF && Ctx && "Invalid block"); - if (PInvF) - *PInvF = InvF; - if (PCtx) - *PCtx = Ctx; - if (PCtxLen) - *PCtxLen = CtxLen; - if (PCtxAlign) - *PCtxAlign = CtxAlign; - } - void - erase(Instruction *I) { - if (!I) - return; - if (I->use_empty()) { - I->dropAllReferences(); - I->eraseFromParent(); - } - else - dumpUsers(I); - } - void - erase(ConstantExpr *I) { - if (!I) - return; - if (I->use_empty()) { - I->dropAllReferences(); - I->destroyConstant(); - } else - dumpUsers(I); - } - void - erase(Function *F) { - if (!F) - return; - if (!F->use_empty()) { - dumpUsers(F); - return; - } - F->dropAllReferences(); - auto &CG = getAnalysis().getCallGraph(); - CG.removeFunctionFromModule(new CallGraphNode(F)); - } - - llvm::PointerType* getOCLClkEventType() { - return getOrCreateOpaquePtrType(M, SPIR_TYPE_NAME_CLK_EVENT_T, - SPIRAS_Global); - } - - llvm::PointerType* getOCLClkEventPtrType() { - return PointerType::get(getOCLClkEventType(), SPIRAS_Generic); - } - - bool isOCLClkEventPtrType(Type *T) { - if (auto PT = dyn_cast(T)) - return isPointerToOpaqueStructType( - PT->getElementType(), SPIR_TYPE_NAME_CLK_EVENT_T); - return false; - } - - llvm::Constant* getOCLNullClkEventPtr() { - return Constant::getNullValue(getOCLClkEventPtrType()); - } - - void dumpGetBlockInvokeUsers(StringRef Prompt) { - DEBUG(dbgs() << Prompt); - dumpUsers(M->getFunction(SPIR_INTRINSIC_GET_BLOCK_INVOKE)); - } -}; - -char SPIRVLowerOCLBlocks::ID = 0; -} - -INITIALIZE_PASS_BEGIN(SPIRVLowerOCLBlocks, "spvblocks", - "SPIR-V lower OCL blocks", false, false) -INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) -INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) -INITIALIZE_AG_DEPENDENCY(AliasAnalysis) -INITIALIZE_PASS_END(SPIRVLowerOCLBlocks, "spvblocks", - "SPIR-V lower OCL blocks", false, false) - -ModulePass *llvm::createSPIRVLowerOCLBlocks() { - return new SPIRVLowerOCLBlocks(); -} - -#endif /* OCLLOWERBLOCKS_H_ */ + lowerBlockBuiltin(CI, InvF, Ctx, CtxLen, CtxAlign, DemangledName); + changed = true; + } else + llvm_unreachable("Invalid block user"); + } + } + erase(CallBlkBind); + } + changed |= eraseUselessFunctions(); + return changed; + } + + void + lowerGetBlockContext(CallInst *CallGetBlkCtx, Value *Ctx = nullptr) { + if (!Ctx) + getBlockInvokeFuncAndContext(CallGetBlkCtx->getArgOperand(0), nullptr, + &Ctx); + CallGetBlkCtx->replaceAllUsesWith(Ctx); + DEBUG(dbgs() << " [lowerGetBlockContext] " << *CallGetBlkCtx << " => " << + *Ctx << "\n\n"); + erase(CallGetBlkCtx); + } + + bool + lowerGetBlockInvoke(CallInst *CallGetBlkInvoke, + Function *InvokeF = nullptr) { + bool changed = false; + for (auto UI = CallGetBlkInvoke->user_begin(), + UE = CallGetBlkInvoke->user_end(); + UI != UE;) { + // Handle block_func_ptr = bitcast(spir_get_block_invoke(block)) + auto CallInv = cast(*UI++); + auto Cast = dyn_cast(CallInv); + if (Cast) + CallInv = dyn_cast(*CallInv->user_begin()); + DEBUG(dbgs() << "[lowerGetBlockInvoke] " << *CallInv); + // Handle ret = block_func_ptr(context_ptr, args) + auto CI = cast(CallInv); + auto F = CI->getCalledValue(); + if (InvokeF == nullptr) { + getBlockInvokeFuncAndContext(CallGetBlkInvoke->getArgOperand(0), + &InvokeF, nullptr); + assert(InvokeF); + } + assert(F->getType() == InvokeF->getType()); + CI->replaceUsesOfWith(F, InvokeF); + DEBUG(dbgs() << " => " << *CI << "\n\n"); + erase(Cast); + changed = true; + } + erase(CallGetBlkInvoke); + return changed; + } + + void + lowerBlockBuiltin(CallInst *CI, Function *InvF, Value *Ctx, Value *CtxLen, + Value *CtxAlign, const std::string& DemangledName) { + mutateCallInstSPIRV (M, CI, [=](CallInst *CI, std::vector &Args) { + size_t I = 0; + size_t E = Args.size(); + for (; I != E; ++I) { + if (isPointerToOpaqueStructType(Args[I]->getType(), + SPIR_TYPE_NAME_BLOCK_T)) { + break; + } + } + assert (I < E); + Args[I] = castToVoidFuncPtr(InvF); + if (I + 1 == E) { + Args.push_back(Ctx); + Args.push_back(CtxLen); + Args.push_back(CtxAlign); + } else { + Args.insert(Args.begin() + I + 1, CtxAlign); + Args.insert(Args.begin() + I + 1, CtxLen); + Args.insert(Args.begin() + I + 1, Ctx); + } + if (DemangledName == kOCLBuiltinName::EnqueueKernel) { + // Insert event arguments if there are not. + if (!isa(Args[3]->getType())) { + Args.insert(Args.begin() + 3, getInt32(M, 0)); + Args.insert(Args.begin() + 4, getOCLNullClkEventPtr()); + } + if (!isOCLClkEventPtrType(Args[5]->getType())) + Args.insert(Args.begin() + 5, getOCLNullClkEventPtr()); + } + return getSPIRVFuncName(OCLSPIRVBuiltinMap::map(DemangledName)); + }); + } + /// Transform return of a block. + /// The function returning a block is inlined since the context cannot be + /// passed to another function. + /// Returns true of module is changed. + bool + lowerReturnBlock(ReturnInst *Ret, Value *CallBlkBind, bool &Inlined) { + auto F = Ret->getParent()->getParent(); + auto changed = false; + for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) { + auto U = *UI++; + dumpUsers(U); + auto Inst = dyn_cast(U); + if (Inst && Inst->use_empty()) { + erase(Inst); + changed = true; + continue; + } + auto CI = dyn_cast(U); + if(!CI || CI->getCalledFunction() != F) + continue; + + DEBUG(dbgs() << "[lowerReturnBlock] inline " << F->getName() << '\n'); + auto CG = &getAnalysis().getCallGraph(); + auto ACT = &getAnalysis(); + //auto AA = &getAnalysis(); + //InlineFunctionInfo IFI(CG, M->getDataLayout(), AA, ACT); + std::function actf= + [&ACT](Function& f) -> AssumptionCache & {return ACT->getAssumptionCache(f);}; + InlineFunctionInfo IFI(CG,&actf); + InlineFunction(CI, IFI); + Inlined = true; + } + return changed || Inlined; + } + + void + getBlockInvokeFuncAndContext(Value *Blk, Function **PInvF, Value **PCtx, + Value **PCtxLen = nullptr, Value **PCtxAlign = nullptr){ + Function *InvF = nullptr; + Value *Ctx = nullptr; + Value *CtxLen = nullptr; + Value *CtxAlign = nullptr; + if (auto CallBlkBind = dyn_cast(Blk)) { + assert(CallBlkBind->getCalledFunction()->getName() == + SPIR_INTRINSIC_BLOCK_BIND && "Invalid block"); + InvF = dyn_cast( + CallBlkBind->getArgOperand(0)->stripPointerCasts()); + CtxLen = CallBlkBind->getArgOperand(1); + CtxAlign = CallBlkBind->getArgOperand(2); + Ctx = CallBlkBind->getArgOperand(3); + } else if (auto F = dyn_cast(Blk->stripPointerCasts())) { + InvF = F; + Ctx = Constant::getNullValue(IntegerType::getInt8PtrTy(M->getContext())); + } else if (auto Load = dyn_cast(Blk)) { + auto Op = Load->getPointerOperand(); + if (auto GV = dyn_cast(Op)) { + if (GV->isConstant()) { + InvF = cast(GV->getInitializer()->stripPointerCasts()); + Ctx = Constant::getNullValue(IntegerType::getInt8PtrTy(M->getContext())); + } else { + llvm_unreachable("load non-constant block?"); + } + } else { + llvm_unreachable("Loading block from non global?"); + } + } else { + llvm_unreachable("Invalid block"); + } + DEBUG(dbgs() << " Block invocation func: " << InvF->getName() << '\n' << + " Block context: " << *Ctx << '\n'); + assert(InvF && Ctx && "Invalid block"); + if (PInvF) + *PInvF = InvF; + if (PCtx) + *PCtx = Ctx; + if (PCtxLen) + *PCtxLen = CtxLen; + if (PCtxAlign) + *PCtxAlign = CtxAlign; + } + void + erase(Instruction *I) { + if (!I) + return; + if (I->use_empty()) { + I->dropAllReferences(); + I->eraseFromParent(); + } + else + dumpUsers(I); + } + void + erase(ConstantExpr *I) { + if (!I) + return; + if (I->use_empty()) { + I->dropAllReferences(); + I->destroyConstant(); + } else + dumpUsers(I); + } + void + erase(Function *F) { + if (!F) + return; + if (!F->use_empty()) { + dumpUsers(F); + return; + } + F->dropAllReferences(); + auto &CG = getAnalysis().getCallGraph(); + CG.removeFunctionFromModule(new CallGraphNode(F)); + } + + llvm::PointerType* getOCLClkEventType() { + return getOrCreateOpaquePtrType(M, SPIR_TYPE_NAME_CLK_EVENT_T, + SPIRAS_Global); + } + + llvm::PointerType* getOCLClkEventPtrType() { + return PointerType::get(getOCLClkEventType(), SPIRAS_Generic); + } + + bool isOCLClkEventPtrType(Type *T) { + if (auto PT = dyn_cast(T)) + return isPointerToOpaqueStructType( + PT->getElementType(), SPIR_TYPE_NAME_CLK_EVENT_T); + return false; + } + + llvm::Constant* getOCLNullClkEventPtr() { + return Constant::getNullValue(getOCLClkEventPtrType()); + } + + void dumpGetBlockInvokeUsers(StringRef Prompt) { + DEBUG(dbgs() << Prompt); + dumpUsers(M->getFunction(SPIR_INTRINSIC_GET_BLOCK_INVOKE)); + } +}; + +char SPIRVLowerOCLBlocks::ID = 0; +} + +INITIALIZE_PASS_BEGIN(SPIRVLowerOCLBlocks, "spvblocks", + "SPIR-V lower OCL blocks", false, false) +INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) +INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) +initializeAnalysis(Registry); +INITIALIZE_PASS_END(SPIRVLowerOCLBlocks, "spvblocks", + "SPIR-V lower OCL blocks", false, false) + +ModulePass *llvm::createSPIRVLowerOCLBlocks() { + return new SPIRVLowerOCLBlocks(); +} + +#endif /* OCLLOWERBLOCKS_H_ */ diff --git a/SPIRVReader.cpp b/SPIRVReader.cpp index b4714cf..d29d52b 100644 --- a/SPIRVReader.cpp +++ b/SPIRVReader.cpp @@ -1,2511 +1,2513 @@ -//===- SPIRVReader.cpp - Converts SPIR-V to LLVM ----------------*- C++ -*-===// -// -// The LLVM/SPIR-V Translator -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. -// -//===----------------------------------------------------------------------===// -/// \file -/// -/// This file implements conversion of SPIR-V binary to LLVM IR. -/// -//===----------------------------------------------------------------------===// -#include "SPIRVUtil.h" -#include "SPIRVType.h" -#include "SPIRVValue.h" -#include "SPIRVModule.h" -#include "SPIRVFunction.h" -#include "SPIRVBasicBlock.h" -#include "SPIRVInstruction.h" -#include "SPIRVExtInst.h" -#include "SPIRVInternal.h" -#include "SPIRVMDBuilder.h" -#include "OCLUtil.h" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/DIBuilder.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Metadata.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Operator.h" -#include "llvm/IR/Type.h" -#include "llvm/PassManager.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/Dwarf.h" -#include "llvm/Support/FileSystem.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/CommandLine.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "spirv" - -using namespace std; -using namespace llvm; -using namespace SPIRV; -using namespace OCLUtil; - -namespace SPIRV{ - -cl::opt SPIRVEnableStepExpansion("spirv-expand-step", cl::init(true), - cl::desc("Enable expansion of OpenCL step and smoothstep function")); - -cl::opt SPIRVGenKernelArgNameMD("spirv-gen-kernel-arg-name-md", - cl::init(false), cl::desc("Enable generating OpenCL kernel argument name " - "metadata")); - -cl::opt SPIRVGenImgTypeAccQualPostfix("spirv-gen-image-type-acc-postfix", - cl::init(false), cl::desc("Enable generating access qualifier postfix" - " in OpenCL image type names")); - -// Prefix for placeholder global variable name. -const char* kPlaceholderPrefix = "placeholder."; - -// Save the translated LLVM before validation for debugging purpose. -static bool DbgSaveTmpLLVM = true; -static const char *DbgTmpLLVMFileName = "_tmp_llvmbil.ll"; - -typedef std::pair < unsigned, AttributeSet > AttributeWithIndex; - -static bool -isOpenCLKernel(SPIRVFunction *BF) { - return BF->getModule()->isEntryPoint(ExecutionModelKernel, BF->getId()); -} - -static void -dumpLLVM(Module *M, const std::string &FName) { - std::error_code EC; - raw_fd_ostream FS(FName, EC, sys::fs::F_None); - if (EC) { - FS << *M; - FS.close(); - } -} - -static MDNode* -getMDNodeStringIntVec(LLVMContext *Context, const std::string& Str, - const std::vector& IntVals) { - std::vector ValueVec; - ValueVec.push_back(MDString::get(*Context, Str)); - for (auto &I:IntVals) - ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), I))); - return MDNode::get(*Context, ValueVec); -} - -static MDNode* -getMDTwoInt(LLVMContext *Context, unsigned Int1, unsigned Int2) { - std::vector ValueVec; - ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), Int1))); - ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), Int2))); - return MDNode::get(*Context, ValueVec); -} - -static MDNode* -getMDString(LLVMContext *Context, const std::string& Str) { - std::vector ValueVec; - if (!Str.empty()) - ValueVec.push_back(MDString::get(*Context, Str)); - return MDNode::get(*Context, ValueVec); -} - -static void -addOCLVersionMetadata(LLVMContext *Context, Module *M, - const std::string &MDName, unsigned Major, unsigned Minor) { - NamedMDNode *NamedMD = M->getOrInsertNamedMetadata(MDName); - NamedMD->addOperand(getMDTwoInt(Context, Major, Minor)); -} - -static void -addNamedMetadataStringSet(LLVMContext *Context, Module *M, - const std::string &MDName, const std::set &StrSet) { - NamedMDNode *NamedMD = M->getOrInsertNamedMetadata(MDName); - std::vector ValueVec; - for (auto &&Str : StrSet) { - ValueVec.push_back(MDString::get(*Context, Str)); - } - NamedMD->addOperand(MDNode::get(*Context, ValueVec)); -} - -static void -addOCLKernelArgumentMetadata(LLVMContext *Context, - std::vector &KernelMD, const std::string &MDName, - SPIRVFunction *BF, std::functionFunc){ - std::vector ValueVec; - ValueVec.push_back(MDString::get(*Context, MDName)); - BF->foreachArgument([&](SPIRVFunctionParameter *Arg) { - ValueVec.push_back(Func(Arg)); - }); - KernelMD.push_back(MDNode::get(*Context, ValueVec)); -} - -class SPIRVToLLVMDbgTran { -public: - SPIRVToLLVMDbgTran(SPIRVModule *TBM, Module *TM) - :BM(TBM), M(TM), SpDbg(BM), Builder(*M){ - Enable = BM->hasDebugInfo(); - } - - void createCompileUnit() { - if (!Enable) - return; - auto File = SpDbg.getEntryPointFileStr(ExecutionModelKernel, 0); - std::string BaseName; - std::string Path; - splitFileName(File, BaseName, Path); - Builder.createCompileUnit(dwarf::DW_LANG_C99, - BaseName, Path, "spirv", false, "", 0, "", DIBuilder::LineTablesOnly); - } - - void addDbgInfoVersion() { - if (!Enable) - return; - M->addModuleFlag(Module::Warning, "Dwarf Version", - dwarf::DWARF_VERSION); - M->addModuleFlag(Module::Warning, "Debug Info Version", - DEBUG_METADATA_VERSION); - } - - DIFile getDIFile(const std::string &FileName){ - return getOrInsert(FileMap, FileName, [=](){ - std::string BaseName; - std::string Path; - splitFileName(FileName, BaseName, Path); - if (!BaseName.empty()) - return Builder.createFile(BaseName, Path); - else - return DIFile(); - }); - } - - DISubprogram getDISubprogram(SPIRVFunction *SF, Function *F){ - return getOrInsert(FuncMap, F, [=](){ - auto DF = getDIFile(SpDbg.getFunctionFileStr(SF)); - auto FN = F->getName(); - auto LN = SpDbg.getFunctionLineNo(SF); - Metadata *Args[] = {DIType()}; - return Builder.createFunction(DF, FN, FN, DF, LN, - Builder.createSubroutineType(DF, Builder.getOrCreateTypeArray(Args)), - Function::isInternalLinkage(F->getLinkage()), - true, LN, 0, 0, NULL, NULL, NULL); - }); - } - - void transDbgInfo(SPIRVValue *SV, Value *V) { - if (!Enable || !SV->hasLine()) - return; - if (auto I = dyn_cast(V)) { - assert(SV->isInst() && "Invalid instruction"); - auto SI = static_cast(SV); - assert(SI->getParent() && - SI->getParent()->getParent() && - "Invalid instruction"); - auto Line = SV->getLine(); - I->setDebugLoc(DebugLoc::get(Line->getLine(), Line->getColumn(), - getDISubprogram(SI->getParent()->getParent(), - I->getParent()->getParent()))); - } - } - - void finalize() { - if (!Enable) - return; - Builder.finalize(); - } - -private: - SPIRVModule *BM; - Module *M; - SPIRVDbgInfo SpDbg; - DIBuilder Builder; - bool Enable; - std::unordered_map FileMap; - std::unordered_map FuncMap; - - void splitFileName(const std::string &FileName, - std::string &BaseName, - std::string &Path) { - auto Loc = FileName.find_last_of("/\\"); - if (Loc != std::string::npos) { - BaseName = FileName.substr(Loc + 1); - Path = FileName.substr(0, Loc); - } else { - BaseName = FileName; - Path = "."; - } - } -}; - -class SPIRVToLLVM { -public: - SPIRVToLLVM(Module *LLVMModule, SPIRVModule *TheSPIRVModule) - :M(LLVMModule), BM(TheSPIRVModule), DbgTran(BM, M){ - assert(M); - Context = &M->getContext(); - } - - std::string getOCLBuiltinName(SPIRVInstruction* BI); - std::string getOCLConvertBuiltinName(SPIRVInstruction *BI); - std::string getOCLGenericCastToPtrName(SPIRVInstruction *BI); - - Type *transType(SPIRVType *BT); - std::string transTypeToOCLTypeName(SPIRVType *BT, bool IsSigned = true); - std::vector transTypeVector(const std::vector&); - bool translate(); - bool transAddressingModel(); - - Value *transValue(SPIRVValue *, Function *F, BasicBlock *, - bool CreatePlaceHolder = true); - Value *transValueWithoutDecoration(SPIRVValue *, Function *F, BasicBlock *, - bool CreatePlaceHolder = true); - bool transDecoration(SPIRVValue *, Value *); - bool transAlign(SPIRVValue *, Value *); - Instruction *transOCLBuiltinFromExtInst(SPIRVExtInst *BC, BasicBlock *BB); - std::vector transValue(const std::vector&, Function *F, - BasicBlock *); - Function *transFunction(SPIRVFunction *F); - bool transFPContractMetadata(); - bool transKernelMetadata(); - bool transSourceLanguage(); - bool transSourceExtension(); - void transGeneratorMD(); - Value *transConvertInst(SPIRVValue* BV, Function* F, BasicBlock* BB); - Instruction *transBuiltinFromInst(const std::string& FuncName, - SPIRVInstruction* BI, BasicBlock* BB); - Instruction *transOCLBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB); - Instruction *transSPIRVBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB); - Instruction *transOCLBarrierFence(SPIRVInstruction* BI, BasicBlock *BB); - void transOCLVectorLoadStore(std::string& UnmangledName, - std::vector &BArgs); - - /// Post-process translated LLVM module for OpenCL. - bool postProcessOCL(); - - /// \brief Post-process OpenCL builtin functions returning struct type. - /// - /// Some OpenCL builtin functions are translated to SPIR-V instructions with - /// struct type result, e.g. NDRange creation functions. Such functions - /// need to be post-processed to return the struct through sret argument. - bool postProcessOCLBuiltinReturnStruct(Function *F); - - /// \brief Post-process OpenCL builtin functions having block argument. - /// - /// These functions are translated to functions with function pointer type - /// argument first, then post-processed to have block argument. - bool postProcessOCLBuiltinWithFuncPointer(Function *F, - Function::arg_iterator I); - - /// \brief Post-process OpenCL builtin functions having array argument. - /// - /// These functions are translated to functions with array type argument - /// first, then post-processed to have pointer arguments. - bool postProcessOCLBuiltinWithArrayArguments(Function *F, - const std::string &DemangledName); - - /// \brief Post-process OpImageSampleExplicitLod. - /// sampled_image = __spirv_SampledImage__(image, sampler); - /// return __spirv_ImageSampleExplicitLod__(sampled_image, image_operands, - /// ...); - /// => - /// read_image(image, sampler, ...) - /// \return transformed call instruction. - Instruction *postProcessOCLReadImage(SPIRVInstruction *BI, CallInst *CI, - const std::string &DemangledName); - - /// \brief Post-process OpImageWrite. - /// return write_image(image, coord, color, image_operands, ...); - /// => - /// write_image(image, coord, ..., color) - /// \return transformed call instruction. - CallInst *postProcessOCLWriteImage(SPIRVInstruction *BI, CallInst *CI, - const std::string &DemangledName); - - /// \brief Post-process OpBuildNDRange. - /// OpBuildNDRange GlobalWorkSize, LocalWorkSize, GlobalWorkOffset - /// => - /// call ndrange_XD(GlobalWorkOffset, GlobalWorkSize, LocalWorkSize) - /// \return transformed call instruction. - CallInst *postProcessOCLBuildNDRange(SPIRVInstruction *BI, CallInst *CI, - const std::string &DemangledName); - - /// \brief Expand OCL builtin functions with scalar argument, e.g. - /// step, smoothstep. - /// gentype func (fp edge, gentype x) - /// => - /// gentype func (gentype edge, gentype x) - /// \return transformed call instruction. - CallInst *expandOCLBuiltinWithScalarArg(CallInst* CI, - const std::string &FuncName); - - /// \brief Post-process OpGroupAll and OpGroupAny instructions translation. - /// i1 func ( arg) - /// => - /// i32 func ( arg) - /// \return transformed call instruction. - Instruction *postProcessGroupAllAny(CallInst *CI, - const std::string &DemangledName); - - typedef DenseMap SPIRVToLLVMTypeMap; - typedef DenseMap SPIRVToLLVMValueMap; - typedef DenseMap SPIRVToLLVMFunctionMap; - typedef DenseMap BuiltinVarMap; - - // A SPIRV value may be translated to a load instruction of a placeholder - // global variable. This map records load instruction of these placeholders - // which are supposed to be replaced by the real values later. - typedef std::map SPIRVToLLVMPlaceholderMap; -private: - Module *M; - BuiltinVarMap BuiltinGVMap; - LLVMContext *Context; - SPIRVModule *BM; - SPIRVToLLVMTypeMap TypeMap; - SPIRVToLLVMValueMap ValueMap; - SPIRVToLLVMFunctionMap FuncMap; - SPIRVToLLVMPlaceholderMap PlaceholderMap; - SPIRVToLLVMDbgTran DbgTran; - - Type *mapType(SPIRVType *BT, Type *T) { - SPIRVDBG(dbgs() << *T << '\n';) - TypeMap[BT] = T; - return T; - } - - // If a value is mapped twice, the existing mapped value is a placeholder, - // which must be a load instruction of a global variable whose name starts - // with kPlaceholderPrefix. - Value *mapValue(SPIRVValue *BV, Value *V) { - auto Loc = ValueMap.find(BV); - if (Loc != ValueMap.end()) { - if (Loc->second == V) - return V; - auto LD = dyn_cast(Loc->second); - auto Placeholder = dyn_cast(LD->getPointerOperand()); - assert (LD && Placeholder && - Placeholder->getName().startswith(kPlaceholderPrefix) && - "A value is translated twice"); - // Replaces placeholders for PHI nodes - LD->replaceAllUsesWith(V); - LD->dropAllReferences(); - LD->removeFromParent(); - Placeholder->dropAllReferences(); - Placeholder->removeFromParent(); - } - ValueMap[BV] = V; - return V; - } - - bool isSPIRVBuiltinVariable(GlobalVariable *GV, - SPIRVBuiltinVariableKind *Kind = nullptr) { - auto Loc = BuiltinGVMap.find(GV); - if (Loc == BuiltinGVMap.end()) - return false; - if (Kind) - *Kind = Loc->second; - return true; - } - // OpenCL function always has NoUnwound attribute. - // Change this if it is no longer true. - bool isFuncNoUnwind() const { return true;} - bool isSPIRVCmpInstTransToLLVMInst(SPIRVInstruction *BI) const; - bool transOCLBuiltinsFromVariables(); - bool transOCLBuiltinFromVariable(GlobalVariable *GV, - SPIRVBuiltinVariableKind Kind); - MDString *transOCLKernelArgTypeName(SPIRVFunctionParameter *); - - Value *mapFunction(SPIRVFunction *BF, Function *F) { - SPIRVDBG(spvdbgs() << "[mapFunction] " << *BF << " -> "; - dbgs() << *F << '\n';) - FuncMap[BF] = F; - return F; - } - - Value *getTranslatedValue(SPIRVValue *BV); - Type *getTranslatedType(SPIRVType *BT); - - SPIRVErrorLog &getErrorLog() { - return BM->getErrorLog(); - } - - void setCallingConv(CallInst *Call) { - Function *F = Call->getCalledFunction(); +//===- SPIRVReader.cpp - Converts SPIR-V to LLVM ----------------*- C++ -*-===// +// +// The LLVM/SPIR-V Translator +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file implements conversion of SPIR-V binary to LLVM IR. +/// +//===----------------------------------------------------------------------===// +#include "SPIRVUtil.h" +#include "SPIRVType.h" +#include "SPIRVValue.h" +#include "SPIRVModule.h" +#include "SPIRVFunction.h" +#include "SPIRVBasicBlock.h" +#include "SPIRVInstruction.h" +#include "SPIRVExtInst.h" +#include "SPIRVInternal.h" +#include "SPIRVMDBuilder.h" +#include "OCLUtil.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DIBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Dwarf.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/CommandLine.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_TYPE "spirv" + +using namespace std; +using namespace llvm; +using namespace SPIRV; +using namespace OCLUtil; + +namespace SPIRV{ + +cl::opt SPIRVEnableStepExpansion("spirv-expand-step", cl::init(true), + cl::desc("Enable expansion of OpenCL step and smoothstep function")); + +cl::opt SPIRVGenKernelArgNameMD("spirv-gen-kernel-arg-name-md", + cl::init(false), cl::desc("Enable generating OpenCL kernel argument name " + "metadata")); + +cl::opt SPIRVGenImgTypeAccQualPostfix("spirv-gen-image-type-acc-postfix", + cl::init(false), cl::desc("Enable generating access qualifier postfix" + " in OpenCL image type names")); + +// Prefix for placeholder global variable name. +const char* kPlaceholderPrefix = "placeholder."; + +// Save the translated LLVM before validation for debugging purpose. +static bool DbgSaveTmpLLVM = true; +static const char *DbgTmpLLVMFileName = "_tmp_llvmbil.ll"; + +typedef std::pair < unsigned, AttributeSet > AttributeWithIndex; + +static bool +isOpenCLKernel(SPIRVFunction *BF) { + return BF->getModule()->isEntryPoint(ExecutionModelKernel, BF->getId()); +} + +static void +dumpLLVM(Module *M, const std::string &FName) { + std::error_code EC; + raw_fd_ostream FS(FName, EC, sys::fs::F_None); + if (EC) { + FS << *M; + FS.close(); + } +} + +static MDNode* +getMDNodeStringIntVec(LLVMContext *Context, const std::string& Str, + const std::vector& IntVals) { + std::vector ValueVec; + ValueVec.push_back(MDString::get(*Context, Str)); + for (auto &I:IntVals) + ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), I))); + return MDNode::get(*Context, ValueVec); +} + +static MDNode* +getMDTwoInt(LLVMContext *Context, unsigned Int1, unsigned Int2) { + std::vector ValueVec; + ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), Int1))); + ValueVec.push_back(ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), Int2))); + return MDNode::get(*Context, ValueVec); +} + +static MDNode* +getMDString(LLVMContext *Context, const std::string& Str) { + std::vector ValueVec; + if (!Str.empty()) + ValueVec.push_back(MDString::get(*Context, Str)); + return MDNode::get(*Context, ValueVec); +} + +static void +addOCLVersionMetadata(LLVMContext *Context, Module *M, + const std::string &MDName, unsigned Major, unsigned Minor) { + NamedMDNode *NamedMD = M->getOrInsertNamedMetadata(MDName); + NamedMD->addOperand(getMDTwoInt(Context, Major, Minor)); +} + +static void +addNamedMetadataStringSet(LLVMContext *Context, Module *M, + const std::string &MDName, const std::set &StrSet) { + NamedMDNode *NamedMD = M->getOrInsertNamedMetadata(MDName); + std::vector ValueVec; + for (auto &&Str : StrSet) { + ValueVec.push_back(MDString::get(*Context, Str)); + } + NamedMD->addOperand(MDNode::get(*Context, ValueVec)); +} + +static void +addOCLKernelArgumentMetadata(LLVMContext *Context, + std::vector &KernelMD, const std::string &MDName, + SPIRVFunction *BF, std::functionFunc){ + std::vector ValueVec; + ValueVec.push_back(MDString::get(*Context, MDName)); + BF->foreachArgument([&](SPIRVFunctionParameter *Arg) { + ValueVec.push_back(Func(Arg)); + }); + KernelMD.push_back(MDNode::get(*Context, ValueVec)); +} + +class SPIRVToLLVMDbgTran { +public: + SPIRVToLLVMDbgTran(SPIRVModule *TBM, Module *TM) + :BM(TBM), M(TM), SpDbg(BM), Builder(*M){ + Enable = BM->hasDebugInfo(); + } + + void createCompileUnit() { + if (!Enable) + return; + auto File = SpDbg.getEntryPointFileStr(ExecutionModelKernel, 0); + //std::string BaseName; + //std::string Path; + //splitFileName(File, BaseName, Path); + Builder.createCompileUnit(dwarf::DW_LANG_C99, + getDIFile(File), "spirv", false, "", 0, "", DICompileUnit::DebugEmissionKind::LineTablesOnly); + } + + void addDbgInfoVersion() { + if (!Enable) + return; + M->addModuleFlag(Module::Warning, "Dwarf Version", + dwarf::DWARF_VERSION); + M->addModuleFlag(Module::Warning, "Debug Info Version", + DEBUG_METADATA_VERSION); + } + + DIFile *getDIFile(const std::string &FileName){ + return getOrInsert(FileMap, FileName, [=](){ + std::string BaseName; + std::string Path; + splitFileName(FileName, BaseName, Path); + if (!BaseName.empty()) + return Builder.createFile(BaseName, Path); + else + return (DIFile *)nullptr; + }); + } + + DISubprogram* getDISubprogram(SPIRVFunction *SF, Function *F){ + return getOrInsert(FuncMap, F, [=](){ + auto DF = getDIFile(SpDbg.getFunctionFileStr(SF)); + auto FN = F->getName(); + auto LN = SpDbg.getFunctionLineNo(SF); + Metadata *Args[] = {nullptr}; + return Builder.createFunction(DF, FN, FN, DF, LN, + Builder.createSubroutineType(Builder.getOrCreateTypeArray({Args})), + Function::isInternalLinkage(F->getLinkage()), + true, LN); + }); + } + + void transDbgInfo(SPIRVValue *SV, Value *V) { + if (!Enable || !SV->hasLine()) + return; + if (auto I = dyn_cast(V)) { + assert(SV->isInst() && "Invalid instruction"); + auto SI = static_cast(SV); + assert(SI->getParent() && + SI->getParent()->getParent() && + "Invalid instruction"); + auto Line = SV->getLine(); + I->setDebugLoc(DebugLoc::get(Line->getLine(), Line->getColumn(), + getDISubprogram(SI->getParent()->getParent(), + I->getParent()->getParent()))); + } + } + + void finalize() { + if (!Enable) + return; + Builder.finalize(); + } + +private: + SPIRVModule *BM; + Module *M; + SPIRVDbgInfo SpDbg; + DIBuilder Builder; + bool Enable; + std::unordered_map FileMap; + std::unordered_map FuncMap; + + void splitFileName(const std::string &FileName, + std::string &BaseName, + std::string &Path) { + auto Loc = FileName.find_last_of("/\\"); + if (Loc != std::string::npos) { + BaseName = FileName.substr(Loc + 1); + Path = FileName.substr(0, Loc); + } else { + BaseName = FileName; + Path = "."; + } + } +}; + +class SPIRVToLLVM { +public: + SPIRVToLLVM(Module *LLVMModule, SPIRVModule *TheSPIRVModule) + :M(LLVMModule), BM(TheSPIRVModule), DbgTran(BM, M){ + assert(M); + Context = &M->getContext(); + } + + std::string getOCLBuiltinName(SPIRVInstruction* BI); + std::string getOCLConvertBuiltinName(SPIRVInstruction *BI); + std::string getOCLGenericCastToPtrName(SPIRVInstruction *BI); + + Type *transType(SPIRVType *BT); + std::string transTypeToOCLTypeName(SPIRVType *BT, bool IsSigned = true); + std::vector transTypeVector(const std::vector&); + bool translate(); + bool transAddressingModel(); + + Value *transValue(SPIRVValue *, Function *F, BasicBlock *, + bool CreatePlaceHolder = true); + Value *transValueWithoutDecoration(SPIRVValue *, Function *F, BasicBlock *, + bool CreatePlaceHolder = true); + bool transDecoration(SPIRVValue *, Value *); + bool transAlign(SPIRVValue *, Value *); + Instruction *transOCLBuiltinFromExtInst(SPIRVExtInst *BC, BasicBlock *BB); + std::vector transValue(const std::vector&, Function *F, + BasicBlock *); + Function *transFunction(SPIRVFunction *F); + bool transFPContractMetadata(); + bool transKernelMetadata(); + bool transSourceLanguage(); + bool transSourceExtension(); + void transGeneratorMD(); + Value *transConvertInst(SPIRVValue* BV, Function* F, BasicBlock* BB); + Instruction *transBuiltinFromInst(const std::string& FuncName, + SPIRVInstruction* BI, BasicBlock* BB); + Instruction *transOCLBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB); + Instruction *transSPIRVBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB); + Instruction *transOCLBarrierFence(SPIRVInstruction* BI, BasicBlock *BB); + void transOCLVectorLoadStore(std::string& UnmangledName, + std::vector &BArgs); + + /// Post-process translated LLVM module for OpenCL. + bool postProcessOCL(); + + /// \brief Post-process OpenCL builtin functions returning struct type. + /// + /// Some OpenCL builtin functions are translated to SPIR-V instructions with + /// struct type result, e.g. NDRange creation functions. Such functions + /// need to be post-processed to return the struct through sret argument. + bool postProcessOCLBuiltinReturnStruct(Function *F); + + /// \brief Post-process OpenCL builtin functions having block argument. + /// + /// These functions are translated to functions with function pointer type + /// argument first, then post-processed to have block argument. + bool postProcessOCLBuiltinWithFuncPointer(Function *F, + Function::arg_iterator I); + + /// \brief Post-process OpenCL builtin functions having array argument. + /// + /// These functions are translated to functions with array type argument + /// first, then post-processed to have pointer arguments. + bool postProcessOCLBuiltinWithArrayArguments(Function *F, + const std::string &DemangledName); + + /// \brief Post-process OpImageSampleExplicitLod. + /// sampled_image = __spirv_SampledImage__(image, sampler); + /// return __spirv_ImageSampleExplicitLod__(sampled_image, image_operands, + /// ...); + /// => + /// read_image(image, sampler, ...) + /// \return transformed call instruction. + Instruction *postProcessOCLReadImage(SPIRVInstruction *BI, CallInst *CI, + const std::string &DemangledName); + + /// \brief Post-process OpImageWrite. + /// return write_image(image, coord, color, image_operands, ...); + /// => + /// write_image(image, coord, ..., color) + /// \return transformed call instruction. + CallInst *postProcessOCLWriteImage(SPIRVInstruction *BI, CallInst *CI, + const std::string &DemangledName); + + /// \brief Post-process OpBuildNDRange. + /// OpBuildNDRange GlobalWorkSize, LocalWorkSize, GlobalWorkOffset + /// => + /// call ndrange_XD(GlobalWorkOffset, GlobalWorkSize, LocalWorkSize) + /// \return transformed call instruction. + CallInst *postProcessOCLBuildNDRange(SPIRVInstruction *BI, CallInst *CI, + const std::string &DemangledName); + + /// \brief Expand OCL builtin functions with scalar argument, e.g. + /// step, smoothstep. + /// gentype func (fp edge, gentype x) + /// => + /// gentype func (gentype edge, gentype x) + /// \return transformed call instruction. + CallInst *expandOCLBuiltinWithScalarArg(CallInst* CI, + const std::string &FuncName); + + /// \brief Post-process OpGroupAll and OpGroupAny instructions translation. + /// i1 func ( arg) + /// => + /// i32 func ( arg) + /// \return transformed call instruction. + Instruction *postProcessGroupAllAny(CallInst *CI, + const std::string &DemangledName); + + typedef DenseMap SPIRVToLLVMTypeMap; + typedef DenseMap SPIRVToLLVMValueMap; + typedef DenseMap SPIRVToLLVMFunctionMap; + typedef DenseMap BuiltinVarMap; + + // A SPIRV value may be translated to a load instruction of a placeholder + // global variable. This map records load instruction of these placeholders + // which are supposed to be replaced by the real values later. + typedef std::map SPIRVToLLVMPlaceholderMap; +private: + Module *M; + BuiltinVarMap BuiltinGVMap; + LLVMContext *Context; + SPIRVModule *BM; + SPIRVToLLVMTypeMap TypeMap; + SPIRVToLLVMValueMap ValueMap; + SPIRVToLLVMFunctionMap FuncMap; + SPIRVToLLVMPlaceholderMap PlaceholderMap; + SPIRVToLLVMDbgTran DbgTran; + + Type *mapType(SPIRVType *BT, Type *T) { + SPIRVDBG(dbgs() << *T << '\n';) + TypeMap[BT] = T; + return T; + } + + // If a value is mapped twice, the existing mapped value is a placeholder, + // which must be a load instruction of a global variable whose name starts + // with kPlaceholderPrefix. + Value *mapValue(SPIRVValue *BV, Value *V) { + auto Loc = ValueMap.find(BV); + if (Loc != ValueMap.end()) { + if (Loc->second == V) + return V; + auto LD = dyn_cast(Loc->second); + auto Placeholder = dyn_cast(LD->getPointerOperand()); + assert (LD && Placeholder && + Placeholder->getName().startswith(kPlaceholderPrefix) && + "A value is translated twice"); + // Replaces placeholders for PHI nodes + LD->replaceAllUsesWith(V); + LD->dropAllReferences(); + LD->removeFromParent(); + Placeholder->dropAllReferences(); + Placeholder->removeFromParent(); + } + ValueMap[BV] = V; + return V; + } + + bool isSPIRVBuiltinVariable(GlobalVariable *GV, + SPIRVBuiltinVariableKind *Kind = nullptr) { + auto Loc = BuiltinGVMap.find(GV); + if (Loc == BuiltinGVMap.end()) + return false; + if (Kind) + *Kind = Loc->second; + return true; + } + // OpenCL function always has NoUnwound attribute. + // Change this if it is no longer true. + bool isFuncNoUnwind() const { return true;} + bool isSPIRVCmpInstTransToLLVMInst(SPIRVInstruction *BI) const; + bool transOCLBuiltinsFromVariables(); + bool transOCLBuiltinFromVariable(GlobalVariable *GV, + SPIRVBuiltinVariableKind Kind); + MDString *transOCLKernelArgTypeName(SPIRVFunctionParameter *); + + Value *mapFunction(SPIRVFunction *BF, Function *F) { + SPIRVDBG(spvdbgs() << "[mapFunction] " << *BF << " -> "; + dbgs() << *F << '\n';) + FuncMap[BF] = F; + return F; + } + + Value *getTranslatedValue(SPIRVValue *BV); + Type *getTranslatedType(SPIRVType *BT); + + SPIRVErrorLog &getErrorLog() { + return BM->getErrorLog(); + } + + void setCallingConv(CallInst *Call) { + Function *F = Call->getCalledFunction(); assert(F); - Call->setCallingConv(F->getCallingConv()); - } - - void setAttrByCalledFunc(CallInst *Call); - Type *transFPType(SPIRVType* T); - BinaryOperator *transShiftLogicalBitwiseInst(SPIRVValue* BV, BasicBlock* BB, - Function* F); - void transFlags(llvm::Value* V); - Instruction *transCmpInst(SPIRVValue* BV, BasicBlock* BB, Function* F); - void transOCLBuiltinFromInstPreproc(SPIRVInstruction* BI, Type *&RetTy, - std::vector &Args); - Instruction* transOCLBuiltinPostproc(SPIRVInstruction* BI, - CallInst* CI, BasicBlock* BB, const std::string &DemangledName); - std::string transOCLImageTypeName(SPIRV::SPIRVTypeImage* ST); - std::string transOCLSampledImageTypeName(SPIRV::SPIRVTypeSampledImage* ST); - std::string transOCLPipeTypeName(SPIRV::SPIRVTypePipe* ST); - std::string transOCLImageTypeAccessQualifier(SPIRV::SPIRVTypeImage* ST); - std::string transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe* ST); - - Value *oclTransConstantSampler(SPIRV::SPIRVConstantSampler* BCS); - void setName(llvm::Value* V, SPIRVValue* BV); - template - bool foreachFuncCtlMask(Source, Func); - llvm::GlobalValue::LinkageTypes transLinkageType(const SPIRVValue* V); - Instruction *transOCLAllAny(SPIRVInstruction* BI, BasicBlock *BB); - Instruction *transOCLRelational(SPIRVInstruction* BI, BasicBlock *BB); -}; - -Type * -SPIRVToLLVM::getTranslatedType(SPIRVType *BV){ - auto Loc = TypeMap.find(BV); - if (Loc != TypeMap.end()) - return Loc->second; - return nullptr; -} - -Value * -SPIRVToLLVM::getTranslatedValue(SPIRVValue *BV){ - auto Loc = ValueMap.find(BV); - if (Loc != ValueMap.end()) - return Loc->second; - return nullptr; -} - -void -SPIRVToLLVM::setAttrByCalledFunc(CallInst *Call) { - Function *F = Call->getCalledFunction(); + Call->setCallingConv(F->getCallingConv()); + } + + void setAttrByCalledFunc(CallInst *Call); + Type *transFPType(SPIRVType* T); + BinaryOperator *transShiftLogicalBitwiseInst(SPIRVValue* BV, BasicBlock* BB, + Function* F); + void transFlags(llvm::Value* V); + Instruction *transCmpInst(SPIRVValue* BV, BasicBlock* BB, Function* F); + void transOCLBuiltinFromInstPreproc(SPIRVInstruction* BI, Type *&RetTy, + std::vector &Args); + Instruction* transOCLBuiltinPostproc(SPIRVInstruction* BI, + CallInst* CI, BasicBlock* BB, const std::string &DemangledName); + std::string transOCLImageTypeName(SPIRV::SPIRVTypeImage* ST); + std::string transOCLSampledImageTypeName(SPIRV::SPIRVTypeSampledImage* ST); + std::string transOCLPipeTypeName(SPIRV::SPIRVTypePipe* ST); + std::string transOCLImageTypeAccessQualifier(SPIRV::SPIRVTypeImage* ST); + std::string transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe* ST); + + Value *oclTransConstantSampler(SPIRV::SPIRVConstantSampler* BCS); + void setName(llvm::Value* V, SPIRVValue* BV); + template + bool foreachFuncCtlMask(Source, Func); + llvm::GlobalValue::LinkageTypes transLinkageType(const SPIRVValue* V); + Instruction *transOCLAllAny(SPIRVInstruction* BI, BasicBlock *BB); + Instruction *transOCLRelational(SPIRVInstruction* BI, BasicBlock *BB); +}; + +Type * +SPIRVToLLVM::getTranslatedType(SPIRVType *BV){ + auto Loc = TypeMap.find(BV); + if (Loc != TypeMap.end()) + return Loc->second; + return nullptr; +} + +Value * +SPIRVToLLVM::getTranslatedValue(SPIRVValue *BV){ + auto Loc = ValueMap.find(BV); + if (Loc != ValueMap.end()) + return Loc->second; + return nullptr; +} + +void +SPIRVToLLVM::setAttrByCalledFunc(CallInst *Call) { + Function *F = Call->getCalledFunction(); assert(F); - if (F->isIntrinsic()) { - return; - } - Call->setCallingConv(F->getCallingConv()); - Call->setAttributes(F->getAttributes()); -} - -bool -SPIRVToLLVM::transOCLBuiltinsFromVariables(){ - std::vector WorkList; - for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) { - SPIRVBuiltinVariableKind Kind; - if (!isSPIRVBuiltinVariable(I, &Kind)) - continue; - if (!transOCLBuiltinFromVariable(I, Kind)) - return false; - WorkList.push_back(I); - } - for (auto &I:WorkList) { - I->dropAllReferences(); - I->removeFromParent(); - } - return true; -} - -// For integer types shorter than 32 bit, unsigned/signedness can be inferred -// from zext/sext attribute. -MDString * -SPIRVToLLVM::transOCLKernelArgTypeName(SPIRVFunctionParameter *Arg) { - auto Ty = Arg->isByVal() ? Arg->getType()->getPointerElementType() : - Arg->getType(); - return MDString::get(*Context, transTypeToOCLTypeName(Ty, !Arg->isZext())); -} - -// Variable like GlobalInvolcationId[x] -> get_global_id(x). -// Variable like WorkDim -> get_work_dim(). -bool -SPIRVToLLVM::transOCLBuiltinFromVariable(GlobalVariable *GV, - SPIRVBuiltinVariableKind Kind) { - std::string FuncName = SPIRSPIRVBuiltinVariableMap::rmap(Kind); - std::string MangledName; - Type *ReturnTy = GV->getType()->getPointerElementType(); - bool IsVec = ReturnTy->isVectorTy(); - if (IsVec) - ReturnTy = cast(ReturnTy)->getElementType(); - std::vector ArgTy; - if (IsVec) - ArgTy.push_back(Type::getInt32Ty(*Context)); - MangleOpenCLBuiltin(FuncName, ArgTy, MangledName); - Function *Func = M->getFunction(MangledName); - if (!Func) { - FunctionType *FT = FunctionType::get(ReturnTy, ArgTy, false); - Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); - Func->setCallingConv(CallingConv::SPIR_FUNC); - Func->addFnAttr(Attribute::NoUnwind); - Func->addFnAttr(Attribute::ReadNone); - } - std::vector Deletes; - std::vector Uses; - for (auto UI = GV->user_begin(), UE = GV->user_end(); UI != UE; ++UI) { - assert (isa(*UI) && "Unsupported use"); - auto LD = dyn_cast(*UI); - if (!IsVec) { - Uses.push_back(LD); - Deletes.push_back(LD); - continue; - } - for (auto LDUI = LD->user_begin(), LDUE = LD->user_end(); LDUI != LDUE; - ++LDUI) { - assert(isa(*LDUI) && "Unsupported use"); - auto EEI = dyn_cast(*LDUI); - Uses.push_back(EEI); - Deletes.push_back(EEI); - } - Deletes.push_back(LD); - } - for (auto &I:Uses) { - std::vector Arg; - if (auto EEI = dyn_cast(I)) - Arg.push_back(EEI->getIndexOperand()); - auto Call = CallInst::Create(Func, Arg, "", I); - Call->takeName(I); - setAttrByCalledFunc(Call); - SPIRVDBG(dbgs() << "[transOCLBuiltinFromVariable] " << *I << " -> " << - *Call << '\n';) - I->replaceAllUsesWith(Call); - } - for (auto &I:Deletes) { - I->dropAllReferences(); - I->removeFromParent(); - } - return true; -} - -Type * -SPIRVToLLVM::transFPType(SPIRVType* T) { - switch(T->getFloatBitWidth()) { - case 16: return Type::getHalfTy(*Context); - case 32: return Type::getFloatTy(*Context); - case 64: return Type::getDoubleTy(*Context); - default: - llvm_unreachable("Invalid type"); - return nullptr; - } -} - -std::string -SPIRVToLLVM::transOCLImageTypeName(SPIRV::SPIRVTypeImage* ST) { - std::string Name = std::string(kSPR2TypeName::OCLPrefix) - + rmap(ST->getDescriptor()); - if (SPIRVGenImgTypeAccQualPostfix) - Name = Name + kSPR2TypeName::Delimiter - + rmap(ST->getAccessQualifier()); - return std::move(Name); -} - -std::string -SPIRVToLLVM::transOCLSampledImageTypeName(SPIRV::SPIRVTypeSampledImage* ST) { - return getSPIRVTypeName(kSPIRVTypeName::SampledImg, - getSPIRVImageTypePostfixes(getSPIRVImageSampledTypeName( - ST->getImageType()->getSampledType()), - ST->getImageType()->getDescriptor(), - ST->getImageType()->getAccessQualifier())); -} - -std::string -SPIRVToLLVM::transOCLPipeTypeName(SPIRV::SPIRVTypePipe* PT) { - return kSPR2TypeName::Pipe; -} - -Type * -SPIRVToLLVM::transType(SPIRVType *T) { - auto Loc = TypeMap.find(T); - if (Loc != TypeMap.end()) - return Loc->second; - - SPIRVDBG(spvdbgs() << "[transType] " << *T << " -> ";) - T->validate(); - switch(T->getOpCode()) { - case OpTypeVoid: - return mapType(T, Type::getVoidTy(*Context)); - case OpTypeBool: - return mapType(T, Type::getInt1Ty(*Context)); - case OpTypeInt: - return mapType(T, Type::getIntNTy(*Context, T->getIntegerBitWidth())); - case OpTypeFloat: - return mapType(T, transFPType(T)); - case OpTypeArray: - return mapType(T, ArrayType::get(transType(T->getArrayElementType()), - T->getArrayLength())); - case OpTypePointer: - return mapType(T, PointerType::get(transType(T->getPointerElementType()), - SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass()))); - case OpTypeVector: - return mapType(T, VectorType::get(transType(T->getVectorComponentType()), - T->getVectorComponentCount())); - case OpTypeOpaque: - return mapType(T, StructType::create(*Context, T->getName())); - case OpTypeFunction: { - auto FT = static_cast(T); - auto RT = transType(FT->getReturnType()); - std::vector PT; - for (size_t I = 0, E = FT->getNumParameters(); I != E; ++I) - PT.push_back(transType(FT->getParameterType(I))); - return mapType(T, FunctionType::get(RT, PT, false)); - } - case OpTypeImage: { - auto ST = static_cast(T); - if (ST->isOCLImage()) - return mapType(T, getOrCreateOpaquePtrType(M, - transOCLImageTypeName(ST))); - else - llvm_unreachable("Unsupported image type"); - return nullptr; - } - case OpTypeSampler: - return mapType(T, Type::getInt32Ty(*Context)); - case OpTypeSampledImage: { - auto ST = static_cast(T); - return mapType(T, getOrCreateOpaquePtrType(M, - transOCLSampledImageTypeName(ST))); - } - case OpTypeStruct: { - auto ST = static_cast(T); - auto Name = ST->getName(); - if (!Name.empty()) { - if (auto OldST = M->getTypeByName(Name)) - OldST->setName(""); - } - auto *StructTy = StructType::create(*Context, Name); - mapType(ST, StructTy); - SmallVector MT; - for (size_t I = 0, E = ST->getMemberCount(); I != E; ++I) - MT.push_back(transType(ST->getMemberType(I))); - StructTy->setBody(MT, ST->isPacked()); - return StructTy; - } - case OpTypePipe: { - auto PT = static_cast(T); - return mapType(T, getOrCreateOpaquePtrType(M, - transOCLPipeTypeName(PT), - getOCLOpaqueTypeAddrSpace(T->getOpCode()))); - } - default: { - auto OC = T->getOpCode(); - if (isOpaqueGenericTypeOpCode(OC)) - return mapType(T, getOrCreateOpaquePtrType(M, - OCLOpaqueTypeOpCodeMap::rmap(OC), - getOCLOpaqueTypeAddrSpace(OC))); - llvm_unreachable("Not implemented"); - } - } - return 0; -} - -std::string -SPIRVToLLVM::transTypeToOCLTypeName(SPIRVType *T, bool IsSigned) { - switch(T->getOpCode()) { - case OpTypeVoid: - return "void"; - case OpTypeBool: - return "bool"; - case OpTypeInt: { - std::string Prefix = IsSigned ? "" : "u"; - switch(T->getIntegerBitWidth()) { - case 8: - return Prefix + "char"; - case 16: - return Prefix + "short"; - case 32: - return Prefix + "int"; - case 64: - return Prefix + "long"; - default: - llvm_unreachable("invalid integer size"); - return Prefix + std::string("int") + T->getIntegerBitWidth() + "_t"; - } - } - break; - case OpTypeFloat: - switch(T->getFloatBitWidth()){ - case 16: - return "half"; - case 32: - return "float"; - case 64: - return "double"; - default: - llvm_unreachable("invalid floating pointer bitwidth"); - return std::string("float") + T->getFloatBitWidth() + "_t"; - } - break; - case OpTypeArray: - return "array"; - case OpTypePointer: - return transTypeToOCLTypeName(T->getPointerElementType()) + "*"; - case OpTypeVector: - return transTypeToOCLTypeName(T->getVectorComponentType()) + - T->getVectorComponentCount(); - case OpTypeOpaque: - return T->getName(); - case OpTypeFunction: - llvm_unreachable("Unsupported"); - return "function"; - case OpTypeStruct: { - auto Name = T->getName(); - if (Name.find("struct.") == 0) - Name[6] = ' '; - else if (Name.find("union.") == 0) - Name[5] = ' '; - return Name; - } - case OpTypePipe: - return "pipe"; - case OpTypeSampler: - return "sampler_t"; - case OpTypeImage: - return rmap(static_cast(T)->getDescriptor()); - default: - if (isOpaqueGenericTypeOpCode(T->getOpCode())) { - return OCLOpaqueTypeOpCodeMap::rmap(T->getOpCode()); - } - llvm_unreachable("Not implemented"); - return "unknown"; - } -} - -std::vector -SPIRVToLLVM::transTypeVector(const std::vector &BT) { - std::vector T; - for (auto I: BT) - T.push_back(transType(I)); - return T; -} - -std::vector -SPIRVToLLVM::transValue(const std::vector &BV, Function *F, - BasicBlock *BB) { - std::vector V; - for (auto I: BV) - V.push_back(transValue(I, F, BB)); - return V; -} - -bool -SPIRVToLLVM::isSPIRVCmpInstTransToLLVMInst(SPIRVInstruction* BI) const { - auto OC = BI->getOpCode(); - return isCmpOpCode(OC) && - !(OC >= OpLessOrGreater && OC <= OpUnordered); -} - -void -SPIRVToLLVM::transFlags(llvm::Value* V) { - if(!isa(V)) - return; - auto OC = cast(V)->getOpcode(); - if (OC == Instruction::AShr || OC == Instruction::LShr) { - cast(V)->setIsExact(); - return; - } -} - -void -SPIRVToLLVM::setName(llvm::Value* V, SPIRVValue* BV) { - auto Name = BV->getName(); - if (!Name.empty() && (!V->hasName() || Name != V->getName())) - V->setName(Name); -} - -Value * -SPIRVToLLVM::transValue(SPIRVValue *BV, Function *F, BasicBlock *BB, - bool CreatePlaceHolder){ - SPIRVToLLVMValueMap::iterator Loc = ValueMap.find(BV); - if (Loc != ValueMap.end() && (!PlaceholderMap.count(BV) || CreatePlaceHolder)) - return Loc->second; - - SPIRVDBG(spvdbgs() << "[transValue] " << *BV << " -> ";) - BV->validate(); - - auto V = transValueWithoutDecoration(BV, F, BB, CreatePlaceHolder); - if (!V) { - SPIRVDBG(dbgs() << " Warning ! nullptr\n";) - return nullptr; - } - setName(V, BV); - if (!transDecoration(BV, V)) { - assert (0 && "trans decoration fail"); - return nullptr; - } - transFlags(V); - - SPIRVDBG(dbgs() << *V << '\n';) - - return V; -} - -Value * -SPIRVToLLVM::transConvertInst(SPIRVValue* BV, Function* F, BasicBlock* BB) { - SPIRVUnary* BC = static_cast(BV); - auto Src = transValue(BC->getOperand(0), F, BB, BB ? true : false); - auto Dst = transType(BC->getType()); - CastInst::CastOps CO = Instruction::BitCast; - bool IsExt = Dst->getScalarSizeInBits() - > Src->getType()->getScalarSizeInBits(); - switch (BC->getOpCode()) { - case OpPtrCastToGeneric: - case OpGenericCastToPtr: - CO = Instruction::AddrSpaceCast; - break; - case OpSConvert: - CO = IsExt ? Instruction::SExt : Instruction::Trunc; - break; - case OpUConvert: - CO = IsExt ? Instruction::ZExt : Instruction::Trunc; - break; - case OpFConvert: - CO = IsExt ? Instruction::FPExt : Instruction::FPTrunc; - break; - default: - CO = static_cast(OpCodeMap::rmap(BC->getOpCode())); - } - assert(CastInst::isCast(CO) && "Invalid cast op code"); - SPIRVDBG(if (!CastInst::castIsValid(CO, Src, Dst)) { - spvdbgs() << "Invalid cast: " << *BV << " -> "; - dbgs() << "Op = " << CO << ", Src = " << *Src << " Dst = " << *Dst << '\n'; - }) - if (BB) - return CastInst::Create(CO, Src, Dst, BV->getName(), BB); - return ConstantExpr::getCast(CO, dyn_cast(Src), Dst); -} - -BinaryOperator *SPIRVToLLVM::transShiftLogicalBitwiseInst(SPIRVValue* BV, - BasicBlock* BB,Function* F) { - SPIRVBinary* BBN = static_cast(BV); - assert(BB && "Invalid BB"); - Instruction::BinaryOps BO; - auto OP = BBN->getOpCode(); - if (isLogicalOpCode(OP)) - OP = IntBoolOpMap::rmap(OP); - BO = static_cast(OpCodeMap::rmap(OP)); - auto Inst = BinaryOperator::Create(BO, - transValue(BBN->getOperand(0), F, BB), - transValue(BBN->getOperand(1), F, BB), BV->getName(), BB); - return Inst; -} - -Instruction * -SPIRVToLLVM::transCmpInst(SPIRVValue* BV, BasicBlock* BB, Function* F) { - SPIRVCompare* BC = static_cast(BV); - assert(BB && "Invalid BB"); - SPIRVType* BT = BC->getOperand(0)->getType(); - Instruction* Inst = nullptr; - auto OP = BC->getOpCode(); - if (isLogicalOpCode(OP)) - OP = IntBoolOpMap::rmap(OP); - if (BT->isTypeVectorOrScalarInt() || BT->isTypeVectorOrScalarBool() || - BT->isTypePointer()) - Inst = new ICmpInst(*BB, CmpMap::rmap(OP), - transValue(BC->getOperand(0), F, BB), - transValue(BC->getOperand(1), F, BB)); - else if (BT->isTypeVectorOrScalarFloat()) - Inst = new FCmpInst(*BB, CmpMap::rmap(OP), - transValue(BC->getOperand(0), F, BB), - transValue(BC->getOperand(1), F, BB)); - assert(Inst && "not implemented"); - return Inst; -} - -bool -SPIRVToLLVM::postProcessOCL() { - std::string DemangledName; - SPIRVWord SrcLangVer = 0; - BM->getSourceLanguage(&SrcLangVer); - bool isCPP = SrcLangVer == kOCLVer::CL21; - for (auto I = M->begin(), E = M->end(); I != E;) { - auto F = I++; - if (F->hasName() && F->isDeclaration()) { - DEBUG(dbgs() << "[postProcessOCL sret] " << *F << '\n'); - if (F->getReturnType()->isStructTy() && - oclIsBuiltin(F->getName(), &DemangledName, isCPP)) { - if (!postProcessOCLBuiltinReturnStruct(F)) - return false; - } - } - } - for (auto I = M->begin(), E = M->end(); I != E;) { - auto F = I++; - if (F->hasName() && F->isDeclaration()) { - DEBUG(dbgs() << "[postProcessOCL func ptr] " << *F << '\n'); - auto AI = F->arg_begin(); - if (hasFunctionPointerArg(F, AI) && isDecoratedSPIRVFunc(F)) - if (!postProcessOCLBuiltinWithFuncPointer(F, AI)) - return false; - } - } - for (auto I = M->begin(), E = M->end(); I != E;) { - auto F = I++; - if (F->hasName() && F->isDeclaration()) { - DEBUG(dbgs() << "[postProcessOCL array arg] " << *F << '\n'); - if (hasArrayArg(F) && oclIsBuiltin(F->getName(), &DemangledName, isCPP)) - if (!postProcessOCLBuiltinWithArrayArguments(F, DemangledName)) - return false; - } - } - return true; -} - -bool -SPIRVToLLVM::postProcessOCLBuiltinReturnStruct(Function *F) { - std::string Name = F->getName(); - F->setName(Name + ".old"); - for (auto I = F->user_begin(), E = F->user_end(); I != E;) { - if (auto CI = dyn_cast(*I++)) { - auto ST = dyn_cast(*(CI->user_begin())); + if (F->isIntrinsic()) { + return; + } + Call->setCallingConv(F->getCallingConv()); + Call->setAttributes(F->getAttributes()); +} + +bool +SPIRVToLLVM::transOCLBuiltinsFromVariables(){ + std::vector WorkList; + for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) { + SPIRVBuiltinVariableKind Kind; + if (!isSPIRVBuiltinVariable(&*I, &Kind)) + continue; + if (!transOCLBuiltinFromVariable(&*I, Kind)) + return false; + WorkList.push_back(&*I); + } + for (auto &I:WorkList) { + I->dropAllReferences(); + I->removeFromParent(); + } + return true; +} + +// For integer types shorter than 32 bit, unsigned/signedness can be inferred +// from zext/sext attribute. +MDString * +SPIRVToLLVM::transOCLKernelArgTypeName(SPIRVFunctionParameter *Arg) { + auto Ty = Arg->isByVal() ? Arg->getType()->getPointerElementType() : + Arg->getType(); + return MDString::get(*Context, transTypeToOCLTypeName(Ty, !Arg->isZext())); +} + +// Variable like GlobalInvolcationId[x] -> get_global_id(x). +// Variable like WorkDim -> get_work_dim(). +bool +SPIRVToLLVM::transOCLBuiltinFromVariable(GlobalVariable *GV, + SPIRVBuiltinVariableKind Kind) { + std::string FuncName = SPIRSPIRVBuiltinVariableMap::rmap(Kind); + std::string MangledName; + Type *ReturnTy = GV->getType()->getPointerElementType(); + bool IsVec = ReturnTy->isVectorTy(); + if (IsVec) + ReturnTy = cast(ReturnTy)->getElementType(); + std::vector ArgTy; + if (IsVec) + ArgTy.push_back(Type::getInt32Ty(*Context)); + MangleOpenCLBuiltin(FuncName, ArgTy, MangledName); + Function *Func = M->getFunction(MangledName); + if (!Func) { + FunctionType *FT = FunctionType::get(ReturnTy, ArgTy, false); + Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); + Func->setCallingConv(CallingConv::SPIR_FUNC); + Func->addFnAttr(Attribute::NoUnwind); + Func->addFnAttr(Attribute::ReadNone); + } + std::vector Deletes; + std::vector Uses; + for (auto UI = GV->user_begin(), UE = GV->user_end(); UI != UE; ++UI) { + assert (isa(*UI) && "Unsupported use"); + auto LD = dyn_cast(*UI); + if (!IsVec) { + Uses.push_back(LD); + Deletes.push_back(LD); + continue; + } + for (auto LDUI = LD->user_begin(), LDUE = LD->user_end(); LDUI != LDUE; + ++LDUI) { + assert(isa(*LDUI) && "Unsupported use"); + auto EEI = dyn_cast(*LDUI); + Uses.push_back(EEI); + Deletes.push_back(EEI); + } + Deletes.push_back(LD); + } + for (auto &I:Uses) { + std::vector Arg; + if (auto EEI = dyn_cast(I)) + Arg.push_back(EEI->getIndexOperand()); + auto Call = CallInst::Create(Func, Arg, "", I); + Call->takeName(I); + setAttrByCalledFunc(Call); + SPIRVDBG(dbgs() << "[transOCLBuiltinFromVariable] " << *I << " -> " << + *Call << '\n';) + I->replaceAllUsesWith(Call); + } + for (auto &I:Deletes) { + I->dropAllReferences(); + I->removeFromParent(); + } + return true; +} + +Type * +SPIRVToLLVM::transFPType(SPIRVType* T) { + switch(T->getFloatBitWidth()) { + case 16: return Type::getHalfTy(*Context); + case 32: return Type::getFloatTy(*Context); + case 64: return Type::getDoubleTy(*Context); + default: + llvm_unreachable("Invalid type"); + return nullptr; + } +} + +std::string +SPIRVToLLVM::transOCLImageTypeName(SPIRV::SPIRVTypeImage* ST) { + std::string Name = std::string(kSPR2TypeName::OCLPrefix) + + rmap(ST->getDescriptor()); + if (SPIRVGenImgTypeAccQualPostfix) + Name = Name + kSPR2TypeName::Delimiter + + rmap(ST->getAccessQualifier()); + return std::move(Name); +} + +std::string +SPIRVToLLVM::transOCLSampledImageTypeName(SPIRV::SPIRVTypeSampledImage* ST) { + return getSPIRVTypeName(kSPIRVTypeName::SampledImg, + getSPIRVImageTypePostfixes(getSPIRVImageSampledTypeName( + ST->getImageType()->getSampledType()), + ST->getImageType()->getDescriptor(), + ST->getImageType()->getAccessQualifier())); +} + +std::string +SPIRVToLLVM::transOCLPipeTypeName(SPIRV::SPIRVTypePipe* PT) { + return kSPR2TypeName::Pipe; +} + +Type * +SPIRVToLLVM::transType(SPIRVType *T) { + auto Loc = TypeMap.find(T); + if (Loc != TypeMap.end()) + return Loc->second; + + SPIRVDBG(spvdbgs() << "[transType] " << *T << " -> ";) + T->validate(); + switch(T->getOpCode()) { + case OpTypeVoid: + return mapType(T, Type::getVoidTy(*Context)); + case OpTypeBool: + return mapType(T, Type::getInt1Ty(*Context)); + case OpTypeInt: + return mapType(T, Type::getIntNTy(*Context, T->getIntegerBitWidth())); + case OpTypeFloat: + return mapType(T, transFPType(T)); + case OpTypeArray: + return mapType(T, ArrayType::get(transType(T->getArrayElementType()), + T->getArrayLength())); + case OpTypePointer: + return mapType(T, PointerType::get(transType(T->getPointerElementType()), + SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass()))); + case OpTypeVector: + return mapType(T, VectorType::get(transType(T->getVectorComponentType()), + T->getVectorComponentCount())); + case OpTypeOpaque: + return mapType(T, StructType::create(*Context, T->getName())); + case OpTypeFunction: { + auto FT = static_cast(T); + auto RT = transType(FT->getReturnType()); + std::vector PT; + for (size_t I = 0, E = FT->getNumParameters(); I != E; ++I) + PT.push_back(transType(FT->getParameterType(I))); + return mapType(T, FunctionType::get(RT, PT, false)); + } + case OpTypeImage: { + auto ST = static_cast(T); + if (ST->isOCLImage()) + return mapType(T, getOrCreateOpaquePtrType(M, + transOCLImageTypeName(ST))); + else + llvm_unreachable("Unsupported image type"); + return nullptr; + } + case OpTypeSampler: + return mapType(T, Type::getInt32Ty(*Context)); + case OpTypeSampledImage: { + auto ST = static_cast(T); + return mapType(T, getOrCreateOpaquePtrType(M, + transOCLSampledImageTypeName(ST))); + } + case OpTypeStruct: { + auto ST = static_cast(T); + auto Name = ST->getName(); + if (!Name.empty()) { + if (auto OldST = M->getTypeByName(Name)) + OldST->setName(""); + } + auto *StructTy = StructType::create(*Context, Name); + mapType(ST, StructTy); + SmallVector MT; + for (size_t I = 0, E = ST->getMemberCount(); I != E; ++I) + MT.push_back(transType(ST->getMemberType(I))); + StructTy->setBody(MT, ST->isPacked()); + return StructTy; + } + case OpTypePipe: { + auto PT = static_cast(T); + return mapType(T, getOrCreateOpaquePtrType(M, + transOCLPipeTypeName(PT), + getOCLOpaqueTypeAddrSpace(T->getOpCode()))); + } + default: { + auto OC = T->getOpCode(); + if (isOpaqueGenericTypeOpCode(OC)) + return mapType(T, getOrCreateOpaquePtrType(M, + OCLOpaqueTypeOpCodeMap::rmap(OC), + getOCLOpaqueTypeAddrSpace(OC))); + llvm_unreachable("Not implemented"); + } + } + return 0; +} + +std::string +SPIRVToLLVM::transTypeToOCLTypeName(SPIRVType *T, bool IsSigned) { + switch(T->getOpCode()) { + case OpTypeVoid: + return "void"; + case OpTypeBool: + return "bool"; + case OpTypeInt: { + std::string Prefix = IsSigned ? "" : "u"; + switch(T->getIntegerBitWidth()) { + case 8: + return Prefix + "char"; + case 16: + return Prefix + "short"; + case 32: + return Prefix + "int"; + case 64: + return Prefix + "long"; + default: + llvm_unreachable("invalid integer size"); + return Prefix + std::string("int") + T->getIntegerBitWidth() + "_t"; + } + } + break; + case OpTypeFloat: + switch(T->getFloatBitWidth()){ + case 16: + return "half"; + case 32: + return "float"; + case 64: + return "double"; + default: + llvm_unreachable("invalid floating pointer bitwidth"); + return std::string("float") + T->getFloatBitWidth() + "_t"; + } + break; + case OpTypeArray: + return "array"; + case OpTypePointer: + return transTypeToOCLTypeName(T->getPointerElementType()) + "*"; + case OpTypeVector: + return transTypeToOCLTypeName(T->getVectorComponentType()) + + T->getVectorComponentCount(); + case OpTypeOpaque: + return T->getName(); + case OpTypeFunction: + llvm_unreachable("Unsupported"); + return "function"; + case OpTypeStruct: { + auto Name = T->getName(); + if (Name.find("struct.") == 0) + Name[6] = ' '; + else if (Name.find("union.") == 0) + Name[5] = ' '; + return Name; + } + case OpTypePipe: + return "pipe"; + case OpTypeSampler: + return "sampler_t"; + case OpTypeImage: + return rmap(static_cast(T)->getDescriptor()); + default: + if (isOpaqueGenericTypeOpCode(T->getOpCode())) { + return OCLOpaqueTypeOpCodeMap::rmap(T->getOpCode()); + } + llvm_unreachable("Not implemented"); + return "unknown"; + } +} + +std::vector +SPIRVToLLVM::transTypeVector(const std::vector &BT) { + std::vector T; + for (auto I: BT) + T.push_back(transType(I)); + return T; +} + +std::vector +SPIRVToLLVM::transValue(const std::vector &BV, Function *F, + BasicBlock *BB) { + std::vector V; + for (auto I: BV) + V.push_back(transValue(I, F, BB)); + return V; +} + +bool +SPIRVToLLVM::isSPIRVCmpInstTransToLLVMInst(SPIRVInstruction* BI) const { + auto OC = BI->getOpCode(); + return isCmpOpCode(OC) && + !(OC >= OpLessOrGreater && OC <= OpUnordered); +} + +void +SPIRVToLLVM::transFlags(llvm::Value* V) { + if(!isa(V)) + return; + auto OC = cast(V)->getOpcode(); + if (OC == Instruction::AShr || OC == Instruction::LShr) { + cast(V)->setIsExact(); + return; + } +} + +void +SPIRVToLLVM::setName(llvm::Value* V, SPIRVValue* BV) { + auto Name = BV->getName(); + if (!Name.empty() && (!V->hasName() || Name != V->getName())) + V->setName(Name); +} + +Value * +SPIRVToLLVM::transValue(SPIRVValue *BV, Function *F, BasicBlock *BB, + bool CreatePlaceHolder){ + SPIRVToLLVMValueMap::iterator Loc = ValueMap.find(BV); + if (Loc != ValueMap.end() && (!PlaceholderMap.count(BV) || CreatePlaceHolder)) + return Loc->second; + + SPIRVDBG(spvdbgs() << "[transValue] " << *BV << " -> ";) + BV->validate(); + + auto V = transValueWithoutDecoration(BV, F, BB, CreatePlaceHolder); + if (!V) { + SPIRVDBG(dbgs() << " Warning ! nullptr\n";) + return nullptr; + } + setName(V, BV); + if (!transDecoration(BV, V)) { + assert (0 && "trans decoration fail"); + return nullptr; + } + transFlags(V); + + SPIRVDBG(dbgs() << *V << '\n';) + + return V; +} + +Value * +SPIRVToLLVM::transConvertInst(SPIRVValue* BV, Function* F, BasicBlock* BB) { + SPIRVUnary* BC = static_cast(BV); + auto Src = transValue(BC->getOperand(0), F, BB, BB ? true : false); + auto Dst = transType(BC->getType()); + CastInst::CastOps CO = Instruction::BitCast; + bool IsExt = Dst->getScalarSizeInBits() + > Src->getType()->getScalarSizeInBits(); + switch (BC->getOpCode()) { + case OpPtrCastToGeneric: + case OpGenericCastToPtr: + CO = Instruction::AddrSpaceCast; + break; + case OpSConvert: + CO = IsExt ? Instruction::SExt : Instruction::Trunc; + break; + case OpUConvert: + CO = IsExt ? Instruction::ZExt : Instruction::Trunc; + break; + case OpFConvert: + CO = IsExt ? Instruction::FPExt : Instruction::FPTrunc; + break; + default: + CO = static_cast(OpCodeMap::rmap(BC->getOpCode())); + } + assert(CastInst::isCast(CO) && "Invalid cast op code"); + SPIRVDBG(if (!CastInst::castIsValid(CO, Src, Dst)) { + spvdbgs() << "Invalid cast: " << *BV << " -> "; + dbgs() << "Op = " << CO << ", Src = " << *Src << " Dst = " << *Dst << '\n'; + }) + if (BB) + return CastInst::Create(CO, Src, Dst, BV->getName(), BB); + return ConstantExpr::getCast(CO, dyn_cast(Src), Dst); +} + +BinaryOperator *SPIRVToLLVM::transShiftLogicalBitwiseInst(SPIRVValue* BV, + BasicBlock* BB,Function* F) { + SPIRVBinary* BBN = static_cast(BV); + assert(BB && "Invalid BB"); + Instruction::BinaryOps BO; + auto OP = BBN->getOpCode(); + if (isLogicalOpCode(OP)) + OP = IntBoolOpMap::rmap(OP); + BO = static_cast(OpCodeMap::rmap(OP)); + auto Inst = BinaryOperator::Create(BO, + transValue(BBN->getOperand(0), F, BB), + transValue(BBN->getOperand(1), F, BB), BV->getName(), BB); + return Inst; +} + +Instruction * +SPIRVToLLVM::transCmpInst(SPIRVValue* BV, BasicBlock* BB, Function* F) { + SPIRVCompare* BC = static_cast(BV); + assert(BB && "Invalid BB"); + SPIRVType* BT = BC->getOperand(0)->getType(); + Instruction* Inst = nullptr; + auto OP = BC->getOpCode(); + if (isLogicalOpCode(OP)) + OP = IntBoolOpMap::rmap(OP); + if (BT->isTypeVectorOrScalarInt() || BT->isTypeVectorOrScalarBool() || + BT->isTypePointer()) + Inst = new ICmpInst(*BB, CmpMap::rmap(OP), + transValue(BC->getOperand(0), F, BB), + transValue(BC->getOperand(1), F, BB)); + else if (BT->isTypeVectorOrScalarFloat()) + Inst = new FCmpInst(*BB, CmpMap::rmap(OP), + transValue(BC->getOperand(0), F, BB), + transValue(BC->getOperand(1), F, BB)); + assert(Inst && "not implemented"); + return Inst; +} + +bool +SPIRVToLLVM::postProcessOCL() { + std::string DemangledName; + SPIRVWord SrcLangVer = 0; + BM->getSourceLanguage(&SrcLangVer); + bool isCPP = SrcLangVer == kOCLVer::CL21; + for (auto I = M->begin(), E = M->end(); I != E;) { + auto F = I++; + if (F->hasName() && F->isDeclaration()) { + DEBUG(dbgs() << "[postProcessOCL sret] " << *F << '\n'); + if (F->getReturnType()->isStructTy() && + oclIsBuiltin(F->getName(), &DemangledName, isCPP)) { + if (!postProcessOCLBuiltinReturnStruct(&*F)) + return false; + } + } + } + for (auto I = M->begin(), E = M->end(); I != E;) { + auto F = I++; + if (F->hasName() && F->isDeclaration()) { + DEBUG(dbgs() << "[postProcessOCL func ptr] " << *F << '\n'); + auto AI = F->arg_begin(); + if (hasFunctionPointerArg(&*F, AI) && isDecoratedSPIRVFunc(&*F)) + if (!postProcessOCLBuiltinWithFuncPointer(&*F, AI)) + return false; + } + } + for (auto I = M->begin(), E = M->end(); I != E;) { + auto F = I++; + if (F->hasName() && F->isDeclaration()) { + DEBUG(dbgs() << "[postProcessOCL array arg] " << *F << '\n'); + if (hasArrayArg(&*F) && oclIsBuiltin(F->getName(), &DemangledName, isCPP)) + if (!postProcessOCLBuiltinWithArrayArguments(&*F, DemangledName)) + return false; + } + } + return true; +} + +bool +SPIRVToLLVM::postProcessOCLBuiltinReturnStruct(Function *F) { + std::string Name = F->getName(); + F->setName(Name + ".old"); + for (auto I = F->user_begin(), E = F->user_end(); I != E;) { + if (auto CI = dyn_cast(*I++)) { + auto ST = dyn_cast(*(CI->user_begin())); assert(ST); - std::vector ArgTys; - getFunctionTypeParameterTypes(F->getFunctionType(), ArgTys); - ArgTys.insert(ArgTys.begin(), PointerType::get(F->getReturnType(), - SPIRAS_Private)); - auto newF = getOrCreateFunction(M, Type::getVoidTy(*Context), - ArgTys, Name); - newF->setCallingConv(F->getCallingConv()); - auto Args = getArguments(CI); - Args.insert(Args.begin(), ST->getPointerOperand()); - auto NewCI = CallInst::Create(newF, Args, CI->getName(), CI); - NewCI->setCallingConv(CI->getCallingConv()); - ST->dropAllReferences(); - ST->removeFromParent(); - CI->dropAllReferences(); - CI->removeFromParent(); - } - } - F->dropAllReferences(); - F->removeFromParent(); - return true; -} - -bool -SPIRVToLLVM::postProcessOCLBuiltinWithFuncPointer(Function* F, - Function::arg_iterator I) { - auto Name = undecorateSPIRVFunction(F->getName()); - std::set InvokeFuncPtrs; - mutateFunctionOCL (F, [=, &InvokeFuncPtrs]( - CallInst *CI, std::vector &Args) { - auto ALoc = std::find_if(Args.begin(), Args.end(), [](Value * elem) { - return isFunctionPointerType(elem->getType()); - }); - assert(ALoc != Args.end() && "Buit-in must accept a pointer to function"); - assert(isa(*ALoc) && "Invalid function pointer usage"); - Value *Ctx = ALoc[1]; - Value *CtxLen = ALoc[2]; - Value *CtxAlign = ALoc[3]; - if (Name == kOCLBuiltinName::EnqueueKernel) - assert(Args.end() - ALoc > 3); - else - assert(Args.end() - ALoc > 0); - // Erase arguments what are hanled by "spir_block_bind" according to SPIR 2.0 - Args.erase(ALoc + 1, ALoc + 4); - - InvokeFuncPtrs.insert(*ALoc); - // There will be as many calls to spir_block_bind as how much device execution - // bult-ins using this block. This doesn't contradict SPIR 2.0 specification. - *ALoc = addBlockBind(M, cast(removeCast(*ALoc)), - Ctx, CtxLen, CtxAlign, CI); - return Name; - }); - for (auto &I:InvokeFuncPtrs) - eraseIfNoUse(I); - return true; -} - -bool -SPIRVToLLVM::postProcessOCLBuiltinWithArrayArguments(Function* F, - const std::string &DemangledName) { - DEBUG(dbgs() << "[postProcessOCLBuiltinWithArrayArguments] " << *F << '\n'); - auto Attrs = F->getAttributes(); - auto Name = F->getName(); - mutateFunction(F, [=](CallInst *CI, std::vector &Args) { - auto FBegin = CI->getParent()->getParent()->begin()->getFirstInsertionPt(); - for (auto &I:Args) { - auto T = I->getType(); - if (!T->isArrayTy()) - continue; - auto Alloca = new AllocaInst(T, "", FBegin); - auto Store = new StoreInst(I, Alloca, false, CI); - auto Zero = ConstantInt::getNullValue(Type::getInt32Ty(T->getContext())); - Value *Index[] = {Zero, Zero}; - I = GetElementPtrInst::CreateInBounds(Alloca, Index, "", CI); - } - return Name; - }, nullptr, &Attrs); - return true; -} - -// ToDo: Handle unsigned integer return type. May need spec change. -Instruction * -SPIRVToLLVM::postProcessOCLReadImage(SPIRVInstruction *BI, CallInst* CI, - const std::string &FuncName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - StringRef ImageTypeName; - bool isDepthImage = false; - if (isOCLImageType( - (cast(CI->getOperand(0)))->getArgOperand(0)->getType(), - &ImageTypeName)) - isDepthImage = ImageTypeName.endswith("depth_t"); - return mutateCallInstOCL( - M, CI, - [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { - CallInst *CallSampledImg = cast(Args[0]); - auto Img = CallSampledImg->getArgOperand(0); - assert(isOCLImageType(Img->getType())); - auto Sampler = CallSampledImg->getArgOperand(1); - Args[0] = Img; - Args.insert(Args.begin() + 1, Sampler); - if(Args.size() > 4 ) { - ConstantInt* ImOp = dyn_cast(Args[3]); - ConstantFP* LodVal = dyn_cast(Args[4]); - // Drop "Image Operands" argument. - Args.erase(Args.begin() + 3, Args.begin() + 4); - // If the image operand is LOD and its value is zero, drop it too. - if (ImOp && LodVal && LodVal->isNullValue() && - ImOp->getZExtValue() == ImageOperandsMask::ImageOperandsLodMask ) - Args.erase(Args.begin() + 3, Args.end()); - } - if (CallSampledImg->hasOneUse()) { - CallSampledImg->replaceAllUsesWith( - UndefValue::get(CallSampledImg->getType())); - CallSampledImg->dropAllReferences(); - CallSampledImg->eraseFromParent(); - } - Type *T = CI->getType(); - if (auto VT = dyn_cast(T)) - T = VT->getElementType(); - RetTy = isDepthImage ? T : CI->getType(); - return std::string(kOCLBuiltinName::SampledReadImage) + - (T->isFloatingPointTy() ? 'f' : 'i'); - }, - [=](CallInst *NewCI) -> Instruction * { - if (isDepthImage) - return InsertElementInst::Create( - UndefValue::get(VectorType::get(NewCI->getType(), 4)), NewCI, - getSizet(M, 0), "", NewCI->getParent()); - return NewCI; - }, - &Attrs); -} - -CallInst* -SPIRVToLLVM::postProcessOCLWriteImage(SPIRVInstruction *BI, CallInst *CI, - const std::string &DemangledName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - return mutateCallInstOCL(M, CI, [=](CallInst *, std::vector &Args) { - llvm::Type *T = Args[2]->getType(); - if (Args.size() > 4) { - ConstantInt* ImOp = dyn_cast(Args[3]); - ConstantFP* LodVal = dyn_cast(Args[4]); - // Drop "Image Operands" argument. - Args.erase(Args.begin() + 3, Args.begin() + 4); - // If the image operand is LOD and its value is zero, drop it too. - if (ImOp && LodVal && LodVal->isNullValue() && - ImOp->getZExtValue() == ImageOperandsMask::ImageOperandsLodMask ) - Args.erase(Args.begin() + 3, Args.end()); - else - std::swap(Args[2], Args[3]); - } - return std::string(kOCLBuiltinName::WriteImage) + - (T->isFPOrFPVectorTy() ? 'f' : 'i'); - }, &Attrs); -} - -CallInst * -SPIRVToLLVM::postProcessOCLBuildNDRange(SPIRVInstruction *BI, CallInst *CI, - const std::string &FuncName) { - assert(CI->getNumArgOperands() == 3); - auto GWS = CI->getArgOperand(0); - auto LWS = CI->getArgOperand(1); - auto GWO = CI->getArgOperand(2); - CI->setArgOperand(0, GWO); - CI->setArgOperand(1, GWS); - CI->setArgOperand(2, LWS); - return CI; -} - -Instruction * -SPIRVToLLVM::postProcessGroupAllAny(CallInst *CI, - const std::string &DemangledName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - return mutateCallInstSPIRV( - M, CI, - [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { - Type *Int32Ty = Type::getInt32Ty(*Context); - RetTy = Int32Ty; - Args[1] = CastInst::CreateZExtOrBitCast(Args[1], Int32Ty, "", CI); - return DemangledName; - }, - [=](CallInst *NewCI) -> Instruction * { - Type *RetTy = Type::getInt1Ty(*Context); - return CastInst::CreateTruncOrBitCast(NewCI, RetTy, "", - NewCI->getNextNode()); - }, - &Attrs); -} - -CallInst * -SPIRVToLLVM::expandOCLBuiltinWithScalarArg(CallInst* CI, - const std::string &FuncName) { - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - if (!CI->getOperand(0)->getType()->isVectorTy() && - CI->getOperand(1)->getType()->isVectorTy()) { - return mutateCallInstOCL(M, CI, [=](CallInst *, std::vector &Args){ - unsigned vecSize = CI->getOperand(1)->getType()->getVectorNumElements(); - Value *NewVec = nullptr; - if (auto CA = dyn_cast(Args[0])) - NewVec = ConstantVector::getSplat(vecSize, CA); - else { - NewVec = ConstantVector::getSplat(vecSize, - Constant::getNullValue(Args[0]->getType())); - NewVec = InsertElementInst::Create(NewVec, Args[0], getInt32(M, 0), "", - CI); - NewVec = new ShuffleVectorInst(NewVec, NewVec, - ConstantVector::getSplat(vecSize, getInt32(M, 0)), "", CI); - } - NewVec->takeName(Args[0]); - Args[0] = NewVec; - return FuncName; - }, &Attrs); - } - return CI; -} - -std::string -SPIRVToLLVM::transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe* ST) { - return SPIRSPIRVAccessQualifierMap::rmap(ST->getAccessQualifier()); -} - -void -SPIRVToLLVM::transGeneratorMD() { - SPIRVMDBuilder B(*M); - B.addNamedMD(kSPIRVMD::Generator) - .addOp() - .addU16(BM->getGeneratorId()) - .addU16(BM->getGeneratorVer()) - .done(); -} - -Value * -SPIRVToLLVM::oclTransConstantSampler(SPIRV::SPIRVConstantSampler* BCS) { - auto Lit = (BCS->getAddrMode() << 1) | - BCS->getNormalized() | - ((BCS->getFilterMode() + 1) << 4); - auto Ty = IntegerType::getInt32Ty(*Context); - return ConstantInt::get(Ty, Lit); -} - -/// For instructions, this function assumes they are created in order -/// and appended to the given basic block. An instruction may use a -/// instruction from another BB which has not been translated. Such -/// instructions should be translated to place holders at the point -/// of first use, then replaced by real instructions when they are -/// created. -/// -/// When CreatePlaceHolder is true, create a load instruction of a -/// global variable as placeholder for SPIRV instruction. Otherwise, -/// create instruction and replace placeholder if there is one. -Value * -SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, - BasicBlock *BB, bool CreatePlaceHolder){ - - auto OC = BV->getOpCode(); - IntBoolOpMap::rfind(OC, &OC); - - // Translation of non-instruction values - switch(OC) { - case OpConstant: { - SPIRVConstant *BConst = static_cast(BV); - SPIRVType *BT = BV->getType(); - Type *LT = transType(BT); - switch(BT->getOpCode()) { - case OpTypeBool: - case OpTypeInt: - return mapValue(BV, ConstantInt::get(LT, BConst->getZExtIntValue(), - static_cast(BT)->isSigned())); - case OpTypeFloat: { - const llvm::fltSemantics *FS = nullptr; - switch (BT->getFloatBitWidth()) { - case 16: - FS = &APFloat::IEEEhalf; - break; - case 32: - FS = &APFloat::IEEEsingle; - break; - case 64: - FS = &APFloat::IEEEdouble; - break; - default: - llvm_unreachable("invalid float type"); - } - return mapValue(BV, ConstantFP::get(*Context, APFloat(*FS, - APInt(BT->getFloatBitWidth(), BConst->getZExtIntValue())))); - } - default: - llvm_unreachable("Not implemented"); - return nullptr; - } - } - - case OpConstantTrue: - return mapValue(BV, ConstantInt::getTrue(*Context)); - - case OpConstantFalse: - return mapValue(BV, ConstantInt::getFalse(*Context)); - - case OpConstantNull: { - auto LT = transType(BV->getType()); - return mapValue(BV, Constant::getNullValue(LT)); - } - - case OpConstantComposite: { - auto BCC = static_cast(BV); - std::vector CV; - for (auto &I:BCC->getElements()) - CV.push_back(dyn_cast(transValue(I, F, BB))); - switch(BV->getType()->getOpCode()) { - case OpTypeVector: - return mapValue(BV, ConstantVector::get(CV)); - case OpTypeArray: - return mapValue(BV, ConstantArray::get( - dyn_cast(transType(BCC->getType())), CV)); - case OpTypeStruct: - return mapValue(BV, ConstantStruct::get( - dyn_cast(transType(BCC->getType())), CV)); - default: - llvm_unreachable("not implemented"); - return nullptr; - } - } - - case OpConstantSampler: { - auto BCS = static_cast(BV); - return mapValue(BV, oclTransConstantSampler(BCS)); - } - - case OpSpecConstantOp: { - auto BI = createInstFromSpecConstantOp( - static_cast(BV)); - return mapValue(BV, transValue(BI, nullptr, nullptr, false)); - } - - case OpUndef: - return mapValue(BV, UndefValue::get(transType(BV->getType()))); - - case OpVariable: { - auto BVar = static_cast(BV); - auto Ty = transType(BVar->getType()->getPointerElementType()); - bool IsConst = BVar->isConstant(); - llvm::GlobalValue::LinkageTypes LinkageTy = transLinkageType(BVar); - Constant *Initializer = nullptr; - SPIRVValue *Init = BVar->getInitializer(); - if (Init) - Initializer = dyn_cast(transValue(Init, F, BB, false)); - else if (LinkageTy == GlobalValue::CommonLinkage) - // In LLVM variables with common linkage type must be initilized by 0 - Initializer = Constant::getNullValue(Ty); - - SPIRVStorageClassKind BS = BVar->getStorageClass(); - if (BS == StorageClassFunction && !Init) { - assert (BB && "Invalid BB"); - return mapValue(BV, new AllocaInst(Ty, BV->getName(), BB)); - } - auto AddrSpace = SPIRSPIRVAddrSpaceMap::rmap(BS); - auto LVar = new GlobalVariable(*M, Ty, IsConst, LinkageTy, Initializer, - BV->getName(), 0, GlobalVariable::NotThreadLocal, AddrSpace); - LVar->setUnnamedAddr(IsConst && Ty->isArrayTy() && - Ty->getArrayElementType()->isIntegerTy(8)); - SPIRVBuiltinVariableKind BVKind; - if (BVar->isBuiltin(&BVKind)) - BuiltinGVMap[LVar] = BVKind; - return mapValue(BV, LVar); - } - - case OpFunctionParameter: { - auto BA = static_cast(BV); - assert (F && "Invalid function"); - unsigned ArgNo = 0; - for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; - ++I, ++ArgNo) { - if (ArgNo == BA->getArgNo()) - return mapValue(BV, I); - } - llvm_unreachable("Invalid argument"); - return nullptr; - } - - case OpFunction: - return mapValue(BV, transFunction(static_cast(BV))); - - case OpLabel: - return mapValue(BV, BasicBlock::Create(*Context, BV->getName(), F)); - - case OpBitcast: // Can be translated without BB pointer - if(!CreatePlaceHolder) // May be a placeholder - return mapValue(BV, transConvertInst(BV, F, BB)); - - default: - // do nothing - break; - } - - // All other values require valid BB pointer. - assert(BB && "Invalid BB"); - - // Creation of place holder - if (CreatePlaceHolder) { - auto GV = new GlobalVariable(*M, - transType(BV->getType()), - false, - GlobalValue::PrivateLinkage, - nullptr, - std::string(kPlaceholderPrefix) + BV->getName(), - 0, GlobalVariable::NotThreadLocal, 0); - auto LD = new LoadInst(GV, BV->getName(), BB); - PlaceholderMap[BV] = LD; - return mapValue(BV, LD); - } - - // Translation of instructions - switch (BV->getOpCode()) { - case OpBranch: { - auto BR = static_cast(BV); - return mapValue(BV, BranchInst::Create( - dyn_cast(transValue(BR->getTargetLabel(), F, BB)), BB)); - } - - case OpBranchConditional: { - auto BR = static_cast(BV); - return mapValue( - BV, BranchInst::Create( - dyn_cast(transValue(BR->getTrueLabel(), F, BB)), - dyn_cast(transValue(BR->getFalseLabel(), F, BB)), - transValue(BR->getCondition(), F, BB), BB)); - } - - case OpPhi: { - auto Phi = static_cast(BV); - auto LPhi = dyn_cast(mapValue( - BV, PHINode::Create(transType(Phi->getType()), - Phi->getPairs().size() / 2, Phi->getName(), BB))); - Phi->foreachPair([&](SPIRVValue *IncomingV, SPIRVBasicBlock *IncomingBB, - size_t Index) { - auto Translated = transValue(IncomingV, F, BB); - LPhi->addIncoming(Translated, - dyn_cast(transValue(IncomingBB, F, BB))); - }); - return LPhi; - } - - case OpReturn: - return mapValue(BV, ReturnInst::Create(*Context, BB)); - - case OpReturnValue: { - auto RV = static_cast(BV); - return mapValue( - BV, ReturnInst::Create(*Context, - transValue(RV->getReturnValue(), F, BB), BB)); - } - - case OpStore: { - SPIRVStore *BS = static_cast(BV); - return mapValue(BV, - new StoreInst(transValue(BS->getSrc(), F, BB), - transValue(BS->getDst(), F, BB), - BS->SPIRVMemoryAccess::isVolatile(), - BS->SPIRVMemoryAccess::getAlignment(), BB)); - } - - case OpLoad: { - SPIRVLoad *BL = static_cast(BV); - return mapValue(BV, - new LoadInst(transValue(BL->getSrc(), F, BB), BV->getName(), - BL->SPIRVMemoryAccess::isVolatile(), - BL->SPIRVMemoryAccess::getAlignment(), BB)); - } - - case OpCopyMemorySized: { - SPIRVCopyMemorySized *BC = static_cast(BV); - std::string FuncName = "llvm.memcpy"; - SPIRVType* BS = BC->getSource()->getType(); - SPIRVType* BT = BC->getTarget()->getType(); - Type *Int1Ty = Type::getInt1Ty(*Context); - Type* Int32Ty = Type::getInt32Ty(*Context); - Type* VoidTy = Type::getVoidTy(*Context); - Type* SrcTy = transType(BS); - Type* TrgTy = transType(BT); - Type* SizeTy = transType(BC->getSize()->getType()); - Type* ArgTy[] = { TrgTy, SrcTy, SizeTy, Int32Ty, Int1Ty }; - - ostringstream TempName; - TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BT->getPointerStorageClass()) << "i8"; - TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BS->getPointerStorageClass()) << "i8"; - FuncName += TempName.str(); - if (BC->getSize()->getType()->getBitWidth() == 32) - FuncName += ".i32"; - else - FuncName += ".i64"; - - FunctionType *FT = FunctionType::get(VoidTy, ArgTy, false); - Function *Func = dyn_cast(M->getOrInsertFunction(FuncName, FT)); - assert(Func && Func->getFunctionType() == FT && "Function type mismatch"); - Func->setLinkage(GlobalValue::ExternalLinkage); - - if (isFuncNoUnwind()) - Func->addFnAttr(Attribute::NoUnwind); - - Value *Arg[] = { transValue(BC->getTarget(), Func, BB), - transValue(BC->getSource(), Func, BB), - dyn_cast(transValue(BC->getSize(), - Func, BB)), - ConstantInt::get(Int32Ty, - BC->SPIRVMemoryAccess::getAlignment()), - ConstantInt::get(Int1Ty, - BC->SPIRVMemoryAccess::isVolatile())}; - return mapValue( BV, CallInst::Create(Func, Arg, "", BB)); - } - - case OpSelect: { - SPIRVSelect *BS = static_cast(BV); - return mapValue(BV, - SelectInst::Create(transValue(BS->getCondition(), F, BB), - transValue(BS->getTrueValue(), F, BB), - transValue(BS->getFalseValue(), F, BB), - BV->getName(), BB)); - } - - case OpSwitch: { - auto BS = static_cast(BV); - auto Select = transValue(BS->getSelect(), F, BB); - auto LS = SwitchInst::Create( - Select, dyn_cast(transValue(BS->getDefault(), F, BB)), - BS->getNumPairs(), BB); - BS->foreachPair( - [&](SPIRVWord Literal, SPIRVBasicBlock *Label, size_t Index) { - LS->addCase(ConstantInt::get(dyn_cast(Select->getType()), - Literal), - dyn_cast(transValue(Label, F, BB))); - }); - return mapValue(BV, LS); - } - - case OpAccessChain: - case OpInBoundsAccessChain: - case OpPtrAccessChain: - case OpInBoundsPtrAccessChain: { - auto AC = static_cast(BV); - auto Base = transValue(AC->getBase(), F, BB); - auto Index = transValue(AC->getIndices(), F, BB); - if (!AC->hasPtrIndex()) - Index.insert(Index.begin(), getInt32(M, 0)); - auto IsInbound = AC->isInBounds(); - Value *V = nullptr; - if (BB) { - auto GEP = GetElementPtrInst::Create(Base, Index, BV->getName(), BB); - GEP->setIsInBounds(IsInbound); - V = GEP; - } else { - V = ConstantExpr::getGetElementPtr(dyn_cast(Base), Index, - IsInbound); - } - return mapValue(BV, V); - } - - case OpCompositeExtract: { - SPIRVCompositeExtract *CE = static_cast(BV); - assert(CE->getComposite()->getType()->isTypeVector() && "Invalid type"); - assert(CE->getIndices().size() == 1 && "Invalid index"); - return mapValue( - BV, ExtractElementInst::Create( - transValue(CE->getComposite(), F, BB), - ConstantInt::get(*Context, APInt(32, CE->getIndices()[0])), - BV->getName(), BB)); - } - - case OpVectorExtractDynamic: { - auto CE = static_cast(BV); - return mapValue( - BV, ExtractElementInst::Create(transValue(CE->getVector(), F, BB), - transValue(CE->getIndex(), F, BB), - BV->getName(), BB)); - } - - case OpCompositeInsert: { - auto CI = static_cast(BV); - assert(CI->getComposite()->getType()->isTypeVector() && "Invalid type"); - assert(CI->getIndices().size() == 1 && "Invalid index"); - return mapValue( - BV, InsertElementInst::Create( - transValue(CI->getComposite(), F, BB), - transValue(CI->getObject(), F, BB), - ConstantInt::get(*Context, APInt(32, CI->getIndices()[0])), - BV->getName(), BB)); - } - - case OpVectorInsertDynamic: { - auto CI = static_cast(BV); - return mapValue( - BV, InsertElementInst::Create(transValue(CI->getVector(), F, BB), - transValue(CI->getComponent(), F, BB), - transValue(CI->getIndex(), F, BB), - BV->getName(), BB)); - } - - case OpVectorShuffle: { - auto VS = static_cast(BV); - std::vector Components; - IntegerType *Int32Ty = IntegerType::get(*Context, 32); - for (auto I : VS->getComponents()) { - if (I == static_cast(-1)) - Components.push_back(UndefValue::get(Int32Ty)); - else - Components.push_back(ConstantInt::get(Int32Ty, I)); - } - return mapValue(BV, - new ShuffleVectorInst(transValue(VS->getVector1(), F, BB), - transValue(VS->getVector2(), F, BB), - ConstantVector::get(Components), - BV->getName(), BB)); - } - - case OpFunctionCall: { - SPIRVFunctionCall *BC = static_cast(BV); - auto Call = CallInst::Create(transFunction(BC->getFunction()), - transValue(BC->getArgumentValues(), F, BB), - BC->getName(), BB); - setCallingConv(Call); - setAttrByCalledFunc(Call); - return mapValue(BV, Call); - } - - case OpExtInst: - return mapValue( - BV, transOCLBuiltinFromExtInst(static_cast(BV), BB)); - - case OpControlBarrier: - case OpMemoryBarrier: - return mapValue( - BV, transOCLBarrierFence(static_cast(BV), BB)); - - case OpSNegate: { - SPIRVUnary *BC = static_cast(BV); - return mapValue( - BV, BinaryOperator::CreateNSWNeg(transValue(BC->getOperand(0), F, BB), - BV->getName(), BB)); - } - - case OpFNegate: { - SPIRVUnary *BC = static_cast(BV); - return mapValue( - BV, BinaryOperator::CreateFNeg(transValue(BC->getOperand(0), F, BB), - BV->getName(), BB)); - } - - case OpNot: { - SPIRVUnary *BC = static_cast(BV); - return mapValue( - BV, BinaryOperator::CreateNot(transValue(BC->getOperand(0), F, BB), - BV->getName(), BB)); - } - - case OpAll : - case OpAny : - return mapValue(BV, - transOCLAllAny(static_cast(BV), BB)); - - case OpIsFinite : - case OpIsInf : - case OpIsNan : - case OpIsNormal : - case OpSignBitSet : - return mapValue(BV, - transOCLRelational(static_cast(BV), BB)); - - default: { - auto OC = BV->getOpCode(); - if (isSPIRVCmpInstTransToLLVMInst(static_cast(BV))) { - return mapValue(BV, transCmpInst(BV, BB, F)); - } else if (OCLSPIRVBuiltinMap::rfind(OC, nullptr) && - !isAtomicOpCode(OC) && - !isGroupOpCode(OC) && - !isPipeOpCode(OC)) { - return mapValue(BV, transOCLBuiltinFromInst( - static_cast(BV), BB)); - } else if (isBinaryShiftLogicalBitwiseOpCode(OC) || - isLogicalOpCode(OC)) { - return mapValue(BV, transShiftLogicalBitwiseInst(BV, BB, F)); - } else if (isCvtOpCode(OC)) { - auto BI = static_cast(BV); - Value *Inst = nullptr; - if (BI->hasFPRoundingMode() || BI->isSaturatedConversion()) - Inst = transOCLBuiltinFromInst(BI, BB); - else - Inst = transConvertInst(BV, F, BB); - return mapValue(BV, Inst); - } - return mapValue(BV, transSPIRVBuiltinFromInst( - static_cast(BV), BB)); - } - - SPIRVDBG(spvdbgs() << "Cannot translate " << *BV << '\n';) - llvm_unreachable("Translation of SPIRV instruction not implemented"); - return NULL; - } -} - -template -bool -SPIRVToLLVM::foreachFuncCtlMask(SourceTy Source, FuncTy Func) { - SPIRVWord FCM = Source->getFuncCtlMask(); - SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, - SPIRVFunctionControlMaskKind Mask){ - if (FCM & Mask) - Func(Attr); - }); - return true; -} - -Function * -SPIRVToLLVM::transFunction(SPIRVFunction *BF) { - auto Loc = FuncMap.find(BF); - if (Loc != FuncMap.end()) - return Loc->second; - - auto IsKernel = BM->isEntryPoint(ExecutionModelKernel, BF->getId()); - auto Linkage = IsKernel ? GlobalValue::ExternalLinkage : transLinkageType(BF); - FunctionType *FT = dyn_cast(transType(BF->getFunctionType())); - Function *F = dyn_cast(mapValue(BF, Function::Create(FT, Linkage, - BF->getName(), M))); + std::vector ArgTys; + getFunctionTypeParameterTypes(F->getFunctionType(), ArgTys); + ArgTys.insert(ArgTys.begin(), PointerType::get(F->getReturnType(), + SPIRAS_Private)); + auto newF = getOrCreateFunction(M, Type::getVoidTy(*Context), + ArgTys, Name); + newF->setCallingConv(F->getCallingConv()); + auto Args = getArguments(CI); + Args.insert(Args.begin(), ST->getPointerOperand()); + auto NewCI = CallInst::Create(newF, Args, CI->getName(), CI); + NewCI->setCallingConv(CI->getCallingConv()); + ST->dropAllReferences(); + ST->removeFromParent(); + CI->dropAllReferences(); + CI->removeFromParent(); + } + } + F->dropAllReferences(); + F->removeFromParent(); + return true; +} + +bool +SPIRVToLLVM::postProcessOCLBuiltinWithFuncPointer(Function* F, + Function::arg_iterator I) { + auto Name = undecorateSPIRVFunction(F->getName()); + std::set InvokeFuncPtrs; + mutateFunctionOCL (F, [=, &InvokeFuncPtrs]( + CallInst *CI, std::vector &Args) { + auto ALoc = std::find_if(Args.begin(), Args.end(), [](Value * elem) { + return isFunctionPointerType(elem->getType()); + }); + assert(ALoc != Args.end() && "Buit-in must accept a pointer to function"); + assert(isa(*ALoc) && "Invalid function pointer usage"); + Value *Ctx = ALoc[1]; + Value *CtxLen = ALoc[2]; + Value *CtxAlign = ALoc[3]; + if (Name == kOCLBuiltinName::EnqueueKernel) + assert(Args.end() - ALoc > 3); + else + assert(Args.end() - ALoc > 0); + // Erase arguments what are hanled by "spir_block_bind" according to SPIR 2.0 + Args.erase(ALoc + 1, ALoc + 4); + + InvokeFuncPtrs.insert(*ALoc); + // There will be as many calls to spir_block_bind as how much device execution + // bult-ins using this block. This doesn't contradict SPIR 2.0 specification. + *ALoc = addBlockBind(M, cast(removeCast(*ALoc)), + Ctx, CtxLen, CtxAlign, CI); + return Name; + }); + for (auto &I:InvokeFuncPtrs) + eraseIfNoUse(I); + return true; +} + +bool +SPIRVToLLVM::postProcessOCLBuiltinWithArrayArguments(Function* F, + const std::string &DemangledName) { + DEBUG(dbgs() << "[postProcessOCLBuiltinWithArrayArguments] " << *F << '\n'); + auto Attrs = F->getAttributes(); + auto Name = F->getName(); + mutateFunction(F, [=](CallInst *CI, std::vector &Args) { + auto FBegin = CI->getParent()->getParent()->begin()->getFirstInsertionPt(); + for (auto &I:Args) { + auto T = I->getType(); + if (!T->isArrayTy()) + continue; + auto Alloca = new AllocaInst(T, "", &*FBegin); + auto Store = new StoreInst(I, Alloca, false, CI); + auto Zero = ConstantInt::getNullValue(Type::getInt32Ty(T->getContext())); + Value *Index[] = {Zero, Zero}; + I = GetElementPtrInst::CreateInBounds(Alloca, Index, "", CI); + } + return Name; + }, nullptr, &Attrs); + return true; +} + +// ToDo: Handle unsigned integer return type. May need spec change. +Instruction * +SPIRVToLLVM::postProcessOCLReadImage(SPIRVInstruction *BI, CallInst* CI, + const std::string &FuncName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + StringRef ImageTypeName; + bool isDepthImage = false; + if (isOCLImageType( + (cast(CI->getOperand(0)))->getArgOperand(0)->getType(), + &ImageTypeName)) + isDepthImage = ImageTypeName.endswith("depth_t"); + return mutateCallInstOCL( + M, CI, + [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { + CallInst *CallSampledImg = cast(Args[0]); + auto Img = CallSampledImg->getArgOperand(0); + assert(isOCLImageType(Img->getType())); + auto Sampler = CallSampledImg->getArgOperand(1); + Args[0] = Img; + Args.insert(Args.begin() + 1, Sampler); + if(Args.size() > 4 ) { + ConstantInt* ImOp = dyn_cast(Args[3]); + ConstantFP* LodVal = dyn_cast(Args[4]); + // Drop "Image Operands" argument. + Args.erase(Args.begin() + 3, Args.begin() + 4); + // If the image operand is LOD and its value is zero, drop it too. + if (ImOp && LodVal && LodVal->isNullValue() && + ImOp->getZExtValue() == ImageOperandsMask::ImageOperandsLodMask ) + Args.erase(Args.begin() + 3, Args.end()); + } + if (CallSampledImg->hasOneUse()) { + CallSampledImg->replaceAllUsesWith( + UndefValue::get(CallSampledImg->getType())); + CallSampledImg->dropAllReferences(); + CallSampledImg->eraseFromParent(); + } + Type *T = CI->getType(); + if (auto VT = dyn_cast(T)) + T = VT->getElementType(); + RetTy = isDepthImage ? T : CI->getType(); + return std::string(kOCLBuiltinName::SampledReadImage) + + (T->isFloatingPointTy() ? 'f' : 'i'); + }, + [=](CallInst *NewCI) -> Instruction * { + if (isDepthImage) + return InsertElementInst::Create( + UndefValue::get(VectorType::get(NewCI->getType(), 4)), NewCI, + getSizet(M, 0), "", NewCI->getParent()); + return NewCI; + }, + &Attrs); +} + +CallInst* +SPIRVToLLVM::postProcessOCLWriteImage(SPIRVInstruction *BI, CallInst *CI, + const std::string &DemangledName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + return mutateCallInstOCL(M, CI, [=](CallInst *, std::vector &Args) { + llvm::Type *T = Args[2]->getType(); + if (Args.size() > 4) { + ConstantInt* ImOp = dyn_cast(Args[3]); + ConstantFP* LodVal = dyn_cast(Args[4]); + // Drop "Image Operands" argument. + Args.erase(Args.begin() + 3, Args.begin() + 4); + // If the image operand is LOD and its value is zero, drop it too. + if (ImOp && LodVal && LodVal->isNullValue() && + ImOp->getZExtValue() == ImageOperandsMask::ImageOperandsLodMask ) + Args.erase(Args.begin() + 3, Args.end()); + else + std::swap(Args[2], Args[3]); + } + return std::string(kOCLBuiltinName::WriteImage) + + (T->isFPOrFPVectorTy() ? 'f' : 'i'); + }, &Attrs); +} + +CallInst * +SPIRVToLLVM::postProcessOCLBuildNDRange(SPIRVInstruction *BI, CallInst *CI, + const std::string &FuncName) { + assert(CI->getNumArgOperands() == 3); + auto GWS = CI->getArgOperand(0); + auto LWS = CI->getArgOperand(1); + auto GWO = CI->getArgOperand(2); + CI->setArgOperand(0, GWO); + CI->setArgOperand(1, GWS); + CI->setArgOperand(2, LWS); + return CI; +} + +Instruction * +SPIRVToLLVM::postProcessGroupAllAny(CallInst *CI, + const std::string &DemangledName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + return mutateCallInstSPIRV( + M, CI, + [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { + Type *Int32Ty = Type::getInt32Ty(*Context); + RetTy = Int32Ty; + Args[1] = CastInst::CreateZExtOrBitCast(Args[1], Int32Ty, "", CI); + return DemangledName; + }, + [=](CallInst *NewCI) -> Instruction * { + Type *RetTy = Type::getInt1Ty(*Context); + return CastInst::CreateTruncOrBitCast(NewCI, RetTy, "", + NewCI->getNextNode()); + }, + &Attrs); +} + +CallInst * +SPIRVToLLVM::expandOCLBuiltinWithScalarArg(CallInst* CI, + const std::string &FuncName) { + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + if (!CI->getOperand(0)->getType()->isVectorTy() && + CI->getOperand(1)->getType()->isVectorTy()) { + return mutateCallInstOCL(M, CI, [=](CallInst *, std::vector &Args){ + unsigned vecSize = CI->getOperand(1)->getType()->getVectorNumElements(); + Value *NewVec = nullptr; + if (auto CA = dyn_cast(Args[0])) + NewVec = ConstantVector::getSplat(vecSize, CA); + else { + NewVec = ConstantVector::getSplat(vecSize, + Constant::getNullValue(Args[0]->getType())); + NewVec = InsertElementInst::Create(NewVec, Args[0], getInt32(M, 0), "", + CI); + NewVec = new ShuffleVectorInst(NewVec, NewVec, + ConstantVector::getSplat(vecSize, getInt32(M, 0)), "", CI); + } + NewVec->takeName(Args[0]); + Args[0] = NewVec; + return FuncName; + }, &Attrs); + } + return CI; +} + +std::string +SPIRVToLLVM::transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe* ST) { + return SPIRSPIRVAccessQualifierMap::rmap(ST->getAccessQualifier()); +} + +void +SPIRVToLLVM::transGeneratorMD() { + SPIRVMDBuilder B(*M); + B.addNamedMD(kSPIRVMD::Generator) + .addOp() + .addU16(BM->getGeneratorId()) + .addU16(BM->getGeneratorVer()) + .done(); +} + +Value * +SPIRVToLLVM::oclTransConstantSampler(SPIRV::SPIRVConstantSampler* BCS) { + auto Lit = (BCS->getAddrMode() << 1) | + BCS->getNormalized() | + ((BCS->getFilterMode() + 1) << 4); + auto Ty = IntegerType::getInt32Ty(*Context); + return ConstantInt::get(Ty, Lit); +} + +const llvm::fltSemantics & fltSemOf(int width) +{ + switch (width) { + case 16: + return APFloat::IEEEhalf(); + case 32: + return APFloat::IEEEsingle(); + case 64: + return APFloat::IEEEdouble(); + default: + llvm_unreachable("invalid float type"); + } +} +/// For instructions, this function assumes they are created in order +/// and appended to the given basic block. An instruction may use a +/// instruction from another BB which has not been translated. Such +/// instructions should be translated to place holders at the point +/// of first use, then replaced by real instructions when they are +/// created. +/// +/// When CreatePlaceHolder is true, create a load instruction of a +/// global variable as placeholder for SPIRV instruction. Otherwise, +/// create instruction and replace placeholder if there is one. +Value * +SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, + BasicBlock *BB, bool CreatePlaceHolder){ + + auto OC = BV->getOpCode(); + IntBoolOpMap::rfind(OC, &OC); + + // Translation of non-instruction values + switch(OC) { + case OpConstant: { + SPIRVConstant *BConst = static_cast(BV); + SPIRVType *BT = BV->getType(); + Type *LT = transType(BT); + switch(BT->getOpCode()) { + case OpTypeBool: + case OpTypeInt: + return mapValue(BV, ConstantInt::get(LT, BConst->getZExtIntValue(), + static_cast(BT)->isSigned())); + case OpTypeFloat: { + const llvm::fltSemantics &FS = fltSemOf(BT->getFloatBitWidth()); + return mapValue(BV, ConstantFP::get(*Context, APFloat(FS, + APInt(BT->getFloatBitWidth(), BConst->getZExtIntValue())))); + } + default: + llvm_unreachable("Not implemented"); + return nullptr; + } + } + + case OpConstantTrue: + return mapValue(BV, ConstantInt::getTrue(*Context)); + + case OpConstantFalse: + return mapValue(BV, ConstantInt::getFalse(*Context)); + + case OpConstantNull: { + auto LT = transType(BV->getType()); + return mapValue(BV, Constant::getNullValue(LT)); + } + + case OpConstantComposite: { + auto BCC = static_cast(BV); + std::vector CV; + for (auto &I:BCC->getElements()) + CV.push_back(dyn_cast(transValue(I, F, BB))); + switch(BV->getType()->getOpCode()) { + case OpTypeVector: + return mapValue(BV, ConstantVector::get(CV)); + case OpTypeArray: + return mapValue(BV, ConstantArray::get( + dyn_cast(transType(BCC->getType())), CV)); + case OpTypeStruct: + return mapValue(BV, ConstantStruct::get( + dyn_cast(transType(BCC->getType())), CV)); + default: + llvm_unreachable("not implemented"); + return nullptr; + } + } + + case OpConstantSampler: { + auto BCS = static_cast(BV); + return mapValue(BV, oclTransConstantSampler(BCS)); + } + + case OpSpecConstantOp: { + auto BI = createInstFromSpecConstantOp( + static_cast(BV)); + return mapValue(BV, transValue(BI, nullptr, nullptr, false)); + } + + case OpUndef: + return mapValue(BV, UndefValue::get(transType(BV->getType()))); + + case OpVariable: { + auto BVar = static_cast(BV); + auto Ty = transType(BVar->getType()->getPointerElementType()); + bool IsConst = BVar->isConstant(); + llvm::GlobalValue::LinkageTypes LinkageTy = transLinkageType(BVar); + Constant *Initializer = nullptr; + SPIRVValue *Init = BVar->getInitializer(); + if (Init) + Initializer = dyn_cast(transValue(Init, F, BB, false)); + else if (LinkageTy == GlobalValue::CommonLinkage) + // In LLVM variables with common linkage type must be initilized by 0 + Initializer = Constant::getNullValue(Ty); + + SPIRVStorageClassKind BS = BVar->getStorageClass(); + if (BS == StorageClassFunction && !Init) { + assert (BB && "Invalid BB"); + return mapValue(BV, new AllocaInst(Ty, BV->getName(), BB)); + } + auto AddrSpace = SPIRSPIRVAddrSpaceMap::rmap(BS); + auto LVar = new GlobalVariable(*M, Ty, IsConst, LinkageTy, Initializer, + BV->getName(), 0, GlobalVariable::NotThreadLocal, AddrSpace); + LVar->setUnnamedAddr(IsConst && Ty->isArrayTy() && + Ty->getArrayElementType()->isIntegerTy(8) ? GlobalValue::UnnamedAddr::Global + : GlobalValue::UnnamedAddr::None); + SPIRVBuiltinVariableKind BVKind; + if (BVar->isBuiltin(&BVKind)) + BuiltinGVMap[LVar] = BVKind; + return mapValue(BV, LVar); + } + + case OpFunctionParameter: { + auto BA = static_cast(BV); + assert (F && "Invalid function"); + unsigned ArgNo = 0; + for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; + ++I, ++ArgNo) { + if (ArgNo == BA->getArgNo()) + return mapValue(BV, &*I); + } + llvm_unreachable("Invalid argument"); + return nullptr; + } + + case OpFunction: + return mapValue(BV, transFunction(static_cast(BV))); + + case OpLabel: + return mapValue(BV, BasicBlock::Create(*Context, BV->getName(), F)); + + case OpBitcast: // Can be translated without BB pointer + if(!CreatePlaceHolder) // May be a placeholder + return mapValue(BV, transConvertInst(BV, F, BB)); + + default: + // do nothing + break; + } + + // All other values require valid BB pointer. + assert(BB && "Invalid BB"); + + // Creation of place holder + if (CreatePlaceHolder) { + auto GV = new GlobalVariable(*M, + transType(BV->getType()), + false, + GlobalValue::PrivateLinkage, + nullptr, + std::string(kPlaceholderPrefix) + BV->getName(), + 0, GlobalVariable::NotThreadLocal, 0); + auto LD = new LoadInst(GV, BV->getName(), BB); + PlaceholderMap[BV] = LD; + return mapValue(BV, LD); + } + + // Translation of instructions + switch (BV->getOpCode()) { + case OpBranch: { + auto BR = static_cast(BV); + return mapValue(BV, BranchInst::Create( + dyn_cast(transValue(BR->getTargetLabel(), F, BB)), BB)); + } + + case OpBranchConditional: { + auto BR = static_cast(BV); + return mapValue( + BV, BranchInst::Create( + dyn_cast(transValue(BR->getTrueLabel(), F, BB)), + dyn_cast(transValue(BR->getFalseLabel(), F, BB)), + transValue(BR->getCondition(), F, BB), BB)); + } + + case OpPhi: { + auto Phi = static_cast(BV); + auto LPhi = dyn_cast(mapValue( + BV, PHINode::Create(transType(Phi->getType()), + Phi->getPairs().size() / 2, Phi->getName(), BB))); + Phi->foreachPair([&](SPIRVValue *IncomingV, SPIRVBasicBlock *IncomingBB, + size_t Index) { + auto Translated = transValue(IncomingV, F, BB); + LPhi->addIncoming(Translated, + dyn_cast(transValue(IncomingBB, F, BB))); + }); + return LPhi; + } + + case OpReturn: + return mapValue(BV, ReturnInst::Create(*Context, BB)); + + case OpReturnValue: { + auto RV = static_cast(BV); + return mapValue( + BV, ReturnInst::Create(*Context, + transValue(RV->getReturnValue(), F, BB), BB)); + } + + case OpStore: { + SPIRVStore *BS = static_cast(BV); + return mapValue(BV, + new StoreInst(transValue(BS->getSrc(), F, BB), + transValue(BS->getDst(), F, BB), + BS->SPIRVMemoryAccess::isVolatile(), + BS->SPIRVMemoryAccess::getAlignment(), BB)); + } + + case OpLoad: { + SPIRVLoad *BL = static_cast(BV); + return mapValue(BV, + new LoadInst(transValue(BL->getSrc(), F, BB), BV->getName(), + BL->SPIRVMemoryAccess::isVolatile(), + BL->SPIRVMemoryAccess::getAlignment(), BB)); + } + + case OpCopyMemorySized: { + SPIRVCopyMemorySized *BC = static_cast(BV); + std::string FuncName = "llvm.memcpy"; + SPIRVType* BS = BC->getSource()->getType(); + SPIRVType* BT = BC->getTarget()->getType(); + Type *Int1Ty = Type::getInt1Ty(*Context); + Type* Int32Ty = Type::getInt32Ty(*Context); + Type* VoidTy = Type::getVoidTy(*Context); + Type* SrcTy = transType(BS); + Type* TrgTy = transType(BT); + Type* SizeTy = transType(BC->getSize()->getType()); + Type* ArgTy[] = { TrgTy, SrcTy, SizeTy, Int32Ty, Int1Ty }; + + ostringstream TempName; + TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BT->getPointerStorageClass()) << "i8"; + TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BS->getPointerStorageClass()) << "i8"; + FuncName += TempName.str(); + if (BC->getSize()->getType()->getBitWidth() == 32) + FuncName += ".i32"; + else + FuncName += ".i64"; + + FunctionType *FT = FunctionType::get(VoidTy, ArgTy, false); + Function *Func = dyn_cast(M->getOrInsertFunction(FuncName, FT)); + assert(Func && Func->getFunctionType() == FT && "Function type mismatch"); + Func->setLinkage(GlobalValue::ExternalLinkage); + + if (isFuncNoUnwind()) + Func->addFnAttr(Attribute::NoUnwind); + + Value *Arg[] = { transValue(BC->getTarget(), Func, BB), + transValue(BC->getSource(), Func, BB), + dyn_cast(transValue(BC->getSize(), + Func, BB)), + ConstantInt::get(Int32Ty, + BC->SPIRVMemoryAccess::getAlignment()), + ConstantInt::get(Int1Ty, + BC->SPIRVMemoryAccess::isVolatile())}; + return mapValue( BV, CallInst::Create(Func, Arg, "", BB)); + } + + case OpSelect: { + SPIRVSelect *BS = static_cast(BV); + return mapValue(BV, + SelectInst::Create(transValue(BS->getCondition(), F, BB), + transValue(BS->getTrueValue(), F, BB), + transValue(BS->getFalseValue(), F, BB), + BV->getName(), BB)); + } + + case OpSwitch: { + auto BS = static_cast(BV); + auto Select = transValue(BS->getSelect(), F, BB); + auto LS = SwitchInst::Create( + Select, dyn_cast(transValue(BS->getDefault(), F, BB)), + BS->getNumPairs(), BB); + BS->foreachPair( + [&](SPIRVWord Literal, SPIRVBasicBlock *Label, size_t Index) { + LS->addCase(ConstantInt::get(dyn_cast(Select->getType()), + Literal), + dyn_cast(transValue(Label, F, BB))); + }); + return mapValue(BV, LS); + } + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + case OpInBoundsPtrAccessChain: { + auto AC = static_cast(BV); + auto Base = transValue(AC->getBase(), F, BB); + auto Index = transValue(AC->getIndices(), F, BB); + if (!AC->hasPtrIndex()) + Index.insert(Index.begin(), getInt32(M, 0)); + auto IsInbound = AC->isInBounds(); + Value *V = nullptr; + if (BB) { + auto GEP = GetElementPtrInst::Create(Base->getType(),Base, Index, BV->getName(), BB); + GEP->setIsInBounds(IsInbound); + V = GEP; + } else { + auto c = dyn_cast(Base); + V = ConstantExpr::getGetElementPtr(c->getType(), c, Index, + IsInbound); + } + return mapValue(BV, V); + } + + case OpCompositeExtract: { + SPIRVCompositeExtract *CE = static_cast(BV); + assert(CE->getComposite()->getType()->isTypeVector() && "Invalid type"); + assert(CE->getIndices().size() == 1 && "Invalid index"); + return mapValue( + BV, ExtractElementInst::Create( + transValue(CE->getComposite(), F, BB), + ConstantInt::get(*Context, APInt(32, CE->getIndices()[0])), + BV->getName(), BB)); + } + + case OpVectorExtractDynamic: { + auto CE = static_cast(BV); + return mapValue( + BV, ExtractElementInst::Create(transValue(CE->getVector(), F, BB), + transValue(CE->getIndex(), F, BB), + BV->getName(), BB)); + } + + case OpCompositeInsert: { + auto CI = static_cast(BV); + assert(CI->getComposite()->getType()->isTypeVector() && "Invalid type"); + assert(CI->getIndices().size() == 1 && "Invalid index"); + return mapValue( + BV, InsertElementInst::Create( + transValue(CI->getComposite(), F, BB), + transValue(CI->getObject(), F, BB), + ConstantInt::get(*Context, APInt(32, CI->getIndices()[0])), + BV->getName(), BB)); + } + + case OpVectorInsertDynamic: { + auto CI = static_cast(BV); + return mapValue( + BV, InsertElementInst::Create(transValue(CI->getVector(), F, BB), + transValue(CI->getComponent(), F, BB), + transValue(CI->getIndex(), F, BB), + BV->getName(), BB)); + } + + case OpVectorShuffle: { + auto VS = static_cast(BV); + std::vector Components; + IntegerType *Int32Ty = IntegerType::get(*Context, 32); + for (auto I : VS->getComponents()) { + if (I == static_cast(-1)) + Components.push_back(UndefValue::get(Int32Ty)); + else + Components.push_back(ConstantInt::get(Int32Ty, I)); + } + return mapValue(BV, + new ShuffleVectorInst(transValue(VS->getVector1(), F, BB), + transValue(VS->getVector2(), F, BB), + ConstantVector::get(Components), + BV->getName(), BB)); + } + + case OpFunctionCall: { + SPIRVFunctionCall *BC = static_cast(BV); + auto Call = CallInst::Create(transFunction(BC->getFunction()), + transValue(BC->getArgumentValues(), F, BB), + BC->getName(), BB); + setCallingConv(Call); + setAttrByCalledFunc(Call); + return mapValue(BV, Call); + } + + case OpExtInst: + return mapValue( + BV, transOCLBuiltinFromExtInst(static_cast(BV), BB)); + + case OpControlBarrier: + case OpMemoryBarrier: + return mapValue( + BV, transOCLBarrierFence(static_cast(BV), BB)); + + case OpSNegate: { + SPIRVUnary *BC = static_cast(BV); + return mapValue( + BV, BinaryOperator::CreateNSWNeg(transValue(BC->getOperand(0), F, BB), + BV->getName(), BB)); + } + + case OpFNegate: { + SPIRVUnary *BC = static_cast(BV); + return mapValue( + BV, BinaryOperator::CreateFNeg(transValue(BC->getOperand(0), F, BB), + BV->getName(), BB)); + } + + case OpNot: { + SPIRVUnary *BC = static_cast(BV); + return mapValue( + BV, BinaryOperator::CreateNot(transValue(BC->getOperand(0), F, BB), + BV->getName(), BB)); + } + + case OpAll : + case OpAny : + return mapValue(BV, + transOCLAllAny(static_cast(BV), BB)); + + case OpIsFinite : + case OpIsInf : + case OpIsNan : + case OpIsNormal : + case OpSignBitSet : + return mapValue(BV, + transOCLRelational(static_cast(BV), BB)); + + default: { + auto OC = BV->getOpCode(); + if (isSPIRVCmpInstTransToLLVMInst(static_cast(BV))) { + return mapValue(BV, transCmpInst(BV, BB, F)); + } else if (OCLSPIRVBuiltinMap::rfind(OC, nullptr) && + !isAtomicOpCode(OC) && + !isGroupOpCode(OC) && + !isPipeOpCode(OC)) { + return mapValue(BV, transOCLBuiltinFromInst( + static_cast(BV), BB)); + } else if (isBinaryShiftLogicalBitwiseOpCode(OC) || + isLogicalOpCode(OC)) { + return mapValue(BV, transShiftLogicalBitwiseInst(BV, BB, F)); + } else if (isCvtOpCode(OC)) { + auto BI = static_cast(BV); + Value *Inst = nullptr; + if (BI->hasFPRoundingMode() || BI->isSaturatedConversion()) + Inst = transOCLBuiltinFromInst(BI, BB); + else + Inst = transConvertInst(BV, F, BB); + return mapValue(BV, Inst); + } + return mapValue(BV, transSPIRVBuiltinFromInst( + static_cast(BV), BB)); + } + + SPIRVDBG(spvdbgs() << "Cannot translate " << *BV << '\n';) + llvm_unreachable("Translation of SPIRV instruction not implemented"); + return NULL; + } +} + +template +bool +SPIRVToLLVM::foreachFuncCtlMask(SourceTy Source, FuncTy Func) { + SPIRVWord FCM = Source->getFuncCtlMask(); + SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, + SPIRVFunctionControlMaskKind Mask){ + if (FCM & Mask) + Func(Attr); + }); + return true; +} + +Function * +SPIRVToLLVM::transFunction(SPIRVFunction *BF) { + auto Loc = FuncMap.find(BF); + if (Loc != FuncMap.end()) + return Loc->second; + + auto IsKernel = BM->isEntryPoint(ExecutionModelKernel, BF->getId()); + auto Linkage = IsKernel ? GlobalValue::ExternalLinkage : transLinkageType(BF); + FunctionType *FT = dyn_cast(transType(BF->getFunctionType())); + Function *F = dyn_cast(mapValue(BF, Function::Create(FT, Linkage, + BF->getName(), M))); assert(F); - mapFunction(BF, F); - if (!F->isIntrinsic()) { - F->setCallingConv(IsKernel ? CallingConv::SPIR_KERNEL : - CallingConv::SPIR_FUNC); - if (isFuncNoUnwind()) - F->addFnAttr(Attribute::NoUnwind); - foreachFuncCtlMask(BF, [&](Attribute::AttrKind Attr){ - F->addFnAttr(Attr); - }); - } - - for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; - ++I) { - auto BA = BF->getArgument(I->getArgNo()); - mapValue(BA, I); - setName(I, BA); - BA->foreachAttr([&](SPIRVFuncParamAttrKind Kind){ - if (Kind == FunctionParameterAttributeNoWrite) - return; - F->addAttribute(I->getArgNo() + 1, SPIRSPIRVFuncParamAttrMap::rmap(Kind)); - }); - } - BF->foreachReturnValueAttr([&](SPIRVFuncParamAttrKind Kind){ - if (Kind == FunctionParameterAttributeNoWrite) - return; - F->addAttribute(AttributeSet::ReturnIndex, - SPIRSPIRVFuncParamAttrMap::rmap(Kind)); - }); - - // Creating all basic blocks before creating instructions. - for (size_t I = 0, E = BF->getNumBasicBlock(); I != E; ++I) { - transValue(BF->getBasicBlock(I), F, nullptr); - } - - for (size_t I = 0, E = BF->getNumBasicBlock(); I != E; ++I) { - SPIRVBasicBlock *BBB = BF->getBasicBlock(I); - BasicBlock *BB = dyn_cast(transValue(BBB, F, nullptr)); - for (size_t BI = 0, BE = BBB->getNumInst(); BI != BE; ++BI) { - SPIRVInstruction *BInst = BBB->getInst(BI); - transValue(BInst, F, BB, false); - } - } - return F; -} - -/// LLVM convert builtin functions is translated to two instructions: -/// y = i32 islessgreater(float x, float z) -> -/// y = i32 ZExt(bool LessGreater(float x, float z)) -/// When translating back, for simplicity, a trunc instruction is inserted -/// w = bool LessGreater(float x, float z) -> -/// w = bool Trunc(i32 islessgreater(float x, float z)) -/// Optimizer should be able to remove the redundant trunc/zext -void -SPIRVToLLVM::transOCLBuiltinFromInstPreproc(SPIRVInstruction* BI, Type *&RetTy, - std::vector &Args) { - if (!BI->hasType()) - return; - auto BT = BI->getType(); - auto OC = BI->getOpCode(); - if (isCmpOpCode(BI->getOpCode())) { - if (BT->isTypeBool()) - RetTy = IntegerType::getInt32Ty(*Context); - else if (BT->isTypeVectorBool()) - RetTy = VectorType::get(IntegerType::get(*Context, - Args[0]->getType()->getVectorComponentType()->isTypeFloat(64)?64:32), - BT->getVectorComponentCount()); - else - llvm_unreachable("invalid compare instruction"); - } else if (OC == OpGenericCastToPtrExplicit) - Args.pop_back(); - else if (OC == OpImageRead && Args.size() > 2) { - // Drop "Image operands" argument - Args.erase(Args.begin() + 2); - } -} - -Instruction* -SPIRVToLLVM::transOCLBuiltinPostproc(SPIRVInstruction* BI, - CallInst* CI, BasicBlock* BB, const std::string &DemangledName) { - auto OC = BI->getOpCode(); - if (isCmpOpCode(OC) && - BI->getType()->isTypeVectorOrScalarBool()) { - return CastInst::Create(Instruction::Trunc, CI, transType(BI->getType()), - "cvt", BB); - } - if (OC == OpImageSampleExplicitLod) - return postProcessOCLReadImage(BI, CI, DemangledName); - if (OC == OpImageWrite) { - return postProcessOCLWriteImage(BI, CI, DemangledName); - } - if (OC == OpGenericPtrMemSemantics) - return BinaryOperator::CreateShl(CI, getInt32(M, 8), "", BB); - if (OC == OpImageQueryFormat) - return BinaryOperator::CreateSub( - CI, getInt32(M, OCLImageChannelDataTypeOffset), "", BB); - if (OC == OpImageQueryOrder) - return BinaryOperator::CreateSub( - CI, getInt32(M, OCLImageChannelOrderOffset), "", BB); - if (OC == OpBuildNDRange) - return postProcessOCLBuildNDRange(BI, CI, DemangledName); - if (OC == OpGroupAll || OC == OpGroupAny) - return postProcessGroupAllAny(CI, DemangledName); - if (SPIRVEnableStepExpansion && - (DemangledName == "smoothstep" || - DemangledName == "step")) - return expandOCLBuiltinWithScalarArg(CI, DemangledName); - return CI; -} - -Instruction * -SPIRVToLLVM::transBuiltinFromInst(const std::string& FuncName, - SPIRVInstruction* BI, BasicBlock* BB) { - std::string MangledName; - auto Ops = BI->getOperands(); - Type* RetTy = BI->hasType() ? transType(BI->getType()) : - Type::getVoidTy(*Context); - transOCLBuiltinFromInstPreproc(BI, RetTy, Ops); - std::vector ArgTys = transTypeVector( - SPIRVInstruction::getOperandTypes(Ops)); - bool HasFuncPtrArg = false; - for (auto& I:ArgTys) { - if (isa(I)) { - I = PointerType::get(I, SPIRAS_Private); - HasFuncPtrArg = true; - } - } - if (!HasFuncPtrArg) - MangleOpenCLBuiltin(FuncName, ArgTys, MangledName); - else - MangledName = decorateSPIRVFunction(FuncName); - Function* Func = M->getFunction(MangledName); - FunctionType* FT = FunctionType::get(RetTy, ArgTys, false); - // ToDo: Some intermediate functions have duplicate names with - // different function types. This is OK if the function name - // is used internally and finally translated to unique function - // names. However it is better to have a way to differentiate - // between intermidiate functions and final functions and make - // sure final functions have unique names. - SPIRVDBG( - if (!HasFuncPtrArg && Func && Func->getFunctionType() != FT) { - dbgs() << "Warning: Function name conflict:\n" - << *Func << '\n' - << " => " << *FT << '\n'; - } - ) - if (!Func || Func->getFunctionType() != FT) { - DEBUG(for (auto& I:ArgTys) { - dbgs() << *I << '\n'; - }); - Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); - Func->setCallingConv(CallingConv::SPIR_FUNC); - if (isFuncNoUnwind()) - Func->addFnAttr(Attribute::NoUnwind); - } - auto Call = CallInst::Create(Func, - transValue(Ops, BB->getParent(), BB), "", BB); - setName(Call, BI); - setAttrByCalledFunc(Call); - SPIRVDBG(spvdbgs() << "[transInstToBuiltinCall] " << *BI << " -> "; dbgs() << - *Call << '\n';) - Instruction *Inst = Call; - Inst = transOCLBuiltinPostproc(BI, Call, BB, FuncName); - return Inst; -} - -std::string -SPIRVToLLVM::getOCLBuiltinName(SPIRVInstruction* BI) { - auto OC = BI->getOpCode(); - if (OC == OpGenericCastToPtrExplicit) - return getOCLGenericCastToPtrName(BI); - if (isCvtOpCode(OC)) - return getOCLConvertBuiltinName(BI); - if (OC == OpBuildNDRange) { - auto NDRangeInst = static_cast(BI); - auto EleTy = ((NDRangeInst->getOperands())[0])->getType(); - int Dim = EleTy->isTypeArray() ? EleTy->getArrayLength() : 1; - // cygwin does not have std::to_string - ostringstream OS; - OS << Dim; - assert((EleTy->isTypeInt() && Dim == 1) || - (EleTy->isTypeArray() && Dim >= 2 && Dim <= 3)); - return std::string(kOCLBuiltinName::NDRangePrefix) + OS.str() + "D"; - } - auto Name = OCLSPIRVBuiltinMap::rmap(OC); - - SPIRVType *T = nullptr; - switch(OC) { - case OpImageRead: - T = BI->getType(); - break; - case OpImageWrite: - T = BI->getOperands()[2]->getType(); - break; - default: - // do nothing - break; - } - if (T && T->isTypeVector()) - T = T->getVectorComponentType(); - if (T) - Name += T->isTypeFloat()?'f':'i'; - - return Name; -} - -Instruction * -SPIRVToLLVM::transOCLBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB) { - assert(BB && "Invalid BB"); - auto FuncName = getOCLBuiltinName(BI); - return transBuiltinFromInst(FuncName, BI, BB); -} - -Instruction * -SPIRVToLLVM::transSPIRVBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB) { - assert(BB && "Invalid BB"); - return transBuiltinFromInst(getSPIRVFuncName(BI->getOpCode()), BI, BB); -} - -bool -SPIRVToLLVM::translate() { - if (!transAddressingModel()) - return false; - - DbgTran.createCompileUnit(); - DbgTran.addDbgInfoVersion(); - - for (unsigned I = 0, E = BM->getNumVariables(); I != E; ++I) { - auto BV = BM->getVariable(I); - if (BV->getStorageClass() != StorageClassFunction) - transValue(BV, nullptr, nullptr); - } - - for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { - transFunction(BM->getFunction(I)); - } - if (!transKernelMetadata()) - return false; - if (!transFPContractMetadata()) - return false; - if (!transSourceLanguage()) - return false; - if (!transSourceExtension()) - return false; - transGeneratorMD(); - if (!transOCLBuiltinsFromVariables()) - return false; - if (!postProcessOCL()) - return false; - eraseUselessFunctions(M); - DbgTran.finalize(); - return true; -} - -bool -SPIRVToLLVM::transAddressingModel() { - switch (BM->getAddressingModel()) { - case AddressingModelPhysical64: - M->setTargetTriple(SPIR_TARGETTRIPLE64); - M->setDataLayout(SPIR_DATALAYOUT64); - break; - case AddressingModelPhysical32: - M->setTargetTriple(SPIR_TARGETTRIPLE32); - M->setDataLayout(SPIR_DATALAYOUT32); - break; - case AddressingModelLogical: - // Do not set target triple and data layout - break; - default: - SPIRVCKRT(0, InvalidAddressingModel, "Actual addressing mode is " + - (unsigned)BM->getAddressingModel()); - } - return true; -} - -bool -SPIRVToLLVM::transDecoration(SPIRVValue *BV, Value *V) { - if (!transAlign(BV, V)) - return false; - DbgTran.transDbgInfo(BV, V); - return true; -} - -bool -SPIRVToLLVM::transFPContractMetadata() { - bool ContractOff = false; - for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { - SPIRVFunction *BF = BM->getFunction(I); - if (!isOpenCLKernel(BF)) - continue; - if (BF->getExecutionMode(ExecutionModeContractionOff)) { - ContractOff = true; - break; - } - } - if (!ContractOff) - M->getOrInsertNamedMetadata(kSPIR2MD::FPContract); - return true; -} - -std::string SPIRVToLLVM::transOCLImageTypeAccessQualifier( - SPIRV::SPIRVTypeImage* ST) { - return SPIRSPIRVAccessQualifierMap::rmap(ST->getAccessQualifier()); -} - -bool -SPIRVToLLVM::transKernelMetadata() { - NamedMDNode *KernelMDs = M->getOrInsertNamedMetadata(SPIR_MD_KERNELS); - for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { - SPIRVFunction *BF = BM->getFunction(I); - Function *F = static_cast(getTranslatedValue(BF)); - assert(F && "Invalid translated function"); - if (F->getCallingConv() != CallingConv::SPIR_KERNEL) - continue; - std::vector KernelMD; - KernelMD.push_back(ValueAsMetadata::get(F)); - - // Generate metadata for kernel_arg_address_spaces - addOCLKernelArgumentMetadata(Context, KernelMD, - SPIR_MD_KERNEL_ARG_ADDR_SPACE, BF, - [=](SPIRVFunctionParameter *Arg){ - SPIRVType *ArgTy = Arg->getType(); - SPIRAddressSpace AS = SPIRAS_Private; - if (ArgTy->isTypePointer()) - AS = SPIRSPIRVAddrSpaceMap::rmap(ArgTy->getPointerStorageClass()); - else if (ArgTy->isTypeOCLImage() || ArgTy->isTypePipe()) - AS = SPIRAS_Global; - return ConstantAsMetadata::get( - ConstantInt::get(Type::getInt32Ty(*Context), AS)); - }); - // Generate metadata for kernel_arg_access_qual - addOCLKernelArgumentMetadata(Context, KernelMD, - SPIR_MD_KERNEL_ARG_ACCESS_QUAL, BF, - [=](SPIRVFunctionParameter *Arg){ - std::string Qual; - auto T = Arg->getType(); - if (T->isTypeOCLImage()) { - auto ST = static_cast(T); - Qual = transOCLImageTypeAccessQualifier(ST); - } else if (T->isTypePipe()){ - auto PT = static_cast(T); - Qual = transOCLPipeTypeAccessQualifier(PT); - } else - Qual = "none"; - return MDString::get(*Context, Qual); - }); - // Generate metadata for kernel_arg_type - addOCLKernelArgumentMetadata(Context, KernelMD, - SPIR_MD_KERNEL_ARG_TYPE, BF, - [=](SPIRVFunctionParameter *Arg){ - return transOCLKernelArgTypeName(Arg); - }); - // Generate metadata for kernel_arg_type_qual - addOCLKernelArgumentMetadata(Context, KernelMD, - SPIR_MD_KERNEL_ARG_TYPE_QUAL, BF, - [=](SPIRVFunctionParameter *Arg){ - std::string Qual; - if (Arg->hasDecorate(DecorationVolatile)) - Qual = kOCLTypeQualifierName::Volatile; - Arg->foreachAttr([&](SPIRVFuncParamAttrKind Kind){ - Qual += Qual.empty() ? "" : " "; - switch(Kind){ - case FunctionParameterAttributeNoAlias: - Qual += kOCLTypeQualifierName::Restrict; - break; - case FunctionParameterAttributeNoWrite: - Qual += kOCLTypeQualifierName::Const; - break; - default: - // do nothing. - break; - } - }); - if (Arg->getType()->isTypePipe()) { - Qual += Qual.empty() ? "" : " "; - Qual += kOCLTypeQualifierName::Pipe; - } - return MDString::get(*Context, Qual); - }); - // Generate metadata for kernel_arg_base_type - addOCLKernelArgumentMetadata(Context, KernelMD, - SPIR_MD_KERNEL_ARG_BASE_TYPE, BF, - [=](SPIRVFunctionParameter *Arg){ - return transOCLKernelArgTypeName(Arg); - }); - // Generate metadata for kernel_arg_name - if (SPIRVGenKernelArgNameMD) { - bool ArgHasName = true; - BF->foreachArgument([&](SPIRVFunctionParameter *Arg){ - ArgHasName &= !Arg->getName().empty(); - }); - if (ArgHasName) - addOCLKernelArgumentMetadata(Context, KernelMD, - SPIR_MD_KERNEL_ARG_NAME, BF, - [=](SPIRVFunctionParameter *Arg){ - return MDString::get(*Context, Arg->getName()); - }); - } - // Generate metadata for reqd_work_group_size - if (auto EM = BF->getExecutionMode(ExecutionModeLocalSize)) { - KernelMD.push_back(getMDNodeStringIntVec(Context, - kSPIR2MD::WGSize, EM->getLiterals())); - } - // Generate metadata for work_group_size_hint - if (auto EM = BF->getExecutionMode(ExecutionModeLocalSizeHint)) { - KernelMD.push_back(getMDNodeStringIntVec(Context, - kSPIR2MD::WGSizeHint, EM->getLiterals())); - } - // Generate metadata for vec_type_hint - if (auto EM = BF->getExecutionMode(ExecutionModeVecTypeHint)) { - std::vector MetadataVec; - MetadataVec.push_back(MDString::get(*Context, kSPIR2MD::VecTyHint)); - Type *VecHintTy = decodeVecTypeHint(*Context, EM->getLiterals()[0]); + mapFunction(BF, F); + if (!F->isIntrinsic()) { + F->setCallingConv(IsKernel ? CallingConv::SPIR_KERNEL : + CallingConv::SPIR_FUNC); + if (isFuncNoUnwind()) + F->addFnAttr(Attribute::NoUnwind); + foreachFuncCtlMask(BF, [&](Attribute::AttrKind Attr){ + F->addFnAttr(Attr); + }); + } + + for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; + ++I) { + auto BA = BF->getArgument(I->getArgNo()); + mapValue(BA, &*I); + setName(&*I, BA); + BA->foreachAttr([&](SPIRVFuncParamAttrKind Kind){ + if (Kind == FunctionParameterAttributeNoWrite) + return; + F->addAttribute(I->getArgNo() + 1, SPIRSPIRVFuncParamAttrMap::rmap(Kind)); + }); + } + BF->foreachReturnValueAttr([&](SPIRVFuncParamAttrKind Kind){ + if (Kind == FunctionParameterAttributeNoWrite) + return; + F->addAttribute(AttributeSet::ReturnIndex, + SPIRSPIRVFuncParamAttrMap::rmap(Kind)); + }); + + // Creating all basic blocks before creating instructions. + for (size_t I = 0, E = BF->getNumBasicBlock(); I != E; ++I) { + transValue(BF->getBasicBlock(I), F, nullptr); + } + + for (size_t I = 0, E = BF->getNumBasicBlock(); I != E; ++I) { + SPIRVBasicBlock *BBB = BF->getBasicBlock(I); + BasicBlock *BB = dyn_cast(transValue(BBB, F, nullptr)); + for (size_t BI = 0, BE = BBB->getNumInst(); BI != BE; ++BI) { + SPIRVInstruction *BInst = BBB->getInst(BI); + transValue(BInst, F, BB, false); + } + } + return F; +} + +/// LLVM convert builtin functions is translated to two instructions: +/// y = i32 islessgreater(float x, float z) -> +/// y = i32 ZExt(bool LessGreater(float x, float z)) +/// When translating back, for simplicity, a trunc instruction is inserted +/// w = bool LessGreater(float x, float z) -> +/// w = bool Trunc(i32 islessgreater(float x, float z)) +/// Optimizer should be able to remove the redundant trunc/zext +void +SPIRVToLLVM::transOCLBuiltinFromInstPreproc(SPIRVInstruction* BI, Type *&RetTy, + std::vector &Args) { + if (!BI->hasType()) + return; + auto BT = BI->getType(); + auto OC = BI->getOpCode(); + if (isCmpOpCode(BI->getOpCode())) { + if (BT->isTypeBool()) + RetTy = IntegerType::getInt32Ty(*Context); + else if (BT->isTypeVectorBool()) + RetTy = VectorType::get(IntegerType::get(*Context, + Args[0]->getType()->getVectorComponentType()->isTypeFloat(64)?64:32), + BT->getVectorComponentCount()); + else + llvm_unreachable("invalid compare instruction"); + } else if (OC == OpGenericCastToPtrExplicit) + Args.pop_back(); + else if (OC == OpImageRead && Args.size() > 2) { + // Drop "Image operands" argument + Args.erase(Args.begin() + 2); + } +} + +Instruction* +SPIRVToLLVM::transOCLBuiltinPostproc(SPIRVInstruction* BI, + CallInst* CI, BasicBlock* BB, const std::string &DemangledName) { + auto OC = BI->getOpCode(); + if (isCmpOpCode(OC) && + BI->getType()->isTypeVectorOrScalarBool()) { + return CastInst::Create(Instruction::Trunc, CI, transType(BI->getType()), + "cvt", BB); + } + if (OC == OpImageSampleExplicitLod) + return postProcessOCLReadImage(BI, CI, DemangledName); + if (OC == OpImageWrite) { + return postProcessOCLWriteImage(BI, CI, DemangledName); + } + if (OC == OpGenericPtrMemSemantics) + return BinaryOperator::CreateShl(CI, getInt32(M, 8), "", BB); + if (OC == OpImageQueryFormat) + return BinaryOperator::CreateSub( + CI, getInt32(M, OCLImageChannelDataTypeOffset), "", BB); + if (OC == OpImageQueryOrder) + return BinaryOperator::CreateSub( + CI, getInt32(M, OCLImageChannelOrderOffset), "", BB); + if (OC == OpBuildNDRange) + return postProcessOCLBuildNDRange(BI, CI, DemangledName); + if (OC == OpGroupAll || OC == OpGroupAny) + return postProcessGroupAllAny(CI, DemangledName); + if (SPIRVEnableStepExpansion && + (DemangledName == "smoothstep" || + DemangledName == "step")) + return expandOCLBuiltinWithScalarArg(CI, DemangledName); + return CI; +} + +Instruction * +SPIRVToLLVM::transBuiltinFromInst(const std::string& FuncName, + SPIRVInstruction* BI, BasicBlock* BB) { + std::string MangledName; + auto Ops = BI->getOperands(); + Type* RetTy = BI->hasType() ? transType(BI->getType()) : + Type::getVoidTy(*Context); + transOCLBuiltinFromInstPreproc(BI, RetTy, Ops); + std::vector ArgTys = transTypeVector( + SPIRVInstruction::getOperandTypes(Ops)); + bool HasFuncPtrArg = false; + for (auto& I:ArgTys) { + if (isa(I)) { + I = PointerType::get(I, SPIRAS_Private); + HasFuncPtrArg = true; + } + } + if (!HasFuncPtrArg) + MangleOpenCLBuiltin(FuncName, ArgTys, MangledName); + else + MangledName = decorateSPIRVFunction(FuncName); + Function* Func = M->getFunction(MangledName); + FunctionType* FT = FunctionType::get(RetTy, ArgTys, false); + // ToDo: Some intermediate functions have duplicate names with + // different function types. This is OK if the function name + // is used internally and finally translated to unique function + // names. However it is better to have a way to differentiate + // between intermidiate functions and final functions and make + // sure final functions have unique names. + SPIRVDBG( + if (!HasFuncPtrArg && Func && Func->getFunctionType() != FT) { + dbgs() << "Warning: Function name conflict:\n" + << *Func << '\n' + << " => " << *FT << '\n'; + } + ) + if (!Func || Func->getFunctionType() != FT) { + DEBUG(for (auto& I:ArgTys) { + dbgs() << *I << '\n'; + }); + Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); + Func->setCallingConv(CallingConv::SPIR_FUNC); + if (isFuncNoUnwind()) + Func->addFnAttr(Attribute::NoUnwind); + } + auto Call = CallInst::Create(Func, + transValue(Ops, BB->getParent(), BB), "", BB); + setName(Call, BI); + setAttrByCalledFunc(Call); + SPIRVDBG(spvdbgs() << "[transInstToBuiltinCall] " << *BI << " -> "; dbgs() << + *Call << '\n';) + Instruction *Inst = Call; + Inst = transOCLBuiltinPostproc(BI, Call, BB, FuncName); + return Inst; +} + +std::string +SPIRVToLLVM::getOCLBuiltinName(SPIRVInstruction* BI) { + auto OC = BI->getOpCode(); + if (OC == OpGenericCastToPtrExplicit) + return getOCLGenericCastToPtrName(BI); + if (isCvtOpCode(OC)) + return getOCLConvertBuiltinName(BI); + if (OC == OpBuildNDRange) { + auto NDRangeInst = static_cast(BI); + auto EleTy = ((NDRangeInst->getOperands())[0])->getType(); + int Dim = EleTy->isTypeArray() ? EleTy->getArrayLength() : 1; + // cygwin does not have std::to_string + ostringstream OS; + OS << Dim; + assert((EleTy->isTypeInt() && Dim == 1) || + (EleTy->isTypeArray() && Dim >= 2 && Dim <= 3)); + return std::string(kOCLBuiltinName::NDRangePrefix) + OS.str() + "D"; + } + auto Name = OCLSPIRVBuiltinMap::rmap(OC); + + SPIRVType *T = nullptr; + switch(OC) { + case OpImageRead: + T = BI->getType(); + break; + case OpImageWrite: + T = BI->getOperands()[2]->getType(); + break; + default: + // do nothing + break; + } + if (T && T->isTypeVector()) + T = T->getVectorComponentType(); + if (T) + Name += T->isTypeFloat()?'f':'i'; + + return Name; +} + +Instruction * +SPIRVToLLVM::transOCLBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB) { + assert(BB && "Invalid BB"); + auto FuncName = getOCLBuiltinName(BI); + return transBuiltinFromInst(FuncName, BI, BB); +} + +Instruction * +SPIRVToLLVM::transSPIRVBuiltinFromInst(SPIRVInstruction *BI, BasicBlock *BB) { + assert(BB && "Invalid BB"); + return transBuiltinFromInst(getSPIRVFuncName(BI->getOpCode()), BI, BB); +} + +bool +SPIRVToLLVM::translate() { + if (!transAddressingModel()) + return false; + + DbgTran.createCompileUnit(); + DbgTran.addDbgInfoVersion(); + + for (unsigned I = 0, E = BM->getNumVariables(); I != E; ++I) { + auto BV = BM->getVariable(I); + if (BV->getStorageClass() != StorageClassFunction) + transValue(BV, nullptr, nullptr); + } + + for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { + transFunction(BM->getFunction(I)); + } + if (!transKernelMetadata()) + return false; + if (!transFPContractMetadata()) + return false; + if (!transSourceLanguage()) + return false; + if (!transSourceExtension()) + return false; + transGeneratorMD(); + if (!transOCLBuiltinsFromVariables()) + return false; + if (!postProcessOCL()) + return false; + eraseUselessFunctions(M); + DbgTran.finalize(); + return true; +} + +bool +SPIRVToLLVM::transAddressingModel() { + switch (BM->getAddressingModel()) { + case AddressingModelPhysical64: + M->setTargetTriple(SPIR_TARGETTRIPLE64); + M->setDataLayout(SPIR_DATALAYOUT64); + break; + case AddressingModelPhysical32: + M->setTargetTriple(SPIR_TARGETTRIPLE32); + M->setDataLayout(SPIR_DATALAYOUT32); + break; + case AddressingModelLogical: + // Do not set target triple and data layout + break; + default: + SPIRVCKRT(0, InvalidAddressingModel, "Actual addressing mode is " + + (unsigned)BM->getAddressingModel()); + } + return true; +} + +bool +SPIRVToLLVM::transDecoration(SPIRVValue *BV, Value *V) { + if (!transAlign(BV, V)) + return false; + DbgTran.transDbgInfo(BV, V); + return true; +} + +bool +SPIRVToLLVM::transFPContractMetadata() { + bool ContractOff = false; + for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { + SPIRVFunction *BF = BM->getFunction(I); + if (!isOpenCLKernel(BF)) + continue; + if (BF->getExecutionMode(ExecutionModeContractionOff)) { + ContractOff = true; + break; + } + } + if (!ContractOff) + M->getOrInsertNamedMetadata(kSPIR2MD::FPContract); + return true; +} + +std::string SPIRVToLLVM::transOCLImageTypeAccessQualifier( + SPIRV::SPIRVTypeImage* ST) { + return SPIRSPIRVAccessQualifierMap::rmap(ST->getAccessQualifier()); +} + +bool +SPIRVToLLVM::transKernelMetadata() { + NamedMDNode *KernelMDs = M->getOrInsertNamedMetadata(SPIR_MD_KERNELS); + for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) { + SPIRVFunction *BF = BM->getFunction(I); + Function *F = static_cast(getTranslatedValue(BF)); + assert(F && "Invalid translated function"); + if (F->getCallingConv() != CallingConv::SPIR_KERNEL) + continue; + std::vector KernelMD; + KernelMD.push_back(ValueAsMetadata::get(F)); + + // Generate metadata for kernel_arg_address_spaces + addOCLKernelArgumentMetadata(Context, KernelMD, + SPIR_MD_KERNEL_ARG_ADDR_SPACE, BF, + [=](SPIRVFunctionParameter *Arg){ + SPIRVType *ArgTy = Arg->getType(); + SPIRAddressSpace AS = SPIRAS_Private; + if (ArgTy->isTypePointer()) + AS = SPIRSPIRVAddrSpaceMap::rmap(ArgTy->getPointerStorageClass()); + else if (ArgTy->isTypeOCLImage() || ArgTy->isTypePipe()) + AS = SPIRAS_Global; + return ConstantAsMetadata::get( + ConstantInt::get(Type::getInt32Ty(*Context), AS)); + }); + // Generate metadata for kernel_arg_access_qual + addOCLKernelArgumentMetadata(Context, KernelMD, + SPIR_MD_KERNEL_ARG_ACCESS_QUAL, BF, + [=](SPIRVFunctionParameter *Arg){ + std::string Qual; + auto T = Arg->getType(); + if (T->isTypeOCLImage()) { + auto ST = static_cast(T); + Qual = transOCLImageTypeAccessQualifier(ST); + } else if (T->isTypePipe()){ + auto PT = static_cast(T); + Qual = transOCLPipeTypeAccessQualifier(PT); + } else + Qual = "none"; + return MDString::get(*Context, Qual); + }); + // Generate metadata for kernel_arg_type + addOCLKernelArgumentMetadata(Context, KernelMD, + SPIR_MD_KERNEL_ARG_TYPE, BF, + [=](SPIRVFunctionParameter *Arg){ + return transOCLKernelArgTypeName(Arg); + }); + // Generate metadata for kernel_arg_type_qual + addOCLKernelArgumentMetadata(Context, KernelMD, + SPIR_MD_KERNEL_ARG_TYPE_QUAL, BF, + [=](SPIRVFunctionParameter *Arg){ + std::string Qual; + if (Arg->hasDecorate(DecorationVolatile)) + Qual = kOCLTypeQualifierName::Volatile; + Arg->foreachAttr([&](SPIRVFuncParamAttrKind Kind){ + Qual += Qual.empty() ? "" : " "; + switch(Kind){ + case FunctionParameterAttributeNoAlias: + Qual += kOCLTypeQualifierName::Restrict; + break; + case FunctionParameterAttributeNoWrite: + Qual += kOCLTypeQualifierName::Const; + break; + default: + // do nothing. + break; + } + }); + if (Arg->getType()->isTypePipe()) { + Qual += Qual.empty() ? "" : " "; + Qual += kOCLTypeQualifierName::Pipe; + } + return MDString::get(*Context, Qual); + }); + // Generate metadata for kernel_arg_base_type + addOCLKernelArgumentMetadata(Context, KernelMD, + SPIR_MD_KERNEL_ARG_BASE_TYPE, BF, + [=](SPIRVFunctionParameter *Arg){ + return transOCLKernelArgTypeName(Arg); + }); + // Generate metadata for kernel_arg_name + if (SPIRVGenKernelArgNameMD) { + bool ArgHasName = true; + BF->foreachArgument([&](SPIRVFunctionParameter *Arg){ + ArgHasName &= !Arg->getName().empty(); + }); + if (ArgHasName) + addOCLKernelArgumentMetadata(Context, KernelMD, + SPIR_MD_KERNEL_ARG_NAME, BF, + [=](SPIRVFunctionParameter *Arg){ + return MDString::get(*Context, Arg->getName()); + }); + } + // Generate metadata for reqd_work_group_size + if (auto EM = BF->getExecutionMode(ExecutionModeLocalSize)) { + KernelMD.push_back(getMDNodeStringIntVec(Context, + kSPIR2MD::WGSize, EM->getLiterals())); + } + // Generate metadata for work_group_size_hint + if (auto EM = BF->getExecutionMode(ExecutionModeLocalSizeHint)) { + KernelMD.push_back(getMDNodeStringIntVec(Context, + kSPIR2MD::WGSizeHint, EM->getLiterals())); + } + // Generate metadata for vec_type_hint + if (auto EM = BF->getExecutionMode(ExecutionModeVecTypeHint)) { + std::vector MetadataVec; + MetadataVec.push_back(MDString::get(*Context, kSPIR2MD::VecTyHint)); + Type *VecHintTy = decodeVecTypeHint(*Context, EM->getLiterals()[0]); assert(VecHintTy); - MetadataVec.push_back(ValueAsMetadata::get(UndefValue::get(VecHintTy))); - MetadataVec.push_back( - ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), - 1))); - KernelMD.push_back(MDNode::get(*Context, MetadataVec)); - } - - llvm::MDNode *Node = MDNode::get(*Context, KernelMD); - KernelMDs->addOperand(Node); - } - return true; -} - -bool -SPIRVToLLVM::transAlign(SPIRVValue *BV, Value *V) { - if (auto AL = dyn_cast(V)) { - SPIRVWord Align = 0; - if (BV->hasAlignment(&Align)) - AL->setAlignment(Align); - return true; - } - if (auto GV = dyn_cast(V)) { - SPIRVWord Align = 0; - if (BV->hasAlignment(&Align)) - GV->setAlignment(Align); - return true; - } - return true; -} - -void -SPIRVToLLVM::transOCLVectorLoadStore(std::string& UnmangledName, - std::vector &BArgs) { - if (UnmangledName.find("vload") == 0 && - UnmangledName.find("n") != std::string::npos) { - if (BArgs.back() != 1) { - std::stringstream SS; - SS << BArgs.back(); - UnmangledName.replace(UnmangledName.find("n"), 1, SS.str()); - } else { - UnmangledName.erase(UnmangledName.find("n"), 1); - } - BArgs.pop_back(); - } else if (UnmangledName.find("vstore") == 0) { - if (UnmangledName.find("n") != std::string::npos) { - auto T = BM->getValueType(BArgs[0]); - if (T->isTypeVector()) { - auto W = T->getVectorComponentCount(); - std::stringstream SS; - SS << W; - UnmangledName.replace(UnmangledName.find("n"), 1, SS.str()); - } else { - UnmangledName.erase(UnmangledName.find("n"), 1); - } - } - if (UnmangledName.find("_r") != std::string::npos) { - UnmangledName.replace(UnmangledName.find("_r"), 2, std::string("_") + - SPIRSPIRVFPRoundingModeMap::rmap(static_cast( - BArgs.back()))); - BArgs.pop_back(); - } - } -} - -// printf is not mangled. The function type should have just one argument. -// read_image*: the second argument should be mangled as sampler. -Instruction * -SPIRVToLLVM::transOCLBuiltinFromExtInst(SPIRVExtInst *BC, BasicBlock *BB) { - assert(BB && "Invalid BB"); - std::string MangledName; - SPIRVWord EntryPoint = BC->getExtOp(); - SPIRVExtInstSetKind Set = BM->getBuiltinSet(BC->getExtSetId()); - bool IsVarArg = false; - bool IsPrintf = false; - std::string UnmangledName; - auto BArgs = BC->getArguments(); - - assert (Set == SPIRVEIS_OpenCL && "Not OpenCL extended instruction"); - if (EntryPoint == OpenCLLIB::Printf) - IsPrintf = true; - else { - UnmangledName = OCLExtOpMap::map(static_cast( - EntryPoint)); - } - - SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] OrigUnmangledName: " << - UnmangledName << '\n'); - transOCLVectorLoadStore(UnmangledName, BArgs); - - std::vector ArgTypes = transTypeVector(BC->getValueTypes(BArgs)); - - if (IsPrintf) { - MangledName = "printf"; - IsVarArg = true; - ArgTypes.resize(1); - } else if (UnmangledName.find("read_image") == 0) { - auto ModifiedArgTypes = ArgTypes; - ModifiedArgTypes[1] = getOrCreateOpaquePtrType(M, "opencl.sampler_t"); - MangleOpenCLBuiltin(UnmangledName, ModifiedArgTypes, MangledName); - } else { - MangleOpenCLBuiltin(UnmangledName, ArgTypes, MangledName); - } - SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] ModifiedUnmangledName: " << - UnmangledName << " MangledName: " << MangledName << '\n'); - - FunctionType *FT = FunctionType::get( - transType(BC->getType()), - ArgTypes, - IsVarArg); - Function *F = M->getFunction(MangledName); - if (!F) { - F = Function::Create(FT, - GlobalValue::ExternalLinkage, - MangledName, - M); - F->setCallingConv(CallingConv::SPIR_FUNC); - if (isFuncNoUnwind()) - F->addFnAttr(Attribute::NoUnwind); - } - auto Args = transValue(BC->getValues(BArgs), F, BB); - SPIRVDBG(dbgs() << "[transOCLBuiltinFromExtInst] Function: " << *F << - ", Args: "; - for (auto &I:Args) dbgs() << *I << ", "; dbgs() << '\n'); - CallInst *Call = CallInst::Create(F, - Args, - BC->getName(), - BB); - setCallingConv(Call); - addFnAttr(Context, Call, Attribute::NoUnwind); - return transOCLBuiltinPostproc(BC, Call, BB, UnmangledName); -} - -Instruction * -SPIRVToLLVM::transOCLBarrierFence(SPIRVInstruction* MB, BasicBlock *BB) { - assert(BB && "Invalid BB"); - std::string FuncName; - auto getIntVal = [](SPIRVValue *value){ - return static_cast(value)->getZExtIntValue(); - }; - SPIRVWord MemSema = 0; - if (MB->getOpCode() == OpMemoryBarrier) { - auto MemB = static_cast(MB); - FuncName = "mem_fence"; - MemSema = getIntVal(MemB->getOpValue(1)); - } else if (MB->getOpCode() == OpControlBarrier) { - auto CtlB = static_cast(MB); - SPIRVWord Ver = 1; - BM->getSourceLanguage(&Ver); - FuncName = (Ver <= 12) ? kOCLBuiltinName::Barrier : - kOCLBuiltinName::WorkGroupBarrier; - MemSema = getIntVal(CtlB->getMemSemantic()); - } else { - llvm_unreachable("Invalid instruction"); - } - std::string MangledName; - Type* Int32Ty = Type::getInt32Ty(*Context); - Type* VoidTy = Type::getVoidTy(*Context); - Type* ArgTy[] = {Int32Ty}; - MangleOpenCLBuiltin(FuncName, ArgTy, MangledName); - Function *Func = M->getFunction(MangledName); - if (!Func) { - FunctionType *FT = FunctionType::get(VoidTy, ArgTy, false); - Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); - Func->setCallingConv(CallingConv::SPIR_FUNC); - if (isFuncNoUnwind()) - Func->addFnAttr(Attribute::NoUnwind); - } - Value *Arg[] = {ConstantInt::get(Int32Ty, - rmapBitMask(MemSema))}; - auto Call = CallInst::Create(Func, Arg, "", BB); - setName(Call, MB); - setAttrByCalledFunc(Call); - SPIRVDBG(spvdbgs() << "[transBarrier] " << *MB << " -> "; - dbgs() << *Call << '\n';) - return Call; -} - -// SPIR-V only contains language version. Use OpenCL language version as -// SPIR version. -bool -SPIRVToLLVM::transSourceLanguage() { - SPIRVWord Ver = 0; - SourceLanguage Lang = BM->getSourceLanguage(&Ver); - assert((Lang == SourceLanguageOpenCL_C || - Lang == SourceLanguageOpenCL_CPP) && "Unsupported source language"); - unsigned short Major = 0; - unsigned char Minor = 0; - unsigned char Rev = 0; - std::tie(Major, Minor, Rev) = decodeOCLVer(Ver); - SPIRVMDBuilder Builder(*M); - Builder.addNamedMD(kSPIRVMD::Source) - .addOp() - .add(Lang) - .add(Ver) - .done(); - // ToDo: Phasing out usage of old SPIR metadata - if (Ver <= kOCLVer::CL12) - addOCLVersionMetadata(Context, M, kSPIR2MD::SPIRVer, 1, 2); - else - addOCLVersionMetadata(Context, M, kSPIR2MD::SPIRVer, 2, 0); - - addOCLVersionMetadata(Context, M, kSPIR2MD::OCLVer, Major, Minor); - return true; -} - -bool -SPIRVToLLVM::transSourceExtension() { - auto ExtSet = rmap(BM->getExtension()); - auto CapSet = rmap(BM->getCapability()); - ExtSet.insert(CapSet.begin(), CapSet.end()); - auto OCLExtensions = map(ExtSet); - std::set OCLOptionalCoreFeatures; - static const char *OCLOptCoreFeatureNames[] = { - "cl_images", "cl_doubles", - }; - for (auto &I : OCLOptCoreFeatureNames) { - auto Loc = OCLExtensions.find(I); - if (Loc != OCLExtensions.end()) { - OCLExtensions.erase(Loc); - OCLOptionalCoreFeatures.insert(I); - } - } - addNamedMetadataStringSet(Context, M, kSPIR2MD::Extensions, OCLExtensions); - addNamedMetadataStringSet(Context, M, kSPIR2MD::OptFeatures, - OCLOptionalCoreFeatures); - return true; -} - -// If the argument is unsigned return uconvert*, otherwise return convert*. -std::string -SPIRVToLLVM::getOCLConvertBuiltinName(SPIRVInstruction* BI) { - auto OC = BI->getOpCode(); - assert(isCvtOpCode(OC) && "Not convert instruction"); - auto U = static_cast(BI); - std::string Name; - if (isCvtFromUnsignedOpCode(OC)) - Name = "u"; - Name += "convert_"; - Name += mapSPIRVTypeToOCLType(U->getType(), - !isCvtToUnsignedOpCode(OC)); - SPIRVFPRoundingModeKind Rounding; - if (U->isSaturatedConversion()) - Name += "_sat"; - if (U->hasFPRoundingMode(&Rounding)) { - Name += "_"; - Name += SPIRSPIRVFPRoundingModeMap::rmap(Rounding); - } - return Name; -} - -//Check Address Space of the Pointer Type -std::string -SPIRVToLLVM::getOCLGenericCastToPtrName(SPIRVInstruction* BI) { - auto GenericCastToPtrInst = BI->getType()->getPointerStorageClass(); - switch (GenericCastToPtrInst) { - case StorageClassCrossWorkgroup: - return std::string(kOCLBuiltinName::ToGlobal); - case StorageClassWorkgroup: - return std::string(kOCLBuiltinName::ToLocal); - case StorageClassFunction: - return std::string(kOCLBuiltinName::ToPrivate); - default: - llvm_unreachable("Invalid address space"); - return ""; - } -} - -llvm::GlobalValue::LinkageTypes -SPIRVToLLVM::transLinkageType(const SPIRVValue* V) { - if (V->getLinkageType() == LinkageTypeInternal) { - return GlobalValue::InternalLinkage; - } - else if (V->getLinkageType() == LinkageTypeImport) { - // Function declaration - if (V->getOpCode() == OpFunction) { - if (static_cast(V)->getNumBasicBlock() == 0) - return GlobalValue::ExternalLinkage; - } - // Variable declaration - if (V->getOpCode() == OpVariable) { - if (static_cast(V)->getInitializer() == 0) - return GlobalValue::ExternalLinkage; - } - // Definition - return GlobalValue::AvailableExternallyLinkage; - } - else {// LinkageTypeExport - if (V->getOpCode() == OpVariable) { - if (static_cast(V)->getInitializer() == 0 ) - // Tentative definition - return GlobalValue::CommonLinkage; - } - return GlobalValue::ExternalLinkage; - } -} - -Instruction *SPIRVToLLVM::transOCLAllAny(SPIRVInstruction *I, BasicBlock *BB) { - CallInst *CI = cast(transSPIRVBuiltinFromInst(I, BB)); - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - return cast(mapValue( - I, mutateCallInstOCL( - M, CI, - [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { - Type *Int32Ty = Type::getInt32Ty(*Context); - auto OldArg = CI->getOperand(0); - auto NewArgTy = VectorType::get( - Int32Ty, OldArg->getType()->getVectorNumElements()); - auto NewArg = - CastInst::CreateSExtOrBitCast(OldArg, NewArgTy, "", CI); - Args[0] = NewArg; - RetTy = Int32Ty; - return CI->getCalledFunction()->getName(); - }, - [=](CallInst *NewCI) -> Instruction * { - return CastInst::CreateTruncOrBitCast( - NewCI, Type::getInt1Ty(*Context), "", NewCI->getNextNode()); - }, - &Attrs))); -} - -Instruction *SPIRVToLLVM::transOCLRelational(SPIRVInstruction *I, BasicBlock *BB) { - CallInst *CI = cast(transSPIRVBuiltinFromInst(I, BB)); - AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); - return cast(mapValue( - I, mutateCallInstOCL( - M, CI, - [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { - Type *IntTy = Type::getInt32Ty(*Context); - RetTy = IntTy; - if (CI->getType()->isVectorTy()) { - if (cast(CI->getOperand(0)->getType()) - ->getElementType() - ->isDoubleTy()) - IntTy = Type::getInt64Ty(*Context); - if (cast(CI->getOperand(0)->getType()) - ->getElementType() - ->isHalfTy()) - IntTy = Type::getInt16Ty(*Context); - RetTy = VectorType::get(IntTy, - CI->getType()->getVectorNumElements()); - } - return CI->getCalledFunction()->getName(); - }, - [=](CallInst *NewCI) -> Instruction * { - Type *RetTy = Type::getInt1Ty(*Context); - if (NewCI->getType()->isVectorTy()) - RetTy = - VectorType::get(Type::getInt1Ty(*Context), - NewCI->getType()->getVectorNumElements()); - return CastInst::CreateTruncOrBitCast(NewCI, RetTy, "", - NewCI->getNextNode()); - }, - &Attrs))); -} -} - -bool -llvm::ReadSPIRV(LLVMContext &C, std::istream &IS, Module *&M, - std::string &ErrMsg) { - M = new Module("", C); - std::unique_ptr BM(SPIRVModule::createSPIRVModule()); - - BM->setAutoAddCapability(false); - IS >> *BM; - - SPIRVToLLVM BTL(M, BM.get()); - bool Succeed = true; - if (!BTL.translate()) { - BM->getError(ErrMsg); - Succeed = false; - } - PassManager PassMgr; - PassMgr.add(createSPIRVToOCL20()); - PassMgr.add(createOCL20To12()); - PassMgr.run(*M); - - if (DbgSaveTmpLLVM) - dumpLLVM(M, DbgTmpLLVMFileName); - if (!Succeed) { - delete M; - M = nullptr; - } - return Succeed; -} + MetadataVec.push_back(ValueAsMetadata::get(UndefValue::get(VecHintTy))); + MetadataVec.push_back( + ConstantAsMetadata::get(ConstantInt::get(Type::getInt32Ty(*Context), + 1))); + KernelMD.push_back(MDNode::get(*Context, MetadataVec)); + } + + llvm::MDNode *Node = MDNode::get(*Context, KernelMD); + KernelMDs->addOperand(Node); + } + return true; +} + +bool +SPIRVToLLVM::transAlign(SPIRVValue *BV, Value *V) { + if (auto AL = dyn_cast(V)) { + SPIRVWord Align = 0; + if (BV->hasAlignment(&Align)) + AL->setAlignment(Align); + return true; + } + if (auto GV = dyn_cast(V)) { + SPIRVWord Align = 0; + if (BV->hasAlignment(&Align)) + GV->setAlignment(Align); + return true; + } + return true; +} + +void +SPIRVToLLVM::transOCLVectorLoadStore(std::string& UnmangledName, + std::vector &BArgs) { + if (UnmangledName.find("vload") == 0 && + UnmangledName.find("n") != std::string::npos) { + if (BArgs.back() != 1) { + std::stringstream SS; + SS << BArgs.back(); + UnmangledName.replace(UnmangledName.find("n"), 1, SS.str()); + } else { + UnmangledName.erase(UnmangledName.find("n"), 1); + } + BArgs.pop_back(); + } else if (UnmangledName.find("vstore") == 0) { + if (UnmangledName.find("n") != std::string::npos) { + auto T = BM->getValueType(BArgs[0]); + if (T->isTypeVector()) { + auto W = T->getVectorComponentCount(); + std::stringstream SS; + SS << W; + UnmangledName.replace(UnmangledName.find("n"), 1, SS.str()); + } else { + UnmangledName.erase(UnmangledName.find("n"), 1); + } + } + if (UnmangledName.find("_r") != std::string::npos) { + UnmangledName.replace(UnmangledName.find("_r"), 2, std::string("_") + + SPIRSPIRVFPRoundingModeMap::rmap(static_cast( + BArgs.back()))); + BArgs.pop_back(); + } + } +} + +// printf is not mangled. The function type should have just one argument. +// read_image*: the second argument should be mangled as sampler. +Instruction * +SPIRVToLLVM::transOCLBuiltinFromExtInst(SPIRVExtInst *BC, BasicBlock *BB) { + assert(BB && "Invalid BB"); + std::string MangledName; + SPIRVWord EntryPoint = BC->getExtOp(); + SPIRVExtInstSetKind Set = BM->getBuiltinSet(BC->getExtSetId()); + bool IsVarArg = false; + bool IsPrintf = false; + std::string UnmangledName; + auto BArgs = BC->getArguments(); + + assert (Set == SPIRVEIS_OpenCL && "Not OpenCL extended instruction"); + if (EntryPoint == OpenCLLIB::Printf) + IsPrintf = true; + else { + UnmangledName = OCLExtOpMap::map(static_cast( + EntryPoint)); + } + + SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] OrigUnmangledName: " << + UnmangledName << '\n'); + transOCLVectorLoadStore(UnmangledName, BArgs); + + std::vector ArgTypes = transTypeVector(BC->getValueTypes(BArgs)); + + if (IsPrintf) { + MangledName = "printf"; + IsVarArg = true; + ArgTypes.resize(1); + } else if (UnmangledName.find("read_image") == 0) { + auto ModifiedArgTypes = ArgTypes; + ModifiedArgTypes[1] = getOrCreateOpaquePtrType(M, "opencl.sampler_t"); + MangleOpenCLBuiltin(UnmangledName, ModifiedArgTypes, MangledName); + } else { + MangleOpenCLBuiltin(UnmangledName, ArgTypes, MangledName); + } + SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] ModifiedUnmangledName: " << + UnmangledName << " MangledName: " << MangledName << '\n'); + + FunctionType *FT = FunctionType::get( + transType(BC->getType()), + ArgTypes, + IsVarArg); + Function *F = M->getFunction(MangledName); + if (!F) { + F = Function::Create(FT, + GlobalValue::ExternalLinkage, + MangledName, + M); + F->setCallingConv(CallingConv::SPIR_FUNC); + if (isFuncNoUnwind()) + F->addFnAttr(Attribute::NoUnwind); + } + auto Args = transValue(BC->getValues(BArgs), F, BB); + SPIRVDBG(dbgs() << "[transOCLBuiltinFromExtInst] Function: " << *F << + ", Args: "; + for (auto &I:Args) dbgs() << *I << ", "; dbgs() << '\n'); + CallInst *Call = CallInst::Create(F, + Args, + BC->getName(), + BB); + setCallingConv(Call); + addFnAttr(Context, Call, Attribute::NoUnwind); + return transOCLBuiltinPostproc(BC, Call, BB, UnmangledName); +} + +Instruction * +SPIRVToLLVM::transOCLBarrierFence(SPIRVInstruction* MB, BasicBlock *BB) { + assert(BB && "Invalid BB"); + std::string FuncName; + auto getIntVal = [](SPIRVValue *value){ + return static_cast(value)->getZExtIntValue(); + }; + SPIRVWord MemSema = 0; + if (MB->getOpCode() == OpMemoryBarrier) { + auto MemB = static_cast(MB); + FuncName = "mem_fence"; + MemSema = getIntVal(MemB->getOpValue(1)); + } else if (MB->getOpCode() == OpControlBarrier) { + auto CtlB = static_cast(MB); + SPIRVWord Ver = 1; + BM->getSourceLanguage(&Ver); + FuncName = (Ver <= 12) ? kOCLBuiltinName::Barrier : + kOCLBuiltinName::WorkGroupBarrier; + MemSema = getIntVal(CtlB->getMemSemantic()); + } else { + llvm_unreachable("Invalid instruction"); + } + std::string MangledName; + Type* Int32Ty = Type::getInt32Ty(*Context); + Type* VoidTy = Type::getVoidTy(*Context); + Type* ArgTy[] = {Int32Ty}; + MangleOpenCLBuiltin(FuncName, ArgTy, MangledName); + Function *Func = M->getFunction(MangledName); + if (!Func) { + FunctionType *FT = FunctionType::get(VoidTy, ArgTy, false); + Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M); + Func->setCallingConv(CallingConv::SPIR_FUNC); + if (isFuncNoUnwind()) + Func->addFnAttr(Attribute::NoUnwind); + } + Value *Arg[] = {ConstantInt::get(Int32Ty, + rmapBitMask(MemSema))}; + auto Call = CallInst::Create(Func, Arg, "", BB); + setName(Call, MB); + setAttrByCalledFunc(Call); + SPIRVDBG(spvdbgs() << "[transBarrier] " << *MB << " -> "; + dbgs() << *Call << '\n';) + return Call; +} + +// SPIR-V only contains language version. Use OpenCL language version as +// SPIR version. +bool +SPIRVToLLVM::transSourceLanguage() { + SPIRVWord Ver = 0; + SourceLanguage Lang = BM->getSourceLanguage(&Ver); + assert((Lang == SourceLanguageOpenCL_C || + Lang == SourceLanguageOpenCL_CPP) && "Unsupported source language"); + unsigned short Major = 0; + unsigned char Minor = 0; + unsigned char Rev = 0; + std::tie(Major, Minor, Rev) = decodeOCLVer(Ver); + SPIRVMDBuilder Builder(*M); + Builder.addNamedMD(kSPIRVMD::Source) + .addOp() + .add(Lang) + .add(Ver) + .done(); + // ToDo: Phasing out usage of old SPIR metadata + if (Ver <= kOCLVer::CL12) + addOCLVersionMetadata(Context, M, kSPIR2MD::SPIRVer, 1, 2); + else + addOCLVersionMetadata(Context, M, kSPIR2MD::SPIRVer, 2, 0); + + addOCLVersionMetadata(Context, M, kSPIR2MD::OCLVer, Major, Minor); + return true; +} + +bool +SPIRVToLLVM::transSourceExtension() { + auto ExtSet = rmap(BM->getExtension()); + auto CapSet = rmap(BM->getCapability()); + ExtSet.insert(CapSet.begin(), CapSet.end()); + auto OCLExtensions = map(ExtSet); + std::set OCLOptionalCoreFeatures; + static const char *OCLOptCoreFeatureNames[] = { + "cl_images", "cl_doubles", + }; + for (auto &I : OCLOptCoreFeatureNames) { + auto Loc = OCLExtensions.find(I); + if (Loc != OCLExtensions.end()) { + OCLExtensions.erase(Loc); + OCLOptionalCoreFeatures.insert(I); + } + } + addNamedMetadataStringSet(Context, M, kSPIR2MD::Extensions, OCLExtensions); + addNamedMetadataStringSet(Context, M, kSPIR2MD::OptFeatures, + OCLOptionalCoreFeatures); + return true; +} + +// If the argument is unsigned return uconvert*, otherwise return convert*. +std::string +SPIRVToLLVM::getOCLConvertBuiltinName(SPIRVInstruction* BI) { + auto OC = BI->getOpCode(); + assert(isCvtOpCode(OC) && "Not convert instruction"); + auto U = static_cast(BI); + std::string Name; + if (isCvtFromUnsignedOpCode(OC)) + Name = "u"; + Name += "convert_"; + Name += mapSPIRVTypeToOCLType(U->getType(), + !isCvtToUnsignedOpCode(OC)); + SPIRVFPRoundingModeKind Rounding; + if (U->isSaturatedConversion()) + Name += "_sat"; + if (U->hasFPRoundingMode(&Rounding)) { + Name += "_"; + Name += SPIRSPIRVFPRoundingModeMap::rmap(Rounding); + } + return Name; +} + +//Check Address Space of the Pointer Type +std::string +SPIRVToLLVM::getOCLGenericCastToPtrName(SPIRVInstruction* BI) { + auto GenericCastToPtrInst = BI->getType()->getPointerStorageClass(); + switch (GenericCastToPtrInst) { + case StorageClassCrossWorkgroup: + return std::string(kOCLBuiltinName::ToGlobal); + case StorageClassWorkgroup: + return std::string(kOCLBuiltinName::ToLocal); + case StorageClassFunction: + return std::string(kOCLBuiltinName::ToPrivate); + default: + llvm_unreachable("Invalid address space"); + return ""; + } +} + +llvm::GlobalValue::LinkageTypes +SPIRVToLLVM::transLinkageType(const SPIRVValue* V) { + if (V->getLinkageType() == LinkageTypeInternal) { + return GlobalValue::InternalLinkage; + } + else if (V->getLinkageType() == LinkageTypeImport) { + // Function declaration + if (V->getOpCode() == OpFunction) { + if (static_cast(V)->getNumBasicBlock() == 0) + return GlobalValue::ExternalLinkage; + } + // Variable declaration + if (V->getOpCode() == OpVariable) { + if (static_cast(V)->getInitializer() == 0) + return GlobalValue::ExternalLinkage; + } + // Definition + return GlobalValue::AvailableExternallyLinkage; + } + else {// LinkageTypeExport + if (V->getOpCode() == OpVariable) { + if (static_cast(V)->getInitializer() == 0 ) + // Tentative definition + return GlobalValue::CommonLinkage; + } + return GlobalValue::ExternalLinkage; + } +} + +Instruction *SPIRVToLLVM::transOCLAllAny(SPIRVInstruction *I, BasicBlock *BB) { + CallInst *CI = cast(transSPIRVBuiltinFromInst(I, BB)); + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + return cast(mapValue( + I, mutateCallInstOCL( + M, CI, + [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { + Type *Int32Ty = Type::getInt32Ty(*Context); + auto OldArg = CI->getOperand(0); + auto NewArgTy = VectorType::get( + Int32Ty, OldArg->getType()->getVectorNumElements()); + auto NewArg = + CastInst::CreateSExtOrBitCast(OldArg, NewArgTy, "", CI); + Args[0] = NewArg; + RetTy = Int32Ty; + return CI->getCalledFunction()->getName(); + }, + [=](CallInst *NewCI) -> Instruction * { + return CastInst::CreateTruncOrBitCast( + NewCI, Type::getInt1Ty(*Context), "", NewCI->getNextNode()); + }, + &Attrs))); +} + +Instruction *SPIRVToLLVM::transOCLRelational(SPIRVInstruction *I, BasicBlock *BB) { + CallInst *CI = cast(transSPIRVBuiltinFromInst(I, BB)); + AttributeSet Attrs = CI->getCalledFunction()->getAttributes(); + return cast(mapValue( + I, mutateCallInstOCL( + M, CI, + [=](CallInst *, std::vector &Args, llvm::Type *&RetTy) { + Type *IntTy = Type::getInt32Ty(*Context); + RetTy = IntTy; + if (CI->getType()->isVectorTy()) { + if (cast(CI->getOperand(0)->getType()) + ->getElementType() + ->isDoubleTy()) + IntTy = Type::getInt64Ty(*Context); + if (cast(CI->getOperand(0)->getType()) + ->getElementType() + ->isHalfTy()) + IntTy = Type::getInt16Ty(*Context); + RetTy = VectorType::get(IntTy, + CI->getType()->getVectorNumElements()); + } + return CI->getCalledFunction()->getName(); + }, + [=](CallInst *NewCI) -> Instruction * { + Type *RetTy = Type::getInt1Ty(*Context); + if (NewCI->getType()->isVectorTy()) + RetTy = + VectorType::get(Type::getInt1Ty(*Context), + NewCI->getType()->getVectorNumElements()); + return CastInst::CreateTruncOrBitCast(NewCI, RetTy, "", + NewCI->getNextNode()); + }, + &Attrs))); +} +} + +bool +llvm::ReadSPIRV(LLVMContext &C, std::istream &IS, Module *&M, + std::string &ErrMsg) { + M = new Module("", C); + std::unique_ptr BM(SPIRVModule::createSPIRVModule()); + + BM->setAutoAddCapability(false); + IS >> *BM; + + SPIRVToLLVM BTL(M, BM.get()); + bool Succeed = true; + if (!BTL.translate()) { + BM->getError(ErrMsg); + Succeed = false; + } + legacy::PassManager PassMgr; + PassMgr.add(createSPIRVToOCL20()); + PassMgr.add(createOCL20To12()); + PassMgr.run(*M); + + if (DbgSaveTmpLLVM) + dumpLLVM(M, DbgTmpLLVMFileName); + if (!Succeed) { + delete M; + M = nullptr; + } + return Succeed; +} diff --git a/SPIRVRegularizeLLVM.cpp b/SPIRVRegularizeLLVM.cpp index 1631b61..b12af32 100644 --- a/SPIRVRegularizeLLVM.cpp +++ b/SPIRVRegularizeLLVM.cpp @@ -116,7 +116,7 @@ SPIRVRegularizeLLVM::regularize() { //lowerConstantExpressions(); for (auto I = M->begin(), E = M->end(); I != E;) { - Function *F = I++; + Function *F = &*I++; if (F->isDeclaration() && F->use_empty()) { F->eraseFromParent(); continue; @@ -193,7 +193,7 @@ void SPIRVRegularizeLLVM::lowerFuncPtr(Module* M) { std::vector> Work; for (auto I = M->begin(), E = M->end(); I != E;) { - Function *F = I++; + Function *F = &*I++; auto AI = F->arg_begin(); if (hasFunctionPointerArg(F, AI)) { auto OC = getSPIRVFuncOC(F->getName()); diff --git a/SPIRVToOCL20.cpp b/SPIRVToOCL20.cpp index d994490..528cfe9 100644 --- a/SPIRVToOCL20.cpp +++ b/SPIRVToOCL20.cpp @@ -308,7 +308,7 @@ void SPIRVToOCL20::visitCallSPRIVImageQuerySize(CallInst *CI) { if (imgArray) { assert((imgDim == 1 || imgDim == 2) && "invalid image array type"); // Insert get_image_array_size to the last position of the resulting vector. - Type * sizeTy = Type::getIntNTy(*Ctx, M->getDataLayout()->getPointerSizeInBits(0)); + Type * sizeTy = Type::getIntNTy(*Ctx, M->getDataLayout().getPointerSizeInBits(0)); Instruction * getImageArraySize = addCallInst(M, kOCLBuiltinName::GetImageArraySize, sizeTy, CI->getArgOperand(0), &attributes, @@ -370,7 +370,7 @@ void SPIRVToOCL20::visitCallSPIRVAtomicBuiltin(CallInst* CI, Op OC) { // value by pointer passed as 2nd argument (aka expected) while SPIR-V // instructions returns this new/original value as a resulting value. AllocaInst *pExpected = new AllocaInst(CI->getType(), "expected", - pInsertBefore->getParent()->getParent()->getEntryBlock().getFirstInsertionPt()); + &*pInsertBefore->getParent()->getParent()->getEntryBlock().getFirstInsertionPt()); pExpected->setAlignment(CI->getType()->getScalarSizeInBits() / 8); new StoreInst(Args[1], pExpected, pInsertBefore); Args[1] = pExpected; diff --git a/SPIRVUtil.cpp b/SPIRVUtil.cpp index 7c5466b..a9fec78 100644 --- a/SPIRVUtil.cpp +++ b/SPIRVUtil.cpp @@ -45,7 +45,8 @@ #include "OCLUtil.h" #include "llvm/ADT/StringSwitch.h" -#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/Bitcode/BitcodeReader.h" #include "llvm/IR/IRBuilder.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -82,8 +83,7 @@ addFnAttr(LLVMContext *Context, CallInst *Call, Attribute::AttrKind Attr) { void removeFnAttr(LLVMContext *Context, CallInst *Call, Attribute::AttrKind Attr) { - Call->removeAttribute(AttributeSet::FunctionIndex, - Attribute::get(*Context, Attr)); + Call->removeAttribute(AttributeSet::FunctionIndex, Attr); } Value * @@ -814,7 +814,7 @@ addBlockBind(Module *M, Function *InvokeFunc, Value *BlkCtx, Value *CtxLen, IntegerType* getSizetType(Module *M) { return IntegerType::getIntNTy(M->getContext(), - M->getDataLayout()->getPointerSizeInBits(0)); + M->getDataLayout().getPointerSizeInBits(0)); } Type * @@ -1031,7 +1031,7 @@ transTypeDesc(Type *Ty, const BuiltinArgTypeMangleInfo &Info) { if (Name.empty()) { std::ostringstream OS; OS << reinterpret_cast(Ty); - Name = std::string("struct_") + OS.str(); + Name = (std::string("struct_") + OS.str()).c_str(); } return SPIR::RefParamType(new SPIR::UserDefinedType(Name)); } @@ -1343,7 +1343,7 @@ bool eraseUselessFunctions(Module *M) { bool changed = false; for (auto I = M->begin(), E = M->end(); I != E;) - changed |= eraseIfNoUse(I++); + changed |= eraseIfNoUse(&*I++); return changed; } diff --git a/SPIRVWriter.cpp b/SPIRVWriter.cpp index 6f5e6fd..2f8190b 100644 --- a/SPIRVWriter.cpp +++ b/SPIRVWriter.cpp @@ -1,1685 +1,1686 @@ -//===- SPIRVWriter.cpp - Converts LLVM to SPIR-V ----------------*- C++ -*-===// -// -// The LLVM/SPIR-V Translator -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. -// -//===----------------------------------------------------------------------===// -/// \file -/// -/// This file implements conversion of LLVM intermediate language to SPIR-V -/// binary. -/// -//===----------------------------------------------------------------------===// - -#include "SPIRVModule.h" -#include "SPIRVEnum.h" -#include "SPIRVEntry.h" -#include "SPIRVType.h" -#include "SPIRVValue.h" -#include "SPIRVFunction.h" -#include "SPIRVBasicBlock.h" -#include "SPIRVInstruction.h" -#include "SPIRVExtInst.h" -#include "SPIRVUtil.h" -#include "SPIRVInternal.h" -#include "SPIRVMDWalker.h" -#include "OCLTypeToSPIRV.h" -#include "OCLUtil.h" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SetVector.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/ADT/Triple.h" -#include "llvm/Bitcode/ReaderWriter.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/DebugInfo.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/InstrTypes.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Operator.h" -#include "llvm/IR/Verifier.h" -#include "llvm/Pass.h" -#include "llvm/PassSupport.h" -#include "llvm/PassManager.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/Debug.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/ToolOutputFile.h" -#include "llvm/Transforms/IPO.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "spirv" - -using namespace llvm; -using namespace SPIRV; -using namespace OCLUtil; - -namespace llvm { - FunctionPass *createPromoteMemoryToRegisterPass(); -} - -namespace SPIRV{ - -cl::opt SPIRVMemToReg("spirv-mem2reg", cl::init(true), - cl::desc("LLVM/SPIR-V translation enable mem2reg")); - - -static void -foreachKernelArgMD(MDNode *MD, SPIRVFunction *BF, - std::functionFunc) { - for (unsigned I = 1, E = MD->getNumOperands(); I != E; ++I) { - SPIRVFunctionParameter *BA = BF->getArgument(I-1); - Func(getMDOperandAsString(MD, I), BA); - } -} - -/// Information for translating OCL builtin. -struct OCLBuiltinSPIRVTransInfo { - std::string UniqName; - /// Postprocessor of operands - std::function&)> PostProc; - OCLBuiltinSPIRVTransInfo(){ - PostProc = [](std::vector&){}; - } -}; - -class LLVMToSPIRVDbgTran { -public: - LLVMToSPIRVDbgTran(Module *TM = nullptr, SPIRVModule *TBM = nullptr) - :BM(TBM), M(TM){ - } - - void setModule(Module *Mod) { M = Mod;} - void setSPIRVModule(SPIRVModule *SMod) { BM = SMod;} - - void transDbgInfo(Value *V, SPIRVValue *BV) { - if (auto I = dyn_cast(V)) { - auto DL = I->getDebugLoc(); - if (!DL.isUnknown()) { - DILocation DIL(DL.getAsMDNode()); - auto File = BM->getString(DIL.getFilename().str()); - // ToDo: SPIR-V rev.31 cannot add debug info for instructions without ids. - // This limitation needs to be addressed. - if (!BV->hasId()) - return; - BM->addLine(BV, File, DIL.getLineNumber(), DIL.getColumnNumber()); - } - } else if (auto F = dyn_cast(V)) { - if (auto DIS = getDISubprogram(F)) { - auto File = BM->getString(DIS.getFilename().str()); - BM->addLine(BV, File, DIS.getLineNumber(), 0); - } - } - } - -private: - SPIRVModule *BM; - Module *M; -}; - -class LLVMToSPIRV: public ModulePass { -public: - LLVMToSPIRV(SPIRVModule *SMod = nullptr) - : ModulePass(ID), - M(nullptr), - Ctx(nullptr), - BM(SMod), - ExtSetId(SPIRVID_INVALID), - SrcLang(0), - SrcLangVer(0), - DbgTran(nullptr, SMod){ - } - - bool runOnModule(Module &Mod) override { - M = &Mod; - Ctx = &M->getContext(); - DbgTran.setModule(M); - assert(BM && "SPIR-V module not initialized"); - translate(); - return true; - } - - void getAnalysisUsage(AnalysisUsage &AU) const { - AU.addRequired(); - } - - static char ID; - - SPIRVType *transType(Type *T); - SPIRVType *transSPIRVOpaqueType(Type *T); - - SPIRVValue *getTranslatedValue(Value *); - - // Translation functions - bool transAddressingMode(); - bool transAlign(Value *V, SPIRVValue *BV); - std::vector transArguments(CallInst *, SPIRVBasicBlock *); - std::vector transArguments(CallInst *, SPIRVBasicBlock *, - SPIRVEntry *); - bool transSourceLanguage(); - bool transExtension(); - bool transBuiltinSet(); - SPIRVValue *transCallInst(CallInst *Call, SPIRVBasicBlock *BB); - bool transDecoration(Value *V, SPIRVValue *BV); - SPIRVWord transFunctionControlMask(CallInst *); - SPIRVWord transFunctionControlMask(Function *); - SPIRVFunction *transFunctionDecl(Function *F); - bool transGlobalVariables(); - - Op transBoolOpCode(SPIRVValue *Opn, Op OC); - // Translate LLVM module to SPIR-V module. - // Returns true if succeeds. - bool translate(); - bool transExecutionMode(); - SPIRVValue *transConstant(Value *V); - SPIRVValue *transValue(Value *V, SPIRVBasicBlock *BB, - bool CreateForward = true); - SPIRVValue *transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB, - bool CreateForward = true); - - typedef DenseMap LLVMToSPIRVTypeMap; - typedef DenseMap LLVMToSPIRVValueMap; -private: - Module *M; - LLVMContext *Ctx; - SPIRVModule *BM; - LLVMToSPIRVTypeMap TypeMap; - LLVMToSPIRVValueMap ValueMap; - //ToDo: support multiple builtin sets. Currently assume one builtin set. - SPIRVId ExtSetId; - SPIRVWord SrcLang; - SPIRVWord SrcLangVer; - LLVMToSPIRVDbgTran DbgTran; - - SPIRVType *mapType(Type *T, SPIRVType *BT) { - TypeMap[T] = BT; - SPIRVDBG(dbgs() << "[mapType] " << *T << " => "; - spvdbgs() << *BT << '\n'); - return BT; - } - - SPIRVValue *mapValue(Value *V, SPIRVValue *BV) { - auto Loc = ValueMap.find(V); - if (Loc != ValueMap.end()) { - if (Loc->second == BV) - return BV; - assert (Loc->second->isForward() && - "LLVM Value is mapped to different SPIRV Values"); - auto Forward = static_cast(Loc->second); - BV->setId(Forward->getId()); - BM->replaceForward(Forward, BV); - } - ValueMap[V] = BV; - SPIRVDBG(dbgs() << "[mapValue] " << *V << " => "; - spvdbgs() << *BV << "\n"); - return BV; - } - - SPIRVType *getSPIRVType(Type *T) { - return TypeMap[T]; - } - - SPIRVValue *getSPIRVValue(Value *V) { - return ValueMap[V]; - } - - SPIRVErrorLog &getErrorLog() { - return BM->getErrorLog(); - } - - llvm::IntegerType* getSizetType(); - std::vector transValue(const std::vector &Values, - SPIRVBasicBlock* BB); - std::vector transValue(const std::vector &Values, - SPIRVBasicBlock* BB, SPIRVEntry *Entry); - - SPIRVInstruction* transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB); - SPIRVInstruction* transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB); - - void dumpUsers(Value *V); - - template - bool oclGetExtInstIndex(const std::string &MangledName, - const std::string& DemangledName, SPIRVWord* EntryPoint); - void oclGetMutatedArgumentTypesByBuiltin(llvm::FunctionType* FT, - std::map& ChangedType, Function* F); - - bool isBuiltinTransToInst(Function *F); - bool isBuiltinTransToExtInst(Function *F, - SPIRVExtInstSetKind *BuiltinSet = nullptr, - SPIRVWord *EntryPoint = nullptr, - SmallVectorImpl *Dec = nullptr); - bool oclIsKernel(Function *F); - - bool transOCLKernelMetadata(); - - SPIRVInstruction *transBuiltinToInst(const std::string& DemangledName, - const std::string &MangledName, CallInst* CI, SPIRVBasicBlock* BB); - SPIRVInstruction *transBuiltinToInstWithoutDecoration(Op OC, - CallInst* CI, SPIRVBasicBlock* BB); - void mutateFuncArgType(const std::map& ChangedType, - Function* F); - - SPIRVValue *transSpcvCast(CallInst* CI, SPIRVBasicBlock *BB); - SPIRVValue *oclTransSpvcCastSampler(CallInst* CI, SPIRVBasicBlock *BB); - - SPIRV::SPIRVInstruction* transUnaryInst(UnaryInstruction* U, - SPIRVBasicBlock* BB); - - /// Add a 32 bit integer constant. - /// \return Id of the constant. - SPIRVId addInt32(int); - void transFunction(Function *I); - SPIRV::SPIRVLinkageTypeKind transLinkageType(const GlobalValue* GV); -}; - - -SPIRVValue * -LLVMToSPIRV::getTranslatedValue(Value *V) { - LLVMToSPIRVValueMap::iterator Loc = ValueMap.find(V); - if (Loc != ValueMap.end()) - return Loc->second; - return nullptr; -} - -bool -LLVMToSPIRV::oclIsKernel(Function *F) { - if (F->getCallingConv() == CallingConv::SPIR_KERNEL) - return true; - return false; -} - -bool -LLVMToSPIRV::isBuiltinTransToInst(Function *F) { - std::string DemangledName; - if (!oclIsBuiltin(F->getName(), &DemangledName) && - !isDecoratedSPIRVFunc(F, &DemangledName)) - return false; - SPIRVDBG(spvdbgs() << "CallInst: demangled name: " << DemangledName << '\n'); - return getSPIRVFuncOC(DemangledName) != OpNop; -} - -bool -LLVMToSPIRV::isBuiltinTransToExtInst(Function *F, - SPIRVExtInstSetKind *ExtSet, - SPIRVWord *ExtOp, - SmallVectorImpl *Dec) { - std::string OrigName = F->getName(); - std::string DemangledName; - if (!oclIsBuiltin(OrigName, &DemangledName)) - return false; - DEBUG(dbgs() << "[oclIsBuiltinTransToExtInst] CallInst: demangled name: " - << DemangledName << '\n'); - StringRef S = DemangledName; - if (!S.startswith(kSPIRVName::Prefix)) - return false; - S = S.drop_front(strlen(kSPIRVName::Prefix)); - auto Loc = S.find(kSPIRVPostfix::Divider); - auto ExtSetName = S.substr(0, Loc); - SPIRVExtInstSetKind Set = SPIRVEIS_Count; - if (!SPIRVExtSetShortNameMap::rfind(ExtSetName, &Set)) - return false; - assert(Set == BM->getBuiltinSet(ExtSetId) && - "Invalid extended instruction set"); - assert(Set == SPIRVEIS_OpenCL && "Unsupported extended instruction set"); - - auto ExtOpName = S.substr(Loc + 1); - auto Splited = ExtOpName.split(kSPIRVPostfix::ExtDivider); - OCLExtOpKind EOC; - if (!OCLExtOpMap::rfind(Splited.first, &EOC)) - return false; - - if (ExtSet) - *ExtSet = Set; - if (ExtOp) - *ExtOp = EOC; - if (Dec) { - SmallVector P; - Splited.second.split(P, kSPIRVPostfix::Divider); - for (auto &I:P) - Dec->push_back(I.str()); - } - return true; -} - -/// Decode SPIR-V type name in the format spirv.{TypeName}._{Postfixes} -/// where Postfixes are strings separated by underscores. -/// \return TypeName. -/// \param Ops contains the integers decoded from postfixes. -static std::string - decodeSPIRVTypeName(StringRef Name, - SmallVectorImpl& Strs) { - SmallVector SubStrs; - const char Delim[] = { kSPIRVTypeName::Delimiter, 0 }; - Name.split(SubStrs, Delim, -1, true); - assert(SubStrs.size() >= 2 && "Invalid SPIRV type name"); - assert(SubStrs[0] == kSPIRVTypeName::Prefix && "Invalid prefix"); - assert((SubStrs.size() == 2 || !SubStrs[2].empty()) && "Invalid postfix"); - - if (SubStrs.size() > 2) { - const char PostDelim[] = { kSPIRVTypeName::PostfixDelim, 0 }; - SmallVector Postfixes; - SubStrs[2].split(Postfixes, PostDelim, -1, true); - assert(Postfixes.size() > 1 && Postfixes[0].empty() && "Invalid postfix"); - for (unsigned I = 1, E = Postfixes.size(); I != E; ++I) - Strs.push_back(std::string(Postfixes[I]).c_str()); - } - return SubStrs[1].str(); -} - -static bool recursiveType(const StructType *ST, const Type *Ty) { - SmallPtrSet Seen; - - std::function Run = [&](const Type *Ty) { - if (!isa(Ty)) - return false; - - if (auto *StructTy = dyn_cast(Ty)) { - if (StructTy == ST) - return true; - - if (Seen.count(StructTy)) - return false; - - Seen.insert(StructTy); - - return find_if(StructTy->subtype_begin(), StructTy->subtype_end(), Run) != - StructTy->subtype_end(); - } - - if (auto *PtrTy = dyn_cast(Ty)) - return Run(PtrTy->getPointerElementType()); - - if (auto *ArrayTy = dyn_cast(Ty)) - return Run(ArrayTy->getArrayElementType()); - - return false; - }; - - return Run(Ty); -} - -SPIRVType * -LLVMToSPIRV::transType(Type *T) { - LLVMToSPIRVTypeMap::iterator Loc = TypeMap.find(T); - if (Loc != TypeMap.end()) - return Loc->second; - - SPIRVDBG(dbgs() << "[transType] " << *T << '\n'); - if (T->isVoidTy()) - return mapType(T, BM->addVoidType()); - - if (T->isIntegerTy(1)) - return mapType(T, BM->addBoolType()); - - if (T->isIntegerTy()) - return mapType(T, BM->addIntegerType(T->getIntegerBitWidth())); - - if (T->isFloatingPointTy()) - return mapType(T, BM->addFloatType(T->getPrimitiveSizeInBits())); - - // A pointer to image or pipe type in LLVM is translated to a SPIRV - // sampler or pipe type. - if (T->isPointerTy()) { - auto ET = T->getPointerElementType(); - assert(!ET->isFunctionTy() && "Function pointer type is not allowed"); - auto ST = dyn_cast(ET); - auto AddrSpc = T->getPointerAddressSpace(); - if (ST && !ST->isSized()) { - Op OpCode; - StringRef STName = ST->getName(); - // Workaround for non-conformant SPIR binary - if (STName == "struct._event_t") { - STName = kSPR2TypeName::Event; - ST->setName(STName); - } - assert (!STName.startswith(kSPR2TypeName::Pipe) && - "OpenCL type names should be translated to SPIR-V type names"); - // ToDo: For SPIR1.2/2.0 there may still be load/store or bitcast - // instructions using opencl.* type names. We need to handle these - // type names until they are all mapped or FE generates SPIR-V type - // names. - if (STName.find(kSPR2TypeName::Pipe) == 0) { - assert(AddrSpc == SPIRAS_Global); - SmallVector SubStrs; - const char Delims[] = {kSPR2TypeName::Delimiter, 0}; - STName.split(SubStrs, Delims); - std::string Acc = kAccessQualName::ReadOnly; - if (SubStrs.size() > 2) { - Acc = SubStrs[2]; - } - auto PipeT = BM->addPipeType(); - PipeT->setPipeAcessQualifier(SPIRSPIRVAccessQualifierMap::map(Acc)); - return mapType(T, PipeT); - } else if (STName.find(kSPR2TypeName::ImagePrefix) == 0) { - assert(AddrSpc == SPIRAS_Global); - auto SPIRVImageTy = getSPIRVImageTypeFromOCL(M, T); - return mapType(T, transSPIRVOpaqueType(SPIRVImageTy)); - } else if (STName.startswith(kSPIRVTypeName::PrefixAndDelim)) - return transSPIRVOpaqueType(T); - else if (OCLOpaqueTypeOpCodeMap::find(STName, &OpCode)) { - if (OpCode == OpTypePipe) { - return mapType(T, BM->addPipeType()); - } - return mapType(T, BM->addOpaqueGenericType(OpCode)); - } else if (isPointerToOpaqueStructType(T)) { - return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map( - static_cast(AddrSpc)), - transType(ET))); - } - } else { - return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map( - static_cast(AddrSpc)), - transType(ET))); - } - } - - if (T->isVectorTy()) - return mapType(T, BM->addVectorType(transType(T->getVectorElementType()), - T->getVectorNumElements())); - - if (T->isArrayTy()) - return mapType(T, BM->addArrayType(transType(T->getArrayElementType()), - static_cast(transValue(ConstantInt::get(getSizetType(), - T->getArrayNumElements(), false), nullptr)))); - - if (T->isStructTy() && !T->isSized()) { - auto ST = dyn_cast(T); - assert(!ST->getName().startswith(kSPR2TypeName::Pipe)); - assert(!ST->getName().startswith(kSPR2TypeName::ImagePrefix)); - return mapType(T, BM->addOpaqueType(T->getStructName())); - } - - if (auto ST = dyn_cast(T)) { - assert(ST->isSized()); - - std::string Name; - if (ST->hasName()) - Name = ST->getName(); - - if(Name == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) - return transType(getSamplerType(M)); - - auto *Struct = BM->openStructType(T->getStructNumElements(), Name); - mapType(T, Struct); - - SmallVector ForwardRefs; - - for (unsigned I = 0, E = T->getStructNumElements(); I != E; ++I) { - auto *ElemTy = ST->getElementType(I); - if (isa(ElemTy) && recursiveType(ST, ElemTy)) - ForwardRefs.push_back(I); - else - Struct->setMemberType(I, transType(ST->getElementType(I))); - } - - BM->closeStructType(Struct, ST->isPacked()); - - for (auto I : ForwardRefs) - Struct->setMemberType(I, transType(ST->getElementType(I))); - - return Struct; - } - - if (FunctionType *FT = dyn_cast(T)) { - SPIRVType *RT = transType(FT->getReturnType()); - std::vector PT; - for (FunctionType::param_iterator I = FT->param_begin(), - E = FT->param_end(); I != E; ++I) - PT.push_back(transType(*I)); - return mapType(T, BM->addFunctionType(RT, PT)); - } - - llvm_unreachable("Not implemented!"); - return 0; -} - -SPIRVType * -LLVMToSPIRV::transSPIRVOpaqueType(Type *T) { - auto ET = T->getPointerElementType(); - auto ST = cast(ET); - auto AddrSpc = T->getPointerAddressSpace(); - auto STName = ST->getStructName(); - assert (STName.startswith(kSPIRVTypeName::PrefixAndDelim) && - "Invalid SPIR-V opaque type name"); - SmallVector Postfixes; - auto TN = decodeSPIRVTypeName(STName, Postfixes); - if (TN == kSPIRVTypeName::Pipe) { - assert(AddrSpc == SPIRAS_Global); - assert(Postfixes.size() == 1 && "Invalid pipe type ops"); - auto PipeT = BM->addPipeType(); - PipeT->setPipeAcessQualifier(static_cast( - atoi(Postfixes[0].c_str()))); - return mapType(T, PipeT); - } else if (TN == kSPIRVTypeName::Image) { - assert(AddrSpc == SPIRAS_Global); - // The sampled type needs to be translated through LLVM type to guarantee - // uniqueness. - auto SampledT = transType(getLLVMTypeForSPIRVImageSampledTypePostfix( - Postfixes[0], *Ctx)); - SmallVector Ops; - for (unsigned I = 1; I < 8; ++I) - Ops.push_back(atoi(Postfixes[I].c_str())); - SPIRVTypeImageDescriptor Desc(static_cast(Ops[0]), - Ops[1], Ops[2], Ops[3], Ops[4], Ops[5]); - return mapType(T, BM->addImageType(SampledT, Desc, - static_cast(Ops[6]))); - } else if (TN == kSPIRVTypeName::SampledImg) { - return mapType(T, BM->addSampledImageType( - static_cast( - transType(getSPIRVTypeByChangeBaseTypeName(M, - T, kSPIRVTypeName::SampledImg, - kSPIRVTypeName::Image))))); - } else if(TN == kSPIRVTypeName::Sampler) - return mapType(T, BM->addSamplerType()); - else if (TN == kSPIRVTypeName::DeviceEvent) - return mapType(T, BM->addDeviceEventType()); - else - return mapType(T, BM->addOpaqueGenericType( - SPIRVOpaqueTypeOpCodeMap::map(TN))); -} - -SPIRVFunction * -LLVMToSPIRV::transFunctionDecl(Function *F) { - if (auto BF= getTranslatedValue(F)) - return static_cast(BF); - - SPIRVTypeFunction *BFT = static_cast(transType( - getAnalysis().getAdaptedType(F))); - SPIRVFunction *BF = static_cast(mapValue(F, - BM->addFunction(BFT))); - BF->setFunctionControlMask(transFunctionControlMask(F)); - if (F->hasName()) - BM->setName(BF, F->getName()); - if (oclIsKernel(F)) - BM->addEntryPoint(ExecutionModelKernel, BF->getId()); - else if (F->getLinkage() != GlobalValue::InternalLinkage) - BF->setLinkageType(transLinkageType(F)); - auto Attrs = F->getAttributes(); - for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; - ++I) { - auto ArgNo = I->getArgNo(); - SPIRVFunctionParameter *BA = BF->getArgument(ArgNo); - if (I->hasName()) - BM->setName(BA, I->getName()); - if (I->hasByValAttr()) - BA->addAttr(FunctionParameterAttributeByVal); - if (I->hasNoAliasAttr()) - BA->addAttr(FunctionParameterAttributeNoAlias); - if (I->hasNoCaptureAttr()) - BA->addAttr(FunctionParameterAttributeNoCapture); - if (I->hasStructRetAttr()) - BA->addAttr(FunctionParameterAttributeSret); - if (Attrs.hasAttribute(ArgNo + 1, Attribute::ZExt)) - BA->addAttr(FunctionParameterAttributeZext); - if (Attrs.hasAttribute(ArgNo + 1, Attribute::SExt)) - BA->addAttr(FunctionParameterAttributeSext); - } - if (Attrs.hasAttribute(AttributeSet::ReturnIndex, Attribute::ZExt)) - BF->addDecorate(DecorationFuncParamAttr, FunctionParameterAttributeZext); - if (Attrs.hasAttribute(AttributeSet::ReturnIndex, Attribute::SExt)) - BF->addDecorate(DecorationFuncParamAttr, FunctionParameterAttributeSext); - DbgTran.transDbgInfo(F, BF); - SPIRVDBG(dbgs() << "[transFunction] " << *F << " => "; - spvdbgs() << *BF << '\n';) - return BF; -} - -#define _SPIRV_OPL(x) OpLogical##x - -#define _SPIRV_OPB(x) OpBitwise##x - -SPIRVValue * -LLVMToSPIRV::transConstant(Value *V) { - if (auto CPNull = dyn_cast(V)) - return BM->addNullConstant(bcast(transType( - CPNull->getType()))); - - if (auto CAZero = dyn_cast(V)) { - Type *AggType = CAZero->getType(); - if (const StructType* ST = dyn_cast(AggType)) { - if (ST->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) - return BM->addSamplerConstant(transType(AggType), 0,0,0); - } else - return BM->addNullConstant(transType(AggType)); - } - - if (auto ConstI = dyn_cast(V)) - return BM->addConstant(transType(V->getType()), ConstI->getZExtValue()); - - if (auto ConstFP = dyn_cast(V)) { - auto BT = static_cast(transType(V->getType())); - return BM->addConstant(BT, - ConstFP->getValueAPF().bitcastToAPInt().getZExtValue()); - } - - if (auto ConstDA = dyn_cast(V)) { - std::vector BV; - for (unsigned I = 0, E = ConstDA->getNumElements(); I != E; ++I) - BV.push_back(transValue(ConstDA->getElementAsConstant(I), nullptr)); - return BM->addCompositeConstant(transType(V->getType()), BV); - } - - if (auto ConstA = dyn_cast(V)) { - std::vector BV; - for (auto I = ConstA->op_begin(), E = ConstA->op_end(); I != E; ++I) - BV.push_back(transValue(*I, nullptr)); - return BM->addCompositeConstant(transType(V->getType()), BV); - } - - if (auto ConstDV = dyn_cast(V)) { - std::vector BV; - for (unsigned I = 0, E = ConstDV->getNumElements(); I != E; ++I) - BV.push_back(transValue(ConstDV->getElementAsConstant(I), nullptr)); - return BM->addCompositeConstant(transType(V->getType()), BV); - } - - if (auto ConstV = dyn_cast(V)) { - std::vector BV; - for (auto I = ConstV->op_begin(), E = ConstV->op_end(); I != E; ++I) - BV.push_back(transValue(*I, nullptr)); - return BM->addCompositeConstant(transType(V->getType()), BV); - } - - if (auto ConstV = dyn_cast(V)) { - if (ConstV->getType()->getName() == - getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) { - assert(ConstV->getNumOperands() == 3); - SPIRVWord - AddrMode = ConstV->getOperand(0)->getUniqueInteger().getZExtValue(), - Normalized = ConstV->getOperand(1)->getUniqueInteger().getZExtValue(), - FilterMode = ConstV->getOperand(2)->getUniqueInteger().getZExtValue(); - assert(AddrMode < 5 && "Invalid addressing mode"); - assert(Normalized < 2 && "Invalid value of normalized coords"); - assert(FilterMode < 2 && "Invalid filter mode"); - SPIRVType* SamplerTy = transType(ConstV->getType()); - return BM->addSamplerConstant(SamplerTy, - AddrMode, Normalized, FilterMode); - } - std::vector BV; - for (auto I = ConstV->op_begin(), E = ConstV->op_end(); I != E; ++I) - BV.push_back(transValue(*I, nullptr)); - return BM->addCompositeConstant(transType(V->getType()), BV); - } - - if (auto ConstUE = dyn_cast(V)) { - auto Inst = ConstUE->getAsInstruction(); - SPIRVDBG(dbgs() << "ConstantExpr: " << *ConstUE << '\n'; - dbgs() << "Instruction: " << *Inst << '\n';) - auto BI = transValue(Inst, nullptr, false); - Inst->dropAllReferences(); - return BI; - } - - if (isa(V)) { - return BM->addUndef(transType(V->getType())); - } - - return nullptr; -} - -SPIRVValue * -LLVMToSPIRV::transValue(Value *V, SPIRVBasicBlock *BB, bool CreateForward) { - LLVMToSPIRVValueMap::iterator Loc = ValueMap.find(V); - if (Loc != ValueMap.end() && (!Loc->second->isForward() || CreateForward)) - return Loc->second; - - SPIRVDBG(dbgs() << "[transValue] " << *V << '\n'); - assert ((!isa(V) || isa(V) || - isa(V) || BB) && - "Invalid SPIRV BB"); - - auto BV = transValueWithoutDecoration(V, BB, CreateForward); - std::string name = V->getName(); - if (!name.empty()) // Don't erase the name, which BM might already have - BM->setName(BV, name); - if(!transDecoration(V, BV)) - return nullptr; - return BV; -} - -SPIRVInstruction* -LLVMToSPIRV::transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB) { - unsigned LLVMOC = B->getOpcode(); - auto Op0 = transValue(B->getOperand(0), BB); - SPIRVInstruction* BI = BM->addBinaryInst( - transBoolOpCode(Op0, OpCodeMap::map(LLVMOC)), - transType(B->getType()), Op0, transValue(B->getOperand(1), BB), BB); - return BI; -} - -SPIRVInstruction* -LLVMToSPIRV::transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB) { - auto Op0 = transValue(Cmp->getOperand(0), BB); - SPIRVInstruction* BI = BM->addCmpInst( - transBoolOpCode(Op0, CmpMap::map(Cmp->getPredicate())), - transType(Cmp->getType()), Op0, - transValue(Cmp->getOperand(1), BB), BB); - return BI; -} - -SPIRV::SPIRVInstruction *LLVMToSPIRV::transUnaryInst(UnaryInstruction *U, - SPIRVBasicBlock *BB) { - Op BOC = OpNop; - if (auto Cast = dyn_cast(U)) { - if (Cast->getDestTy()->getPointerAddressSpace() == SPIRAS_Generic) { - assert(Cast->getSrcTy()->getPointerAddressSpace() != SPIRAS_Constant && - "Casts from constant address space to generic are illegal"); - BOC = OpPtrCastToGeneric; - } else { - assert(Cast->getDestTy()->getPointerAddressSpace() != SPIRAS_Constant && - "Casts from generic address space to constant are illegal"); - assert(Cast->getSrcTy()->getPointerAddressSpace() == SPIRAS_Generic); - BOC = OpGenericCastToPtr; - } - } else { - auto OpCode = U->getOpcode(); - BOC = OpCodeMap::map(OpCode); - } - - auto Op = transValue(U->getOperand(0), BB); - return BM->addUnaryInst(transBoolOpCode(Op, BOC), - transType(U->getType()), Op, BB); -} - -/// An instruction may use an instruction from another BB which has not been -/// translated. SPIRVForward should be created as place holder for these -/// instructions and replaced later by the real instructions. -/// Use CreateForward = true to indicate such situation. -SPIRVValue * -LLVMToSPIRV::transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB, - bool CreateForward) { - if (auto LBB = dyn_cast(V)) { - auto BF = static_cast(getTranslatedValue(LBB->getParent())); - assert (BF && "Function not translated"); - BB = static_cast(mapValue(V, BM->addBasicBlock(BF))); - BM->setName(BB, LBB->getName()); - return BB; - } - - if (auto F = dyn_cast(V)) - return transFunctionDecl(F); - - if (auto GV = dyn_cast(V)) { - llvm::PointerType * Ty = GV->getType(); - // Though variables with common linkage type are initialized by 0, - // they can be represented in SPIR-V as uninitialized variables with - // 'Export' linkage type, just as tentative definitions look in C - llvm::Value *Init = GV->hasInitializer() && !GV->hasCommonLinkage() ? - GV->getInitializer() : nullptr; - StructType *ST = Init ? dyn_cast(Init->getType()) : nullptr; - if (ST && ST->hasName() && - ST->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) { - auto BV = transConstant(Init); - assert(BV); - return mapValue(V, BV); - } else if (ConstantExpr *ConstUE = dyn_cast_or_null(Init)) { - Instruction * Inst = ConstUE->getAsInstruction(); - if (isSamplerInitializer(Inst)) { - Init = Inst->getOperand(0); - Ty = static_cast(Init->getType()); - } - Inst->dropAllReferences(); - } - auto BVar = static_cast(BM->addVariable( - transType(Ty), GV->isConstant(), - transLinkageType(GV), - Init ? transValue(Init, nullptr) : nullptr, - GV->getName(), - SPIRSPIRVAddrSpaceMap::map( - static_cast(Ty->getAddressSpace())), - nullptr - )); - mapValue(V, BVar); - spv::BuiltIn Builtin = spv::BuiltInPosition; - if (!GV->hasName() || !getSPIRVBuiltin(GV->getName().str(), Builtin)) - return BVar; - BVar->setBuiltin(Builtin); - return BVar; - } - - if (isa(V)) { - auto BV = transConstant(V); - assert(BV); - return mapValue(V, BV); - } - - if (auto Arg = dyn_cast(V)) { - unsigned ArgNo = Arg->getArgNo(); - SPIRVFunction *BF = BB->getParent(); - //assert(BF->existArgument(ArgNo)); - return mapValue(V, BF->getArgument(ArgNo)); - } - - if (CreateForward) - return mapValue(V, BM->addForward(transType(V->getType()))); - - if (StoreInst *ST = dyn_cast(V)) { - std::vector MemoryAccess; - if (ST->isVolatile()) - MemoryAccess.push_back(MemoryAccessVolatileMask); - MemoryAccess.push_back(MemoryAccessAlignedMask); - MemoryAccess.push_back(ST->getAlignment()); - return mapValue(V, BM->addStoreInst( - transValue(ST->getPointerOperand(), BB), - transValue(ST->getValueOperand(), BB), - MemoryAccess, BB)); - } - - if (LoadInst *LD = dyn_cast(V)) { - std::vector MemoryAccess; - if (LD->isVolatile()) - MemoryAccess.push_back(MemoryAccessVolatileMask); - MemoryAccess.push_back(MemoryAccessAlignedMask); - MemoryAccess.push_back(LD->getAlignment()); - return mapValue(V, BM->addLoadInst( - transValue(LD->getPointerOperand(), BB), - MemoryAccess, BB)); - } - - if (BinaryOperator *B = dyn_cast(V)) { - SPIRVInstruction* BI = transBinaryInst(B, BB); - return mapValue(V, BI); - } - - if (auto RI = dyn_cast(V)) { - if (auto RV = RI->getReturnValue()) - return mapValue(V, BM->addReturnValueInst( - transValue(RV, BB), BB)); - return mapValue(V, BM->addReturnInst(BB)); - } - - if (CmpInst *Cmp = dyn_cast(V)) { - SPIRVInstruction* BI = transCmpInst(Cmp, BB); - return mapValue(V, BI); - } - - if (SelectInst *Sel = dyn_cast(V)) - return mapValue(V, BM->addSelectInst( - transValue(Sel->getCondition(), BB), - transValue(Sel->getTrueValue(), BB), - transValue(Sel->getFalseValue(), BB),BB)); - - if (AllocaInst *Alc = dyn_cast(V)) - return mapValue(V, BM->addVariable( - transType(Alc->getType()), false, - SPIRVLinkageTypeKind::LinkageTypeInternal, - nullptr, Alc->getName(), - StorageClassFunction, BB)); - - if (auto *Switch = dyn_cast(V)) { - std::vector> Pairs; - for (auto I = Switch->case_begin(), E = Switch->case_end(); I != E; ++I) - Pairs.push_back(std::make_pair(I.getCaseValue()->getZExtValue(), - static_cast(transValue(I.getCaseSuccessor(), - nullptr)))); - return mapValue(V, BM->addSwitchInst( - transValue(Switch->getCondition(), BB), - static_cast(transValue(Switch->getDefaultDest(), - nullptr)), Pairs, BB)); - } - - if (auto Branch = dyn_cast(V)) { - if (Branch->isUnconditional()) - return mapValue(V, BM->addBranchInst( - static_cast(transValue(Branch->getSuccessor(0), BB)), - BB)); - return mapValue(V, BM->addBranchConditionalInst( - transValue(Branch->getCondition(), BB), - static_cast(transValue(Branch->getSuccessor(0), BB)), - static_cast(transValue(Branch->getSuccessor(1), BB)), - BB)); - } - - if (auto Phi = dyn_cast(V)) { - std::vector IncomingPairs; - for (size_t I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { - IncomingPairs.push_back(transValue(Phi->getIncomingValue(I), BB)); - IncomingPairs.push_back(transValue(Phi->getIncomingBlock(I), nullptr)); - } - return mapValue(V, BM->addPhiInst(transType(Phi->getType()), IncomingPairs, - BB)); - } - - if (UnaryInstruction *U = dyn_cast(V)) { - if(isSamplerInitializer(U)) - return mapValue(V, transValue(U->getOperand(0), BB)); - return mapValue(V, transUnaryInst(U, BB)); - } - - if (GetElementPtrInst *GEP = dyn_cast(V)) { - std::vector Indices; - for (unsigned i = 0, e = GEP->getNumIndices(); i != e; ++i) - Indices.push_back(transValue(GEP->getOperand(i+1), BB)); - return mapValue(V, BM->addPtrAccessChainInst( - transType(GEP->getType()), - transValue(GEP->getPointerOperand(), BB), - Indices, BB, GEP->isInBounds())); - } - - if (auto Ext = dyn_cast(V)) { - auto Index = Ext->getIndexOperand(); - if (auto Const = dyn_cast(Index)) - return mapValue(V, BM->addCompositeExtractInst( - transType(Ext->getType()), - transValue(Ext->getVectorOperand(), BB), - std::vector(1, Const->getZExtValue()), - BB)); - else - return mapValue(V, BM->addVectorExtractDynamicInst( - transValue(Ext->getVectorOperand(), BB), - transValue(Index, BB), - BB)); - } - - if (auto Ins = dyn_cast(V)) { - auto Index = Ins->getOperand(2); - if (auto Const = dyn_cast(Index)) - return mapValue(V, BM->addCompositeInsertInst( - transValue(Ins->getOperand(1), BB), - transValue(Ins->getOperand(0), BB), - std::vector(1, Const->getZExtValue()), - BB)); - else - return mapValue(V, BM->addVectorInsertDynamicInst( - transValue(Ins->getOperand(0), BB), - transValue(Ins->getOperand(1), BB), - transValue(Index, BB), - BB)); - } - - if (auto SF = dyn_cast(V)) { - std::vector Comp; - for (auto &I:SF->getShuffleMask()) - Comp.push_back(I); - return mapValue(V, BM->addVectorShuffleInst( - transType(SF->getType()), - transValue(SF->getOperand(0), BB), - transValue(SF->getOperand(1), BB), - Comp, - BB)); - } - - if (CallInst *CI = dyn_cast(V)) - return mapValue(V, transCallInst(CI, BB)); - - llvm_unreachable("Not implemented"); - return nullptr; -} - -bool -LLVMToSPIRV::transDecoration(Value *V, SPIRVValue *BV) { - if (!transAlign(V, BV)) - return false; - if ((isa(V) && - cast(V)->isVolatile()) || - (isa(V) && cast(V)->isVolatile())) - BV->setVolatile(true); - DbgTran.transDbgInfo(V, BV); - return true; -} - -bool -LLVMToSPIRV::transAlign(Value *V, SPIRVValue *BV) { - if (auto AL = dyn_cast(V)) { - BM->setAlignment(BV, AL->getAlignment()); - return true; - } - if (auto GV = dyn_cast(V)) { - BM->setAlignment(BV, GV->getAlignment()); - return true; - } - return true; -} - -/// Do this after source language is set. -bool -LLVMToSPIRV::transBuiltinSet() { - SPIRVWord Ver = 0; - SourceLanguage Kind = BM->getSourceLanguage(&Ver); - assert((Kind == SourceLanguageOpenCL_C || - Kind == SourceLanguageOpenCL_CPP ) && "not supported"); - std::stringstream SS; - SS << "OpenCL.std"; - return BM->importBuiltinSet(SS.str(), &ExtSetId); -} - -/// Transform sampler* spcv.cast(i32 arg) -/// Only two cases are possible: -/// arg = ConstantInt x -> SPIRVConstantSampler -/// arg = i32 argument -> transValue(arg) -/// arg = load from sampler -> look through load -SPIRVValue * -LLVMToSPIRV::oclTransSpvcCastSampler(CallInst* CI, SPIRVBasicBlock *BB) { - llvm::Function* F = CI->getCalledFunction(); - auto FT = F->getFunctionType(); - auto RT = FT->getReturnType(); - assert(FT->getNumParams() == 1); - assert(isSPIRVType(RT, kSPIRVTypeName::Sampler) && - FT->getParamType(0)->isIntegerTy() && "Invalid sampler type"); - auto Arg = CI->getArgOperand(0); - - auto GetSamplerConstant = [&](uint64_t SamplerValue) { - auto AddrMode = (SamplerValue & 0xE) >> 1; - auto Param = SamplerValue & 0x1; - auto Filter = ((SamplerValue & 0x30) >> 4) - 1; - auto BV = BM->addSamplerConstant(transType(RT), AddrMode, Param, Filter); - return BV; - }; - - if (auto Const = dyn_cast(Arg)) { - // Sampler is declared as a kernel scope constant - return GetSamplerConstant(Const->getZExtValue()); - } else if (auto Load = dyn_cast(Arg)) { - // If value of the sampler is loaded from a global constant, use its - // initializer for initialization of the sampler. - auto Op = Load->getPointerOperand(); - assert(isa(Op) && "Unknown sampler pattern!"); - auto GV = cast(Op); - assert(GV->isConstant() || - GV->getType()->getPointerAddressSpace() == SPIRAS_Constant); - auto Initializer = GV->getInitializer(); - assert(isa(Initializer) && "sampler not constant int?"); - return GetSamplerConstant(cast(Initializer)->getZExtValue()); - } - // Sampler is a function argument - auto BV = transValue(Arg, BB); - assert(BV && BV->getType() == transType(RT)); - return BV; -} - -SPIRVValue * -LLVMToSPIRV::transSpcvCast(CallInst* CI, SPIRVBasicBlock *BB) { - return oclTransSpvcCastSampler(CI, BB); -} - -SPIRVValue * -LLVMToSPIRV::transCallInst(CallInst *CI, SPIRVBasicBlock *BB) { - SPIRVExtInstSetKind ExtSetKind = SPIRVEIS_Count; - SPIRVWord ExtOp = SPIRVWORD_MAX; - llvm::Function* F = CI->getCalledFunction(); - auto MangledName = F->getName(); - std::string DemangledName; - - if (MangledName.startswith(SPCV_CAST)) - return transSpcvCast(CI, BB); - - if (MangledName.startswith("llvm.memcpy")) { - std::vector MemoryAccess; - - if (isa(CI->getOperand(4)) && - dyn_cast(CI->getOperand(4)) - ->getZExtValue() == 1) - MemoryAccess.push_back(MemoryAccessVolatileMask); - if (isa(CI->getOperand(3))) { - MemoryAccess.push_back(MemoryAccessAlignedMask); - MemoryAccess.push_back(dyn_cast(CI->getOperand(3)) - ->getZExtValue()); - } - - return BM->addCopyMemorySizedInst( - transValue(CI->getOperand(0), BB), - transValue(CI->getOperand(1), BB), - transValue(CI->getOperand(2), BB), - MemoryAccess, - BB); - } - - if (oclIsBuiltin(MangledName, &DemangledName) || - isDecoratedSPIRVFunc(F, &DemangledName)) - if (auto BV = transBuiltinToInst(DemangledName, MangledName, CI, BB)) - return BV; - - SmallVector Dec; - if (isBuiltinTransToExtInst(CI->getCalledFunction(), &ExtSetKind, - &ExtOp, &Dec)) - return addDecorations(BM->addExtInst( - transType(CI->getType()), - ExtSetId, - ExtOp, - transArguments(CI, BB, SPIRVEntry::create_unique(ExtSetKind, ExtOp).get()), - BB), Dec); - - return BM->addCallInst( - transFunctionDecl(CI->getCalledFunction()), - transArguments(CI, BB, SPIRVEntry::create_unique(OpFunctionCall).get()), - BB); -} - -bool -LLVMToSPIRV::transAddressingMode() { - Triple TargetTriple(M->getTargetTriple()); - Triple::ArchType Arch = TargetTriple.getArch(); - - SPIRVCKRT(Arch == Triple::spir || Arch == Triple::spir64, - InvalidTargetTriple, - "Actual target triple is " + M->getTargetTriple()); - - if (Arch == Triple::spir) - BM->setAddressingModel(AddressingModelPhysical32); - else - BM->setAddressingModel(AddressingModelPhysical64); - // Physical addressing model requires Addresses capability - BM->addCapability(CapabilityAddresses); - return true; -} -std::vector -LLVMToSPIRV::transValue(const std::vector &Args, SPIRVBasicBlock* BB) { - std::vector BArgs; - for (auto &I: Args) - BArgs.push_back(transValue(I, BB)); - return BArgs; -} - -std::vector -LLVMToSPIRV::transArguments(CallInst *CI, SPIRVBasicBlock *BB) { - return transValue(getArguments(CI), BB); -} - -std::vector -LLVMToSPIRV::transValue(const std::vector &Args, SPIRVBasicBlock* BB, - SPIRVEntry *Entry) { - std::vector Operands; - for (size_t I = 0, E = Args.size(); I != E; ++I) { - Operands.push_back(Entry->isOperandLiteral(I) ? - cast(Args[I])->getZExtValue() : - transValue(Args[I], BB)->getId()); - } - return Operands; -} - -std::vector -LLVMToSPIRV::transArguments(CallInst *CI, SPIRVBasicBlock *BB, SPIRVEntry *Entry) { - return transValue(getArguments(CI), BB, Entry); -} - -SPIRVWord -LLVMToSPIRV::transFunctionControlMask(CallInst *CI) { - SPIRVWord FCM = 0; - SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, - SPIRVFunctionControlMaskKind Mask){ - if (CI->hasFnAttr(Attr)) - FCM |= Mask; - }); - return FCM; -} - -SPIRVWord -LLVMToSPIRV::transFunctionControlMask(Function *F) { - SPIRVWord FCM = 0; - SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, - SPIRVFunctionControlMaskKind Mask){ - if (F->hasFnAttribute(Attr)) - FCM |= Mask; - }); - return FCM; -} - -bool -LLVMToSPIRV::transGlobalVariables() { - for (auto I = M->global_begin(), - E = M->global_end(); I != E; ++I) { - if (!transValue(I, nullptr)) - return false; - } - return true; -} - -void -LLVMToSPIRV::mutateFuncArgType(const std::map& ChangedType, - Function* F) { - for (auto &I : ChangedType) { - for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE; ++UI) { - auto Call = dyn_cast(*UI); - if (!Call) - continue; - auto Arg = Call->getArgOperand(I.first); - auto OrigTy = Arg->getType(); - if (OrigTy == I.second) - continue; - SPIRVDBG(dbgs() << "[mutate arg type] " << *Call << ", " << *Arg << '\n'); - auto CastF = M->getOrInsertFunction(SPCV_CAST, I.second, OrigTy, nullptr); - std::vector Args; - Args.push_back(Arg); - auto Cast = CallInst::Create(CastF, Args, "", Call); - Call->replaceUsesOfWith(Arg, Cast); - SPIRVDBG(dbgs() << "[mutate arg type] -> " << *Cast << '\n'); - } - } -} - -void -LLVMToSPIRV::transFunction(Function *I) { - transFunctionDecl(I); - // Creating all basic blocks before creating any instruction. - for (Function::iterator FI = I->begin(), FE = I->end(); FI != FE; ++FI) { - transValue(FI, nullptr); - } - for (Function::iterator FI = I->begin(), FE = I->end(); FI != FE; ++FI) { - SPIRVBasicBlock* BB = static_cast(transValue(FI, nullptr)); - for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; - ++BI) { - transValue(BI, BB, false); - } - } -} - -bool -LLVMToSPIRV::translate() { - BM->setGeneratorVer(kTranslatorVer); - - if (!transSourceLanguage()) - return false; - if (!transExtension()) - return false; - if (!transBuiltinSet()) - return false; - if (!transAddressingMode()) - return false; - if (!transGlobalVariables()) - return false; - - for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) { - Function *F = I; - auto FT = F->getFunctionType(); - std::map ChangedType; - oclGetMutatedArgumentTypesByBuiltin(FT, ChangedType, F); - mutateFuncArgType(ChangedType, F); - } - - // SPIR-V logical layout requires all function declarations go before - // function definitions. - std::vector Decls, Defs; - for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) { - if (isBuiltinTransToInst(I) || isBuiltinTransToExtInst(I) - || I->getName().startswith(SPCV_CAST) || - I->getName().startswith(LLVM_MEMCPY)) - continue; - if (I->isDeclaration()) - Decls.push_back(I); - else - Defs.push_back(I); - } - for (auto I:Decls) - transFunctionDecl(I); - for (auto I:Defs) - transFunction(I); - - if (!transOCLKernelMetadata()) - return false; - if (!transExecutionMode()) - return false; - - BM->optimizeDecorates(); - BM->resolveUnknownStructFields(); - BM->createForwardPointers(); - return true; -} - -llvm::IntegerType* LLVMToSPIRV::getSizetType() { - return IntegerType::getIntNTy(M->getContext(), - M->getDataLayout()->getPointerSizeInBits()); -} - -void -LLVMToSPIRV::oclGetMutatedArgumentTypesByBuiltin( - llvm::FunctionType* FT, std::map& ChangedType, - Function* F) { - auto Name = F->getName(); - std::string Demangled; - if (!oclIsBuiltin(Name, &Demangled)) - return; - if (Demangled.find(kSPIRVName::SampledImage) == std::string::npos) - return; - if (FT->getParamType(1)->isIntegerTy()) - ChangedType[1] = getSamplerType(F->getParent()); -} - -SPIRVInstruction * -LLVMToSPIRV::transBuiltinToInst(const std::string& DemangledName, - const std::string &MangledName, CallInst* CI, SPIRVBasicBlock* BB) { - SmallVector Dec; - auto OC = getSPIRVFuncOC(DemangledName, &Dec); - - if (OC == OpNop) - return nullptr; - - auto Inst = transBuiltinToInstWithoutDecoration(OC, CI, BB); - addDecorations(Inst, Dec); - return Inst; -} - -bool -LLVMToSPIRV::transExecutionMode() { - if (auto NMD = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::ExecutionMode)) { - while (!NMD.atEnd()) { - unsigned EMode = ~0U; - unsigned EModel = ~0U; - Function *F = nullptr; - auto N = NMD.nextOp(); /* execution mode MDNode */ - N.nextOp() /* entry point MDNode */ - .get(EModel) - .get(F) - .done() - .get(EMode); - assert (EModel == spv::ExecutionModelKernel && - "Unsupported execution model"); - SPIRVFunction *BF = static_cast(getTranslatedValue(F)); - assert(BF && "Invalid kernel function"); - switch(EMode) { - case spv::ExecutionModeContractionOff: - BF->addExecutionMode(new SPIRVExecutionMode(BF, - ExecutionModeContractionOff)); - break; - case spv::ExecutionModeLocalSize: { - unsigned X, Y, Z; - N.get(X).get(Y).get(Z); - BF->addExecutionMode(new SPIRVExecutionMode(BF, - ExecutionModeLocalSize, X, Y, Z)); - } - break; - case spv::ExecutionModeLocalSizeHint: { - unsigned X, Y, Z; - N.get(X).get(Y).get(Z); - BF->addExecutionMode(new SPIRVExecutionMode(BF, - ExecutionModeLocalSizeHint, X, Y, Z)); - } - break; - case spv::ExecutionModeVecTypeHint: { - unsigned X; - N.get(X); - BF->addExecutionMode(new SPIRVExecutionMode(BF, - ExecutionModeVecTypeHint, X)); - } - break; - default: - llvm_unreachable("invalid execution mode"); - } - } - } - return true; -} - -bool -LLVMToSPIRV::transOCLKernelMetadata() { - NamedMDNode *KernelMDs = M->getNamedMetadata(SPIR_MD_KERNELS); - std::vector argAccessQual; - if (!KernelMDs) - return true; - - for (unsigned I = 0, E = KernelMDs->getNumOperands(); I < E; ++I) { - MDNode *KernelMD = KernelMDs->getOperand(I); - if (KernelMD->getNumOperands() == 0) - continue; - Function *Kernel = mdconst::dyn_extract(KernelMD->getOperand(0)); - - SPIRVFunction *BF = static_cast(getTranslatedValue(Kernel)); - assert(BF && "Kernel function should be translated first"); - assert(Kernel && oclIsKernel(Kernel) - && "Invalid kernel calling convention or metadata"); - for (unsigned MI = 1, ME = KernelMD->getNumOperands(); MI < ME; ++MI) { - MDNode *MD = dyn_cast(KernelMD->getOperand(MI)); - if (!MD) - continue; - MDString *NameMD = dyn_cast(MD->getOperand(0)); - if (!NameMD) - continue; - StringRef Name = NameMD->getString(); - if (Name == SPIR_MD_KERNEL_ARG_TYPE_QUAL) { - foreachKernelArgMD(MD, BF, - [](const std::string &Str, SPIRVFunctionParameter *BA){ - if (Str.find("volatile") != std::string::npos) - BA->addDecorate(new SPIRVDecorate(DecorationVolatile, BA)); - if (Str.find("restrict") != std::string::npos) - BA->addDecorate(new SPIRVDecorate(DecorationFuncParamAttr, - BA, FunctionParameterAttributeNoAlias)); - if (Str.find("const") != std::string::npos) - BA->addDecorate(new SPIRVDecorate(DecorationFuncParamAttr, - BA, FunctionParameterAttributeNoWrite)); - }); - } else if (Name == SPIR_MD_KERNEL_ARG_NAME) { - foreachKernelArgMD(MD, BF, - [=](const std::string &Str, SPIRVFunctionParameter *BA){ - BM->setName(BA, Str); - }); - } - } - } - return true; -} - -bool -LLVMToSPIRV::transSourceLanguage() { - auto Src = getSPIRVSource(M); - SrcLang = std::get<0>(Src); - SrcLangVer = std::get<1>(Src); - BM->setSourceLanguage(static_cast(SrcLang), SrcLangVer); - return true; -} - -bool -LLVMToSPIRV::transExtension() { - if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::Extension)) { - while (!N.atEnd()) { - std::string S; - N.nextOp().get(S); - assert(!S.empty() && "Invalid extension"); - BM->getExtension().insert(S); - } - } - if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::SourceExtension)) { - while (!N.atEnd()) { - std::string S; - N.nextOp().get(S); - assert(!S.empty() && "Invalid extension"); - BM->getSourceExtension().insert(S); - } - } - for (auto &I:map(rmap(BM->getExtension()))) - BM->addCapability(I); - - return true; -} - -void -LLVMToSPIRV::dumpUsers(Value* V) { - SPIRVDBG(dbgs() << "Users of " << *V << " :\n"); - for (auto UI = V->user_begin(), UE = V->user_end(); - UI != UE; ++UI) - SPIRVDBG(dbgs() << " " << **UI << '\n'); -} - -Op -LLVMToSPIRV::transBoolOpCode(SPIRVValue* Opn, Op OC) { - if (!Opn->getType()->isTypeVectorOrScalarBool()) - return OC; - IntBoolOpMap::find(OC, &OC); - return OC; -} - -SPIRVInstruction * -LLVMToSPIRV::transBuiltinToInstWithoutDecoration(Op OC, - CallInst* CI, SPIRVBasicBlock* BB) { - if (isGroupOpCode(OC)) - BM->addCapability(CapabilityGroups); - switch (OC) { - case OpControlBarrier: { - auto BArgs = transValue(getArguments(CI), BB); - return BM->addControlBarrierInst( - BArgs[0], BArgs[1], BArgs[2], BB); - } - break; - case OpGroupAsyncCopy: { - auto BArgs = transValue(getArguments(CI), BB); - return BM->addAsyncGroupCopy(BArgs[0], BArgs[1], BArgs[2], BArgs[3], - BArgs[4], BArgs[5], BB); - } - break; - default: { - if (isCvtOpCode(OC) && OC != OpGenericCastToPtrExplicit) { - return BM->addUnaryInst(OC, transType(CI->getType()), - transValue(CI->getArgOperand(0), BB), BB); - } else if (isCmpOpCode(OC)) { - assert(CI && CI->getNumArgOperands() == 2 && "Invalid call inst"); - auto ResultTy = CI->getType(); - Type *BoolTy = IntegerType::getInt1Ty(M->getContext()); - auto IsVector = ResultTy->isVectorTy(); - if (IsVector) - BoolTy = VectorType::get(BoolTy, ResultTy->getVectorNumElements()); - auto BBT = transType(BoolTy); - auto Cmp = BM->addCmpInst(OC, BBT, - transValue(CI->getArgOperand(0), BB), - transValue(CI->getArgOperand(1), BB), BB); - auto Zero = transValue(Constant::getNullValue(ResultTy), BB); - auto One = transValue( - IsVector ? Constant::getAllOnesValue(ResultTy) : getInt32(M, 1), BB); - return BM->addSelectInst(Cmp, One, Zero, BB); - } else if (isBinaryOpCode(OC)) { - assert(CI && CI->getNumArgOperands() == 2 && "Invalid call inst"); - return BM->addBinaryInst(OC, transType(CI->getType()), - transValue(CI->getArgOperand(0), BB), - transValue(CI->getArgOperand(1), BB), BB); - } else if (CI->getNumArgOperands() == 1 && - !CI->getType()->isVoidTy() && - !hasExecScope(OC) && - !isAtomicOpCode(OC)) { - return BM->addUnaryInst(OC, transType(CI->getType()), - transValue(CI->getArgOperand(0), BB), BB); - } else { - auto Args = getArguments(CI); - SPIRVType *SPRetTy = nullptr; - Type *RetTy = CI->getType(); - auto F = CI->getCalledFunction(); - if (!RetTy->isVoidTy()) { - SPRetTy = transType(RetTy); - } else if (Args.size() > 0 && F->arg_begin()->hasStructRetAttr()) { - SPRetTy = transType(F->arg_begin()->getType()->getPointerElementType()); - Args.erase(Args.begin()); - } - auto SPI = BM->addInstTemplate(OC, BB, SPRetTy); - std::vector SPArgs; - for (size_t I = 0, E = Args.size(); I != E; ++I) { - assert((!isFunctionPointerType(Args[I]->getType()) || - isa(Args[I])) && - "Invalid function pointer argument"); - SPArgs.push_back(SPI->isOperandLiteral(I) ? - cast(Args[I])->getZExtValue() : - transValue(Args[I], BB)->getId()); - } - SPI->setOpWordsAndValidate(SPArgs); - if (!SPRetTy || !SPRetTy->isTypeStruct()) - return SPI; - std::vector Mem; - SPIRVDBG(spvdbgs() << *SPI << '\n'); - return BM->addStoreInst(transValue(CI->getArgOperand(0), BB), SPI, - Mem, BB); - } - } - } - return nullptr; -} - - -SPIRVId -LLVMToSPIRV::addInt32(int I) { - return transValue(getInt32(M, I), nullptr, false)->getId(); -} - -SPIRV::SPIRVLinkageTypeKind -LLVMToSPIRV::transLinkageType(const GlobalValue* GV) { - if(GV->isDeclarationForLinker()) - return SPIRVLinkageTypeKind::LinkageTypeImport; - if(GV->hasInternalLinkage() || GV->hasPrivateLinkage()) - return SPIRVLinkageTypeKind::LinkageTypeInternal; - return SPIRVLinkageTypeKind::LinkageTypeExport; -} -} // end of SPIRV namespace - -char LLVMToSPIRV::ID = 0; - -INITIALIZE_PASS_BEGIN(LLVMToSPIRV, "llvmtospv", "Translate LLVM to SPIR-V", - false, false) -INITIALIZE_PASS_DEPENDENCY(OCLTypeToSPIRV) -INITIALIZE_PASS_END(LLVMToSPIRV, "llvmtospv", "Translate LLVM to SPIR-V", - false, false) - -ModulePass *llvm::createLLVMToSPIRV(SPIRVModule *SMod) { - return new LLVMToSPIRV(SMod); -} - -void -addPassesForSPIRV(PassManager &PassMgr) { - if (SPIRVMemToReg) - PassMgr.add(createPromoteMemoryToRegisterPass()); - PassMgr.add(createTransOCLMD()); - PassMgr.add(createOCL21ToSPIRV()); - PassMgr.add(createSPIRVLowerOCLBlocks()); - PassMgr.add(createOCLTypeToSPIRV()); - PassMgr.add(createOCL20ToSPIRV()); - PassMgr.add(createSPIRVRegularizeLLVM()); - PassMgr.add(createSPIRVLowerConstExpr()); - PassMgr.add(createSPIRVLowerBool()); -} - -bool -llvm::WriteSPIRV(Module *M, llvm::raw_ostream &OS, std::string &ErrMsg) { - std::unique_ptr BM(SPIRVModule::createSPIRVModule()); - PassManager PassMgr; - addPassesForSPIRV(PassMgr); - PassMgr.add(createLLVMToSPIRV(BM.get())); - PassMgr.run(*M); - - if (BM->getError(ErrMsg) != SPIRVEC_Success) - return false; - OS << *BM; - return true; -} - -bool -llvm::RegularizeLLVMForSPIRV(Module *M, std::string &ErrMsg) { - std::unique_ptr BM(SPIRVModule::createSPIRVModule()); - PassManager PassMgr; - addPassesForSPIRV(PassMgr); - PassMgr.run(*M); - return true; -} - +//===- SPIRVWriter.cpp - Converts LLVM to SPIR-V ----------------*- C++ -*-===// +// +// The LLVM/SPIR-V Translator +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file implements conversion of LLVM intermediate language to SPIR-V +/// binary. +/// +//===----------------------------------------------------------------------===// + +#include "SPIRVModule.h" +#include "SPIRVEnum.h" +#include "SPIRVEntry.h" +#include "SPIRVType.h" +#include "SPIRVValue.h" +#include "SPIRVFunction.h" +#include "SPIRVBasicBlock.h" +#include "SPIRVInstruction.h" +#include "SPIRVExtInst.h" +#include "SPIRVUtil.h" +#include "SPIRVInternal.h" +#include "SPIRVMDWalker.h" +#include "OCLTypeToSPIRV.h" +#include "OCLUtil.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Bitcode/BitcodeWriter.h" +#include "llvm/Bitcode/BitcodeReader.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Pass.h" +#include "llvm/PassSupport.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Transforms/IPO.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_TYPE "spirv" + +using namespace llvm; +using namespace SPIRV; +using namespace OCLUtil; + +namespace llvm { + FunctionPass *createPromoteMemoryToRegisterPass(); +} + +namespace SPIRV{ + +cl::opt SPIRVMemToReg("spirv-mem2reg", cl::init(true), + cl::desc("LLVM/SPIR-V translation enable mem2reg")); + + +static void +foreachKernelArgMD(MDNode *MD, SPIRVFunction *BF, + std::functionFunc) { + for (unsigned I = 1, E = MD->getNumOperands(); I != E; ++I) { + SPIRVFunctionParameter *BA = BF->getArgument(I-1); + Func(getMDOperandAsString(MD, I), BA); + } +} + +/// Information for translating OCL builtin. +struct OCLBuiltinSPIRVTransInfo { + std::string UniqName; + /// Postprocessor of operands + std::function&)> PostProc; + OCLBuiltinSPIRVTransInfo(){ + PostProc = [](std::vector&){}; + } +}; + +class LLVMToSPIRVDbgTran { +public: + LLVMToSPIRVDbgTran(Module *TM = nullptr, SPIRVModule *TBM = nullptr) + :BM(TBM), M(TM){ + } + + void setModule(Module *Mod) { M = Mod;} + void setSPIRVModule(SPIRVModule *SMod) { BM = SMod;} + + void transDbgInfo(Value *V, SPIRVValue *BV) { + if (auto I = dyn_cast(V)) { + auto DL = I->getDebugLoc(); + if (!DL) { + //DILocation DIL(*DL.get()); + auto File = BM->getString((*DL).getFilename().str()); + // ToDo: SPIR-V rev.31 cannot add debug info for instructions without ids. + // This limitation needs to be addressed. + if (!BV->hasId()) + return; + BM->addLine(BV, File, (*DL).getLine(), (*DL).getColumn()); + } + } else if (auto F = dyn_cast(V)) { + if (auto DIS = F->getSubprogram()) { + auto File = BM->getString(DIS->getFilename().str()); + BM->addLine(BV, File, DIS->getLine(), 0); + } + } + } + +private: + SPIRVModule *BM; + Module *M; +}; + +class LLVMToSPIRV: public ModulePass { +public: + LLVMToSPIRV(SPIRVModule *SMod = nullptr) + : ModulePass(ID), + M(nullptr), + Ctx(nullptr), + BM(SMod), + ExtSetId(SPIRVID_INVALID), + SrcLang(0), + SrcLangVer(0), + DbgTran(nullptr, SMod){ + } + + bool runOnModule(Module &Mod) override { + M = &Mod; + Ctx = &M->getContext(); + DbgTran.setModule(M); + assert(BM && "SPIR-V module not initialized"); + translate(); + return true; + } + + void getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + } + + static char ID; + + SPIRVType *transType(Type *T); + SPIRVType *transSPIRVOpaqueType(Type *T); + + SPIRVValue *getTranslatedValue(Value *); + + // Translation functions + bool transAddressingMode(); + bool transAlign(Value *V, SPIRVValue *BV); + std::vector transArguments(CallInst *, SPIRVBasicBlock *); + std::vector transArguments(CallInst *, SPIRVBasicBlock *, + SPIRVEntry *); + bool transSourceLanguage(); + bool transExtension(); + bool transBuiltinSet(); + SPIRVValue *transCallInst(CallInst *Call, SPIRVBasicBlock *BB); + bool transDecoration(Value *V, SPIRVValue *BV); + SPIRVWord transFunctionControlMask(CallInst *); + SPIRVWord transFunctionControlMask(Function *); + SPIRVFunction *transFunctionDecl(Function *F); + bool transGlobalVariables(); + + Op transBoolOpCode(SPIRVValue *Opn, Op OC); + // Translate LLVM module to SPIR-V module. + // Returns true if succeeds. + bool translate(); + bool transExecutionMode(); + SPIRVValue *transConstant(Value *V); + SPIRVValue *transValue(Value *V, SPIRVBasicBlock *BB, + bool CreateForward = true); + SPIRVValue *transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB, + bool CreateForward = true); + + typedef DenseMap LLVMToSPIRVTypeMap; + typedef DenseMap LLVMToSPIRVValueMap; +private: + Module *M; + LLVMContext *Ctx; + SPIRVModule *BM; + LLVMToSPIRVTypeMap TypeMap; + LLVMToSPIRVValueMap ValueMap; + //ToDo: support multiple builtin sets. Currently assume one builtin set. + SPIRVId ExtSetId; + SPIRVWord SrcLang; + SPIRVWord SrcLangVer; + LLVMToSPIRVDbgTran DbgTran; + + SPIRVType *mapType(Type *T, SPIRVType *BT) { + TypeMap[T] = BT; + SPIRVDBG(dbgs() << "[mapType] " << *T << " => "; + spvdbgs() << *BT << '\n'); + return BT; + } + + SPIRVValue *mapValue(Value *V, SPIRVValue *BV) { + auto Loc = ValueMap.find(V); + if (Loc != ValueMap.end()) { + if (Loc->second == BV) + return BV; + assert (Loc->second->isForward() && + "LLVM Value is mapped to different SPIRV Values"); + auto Forward = static_cast(Loc->second); + BV->setId(Forward->getId()); + BM->replaceForward(Forward, BV); + } + ValueMap[V] = BV; + SPIRVDBG(dbgs() << "[mapValue] " << *V << " => "; + spvdbgs() << *BV << "\n"); + return BV; + } + + SPIRVType *getSPIRVType(Type *T) { + return TypeMap[T]; + } + + SPIRVValue *getSPIRVValue(Value *V) { + return ValueMap[V]; + } + + SPIRVErrorLog &getErrorLog() { + return BM->getErrorLog(); + } + + llvm::IntegerType* getSizetType(); + std::vector transValue(const std::vector &Values, + SPIRVBasicBlock* BB); + std::vector transValue(const std::vector &Values, + SPIRVBasicBlock* BB, SPIRVEntry *Entry); + + SPIRVInstruction* transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB); + SPIRVInstruction* transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB); + + void dumpUsers(Value *V); + + template + bool oclGetExtInstIndex(const std::string &MangledName, + const std::string& DemangledName, SPIRVWord* EntryPoint); + void oclGetMutatedArgumentTypesByBuiltin(llvm::FunctionType* FT, + std::map& ChangedType, Function* F); + + bool isBuiltinTransToInst(Function *F); + bool isBuiltinTransToExtInst(Function *F, + SPIRVExtInstSetKind *BuiltinSet = nullptr, + SPIRVWord *EntryPoint = nullptr, + SmallVectorImpl *Dec = nullptr); + bool oclIsKernel(Function *F); + + bool transOCLKernelMetadata(); + + SPIRVInstruction *transBuiltinToInst(const std::string& DemangledName, + const std::string &MangledName, CallInst* CI, SPIRVBasicBlock* BB); + SPIRVInstruction *transBuiltinToInstWithoutDecoration(Op OC, + CallInst* CI, SPIRVBasicBlock* BB); + void mutateFuncArgType(const std::map& ChangedType, + Function* F); + + SPIRVValue *transSpcvCast(CallInst* CI, SPIRVBasicBlock *BB); + SPIRVValue *oclTransSpvcCastSampler(CallInst* CI, SPIRVBasicBlock *BB); + + SPIRV::SPIRVInstruction* transUnaryInst(UnaryInstruction* U, + SPIRVBasicBlock* BB); + + /// Add a 32 bit integer constant. + /// \return Id of the constant. + SPIRVId addInt32(int); + void transFunction(Function *I); + SPIRV::SPIRVLinkageTypeKind transLinkageType(const GlobalValue* GV); +}; + + +SPIRVValue * +LLVMToSPIRV::getTranslatedValue(Value *V) { + LLVMToSPIRVValueMap::iterator Loc = ValueMap.find(V); + if (Loc != ValueMap.end()) + return Loc->second; + return nullptr; +} + +bool +LLVMToSPIRV::oclIsKernel(Function *F) { + if (F->getCallingConv() == CallingConv::SPIR_KERNEL) + return true; + return false; +} + +bool +LLVMToSPIRV::isBuiltinTransToInst(Function *F) { + std::string DemangledName; + if (!oclIsBuiltin(F->getName(), &DemangledName) && + !isDecoratedSPIRVFunc(F, &DemangledName)) + return false; + SPIRVDBG(spvdbgs() << "CallInst: demangled name: " << DemangledName << '\n'); + return getSPIRVFuncOC(DemangledName) != OpNop; +} + +bool +LLVMToSPIRV::isBuiltinTransToExtInst(Function *F, + SPIRVExtInstSetKind *ExtSet, + SPIRVWord *ExtOp, + SmallVectorImpl *Dec) { + std::string OrigName = F->getName(); + std::string DemangledName; + if (!oclIsBuiltin(OrigName, &DemangledName)) + return false; + DEBUG(dbgs() << "[oclIsBuiltinTransToExtInst] CallInst: demangled name: " + << DemangledName << '\n'); + StringRef S = DemangledName; + if (!S.startswith(kSPIRVName::Prefix)) + return false; + S = S.drop_front(strlen(kSPIRVName::Prefix)); + auto Loc = S.find(kSPIRVPostfix::Divider); + auto ExtSetName = S.substr(0, Loc); + SPIRVExtInstSetKind Set = SPIRVEIS_Count; + if (!SPIRVExtSetShortNameMap::rfind(ExtSetName, &Set)) + return false; + assert(Set == BM->getBuiltinSet(ExtSetId) && + "Invalid extended instruction set"); + assert(Set == SPIRVEIS_OpenCL && "Unsupported extended instruction set"); + + auto ExtOpName = S.substr(Loc + 1); + auto Splited = ExtOpName.split(kSPIRVPostfix::ExtDivider); + OCLExtOpKind EOC; + if (!OCLExtOpMap::rfind(Splited.first, &EOC)) + return false; + + if (ExtSet) + *ExtSet = Set; + if (ExtOp) + *ExtOp = EOC; + if (Dec) { + SmallVector P; + Splited.second.split(P, kSPIRVPostfix::Divider); + for (auto &I:P) + Dec->push_back(I.str()); + } + return true; +} + +/// Decode SPIR-V type name in the format spirv.{TypeName}._{Postfixes} +/// where Postfixes are strings separated by underscores. +/// \return TypeName. +/// \param Ops contains the integers decoded from postfixes. +static std::string + decodeSPIRVTypeName(StringRef Name, + SmallVectorImpl& Strs) { + SmallVector SubStrs; + const char Delim[] = { kSPIRVTypeName::Delimiter, 0 }; + Name.split(SubStrs, Delim, -1, true); + assert(SubStrs.size() >= 2 && "Invalid SPIRV type name"); + assert(SubStrs[0] == kSPIRVTypeName::Prefix && "Invalid prefix"); + assert((SubStrs.size() == 2 || !SubStrs[2].empty()) && "Invalid postfix"); + + if (SubStrs.size() > 2) { + const char PostDelim[] = { kSPIRVTypeName::PostfixDelim, 0 }; + SmallVector Postfixes; + SubStrs[2].split(Postfixes, PostDelim, -1, true); + assert(Postfixes.size() > 1 && Postfixes[0].empty() && "Invalid postfix"); + for (unsigned I = 1, E = Postfixes.size(); I != E; ++I) + Strs.push_back(std::string(Postfixes[I]).c_str()); + } + return SubStrs[1].str(); +} + +static bool recursiveType(const StructType *ST, const Type *Ty) { + SmallPtrSet Seen; + + std::function Run = [&](const Type *Ty) { + if (!isa(Ty)) + return false; + + if (auto *StructTy = dyn_cast(Ty)) { + if (StructTy == ST) + return true; + + if (Seen.count(StructTy)) + return false; + + Seen.insert(StructTy); + + return find_if(StructTy->subtype_begin(), StructTy->subtype_end(), Run) != + StructTy->subtype_end(); + } + + if (auto *PtrTy = dyn_cast(Ty)) + return Run(PtrTy->getPointerElementType()); + + if (auto *ArrayTy = dyn_cast(Ty)) + return Run(ArrayTy->getArrayElementType()); + + return false; + }; + + return Run(Ty); +} + +SPIRVType * +LLVMToSPIRV::transType(Type *T) { + LLVMToSPIRVTypeMap::iterator Loc = TypeMap.find(T); + if (Loc != TypeMap.end()) + return Loc->second; + + SPIRVDBG(dbgs() << "[transType] " << *T << '\n'); + if (T->isVoidTy()) + return mapType(T, BM->addVoidType()); + + if (T->isIntegerTy(1)) + return mapType(T, BM->addBoolType()); + + if (T->isIntegerTy()) + return mapType(T, BM->addIntegerType(T->getIntegerBitWidth())); + + if (T->isFloatingPointTy()) + return mapType(T, BM->addFloatType(T->getPrimitiveSizeInBits())); + + // A pointer to image or pipe type in LLVM is translated to a SPIRV + // sampler or pipe type. + if (T->isPointerTy()) { + auto ET = T->getPointerElementType(); + assert(!ET->isFunctionTy() && "Function pointer type is not allowed"); + auto ST = dyn_cast(ET); + auto AddrSpc = T->getPointerAddressSpace(); + if (ST && !ST->isSized()) { + Op OpCode; + StringRef STName = ST->getName(); + // Workaround for non-conformant SPIR binary + if (STName == "struct._event_t") { + STName = kSPR2TypeName::Event; + ST->setName(STName); + } + assert (!STName.startswith(kSPR2TypeName::Pipe) && + "OpenCL type names should be translated to SPIR-V type names"); + // ToDo: For SPIR1.2/2.0 there may still be load/store or bitcast + // instructions using opencl.* type names. We need to handle these + // type names until they are all mapped or FE generates SPIR-V type + // names. + if (STName.find(kSPR2TypeName::Pipe) == 0) { + assert(AddrSpc == SPIRAS_Global); + SmallVector SubStrs; + const char Delims[] = {kSPR2TypeName::Delimiter, 0}; + STName.split(SubStrs, Delims); + std::string Acc = kAccessQualName::ReadOnly; + if (SubStrs.size() > 2) { + Acc = SubStrs[2]; + } + auto PipeT = BM->addPipeType(); + PipeT->setPipeAcessQualifier(SPIRSPIRVAccessQualifierMap::map(Acc)); + return mapType(T, PipeT); + } else if (STName.find(kSPR2TypeName::ImagePrefix) == 0) { + assert(AddrSpc == SPIRAS_Global); + auto SPIRVImageTy = getSPIRVImageTypeFromOCL(M, T); + return mapType(T, transSPIRVOpaqueType(SPIRVImageTy)); + } else if (STName.startswith(kSPIRVTypeName::PrefixAndDelim)) + return transSPIRVOpaqueType(T); + else if (OCLOpaqueTypeOpCodeMap::find(STName, &OpCode)) { + if (OpCode == OpTypePipe) { + return mapType(T, BM->addPipeType()); + } + return mapType(T, BM->addOpaqueGenericType(OpCode)); + } else if (isPointerToOpaqueStructType(T)) { + return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map( + static_cast(AddrSpc)), + transType(ET))); + } + } else { + return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map( + static_cast(AddrSpc)), + transType(ET))); + } + } + + if (T->isVectorTy()) + return mapType(T, BM->addVectorType(transType(T->getVectorElementType()), + T->getVectorNumElements())); + + if (T->isArrayTy()) + return mapType(T, BM->addArrayType(transType(T->getArrayElementType()), + static_cast(transValue(ConstantInt::get(getSizetType(), + T->getArrayNumElements(), false), nullptr)))); + + if (T->isStructTy() && !T->isSized()) { + auto ST = dyn_cast(T); + assert(!ST->getName().startswith(kSPR2TypeName::Pipe)); + assert(!ST->getName().startswith(kSPR2TypeName::ImagePrefix)); + return mapType(T, BM->addOpaqueType(T->getStructName())); + } + + if (auto ST = dyn_cast(T)) { + assert(ST->isSized()); + + std::string Name; + if (ST->hasName()) + Name = ST->getName(); + + if(Name == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) + return transType(getSamplerType(M)); + + auto *Struct = BM->openStructType(T->getStructNumElements(), Name); + mapType(T, Struct); + + SmallVector ForwardRefs; + + for (unsigned I = 0, E = T->getStructNumElements(); I != E; ++I) { + auto *ElemTy = ST->getElementType(I); + if (isa(ElemTy) && recursiveType(ST, ElemTy)) + ForwardRefs.push_back(I); + else + Struct->setMemberType(I, transType(ST->getElementType(I))); + } + + BM->closeStructType(Struct, ST->isPacked()); + + for (auto I : ForwardRefs) + Struct->setMemberType(I, transType(ST->getElementType(I))); + + return Struct; + } + + if (FunctionType *FT = dyn_cast(T)) { + SPIRVType *RT = transType(FT->getReturnType()); + std::vector PT; + for (FunctionType::param_iterator I = FT->param_begin(), + E = FT->param_end(); I != E; ++I) + PT.push_back(transType(*I)); + return mapType(T, BM->addFunctionType(RT, PT)); + } + + llvm_unreachable("Not implemented!"); + return 0; +} + +SPIRVType * +LLVMToSPIRV::transSPIRVOpaqueType(Type *T) { + auto ET = T->getPointerElementType(); + auto ST = cast(ET); + auto AddrSpc = T->getPointerAddressSpace(); + auto STName = ST->getStructName(); + assert (STName.startswith(kSPIRVTypeName::PrefixAndDelim) && + "Invalid SPIR-V opaque type name"); + SmallVector Postfixes; + auto TN = decodeSPIRVTypeName(STName, Postfixes); + if (TN == kSPIRVTypeName::Pipe) { + assert(AddrSpc == SPIRAS_Global); + assert(Postfixes.size() == 1 && "Invalid pipe type ops"); + auto PipeT = BM->addPipeType(); + PipeT->setPipeAcessQualifier(static_cast( + atoi(Postfixes[0].c_str()))); + return mapType(T, PipeT); + } else if (TN == kSPIRVTypeName::Image) { + assert(AddrSpc == SPIRAS_Global); + // The sampled type needs to be translated through LLVM type to guarantee + // uniqueness. + auto SampledT = transType(getLLVMTypeForSPIRVImageSampledTypePostfix( + Postfixes[0], *Ctx)); + SmallVector Ops; + for (unsigned I = 1; I < 8; ++I) + Ops.push_back(atoi(Postfixes[I].c_str())); + SPIRVTypeImageDescriptor Desc(static_cast(Ops[0]), + Ops[1], Ops[2], Ops[3], Ops[4], Ops[5]); + return mapType(T, BM->addImageType(SampledT, Desc, + static_cast(Ops[6]))); + } else if (TN == kSPIRVTypeName::SampledImg) { + return mapType(T, BM->addSampledImageType( + static_cast( + transType(getSPIRVTypeByChangeBaseTypeName(M, + T, kSPIRVTypeName::SampledImg, + kSPIRVTypeName::Image))))); + } else if(TN == kSPIRVTypeName::Sampler) + return mapType(T, BM->addSamplerType()); + else if (TN == kSPIRVTypeName::DeviceEvent) + return mapType(T, BM->addDeviceEventType()); + else + return mapType(T, BM->addOpaqueGenericType( + SPIRVOpaqueTypeOpCodeMap::map(TN))); +} + +SPIRVFunction * +LLVMToSPIRV::transFunctionDecl(Function *F) { + if (auto BF= getTranslatedValue(F)) + return static_cast(BF); + + SPIRVTypeFunction *BFT = static_cast(transType( + getAnalysis().getAdaptedType(F))); + SPIRVFunction *BF = static_cast(mapValue(F, + BM->addFunction(BFT))); + BF->setFunctionControlMask(transFunctionControlMask(F)); + if (F->hasName()) + BM->setName(BF, F->getName()); + if (oclIsKernel(F)) + BM->addEntryPoint(ExecutionModelKernel, BF->getId()); + else if (F->getLinkage() != GlobalValue::InternalLinkage) + BF->setLinkageType(transLinkageType(F)); + auto Attrs = F->getAttributes(); + for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E; + ++I) { + auto ArgNo = I->getArgNo(); + SPIRVFunctionParameter *BA = BF->getArgument(ArgNo); + if (I->hasName()) + BM->setName(BA, I->getName()); + if (I->hasByValAttr()) + BA->addAttr(FunctionParameterAttributeByVal); + if (I->hasNoAliasAttr()) + BA->addAttr(FunctionParameterAttributeNoAlias); + if (I->hasNoCaptureAttr()) + BA->addAttr(FunctionParameterAttributeNoCapture); + if (I->hasStructRetAttr()) + BA->addAttr(FunctionParameterAttributeSret); + if (Attrs.hasAttribute(ArgNo + 1, Attribute::ZExt)) + BA->addAttr(FunctionParameterAttributeZext); + if (Attrs.hasAttribute(ArgNo + 1, Attribute::SExt)) + BA->addAttr(FunctionParameterAttributeSext); + } + if (Attrs.hasAttribute(AttributeSet::ReturnIndex, Attribute::ZExt)) + BF->addDecorate(DecorationFuncParamAttr, FunctionParameterAttributeZext); + if (Attrs.hasAttribute(AttributeSet::ReturnIndex, Attribute::SExt)) + BF->addDecorate(DecorationFuncParamAttr, FunctionParameterAttributeSext); + DbgTran.transDbgInfo(F, BF); + SPIRVDBG(dbgs() << "[transFunction] " << *F << " => "; + spvdbgs() << *BF << '\n';) + return BF; +} + +#define _SPIRV_OPL(x) OpLogical##x + +#define _SPIRV_OPB(x) OpBitwise##x + +SPIRVValue * +LLVMToSPIRV::transConstant(Value *V) { + if (auto CPNull = dyn_cast(V)) + return BM->addNullConstant(bcast(transType( + CPNull->getType()))); + + if (auto CAZero = dyn_cast(V)) { + Type *AggType = CAZero->getType(); + if (const StructType* ST = dyn_cast(AggType)) { + if (ST->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) + return BM->addSamplerConstant(transType(AggType), 0,0,0); + } else + return BM->addNullConstant(transType(AggType)); + } + + if (auto ConstI = dyn_cast(V)) + return BM->addConstant(transType(V->getType()), ConstI->getZExtValue()); + + if (auto ConstFP = dyn_cast(V)) { + auto BT = static_cast(transType(V->getType())); + return BM->addConstant(BT, + ConstFP->getValueAPF().bitcastToAPInt().getZExtValue()); + } + + if (auto ConstDA = dyn_cast(V)) { + std::vector BV; + for (unsigned I = 0, E = ConstDA->getNumElements(); I != E; ++I) + BV.push_back(transValue(ConstDA->getElementAsConstant(I), nullptr)); + return BM->addCompositeConstant(transType(V->getType()), BV); + } + + if (auto ConstA = dyn_cast(V)) { + std::vector BV; + for (auto I = ConstA->op_begin(), E = ConstA->op_end(); I != E; ++I) + BV.push_back(transValue(*I, nullptr)); + return BM->addCompositeConstant(transType(V->getType()), BV); + } + + if (auto ConstDV = dyn_cast(V)) { + std::vector BV; + for (unsigned I = 0, E = ConstDV->getNumElements(); I != E; ++I) + BV.push_back(transValue(ConstDV->getElementAsConstant(I), nullptr)); + return BM->addCompositeConstant(transType(V->getType()), BV); + } + + if (auto ConstV = dyn_cast(V)) { + std::vector BV; + for (auto I = ConstV->op_begin(), E = ConstV->op_end(); I != E; ++I) + BV.push_back(transValue(*I, nullptr)); + return BM->addCompositeConstant(transType(V->getType()), BV); + } + + if (auto ConstV = dyn_cast(V)) { + if (ConstV->getType()->getName() == + getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) { + assert(ConstV->getNumOperands() == 3); + SPIRVWord + AddrMode = ConstV->getOperand(0)->getUniqueInteger().getZExtValue(), + Normalized = ConstV->getOperand(1)->getUniqueInteger().getZExtValue(), + FilterMode = ConstV->getOperand(2)->getUniqueInteger().getZExtValue(); + assert(AddrMode < 5 && "Invalid addressing mode"); + assert(Normalized < 2 && "Invalid value of normalized coords"); + assert(FilterMode < 2 && "Invalid filter mode"); + SPIRVType* SamplerTy = transType(ConstV->getType()); + return BM->addSamplerConstant(SamplerTy, + AddrMode, Normalized, FilterMode); + } + std::vector BV; + for (auto I = ConstV->op_begin(), E = ConstV->op_end(); I != E; ++I) + BV.push_back(transValue(*I, nullptr)); + return BM->addCompositeConstant(transType(V->getType()), BV); + } + + if (auto ConstUE = dyn_cast(V)) { + auto Inst = ConstUE->getAsInstruction(); + SPIRVDBG(dbgs() << "ConstantExpr: " << *ConstUE << '\n'; + dbgs() << "Instruction: " << *Inst << '\n';) + auto BI = transValue(Inst, nullptr, false); + Inst->dropAllReferences(); + return BI; + } + + if (isa(V)) { + return BM->addUndef(transType(V->getType())); + } + + return nullptr; +} + +SPIRVValue * +LLVMToSPIRV::transValue(Value *V, SPIRVBasicBlock *BB, bool CreateForward) { + LLVMToSPIRVValueMap::iterator Loc = ValueMap.find(V); + if (Loc != ValueMap.end() && (!Loc->second->isForward() || CreateForward)) + return Loc->second; + + SPIRVDBG(dbgs() << "[transValue] " << *V << '\n'); + assert ((!isa(V) || isa(V) || + isa(V) || BB) && + "Invalid SPIRV BB"); + + auto BV = transValueWithoutDecoration(V, BB, CreateForward); + std::string name = V->getName(); + if (!name.empty()) // Don't erase the name, which BM might already have + BM->setName(BV, name); + if(!transDecoration(V, BV)) + return nullptr; + return BV; +} + +SPIRVInstruction* +LLVMToSPIRV::transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB) { + unsigned LLVMOC = B->getOpcode(); + auto Op0 = transValue(B->getOperand(0), BB); + SPIRVInstruction* BI = BM->addBinaryInst( + transBoolOpCode(Op0, OpCodeMap::map(LLVMOC)), + transType(B->getType()), Op0, transValue(B->getOperand(1), BB), BB); + return BI; +} + +SPIRVInstruction* +LLVMToSPIRV::transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB) { + auto Op0 = transValue(Cmp->getOperand(0), BB); + SPIRVInstruction* BI = BM->addCmpInst( + transBoolOpCode(Op0, CmpMap::map(Cmp->getPredicate())), + transType(Cmp->getType()), Op0, + transValue(Cmp->getOperand(1), BB), BB); + return BI; +} + +SPIRV::SPIRVInstruction *LLVMToSPIRV::transUnaryInst(UnaryInstruction *U, + SPIRVBasicBlock *BB) { + Op BOC = OpNop; + if (auto Cast = dyn_cast(U)) { + if (Cast->getDestTy()->getPointerAddressSpace() == SPIRAS_Generic) { + assert(Cast->getSrcTy()->getPointerAddressSpace() != SPIRAS_Constant && + "Casts from constant address space to generic are illegal"); + BOC = OpPtrCastToGeneric; + } else { + assert(Cast->getDestTy()->getPointerAddressSpace() != SPIRAS_Constant && + "Casts from generic address space to constant are illegal"); + assert(Cast->getSrcTy()->getPointerAddressSpace() == SPIRAS_Generic); + BOC = OpGenericCastToPtr; + } + } else { + auto OpCode = U->getOpcode(); + BOC = OpCodeMap::map(OpCode); + } + + auto Op = transValue(U->getOperand(0), BB); + return BM->addUnaryInst(transBoolOpCode(Op, BOC), + transType(U->getType()), Op, BB); +} + +/// An instruction may use an instruction from another BB which has not been +/// translated. SPIRVForward should be created as place holder for these +/// instructions and replaced later by the real instructions. +/// Use CreateForward = true to indicate such situation. +SPIRVValue * +LLVMToSPIRV::transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB, + bool CreateForward) { + if (auto LBB = dyn_cast(V)) { + auto BF = static_cast(getTranslatedValue(LBB->getParent())); + assert (BF && "Function not translated"); + BB = static_cast(mapValue(V, BM->addBasicBlock(BF))); + BM->setName(BB, LBB->getName()); + return BB; + } + + if (auto F = dyn_cast(V)) + return transFunctionDecl(F); + + if (auto GV = dyn_cast(V)) { + llvm::PointerType * Ty = GV->getType(); + // Though variables with common linkage type are initialized by 0, + // they can be represented in SPIR-V as uninitialized variables with + // 'Export' linkage type, just as tentative definitions look in C + llvm::Value *Init = GV->hasInitializer() && !GV->hasCommonLinkage() ? + GV->getInitializer() : nullptr; + StructType *ST = Init ? dyn_cast(Init->getType()) : nullptr; + if (ST && ST->hasName() && + ST->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler)) { + auto BV = transConstant(Init); + assert(BV); + return mapValue(V, BV); + } else if (ConstantExpr *ConstUE = dyn_cast_or_null(Init)) { + Instruction * Inst = ConstUE->getAsInstruction(); + if (isSamplerInitializer(Inst)) { + Init = Inst->getOperand(0); + Ty = static_cast(Init->getType()); + } + Inst->dropAllReferences(); + } + auto BVar = static_cast(BM->addVariable( + transType(Ty), GV->isConstant(), + transLinkageType(GV), + Init ? transValue(Init, nullptr) : nullptr, + GV->getName(), + SPIRSPIRVAddrSpaceMap::map( + static_cast(Ty->getAddressSpace())), + nullptr + )); + mapValue(V, BVar); + spv::BuiltIn Builtin = spv::BuiltInPosition; + if (!GV->hasName() || !getSPIRVBuiltin(GV->getName().str(), Builtin)) + return BVar; + BVar->setBuiltin(Builtin); + return BVar; + } + + if (isa(V)) { + auto BV = transConstant(V); + assert(BV); + return mapValue(V, BV); + } + + if (auto Arg = dyn_cast(V)) { + unsigned ArgNo = Arg->getArgNo(); + SPIRVFunction *BF = BB->getParent(); + //assert(BF->existArgument(ArgNo)); + return mapValue(V, BF->getArgument(ArgNo)); + } + + if (CreateForward) + return mapValue(V, BM->addForward(transType(V->getType()))); + + if (StoreInst *ST = dyn_cast(V)) { + std::vector MemoryAccess; + if (ST->isVolatile()) + MemoryAccess.push_back(MemoryAccessVolatileMask); + MemoryAccess.push_back(MemoryAccessAlignedMask); + MemoryAccess.push_back(ST->getAlignment()); + return mapValue(V, BM->addStoreInst( + transValue(ST->getPointerOperand(), BB), + transValue(ST->getValueOperand(), BB), + MemoryAccess, BB)); + } + + if (LoadInst *LD = dyn_cast(V)) { + std::vector MemoryAccess; + if (LD->isVolatile()) + MemoryAccess.push_back(MemoryAccessVolatileMask); + MemoryAccess.push_back(MemoryAccessAlignedMask); + MemoryAccess.push_back(LD->getAlignment()); + return mapValue(V, BM->addLoadInst( + transValue(LD->getPointerOperand(), BB), + MemoryAccess, BB)); + } + + if (BinaryOperator *B = dyn_cast(V)) { + SPIRVInstruction* BI = transBinaryInst(B, BB); + return mapValue(V, BI); + } + + if (auto RI = dyn_cast(V)) { + if (auto RV = RI->getReturnValue()) + return mapValue(V, BM->addReturnValueInst( + transValue(RV, BB), BB)); + return mapValue(V, BM->addReturnInst(BB)); + } + + if (CmpInst *Cmp = dyn_cast(V)) { + SPIRVInstruction* BI = transCmpInst(Cmp, BB); + return mapValue(V, BI); + } + + if (SelectInst *Sel = dyn_cast(V)) + return mapValue(V, BM->addSelectInst( + transValue(Sel->getCondition(), BB), + transValue(Sel->getTrueValue(), BB), + transValue(Sel->getFalseValue(), BB),BB)); + + if (AllocaInst *Alc = dyn_cast(V)) + return mapValue(V, BM->addVariable( + transType(Alc->getType()), false, + SPIRVLinkageTypeKind::LinkageTypeInternal, + nullptr, Alc->getName(), + StorageClassFunction, BB)); + + if (auto *Switch = dyn_cast(V)) { + std::vector> Pairs; + for (auto I = Switch->case_begin(), E = Switch->case_end(); I != E; ++I) + Pairs.push_back(std::make_pair(I.getCaseValue()->getZExtValue(), + static_cast(transValue(I.getCaseSuccessor(), + nullptr)))); + return mapValue(V, BM->addSwitchInst( + transValue(Switch->getCondition(), BB), + static_cast(transValue(Switch->getDefaultDest(), + nullptr)), Pairs, BB)); + } + + if (auto Branch = dyn_cast(V)) { + if (Branch->isUnconditional()) + return mapValue(V, BM->addBranchInst( + static_cast(transValue(Branch->getSuccessor(0), BB)), + BB)); + return mapValue(V, BM->addBranchConditionalInst( + transValue(Branch->getCondition(), BB), + static_cast(transValue(Branch->getSuccessor(0), BB)), + static_cast(transValue(Branch->getSuccessor(1), BB)), + BB)); + } + + if (auto Phi = dyn_cast(V)) { + std::vector IncomingPairs; + for (size_t I = 0, E = Phi->getNumIncomingValues(); I != E; ++I) { + IncomingPairs.push_back(transValue(Phi->getIncomingValue(I), BB)); + IncomingPairs.push_back(transValue(Phi->getIncomingBlock(I), nullptr)); + } + return mapValue(V, BM->addPhiInst(transType(Phi->getType()), IncomingPairs, + BB)); + } + + if (UnaryInstruction *U = dyn_cast(V)) { + if(isSamplerInitializer(U)) + return mapValue(V, transValue(U->getOperand(0), BB)); + return mapValue(V, transUnaryInst(U, BB)); + } + + if (GetElementPtrInst *GEP = dyn_cast(V)) { + std::vector Indices; + for (unsigned i = 0, e = GEP->getNumIndices(); i != e; ++i) + Indices.push_back(transValue(GEP->getOperand(i+1), BB)); + return mapValue(V, BM->addPtrAccessChainInst( + transType(GEP->getType()), + transValue(GEP->getPointerOperand(), BB), + Indices, BB, GEP->isInBounds())); + } + + if (auto Ext = dyn_cast(V)) { + auto Index = Ext->getIndexOperand(); + if (auto Const = dyn_cast(Index)) + return mapValue(V, BM->addCompositeExtractInst( + transType(Ext->getType()), + transValue(Ext->getVectorOperand(), BB), + std::vector(1, Const->getZExtValue()), + BB)); + else + return mapValue(V, BM->addVectorExtractDynamicInst( + transValue(Ext->getVectorOperand(), BB), + transValue(Index, BB), + BB)); + } + + if (auto Ins = dyn_cast(V)) { + auto Index = Ins->getOperand(2); + if (auto Const = dyn_cast(Index)) + return mapValue(V, BM->addCompositeInsertInst( + transValue(Ins->getOperand(1), BB), + transValue(Ins->getOperand(0), BB), + std::vector(1, Const->getZExtValue()), + BB)); + else + return mapValue(V, BM->addVectorInsertDynamicInst( + transValue(Ins->getOperand(0), BB), + transValue(Ins->getOperand(1), BB), + transValue(Index, BB), + BB)); + } + + if (auto SF = dyn_cast(V)) { + std::vector Comp; + for (auto &I:SF->getShuffleMask()) + Comp.push_back(I); + return mapValue(V, BM->addVectorShuffleInst( + transType(SF->getType()), + transValue(SF->getOperand(0), BB), + transValue(SF->getOperand(1), BB), + Comp, + BB)); + } + + if (CallInst *CI = dyn_cast(V)) + return mapValue(V, transCallInst(CI, BB)); + + llvm_unreachable("Not implemented"); + return nullptr; +} + +bool +LLVMToSPIRV::transDecoration(Value *V, SPIRVValue *BV) { + if (!transAlign(V, BV)) + return false; + if ((isa(V) && + cast(V)->isVolatile()) || + (isa(V) && cast(V)->isVolatile())) + BV->setVolatile(true); + DbgTran.transDbgInfo(V, BV); + return true; +} + +bool +LLVMToSPIRV::transAlign(Value *V, SPIRVValue *BV) { + if (auto AL = dyn_cast(V)) { + BM->setAlignment(BV, AL->getAlignment()); + return true; + } + if (auto GV = dyn_cast(V)) { + BM->setAlignment(BV, GV->getAlignment()); + return true; + } + return true; +} + +/// Do this after source language is set. +bool +LLVMToSPIRV::transBuiltinSet() { + SPIRVWord Ver = 0; + SourceLanguage Kind = BM->getSourceLanguage(&Ver); + assert((Kind == SourceLanguageOpenCL_C || + Kind == SourceLanguageOpenCL_CPP ) && "not supported"); + std::stringstream SS; + SS << "OpenCL.std"; + return BM->importBuiltinSet(SS.str(), &ExtSetId); +} + +/// Transform sampler* spcv.cast(i32 arg) +/// Only two cases are possible: +/// arg = ConstantInt x -> SPIRVConstantSampler +/// arg = i32 argument -> transValue(arg) +/// arg = load from sampler -> look through load +SPIRVValue * +LLVMToSPIRV::oclTransSpvcCastSampler(CallInst* CI, SPIRVBasicBlock *BB) { + llvm::Function* F = CI->getCalledFunction(); + auto FT = F->getFunctionType(); + auto RT = FT->getReturnType(); + assert(FT->getNumParams() == 1); + assert(isSPIRVType(RT, kSPIRVTypeName::Sampler) && + FT->getParamType(0)->isIntegerTy() && "Invalid sampler type"); + auto Arg = CI->getArgOperand(0); + + auto GetSamplerConstant = [&](uint64_t SamplerValue) { + auto AddrMode = (SamplerValue & 0xE) >> 1; + auto Param = SamplerValue & 0x1; + auto Filter = ((SamplerValue & 0x30) >> 4) - 1; + auto BV = BM->addSamplerConstant(transType(RT), AddrMode, Param, Filter); + return BV; + }; + + if (auto Const = dyn_cast(Arg)) { + // Sampler is declared as a kernel scope constant + return GetSamplerConstant(Const->getZExtValue()); + } else if (auto Load = dyn_cast(Arg)) { + // If value of the sampler is loaded from a global constant, use its + // initializer for initialization of the sampler. + auto Op = Load->getPointerOperand(); + assert(isa(Op) && "Unknown sampler pattern!"); + auto GV = cast(Op); + assert(GV->isConstant() || + GV->getType()->getPointerAddressSpace() == SPIRAS_Constant); + auto Initializer = GV->getInitializer(); + assert(isa(Initializer) && "sampler not constant int?"); + return GetSamplerConstant(cast(Initializer)->getZExtValue()); + } + // Sampler is a function argument + auto BV = transValue(Arg, BB); + assert(BV && BV->getType() == transType(RT)); + return BV; +} + +SPIRVValue * +LLVMToSPIRV::transSpcvCast(CallInst* CI, SPIRVBasicBlock *BB) { + return oclTransSpvcCastSampler(CI, BB); +} + +SPIRVValue * +LLVMToSPIRV::transCallInst(CallInst *CI, SPIRVBasicBlock *BB) { + SPIRVExtInstSetKind ExtSetKind = SPIRVEIS_Count; + SPIRVWord ExtOp = SPIRVWORD_MAX; + llvm::Function* F = CI->getCalledFunction(); + auto MangledName = F->getName(); + std::string DemangledName; + + if (MangledName.startswith(SPCV_CAST)) + return transSpcvCast(CI, BB); + + if (MangledName.startswith("llvm.memcpy")) { + std::vector MemoryAccess; + + if (isa(CI->getOperand(4)) && + dyn_cast(CI->getOperand(4)) + ->getZExtValue() == 1) + MemoryAccess.push_back(MemoryAccessVolatileMask); + if (isa(CI->getOperand(3))) { + MemoryAccess.push_back(MemoryAccessAlignedMask); + MemoryAccess.push_back(dyn_cast(CI->getOperand(3)) + ->getZExtValue()); + } + + return BM->addCopyMemorySizedInst( + transValue(CI->getOperand(0), BB), + transValue(CI->getOperand(1), BB), + transValue(CI->getOperand(2), BB), + MemoryAccess, + BB); + } + + if (oclIsBuiltin(MangledName, &DemangledName) || + isDecoratedSPIRVFunc(F, &DemangledName)) + if (auto BV = transBuiltinToInst(DemangledName, MangledName, CI, BB)) + return BV; + + SmallVector Dec; + if (isBuiltinTransToExtInst(CI->getCalledFunction(), &ExtSetKind, + &ExtOp, &Dec)) + return addDecorations(BM->addExtInst( + transType(CI->getType()), + ExtSetId, + ExtOp, + transArguments(CI, BB, SPIRVEntry::create_unique(ExtSetKind, ExtOp).get()), + BB), Dec); + + return BM->addCallInst( + transFunctionDecl(CI->getCalledFunction()), + transArguments(CI, BB, SPIRVEntry::create_unique(OpFunctionCall).get()), + BB); +} + +bool +LLVMToSPIRV::transAddressingMode() { + Triple TargetTriple(M->getTargetTriple()); + Triple::ArchType Arch = TargetTriple.getArch(); + + SPIRVCKRT(Arch == Triple::spir || Arch == Triple::spir64, + InvalidTargetTriple, + "Actual target triple is " + M->getTargetTriple()); + + if (Arch == Triple::spir) + BM->setAddressingModel(AddressingModelPhysical32); + else + BM->setAddressingModel(AddressingModelPhysical64); + // Physical addressing model requires Addresses capability + BM->addCapability(CapabilityAddresses); + return true; +} +std::vector +LLVMToSPIRV::transValue(const std::vector &Args, SPIRVBasicBlock* BB) { + std::vector BArgs; + for (auto &I: Args) + BArgs.push_back(transValue(I, BB)); + return BArgs; +} + +std::vector +LLVMToSPIRV::transArguments(CallInst *CI, SPIRVBasicBlock *BB) { + return transValue(getArguments(CI), BB); +} + +std::vector +LLVMToSPIRV::transValue(const std::vector &Args, SPIRVBasicBlock* BB, + SPIRVEntry *Entry) { + std::vector Operands; + for (size_t I = 0, E = Args.size(); I != E; ++I) { + Operands.push_back(Entry->isOperandLiteral(I) ? + cast(Args[I])->getZExtValue() : + transValue(Args[I], BB)->getId()); + } + return Operands; +} + +std::vector +LLVMToSPIRV::transArguments(CallInst *CI, SPIRVBasicBlock *BB, SPIRVEntry *Entry) { + return transValue(getArguments(CI), BB, Entry); +} + +SPIRVWord +LLVMToSPIRV::transFunctionControlMask(CallInst *CI) { + SPIRVWord FCM = 0; + SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, + SPIRVFunctionControlMaskKind Mask){ + if (CI->hasFnAttr(Attr)) + FCM |= Mask; + }); + return FCM; +} + +SPIRVWord +LLVMToSPIRV::transFunctionControlMask(Function *F) { + SPIRVWord FCM = 0; + SPIRSPIRVFuncCtlMaskMap::foreach([&](Attribute::AttrKind Attr, + SPIRVFunctionControlMaskKind Mask){ + if (F->hasFnAttribute(Attr)) + FCM |= Mask; + }); + return FCM; +} + +bool +LLVMToSPIRV::transGlobalVariables() { + for (auto I = M->global_begin(), + E = M->global_end(); I != E; ++I) { + if (!transValue(&*I, nullptr)) + return false; + } + return true; +} + +void +LLVMToSPIRV::mutateFuncArgType(const std::map& ChangedType, + Function* F) { + for (auto &I : ChangedType) { + for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE; ++UI) { + auto Call = dyn_cast(*UI); + if (!Call) + continue; + auto Arg = Call->getArgOperand(I.first); + auto OrigTy = Arg->getType(); + if (OrigTy == I.second) + continue; + SPIRVDBG(dbgs() << "[mutate arg type] " << *Call << ", " << *Arg << '\n'); + auto CastF = M->getOrInsertFunction(SPCV_CAST, I.second, OrigTy, nullptr); + std::vector Args; + Args.push_back(Arg); + auto Cast = CallInst::Create(CastF, Args, "", Call); + Call->replaceUsesOfWith(Arg, Cast); + SPIRVDBG(dbgs() << "[mutate arg type] -> " << *Cast << '\n'); + } + } +} + +void +LLVMToSPIRV::transFunction(Function *I) { + transFunctionDecl(I); + // Creating all basic blocks before creating any instruction. + for (Function::iterator FI = I->begin(), FE = I->end(); FI != FE; ++FI) { + transValue(&*FI, nullptr); + } + for (Function::iterator FI = I->begin(), FE = I->end(); FI != FE; ++FI) { + SPIRVBasicBlock* BB = static_cast(transValue(&*FI, nullptr)); + for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE; + ++BI) { + transValue(&*BI, BB, false); + } + } +} + +bool +LLVMToSPIRV::translate() { + BM->setGeneratorVer(kTranslatorVer); + + if (!transSourceLanguage()) + return false; + if (!transExtension()) + return false; + if (!transBuiltinSet()) + return false; + if (!transAddressingMode()) + return false; + if (!transGlobalVariables()) + return false; + + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) { + Function *F = &*I; + auto FT = F->getFunctionType(); + std::map ChangedType; + oclGetMutatedArgumentTypesByBuiltin(FT, ChangedType, F); + mutateFuncArgType(ChangedType, F); + } + + // SPIR-V logical layout requires all function declarations go before + // function definitions. + std::vector Decls, Defs; + for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I) { + if (isBuiltinTransToInst(&*I) || isBuiltinTransToExtInst(&*I) + || I->getName().startswith(SPCV_CAST) || + I->getName().startswith(LLVM_MEMCPY)) + continue; + if (I->isDeclaration()) + Decls.push_back(&*I); + else + Defs.push_back(&*I); + } + for (auto I:Decls) + transFunctionDecl(&*I); + for (auto I:Defs) + transFunction(&*I); + + if (!transOCLKernelMetadata()) + return false; + if (!transExecutionMode()) + return false; + + BM->optimizeDecorates(); + BM->resolveUnknownStructFields(); + BM->createForwardPointers(); + return true; +} + +llvm::IntegerType* LLVMToSPIRV::getSizetType() { + return IntegerType::getIntNTy(M->getContext(), + M->getDataLayout().getPointerSizeInBits()); +} + +void +LLVMToSPIRV::oclGetMutatedArgumentTypesByBuiltin( + llvm::FunctionType* FT, std::map& ChangedType, + Function* F) { + auto Name = F->getName(); + std::string Demangled; + if (!oclIsBuiltin(Name, &Demangled)) + return; + if (Demangled.find(kSPIRVName::SampledImage) == std::string::npos) + return; + if (FT->getParamType(1)->isIntegerTy()) + ChangedType[1] = getSamplerType(F->getParent()); +} + +SPIRVInstruction * +LLVMToSPIRV::transBuiltinToInst(const std::string& DemangledName, + const std::string &MangledName, CallInst* CI, SPIRVBasicBlock* BB) { + SmallVector Dec; + auto OC = getSPIRVFuncOC(DemangledName, &Dec); + + if (OC == OpNop) + return nullptr; + + auto Inst = transBuiltinToInstWithoutDecoration(OC, CI, BB); + addDecorations(Inst, Dec); + return Inst; +} + +bool +LLVMToSPIRV::transExecutionMode() { + if (auto NMD = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::ExecutionMode)) { + while (!NMD.atEnd()) { + unsigned EMode = ~0U; + unsigned EModel = ~0U; + Function *F = nullptr; + auto N = NMD.nextOp(); /* execution mode MDNode */ + N.nextOp() /* entry point MDNode */ + .get(EModel) + .get(F) + .done() + .get(EMode); + assert (EModel == spv::ExecutionModelKernel && + "Unsupported execution model"); + SPIRVFunction *BF = static_cast(getTranslatedValue(F)); + assert(BF && "Invalid kernel function"); + switch(EMode) { + case spv::ExecutionModeContractionOff: + BF->addExecutionMode(new SPIRVExecutionMode(BF, + ExecutionModeContractionOff)); + break; + case spv::ExecutionModeLocalSize: { + unsigned X, Y, Z; + N.get(X).get(Y).get(Z); + BF->addExecutionMode(new SPIRVExecutionMode(BF, + ExecutionModeLocalSize, X, Y, Z)); + } + break; + case spv::ExecutionModeLocalSizeHint: { + unsigned X, Y, Z; + N.get(X).get(Y).get(Z); + BF->addExecutionMode(new SPIRVExecutionMode(BF, + ExecutionModeLocalSizeHint, X, Y, Z)); + } + break; + case spv::ExecutionModeVecTypeHint: { + unsigned X; + N.get(X); + BF->addExecutionMode(new SPIRVExecutionMode(BF, + ExecutionModeVecTypeHint, X)); + } + break; + default: + llvm_unreachable("invalid execution mode"); + } + } + } + return true; +} + +bool +LLVMToSPIRV::transOCLKernelMetadata() { + NamedMDNode *KernelMDs = M->getNamedMetadata(SPIR_MD_KERNELS); + std::vector argAccessQual; + if (!KernelMDs) + return true; + + for (unsigned I = 0, E = KernelMDs->getNumOperands(); I < E; ++I) { + MDNode *KernelMD = KernelMDs->getOperand(I); + if (KernelMD->getNumOperands() == 0) + continue; + Function *Kernel = mdconst::dyn_extract(KernelMD->getOperand(0)); + + SPIRVFunction *BF = static_cast(getTranslatedValue(Kernel)); + assert(BF && "Kernel function should be translated first"); + assert(Kernel && oclIsKernel(Kernel) + && "Invalid kernel calling convention or metadata"); + for (unsigned MI = 1, ME = KernelMD->getNumOperands(); MI < ME; ++MI) { + MDNode *MD = dyn_cast(KernelMD->getOperand(MI)); + if (!MD) + continue; + MDString *NameMD = dyn_cast(MD->getOperand(0)); + if (!NameMD) + continue; + StringRef Name = NameMD->getString(); + if (Name == SPIR_MD_KERNEL_ARG_TYPE_QUAL) { + foreachKernelArgMD(MD, BF, + [](const std::string &Str, SPIRVFunctionParameter *BA){ + if (Str.find("volatile") != std::string::npos) + BA->addDecorate(new SPIRVDecorate(DecorationVolatile, BA)); + if (Str.find("restrict") != std::string::npos) + BA->addDecorate(new SPIRVDecorate(DecorationFuncParamAttr, + BA, FunctionParameterAttributeNoAlias)); + if (Str.find("const") != std::string::npos) + BA->addDecorate(new SPIRVDecorate(DecorationFuncParamAttr, + BA, FunctionParameterAttributeNoWrite)); + }); + } else if (Name == SPIR_MD_KERNEL_ARG_NAME) { + foreachKernelArgMD(MD, BF, + [=](const std::string &Str, SPIRVFunctionParameter *BA){ + BM->setName(BA, Str); + }); + } + } + } + return true; +} + +bool +LLVMToSPIRV::transSourceLanguage() { + auto Src = getSPIRVSource(M); + SrcLang = std::get<0>(Src); + SrcLangVer = std::get<1>(Src); + BM->setSourceLanguage(static_cast(SrcLang), SrcLangVer); + return true; +} + +bool +LLVMToSPIRV::transExtension() { + if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::Extension)) { + while (!N.atEnd()) { + std::string S; + N.nextOp().get(S); + assert(!S.empty() && "Invalid extension"); + BM->getExtension().insert(S); + } + } + if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::SourceExtension)) { + while (!N.atEnd()) { + std::string S; + N.nextOp().get(S); + assert(!S.empty() && "Invalid extension"); + BM->getSourceExtension().insert(S); + } + } + for (auto &I:map(rmap(BM->getExtension()))) + BM->addCapability(I); + + return true; +} + +void +LLVMToSPIRV::dumpUsers(Value* V) { + SPIRVDBG(dbgs() << "Users of " << *V << " :\n"); + for (auto UI = V->user_begin(), UE = V->user_end(); + UI != UE; ++UI) + SPIRVDBG(dbgs() << " " << **UI << '\n'); +} + +Op +LLVMToSPIRV::transBoolOpCode(SPIRVValue* Opn, Op OC) { + if (!Opn->getType()->isTypeVectorOrScalarBool()) + return OC; + IntBoolOpMap::find(OC, &OC); + return OC; +} + +SPIRVInstruction * +LLVMToSPIRV::transBuiltinToInstWithoutDecoration(Op OC, + CallInst* CI, SPIRVBasicBlock* BB) { + if (isGroupOpCode(OC)) + BM->addCapability(CapabilityGroups); + switch (OC) { + case OpControlBarrier: { + auto BArgs = transValue(getArguments(CI), BB); + return BM->addControlBarrierInst( + BArgs[0], BArgs[1], BArgs[2], BB); + } + break; + case OpGroupAsyncCopy: { + auto BArgs = transValue(getArguments(CI), BB); + return BM->addAsyncGroupCopy(BArgs[0], BArgs[1], BArgs[2], BArgs[3], + BArgs[4], BArgs[5], BB); + } + break; + default: { + if (isCvtOpCode(OC) && OC != OpGenericCastToPtrExplicit) { + return BM->addUnaryInst(OC, transType(CI->getType()), + transValue(CI->getArgOperand(0), BB), BB); + } else if (isCmpOpCode(OC)) { + assert(CI && CI->getNumArgOperands() == 2 && "Invalid call inst"); + auto ResultTy = CI->getType(); + Type *BoolTy = IntegerType::getInt1Ty(M->getContext()); + auto IsVector = ResultTy->isVectorTy(); + if (IsVector) + BoolTy = VectorType::get(BoolTy, ResultTy->getVectorNumElements()); + auto BBT = transType(BoolTy); + auto Cmp = BM->addCmpInst(OC, BBT, + transValue(CI->getArgOperand(0), BB), + transValue(CI->getArgOperand(1), BB), BB); + auto Zero = transValue(Constant::getNullValue(ResultTy), BB); + auto One = transValue( + IsVector ? Constant::getAllOnesValue(ResultTy) : getInt32(M, 1), BB); + return BM->addSelectInst(Cmp, One, Zero, BB); + } else if (isBinaryOpCode(OC)) { + assert(CI && CI->getNumArgOperands() == 2 && "Invalid call inst"); + return BM->addBinaryInst(OC, transType(CI->getType()), + transValue(CI->getArgOperand(0), BB), + transValue(CI->getArgOperand(1), BB), BB); + } else if (CI->getNumArgOperands() == 1 && + !CI->getType()->isVoidTy() && + !hasExecScope(OC) && + !isAtomicOpCode(OC)) { + return BM->addUnaryInst(OC, transType(CI->getType()), + transValue(CI->getArgOperand(0), BB), BB); + } else { + auto Args = getArguments(CI); + SPIRVType *SPRetTy = nullptr; + Type *RetTy = CI->getType(); + auto F = CI->getCalledFunction(); + if (!RetTy->isVoidTy()) { + SPRetTy = transType(RetTy); + } else if (Args.size() > 0 && F->arg_begin()->hasStructRetAttr()) { + SPRetTy = transType(F->arg_begin()->getType()->getPointerElementType()); + Args.erase(Args.begin()); + } + auto SPI = BM->addInstTemplate(OC, BB, SPRetTy); + std::vector SPArgs; + for (size_t I = 0, E = Args.size(); I != E; ++I) { + assert((!isFunctionPointerType(Args[I]->getType()) || + isa(Args[I])) && + "Invalid function pointer argument"); + SPArgs.push_back(SPI->isOperandLiteral(I) ? + cast(Args[I])->getZExtValue() : + transValue(Args[I], BB)->getId()); + } + SPI->setOpWordsAndValidate(SPArgs); + if (!SPRetTy || !SPRetTy->isTypeStruct()) + return SPI; + std::vector Mem; + SPIRVDBG(spvdbgs() << *SPI << '\n'); + return BM->addStoreInst(transValue(CI->getArgOperand(0), BB), SPI, + Mem, BB); + } + } + } + return nullptr; +} + + +SPIRVId +LLVMToSPIRV::addInt32(int I) { + return transValue(getInt32(M, I), nullptr, false)->getId(); +} + +SPIRV::SPIRVLinkageTypeKind +LLVMToSPIRV::transLinkageType(const GlobalValue* GV) { + if(GV->isDeclarationForLinker()) + return SPIRVLinkageTypeKind::LinkageTypeImport; + if(GV->hasInternalLinkage() || GV->hasPrivateLinkage()) + return SPIRVLinkageTypeKind::LinkageTypeInternal; + return SPIRVLinkageTypeKind::LinkageTypeExport; +} +} // end of SPIRV namespace + +char LLVMToSPIRV::ID = 0; + +INITIALIZE_PASS_BEGIN(LLVMToSPIRV, "llvmtospv", "Translate LLVM to SPIR-V", + false, false) +INITIALIZE_PASS_DEPENDENCY(OCLTypeToSPIRV) +INITIALIZE_PASS_END(LLVMToSPIRV, "llvmtospv", "Translate LLVM to SPIR-V", + false, false) + +ModulePass *llvm::createLLVMToSPIRV(SPIRVModule *SMod) { + return new LLVMToSPIRV(SMod); +} + +void +addPassesForSPIRV(legacy::PassManager &PassMgr) { + if (SPIRVMemToReg) + PassMgr.add(createPromoteMemoryToRegisterPass()); + PassMgr.add(createTransOCLMD()); + PassMgr.add(createOCL21ToSPIRV()); + PassMgr.add(createSPIRVLowerOCLBlocks()); + PassMgr.add(createOCLTypeToSPIRV()); + PassMgr.add(createOCL20ToSPIRV()); + PassMgr.add(createSPIRVRegularizeLLVM()); + PassMgr.add(createSPIRVLowerConstExpr()); + PassMgr.add(createSPIRVLowerBool()); +} + +bool +llvm::WriteSPIRV(Module *M, llvm::raw_ostream &OS, std::string &ErrMsg) { + std::unique_ptr BM(SPIRVModule::createSPIRVModule()); + legacy::PassManager PassMgr; + addPassesForSPIRV(PassMgr); + PassMgr.add(createLLVMToSPIRV(BM.get())); + PassMgr.run(*M); + + if (BM->getError(ErrMsg) != SPIRVEC_Success) + return false; + OS << *BM; + return true; +} + +bool +llvm::RegularizeLLVMForSPIRV(Module *M, std::string &ErrMsg) { + std::unique_ptr BM(SPIRVModule::createSPIRVModule()); + legacy::PassManager PassMgr; + addPassesForSPIRV(PassMgr); + PassMgr.run(*M); + return true; +} + diff --git a/SPIRVWriterPass.cpp b/SPIRVWriterPass.cpp index 071c26c..6042ff3 100644 --- a/SPIRVWriterPass.cpp +++ b/SPIRVWriterPass.cpp @@ -33,7 +33,7 @@ namespace { explicit WriteSPIRVPass(raw_ostream &o) : ModulePass(ID), OS(o) {} - const char *getPassName() const override { return "SPIRV Writer"; } + StringRef getPassName() const override { return "SPIRV Writer"; } bool runOnModule(Module &M) override { // FIXME: at the moment LLVM/SPIR-V translation errors are ignored. diff --git a/libSPIRV/SPIRVType.h b/libSPIRV/SPIRVType.h index 0f4ee02..c093805 100644 --- a/libSPIRV/SPIRVType.h +++ b/libSPIRV/SPIRVType.h @@ -301,7 +301,7 @@ class SPIRVTypeArray:public SPIRVType { SPIRVType *getElementType() const { return ElemType;} SPIRVConstant *getLength() const; SPIRVCapVec getRequiredCapability() const { - return std::move(getElementType()->getRequiredCapability()); + return getElementType()->getRequiredCapability(); } virtual std::vector getNonLiteralOperands() const { std::vector Operands(2, ElemType); diff --git a/libSPIRV/SPIRVValue.h b/libSPIRV/SPIRVValue.h index 5c9dcee..c87e41c 100644 --- a/libSPIRV/SPIRVValue.h +++ b/libSPIRV/SPIRVValue.h @@ -1,364 +1,364 @@ -//===- SPIRVValue.h - Class to represent a SPIR-V Value ----------*- C++ -*-===// -// -// The LLVM/SPIRV Translator -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. -// -//===----------------------------------------------------------------------===// -/// \file -/// -/// This file defines the values defined in SPIR-V spec with op codes. -/// -/// The name of the SPIR-V values follow the op code name in the spec. -/// This is for readability and ease of using macro to handle types. -// -//===----------------------------------------------------------------------===// - -#ifndef SPIRVVALUE_HPP_ -#define SPIRVVALUE_HPP_ - -#include "SPIRVEntry.h" -#include "SPIRVType.h" -#include "SPIRVDecorate.h" - -#include -#include -#include - -namespace SPIRV{ - -class SPIRVValue: public SPIRVEntry { -public: - // Complete constructor for value with id and type - SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, - SPIRVType *TheType, SPIRVId TheId) - :SPIRVEntry(M, TheWordCount, TheOpCode, TheId), Type(TheType) { - validate(); - } - // Complete constructor for value with type but without id - SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, - SPIRVType *TheType) - :SPIRVEntry(M, TheWordCount, TheOpCode), Type(TheType) { - setHasNoId(); - validate(); - } - // Complete constructor for value with id but without type - SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, - SPIRVId TheId) - :SPIRVEntry(M, TheWordCount, TheOpCode, TheId), Type(NULL) { - setHasNoType(); - validate(); - } - // Complete constructor for value without id and type - SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode) - :SPIRVEntry(M, TheWordCount, TheOpCode), Type(NULL) { - setHasNoId(); - setHasNoType(); - validate(); - } - // Incomplete constructor - SPIRVValue(Op TheOpCode):SPIRVEntry(TheOpCode), Type(NULL) {} - - bool hasType()const { return !(Attrib & SPIRVEA_NOTYPE);} - SPIRVType *getType()const { - assert(hasType() && "value has no type"); - return Type; - } - bool isVolatile()const; - bool hasAlignment(SPIRVWord *Result=0)const; - - void setAlignment(SPIRVWord); - void setVolatile(bool IsVolatile); - - void validate()const { - SPIRVEntry::validate(); - assert((!hasType() || Type) && "Invalid type"); - } - - void setType(SPIRVType *Ty) { - Type = Ty; - assert(!Ty || !Ty->isTypeVoid() || OpCode == OpFunction); - if (Ty && (!Ty->isTypeVoid() || OpCode == OpFunction)) - setHasType(); - else - setHasNoType(); - } - - SPIRVCapVec getRequiredCapability() const { - SPIRVCapVec CV; - if (!hasType()) - return std::move(CV); - return std::move(Type->getRequiredCapability()); - } - -protected: - void setHasNoType() { Attrib |= SPIRVEA_NOTYPE;} - void setHasType() { Attrib &= ~SPIRVEA_NOTYPE;} - - SPIRVType *Type; // Value Type -}; - -class SPIRVConstant: public SPIRVValue { -public: - // Complete constructor for integer constant - SPIRVConstant(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, - uint64_t TheValue) - :SPIRVValue(M, 0, OpConstant, TheType, TheId){ - Union.UInt64Val = TheValue; - recalculateWordCount(); - validate(); - } - // Complete constructor for float constant - SPIRVConstant(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, float TheValue) - :SPIRVValue(M, 0, OpConstant, TheType, TheId){ - Union.FloatVal = TheValue; - recalculateWordCount(); - validate(); - } - // Complete constructor for double constant - SPIRVConstant(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, double TheValue) - :SPIRVValue(M, 0, OpConstant, TheType, TheId){ - Union.DoubleVal = TheValue; - recalculateWordCount(); - validate(); - } - // Incomplete constructor - SPIRVConstant():SPIRVValue(OpConstant), NumWords(0){} - uint64_t getZExtIntValue() const { return Union.UInt64Val;} - float getFloatValue() const { return Union.FloatVal;} - double getDoubleValue() const { return Union.DoubleVal;} -protected: - void recalculateWordCount() { - NumWords = Type->getBitWidth()/32; - if (NumWords < 1) - NumWords = 1; - WordCount = 3 + NumWords; - } - void validate() const { - SPIRVValue::validate(); - assert(NumWords >= 1 && NumWords <= 2 && "Invalid constant size"); - } - void encode(spv_ostream &O) const { - getEncoder(O) << Type << Id; - for (unsigned i = 0; i < NumWords; ++i) - getEncoder(O) << Union.Words[i]; - } - void setWordCount(SPIRVWord WordCount) { - SPIRVValue::setWordCount(WordCount); - NumWords = WordCount - 3; - } - void decode(std::istream &I) { - getDecoder(I) >> Type >> Id; - for (unsigned i = 0; i < NumWords; ++i) - getDecoder(I) >> Union.Words[i]; - } - - unsigned NumWords; - union UnionType{ - uint64_t UInt64Val; - float FloatVal; - double DoubleVal; - SPIRVWord Words[2]; - UnionType() { - UInt64Val = 0; - } - } Union; -}; - -template -class SPIRVConstantEmpty: public SPIRVValue { -public: - // Complete constructor - SPIRVConstantEmpty(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) - :SPIRVValue(M, 3, OC, TheType, TheId){ - validate(); - } - // Incomplete constructor - SPIRVConstantEmpty():SPIRVValue(OC){} -protected: - void validate() const { - SPIRVValue::validate(); - } - _SPIRV_DEF_ENCDEC2(Type, Id) -}; - -template -class SPIRVConstantBool: public SPIRVConstantEmpty { -public: - // Complete constructor - SPIRVConstantBool(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) - :SPIRVConstantEmpty(M, TheType, TheId){} - // Incomplete constructor - SPIRVConstantBool(){} -protected: - void validate() const { - SPIRVConstantEmpty::validate(); - assert(this->Type->isTypeBool() && "Invalid type"); - } -}; - -typedef SPIRVConstantBool SPIRVConstantTrue; -typedef SPIRVConstantBool SPIRVConstantFalse; - -class SPIRVConstantNull: - public SPIRVConstantEmpty { -public: - // Complete constructor - SPIRVConstantNull(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) - :SPIRVConstantEmpty(M, TheType, TheId){ - validate(); - } - // Incomplete constructor - SPIRVConstantNull(){} -protected: - void validate() const { - SPIRVConstantEmpty::validate(); - assert((Type->isTypeComposite() || - Type->isTypeOpaque() || - Type->isTypeEvent() || - Type->isTypePointer() || - Type->isTypeReserveId() || - Type->isTypeDeviceEvent()) && - "Invalid type"); - } -}; - -class SPIRVUndef: - public SPIRVConstantEmpty { -public: - // Complete constructor - SPIRVUndef(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) - :SPIRVConstantEmpty(M, TheType, TheId){ - validate(); - } - // Incomplete constructor - SPIRVUndef(){} -protected: - void validate() const { - SPIRVConstantEmpty::validate(); - } -}; - -class SPIRVConstantComposite: public SPIRVValue { -public: - // Complete constructor for composite constant - SPIRVConstantComposite(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, - const std::vector TheElements) - :SPIRVValue(M, TheElements.size()+3, OpConstantComposite, TheType, - TheId){ - Elements = getIds(TheElements); - validate(); - } - // Incomplete constructor - SPIRVConstantComposite():SPIRVValue(OpConstantComposite){} - std::vector getElements()const { - return getValues(Elements); - } - std::vector getNonLiteralOperands() const { - std::vector Elements = getElements(); - return std::vector(Elements.begin(), Elements.end()); - } -protected: - void validate() const { - SPIRVValue::validate(); - for (auto &I:Elements) - getValue(I)->validate(); - } - void setWordCount(SPIRVWord WordCount) { - Elements.resize(WordCount - 3); - } - _SPIRV_DEF_ENCDEC3(Type, Id, Elements) - - std::vector Elements; -}; - -class SPIRVConstantSampler: public SPIRVValue { -public: - const static Op OC = OpConstantSampler; - const static SPIRVWord WC = 6; - // Complete constructor - SPIRVConstantSampler(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, - SPIRVWord TheAddrMode, SPIRVWord TheNormalized, SPIRVWord TheFilterMode) - :SPIRVValue(M, WC, OC, TheType, TheId), AddrMode(TheAddrMode), - Normalized(TheNormalized), FilterMode(TheFilterMode){ - validate(); - } - // Incomplete constructor - SPIRVConstantSampler():SPIRVValue(OC), AddrMode(SPIRVSAM_Invalid), - Normalized(SPIRVWORD_MAX), FilterMode(SPIRVSFM_Invalid){} - - SPIRVWord getAddrMode() const { - return AddrMode; - } - - SPIRVWord getFilterMode() const { - return FilterMode; - } - - SPIRVWord getNormalized() const { - return Normalized; - } - SPIRVCapVec getRequiredCapability() const { - return getVec(CapabilityLiteralSampler); - } -protected: - SPIRVWord AddrMode; - SPIRVWord Normalized; - SPIRVWord FilterMode; - void validate() const { - SPIRVValue::validate(); - assert(OpCode == OC); - assert(WordCount == WC); - assert(Type->isTypeSampler()); - } - _SPIRV_DEF_ENCDEC5(Type, Id, AddrMode, Normalized, FilterMode) -}; - -class SPIRVForward:public SPIRVValue, public SPIRVComponentExecutionModes { -public: - const static Op OC = OpForward; - // Complete constructor - SPIRVForward(SPIRVModule *TheModule, SPIRVType *TheTy, SPIRVId TheId): - SPIRVValue(TheModule, 0, OC, TheId){ - if (TheTy) - setType(TheTy); - } - SPIRVForward():SPIRVValue(OC) { - assert(0 && "should never be called"); - } - _SPIRV_DEF_ENCDEC1(Id) - friend class SPIRVFunction; -protected: - void validate() const {} -}; - -} - - -#endif /* SPIRVVALUE_HPP_ */ +//===- SPIRVValue.h - Class to represent a SPIR-V Value ----------*- C++ -*-===// +// +// The LLVM/SPIRV Translator +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// Copyright (c) 2014 Advanced Micro Devices, Inc. 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 Advanced Micro Devices, Inc., 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. +// +//===----------------------------------------------------------------------===// +/// \file +/// +/// This file defines the values defined in SPIR-V spec with op codes. +/// +/// The name of the SPIR-V values follow the op code name in the spec. +/// This is for readability and ease of using macro to handle types. +// +//===----------------------------------------------------------------------===// + +#ifndef SPIRVVALUE_HPP_ +#define SPIRVVALUE_HPP_ + +#include "SPIRVEntry.h" +#include "SPIRVType.h" +#include "SPIRVDecorate.h" + +#include +#include +#include + +namespace SPIRV{ + +class SPIRVValue: public SPIRVEntry { +public: + // Complete constructor for value with id and type + SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, + SPIRVType *TheType, SPIRVId TheId) + :SPIRVEntry(M, TheWordCount, TheOpCode, TheId), Type(TheType) { + validate(); + } + // Complete constructor for value with type but without id + SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, + SPIRVType *TheType) + :SPIRVEntry(M, TheWordCount, TheOpCode), Type(TheType) { + setHasNoId(); + validate(); + } + // Complete constructor for value with id but without type + SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode, + SPIRVId TheId) + :SPIRVEntry(M, TheWordCount, TheOpCode, TheId), Type(NULL) { + setHasNoType(); + validate(); + } + // Complete constructor for value without id and type + SPIRVValue(SPIRVModule *M, unsigned TheWordCount, Op TheOpCode) + :SPIRVEntry(M, TheWordCount, TheOpCode), Type(NULL) { + setHasNoId(); + setHasNoType(); + validate(); + } + // Incomplete constructor + SPIRVValue(Op TheOpCode):SPIRVEntry(TheOpCode), Type(NULL) {} + + bool hasType()const { return !(Attrib & SPIRVEA_NOTYPE);} + SPIRVType *getType()const { + assert(hasType() && "value has no type"); + return Type; + } + bool isVolatile()const; + bool hasAlignment(SPIRVWord *Result=0)const; + + void setAlignment(SPIRVWord); + void setVolatile(bool IsVolatile); + + void validate()const { + SPIRVEntry::validate(); + assert((!hasType() || Type) && "Invalid type"); + } + + void setType(SPIRVType *Ty) { + Type = Ty; + assert(!Ty || !Ty->isTypeVoid() || OpCode == OpFunction); + if (Ty && (!Ty->isTypeVoid() || OpCode == OpFunction)) + setHasType(); + else + setHasNoType(); + } + + SPIRVCapVec getRequiredCapability() const { + SPIRVCapVec CV; + if (!hasType()) + return CV; + return Type->getRequiredCapability(); + } + +protected: + void setHasNoType() { Attrib |= SPIRVEA_NOTYPE;} + void setHasType() { Attrib &= ~SPIRVEA_NOTYPE;} + + SPIRVType *Type; // Value Type +}; + +class SPIRVConstant: public SPIRVValue { +public: + // Complete constructor for integer constant + SPIRVConstant(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, + uint64_t TheValue) + :SPIRVValue(M, 0, OpConstant, TheType, TheId){ + Union.UInt64Val = TheValue; + recalculateWordCount(); + validate(); + } + // Complete constructor for float constant + SPIRVConstant(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, float TheValue) + :SPIRVValue(M, 0, OpConstant, TheType, TheId){ + Union.FloatVal = TheValue; + recalculateWordCount(); + validate(); + } + // Complete constructor for double constant + SPIRVConstant(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, double TheValue) + :SPIRVValue(M, 0, OpConstant, TheType, TheId){ + Union.DoubleVal = TheValue; + recalculateWordCount(); + validate(); + } + // Incomplete constructor + SPIRVConstant():SPIRVValue(OpConstant), NumWords(0){} + uint64_t getZExtIntValue() const { return Union.UInt64Val;} + float getFloatValue() const { return Union.FloatVal;} + double getDoubleValue() const { return Union.DoubleVal;} +protected: + void recalculateWordCount() { + NumWords = Type->getBitWidth()/32; + if (NumWords < 1) + NumWords = 1; + WordCount = 3 + NumWords; + } + void validate() const { + SPIRVValue::validate(); + assert(NumWords >= 1 && NumWords <= 2 && "Invalid constant size"); + } + void encode(spv_ostream &O) const { + getEncoder(O) << Type << Id; + for (unsigned i = 0; i < NumWords; ++i) + getEncoder(O) << Union.Words[i]; + } + void setWordCount(SPIRVWord WordCount) { + SPIRVValue::setWordCount(WordCount); + NumWords = WordCount - 3; + } + void decode(std::istream &I) { + getDecoder(I) >> Type >> Id; + for (unsigned i = 0; i < NumWords; ++i) + getDecoder(I) >> Union.Words[i]; + } + + unsigned NumWords; + union UnionType{ + uint64_t UInt64Val; + float FloatVal; + double DoubleVal; + SPIRVWord Words[2]; + UnionType() { + UInt64Val = 0; + } + } Union; +}; + +template +class SPIRVConstantEmpty: public SPIRVValue { +public: + // Complete constructor + SPIRVConstantEmpty(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) + :SPIRVValue(M, 3, OC, TheType, TheId){ + validate(); + } + // Incomplete constructor + SPIRVConstantEmpty():SPIRVValue(OC){} +protected: + void validate() const { + SPIRVValue::validate(); + } + _SPIRV_DEF_ENCDEC2(Type, Id) +}; + +template +class SPIRVConstantBool: public SPIRVConstantEmpty { +public: + // Complete constructor + SPIRVConstantBool(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) + :SPIRVConstantEmpty(M, TheType, TheId){} + // Incomplete constructor + SPIRVConstantBool(){} +protected: + void validate() const { + SPIRVConstantEmpty::validate(); + assert(this->Type->isTypeBool() && "Invalid type"); + } +}; + +typedef SPIRVConstantBool SPIRVConstantTrue; +typedef SPIRVConstantBool SPIRVConstantFalse; + +class SPIRVConstantNull: + public SPIRVConstantEmpty { +public: + // Complete constructor + SPIRVConstantNull(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) + :SPIRVConstantEmpty(M, TheType, TheId){ + validate(); + } + // Incomplete constructor + SPIRVConstantNull(){} +protected: + void validate() const { + SPIRVConstantEmpty::validate(); + assert((Type->isTypeComposite() || + Type->isTypeOpaque() || + Type->isTypeEvent() || + Type->isTypePointer() || + Type->isTypeReserveId() || + Type->isTypeDeviceEvent()) && + "Invalid type"); + } +}; + +class SPIRVUndef: + public SPIRVConstantEmpty { +public: + // Complete constructor + SPIRVUndef(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId) + :SPIRVConstantEmpty(M, TheType, TheId){ + validate(); + } + // Incomplete constructor + SPIRVUndef(){} +protected: + void validate() const { + SPIRVConstantEmpty::validate(); + } +}; + +class SPIRVConstantComposite: public SPIRVValue { +public: + // Complete constructor for composite constant + SPIRVConstantComposite(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, + const std::vector TheElements) + :SPIRVValue(M, TheElements.size()+3, OpConstantComposite, TheType, + TheId){ + Elements = getIds(TheElements); + validate(); + } + // Incomplete constructor + SPIRVConstantComposite():SPIRVValue(OpConstantComposite){} + std::vector getElements()const { + return getValues(Elements); + } + std::vector getNonLiteralOperands() const { + std::vector Elements = getElements(); + return std::vector(Elements.begin(), Elements.end()); + } +protected: + void validate() const { + SPIRVValue::validate(); + for (auto &I:Elements) + getValue(I)->validate(); + } + void setWordCount(SPIRVWord WordCount) { + Elements.resize(WordCount - 3); + } + _SPIRV_DEF_ENCDEC3(Type, Id, Elements) + + std::vector Elements; +}; + +class SPIRVConstantSampler: public SPIRVValue { +public: + const static Op OC = OpConstantSampler; + const static SPIRVWord WC = 6; + // Complete constructor + SPIRVConstantSampler(SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId, + SPIRVWord TheAddrMode, SPIRVWord TheNormalized, SPIRVWord TheFilterMode) + :SPIRVValue(M, WC, OC, TheType, TheId), AddrMode(TheAddrMode), + Normalized(TheNormalized), FilterMode(TheFilterMode){ + validate(); + } + // Incomplete constructor + SPIRVConstantSampler():SPIRVValue(OC), AddrMode(SPIRVSAM_Invalid), + Normalized(SPIRVWORD_MAX), FilterMode(SPIRVSFM_Invalid){} + + SPIRVWord getAddrMode() const { + return AddrMode; + } + + SPIRVWord getFilterMode() const { + return FilterMode; + } + + SPIRVWord getNormalized() const { + return Normalized; + } + SPIRVCapVec getRequiredCapability() const { + return getVec(CapabilityLiteralSampler); + } +protected: + SPIRVWord AddrMode; + SPIRVWord Normalized; + SPIRVWord FilterMode; + void validate() const { + SPIRVValue::validate(); + assert(OpCode == OC); + assert(WordCount == WC); + assert(Type->isTypeSampler()); + } + _SPIRV_DEF_ENCDEC5(Type, Id, AddrMode, Normalized, FilterMode) +}; + +class SPIRVForward:public SPIRVValue, public SPIRVComponentExecutionModes { +public: + const static Op OC = OpForward; + // Complete constructor + SPIRVForward(SPIRVModule *TheModule, SPIRVType *TheTy, SPIRVId TheId): + SPIRVValue(TheModule, 0, OC, TheId){ + if (TheTy) + setType(TheTy); + } + SPIRVForward():SPIRVValue(OC) { + assert(0 && "should never be called"); + } + _SPIRV_DEF_ENCDEC1(Id) + friend class SPIRVFunction; +protected: + void validate() const {} +}; + +} + + +#endif /* SPIRVVALUE_HPP_ */