1738 lines
58 KiB
C++
1738 lines
58 KiB
C++
//===- 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/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 <iostream>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <functional>
|
|
#include <cstdlib>
|
|
|
|
#define DEBUG_TYPE "spirv"
|
|
|
|
using namespace llvm;
|
|
using namespace SPIRV;
|
|
using namespace OCLUtil;
|
|
|
|
namespace llvm {
|
|
FunctionPass *createPromoteMemoryToRegisterPass();
|
|
}
|
|
|
|
namespace SPIRV{
|
|
|
|
cl::opt<bool> SPIRVMemToReg("spirv-mem2reg", cl::init(true),
|
|
cl::desc("LLVM/SPIR-V translation enable mem2reg"));
|
|
|
|
|
|
static void
|
|
foreachKernelArgMD(MDNode *MD, SPIRVFunction *BF,
|
|
std::function<void(const std::string& Str,
|
|
SPIRVFunctionParameter *BA)>Func) {
|
|
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<void(std::vector<SPIRVWord>&)> PostProc;
|
|
OCLBuiltinSPIRVTransInfo(){
|
|
PostProc = [](std::vector<SPIRVWord>&){};
|
|
}
|
|
};
|
|
|
|
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<Instruction>(V)) {
|
|
auto DL = I->getDebugLoc();
|
|
if (DL.get() != nullptr) {
|
|
DILocation* DIL = DL.get();
|
|
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, DL.getLine(), DL.getCol());
|
|
}
|
|
} else if (auto F = dyn_cast<Function>(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<OCLTypeToSPIRV>();
|
|
}
|
|
|
|
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<SPIRVValue *> transArguments(CallInst *, SPIRVBasicBlock *);
|
|
std::vector<SPIRVWord> 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<Type *, SPIRVType *> LLVMToSPIRVTypeMap;
|
|
typedef DenseMap<Value *, SPIRVValue *> 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<SPIRVForward *>(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<SPIRVValue*> transValue(const std::vector<Value *> &Values,
|
|
SPIRVBasicBlock* BB);
|
|
std::vector<SPIRVWord> transValue(const std::vector<Value *> &Values,
|
|
SPIRVBasicBlock* BB, SPIRVEntry *Entry);
|
|
|
|
SPIRVInstruction* transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB);
|
|
SPIRVInstruction* transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB);
|
|
|
|
void dumpUsers(Value *V);
|
|
|
|
template<class ExtInstKind>
|
|
bool oclGetExtInstIndex(const std::string &MangledName,
|
|
const std::string& DemangledName, SPIRVWord* EntryPoint);
|
|
void oclGetMutatedArgumentTypesByBuiltin(llvm::FunctionType* FT,
|
|
std::map<unsigned, Type*>& ChangedType, Function* F);
|
|
|
|
bool isBuiltinTransToInst(Function *F);
|
|
bool isBuiltinTransToExtInst(Function *F,
|
|
SPIRVExtInstSetKind *BuiltinSet = nullptr,
|
|
SPIRVWord *EntryPoint = nullptr,
|
|
SmallVectorImpl<std::string> *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<unsigned, Type*>& 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<std::string> *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<StringRef, 2> 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<std::string>& Strs) {
|
|
SmallVector<StringRef, 4> 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<StringRef, 4> 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<const StructType *, 4> Seen;
|
|
|
|
std::function<bool(const Type *Ty)> Run = [&](const Type *Ty) {
|
|
if (!isa<CompositeType>(Ty))
|
|
return false;
|
|
|
|
if (auto *StructTy = dyn_cast<StructType>(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<PointerType>(Ty))
|
|
return Run(PtrTy->getPointerElementType());
|
|
|
|
if (auto *ArrayTy = dyn_cast<ArrayType>(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<StructType>(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<StringRef, 4> 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)) {
|
|
switch (OpCode) {
|
|
default:
|
|
return mapType(T, BM->addOpaqueGenericType(OpCode));
|
|
case OpTypePipe:
|
|
return mapType(T, BM->addPipeType());
|
|
case OpTypeDeviceEvent:
|
|
return mapType(T, BM->addDeviceEventType());
|
|
case OpTypeQueue:
|
|
return mapType(T, BM->addQueueType());
|
|
}
|
|
} else if (isPointerToOpaqueStructType(T)) {
|
|
return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map(
|
|
static_cast<SPIRAddressSpace>(AddrSpc)),
|
|
transType(ET)));
|
|
}
|
|
} else {
|
|
return mapType(T, BM->addPointerType(SPIRSPIRVAddrSpaceMap::map(
|
|
static_cast<SPIRAddressSpace>(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<SPIRVConstant*>(transValue(ConstantInt::get(getSizetType(),
|
|
T->getArrayNumElements(), false), nullptr))));
|
|
|
|
if (T->isStructTy() && !T->isSized()) {
|
|
auto ST = dyn_cast<StructType>(T);
|
|
(void) ST;
|
|
assert(!ST->getName().startswith(kSPR2TypeName::Pipe));
|
|
assert(!ST->getName().startswith(kSPR2TypeName::ImagePrefix));
|
|
return mapType(T, BM->addOpaqueType(T->getStructName()));
|
|
}
|
|
|
|
if (auto ST = dyn_cast<StructType>(T)) {
|
|
assert(ST->isSized());
|
|
|
|
std::string Name;
|
|
if (ST->hasName())
|
|
Name = ST->getName();
|
|
|
|
if(Name == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler))
|
|
return transType(getSamplerType(M));
|
|
if (Name == getSPIRVTypeName(kSPIRVTypeName::ConstantPipeStorage))
|
|
return transType(getPipeStorageType(M));
|
|
|
|
auto *Struct = BM->openStructType(T->getStructNumElements(), Name);
|
|
mapType(T, Struct);
|
|
|
|
SmallVector<unsigned, 4> ForwardRefs;
|
|
|
|
for (unsigned I = 0, E = T->getStructNumElements(); I != E; ++I) {
|
|
auto *ElemTy = ST->getElementType(I);
|
|
if (isa<CompositeType>(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<FunctionType>(T)) {
|
|
SPIRVType *RT = transType(FT->getReturnType());
|
|
std::vector<SPIRVType *> 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<StructType>(ET);
|
|
auto AddrSpc = T->getPointerAddressSpace();
|
|
(void)AddrSpc; // prevent warning about unused variable in NDEBUG build
|
|
auto STName = ST->getStructName();
|
|
assert (STName.startswith(kSPIRVTypeName::PrefixAndDelim) &&
|
|
"Invalid SPIR-V opaque type name");
|
|
SmallVector<std::string, 8> 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<spv::AccessQualifier>(
|
|
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<int, 7> Ops;
|
|
for (unsigned I = 1; I < 8; ++I)
|
|
Ops.push_back(atoi(Postfixes[I].c_str()));
|
|
SPIRVTypeImageDescriptor Desc(static_cast<SPIRVImageDimKind>(Ops[0]),
|
|
Ops[1], Ops[2], Ops[3], Ops[4], Ops[5]);
|
|
return mapType(T, BM->addImageType(SampledT, Desc,
|
|
static_cast<spv::AccessQualifier>(Ops[6])));
|
|
} else if (TN == kSPIRVTypeName::SampledImg) {
|
|
return mapType(T, BM->addSampledImageType(
|
|
static_cast<SPIRVTypeImage *>(
|
|
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 if (TN == kSPIRVTypeName::Queue)
|
|
return mapType(T, BM->addQueueType());
|
|
else if (TN == kSPIRVTypeName::PipeStorage)
|
|
return mapType(T, BM->addPipeStorageType());
|
|
else
|
|
return mapType(T, BM->addOpaqueGenericType(
|
|
SPIRVOpaqueTypeOpCodeMap::map(TN)));
|
|
}
|
|
|
|
SPIRVFunction *
|
|
LLVMToSPIRV::transFunctionDecl(Function *F) {
|
|
if (auto BF= getTranslatedValue(F))
|
|
return static_cast<SPIRVFunction *>(BF);
|
|
|
|
SPIRVTypeFunction *BFT = static_cast<SPIRVTypeFunction *>(transType(
|
|
getAnalysis<OCLTypeToSPIRV>().getAdaptedType(F)));
|
|
SPIRVFunction *BF = static_cast<SPIRVFunction *>(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(ArgNo + 1, Attribute::Dereferenceable))
|
|
BA->addDecorate(DecorationMaxByteOffset,
|
|
Attrs.getAttribute(ArgNo + 1, Attribute::Dereferenceable)
|
|
.getDereferenceableBytes());
|
|
}
|
|
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<ConstantPointerNull>(V))
|
|
return BM->addNullConstant(bcast<SPIRVTypePointer>(transType(
|
|
CPNull->getType())));
|
|
|
|
if (auto CAZero = dyn_cast<ConstantAggregateZero>(V)) {
|
|
Type *AggType = CAZero->getType();
|
|
if (const StructType* ST = dyn_cast<StructType>(AggType))
|
|
if (ST->getName() == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler))
|
|
return BM->addSamplerConstant(transType(AggType), 0,0,0);
|
|
|
|
return BM->addNullConstant(transType(AggType));
|
|
}
|
|
|
|
if (auto ConstI = dyn_cast<ConstantInt>(V))
|
|
return BM->addConstant(transType(V->getType()), ConstI->getZExtValue());
|
|
|
|
if (auto ConstFP = dyn_cast<ConstantFP>(V)) {
|
|
auto BT = static_cast<SPIRVType *>(transType(V->getType()));
|
|
return BM->addConstant(BT,
|
|
ConstFP->getValueAPF().bitcastToAPInt().getZExtValue());
|
|
}
|
|
|
|
if (auto ConstDA = dyn_cast<ConstantDataArray>(V)) {
|
|
std::vector<SPIRVValue *> 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<ConstantArray>(V)) {
|
|
std::vector<SPIRVValue *> 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<ConstantDataVector>(V)) {
|
|
std::vector<SPIRVValue *> 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<ConstantVector>(V)) {
|
|
std::vector<SPIRVValue *> 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<ConstantStruct>(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);
|
|
}
|
|
if (ConstV->getType()->getName() ==
|
|
getSPIRVTypeName(kSPIRVTypeName::ConstantPipeStorage)) {
|
|
assert(ConstV->getNumOperands() == 3);
|
|
SPIRVWord
|
|
PacketSize = ConstV->getOperand(0)->getUniqueInteger().getZExtValue(),
|
|
PacketAlign = ConstV->getOperand(1)->getUniqueInteger().getZExtValue(),
|
|
Capacity = ConstV->getOperand(2)->getUniqueInteger().getZExtValue();
|
|
assert(PacketAlign >= 1 && "Invalid packet alignment");
|
|
assert(PacketSize >= PacketAlign && PacketSize % PacketAlign == 0 &&
|
|
"Invalid packet size and/or alignment.");
|
|
SPIRVType* PipeStorageTy = transType(ConstV->getType());
|
|
return BM->addPipeStorageConstant(PipeStorageTy, PacketSize, PacketAlign,
|
|
Capacity);
|
|
}
|
|
std::vector<SPIRVValue *> 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<ConstantExpr>(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<UndefValue>(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<Instruction>(V) || isa<GetElementPtrInst>(V) ||
|
|
isa<CastInst>(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<AddrSpaceCastInst>(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<BasicBlock>(V)) {
|
|
auto BF = static_cast<SPIRVFunction *>(getTranslatedValue(LBB->getParent()));
|
|
assert (BF && "Function not translated");
|
|
BB = static_cast<SPIRVBasicBlock *>(mapValue(V, BM->addBasicBlock(BF)));
|
|
BM->setName(BB, LBB->getName());
|
|
return BB;
|
|
}
|
|
|
|
if (auto F = dyn_cast<Function>(V))
|
|
return transFunctionDecl(F);
|
|
|
|
if (auto GV = dyn_cast<GlobalVariable>(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<StructType>(Init->getType()) : nullptr;
|
|
if (ST && ST->hasName() && isSPIRVConstantName(ST->getName())) {
|
|
auto BV = transConstant(Init);
|
|
assert(BV);
|
|
return mapValue(V, BV);
|
|
} else if (ConstantExpr *ConstUE = dyn_cast_or_null<ConstantExpr>(Init)) {
|
|
Instruction * Inst = ConstUE->getAsInstruction();
|
|
if (isSpecialTypeInitializer(Inst)) {
|
|
Init = Inst->getOperand(0);
|
|
Ty = static_cast<PointerType*>(Init->getType());
|
|
}
|
|
Inst->dropAllReferences();
|
|
}
|
|
auto BVar = static_cast<SPIRVVariable *>(BM->addVariable(
|
|
transType(Ty), GV->isConstant(),
|
|
transLinkageType(GV),
|
|
Init ? transValue(Init, nullptr) : nullptr,
|
|
GV->getName(),
|
|
SPIRSPIRVAddrSpaceMap::map(
|
|
static_cast<SPIRAddressSpace>(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<Constant>(V)) {
|
|
auto BV = transConstant(V);
|
|
assert(BV);
|
|
return mapValue(V, BV);
|
|
}
|
|
|
|
if (auto Arg = dyn_cast<Argument>(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<StoreInst>(V)) {
|
|
std::vector<SPIRVWord> MemoryAccess(1,0);
|
|
if (ST->isVolatile())
|
|
MemoryAccess[0] |= MemoryAccessVolatileMask;
|
|
if (ST->getAlignment()) {
|
|
MemoryAccess[0] |= MemoryAccessAlignedMask;
|
|
MemoryAccess.push_back(ST->getAlignment());
|
|
}
|
|
if (ST->getMetadata(LLVMContext::MD_nontemporal))
|
|
MemoryAccess[0] |= MemoryAccessNontemporalMask;
|
|
if (MemoryAccess.front() == 0)
|
|
MemoryAccess.clear();
|
|
return mapValue(V, BM->addStoreInst(
|
|
transValue(ST->getPointerOperand(), BB),
|
|
transValue(ST->getValueOperand(), BB),
|
|
MemoryAccess, BB));
|
|
}
|
|
|
|
if (LoadInst *LD = dyn_cast<LoadInst>(V)) {
|
|
std::vector<SPIRVWord> MemoryAccess(1,0);
|
|
if (LD->isVolatile())
|
|
MemoryAccess[0] |= MemoryAccessVolatileMask;
|
|
if (LD->getAlignment()) {
|
|
MemoryAccess[0] |= MemoryAccessAlignedMask;
|
|
MemoryAccess.push_back(LD->getAlignment());
|
|
}
|
|
if (LD->getMetadata(LLVMContext::MD_nontemporal))
|
|
MemoryAccess[0] |= MemoryAccessNontemporalMask;
|
|
if (MemoryAccess.front() == 0)
|
|
MemoryAccess.clear();
|
|
return mapValue(V, BM->addLoadInst(
|
|
transValue(LD->getPointerOperand(), BB),
|
|
MemoryAccess, BB));
|
|
}
|
|
|
|
if (BinaryOperator *B = dyn_cast<BinaryOperator>(V)) {
|
|
SPIRVInstruction* BI = transBinaryInst(B, BB);
|
|
return mapValue(V, BI);
|
|
}
|
|
|
|
if (auto RI = dyn_cast<ReturnInst>(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<CmpInst>(V)) {
|
|
SPIRVInstruction* BI = transCmpInst(Cmp, BB);
|
|
return mapValue(V, BI);
|
|
}
|
|
|
|
if (SelectInst *Sel = dyn_cast<SelectInst>(V))
|
|
return mapValue(V, BM->addSelectInst(
|
|
transValue(Sel->getCondition(), BB),
|
|
transValue(Sel->getTrueValue(), BB),
|
|
transValue(Sel->getFalseValue(), BB),BB));
|
|
|
|
if (AllocaInst *Alc = dyn_cast<AllocaInst>(V))
|
|
return mapValue(V, BM->addVariable(
|
|
transType(Alc->getType()), false,
|
|
SPIRVLinkageTypeKind::LinkageTypeInternal,
|
|
nullptr, Alc->getName(),
|
|
StorageClassFunction, BB));
|
|
|
|
if (auto *Switch = dyn_cast<SwitchInst>(V)) {
|
|
std::vector<std::pair<SPIRVWord, SPIRVBasicBlock *>> 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<SPIRVBasicBlock*>(transValue(I.getCaseSuccessor(),
|
|
nullptr))));
|
|
return mapValue(V, BM->addSwitchInst(
|
|
transValue(Switch->getCondition(), BB),
|
|
static_cast<SPIRVBasicBlock*>(transValue(Switch->getDefaultDest(),
|
|
nullptr)), Pairs, BB));
|
|
}
|
|
|
|
if (auto Branch = dyn_cast<BranchInst>(V)) {
|
|
if (Branch->isUnconditional())
|
|
return mapValue(V, BM->addBranchInst(
|
|
static_cast<SPIRVLabel*>(transValue(Branch->getSuccessor(0), BB)),
|
|
BB));
|
|
return mapValue(V, BM->addBranchConditionalInst(
|
|
transValue(Branch->getCondition(), BB),
|
|
static_cast<SPIRVLabel*>(transValue(Branch->getSuccessor(0), BB)),
|
|
static_cast<SPIRVLabel*>(transValue(Branch->getSuccessor(1), BB)),
|
|
BB));
|
|
}
|
|
|
|
if (auto Phi = dyn_cast<PHINode>(V)) {
|
|
std::vector<SPIRVValue *> 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 (auto Ext = dyn_cast<ExtractValueInst>(V)) {
|
|
return mapValue(V, BM->addCompositeExtractInst(
|
|
transType(Ext->getType()),
|
|
transValue(Ext->getAggregateOperand(), BB),
|
|
Ext->getIndices(), BB));
|
|
}
|
|
|
|
if (auto Ins = dyn_cast<InsertValueInst>(V)) {
|
|
return mapValue(V, BM->addCompositeInsertInst(
|
|
transValue(Ins->getInsertedValueOperand(), BB),
|
|
transValue(Ins->getAggregateOperand(), BB),
|
|
Ins->getIndices(), BB));
|
|
}
|
|
|
|
if (UnaryInstruction *U = dyn_cast<UnaryInstruction>(V)) {
|
|
if (isSpecialTypeInitializer(U))
|
|
return mapValue(V, transValue(U->getOperand(0), BB));
|
|
return mapValue(V, transUnaryInst(U, BB));
|
|
}
|
|
|
|
if (GetElementPtrInst *GEP = dyn_cast<GetElementPtrInst>(V)) {
|
|
std::vector<SPIRVValue *> 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<ExtractElementInst>(V)) {
|
|
auto Index = Ext->getIndexOperand();
|
|
if (auto Const = dyn_cast<ConstantInt>(Index))
|
|
return mapValue(V, BM->addCompositeExtractInst(
|
|
transType(Ext->getType()),
|
|
transValue(Ext->getVectorOperand(), BB),
|
|
std::vector<SPIRVWord>(1, Const->getZExtValue()),
|
|
BB));
|
|
else
|
|
return mapValue(V, BM->addVectorExtractDynamicInst(
|
|
transValue(Ext->getVectorOperand(), BB),
|
|
transValue(Index, BB),
|
|
BB));
|
|
}
|
|
|
|
if (auto Ins = dyn_cast<InsertElementInst>(V)) {
|
|
auto Index = Ins->getOperand(2);
|
|
if (auto Const = dyn_cast<ConstantInt>(Index))
|
|
return mapValue(V, BM->addCompositeInsertInst(
|
|
transValue(Ins->getOperand(1), BB),
|
|
transValue(Ins->getOperand(0), BB),
|
|
std::vector<SPIRVWord>(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<ShuffleVectorInst>(V)) {
|
|
std::vector<SPIRVWord> 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<CallInst>(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<AtomicCmpXchgInst>(V) &&
|
|
cast<AtomicCmpXchgInst>(V)->isVolatile()) ||
|
|
(isa<AtomicRMWInst>(V) && cast<AtomicRMWInst>(V)->isVolatile()))
|
|
BV->setVolatile(true);
|
|
DbgTran.transDbgInfo(V, BV);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
LLVMToSPIRV::transAlign(Value *V, SPIRVValue *BV) {
|
|
if (auto AL = dyn_cast<AllocaInst>(V)) {
|
|
BM->setAlignment(BV, AL->getAlignment());
|
|
return true;
|
|
}
|
|
if (auto GV = dyn_cast<GlobalVariable>(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);
|
|
(void) Kind;
|
|
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<ConstantInt>(Arg)) {
|
|
// Sampler is declared as a kernel scope constant
|
|
return GetSamplerConstant(Const->getZExtValue());
|
|
} else if (auto Load = dyn_cast<LoadInst>(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<GlobalVariable>(Op) && "Unknown sampler pattern!");
|
|
auto GV = cast<GlobalVariable>(Op);
|
|
assert(GV->isConstant() ||
|
|
GV->getType()->getPointerAddressSpace() == SPIRAS_Constant);
|
|
auto Initializer = GV->getInitializer();
|
|
assert(isa<ConstantInt>(Initializer) && "sampler not constant int?");
|
|
return GetSamplerConstant(cast<ConstantInt>(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<SPIRVWord> MemoryAccess;
|
|
|
|
if (isa<ConstantInt>(CI->getOperand(4)) &&
|
|
dyn_cast<ConstantInt>(CI->getOperand(4))
|
|
->getZExtValue() == 1)
|
|
MemoryAccess.push_back(MemoryAccessVolatileMask);
|
|
if (isa<ConstantInt>(CI->getOperand(3))) {
|
|
MemoryAccess.push_back(MemoryAccessAlignedMask);
|
|
MemoryAccess.push_back(dyn_cast<ConstantInt>(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<std::string, 2> 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<SPIRVValue*>
|
|
LLVMToSPIRV::transValue(const std::vector<Value *> &Args, SPIRVBasicBlock* BB) {
|
|
std::vector<SPIRVValue*> BArgs;
|
|
for (auto &I: Args)
|
|
BArgs.push_back(transValue(I, BB));
|
|
return BArgs;
|
|
}
|
|
|
|
std::vector<SPIRVValue*>
|
|
LLVMToSPIRV::transArguments(CallInst *CI, SPIRVBasicBlock *BB) {
|
|
return transValue(getArguments(CI), BB);
|
|
}
|
|
|
|
std::vector<SPIRVWord>
|
|
LLVMToSPIRV::transValue(const std::vector<Value *> &Args, SPIRVBasicBlock* BB,
|
|
SPIRVEntry *Entry) {
|
|
std::vector<SPIRVWord> Operands;
|
|
for (size_t I = 0, E = Args.size(); I != E; ++I) {
|
|
Operands.push_back(Entry->isOperandLiteral(I) ?
|
|
cast<ConstantInt>(Args[I])->getZExtValue() :
|
|
transValue(Args[I], BB)->getId());
|
|
}
|
|
return Operands;
|
|
}
|
|
|
|
std::vector<SPIRVWord>
|
|
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(static_cast<GlobalVariable*>(I), nullptr))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
LLVMToSPIRV::mutateFuncArgType(const std::map<unsigned, Type*>& ChangedType,
|
|
Function* F) {
|
|
for (auto &I : ChangedType) {
|
|
for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE; ++UI) {
|
|
auto Call = dyn_cast<CallInst>(*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<Value *> 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(static_cast<BasicBlock*>(FI), nullptr);
|
|
}
|
|
for (Function::iterator FI = I->begin(), FE = I->end(); FI != FE; ++FI) {
|
|
SPIRVBasicBlock* BB = static_cast<SPIRVBasicBlock*>(transValue(static_cast<BasicBlock*>(FI), nullptr));
|
|
for (BasicBlock::iterator BI = FI->begin(), BE = FI->end(); BI != BE;
|
|
++BI) {
|
|
transValue(static_cast<Instruction*>(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 = static_cast<Function*>(I);
|
|
auto FT = F->getFunctionType();
|
|
std::map<unsigned, Type *> ChangedType;
|
|
oclGetMutatedArgumentTypesByBuiltin(FT, ChangedType, F);
|
|
mutateFuncArgType(ChangedType, F);
|
|
}
|
|
|
|
// SPIR-V logical layout requires all function declarations go before
|
|
// function definitions.
|
|
std::vector<Function *> Decls, Defs;
|
|
for (Module::iterator I1 = M->begin(), E = M->end(); I1 != E; ++I1) {
|
|
auto I = static_cast<Function*>(I1);
|
|
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<unsigned, Type*>& 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<std::string, 2> 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;
|
|
Function *F = nullptr;
|
|
auto N = NMD.nextOp(); /* execution mode MDNode */
|
|
N.get(F).get(EMode);
|
|
|
|
SPIRVFunction *BF = static_cast<SPIRVFunction *>(getTranslatedValue(F));
|
|
assert(BF && "Invalid kernel function");
|
|
if (!BF)
|
|
return false;
|
|
|
|
switch (EMode) {
|
|
case spv::ExecutionModeContractionOff:
|
|
case spv::ExecutionModeInitializer:
|
|
case spv::ExecutionModeFinalizer:
|
|
BF->addExecutionMode(new SPIRVExecutionMode(BF,
|
|
static_cast<ExecutionMode>(EMode)));
|
|
break;
|
|
case spv::ExecutionModeLocalSize:
|
|
case spv::ExecutionModeLocalSizeHint: {
|
|
unsigned X, Y, Z;
|
|
N.get(X).get(Y).get(Z);
|
|
BF->addExecutionMode(new SPIRVExecutionMode(BF,
|
|
static_cast<ExecutionMode>(EMode), X, Y, Z));
|
|
}
|
|
break;
|
|
case spv::ExecutionModeVecTypeHint:
|
|
case spv::ExecutionModeSubgroupSize:
|
|
case spv::ExecutionModeSubgroupsPerWorkgroup: {
|
|
unsigned X;
|
|
N.get(X);
|
|
BF->addExecutionMode(new SPIRVExecutionMode(BF,
|
|
static_cast<ExecutionMode>(EMode), X));
|
|
}
|
|
break;
|
|
default:
|
|
llvm_unreachable("invalid execution mode");
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
LLVMToSPIRV::transOCLKernelMetadata() {
|
|
NamedMDNode *KernelMDs = M->getNamedMetadata(SPIR_MD_KERNELS);
|
|
std::vector<std::string> 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<Function>(KernelMD->getOperand(0));
|
|
|
|
SPIRVFunction *BF = static_cast<SPIRVFunction *>(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<MDNode>(KernelMD->getOperand(MI));
|
|
if (!MD)
|
|
continue;
|
|
MDString *NameMD = dyn_cast<MDString>(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<SourceLanguage>(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<SPIRVCapabilityKind>(rmap<OclExt::Kind>(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<SPIRVWord> SPArgs;
|
|
for (size_t I = 0, E = Args.size(); I != E; ++I) {
|
|
assert((!isFunctionPointerType(Args[I]->getType()) ||
|
|
isa<Function>(Args[I])) &&
|
|
"Invalid function pointer argument");
|
|
SPArgs.push_back(SPI->isOperandLiteral(I) ?
|
|
cast<ConstantInt>(Args[I])->getZExtValue() :
|
|
transValue(Args[I], BB)->getId());
|
|
}
|
|
SPI->setOpWordsAndValidate(SPArgs);
|
|
if (!SPRetTy || !SPRetTy->isTypeStruct())
|
|
return SPI;
|
|
std::vector<SPIRVWord> 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<SPIRVModule> 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<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
|
|
legacy::PassManager PassMgr;
|
|
addPassesForSPIRV(PassMgr);
|
|
PassMgr.run(*M);
|
|
return true;
|
|
}
|