2748 lines
94 KiB
C++
2748 lines
94 KiB
C++
//===- 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 <algorithm>
|
|
#include <cstdlib>
|
|
#include <functional>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <map>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#define DEBUG_TYPE "spirv"
|
|
|
|
using namespace std;
|
|
using namespace llvm;
|
|
using namespace SPIRV;
|
|
using namespace OCLUtil;
|
|
|
|
namespace SPIRV{
|
|
|
|
cl::opt<bool> SPIRVEnableStepExpansion("spirv-expand-step", cl::init(true),
|
|
cl::desc("Enable expansion of OpenCL step and smoothstep function"));
|
|
|
|
cl::opt<bool> SPIRVGenKernelArgNameMD("spirv-gen-kernel-arg-name-md",
|
|
cl::init(false), cl::desc("Enable generating OpenCL kernel argument name "
|
|
"metadata"));
|
|
|
|
cl::opt<bool> 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<SPIRVWord>& IntVals) {
|
|
std::vector<Metadata*> 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<Metadata*> 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);
|
|
}
|
|
|
|
#if 0
|
|
// this function is currently unneeded
|
|
static MDNode*
|
|
getMDString(LLVMContext *Context, const std::string& Str) {
|
|
std::vector<Metadata*> ValueVec;
|
|
if (!Str.empty())
|
|
ValueVec.push_back(MDString::get(*Context, Str));
|
|
return MDNode::get(*Context, ValueVec);
|
|
}
|
|
#endif
|
|
|
|
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<std::string> &StrSet) {
|
|
NamedMDNode *NamedMD = M->getOrInsertNamedMetadata(MDName);
|
|
std::vector<Metadata*> 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<llvm::Metadata*> &KernelMD, const std::string &MDName,
|
|
SPIRVFunction *BF, std::function<Metadata *(SPIRVFunctionParameter *)>Func){
|
|
std::vector<Metadata*> 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, "", 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 Builder.createFile("","");//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[] = {Builder.createUnspecifiedType("")};
|
|
return Builder.createFunction(static_cast<DIScope*>(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<Instruction>(V)) {
|
|
assert(SV->isInst() && "Invalid instruction");
|
|
auto SI = static_cast<SPIRVInstruction *>(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<std::string, DIFile*> FileMap;
|
|
std::unordered_map<Function *, DISubprogram*> 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, bool IsClassMember = false);
|
|
std::string transTypeToOCLTypeName(SPIRVType *BT, bool IsSigned = true);
|
|
std::vector<Type *> transTypeVector(const std::vector<SPIRVType *>&);
|
|
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<Value *> transValue(const std::vector<SPIRVValue *>&, Function *F,
|
|
BasicBlock *);
|
|
Function *transFunction(SPIRVFunction *F);
|
|
bool transFPContractMetadata();
|
|
bool transKernelMetadata();
|
|
bool transNonTemporalMetadata(Instruction *I);
|
|
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<SPIRVWord> &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 (<n x i1> arg)
|
|
/// =>
|
|
/// i32 func (<n x i32> arg)
|
|
/// \return transformed call instruction.
|
|
Instruction *postProcessGroupAllAny(CallInst *CI,
|
|
const std::string &DemangledName);
|
|
|
|
typedef DenseMap<SPIRVType *, Type *> SPIRVToLLVMTypeMap;
|
|
typedef DenseMap<SPIRVValue *, Value *> SPIRVToLLVMValueMap;
|
|
typedef DenseMap<SPIRVFunction *, Function *> SPIRVToLLVMFunctionMap;
|
|
typedef DenseMap<GlobalVariable *, SPIRVBuiltinVariableKind> 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<SPIRVValue *, LoadInst*> 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<LoadInst>(Loc->second);
|
|
auto Placeholder = dyn_cast<GlobalVariable>(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<SPIRVValue *> &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,
|
|
bool UseSPIRVFriendlyFormat = false, int PipeAccess = 0);
|
|
std::string transOCLPipeStorageTypeName(SPIRV::SPIRVTypePipeStorage* PST);
|
|
std::string transOCLImageTypeAccessQualifier(SPIRV::SPIRVTypeImage* ST);
|
|
std::string transOCLPipeTypeAccessQualifier(SPIRV::SPIRVTypePipe* ST);
|
|
|
|
Value *oclTransConstantSampler(SPIRV::SPIRVConstantSampler* BCS);
|
|
Value * oclTransConstantPipeStorage(SPIRV::SPIRVConstantPipeStorage* BCPS);
|
|
void setName(llvm::Value* V, SPIRVValue* BV);
|
|
void insertImageNameAccessQualifier(SPIRV::SPIRVTypeImage* ST, std::string &Name);
|
|
template<class Source, class Func>
|
|
bool foreachFuncCtlMask(Source, Func);
|
|
llvm::GlobalValue::LinkageTypes transLinkageType(const SPIRVValue* V);
|
|
Instruction *transOCLAllAny(SPIRVInstruction* BI, BasicBlock *BB);
|
|
Instruction *transOCLRelational(SPIRVInstruction* BI, BasicBlock *BB);
|
|
|
|
CallInst *transOCLBarrier(BasicBlock *BB, SPIRVWord ExecScope,
|
|
SPIRVWord MemSema, SPIRVWord MemScope);
|
|
|
|
CallInst *transOCLMemFence(BasicBlock *BB,
|
|
SPIRVWord MemSema, SPIRVWord MemScope);
|
|
};
|
|
|
|
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<GlobalVariable *> WorkList;
|
|
for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) {
|
|
SPIRVBuiltinVariableKind Kind;
|
|
auto I1 = static_cast<GlobalVariable*>(I);
|
|
if (!isSPIRVBuiltinVariable(I1, &Kind))
|
|
continue;
|
|
if (!transOCLBuiltinFromVariable(I1, Kind))
|
|
return false;
|
|
WorkList.push_back(I1);
|
|
}
|
|
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<VectorType>(ReturnTy)->getElementType();
|
|
std::vector<Type*> 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<Instruction *> Deletes;
|
|
std::vector<Instruction *> Uses;
|
|
for (auto UI = GV->user_begin(), UE = GV->user_end(); UI != UE; ++UI) {
|
|
assert (isa<LoadInst>(*UI) && "Unsupported use");
|
|
auto LD = dyn_cast<LoadInst>(*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<ExtractElementInst>(*LDUI) && "Unsupported use");
|
|
auto EEI = dyn_cast<ExtractElementInst>(*LDUI);
|
|
Uses.push_back(EEI);
|
|
Deletes.push_back(EEI);
|
|
}
|
|
Deletes.push_back(LD);
|
|
}
|
|
for (auto &I:Uses) {
|
|
std::vector<Value *> Arg;
|
|
if (auto EEI = dyn_cast<ExtractElementInst>(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<std::string>(ST->getDescriptor());
|
|
if (SPIRVGenImgTypeAccQualPostfix)
|
|
SPIRVToLLVM::insertImageNameAccessQualifier(ST, Name);
|
|
return 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,
|
|
bool UseSPIRVFriendlyFormat, int PipeAccess){
|
|
if (!UseSPIRVFriendlyFormat)
|
|
return kSPR2TypeName::Pipe;
|
|
else
|
|
return std::string(kSPIRVTypeName::PrefixAndDelim)
|
|
+ kSPIRVTypeName::Pipe
|
|
+ kSPIRVTypeName::Delimiter
|
|
+ kSPIRVTypeName::PostfixDelim
|
|
+ PipeAccess;
|
|
}
|
|
|
|
std::string
|
|
SPIRVToLLVM::transOCLPipeStorageTypeName(SPIRV::SPIRVTypePipeStorage* PST) {
|
|
return std::string(kSPIRVTypeName::PrefixAndDelim)
|
|
+ kSPIRVTypeName::PipeStorage;
|
|
}
|
|
|
|
Type *
|
|
SPIRVToLLVM::transType(SPIRVType *T, bool IsClassMember) {
|
|
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(), IsClassMember),
|
|
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<SPIRVTypeFunction *>(T);
|
|
auto RT = transType(FT->getReturnType());
|
|
std::vector<Type *> 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<SPIRVTypeImage *>(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<SPIRVTypeSampledImage *>(T);
|
|
return mapType(T, getOrCreateOpaquePtrType(M,
|
|
transOCLSampledImageTypeName(ST)));
|
|
}
|
|
case OpTypeStruct: {
|
|
auto ST = static_cast<SPIRVTypeStruct *>(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<Type *, 4> MT;
|
|
for (size_t I = 0, E = ST->getMemberCount(); I != E; ++I)
|
|
MT.push_back(transType(ST->getMemberType(I), true));
|
|
StructTy->setBody(MT, ST->isPacked());
|
|
return StructTy;
|
|
}
|
|
case OpTypePipe: {
|
|
auto PT = static_cast<SPIRVTypePipe *>(T);
|
|
return mapType(T, getOrCreateOpaquePtrType(M,
|
|
transOCLPipeTypeName(PT, IsClassMember, PT->getAccessQualifier()),
|
|
getOCLOpaqueTypeAddrSpace(T->getOpCode())));
|
|
|
|
}
|
|
case OpTypePipeStorage: {
|
|
auto PST = static_cast<SPIRVTypePipeStorage *>(T);
|
|
return mapType(T, getOrCreateOpaquePtrType(M,
|
|
transOCLPipeStorageTypeName(PST),
|
|
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: {
|
|
std::string Name;
|
|
Name = rmap<std::string>(static_cast<SPIRVTypeImage *>(T)->getDescriptor());
|
|
if (SPIRVGenImgTypeAccQualPostfix) {
|
|
auto ST = static_cast<SPIRVTypeImage *>(T);
|
|
insertImageNameAccessQualifier(ST, Name);
|
|
}
|
|
return Name;
|
|
}
|
|
default:
|
|
if (isOpaqueGenericTypeOpCode(T->getOpCode())) {
|
|
return OCLOpaqueTypeOpCodeMap::rmap(T->getOpCode());
|
|
}
|
|
llvm_unreachable("Not implemented");
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
std::vector<Type *>
|
|
SPIRVToLLVM::transTypeVector(const std::vector<SPIRVType *> &BT) {
|
|
std::vector<Type *> T;
|
|
for (auto I: BT)
|
|
T.push_back(transType(I));
|
|
return T;
|
|
}
|
|
|
|
std::vector<Value *>
|
|
SPIRVToLLVM::transValue(const std::vector<SPIRVValue *> &BV, Function *F,
|
|
BasicBlock *BB) {
|
|
std::vector<Value *> 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<Instruction>(V))
|
|
return;
|
|
auto OC = cast<Instruction>(V)->getOpcode();
|
|
if (OC == Instruction::AShr || OC == Instruction::LShr) {
|
|
cast<BinaryOperator>(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);
|
|
}
|
|
|
|
void SPIRVToLLVM::insertImageNameAccessQualifier(SPIRV::SPIRVTypeImage* ST, std::string &Name) {
|
|
std::string QName = rmap<std::string>(ST->getAccessQualifier());
|
|
// transform: read_only -> ro, write_only -> wo, read_write -> rw
|
|
QName = QName.substr(0,1) + QName.substr(QName.find("_") + 1, 1) + "_";
|
|
assert(!Name.empty() && "image name should not be empty");
|
|
Name.insert(Name.size() - 1, QName);
|
|
}
|
|
|
|
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<SPIRVUnary*>(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<CastInst::CastOps>(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<Constant>(Src), Dst);
|
|
}
|
|
|
|
BinaryOperator *SPIRVToLLVM::transShiftLogicalBitwiseInst(SPIRVValue* BV,
|
|
BasicBlock* BB,Function* F) {
|
|
SPIRVBinary* BBN = static_cast<SPIRVBinary*>(BV);
|
|
assert(BB && "Invalid BB");
|
|
Instruction::BinaryOps BO;
|
|
auto OP = BBN->getOpCode();
|
|
if (isLogicalOpCode(OP))
|
|
OP = IntBoolOpMap::rmap(OP);
|
|
BO = static_cast<Instruction::BinaryOps>(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<SPIRVCompare*>(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(static_cast<Function*>(F)))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
for (auto I = M->begin(), E = M->end(); I != E;) {
|
|
auto F = static_cast<Function*>(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 = static_cast<Function*>(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<CallInst>(*I++)) {
|
|
auto ST = dyn_cast<StoreInst>(*(CI->user_begin()));
|
|
assert(ST);
|
|
std::vector<Type *> 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<Value *> InvokeFuncPtrs;
|
|
mutateFunctionOCL (F, [=, &InvokeFuncPtrs](
|
|
CallInst *CI, std::vector<Value *> &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<Function>(*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<Function>(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<Value *> &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, "", static_cast<Instruction*>(FBegin));
|
|
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<CallInst>(CI->getOperand(0)))->getArgOperand(0)->getType(),
|
|
&ImageTypeName))
|
|
isDepthImage = ImageTypeName.endswith("depth_t");
|
|
return mutateCallInstOCL(
|
|
M, CI,
|
|
[=](CallInst *, std::vector<Value *> &Args, llvm::Type *&RetTy) {
|
|
CallInst *CallSampledImg = cast<CallInst>(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<ConstantInt>(Args[3]);
|
|
ConstantFP* LodVal = dyn_cast<ConstantFP>(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<VectorType>(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<Value *> &Args) {
|
|
llvm::Type *T = Args[2]->getType();
|
|
if (Args.size() > 4) {
|
|
ConstantInt* ImOp = dyn_cast<ConstantInt>(Args[3]);
|
|
ConstantFP* LodVal = dyn_cast<ConstantFP>(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<Value *> &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<Value *> &Args){
|
|
unsigned vecSize = CI->getOperand(1)->getType()->getVectorNumElements();
|
|
Value *NewVec = nullptr;
|
|
if (auto CA = dyn_cast<Constant>(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);
|
|
}
|
|
|
|
Value *
|
|
SPIRVToLLVM::oclTransConstantPipeStorage(
|
|
SPIRV::SPIRVConstantPipeStorage* BCPS) {
|
|
|
|
string CPSName = string(kSPIRVTypeName::PrefixAndDelim)
|
|
+ kSPIRVTypeName::ConstantPipeStorage;
|
|
|
|
auto Int32Ty = IntegerType::getInt32Ty(*Context);
|
|
auto CPSTy = M->getTypeByName(CPSName);
|
|
if (!CPSTy) {
|
|
Type* CPSElemsTy[] = { Int32Ty, Int32Ty, Int32Ty };
|
|
CPSTy = StructType::create(*Context, CPSElemsTy, CPSName);
|
|
}
|
|
|
|
assert(CPSTy != nullptr && "Could not create spirv.ConstantPipeStorage");
|
|
|
|
Constant* CPSElems[] = {
|
|
ConstantInt::get(Int32Ty, BCPS->getPacketSize()),
|
|
ConstantInt::get(Int32Ty, BCPS->getPacketAlign()),
|
|
ConstantInt::get(Int32Ty, BCPS->getCapacity())
|
|
};
|
|
|
|
return new GlobalVariable(*M, CPSTy, false, GlobalValue::LinkOnceODRLinkage,
|
|
ConstantStruct::get(CPSTy, CPSElems), BCPS->getName(),
|
|
nullptr, GlobalValue::NotThreadLocal, SPIRAS_Global);
|
|
}
|
|
|
|
/// 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<SPIRVConstant *>(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<SPIRVTypeInt*>(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<SPIRVConstantComposite*>(BV);
|
|
std::vector<Constant *> CV;
|
|
for (auto &I:BCC->getElements())
|
|
CV.push_back(dyn_cast<Constant>(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<ArrayType>(transType(BCC->getType())), CV));
|
|
case OpTypeStruct: {
|
|
auto BCCTy = dyn_cast<StructType>(transType(BCC->getType()));
|
|
auto Members = BCCTy->getNumElements();
|
|
auto Constants = CV.size();
|
|
//if we try to initialize constant TypeStruct, add bitcasts
|
|
//if src and dst types are both pointers but to different types
|
|
if (Members == Constants) {
|
|
for (unsigned i = 0; i < Members; ++i) {
|
|
if (CV[i]->getType() == BCCTy->getElementType(i))
|
|
continue;
|
|
if (!CV[i]->getType()->isPointerTy() ||
|
|
!BCCTy->getElementType(i)->isPointerTy())
|
|
continue;
|
|
|
|
CV[i] = ConstantExpr::getBitCast(CV[i], BCCTy->getElementType(i));
|
|
}
|
|
}
|
|
|
|
return mapValue(BV, ConstantStruct::get(
|
|
dyn_cast<StructType>(transType(BCC->getType())), CV));
|
|
}
|
|
default:
|
|
llvm_unreachable("not implemented");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
case OpConstantSampler: {
|
|
auto BCS = static_cast<SPIRVConstantSampler*>(BV);
|
|
return mapValue(BV, oclTransConstantSampler(BCS));
|
|
}
|
|
|
|
case OpConstantPipeStorage: {
|
|
auto BCPS = static_cast<SPIRVConstantPipeStorage*>(BV);
|
|
return mapValue(BV, oclTransConstantPipeStorage(BCPS));
|
|
}
|
|
|
|
case OpSpecConstantOp: {
|
|
auto BI = createInstFromSpecConstantOp(
|
|
static_cast<SPIRVSpecConstantOp*>(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<SPIRVVariable *>(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<Constant>(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<SPIRVFunctionParameter*>(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, static_cast<Argument*>(I));
|
|
}
|
|
llvm_unreachable("Invalid argument");
|
|
return nullptr;
|
|
}
|
|
|
|
case OpFunction:
|
|
return mapValue(BV, transFunction(static_cast<SPIRVFunction *>(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<SPIRVBranch *>(BV);
|
|
return mapValue(BV, BranchInst::Create(
|
|
dyn_cast<BasicBlock>(transValue(BR->getTargetLabel(), F, BB)), BB));
|
|
}
|
|
|
|
case OpBranchConditional: {
|
|
auto BR = static_cast<SPIRVBranchConditional *>(BV);
|
|
return mapValue(
|
|
BV, BranchInst::Create(
|
|
dyn_cast<BasicBlock>(transValue(BR->getTrueLabel(), F, BB)),
|
|
dyn_cast<BasicBlock>(transValue(BR->getFalseLabel(), F, BB)),
|
|
transValue(BR->getCondition(), F, BB), BB));
|
|
}
|
|
|
|
case OpPhi: {
|
|
auto Phi = static_cast<SPIRVPhi *>(BV);
|
|
auto LPhi = dyn_cast<PHINode>(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<BasicBlock>(transValue(IncomingBB, F, BB)));
|
|
});
|
|
return LPhi;
|
|
}
|
|
|
|
case OpReturn:
|
|
return mapValue(BV, ReturnInst::Create(*Context, BB));
|
|
|
|
case OpReturnValue: {
|
|
auto RV = static_cast<SPIRVReturnValue *>(BV);
|
|
return mapValue(
|
|
BV, ReturnInst::Create(*Context,
|
|
transValue(RV->getReturnValue(), F, BB), BB));
|
|
}
|
|
|
|
case OpStore: {
|
|
SPIRVStore *BS = static_cast<SPIRVStore*>(BV);
|
|
StoreInst *SI = new StoreInst(transValue(BS->getSrc(), F, BB),
|
|
transValue(BS->getDst(), F, BB),
|
|
BS->SPIRVMemoryAccess::isVolatile(),
|
|
BS->SPIRVMemoryAccess::getAlignment(), BB);
|
|
if (BS->SPIRVMemoryAccess::isNonTemporal())
|
|
transNonTemporalMetadata(SI);
|
|
return mapValue(BV, SI);
|
|
}
|
|
|
|
case OpLoad: {
|
|
SPIRVLoad *BL = static_cast<SPIRVLoad*>(BV);
|
|
LoadInst *LI = new LoadInst(transValue(BL->getSrc(), F, BB), BV->getName(),
|
|
BL->SPIRVMemoryAccess::isVolatile(),
|
|
BL->SPIRVMemoryAccess::getAlignment(), BB);
|
|
if (BL->SPIRVMemoryAccess::isNonTemporal())
|
|
transNonTemporalMetadata(LI);
|
|
return mapValue(BV, LI);
|
|
}
|
|
|
|
case OpCopyMemorySized: {
|
|
SPIRVCopyMemorySized *BC = static_cast<SPIRVCopyMemorySized *>(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<Function>(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<llvm::ConstantInt>(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<SPIRVSelect*>(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<SPIRVSwitch *>(BV);
|
|
auto Select = transValue(BS->getSelect(), F, BB);
|
|
auto LS = SwitchInst::Create(
|
|
Select, dyn_cast<BasicBlock>(transValue(BS->getDefault(), F, BB)),
|
|
BS->getNumPairs(), BB);
|
|
BS->foreachPair(
|
|
[&](SPIRVWord Literal, SPIRVBasicBlock *Label, size_t Index) {
|
|
LS->addCase(ConstantInt::get(dyn_cast<IntegerType>(Select->getType()),
|
|
Literal),
|
|
dyn_cast<BasicBlock>(transValue(Label, F, BB)));
|
|
});
|
|
return mapValue(BV, LS);
|
|
}
|
|
|
|
case OpAccessChain:
|
|
case OpInBoundsAccessChain:
|
|
case OpPtrAccessChain:
|
|
case OpInBoundsPtrAccessChain: {
|
|
auto AC = static_cast<SPIRVAccessChainBase *>(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(nullptr, Base, Index,
|
|
BV->getName(), BB);
|
|
GEP->setIsInBounds(IsInbound);
|
|
V = GEP;
|
|
} else {
|
|
V = ConstantExpr::getGetElementPtr(Base->getType(),
|
|
dyn_cast<Constant>(Base),
|
|
Index,
|
|
IsInbound);
|
|
}
|
|
return mapValue(BV, V);
|
|
}
|
|
|
|
case OpCompositeExtract: {
|
|
SPIRVCompositeExtract *CE = static_cast<SPIRVCompositeExtract *>(BV);
|
|
if (CE->getComposite()->getType()->isTypeVector()) {
|
|
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));
|
|
}
|
|
return mapValue(
|
|
BV, ExtractValueInst::Create(
|
|
transValue(CE->getComposite(), F, BB),
|
|
CE->getIndices(), BV->getName(), BB));
|
|
}
|
|
|
|
case OpVectorExtractDynamic: {
|
|
auto CE = static_cast<SPIRVVectorExtractDynamic *>(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<SPIRVCompositeInsert *>(BV);
|
|
if (CI->getComposite()->getType()->isTypeVector()) {
|
|
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));
|
|
}
|
|
return mapValue(
|
|
BV, InsertValueInst::Create(
|
|
transValue(CI->getComposite(), F, BB),
|
|
transValue(CI->getObject(), F, BB),
|
|
CI->getIndices(), BV->getName(), BB));
|
|
}
|
|
|
|
case OpVectorInsertDynamic: {
|
|
auto CI = static_cast<SPIRVVectorInsertDynamic *>(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<SPIRVVectorShuffle *>(BV);
|
|
std::vector<Constant *> Components;
|
|
IntegerType *Int32Ty = IntegerType::get(*Context, 32);
|
|
for (auto I : VS->getComponents()) {
|
|
if (I == static_cast<SPIRVWord>(-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<SPIRVFunctionCall *>(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<SPIRVExtInst *>(BV), BB));
|
|
|
|
case OpControlBarrier:
|
|
case OpMemoryBarrier:
|
|
return mapValue(
|
|
BV, transOCLBarrierFence(static_cast<SPIRVInstruction *>(BV), BB));
|
|
|
|
case OpSNegate: {
|
|
SPIRVUnary *BC = static_cast<SPIRVUnary *>(BV);
|
|
return mapValue(
|
|
BV, BinaryOperator::CreateNSWNeg(transValue(BC->getOperand(0), F, BB),
|
|
BV->getName(), BB));
|
|
}
|
|
|
|
case OpFNegate: {
|
|
SPIRVUnary *BC = static_cast<SPIRVUnary *>(BV);
|
|
return mapValue(
|
|
BV, BinaryOperator::CreateFNeg(transValue(BC->getOperand(0), F, BB),
|
|
BV->getName(), BB));
|
|
}
|
|
|
|
case OpNot: {
|
|
SPIRVUnary *BC = static_cast<SPIRVUnary *>(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<SPIRVInstruction *>(BV), BB));
|
|
|
|
case OpIsFinite :
|
|
case OpIsInf :
|
|
case OpIsNan :
|
|
case OpIsNormal :
|
|
case OpSignBitSet :
|
|
return mapValue(BV,
|
|
transOCLRelational(static_cast<SPIRVInstruction *>(BV), BB));
|
|
|
|
default: {
|
|
auto OC = BV->getOpCode();
|
|
if (isSPIRVCmpInstTransToLLVMInst(static_cast<SPIRVInstruction*>(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<SPIRVInstruction *>(BV), BB));
|
|
} else if (isBinaryShiftLogicalBitwiseOpCode(OC) ||
|
|
isLogicalOpCode(OC)) {
|
|
return mapValue(BV, transShiftLogicalBitwiseInst(BV, BB, F));
|
|
} else if (isCvtOpCode(OC)) {
|
|
auto BI = static_cast<SPIRVInstruction *>(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<SPIRVInstruction *>(BV), BB));
|
|
}
|
|
|
|
SPIRVDBG(spvdbgs() << "Cannot translate " << *BV << '\n';)
|
|
llvm_unreachable("Translation of SPIRV instruction not implemented");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
template<class SourceTy, class FuncTy>
|
|
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<FunctionType>(transType(BF->getFunctionType()));
|
|
Function *F = dyn_cast<Function>(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, static_cast<Argument*>(I));
|
|
setName(static_cast<Argument*>(I), BA);
|
|
BA->foreachAttr([&](SPIRVFuncParamAttrKind Kind){
|
|
if (Kind == FunctionParameterAttributeNoWrite)
|
|
return;
|
|
F->addAttribute(I->getArgNo() + 1, SPIRSPIRVFuncParamAttrMap::rmap(Kind));
|
|
});
|
|
|
|
SPIRVWord MaxOffset = 0;
|
|
if (BA->hasDecorate(DecorationMaxByteOffset, 0, &MaxOffset)) {
|
|
AttrBuilder Builder;
|
|
Builder.addDereferenceableAttr(MaxOffset);
|
|
I->addAttr(AttributeSet::get(*Context, I->getArgNo() + 1, Builder));
|
|
}
|
|
}
|
|
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<BasicBlock>(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<SPIRVValue *> &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<Type*> ArgTys = transTypeVector(
|
|
SPIRVInstruction::getOperandTypes(Ops));
|
|
bool HasFuncPtrArg = false;
|
|
for (auto& I:ArgTys) {
|
|
if (isa<FunctionType>(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<SPIRVBuildNDRange *>(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");
|
|
string Suffix = "";
|
|
if (BI->getOpCode() == OpCreatePipeFromPipeStorage) {
|
|
auto CPFPS = static_cast<SPIRVCreatePipeFromPipeStorage*>(BI);
|
|
assert(CPFPS->getType()->isTypePipe() &&
|
|
"Invalid type of CreatePipeFromStorage");
|
|
auto PipeType = static_cast<SPIRVTypePipe*>(CPFPS->getType());
|
|
switch (PipeType->getAccessQualifier()) {
|
|
case AccessQualifierReadOnly: Suffix = "_read"; break;
|
|
case AccessQualifierWriteOnly: Suffix = "_write"; break;
|
|
case AccessQualifierReadWrite: Suffix = "_read_write"; break;
|
|
}
|
|
}
|
|
|
|
return transBuiltinFromInst(getSPIRVFuncName(BI->getOpCode(), Suffix), 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::transNonTemporalMetadata(Instruction *I) {
|
|
Constant* One = ConstantInt::get(Type::getInt32Ty(*Context), 1);
|
|
MDNode *Node = MDNode::get(*Context, ConstantAsMetadata::get(One));
|
|
I->setMetadata(M->getMDKindID("nontemporal"), Node);
|
|
return true;
|
|
}
|
|
|
|
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<Function *>(getTranslatedValue(BF));
|
|
assert(F && "Invalid translated function");
|
|
if (F->getCallingConv() != CallingConv::SPIR_KERNEL)
|
|
continue;
|
|
std::vector<llvm::Metadata*> 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<SPIRVTypeImage *>(T);
|
|
Qual = transOCLImageTypeAccessQualifier(ST);
|
|
} else if (T->isTypePipe()){
|
|
auto PT = static_cast<SPIRVTypePipe *>(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<Metadata*> 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<AllocaInst>(V)) {
|
|
SPIRVWord Align = 0;
|
|
if (BV->hasAlignment(&Align))
|
|
AL->setAlignment(Align);
|
|
return true;
|
|
}
|
|
if (auto GV = dyn_cast<GlobalVariable>(V)) {
|
|
SPIRVWord Align = 0;
|
|
if (BV->hasAlignment(&Align))
|
|
GV->setAlignment(Align);
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void
|
|
SPIRVToLLVM::transOCLVectorLoadStore(std::string& UnmangledName,
|
|
std::vector<SPIRVWord> &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<SPIRVFPRoundingModeKind>(
|
|
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();
|
|
|
|
(void) Set;
|
|
assert (Set == SPIRVEIS_OpenCL && "Not OpenCL extended instruction");
|
|
if (EntryPoint == OpenCLLIB::Printf)
|
|
IsPrintf = true;
|
|
else {
|
|
UnmangledName = OCLExtOpMap::map(static_cast<OCLExtOpKind>(
|
|
EntryPoint));
|
|
}
|
|
|
|
SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] OrigUnmangledName: " <<
|
|
UnmangledName << '\n');
|
|
transOCLVectorLoadStore(UnmangledName, BArgs);
|
|
|
|
std::vector<Type *> 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);
|
|
}
|
|
|
|
CallInst *
|
|
SPIRVToLLVM::transOCLBarrier(BasicBlock *BB, SPIRVWord ExecScope,
|
|
SPIRVWord MemSema, SPIRVWord MemScope) {
|
|
SPIRVWord Ver = 0;
|
|
BM->getSourceLanguage(&Ver);
|
|
|
|
Type* Int32Ty = Type::getInt32Ty(*Context);
|
|
Type* VoidTy = Type::getVoidTy(*Context);
|
|
|
|
std::string FuncName;
|
|
SmallVector<Type *, 2> ArgTy;
|
|
SmallVector<Value *, 2> Arg;
|
|
|
|
Constant *MemFenceFlags =
|
|
ConstantInt::get(Int32Ty, rmapBitMask<OCLMemFenceMap>(MemSema));
|
|
|
|
FuncName = (ExecScope == ScopeWorkgroup) ? kOCLBuiltinName::WorkGroupBarrier
|
|
: kOCLBuiltinName::SubGroupBarrier;
|
|
|
|
if (ExecScope == ScopeWorkgroup && Ver > 0 && Ver <= kOCLVer::CL12) {
|
|
FuncName = kOCLBuiltinName::Barrier;
|
|
ArgTy.push_back(Int32Ty);
|
|
Arg.push_back(MemFenceFlags);
|
|
} else {
|
|
Constant *Scope = ConstantInt::get(Int32Ty, OCLMemScopeMap::rmap(
|
|
static_cast<spv::Scope>(MemScope)));
|
|
|
|
ArgTy.append(2, Int32Ty);
|
|
Arg.push_back(MemFenceFlags);
|
|
Arg.push_back(Scope);
|
|
}
|
|
|
|
std::string MangledName;
|
|
|
|
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);
|
|
}
|
|
|
|
return CallInst::Create(Func, Arg, "", BB);
|
|
}
|
|
|
|
CallInst *
|
|
SPIRVToLLVM::transOCLMemFence(BasicBlock *BB,
|
|
SPIRVWord MemSema, SPIRVWord MemScope) {
|
|
SPIRVWord Ver = 0;
|
|
BM->getSourceLanguage(&Ver);
|
|
|
|
Type* Int32Ty = Type::getInt32Ty(*Context);
|
|
Type* VoidTy = Type::getVoidTy(*Context);
|
|
|
|
std::string FuncName;
|
|
SmallVector<Type *, 3> ArgTy;
|
|
SmallVector<Value *, 3> Arg;
|
|
|
|
Constant *MemFenceFlags =
|
|
ConstantInt::get(Int32Ty, rmapBitMask<OCLMemFenceMap>(MemSema));
|
|
|
|
if (Ver > 0 && Ver <= kOCLVer::CL12) {
|
|
FuncName = kOCLBuiltinName::MemFence;
|
|
ArgTy.push_back(Int32Ty);
|
|
Arg.push_back(MemFenceFlags);
|
|
} else {
|
|
Constant *Order =
|
|
ConstantInt::get(Int32Ty, mapSPIRVMemOrderToOCL(MemSema));
|
|
|
|
Constant *Scope = ConstantInt::get(Int32Ty, OCLMemScopeMap::rmap(
|
|
static_cast<spv::Scope>(MemScope)));
|
|
|
|
FuncName = kOCLBuiltinName::AtomicWorkItemFence;
|
|
ArgTy.append(3, Int32Ty);
|
|
Arg.push_back(MemFenceFlags);
|
|
Arg.push_back(Order);
|
|
Arg.push_back(Scope);
|
|
}
|
|
|
|
std::string MangledName;
|
|
|
|
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);
|
|
}
|
|
|
|
return CallInst::Create(Func, Arg, "", BB);
|
|
}
|
|
|
|
Instruction *
|
|
SPIRVToLLVM::transOCLBarrierFence(SPIRVInstruction *MB, BasicBlock *BB) {
|
|
assert(BB && "Invalid BB");
|
|
std::string FuncName;
|
|
auto getIntVal = [](SPIRVValue *value){
|
|
return static_cast<SPIRVConstant*>(value)->getZExtIntValue();
|
|
};
|
|
|
|
CallInst* Call = nullptr;
|
|
|
|
if (MB->getOpCode() == OpMemoryBarrier) {
|
|
auto MemB = static_cast<SPIRVMemoryBarrier*>(MB);
|
|
|
|
SPIRVWord MemScope = getIntVal(MemB->getOpValue(0));
|
|
SPIRVWord MemSema = getIntVal(MemB->getOpValue(1));
|
|
|
|
Call = transOCLMemFence(BB, MemSema, MemScope);
|
|
} else if (MB->getOpCode() == OpControlBarrier) {
|
|
auto CtlB = static_cast<SPIRVControlBarrier*>(MB);
|
|
|
|
SPIRVWord ExecScope = getIntVal(CtlB->getExecScope());
|
|
SPIRVWord MemSema = getIntVal(CtlB->getMemSemantic());
|
|
SPIRVWord MemScope = getIntVal(CtlB->getMemScope());
|
|
|
|
Call = transOCLBarrier(BB, ExecScope, MemSema, MemScope);
|
|
} else {
|
|
llvm_unreachable("Invalid instruction");
|
|
}
|
|
|
|
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<OclExt::Kind>(BM->getExtension());
|
|
auto CapSet = rmap<OclExt::Kind>(BM->getCapability());
|
|
ExtSet.insert(CapSet.begin(), CapSet.end());
|
|
auto OCLExtensions = map<std::string>(ExtSet);
|
|
std::set<std::string> 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<SPIRVUnary *>(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<const SPIRVFunction*>(V)->getNumBasicBlock() == 0)
|
|
return GlobalValue::ExternalLinkage;
|
|
}
|
|
// Variable declaration
|
|
if (V->getOpCode() == OpVariable) {
|
|
if (static_cast<const SPIRVVariable*>(V)->getInitializer() == 0)
|
|
return GlobalValue::ExternalLinkage;
|
|
}
|
|
// Definition
|
|
return GlobalValue::AvailableExternallyLinkage;
|
|
}
|
|
else {// LinkageTypeExport
|
|
if (V->getOpCode() == OpVariable) {
|
|
if (static_cast<const SPIRVVariable*>(V)->getInitializer() == 0 )
|
|
// Tentative definition
|
|
return GlobalValue::CommonLinkage;
|
|
}
|
|
return GlobalValue::ExternalLinkage;
|
|
}
|
|
}
|
|
|
|
Instruction *SPIRVToLLVM::transOCLAllAny(SPIRVInstruction *I, BasicBlock *BB) {
|
|
CallInst *CI = cast<CallInst>(transSPIRVBuiltinFromInst(I, BB));
|
|
AttributeSet Attrs = CI->getCalledFunction()->getAttributes();
|
|
return cast<Instruction>(mapValue(
|
|
I, mutateCallInstOCL(
|
|
M, CI,
|
|
[=](CallInst *, std::vector<Value *> &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<CallInst>(transSPIRVBuiltinFromInst(I, BB));
|
|
AttributeSet Attrs = CI->getCalledFunction()->getAttributes();
|
|
return cast<Instruction>(mapValue(
|
|
I, mutateCallInstOCL(
|
|
M, CI,
|
|
[=](CallInst *, std::vector<Value *> &Args, llvm::Type *&RetTy) {
|
|
Type *IntTy = Type::getInt32Ty(*Context);
|
|
RetTy = IntTy;
|
|
if (CI->getType()->isVectorTy()) {
|
|
if (cast<VectorType>(CI->getOperand(0)->getType())
|
|
->getElementType()
|
|
->isDoubleTy())
|
|
IntTy = Type::getInt64Ty(*Context);
|
|
if (cast<VectorType>(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<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
|
|
|
|
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;
|
|
}
|