6000 lines
228 KiB
C++
6000 lines
228 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 "SPIRVReader.h"
|
||
#include "OCLUtil.h"
|
||
#include "SPIRVAsm.h"
|
||
#include "SPIRVBasicBlock.h"
|
||
#include "SPIRVExtInst.h"
|
||
#include "SPIRVFnVar.h"
|
||
#include "SPIRVFunction.h"
|
||
#include "SPIRVInstruction.h"
|
||
#include "SPIRVInternal.h"
|
||
#include "SPIRVMDBuilder.h"
|
||
#include "SPIRVMemAliasingINTEL.h"
|
||
#include "SPIRVModule.h"
|
||
#include "SPIRVToLLVMDbgTran.h"
|
||
#include "SPIRVToOCL.h"
|
||
#include "SPIRVType.h"
|
||
#include "SPIRVUtil.h"
|
||
#include "SPIRVValue.h"
|
||
#include "VectorComputeUtil.h"
|
||
|
||
#include "llvm/ADT/DenseMap.h"
|
||
#include "llvm/ADT/SmallSet.h"
|
||
#include "llvm/ADT/StringExtras.h"
|
||
#include "llvm/Analysis/LoopInfo.h"
|
||
#include "llvm/BinaryFormat/Dwarf.h"
|
||
#include "llvm/IR/AttributeMask.h"
|
||
#include "llvm/IR/Constants.h"
|
||
#include "llvm/IR/DebugProgramInstruction.h"
|
||
#include "llvm/IR/DerivedTypes.h"
|
||
#include "llvm/IR/Dominators.h"
|
||
#include "llvm/IR/IRBuilder.h"
|
||
#include "llvm/IR/InlineAsm.h"
|
||
#include "llvm/IR/Instructions.h"
|
||
#include "llvm/IR/IntrinsicInst.h"
|
||
#include "llvm/IR/MDBuilder.h"
|
||
#include "llvm/IR/Metadata.h"
|
||
#include "llvm/IR/Module.h"
|
||
#include "llvm/IR/PassInstrumentation.h"
|
||
#include "llvm/IR/Type.h"
|
||
#include "llvm/IR/TypedPointerType.h"
|
||
#include "llvm/Support/Casting.h"
|
||
#include "llvm/Support/CommandLine.h"
|
||
#include "llvm/Support/Debug.h"
|
||
#include "llvm/Support/FileSystem.h"
|
||
|
||
#include <algorithm>
|
||
#include <cstdlib>
|
||
#include <fstream>
|
||
#include <functional>
|
||
#include <iostream>
|
||
#include <iterator>
|
||
#include <map>
|
||
#include <set>
|
||
#include <sstream>
|
||
#include <string>
|
||
|
||
#define DEBUG_TYPE "spirv"
|
||
|
||
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"));
|
||
|
||
// Prefix for placeholder global variable name.
|
||
const char *KPlaceholderPrefix = "placeholder.";
|
||
|
||
// Save the translated LLVM before validation for debugging purpose.
|
||
static bool DbgSaveTmpLLVM = false;
|
||
static const char *DbgTmpLLVMFileName = "_tmp_llvmbil.ll";
|
||
|
||
namespace kOCLTypeQualifierName {
|
||
const static char *Volatile = "volatile";
|
||
const static char *Restrict = "restrict";
|
||
const static char *Pipe = "pipe";
|
||
} // namespace kOCLTypeQualifierName
|
||
|
||
static bool isKernel(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::OF_None);
|
||
if (!EC) {
|
||
FS << *M;
|
||
FS.close();
|
||
}
|
||
}
|
||
|
||
static MDNode *getMDNodeStringIntVec(LLVMContext *Context,
|
||
const std::vector<SPIRVWord> &IntVals) {
|
||
std::vector<Metadata *> ValueVec;
|
||
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);
|
||
}
|
||
|
||
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 addKernelArgumentMetadata(
|
||
LLVMContext *Context, const std::string &MDName, SPIRVFunction *BF,
|
||
llvm::Function *Fn,
|
||
std::function<Metadata *(SPIRVFunctionParameter *)> ForeachFnArg) {
|
||
std::vector<Metadata *> ValueVec;
|
||
BF->foreachArgument([&](SPIRVFunctionParameter *Arg) {
|
||
ValueVec.push_back(ForeachFnArg(Arg));
|
||
});
|
||
Fn->setMetadata(MDName, MDNode::get(*Context, ValueVec));
|
||
}
|
||
|
||
static void addBufferLocationMetadata(
|
||
LLVMContext *Context, SPIRVFunction *BF, llvm::Function *Fn,
|
||
std::function<Metadata *(SPIRVFunctionParameter *)> ForeachFnArg) {
|
||
std::vector<Metadata *> ValueVec;
|
||
bool DecorationFound = false;
|
||
BF->foreachArgument([&](SPIRVFunctionParameter *Arg) {
|
||
if (Arg->getType()->isTypePointer() &&
|
||
Arg->hasDecorate(DecorationBufferLocationINTEL)) {
|
||
DecorationFound = true;
|
||
ValueVec.push_back(ForeachFnArg(Arg));
|
||
} else {
|
||
llvm::Metadata *DefaultNode = ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt32Ty(*Context), -1));
|
||
ValueVec.push_back(DefaultNode);
|
||
}
|
||
});
|
||
if (DecorationFound)
|
||
Fn->setMetadata("kernel_arg_buffer_location",
|
||
MDNode::get(*Context, ValueVec));
|
||
}
|
||
|
||
static void addRuntimeAlignedMetadata(
|
||
LLVMContext *Context, SPIRVFunction *BF, llvm::Function *Fn,
|
||
std::function<Metadata *(SPIRVFunctionParameter *)> ForeachFnArg) {
|
||
std::vector<Metadata *> ValueVec;
|
||
bool RuntimeAlignedFound = false;
|
||
[[maybe_unused]] llvm::Metadata *DefaultNode =
|
||
ConstantAsMetadata::get(ConstantInt::get(Type::getInt1Ty(*Context), 0));
|
||
BF->foreachArgument([&](SPIRVFunctionParameter *Arg) {
|
||
if (Arg->hasAttr(FunctionParameterAttributeRuntimeAlignedINTEL) ||
|
||
Arg->hasDecorate(internal::DecorationRuntimeAlignedINTEL)) {
|
||
RuntimeAlignedFound = true;
|
||
ValueVec.push_back(ForeachFnArg(Arg));
|
||
} else {
|
||
ValueVec.push_back(DefaultNode);
|
||
}
|
||
});
|
||
if (RuntimeAlignedFound)
|
||
Fn->setMetadata("kernel_arg_runtime_aligned",
|
||
MDNode::get(*Context, ValueVec));
|
||
}
|
||
|
||
Value *SPIRVToLLVM::getTranslatedValue(SPIRVValue *BV) {
|
||
auto Loc = ValueMap.find(BV);
|
||
if (Loc != ValueMap.end())
|
||
return Loc->second;
|
||
return nullptr;
|
||
}
|
||
|
||
static std::optional<llvm::Attribute>
|
||
translateSEVMetadata(SPIRVValue *BV, llvm::LLVMContext &Context) {
|
||
std::optional<llvm::Attribute> RetAttr;
|
||
|
||
if (!BV->hasDecorate(DecorationSingleElementVectorINTEL))
|
||
return RetAttr;
|
||
|
||
auto VecDecorateSEV = BV->getDecorations(DecorationSingleElementVectorINTEL);
|
||
assert(VecDecorateSEV.size() == 1 &&
|
||
"Entry must have no more than one SingleElementVectorINTEL "
|
||
"decoration");
|
||
auto *DecorateSEV = VecDecorateSEV.back();
|
||
auto LiteralCount = DecorateSEV->getLiteralCount();
|
||
assert(LiteralCount <= 1 && "SingleElementVectorINTEL decoration must "
|
||
"have no more than one literal");
|
||
|
||
SPIRVWord IndirectLevelsOnElement =
|
||
(LiteralCount == 1) ? DecorateSEV->getLiteral(0) : 0;
|
||
|
||
RetAttr = Attribute::get(Context, kVCMetadata::VCSingleElementVector,
|
||
std::to_string(IndirectLevelsOnElement));
|
||
return RetAttr;
|
||
}
|
||
|
||
IntrinsicInst *SPIRVToLLVM::getLifetimeStartIntrinsic(Instruction *I) {
|
||
auto *II = dyn_cast<IntrinsicInst>(I);
|
||
if (II && II->getIntrinsicID() == Intrinsic::lifetime_start)
|
||
return II;
|
||
// Bitcast might be inserted during translation of OpLifetimeStart
|
||
auto *BC = dyn_cast<BitCastInst>(I);
|
||
if (BC) {
|
||
for (const auto &U : BC->users()) {
|
||
II = dyn_cast<IntrinsicInst>(U);
|
||
if (II && II->getIntrinsicID() == Intrinsic::lifetime_start)
|
||
return II;
|
||
;
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
SPIRVErrorLog &SPIRVToLLVM::getErrorLog() { return BM->getErrorLog(); }
|
||
|
||
void SPIRVToLLVM::setCallingConv(CallInst *Call) {
|
||
Function *F = Call->getCalledFunction();
|
||
assert(F && "Function pointers are not allowed in SPIRV");
|
||
Call->setCallingConv(F->getCallingConv());
|
||
}
|
||
|
||
// 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()));
|
||
}
|
||
|
||
Value *SPIRVToLLVM::mapFunction(SPIRVFunction *BF, Function *F) {
|
||
SPIRVDBG(spvdbgs() << "[mapFunction] " << *BF << " -> ";
|
||
dbgs() << *F << '\n';)
|
||
FuncMap[BF] = F;
|
||
return F;
|
||
}
|
||
|
||
std::optional<uint64_t> SPIRVToLLVM::transIdAsConstant(SPIRVId Id) {
|
||
auto *V = BM->get<SPIRVValue>(Id);
|
||
const auto *ConstValue =
|
||
dyn_cast<ConstantInt>(transValue(V, nullptr, nullptr));
|
||
if (!ConstValue)
|
||
return {};
|
||
return ConstValue->getZExtValue();
|
||
}
|
||
|
||
std::optional<uint64_t> SPIRVToLLVM::getAlignment(SPIRVValue *V) {
|
||
SPIRVWord AlignmentBytes = 0;
|
||
if (V->hasAlignment(&AlignmentBytes)) {
|
||
return AlignmentBytes;
|
||
}
|
||
|
||
// If there was no Alignment decoration, look for AlignmentId instead.
|
||
SPIRVId AlignId;
|
||
if (V->hasDecorateId(DecorationAlignmentId, 0, &AlignId)) {
|
||
return transIdAsConstant(AlignId);
|
||
}
|
||
return {};
|
||
}
|
||
|
||
Type *SPIRVToLLVM::transFPType(SPIRVType *T) {
|
||
switch (T->getFloatBitWidth()) {
|
||
case 4:
|
||
// No LLVM IR counter part for FP4 - map it on i4.
|
||
return Type::getIntNTy(*Context, 4);
|
||
case 8:
|
||
// No LLVM IR counter part for FP8 - map it on i8.
|
||
return Type::getIntNTy(*Context, 8);
|
||
case 16:
|
||
if (T->isTypeFloat(16, FPEncodingBFloat16KHR))
|
||
return Type::getBFloatTy(*Context);
|
||
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::transVCTypeName(SPIRVTypeBufferSurfaceINTEL *PST) {
|
||
if (PST->hasAccessQualifier())
|
||
return VectorComputeUtil::getVCBufferSurfaceName(PST->getAccessQualifier());
|
||
return VectorComputeUtil::getVCBufferSurfaceName();
|
||
}
|
||
|
||
template <typename ImageType>
|
||
std::optional<SPIRVAccessQualifierKind> getAccessQualifier(ImageType *T) {
|
||
if (!T->hasAccessQualifier())
|
||
return {};
|
||
return T->getAccessQualifier();
|
||
}
|
||
|
||
Type *SPIRVToLLVM::transType(SPIRVType *T, bool UseTPT) {
|
||
// Try to reuse a known type if it's already matched. However, if we want to
|
||
// produce a TypedPointerType in lieu of a PointerType, we *do not* want to
|
||
// pull a PointerType out of the type map, nor do we want to store a
|
||
// TypedPointerType in there. This is generally safe to do, as types are
|
||
// usually uniqued by LLVM, but we need to be cautious around struct types.
|
||
auto Loc = TypeMap.find(T);
|
||
if (Loc != TypeMap.end() && !UseTPT)
|
||
return Loc->second;
|
||
|
||
SPIRVDBG(spvdbgs() << "[transType] " << *T << " -> ";)
|
||
T->validate();
|
||
switch (static_cast<SPIRVWord>(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: {
|
||
// The length might be an OpSpecConstantOp, that needs to be specialized
|
||
// and evaluated before the LLVM ArrayType can be constructed.
|
||
auto *LenExpr = static_cast<const SPIRVTypeArray *>(T)->getLength();
|
||
auto *LenValue = cast<ConstantInt>(transValue(LenExpr, nullptr, nullptr));
|
||
return mapType(T, ArrayType::get(transType(T->getArrayElementType()),
|
||
LenValue->getZExtValue()));
|
||
}
|
||
case internal::OpTypeTokenINTEL:
|
||
return mapType(T, Type::getTokenTy(*Context));
|
||
case OpTypePointer: {
|
||
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
|
||
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
|
||
AS = SPIRAS_Private;
|
||
if (BM->shouldEmitFunctionPtrAddrSpace() &&
|
||
T->getPointerElementType()->getOpCode() == OpTypeFunction)
|
||
AS = SPIRAS_CodeSectionINTEL;
|
||
Type *ElementTy = transType(T->getPointerElementType(), UseTPT);
|
||
if (UseTPT)
|
||
return TypedPointerType::get(ElementTy, AS);
|
||
return mapType(T, PointerType::get(*Context, AS));
|
||
}
|
||
case OpTypeUntypedPointerKHR: {
|
||
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
|
||
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
|
||
AS = SPIRAS_Private;
|
||
return mapType(T, PointerType::get(*Context, AS));
|
||
}
|
||
case OpTypeVector:
|
||
return mapType(T,
|
||
FixedVectorType::get(transType(T->getVectorComponentType()),
|
||
T->getVectorComponentCount()));
|
||
case OpTypeMatrix:
|
||
return mapType(T, ArrayType::get(transType(T->getMatrixColumnType()),
|
||
T->getMatrixColumnCount()));
|
||
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,
|
||
getSPIRVType(OpTypeImage, transType(ST->getSampledType()),
|
||
ST->getDescriptor(), getAccessQualifier(ST),
|
||
!UseTPT));
|
||
else
|
||
llvm_unreachable("Unsupported image type");
|
||
return nullptr;
|
||
}
|
||
case OpTypeSampledImage: {
|
||
const auto *ST = static_cast<SPIRVTypeSampledImage *>(T)->getImageType();
|
||
return mapType(
|
||
T, getSPIRVType(OpTypeSampledImage, transType(ST->getSampledType()),
|
||
ST->getDescriptor(), getAccessQualifier(ST), !UseTPT));
|
||
}
|
||
case OpTypeStruct: {
|
||
// We do not generate structs with any TypedPointerType members. To ensure
|
||
// that uniqueness of struct types is maintained, reuse an existing struct
|
||
// type in the type map, even if UseTPT is true.
|
||
if (Loc != TypeMap.end())
|
||
return Loc->second;
|
||
auto *ST = static_cast<SPIRVTypeStruct *>(T);
|
||
auto Name = ST->getName();
|
||
if (!Name.empty()) {
|
||
if (auto *OldST = StructType::getTypeByName(*Context, Name))
|
||
OldST->setName("");
|
||
} else {
|
||
Name = "structtype";
|
||
}
|
||
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)));
|
||
for (auto &CI : ST->getContinuedInstructions())
|
||
for (size_t I = 0, E = CI->getNumElements(); I != E; ++I)
|
||
MT.push_back(transType(CI->getMemberType(I)));
|
||
StructTy->setBody(MT, ST->isPacked());
|
||
return StructTy;
|
||
}
|
||
case OpTypePipe: {
|
||
auto *PT = static_cast<SPIRVTypePipe *>(T);
|
||
return mapType(T,
|
||
getSPIRVType(OpTypePipe, PT->getAccessQualifier(), !UseTPT));
|
||
}
|
||
case OpTypePipeStorage: {
|
||
StringRef FullName = "spirv.PipeStorage";
|
||
auto *STy = StructType::getTypeByName(*Context, FullName);
|
||
if (!STy)
|
||
STy = StructType::create(*Context, FullName);
|
||
if (UseTPT) {
|
||
return mapType(T, TypedPointerType::get(STy, 1));
|
||
}
|
||
return mapType(T, PointerType::get(*Context, 1));
|
||
}
|
||
case OpTypeVmeImageINTEL: {
|
||
auto *VT = static_cast<SPIRVTypeVmeImageINTEL *>(T)->getImageType();
|
||
return mapType(
|
||
T, getSPIRVType(OpTypeVmeImageINTEL, transType(VT->getSampledType()),
|
||
VT->getDescriptor(), getAccessQualifier(VT), !UseTPT));
|
||
}
|
||
case OpTypeBufferSurfaceINTEL: {
|
||
auto *PST = static_cast<SPIRVTypeBufferSurfaceINTEL *>(T);
|
||
Type *Ty = nullptr;
|
||
if (UseTPT) {
|
||
Type *StructTy = getOrCreateOpaqueStructType(M, transVCTypeName(PST));
|
||
Ty = TypedPointerType::get(StructTy, SPIRAS_Global);
|
||
} else {
|
||
std::vector<unsigned> Params;
|
||
if (PST->hasAccessQualifier()) {
|
||
unsigned Access = static_cast<unsigned>(PST->getAccessQualifier());
|
||
Params.push_back(Access);
|
||
}
|
||
Ty = TargetExtType::get(*Context, "spirv.BufferSurfaceINTEL", {}, Params);
|
||
}
|
||
return mapType(T, Ty);
|
||
}
|
||
case internal::OpTypeJointMatrixINTEL: {
|
||
auto *MT = static_cast<SPIRVTypeJointMatrixINTEL *>(T);
|
||
auto R = static_cast<SPIRVConstant *>(MT->getRows())->getZExtIntValue();
|
||
auto C = static_cast<SPIRVConstant *>(MT->getColumns())->getZExtIntValue();
|
||
std::vector<unsigned> Params = {(unsigned)R, (unsigned)C};
|
||
if (auto *Layout = MT->getLayout())
|
||
Params.push_back(static_cast<SPIRVConstant *>(Layout)->getZExtIntValue());
|
||
Params.push_back(
|
||
static_cast<SPIRVConstant *>(MT->getScope())->getZExtIntValue());
|
||
if (auto *Use = MT->getUse())
|
||
Params.push_back(static_cast<SPIRVConstant *>(Use)->getZExtIntValue());
|
||
auto *CTI = MT->getComponentTypeInterpretation();
|
||
if (!CTI)
|
||
return mapType(
|
||
T, llvm::TargetExtType::get(*Context, "spirv.JointMatrixINTEL",
|
||
transType(MT->getCompType()), Params));
|
||
const unsigned CTIValue =
|
||
static_cast<SPIRVConstant *>(CTI)->getZExtIntValue();
|
||
assert(CTIValue <= internal::InternalJointMatrixCTI::PackedInt4 &&
|
||
"Unknown matrix component type interpretation");
|
||
Params.push_back(CTIValue);
|
||
return mapType(
|
||
T, llvm::TargetExtType::get(*Context, "spirv.JointMatrixINTEL",
|
||
transType(MT->getCompType()), Params));
|
||
}
|
||
case OpTypeCooperativeMatrixKHR: {
|
||
auto *MT = static_cast<SPIRVTypeCooperativeMatrixKHR *>(T);
|
||
unsigned Scope =
|
||
static_cast<SPIRVConstant *>(MT->getScope())->getZExtIntValue();
|
||
unsigned Rows =
|
||
static_cast<SPIRVConstant *>(MT->getRows())->getZExtIntValue();
|
||
unsigned Cols =
|
||
static_cast<SPIRVConstant *>(MT->getColumns())->getZExtIntValue();
|
||
unsigned Use =
|
||
static_cast<SPIRVConstant *>(MT->getUse())->getZExtIntValue();
|
||
|
||
std::vector<unsigned> Params = {Scope, Rows, Cols, Use};
|
||
return mapType(
|
||
T, llvm::TargetExtType::get(*Context, "spirv.CooperativeMatrixKHR",
|
||
transType(MT->getCompType()), Params));
|
||
}
|
||
case OpTypeForwardPointer: {
|
||
SPIRVTypeForwardPointer *FP =
|
||
static_cast<SPIRVTypeForwardPointer *>(static_cast<SPIRVEntry *>(T));
|
||
return mapType(T, transType(static_cast<SPIRVType *>(
|
||
BM->getEntry(FP->getPointerId()))));
|
||
}
|
||
case internal::OpTypeTaskSequenceINTEL:
|
||
return mapType(
|
||
T, llvm::TargetExtType::get(*Context, "spirv.TaskSequenceINTEL"));
|
||
|
||
default: {
|
||
auto OC = T->getOpCode();
|
||
if (isOpaqueGenericTypeOpCode(OC) || isSubgroupAvcINTELTypeOpCode(OC)) {
|
||
return mapType(T, getSPIRVType(OC, !UseTPT));
|
||
}
|
||
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:
|
||
// Arbitrary precision integer
|
||
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: {
|
||
SPIRVType *ET = T->getPointerElementType();
|
||
if (isa<OpTypeFunction>(ET)) {
|
||
SPIRVTypeFunction *TF = static_cast<SPIRVTypeFunction *>(ET);
|
||
std::string name = transTypeToOCLTypeName(TF->getReturnType());
|
||
name += " (*)(";
|
||
for (unsigned I = 0, E = TF->getNumParameters(); I < E; ++I)
|
||
name += transTypeToOCLTypeName(TF->getParameterType(I)) + ',';
|
||
name.back() = ')'; // replace the last comma with a closing brace.
|
||
return name;
|
||
}
|
||
return transTypeToOCLTypeName(ET) + "*";
|
||
}
|
||
case OpTypeUntypedPointerKHR:
|
||
return "int*";
|
||
case OpTypeVector:
|
||
return transTypeToOCLTypeName(T->getVectorComponentType()) +
|
||
T->getVectorComponentCount();
|
||
case OpTypeMatrix:
|
||
return transTypeToOCLTypeName(T->getMatrixColumnType()) +
|
||
T->getMatrixColumnCount();
|
||
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());
|
||
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, bool UseTPT) {
|
||
std::vector<Type *> T;
|
||
for (auto *I : BT)
|
||
T.push_back(transType(I, UseTPT));
|
||
return T;
|
||
}
|
||
|
||
static Type *opaquifyType(Type *Ty) {
|
||
if (auto *TPT = dyn_cast<TypedPointerType>(Ty)) {
|
||
Ty = PointerType::get(Ty->getContext(), TPT->getAddressSpace());
|
||
}
|
||
return Ty;
|
||
}
|
||
|
||
static void opaquifyTypedPointers(MutableArrayRef<Type *> Types) {
|
||
for (Type *&Ty : Types) {
|
||
Ty = opaquifyType(Ty);
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
void SPIRVToLLVM::setName(llvm::Value *V, SPIRVValue *BV) {
|
||
auto Name = BV->getName();
|
||
if (!Name.empty() && (!V->hasName() || Name != V->getName()))
|
||
V->setName(Name);
|
||
}
|
||
|
||
inline llvm::Metadata *SPIRVToLLVM::getMetadataFromName(std::string Name) {
|
||
return llvm::MDNode::get(*Context, llvm::MDString::get(*Context, Name));
|
||
}
|
||
|
||
inline std::vector<llvm::Metadata *>
|
||
SPIRVToLLVM::getMetadataFromNameAndParameter(std::string Name,
|
||
SPIRVWord Parameter) {
|
||
return {MDString::get(*Context, Name),
|
||
ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt32Ty(*Context), Parameter))};
|
||
}
|
||
|
||
inline llvm::MDNode *
|
||
SPIRVToLLVM::getMetadataFromNameAndParameter(std::string Name,
|
||
int64_t Parameter) {
|
||
std::vector<llvm::Metadata *> Metadata = {
|
||
MDString::get(*Context, Name),
|
||
ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt64Ty(*Context), Parameter))};
|
||
return llvm::MDNode::get(*Context, Metadata);
|
||
}
|
||
|
||
template <typename LoopInstType>
|
||
void SPIRVToLLVM::setLLVMLoopMetadata(const LoopInstType *LM,
|
||
const Loop *LoopObj) {
|
||
if (!LM)
|
||
return;
|
||
|
||
auto Temp = MDNode::getTemporary(*Context, {});
|
||
auto *Self = MDNode::get(*Context, Temp.get());
|
||
Self->replaceOperandWith(0, Self);
|
||
SPIRVWord LC = LM->getLoopControl();
|
||
if (LC == LoopControlMaskNone) {
|
||
LoopObj->setLoopID(Self);
|
||
return;
|
||
}
|
||
|
||
unsigned NumParam = 0;
|
||
std::vector<llvm::Metadata *> Metadata;
|
||
std::vector<SPIRVWord> LoopControlParameters = LM->getLoopControlParameters();
|
||
Metadata.push_back(llvm::MDNode::get(*Context, Self));
|
||
|
||
// To correctly decode loop control parameters, order of checks for loop
|
||
// control masks must match with the order given in the spec (see 3.23),
|
||
// i.e. check smaller-numbered bits first.
|
||
// Unroll and UnrollCount loop controls can't be applied simultaneously with
|
||
// DontUnroll loop control.
|
||
if (LC & LoopControlUnrollMask && !(LC & LoopControlPartialCountMask))
|
||
Metadata.push_back(getMetadataFromName("llvm.loop.unroll.enable"));
|
||
else if (LC & LoopControlDontUnrollMask)
|
||
Metadata.push_back(getMetadataFromName("llvm.loop.unroll.disable"));
|
||
if (LC & LoopControlDependencyInfiniteMask)
|
||
Metadata.push_back(getMetadataFromName("llvm.loop.ivdep.enable"));
|
||
if (LC & LoopControlDependencyLengthMask) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context,
|
||
getMetadataFromNameAndParameter("llvm.loop.ivdep.safelen",
|
||
LoopControlParameters[NumParam])));
|
||
++NumParam;
|
||
// TODO: Fix the increment/assertion logic in all of the conditions
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
// Placeholder for LoopControls added in SPIR-V 1.4 spec (see 3.23)
|
||
if (LC & LoopControlMinIterationsMask) {
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlMaxIterationsMask) {
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlIterationMultipleMask) {
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlPeelCountMask) {
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlPartialCountMask && !(LC & LoopControlDontUnrollMask)) {
|
||
// If unroll factor is set as '1' and Unroll mask is applied attempt to do
|
||
// full unrolling and disable it if the trip count is not known at compile
|
||
// time.
|
||
if (1 == LoopControlParameters[NumParam] && (LC & LoopControlUnrollMask))
|
||
Metadata.push_back(getMetadataFromName("llvm.loop.unroll.full"));
|
||
else
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context,
|
||
getMetadataFromNameAndParameter("llvm.loop.unroll.count",
|
||
LoopControlParameters[NumParam])));
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlInitiationIntervalINTELMask) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context, getMetadataFromNameAndParameter(
|
||
"llvm.loop.ii.count", LoopControlParameters[NumParam])));
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlMaxConcurrencyINTELMask) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context,
|
||
getMetadataFromNameAndParameter("llvm.loop.max_concurrency.count",
|
||
LoopControlParameters[NumParam])));
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlDependencyArrayINTELMask) {
|
||
// Collect pointer variable <-> safelen information
|
||
std::unordered_map<Value *, unsigned> PointerSflnMap;
|
||
unsigned NumOperandPairs = LoopControlParameters[NumParam];
|
||
unsigned OperandsEndIndex = NumParam + NumOperandPairs * 2;
|
||
assert(OperandsEndIndex <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
SPIRVModule *M = LM->getModule();
|
||
while (NumParam < OperandsEndIndex) {
|
||
SPIRVId ArraySPIRVId = LoopControlParameters[++NumParam];
|
||
Value *PointerVar = ValueMap[M->getValue(ArraySPIRVId)];
|
||
unsigned Safelen = LoopControlParameters[++NumParam];
|
||
PointerSflnMap.emplace(PointerVar, Safelen);
|
||
}
|
||
|
||
// A single run over the loop to retrieve all GetElementPtr instructions
|
||
// that access relevant array variables
|
||
std::unordered_map<Value *, std::vector<GetElementPtrInst *>> ArrayGEPMap;
|
||
for (const auto &BB : LoopObj->blocks()) {
|
||
for (Instruction &I : *BB) {
|
||
auto *GEP = dyn_cast<GetElementPtrInst>(&I);
|
||
if (!GEP)
|
||
continue;
|
||
|
||
Value *AccessedPointer = GEP->getPointerOperand();
|
||
if (auto *BC = dyn_cast<CastInst>(AccessedPointer))
|
||
if (BC->getSrcTy() == BC->getDestTy())
|
||
AccessedPointer = BC->getOperand(0);
|
||
if (auto *LI = dyn_cast<LoadInst>(AccessedPointer))
|
||
AccessedPointer = LI->getPointerOperand();
|
||
auto PointerSflnIt = PointerSflnMap.find(AccessedPointer);
|
||
if (PointerSflnIt != PointerSflnMap.end()) {
|
||
ArrayGEPMap[AccessedPointer].push_back(GEP);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Create index group metadata nodes - one per each of the array
|
||
// variables. Mark each GEP accessing a particular array variable
|
||
// into a corresponding index group
|
||
std::map<unsigned, SmallSet<MDNode *, 4>> SafelenIdxGroupMap;
|
||
// Whenever a kernel closure field access is pointed to instead of
|
||
// an array/pointer variable, ensure that all GEPs to that memory
|
||
// share the same index group by hashing the newly added index groups.
|
||
// "Memory offset info" represents a handle to the whole closure block
|
||
// + an integer offset to a particular captured parameter.
|
||
using MemoryOffsetInfo = std::pair<Value *, unsigned>;
|
||
std::map<MemoryOffsetInfo, MDNode *> OffsetIdxGroupMap;
|
||
|
||
for (auto &ArrayGEPIt : ArrayGEPMap) {
|
||
MDNode *CurrentDepthIdxGroup = nullptr;
|
||
if (auto *PrecedingGEP = dyn_cast<GetElementPtrInst>(ArrayGEPIt.first)) {
|
||
Value *ClosureFieldPointer = PrecedingGEP->getPointerOperand();
|
||
unsigned Offset =
|
||
cast<ConstantInt>(PrecedingGEP->getOperand(2))->getZExtValue();
|
||
MemoryOffsetInfo Info{ClosureFieldPointer, Offset};
|
||
auto OffsetIdxGroupIt = OffsetIdxGroupMap.find(Info);
|
||
if (OffsetIdxGroupIt == OffsetIdxGroupMap.end()) {
|
||
// This is the first GEP encountered for this closure field.
|
||
// Emit a distinct index group that will be referenced from
|
||
// llvm.loop.parallel_access_indices metadata; hash the new
|
||
// MDNode for future accesses to the same memory.
|
||
CurrentDepthIdxGroup = llvm::MDNode::getDistinct(*Context, {});
|
||
OffsetIdxGroupMap.emplace(Info, CurrentDepthIdxGroup);
|
||
} else {
|
||
// Previous accesses to that field have already been indexed,
|
||
// just use the already-existing metadata.
|
||
CurrentDepthIdxGroup = OffsetIdxGroupIt->second;
|
||
}
|
||
} else /* Regular kernel-scope array/pointer variable */ {
|
||
// Emit a distinct index group that will be referenced from
|
||
// llvm.loop.parallel_access_indices metadata
|
||
CurrentDepthIdxGroup = llvm::MDNode::getDistinct(*Context, {});
|
||
}
|
||
|
||
unsigned Safelen = PointerSflnMap.find(ArrayGEPIt.first)->second;
|
||
SafelenIdxGroupMap[Safelen].insert(CurrentDepthIdxGroup);
|
||
for (auto *GEP : ArrayGEPIt.second) {
|
||
StringRef IdxGroupMDName("llvm.index.group");
|
||
llvm::MDNode *PreviousIdxGroup = GEP->getMetadata(IdxGroupMDName);
|
||
if (!PreviousIdxGroup) {
|
||
GEP->setMetadata(IdxGroupMDName, CurrentDepthIdxGroup);
|
||
continue;
|
||
}
|
||
|
||
// If we're dealing with an embedded loop, it may be the case
|
||
// that GEP instructions for some of the arrays were already
|
||
// marked by the algorithm when it went over the outer level loops.
|
||
// In order to retain the IVDep information for each "loop
|
||
// dimension", we will mark such GEP's into a separate joined node
|
||
// that will refer to the previous levels' index groups AND to the
|
||
// index group specific to the current loop.
|
||
std::vector<llvm::Metadata *> CurrentDepthOperands(
|
||
PreviousIdxGroup->op_begin(), PreviousIdxGroup->op_end());
|
||
if (CurrentDepthOperands.empty())
|
||
CurrentDepthOperands.push_back(PreviousIdxGroup);
|
||
CurrentDepthOperands.push_back(CurrentDepthIdxGroup);
|
||
auto *JointIdxGroup = llvm::MDNode::get(*Context, CurrentDepthOperands);
|
||
GEP->setMetadata(IdxGroupMDName, JointIdxGroup);
|
||
}
|
||
}
|
||
|
||
for (auto &SflnIdxGroupIt : SafelenIdxGroupMap) {
|
||
auto *Name = MDString::get(*Context, "llvm.loop.parallel_access_indices");
|
||
unsigned SflnValue = SflnIdxGroupIt.first;
|
||
llvm::Metadata *SafelenMDOp =
|
||
SflnValue ? ConstantAsMetadata::get(ConstantInt::get(
|
||
Type::getInt32Ty(*Context), SflnValue))
|
||
: nullptr;
|
||
std::vector<llvm::Metadata *> Parameters{Name};
|
||
for (auto *Node : SflnIdxGroupIt.second)
|
||
Parameters.push_back(Node);
|
||
if (SafelenMDOp)
|
||
Parameters.push_back(SafelenMDOp);
|
||
Metadata.push_back(llvm::MDNode::get(*Context, Parameters));
|
||
}
|
||
++NumParam;
|
||
}
|
||
if (LC & LoopControlPipelineEnableINTELMask) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context,
|
||
getMetadataFromNameAndParameter("llvm.loop.intel.pipelining.enable",
|
||
LoopControlParameters[NumParam++])));
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlLoopCoalesceINTELMask) {
|
||
// If LoopCoalesce has a parameter of '0'
|
||
if (!LoopControlParameters[NumParam]) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context, getMetadataFromName("llvm.loop.coalesce.enable")));
|
||
} else {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context,
|
||
getMetadataFromNameAndParameter("llvm.loop.coalesce.count",
|
||
LoopControlParameters[NumParam])));
|
||
}
|
||
++NumParam;
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlMaxInterleavingINTELMask) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context,
|
||
getMetadataFromNameAndParameter("llvm.loop.max_interleaving.count",
|
||
LoopControlParameters[NumParam++])));
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlSpeculatedIterationsINTELMask) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context, getMetadataFromNameAndParameter(
|
||
"llvm.loop.intel.speculated.iterations.count",
|
||
LoopControlParameters[NumParam++])));
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
if (LC & LoopControlNoFusionINTELMask)
|
||
Metadata.push_back(getMetadataFromName("llvm.loop.fusion.disable"));
|
||
if (LC & spv::LoopControlLoopCountINTELMask) {
|
||
// LoopCountINTELMask parameters are int64 and each parameter is stored
|
||
// as 2 SPIRVWords (int32)
|
||
assert(NumParam + 6 <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
|
||
uint64_t LoopCountMin =
|
||
static_cast<uint64_t>(LoopControlParameters[NumParam++]);
|
||
LoopCountMin |= static_cast<uint64_t>(LoopControlParameters[NumParam++])
|
||
<< 32;
|
||
if (static_cast<int64_t>(LoopCountMin) >= 0) {
|
||
Metadata.push_back(getMetadataFromNameAndParameter(
|
||
"llvm.loop.intel.loopcount_min", static_cast<int64_t>(LoopCountMin)));
|
||
}
|
||
|
||
uint64_t LoopCountMax =
|
||
static_cast<uint64_t>(LoopControlParameters[NumParam++]);
|
||
LoopCountMax |= static_cast<uint64_t>(LoopControlParameters[NumParam++])
|
||
<< 32;
|
||
if (static_cast<int64_t>(LoopCountMax) >= 0) {
|
||
Metadata.push_back(getMetadataFromNameAndParameter(
|
||
"llvm.loop.intel.loopcount_max", static_cast<int64_t>(LoopCountMax)));
|
||
}
|
||
|
||
uint64_t LoopCountAvg =
|
||
static_cast<uint64_t>(LoopControlParameters[NumParam++]);
|
||
LoopCountAvg |= static_cast<uint64_t>(LoopControlParameters[NumParam++])
|
||
<< 32;
|
||
if (static_cast<int64_t>(LoopCountAvg) >= 0) {
|
||
Metadata.push_back(getMetadataFromNameAndParameter(
|
||
"llvm.loop.intel.loopcount_avg", static_cast<int64_t>(LoopCountAvg)));
|
||
}
|
||
}
|
||
if (LC & spv::LoopControlMaxReinvocationDelayINTELMask) {
|
||
Metadata.push_back(llvm::MDNode::get(
|
||
*Context, getMetadataFromNameAndParameter(
|
||
"llvm.loop.intel.max_reinvocation_delay.count",
|
||
LoopControlParameters[NumParam++])));
|
||
assert(NumParam <= LoopControlParameters.size() &&
|
||
"Missing loop control parameter!");
|
||
}
|
||
llvm::MDNode *Node = llvm::MDNode::get(*Context, Metadata);
|
||
|
||
// Set the first operand to refer itself
|
||
Node->replaceOperandWith(0, Node);
|
||
LoopObj->setLoopID(Node);
|
||
}
|
||
|
||
void SPIRVToLLVM::transLLVMLoopMetadata(const Function *F) {
|
||
assert(F);
|
||
|
||
if (FuncLoopMetadataMap.empty())
|
||
return;
|
||
|
||
// Function declaration doesn't contain loop metadata.
|
||
if (F->isDeclaration())
|
||
return;
|
||
|
||
DominatorTree DomTree(*(const_cast<Function *>(F)));
|
||
LoopInfo LI(DomTree);
|
||
|
||
// In SPIRV loop metadata is linked to a header basic block of a loop
|
||
// whilst in LLVM IR it is linked to a latch basic block (the one
|
||
// whose back edge goes to a header basic block) of the loop.
|
||
// To ensure consistent behaviour, we can rely on the `llvm::Loop`
|
||
// class to handle the metadata placement
|
||
for (const auto *LoopObj : LI.getLoopsInPreorder()) {
|
||
// Check that loop header BB contains loop metadata.
|
||
const auto LMDItr = FuncLoopMetadataMap.find(LoopObj->getHeader());
|
||
if (LMDItr == FuncLoopMetadataMap.end())
|
||
continue;
|
||
|
||
const auto *LMD = LMDItr->second;
|
||
if (LMD->getOpCode() == OpLoopMerge) {
|
||
const auto *LM = static_cast<const SPIRVLoopMerge *>(LMD);
|
||
setLLVMLoopMetadata<SPIRVLoopMerge>(LM, LoopObj);
|
||
} else if (LMD->getOpCode() == OpLoopControlINTEL) {
|
||
const auto *LCI = static_cast<const SPIRVLoopControlINTEL *>(LMD);
|
||
setLLVMLoopMetadata<SPIRVLoopControlINTEL>(LCI, LoopObj);
|
||
}
|
||
|
||
FuncLoopMetadataMap.erase(LMDItr);
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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();
|
||
|
||
auto GetFPEncoding = [](SPIRVType *Ty) -> FPEncodingWrap {
|
||
if (Ty->isTypeFloat()) {
|
||
unsigned Enc =
|
||
static_cast<SPIRVTypeFloat *>(Ty)->getFloatingPointEncoding();
|
||
return static_cast<FPEncodingWrap>(Enc);
|
||
}
|
||
if (Ty->isTypeInt())
|
||
return FPEncodingWrap::Integer;
|
||
return FPEncodingWrap::IEEE754;
|
||
};
|
||
|
||
auto IsFP4OrFP8Encoding = [](FPEncodingWrap Encoding) -> bool {
|
||
return Encoding == FPEncodingWrap::E4M3 ||
|
||
Encoding == FPEncodingWrap::E5M2 ||
|
||
Encoding == FPEncodingWrap::E2M1 ||
|
||
Encoding == FPEncodingWrap::E2M1INTEL;
|
||
};
|
||
|
||
switch (static_cast<unsigned>(BC->getOpCode())) {
|
||
case OpPtrCastToGeneric:
|
||
case OpGenericCastToPtr:
|
||
case OpPtrCastToCrossWorkgroupINTEL:
|
||
case OpCrossWorkgroupCastToPtrINTEL: {
|
||
// If module has pointers with DeviceOnlyINTEL and HostOnlyINTEL storage
|
||
// classes there will be a situation, when global_device/global_host
|
||
// address space will be lowered to just global address space. If there also
|
||
// is an addrspacecast - we need to replace it with source pointer.
|
||
if (Src->getType()->getPointerAddressSpace() ==
|
||
Dst->getPointerAddressSpace())
|
||
return Src;
|
||
CO = Instruction::AddrSpaceCast;
|
||
break;
|
||
}
|
||
case OpSConvert:
|
||
CO = IsExt ? Instruction::SExt : Instruction::Trunc;
|
||
break;
|
||
case OpUConvert:
|
||
CO = IsExt ? Instruction::ZExt : Instruction::Trunc;
|
||
break;
|
||
case internal::OpClampConvertFToFINTEL:
|
||
case internal::OpClampConvertFToSINTEL:
|
||
case internal::OpStochasticRoundFToFINTEL:
|
||
case internal::OpClampStochasticRoundFToFINTEL:
|
||
case internal::OpClampStochasticRoundFToSINTEL:
|
||
case OpConvertSToF:
|
||
case OpConvertFToS:
|
||
case OpConvertUToF:
|
||
case OpConvertFToU:
|
||
case OpFConvert: {
|
||
const auto OC = BC->getOpCode();
|
||
// These 2 old opcodes should follow exactly the same translation
|
||
// path as OpFConvert/OpStochasticRoundFToFINTEL with
|
||
// SaturatedToLargestFloat8NormalConversionEXT
|
||
const bool IsOldConvertFToFOp =
|
||
OC == internal::OpClampConvertFToFINTEL ||
|
||
OC == internal::OpClampStochasticRoundFToFINTEL;
|
||
{
|
||
auto SPVOps = BC->getOperands();
|
||
auto *SPVSrcTy = SPVOps[0]->getType();
|
||
auto *SPVDstTy = BC->getType();
|
||
|
||
auto GetEncodingAndUpdateType =
|
||
[GetFPEncoding](SPIRVType *&SPVTy) -> FPEncodingWrap {
|
||
if (SPVTy->isTypeVector()) {
|
||
SPVTy = SPVTy->getVectorComponentType();
|
||
} else if (SPVTy->isTypeCooperativeMatrixKHR()) {
|
||
auto *MT = static_cast<SPIRVTypeCooperativeMatrixKHR *>(SPVTy);
|
||
SPVTy = MT->getCompType();
|
||
}
|
||
return GetFPEncoding(SPVTy);
|
||
};
|
||
|
||
FPEncodingWrap SrcEnc = GetEncodingAndUpdateType(SPVSrcTy);
|
||
FPEncodingWrap DstEnc = GetEncodingAndUpdateType(SPVDstTy);
|
||
const bool HasSaturatedFP8Decor = BC->hasDecorate(
|
||
DecorationSaturatedToLargestFloat8NormalConversionEXT);
|
||
bool IsSaturatedFP8 = false;
|
||
if (IsOldConvertFToFOp) {
|
||
BM->getErrorLog().checkError(
|
||
!HasSaturatedFP8Decor, SPIRVEC_InvalidInstruction,
|
||
"SaturatedToLargestFloat8NormalConversionEXT is not valid on "
|
||
"OpClampConvertFToFINTEL or OpClampStochasticRoundFToFINTEL.\n");
|
||
IsSaturatedFP8 =
|
||
DstEnc == FPEncodingWrap::E4M3 || DstEnc == FPEncodingWrap::E5M2;
|
||
} else if (HasSaturatedFP8Decor) {
|
||
BM->getErrorLog().checkError(
|
||
(OC == OpFConvert || OC == OpConvertSToF || OC == OpConvertUToF ||
|
||
OC == internal::OpStochasticRoundFToFINTEL) &&
|
||
(DstEnc == FPEncodingWrap::E4M3 ||
|
||
DstEnc == FPEncodingWrap::E5M2),
|
||
SPIRVEC_InvalidInstruction,
|
||
"SaturatedToLargestFloat8NormalConversionEXT decoration is only "
|
||
"valid on OpFConvert/OpConvertSToF/OpConvertUToF/"
|
||
"OpStochasticRoundFToFINTEL whose Result Type uses Float8E4M3EXT "
|
||
"or Float8E5M2EXT encoding.\n");
|
||
IsSaturatedFP8 = true;
|
||
}
|
||
if (IsFP4OrFP8Encoding(SrcEnc) || IsFP4OrFP8Encoding(DstEnc) ||
|
||
SPVSrcTy->isTypeInt(4) || SPVDstTy->isTypeInt(4)) {
|
||
// The old opcodes share the encoding map with their surviving
|
||
// equivalents: OpClampConvertFToFINTEL with OpFConvert and
|
||
// OpClampStochasticRoundFToFINTEL with OpStochasticRoundFToFINTEL.
|
||
SPIRVWord LookupOC = OC;
|
||
if (OC == internal::OpClampConvertFToFINTEL)
|
||
LookupOC = OpFConvert;
|
||
else if (OC == internal::OpClampStochasticRoundFToFINTEL)
|
||
LookupOC = internal::OpStochasticRoundFToFINTEL;
|
||
FPEncodingWrap LookupDstEnc = DstEnc;
|
||
if (LookupOC == internal::OpStochasticRoundFToFINTEL &&
|
||
DstEnc == FPEncodingWrap::E2M1INTEL)
|
||
LookupDstEnc = FPEncodingWrap::E2M1;
|
||
FPConversionDesc FPDesc = {SrcEnc, LookupDstEnc, LookupOC,
|
||
/*Saturate=*/IsSaturatedFP8};
|
||
auto Conv = SPIRV::FPConvertToEncodingMap::rmap(FPDesc);
|
||
std::vector<Value *> Ops = {Src};
|
||
std::vector<Type *> OpsTys = {Src->getType()};
|
||
|
||
std::string BuiltinName =
|
||
kSPIRVName::InternalBuiltinPrefix + std::string(Conv);
|
||
BuiltinFuncMangleInfo Info;
|
||
std::string MangledName;
|
||
// Translate additional Ops for stochastic conversions.
|
||
if (OC == internal::OpStochasticRoundFToFINTEL ||
|
||
OC == internal::OpClampStochasticRoundFToFINTEL ||
|
||
OC == internal::OpClampStochasticRoundFToSINTEL) {
|
||
// Seed.
|
||
Ops.emplace_back(transValue(SPVOps[1], F, BB, true));
|
||
OpsTys.emplace_back(Ops[1]->getType());
|
||
constexpr unsigned MaxOpsSize = 3;
|
||
if (SPVOps.size() == MaxOpsSize) {
|
||
// New Seed.
|
||
Ops.emplace_back(transValue(SPVOps[2], F, BB, true));
|
||
|
||
// The following mess is needed to create a function with correct
|
||
// mangling.
|
||
SPIRVType *PtrTy = SPVOps[2]->getType();
|
||
const unsigned AS =
|
||
SPIRSPIRVAddrSpaceMap::rmap(PtrTy->getPointerStorageClass());
|
||
Type *ElementTy = transType(PtrTy->getPointerElementType());
|
||
OpsTys.emplace_back(TypedPointerType::get(ElementTy, AS));
|
||
MangledName = mangleBuiltin(BuiltinName, OpsTys, &Info);
|
||
// But to create function itself we need untyped pointer type.
|
||
OpsTys[2] = opaquifyType(OpsTys[2]);
|
||
}
|
||
}
|
||
|
||
if (MangledName.empty())
|
||
MangledName = mangleBuiltin(BuiltinName, OpsTys, &Info);
|
||
|
||
Function *Func = getOrCreateFunction(M, Dst, OpsTys, MangledName);
|
||
auto *CI = CallInst::Create(Func, Ops, "", BB);
|
||
CI->setCallingConv(CallingConv::SPIR_FUNC);
|
||
return CI;
|
||
}
|
||
}
|
||
// OpStochasticRoundFToFINTEL and the old OpClampConvertFToFINTEL /
|
||
// OpClampStochasticRoundFToFINTEL opcodes have no native LLVM cast
|
||
// equivalent. For fp4/fp8/int4 types, they are handled via the
|
||
// __builtin_spirv path above. For the remaining types they are emitted as
|
||
// an __spirv_<OpName>_R<type> builtin call.
|
||
if (OC == internal::OpStochasticRoundFToFINTEL || IsOldConvertFToFOp)
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(
|
||
static_cast<SPIRVInstruction *>(BV), BB));
|
||
|
||
if (OC == OpFConvert) {
|
||
CO = IsExt ? Instruction::FPExt : Instruction::FPTrunc;
|
||
break;
|
||
}
|
||
CO = static_cast<CastInst::CastOps>(OpCodeMap::rmap(OC));
|
||
break;
|
||
}
|
||
case OpBitcast:
|
||
if (!Dst->isPointerTy() && Dst == Src->getType())
|
||
return Src;
|
||
// OpBitcast need to be handled as a special-case when the source is a
|
||
// pointer and the destination is not a pointer, and where the source is not
|
||
// a pointer and the destination is a pointer. This is supported by the
|
||
// SPIR-V bitcast, but not by the LLVM bitcast.
|
||
CO = Instruction::BitCast;
|
||
if (Src->getType()->isPointerTy() && !Dst->isPointerTy()) {
|
||
if (auto *DstVecTy = dyn_cast<FixedVectorType>(Dst)) {
|
||
unsigned TotalBitWidth =
|
||
DstVecTy->getElementType()->getIntegerBitWidth() *
|
||
DstVecTy->getNumElements();
|
||
auto *IntTy = Type::getIntNTy(Src->getContext(), TotalBitWidth);
|
||
if (BB) {
|
||
Src = CastInst::CreatePointerCast(Src, IntTy, "", BB);
|
||
} else {
|
||
Src = ConstantExpr::getPointerCast(dyn_cast<Constant>(Src), IntTy);
|
||
}
|
||
} else {
|
||
CO = Instruction::PtrToInt;
|
||
}
|
||
} else if (!Src->getType()->isPointerTy() && Dst->isPointerTy()) {
|
||
if (auto *SrcVecTy = dyn_cast<FixedVectorType>(Src->getType())) {
|
||
unsigned TotalBitWidth =
|
||
SrcVecTy->getElementType()->getIntegerBitWidth() *
|
||
SrcVecTy->getNumElements();
|
||
auto *IntTy = Type::getIntNTy(Src->getContext(), TotalBitWidth);
|
||
if (BB) {
|
||
Src = CastInst::Create(Instruction::BitCast, Src, IntTy, "", BB);
|
||
} else {
|
||
Src = ConstantExpr::getBitCast(dyn_cast<Constant>(Src), IntTy);
|
||
}
|
||
}
|
||
CO = Instruction::IntToPtr;
|
||
}
|
||
break;
|
||
// Workaround: OpConvertPtrToU on a target extension type (e.g. spirv.Image)
|
||
// is invalid SPIR-V, but is generated by the DPC++ frontend for ESIMD
|
||
// get_surface_index() on image accessors.
|
||
// Translate as a builtin call (valid LLVM IR) instead of PtrToInt (invalid
|
||
// IR). Removing this requires fixing the root cause: either a new SPIR-V
|
||
// extension for image-handle-to-index conversion, or redesigning ESIMD
|
||
// accessor storage.
|
||
case OpConvertPtrToU: {
|
||
if (Src->getType()->isTargetExtTy()) {
|
||
if (BM->getExtension().count("SPV_INTEL_vector_compute"))
|
||
return transSPIRVBuiltinFromInst(BC, BB);
|
||
BM->getErrorLog().checkError(false, SPIRVEC_InvalidInstruction,
|
||
"OpConvertPtrToU on a target extension type "
|
||
"requires SPV_INTEL_vector_compute");
|
||
}
|
||
[[fallthrough]];
|
||
}
|
||
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);
|
||
}
|
||
|
||
static void applyNoIntegerWrapDecorations(const SPIRVValue *BV,
|
||
Instruction *Inst) {
|
||
if (BV->hasDecorate(DecorationNoSignedWrap)) {
|
||
Inst->setHasNoSignedWrap(true);
|
||
}
|
||
|
||
if (BV->hasDecorate(DecorationNoUnsignedWrap)) {
|
||
Inst->setHasNoUnsignedWrap(true);
|
||
}
|
||
}
|
||
|
||
static void applyFPFastMathModeDecorations(const SPIRVValue *BV,
|
||
Instruction *Inst) {
|
||
SPIRVWord V;
|
||
FastMathFlags FMF;
|
||
if (BV->hasDecorate(DecorationFPFastMathMode, 0, &V)) {
|
||
if (V & FPFastMathModeNotNaNMask)
|
||
FMF.setNoNaNs();
|
||
if (V & FPFastMathModeNotInfMask)
|
||
FMF.setNoInfs();
|
||
if (V & FPFastMathModeNSZMask)
|
||
FMF.setNoSignedZeros();
|
||
if (V & FPFastMathModeAllowRecipMask)
|
||
FMF.setAllowReciprocal();
|
||
if (V & FPFastMathModeAllowContractFastINTELMask)
|
||
FMF.setAllowContract();
|
||
if (V & FPFastMathModeAllowReassocINTELMask)
|
||
FMF.setAllowReassoc();
|
||
if (V & FPFastMathModeFastMask)
|
||
FMF.setFast();
|
||
Inst->setFastMathFlags(FMF);
|
||
}
|
||
}
|
||
|
||
Value *SPIRVToLLVM::transShiftLogicalBitwiseInst(SPIRVValue *BV, BasicBlock *BB,
|
||
Function *F) {
|
||
SPIRVBinary *BBN = static_cast<SPIRVBinary *>(BV);
|
||
if (BV->getType()->isTypeCooperativeMatrixKHR()) {
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(BBN, BB));
|
||
}
|
||
Instruction::BinaryOps BO;
|
||
auto OP = BBN->getOpCode();
|
||
if (isLogicalOpCode(OP))
|
||
OP = IntBoolOpMap::rmap(OP);
|
||
BO = static_cast<Instruction::BinaryOps>(OpCodeMap::rmap(OP));
|
||
|
||
Value *Op0 = transValue(BBN->getOperand(0), F, BB);
|
||
Value *Op1 = transValue(BBN->getOperand(1), F, BB);
|
||
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
|
||
Value *NewOp = Builder.CreateBinOp(BO, Op0, Op1, BV->getName());
|
||
if (auto *Inst = dyn_cast<Instruction>(NewOp)) {
|
||
applyNoIntegerWrapDecorations(BV, Inst);
|
||
applyFPFastMathModeDecorations(BV, Inst);
|
||
}
|
||
return NewOp;
|
||
}
|
||
|
||
Value *SPIRVToLLVM::transCmpInst(SPIRVValue *BV, BasicBlock *BB, Function *F) {
|
||
SPIRVCompare *BC = static_cast<SPIRVCompare *>(BV);
|
||
SPIRVType *BT = BC->getOperand(0)->getType();
|
||
Value *Inst = nullptr;
|
||
auto OP = BC->getOpCode();
|
||
if (isLogicalOpCode(OP))
|
||
OP = IntBoolOpMap::rmap(OP);
|
||
|
||
Value *Op0 = transValue(BC->getOperand(0), F, BB);
|
||
Value *Op1 = transValue(BC->getOperand(1), F, BB);
|
||
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
|
||
if (OP == OpLessOrGreater)
|
||
OP = OpFOrdNotEqual;
|
||
|
||
if (BT->isTypeVectorOrScalarInt() || BT->isTypeVectorOrScalarBool() ||
|
||
BT->isTypePointer())
|
||
Inst = Builder.CreateICmp(CmpMap::rmap(OP), Op0, Op1);
|
||
else if (BT->isTypeVectorOrScalarFloat())
|
||
Inst = Builder.CreateFCmp(CmpMap::rmap(OP), Op0, Op1);
|
||
assert(Inst && "not implemented");
|
||
applyFPFastMathModeDecorations(BV, static_cast<Instruction *>(Inst));
|
||
return Inst;
|
||
}
|
||
|
||
Type *SPIRVToLLVM::mapType(SPIRVType *BT, Type *T) {
|
||
SPIRVDBG(dbgs() << *T << '\n';)
|
||
// We don't want to store a TypedPointerType in the type map, since we can't
|
||
// actually use it in LLVM IR directly. Note that in the cases where we do
|
||
// want to construct TypedPointerType, we don't check the type map here.
|
||
if (!isa<TypedPointerType>(T))
|
||
TypeMap[BT] = T;
|
||
return T;
|
||
}
|
||
|
||
Value *SPIRVToLLVM::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().starts_with(KPlaceholderPrefix) &&
|
||
"A value is translated twice");
|
||
// Replaces placeholders for PHI nodes
|
||
LD->replaceAllUsesWith(V);
|
||
LD->eraseFromParent();
|
||
Placeholder->eraseFromParent();
|
||
}
|
||
ValueMap[BV] = V;
|
||
return V;
|
||
}
|
||
|
||
CallInst *
|
||
SPIRVToLLVM::expandOCLBuiltinWithScalarArg(CallInst *CI,
|
||
const std::string &FuncName) {
|
||
if (!CI->getOperand(0)->getType()->isVectorTy() &&
|
||
CI->getOperand(1)->getType()->isVectorTy()) {
|
||
auto VecElemCount =
|
||
cast<VectorType>(CI->getOperand(1)->getType())->getElementCount();
|
||
auto Mutator = mutateCallInst(CI, FuncName);
|
||
Mutator.mapArg(0, [=](Value *Arg) {
|
||
Value *NewVec = nullptr;
|
||
if (auto *CA = dyn_cast<Constant>(Arg))
|
||
NewVec = ConstantVector::getSplat(VecElemCount, CA);
|
||
else {
|
||
NewVec = ConstantVector::getSplat(
|
||
VecElemCount, Constant::getNullValue(Arg->getType()));
|
||
NewVec = InsertElementInst::Create(NewVec, Arg, getInt32(M, 0), "",
|
||
CI->getIterator());
|
||
NewVec = new ShuffleVectorInst(
|
||
NewVec, NewVec,
|
||
ConstantVector::getSplat(VecElemCount, getInt32(M, 0)), "",
|
||
CI->getIterator());
|
||
}
|
||
NewVec->takeName(Arg);
|
||
return NewVec;
|
||
});
|
||
return cast<CallInst>(Mutator.getMutated());
|
||
}
|
||
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,
|
||
BasicBlock *BB) {
|
||
auto *SamplerT = getSPIRVType(OpTypeSampler, true);
|
||
auto *I32Ty = IntegerType::getInt32Ty(*Context);
|
||
auto *FTy = FunctionType::get(SamplerT, {I32Ty}, false);
|
||
|
||
FunctionCallee Func = M->getOrInsertFunction(SAMPLER_INIT, FTy);
|
||
|
||
auto Lit = (BCS->getAddrMode() << 1) | BCS->getNormalized() |
|
||
((BCS->getFilterMode() + 1) << 4);
|
||
|
||
return CallInst::Create(Func, {ConstantInt::get(I32Ty, Lit)}, "", BB);
|
||
}
|
||
|
||
Value *SPIRVToLLVM::oclTransConstantPipeStorage(
|
||
SPIRV::SPIRVConstantPipeStorage *BCPS) {
|
||
|
||
std::string CPSName = std::string(kSPIRVTypeName::PrefixAndDelim) +
|
||
kSPIRVTypeName::ConstantPipeStorage;
|
||
|
||
auto *Int32Ty = IntegerType::getInt32Ty(*Context);
|
||
auto *CPSTy = StructType::getTypeByName(*Context, 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);
|
||
}
|
||
|
||
// Translate aliasing memory access masks for SPIRVLoad and SPIRVStore
|
||
// instructions. These masks are mapped on alias.scope and noalias
|
||
// metadata in LLVM. Translation of optional string operand isn't yet supported
|
||
// in the translator.
|
||
template <typename SPIRVInstType>
|
||
void SPIRVToLLVM::transAliasingMemAccess(SPIRVInstType *BI, Instruction *I) {
|
||
static_assert(std::is_same<SPIRVInstType, SPIRVStore>::value ||
|
||
std::is_same<SPIRVInstType, SPIRVLoad>::value,
|
||
"Only stores and loads can be aliased by memory access mask");
|
||
if (BI->SPIRVMemoryAccess::isNoAlias())
|
||
addMemAliasMetadata(I, BI->SPIRVMemoryAccess::getNoAliasInstID(),
|
||
LLVMContext::MD_noalias);
|
||
if (BI->SPIRVMemoryAccess::isAliasScope())
|
||
addMemAliasMetadata(I, BI->SPIRVMemoryAccess::getAliasScopeInstID(),
|
||
LLVMContext::MD_alias_scope);
|
||
}
|
||
|
||
// Create and apply alias.scope/noalias metadata
|
||
void SPIRVToLLVM::addMemAliasMetadata(Instruction *I, SPIRVId AliasListId,
|
||
uint32_t AliasMDKind) {
|
||
SPIRVAliasScopeListDeclINTEL *AliasList =
|
||
BM->get<SPIRVAliasScopeListDeclINTEL>(AliasListId);
|
||
std::vector<SPIRVId> AliasScopeIds = AliasList->getArguments();
|
||
MDBuilder MDB(*Context);
|
||
SmallVector<Metadata *, 4> MDScopes;
|
||
for (const auto ScopeId : AliasScopeIds) {
|
||
SPIRVAliasScopeDeclINTEL *AliasScope =
|
||
BM->get<SPIRVAliasScopeDeclINTEL>(ScopeId);
|
||
std::vector<SPIRVId> AliasDomainIds = AliasScope->getArguments();
|
||
// Currently we expect exactly one argument for aliasing scope
|
||
// instruction.
|
||
// TODO: add translation of string scope and domain operand.
|
||
assert(AliasDomainIds.size() == 1 &&
|
||
"AliasScopeDeclINTEL must have exactly one argument");
|
||
SPIRVId AliasDomainId = AliasDomainIds[0];
|
||
// Create and store unique domain and scope metadata
|
||
MDAliasDomainMap.emplace(AliasDomainId,
|
||
MDB.createAnonymousAliasScopeDomain());
|
||
MDAliasScopeMap.emplace(ScopeId, MDB.createAnonymousAliasScope(
|
||
MDAliasDomainMap[AliasDomainId]));
|
||
MDScopes.emplace_back(MDAliasScopeMap[ScopeId]);
|
||
}
|
||
// Create and store unique alias.scope/noalias metadata
|
||
MDAliasListMap.emplace(AliasListId,
|
||
MDNode::concatenate(I->getMetadata(AliasMDKind),
|
||
MDNode::get(*Context, MDScopes)));
|
||
I->setMetadata(AliasMDKind, MDAliasListMap[AliasListId]);
|
||
}
|
||
|
||
void SPIRVToLLVM::transFunctionPointerCallArgumentAttributes(
|
||
SPIRVValue *BV, CallInst *CI, SPIRVTypeFunction *CalledFnTy) {
|
||
std::vector<SPIRVDecorate const *> ArgumentAttributes =
|
||
BV->getDecorations(internal::DecorationArgumentAttributeINTEL);
|
||
|
||
for (const auto *Dec : ArgumentAttributes) {
|
||
std::vector<SPIRVWord> Literals = Dec->getVecLiteral();
|
||
SPIRVWord ArgNo = Literals[0];
|
||
SPIRVWord SpirvAttr = Literals[1];
|
||
// There is no value to rmap SPIR-V FunctionParameterAttributeNoCapture, as
|
||
// LLVM does not have Attribute::NoCapture anymore. Adding special handling
|
||
// for this case.
|
||
if (SpirvAttr == FunctionParameterAttributeNoCapture) {
|
||
CI->addParamAttr(ArgNo, Attribute::getWithCaptureInfo(
|
||
CI->getContext(), CaptureInfo::none()));
|
||
continue;
|
||
}
|
||
Attribute::AttrKind LlvmAttrKind = SPIRSPIRVFuncParamAttrMap::rmap(
|
||
static_cast<SPIRVFuncParamAttrKind>(SpirvAttr));
|
||
auto LlvmAttr =
|
||
Attribute::isTypeAttrKind(LlvmAttrKind)
|
||
? Attribute::get(CI->getContext(), LlvmAttrKind,
|
||
transType(CalledFnTy->getParameterType(ArgNo)
|
||
->getPointerElementType()))
|
||
: Attribute::get(CI->getContext(), LlvmAttrKind);
|
||
CI->addParamAttr(ArgNo, LlvmAttr);
|
||
}
|
||
}
|
||
|
||
/// 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:
|
||
case OpSpecConstant: {
|
||
SPIRVConstant *BConst = static_cast<SPIRVConstant *>(BV);
|
||
SPIRVType *BT = BV->getType();
|
||
Type *LT = transType(BT);
|
||
uint64_t ConstValue = BConst->getZExtIntValue();
|
||
SPIRVWord SpecId = 0;
|
||
if (OC == OpSpecConstant && BV->hasDecorate(DecorationSpecId, 0, &SpecId)) {
|
||
// Update the value with possibly provided external specialization.
|
||
if (BM->getSpecializationConstant(SpecId, ConstValue)) {
|
||
assert(
|
||
(BT->getBitWidth() == 64 ||
|
||
(ConstValue >> BT->getBitWidth()) == 0) &&
|
||
"Size of externally provided specialization constant value doesn't"
|
||
"fit into the specialization constant type");
|
||
}
|
||
}
|
||
switch (BT->getOpCode()) {
|
||
case OpTypeBool:
|
||
case OpTypeInt: {
|
||
const unsigned NumBits = BT->getBitWidth();
|
||
if (NumBits > 64) {
|
||
// Translate huge arbitrary precision integer constants
|
||
const unsigned RawDataNumWords = BConst->getNumWords();
|
||
const unsigned BigValNumWords = (RawDataNumWords + 1) / 2;
|
||
std::vector<uint64_t> BigValVec(BigValNumWords);
|
||
const std::vector<SPIRVWord> &RawData = BConst->getSPIRVWords();
|
||
// SPIRV words are integers of 32-bit width, meanwhile llvm::APInt
|
||
// is storing data using an array of 64-bit words. Here we pack SPIRV
|
||
// words into 64-bit integer array.
|
||
for (size_t I = 0; I != RawDataNumWords / 2; ++I)
|
||
BigValVec[I] =
|
||
(static_cast<uint64_t>(RawData[2 * I + 1]) << SpirvWordBitWidth) |
|
||
RawData[2 * I];
|
||
if (RawDataNumWords % 2)
|
||
BigValVec.back() = RawData.back();
|
||
return mapValue(BV, ConstantInt::get(LT, APInt(NumBits, BigValVec)));
|
||
}
|
||
return mapValue(
|
||
BV, ConstantInt::get(LT, ConstValue,
|
||
static_cast<SPIRVTypeInt *>(BT)->isSigned()));
|
||
}
|
||
case OpTypeFloat: {
|
||
const llvm::fltSemantics *FS = nullptr;
|
||
switch (BT->getFloatBitWidth()) {
|
||
case 16:
|
||
FS =
|
||
(BT->isTypeFloat(16, FPEncodingBFloat16KHR) ? &APFloat::BFloat()
|
||
: &APFloat::IEEEhalf());
|
||
break;
|
||
case 32:
|
||
FS = &APFloat::IEEEsingle();
|
||
break;
|
||
case 64:
|
||
FS = &APFloat::IEEEdouble();
|
||
break;
|
||
default:
|
||
llvm_unreachable("invalid floating-point type");
|
||
}
|
||
APFloat FPConstValue(*FS, APInt(BT->getFloatBitWidth(), ConstValue));
|
||
return mapValue(BV, ConstantFP::get(*Context, FPConstValue));
|
||
}
|
||
default:
|
||
llvm_unreachable("Not implemented");
|
||
return nullptr;
|
||
}
|
||
}
|
||
|
||
case OpConstantTrue:
|
||
return mapValue(BV, ConstantInt::getTrue(*Context));
|
||
|
||
case OpConstantFalse:
|
||
return mapValue(BV, ConstantInt::getFalse(*Context));
|
||
|
||
case OpSpecConstantTrue:
|
||
case OpSpecConstantFalse: {
|
||
bool IsTrue = OC == OpSpecConstantTrue;
|
||
SPIRVWord SpecId = 0;
|
||
if (BV->hasDecorate(DecorationSpecId, 0, &SpecId)) {
|
||
uint64_t ConstValue = 0;
|
||
if (BM->getSpecializationConstant(SpecId, ConstValue)) {
|
||
IsTrue = ConstValue;
|
||
}
|
||
}
|
||
return mapValue(BV, IsTrue ? ConstantInt::getTrue(*Context)
|
||
: ConstantInt::getFalse(*Context));
|
||
}
|
||
|
||
case OpConstantNull: {
|
||
auto *LT = transType(BV->getType());
|
||
return mapValue(BV, Constant::getNullValue(LT));
|
||
}
|
||
|
||
case OpConstantComposite:
|
||
case OpSpecConstantComposite: {
|
||
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)));
|
||
for (auto &CI : BCC->getContinuedInstructions()) {
|
||
for (auto &I : CI->getElements())
|
||
CV.push_back(dyn_cast<Constant>(transValue(I, F, BB)));
|
||
}
|
||
switch (BV->getType()->getOpCode()) {
|
||
case OpTypeVector:
|
||
return mapValue(BV, ConstantVector::get(CV));
|
||
case OpTypeMatrix:
|
||
case OpTypeArray: {
|
||
auto *AT = cast<ArrayType>(transType(BCC->getType()));
|
||
for (size_t I = 0; I != AT->getNumElements(); ++I) {
|
||
auto *ElemTy = AT->getElementType();
|
||
if (auto *ElemPtrTy = dyn_cast<PointerType>(ElemTy)) {
|
||
assert(isa<PointerType>(CV[I]->getType()) &&
|
||
"Constant type doesn't match constexpr array element type");
|
||
if (ElemPtrTy->getAddressSpace() !=
|
||
cast<PointerType>(CV[I]->getType())->getAddressSpace())
|
||
CV[I] = ConstantExpr::getAddrSpaceCast(CV[I], AT->getElementType());
|
||
}
|
||
}
|
||
|
||
return mapValue(BV, ConstantArray::get(AT, CV));
|
||
}
|
||
case OpTypeStruct: {
|
||
auto *BCCTy = 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;
|
||
|
||
if (cast<PointerType>(CV[I]->getType())->getAddressSpace() !=
|
||
cast<PointerType>(BCCTy->getElementType(I))->getAddressSpace())
|
||
CV[I] =
|
||
ConstantExpr::getAddrSpaceCast(CV[I], BCCTy->getElementType(I));
|
||
else
|
||
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
|
||
}
|
||
}
|
||
|
||
return mapValue(BV, ConstantStruct::get(BCCTy, CV));
|
||
}
|
||
case OpTypeCooperativeMatrixKHR: {
|
||
assert(CV.size() == 1 &&
|
||
"expecting exactly one operand for cooperative matrix types");
|
||
llvm::Type *RetTy = transType(BCC->getType());
|
||
llvm::Type *EltTy = transType(
|
||
static_cast<const SPIRVTypeCooperativeMatrixKHR *>(BV->getType())
|
||
->getCompType());
|
||
auto *FTy = FunctionType::get(RetTy, {EltTy}, false);
|
||
FunctionCallee Func =
|
||
M->getOrInsertFunction(getSPIRVFuncName(OC, RetTy), FTy);
|
||
IRBuilder<> Builder(BB);
|
||
CallInst *Call = Builder.CreateCall(Func, CV.front());
|
||
Call->setCallingConv(CallingConv::SPIR_FUNC);
|
||
return Call;
|
||
}
|
||
default:
|
||
llvm_unreachable("not implemented");
|
||
return nullptr;
|
||
}
|
||
}
|
||
|
||
case OpConstantSampler: {
|
||
auto *BCS = static_cast<SPIRVConstantSampler *>(BV);
|
||
// Intentially do not map this value. We want to generate constant
|
||
// sampler initializer every time constant sampler is used, otherwise
|
||
// initializer may not dominate all its uses.
|
||
return oclTransConstantSampler(BCS, BB);
|
||
}
|
||
|
||
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 OpConstantFunctionPointerINTEL: {
|
||
SPIRVConstantFunctionPointerINTEL *BC =
|
||
static_cast<SPIRVConstantFunctionPointerINTEL *>(BV);
|
||
SPIRVFunction *F = BC->getFunction();
|
||
BV->setName(F->getName());
|
||
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
|
||
? SPIRAS_CodeSectionINTEL
|
||
: SPIRAS_Private;
|
||
return mapValue(BV, transFunction(F, AS));
|
||
}
|
||
|
||
case OpUndef:
|
||
return mapValue(BV, UndefValue::get(transType(BV->getType())));
|
||
|
||
case OpSizeOf: {
|
||
Type *ResTy = transType(BV->getType());
|
||
auto *BI = static_cast<SPIRVSizeOf *>(BV);
|
||
SPIRVType *TypeArg = reinterpret_cast<SPIRVType *>(BI->getOpValue(0));
|
||
Type *EltTy = transType(TypeArg->getPointerElementType());
|
||
uint64_t Size = M->getDataLayout().getTypeStoreSize(EltTy).getFixedValue();
|
||
return mapValue(BV, ConstantInt::get(ResTy, Size));
|
||
}
|
||
|
||
case OpVariable:
|
||
case OpUntypedVariableKHR: {
|
||
auto *BVar = static_cast<SPIRVVariableBase *>(BV);
|
||
SPIRVType *PreTransTy = BVar->getType()->getPointerElementType();
|
||
if (BVar->getType()->isTypeUntypedPointerKHR()) {
|
||
auto *UntypedVar = static_cast<SPIRVUntypedVariableKHR *>(BVar);
|
||
if (SPIRVType *DT = UntypedVar->getDataType())
|
||
PreTransTy = DT;
|
||
}
|
||
auto *Ty = transType(PreTransTy);
|
||
bool IsConst = BVar->isConstant();
|
||
llvm::GlobalValue::LinkageTypes LinkageTy = transLinkageType(BVar);
|
||
SPIRVStorageClassKind BS = BVar->getStorageClass();
|
||
SPIRVValue *Init = BVar->getInitializer();
|
||
|
||
if (PreTransTy->isTypeSampler() && BS == StorageClassUniformConstant) {
|
||
// Skip generating llvm code during translation of a variable definition,
|
||
// generate code only for its uses
|
||
if (!BB)
|
||
return nullptr;
|
||
|
||
assert(Init && "UniformConstant OpVariable with sampler type must have "
|
||
"an initializer!");
|
||
return transValue(Init, F, BB);
|
||
}
|
||
|
||
if (BS == StorageClassFunction) {
|
||
// A Function storage class variable needs storage for each dynamic
|
||
// execution instance, so emit an alloca instead of a global.
|
||
assert(BB && "OpVariable with Function storage class requires BB");
|
||
IRBuilder<> Builder(BB);
|
||
AllocaInst *AI = Builder.CreateAlloca(Ty, nullptr, BV->getName());
|
||
if (Init) {
|
||
auto *Src = transValue(Init, F, BB);
|
||
const bool IsVolatile = BVar->hasDecorate(DecorationVolatile);
|
||
Builder.CreateStore(Src, AI, IsVolatile);
|
||
}
|
||
return mapValue(BV, AI);
|
||
}
|
||
|
||
SPIRAddressSpace AddrSpace;
|
||
bool IsVectorCompute =
|
||
BVar->hasDecorate(DecorationVectorComputeVariableINTEL);
|
||
Constant *Initializer = nullptr;
|
||
if (IsVectorCompute) {
|
||
AddrSpace = VectorComputeUtil::getVCGlobalVarAddressSpace(BS);
|
||
Initializer = PoisonValue::get(Ty);
|
||
} else
|
||
AddrSpace = SPIRSPIRVAddrSpaceMap::rmap(BS);
|
||
// Force SPIRV BuiltIn variable's name to be __spirv_BuiltInXXXX.
|
||
// No matter what BV's linkage name is.
|
||
SPIRVBuiltinVariableKind BVKind;
|
||
if (BVar->isBuiltin(&BVKind))
|
||
BV->setName(prefixSPIRVName(SPIRVBuiltInNameMap::map(BVKind)));
|
||
auto *LVar = new GlobalVariable(*M, Ty, IsConst, LinkageTy,
|
||
/*Initializer=*/nullptr, BV->getName(), 0,
|
||
GlobalVariable::NotThreadLocal, AddrSpace);
|
||
auto *Res = mapValue(BV, LVar);
|
||
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 initialized to 0.
|
||
Initializer = Constant::getNullValue(Ty);
|
||
else if (BS == SPIRVStorageClassKind::StorageClassWorkgroup &&
|
||
LinkageTy != GlobalValue::ExternalLinkage)
|
||
Initializer = dyn_cast<Constant>(PoisonValue::get(Ty));
|
||
else if ((LinkageTy != GlobalValue::ExternalLinkage) &&
|
||
(BS == SPIRVStorageClassKind::StorageClassCrossWorkgroup))
|
||
Initializer = Constant::getNullValue(Ty);
|
||
|
||
LVar->setUnnamedAddr((IsConst && Ty->isArrayTy() &&
|
||
Ty->getArrayElementType()->isIntegerTy(8))
|
||
? GlobalValue::UnnamedAddr::Global
|
||
: GlobalValue::UnnamedAddr::None);
|
||
LVar->setInitializer(Initializer);
|
||
|
||
if (IsVectorCompute) {
|
||
LVar->addAttribute(kVCMetadata::VCGlobalVariable);
|
||
SPIRVWord Offset;
|
||
if (BVar->hasDecorate(DecorationGlobalVariableOffsetINTEL, 0, &Offset))
|
||
LVar->addAttribute(kVCMetadata::VCByteOffset, utostr(Offset));
|
||
if (BVar->hasDecorate(DecorationVolatile))
|
||
LVar->addAttribute(kVCMetadata::VCVolatile);
|
||
auto SEVAttr = translateSEVMetadata(BVar, LVar->getContext());
|
||
if (SEVAttr)
|
||
LVar->addAttribute(SEVAttr.value().getKindAsString(),
|
||
SEVAttr.value().getValueAsString());
|
||
}
|
||
|
||
return Res;
|
||
}
|
||
|
||
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, &(*I));
|
||
}
|
||
llvm_unreachable("Invalid argument");
|
||
return nullptr;
|
||
}
|
||
|
||
case OpFunction:
|
||
return mapValue(BV, transFunction(static_cast<SPIRVFunction *>(BV)));
|
||
|
||
case OpAsmINTEL:
|
||
return mapValue(BV, transAsmINTEL(static_cast<SPIRVAsmINTEL *>(BV)));
|
||
|
||
case OpLabel:
|
||
return mapValue(BV, BasicBlock::Create(*Context, BV->getName(), F));
|
||
|
||
case OpSpecConstantArchitectureINTEL:
|
||
llvm_unreachable(
|
||
"Encountered non-specialized OpSpecConstantArchitectureINTEL");
|
||
return nullptr;
|
||
|
||
case OpSpecConstantTargetINTEL:
|
||
llvm_unreachable("Encountered non-specialized OpSpecConstantTargetINTEL");
|
||
return nullptr;
|
||
|
||
case OpSpecConstantCapabilitiesINTEL:
|
||
llvm_unreachable(
|
||
"Encountered non-specialized OpSpecConstantCapabilitiesINTEL");
|
||
return nullptr;
|
||
|
||
default:
|
||
// do nothing
|
||
break;
|
||
}
|
||
|
||
// During translation of OpSpecConstantOp we create an instruction
|
||
// corresponding to the Opcode operand and then translate this instruction.
|
||
// For such instruction BB and F should be nullptr, because it is a constant
|
||
// expression declared out of scope of any basic block or function.
|
||
// All other values require valid BB pointer.
|
||
assert(((isSpecConstantOpAllowedOp(OC) && !F && !BB) || BB) && "Invalid BB");
|
||
|
||
// Creation of place holder
|
||
if (CreatePlaceHolder) {
|
||
auto *Ty = transType(BV->getType());
|
||
auto *GV =
|
||
new GlobalVariable(*M, Ty, false, GlobalValue::PrivateLinkage, nullptr,
|
||
std::string(KPlaceholderPrefix) + BV->getName(), 0,
|
||
GlobalVariable::NotThreadLocal, 0);
|
||
auto *LD = new LoadInst(Ty, GV, BV->getName(), BB);
|
||
PlaceholderMap[BV] = LD;
|
||
return mapValue(BV, LD);
|
||
}
|
||
|
||
// Translation of instructions
|
||
int OpCode = BV->getOpCode();
|
||
switch (OpCode) {
|
||
case OpVariableLengthArrayINTEL: {
|
||
auto *VLA = static_cast<SPIRVVariableLengthArrayINTEL *>(BV);
|
||
llvm::Type *Ty = transType(BV->getType()->getPointerElementType());
|
||
llvm::Value *ArrSize = transValue(VLA->getOperand(0), F, BB);
|
||
return mapValue(BV,
|
||
new AllocaInst(Ty, M->getDataLayout().getAllocaAddrSpace(),
|
||
ArrSize, BV->getName(), BB));
|
||
}
|
||
|
||
case OpRestoreMemoryINTEL: {
|
||
IRBuilder<> Builder(BB);
|
||
auto *Restore = static_cast<SPIRVRestoreMemoryINTEL *>(BV);
|
||
llvm::Value *Ptr = transValue(Restore->getOperand(0), F, BB);
|
||
auto *StackRestore = Builder.CreateStackRestore(Ptr);
|
||
return mapValue(BV, StackRestore);
|
||
}
|
||
|
||
case OpSaveMemoryINTEL: {
|
||
IRBuilder<> Builder(BB);
|
||
auto *StackSave = Builder.CreateStackSave();
|
||
return mapValue(BV, StackSave);
|
||
}
|
||
|
||
case OpBranch: {
|
||
auto *BR = static_cast<SPIRVBranch *>(BV);
|
||
auto *BI = BranchInst::Create(
|
||
cast<BasicBlock>(transValue(BR->getTargetLabel(), F, BB)), BB);
|
||
// Loop metadata will be translated in the end of function translation.
|
||
return mapValue(BV, BI);
|
||
}
|
||
|
||
case OpBranchConditional: {
|
||
auto *BR = static_cast<SPIRVBranchConditional *>(BV);
|
||
auto *BC = BranchInst::Create(
|
||
cast<BasicBlock>(transValue(BR->getTrueLabel(), F, BB)),
|
||
cast<BasicBlock>(transValue(BR->getFalseLabel(), F, BB)),
|
||
transValue(BR->getCondition(), F, BB), BB);
|
||
// Loop metadata will be translated in the end of function translation.
|
||
return mapValue(BV, BC);
|
||
}
|
||
|
||
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 OpUnreachable:
|
||
return mapValue(BV, new UnreachableInst(*Context, BB));
|
||
|
||
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 OpLifetimeStart: {
|
||
SPIRVLifetimeStart *LTStart = static_cast<SPIRVLifetimeStart *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
SPIRVWord Size = LTStart->getSize();
|
||
ConstantInt *S = nullptr;
|
||
if (Size)
|
||
S = Builder.getInt64(Size);
|
||
Value *Var = transValue(LTStart->getObject(), F, BB);
|
||
CallInst *Start = Builder.CreateLifetimeStart(Var, S);
|
||
return mapValue(BV, Start);
|
||
}
|
||
|
||
case OpLifetimeStop: {
|
||
SPIRVLifetimeStop *LTStop = static_cast<SPIRVLifetimeStop *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
SPIRVWord Size = LTStop->getSize();
|
||
ConstantInt *S = nullptr;
|
||
if (Size)
|
||
S = Builder.getInt64(Size);
|
||
auto *Var = transValue(LTStop->getObject(), F, BB);
|
||
for (const auto &I : Var->users())
|
||
if (auto *II = getLifetimeStartIntrinsic(dyn_cast<Instruction>(I)))
|
||
return mapValue(BV, Builder.CreateLifetimeEnd(II->getOperand(1), S));
|
||
return mapValue(BV, Builder.CreateLifetimeEnd(Var, S));
|
||
}
|
||
|
||
case OpStore: {
|
||
SPIRVStore *BS = static_cast<SPIRVStore *>(BV);
|
||
StoreInst *SI = nullptr;
|
||
auto *Src = transValue(BS->getSrc(), F, BB);
|
||
auto *Dst = transValue(BS->getDst(), F, BB);
|
||
|
||
bool isVolatile = BS->SPIRVMemoryAccess::isVolatile();
|
||
uint64_t AlignValue = BS->SPIRVMemoryAccess::getAlignment();
|
||
if (0 == AlignValue)
|
||
SI = new StoreInst(Src, Dst, isVolatile, BB);
|
||
else
|
||
SI = new StoreInst(Src, Dst, isVolatile, Align(AlignValue), BB);
|
||
if (BS->SPIRVMemoryAccess::isNonTemporal())
|
||
transNonTemporalMetadata(SI);
|
||
transAliasingMemAccess<SPIRVStore>(BS, SI);
|
||
return mapValue(BV, SI);
|
||
}
|
||
|
||
case OpLoad: {
|
||
SPIRVLoad *BL = static_cast<SPIRVLoad *>(BV);
|
||
auto *V = transValue(BL->getSrc(), F, BB);
|
||
|
||
Type *Ty = transType(BL->getType());
|
||
LoadInst *LI = nullptr;
|
||
uint64_t AlignValue = BL->SPIRVMemoryAccess::getAlignment();
|
||
if (0 == AlignValue) {
|
||
LI = new LoadInst(Ty, V, BV->getName(),
|
||
BL->SPIRVMemoryAccess::isVolatile(), BB);
|
||
} else {
|
||
LI = new LoadInst(Ty, V, BV->getName(),
|
||
BL->SPIRVMemoryAccess::isVolatile(), Align(AlignValue),
|
||
BB);
|
||
}
|
||
if (BL->SPIRVMemoryAccess::isNonTemporal())
|
||
transNonTemporalMetadata(LI);
|
||
transAliasingMemAccess<SPIRVLoad>(BL, LI);
|
||
return mapValue(BV, LI);
|
||
}
|
||
|
||
case OpCopyMemory: {
|
||
auto *BC = static_cast<SPIRVCopyMemory *>(BV);
|
||
llvm::Value *Dst = transValue(BC->getTarget(), F, BB);
|
||
MaybeAlign Align(BC->getAlignment());
|
||
MaybeAlign SrcAlign =
|
||
BC->getSrcAlignment() ? MaybeAlign(BC->getSrcAlignment()) : Align;
|
||
Type *EltTy =
|
||
transType(BC->getSource()->getType()->getPointerElementType());
|
||
uint64_t Size = M->getDataLayout().getTypeStoreSize(EltTy).getFixedValue();
|
||
bool IsVolatile = BC->SPIRVMemoryAccess::isVolatile();
|
||
IRBuilder<> Builder(BB);
|
||
|
||
llvm::Value *Src = transValue(BC->getSource(), F, BB);
|
||
CallInst *CI =
|
||
Builder.CreateMemCpy(Dst, Align, Src, SrcAlign, Size, IsVolatile);
|
||
if (isFuncNoUnwind())
|
||
CI->getFunction()->addFnAttr(Attribute::NoUnwind);
|
||
return mapValue(BV, CI);
|
||
}
|
||
|
||
case OpCopyMemorySized: {
|
||
SPIRVCopyMemorySized *BC = static_cast<SPIRVCopyMemorySized *>(BV);
|
||
llvm::Value *Dst = transValue(BC->getTarget(), F, BB);
|
||
MaybeAlign Align(BC->getAlignment());
|
||
MaybeAlign SrcAlign =
|
||
BC->getSrcAlignment() ? MaybeAlign(BC->getSrcAlignment()) : Align;
|
||
llvm::Value *Size = transValue(BC->getSize(), F, BB);
|
||
bool IsVolatile = BC->SPIRVMemoryAccess::isVolatile();
|
||
IRBuilder<> Builder(BB);
|
||
|
||
llvm::Value *Src = transValue(BC->getSource(), F, BB);
|
||
CallInst *CI =
|
||
Builder.CreateMemCpy(Dst, Align, Src, SrcAlign, Size, IsVolatile);
|
||
if (isFuncNoUnwind())
|
||
CI->getFunction()->addFnAttr(Attribute::NoUnwind);
|
||
return mapValue(BV, CI);
|
||
}
|
||
|
||
case OpSelect: {
|
||
SPIRVSelect *BS = static_cast<SPIRVSelect *>(BV);
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
return mapValue(BV,
|
||
Builder.CreateSelect(transValue(BS->getCondition(), F, BB),
|
||
transValue(BS->getTrueValue(), F, BB),
|
||
transValue(BS->getFalseValue(), F, BB),
|
||
BV->getName()));
|
||
}
|
||
|
||
case OpLine:
|
||
case OpSelectionMerge: // OpenCL Compiler does not use this instruction
|
||
return nullptr;
|
||
|
||
case OpLoopMerge: // Will be translated after all other function's
|
||
case OpLoopControlINTEL: // instructions are translated.
|
||
FuncLoopMetadataMap[BB] = BV;
|
||
return nullptr;
|
||
|
||
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(
|
||
[&](SPIRVSwitch::LiteralTy Literals, SPIRVBasicBlock *Label) {
|
||
assert(!Literals.empty() && "Literals should not be empty");
|
||
assert(Literals.size() <= 2 &&
|
||
"Number of literals should not be more then two");
|
||
uint64_t Literal = uint64_t(Literals.at(0));
|
||
if (Literals.size() == 2) {
|
||
Literal += uint64_t(Literals.at(1)) << 32;
|
||
}
|
||
LS->addCase(
|
||
ConstantInt::get(cast<IntegerType>(Select->getType()), Literal),
|
||
cast<BasicBlock>(transValue(Label, F, BB)));
|
||
});
|
||
return mapValue(BV, LS);
|
||
}
|
||
|
||
case OpVectorTimesScalar: {
|
||
auto *VTS = static_cast<SPIRVVectorTimesScalar *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
auto *Scalar = transValue(VTS->getScalar(), F, BB);
|
||
auto *Vector = transValue(VTS->getVector(), F, BB);
|
||
auto *VecTy = cast<FixedVectorType>(Vector->getType());
|
||
unsigned VecSize = VecTy->getNumElements();
|
||
auto *NewVec =
|
||
Builder.CreateVectorSplat(VecSize, Scalar, Scalar->getName());
|
||
NewVec->takeName(Scalar);
|
||
auto *Scale = Builder.CreateFMul(Vector, NewVec, "scale");
|
||
return mapValue(BV, Scale);
|
||
}
|
||
|
||
case OpVectorTimesMatrix: {
|
||
auto *VTM = static_cast<SPIRVVectorTimesMatrix *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
Value *Mat = transValue(VTM->getMatrix(), F, BB);
|
||
Value *Vec = transValue(VTM->getVector(), F, BB);
|
||
|
||
// Vec is of N elements.
|
||
// Mat is of M columns and N rows.
|
||
// Mat consists of vectors: V_1, V_2, ..., V_M
|
||
//
|
||
// The product is:
|
||
//
|
||
// |------- M ----------|
|
||
// Result = sum ( {Vec_1, Vec_1, ..., Vec_1} * {V_1_1, V_2_1, ..., V_M_1},
|
||
// {Vec_2, Vec_2, ..., Vec_2} * {V_1_2, V_2_2, ..., V_M_2},
|
||
// ...
|
||
// {Vec_N, Vec_N, ..., Vec_N} * {V_1_N, V_2_N, ..., V_M_N});
|
||
|
||
unsigned M = Mat->getType()->getArrayNumElements();
|
||
|
||
auto *VecTy = cast<FixedVectorType>(Vec->getType());
|
||
FixedVectorType *VTy = FixedVectorType::get(VecTy->getElementType(), M);
|
||
auto *ETy = VTy->getElementType();
|
||
unsigned N = VecTy->getNumElements();
|
||
Value *V = Builder.CreateVectorSplat(M, ConstantFP::get(ETy, 0.0));
|
||
|
||
for (unsigned Idx = 0; Idx != N; ++Idx) {
|
||
Value *S = Builder.CreateExtractElement(Vec, Builder.getInt32(Idx));
|
||
Value *Lhs = Builder.CreateVectorSplat(M, S);
|
||
Value *Rhs = PoisonValue::get(VTy);
|
||
for (unsigned Idx2 = 0; Idx2 != M; ++Idx2) {
|
||
Value *Vx = Builder.CreateExtractValue(Mat, Idx2);
|
||
Value *Vxi = Builder.CreateExtractElement(Vx, Builder.getInt32(Idx));
|
||
Rhs = Builder.CreateInsertElement(Rhs, Vxi, Builder.getInt32(Idx2));
|
||
}
|
||
Value *Mul = Builder.CreateFMul(Lhs, Rhs);
|
||
V = Builder.CreateFAdd(V, Mul);
|
||
}
|
||
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case OpMatrixTimesScalar: {
|
||
auto *MTS = static_cast<SPIRVMatrixTimesScalar *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
auto *Scalar = transValue(MTS->getScalar(), F, BB);
|
||
auto *Matrix = transValue(MTS->getMatrix(), F, BB);
|
||
uint64_t ColNum = Matrix->getType()->getArrayNumElements();
|
||
auto *ColType = cast<ArrayType>(Matrix->getType())->getElementType();
|
||
auto VecSize = cast<FixedVectorType>(ColType)->getNumElements();
|
||
auto *NewVec =
|
||
Builder.CreateVectorSplat(VecSize, Scalar, Scalar->getName());
|
||
NewVec->takeName(Scalar);
|
||
|
||
Value *V = PoisonValue::get(Matrix->getType());
|
||
for (uint64_t Idx = 0; Idx != ColNum; Idx++) {
|
||
auto *Col = Builder.CreateExtractValue(Matrix, Idx);
|
||
auto *I = Builder.CreateFMul(Col, NewVec);
|
||
V = Builder.CreateInsertValue(V, I, Idx);
|
||
}
|
||
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case OpMatrixTimesVector: {
|
||
auto *MTV = static_cast<SPIRVMatrixTimesVector *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
Value *Mat = transValue(MTV->getMatrix(), F, BB);
|
||
Value *Vec = transValue(MTV->getVector(), F, BB);
|
||
|
||
// Result is similar to Matrix * Matrix
|
||
// Mat is of M columns and N rows.
|
||
// Mat consists of vectors: V_1, V_2, ..., V_M
|
||
// where each vector is of size N.
|
||
//
|
||
// Vec is of size M.
|
||
// The product is a vector of size N.
|
||
//
|
||
// |------- N ----------|
|
||
// Result = sum ( {Vec_1, Vec_1, ..., Vec_1} * V_1,
|
||
// {Vec_2, Vec_2, ..., Vec_2} * V_2,
|
||
// ...
|
||
// {Vec_M, Vec_M, ..., Vec_M} * V_N );
|
||
//
|
||
// where sum is defined as vector sum.
|
||
|
||
unsigned M = Mat->getType()->getArrayNumElements();
|
||
FixedVectorType *VTy = cast<FixedVectorType>(
|
||
cast<ArrayType>(Mat->getType())->getElementType());
|
||
unsigned N = VTy->getNumElements();
|
||
auto *ETy = VTy->getElementType();
|
||
Value *V = Builder.CreateVectorSplat(N, ConstantFP::get(ETy, 0.0));
|
||
|
||
for (unsigned Idx = 0; Idx != M; ++Idx) {
|
||
Value *S = Builder.CreateExtractElement(Vec, Builder.getInt32(Idx));
|
||
Value *Lhs = Builder.CreateVectorSplat(N, S);
|
||
Value *Vx = Builder.CreateExtractValue(Mat, Idx);
|
||
Value *Mul = Builder.CreateFMul(Lhs, Vx);
|
||
V = Builder.CreateFAdd(V, Mul);
|
||
}
|
||
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case OpMatrixTimesMatrix: {
|
||
auto *MTM = static_cast<SPIRVMatrixTimesMatrix *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
Value *M1 = transValue(MTM->getLeftMatrix(), F, BB);
|
||
Value *M2 = transValue(MTM->getRightMatrix(), F, BB);
|
||
|
||
// Each matrix consists of a list of columns.
|
||
// M1 (the left matrix) is of C1 columns and R1 rows.
|
||
// M1 consists of a list of vectors: V_1, V_2, ..., V_C1
|
||
// where V_x are vectors of size R1.
|
||
//
|
||
// M2 (the right matrix) is of C2 columns and R2 rows.
|
||
// M2 consists of a list of vectors: U_1, U_2, ..., U_C2
|
||
// where U_x are vectors of size R2.
|
||
//
|
||
// Now M1 * M2 requires C1 == R2.
|
||
// The result is a matrix of C2 columns and R1 rows.
|
||
// That is, consists of C2 vectors of size R1.
|
||
//
|
||
// M1 * M2 algorithm is as below:
|
||
//
|
||
// Result = { dot_product(U_1, M1),
|
||
// dot_product(U_2, M1),
|
||
// ...
|
||
// dot_product(U_C2, M1) };
|
||
// where
|
||
// dot_product (U, M) is defined as:
|
||
//
|
||
// |-------- C1 ------|
|
||
// Result = sum ( {U[1], U[1], ..., U[1]} * V_1,
|
||
// {U[2], U[2], ..., U[2]} * V_2,
|
||
// ...
|
||
// {U[R2], U[R2], ..., U[R2]} * V_C1 );
|
||
// Note that C1 == R2
|
||
// sum is defined as vector sum.
|
||
|
||
unsigned C1 = M1->getType()->getArrayNumElements();
|
||
unsigned C2 = M2->getType()->getArrayNumElements();
|
||
FixedVectorType *V1Ty =
|
||
cast<FixedVectorType>(cast<ArrayType>(M1->getType())->getElementType());
|
||
FixedVectorType *V2Ty =
|
||
cast<FixedVectorType>(cast<ArrayType>(M2->getType())->getElementType());
|
||
unsigned R1 = V1Ty->getNumElements();
|
||
unsigned R2 = V2Ty->getNumElements();
|
||
auto *ETy = V1Ty->getElementType();
|
||
|
||
(void)C1;
|
||
assert(C1 == R2 && "Unmatched matrix");
|
||
|
||
auto *VTy = FixedVectorType::get(ETy, R1);
|
||
auto *ResultTy = ArrayType::get(VTy, C2);
|
||
|
||
Value *Res = PoisonValue::get(ResultTy);
|
||
|
||
for (unsigned Idx = 0; Idx != C2; ++Idx) {
|
||
Value *U = Builder.CreateExtractValue(M2, Idx);
|
||
|
||
// Calculate dot_product(U, M1)
|
||
Value *Dot = Builder.CreateVectorSplat(R1, ConstantFP::get(ETy, 0.0));
|
||
|
||
for (unsigned Idx2 = 0; Idx2 != R2; ++Idx2) {
|
||
Value *Ux = Builder.CreateExtractElement(U, Builder.getInt32(Idx2));
|
||
Value *Lhs = Builder.CreateVectorSplat(R1, Ux);
|
||
Value *Rhs = Builder.CreateExtractValue(M1, Idx2);
|
||
Value *Mul = Builder.CreateFMul(Lhs, Rhs);
|
||
Dot = Builder.CreateFAdd(Dot, Mul);
|
||
}
|
||
|
||
Res = Builder.CreateInsertValue(Res, Dot, Idx);
|
||
}
|
||
|
||
return mapValue(BV, Res);
|
||
}
|
||
|
||
case OpTranspose: {
|
||
auto *TR = static_cast<SPIRVTranspose *>(BV);
|
||
IRBuilder<> Builder(BB);
|
||
auto *Matrix = transValue(TR->getMatrix(), F, BB);
|
||
unsigned ColNum = Matrix->getType()->getArrayNumElements();
|
||
FixedVectorType *ColTy = cast<FixedVectorType>(
|
||
cast<ArrayType>(Matrix->getType())->getElementType());
|
||
unsigned RowNum = ColTy->getNumElements();
|
||
|
||
auto *VTy = FixedVectorType::get(ColTy->getElementType(), ColNum);
|
||
auto *ResultTy = ArrayType::get(VTy, RowNum);
|
||
Value *V = PoisonValue::get(ResultTy);
|
||
|
||
SmallVector<Value *, 16> MCache;
|
||
MCache.reserve(ColNum);
|
||
for (unsigned Idx = 0; Idx != ColNum; ++Idx)
|
||
MCache.push_back(Builder.CreateExtractValue(Matrix, Idx));
|
||
|
||
if (ColNum == RowNum) {
|
||
// Fastpath
|
||
switch (ColNum) {
|
||
case 2: {
|
||
Value *V1 = Builder.CreateShuffleVector(MCache[0], MCache[1],
|
||
ArrayRef<int>{0, 2});
|
||
V = Builder.CreateInsertValue(V, V1, 0);
|
||
Value *V2 = Builder.CreateShuffleVector(MCache[0], MCache[1],
|
||
ArrayRef<int>{1, 3});
|
||
V = Builder.CreateInsertValue(V, V2, 1);
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case 4: {
|
||
for (int Idx = 0; Idx < 4; ++Idx) {
|
||
Value *V1 = Builder.CreateShuffleVector(MCache[0], MCache[1],
|
||
ArrayRef<int>{Idx, Idx + 4});
|
||
Value *V2 = Builder.CreateShuffleVector(MCache[2], MCache[3],
|
||
ArrayRef<int>{Idx, Idx + 4});
|
||
Value *V3 =
|
||
Builder.CreateShuffleVector(V1, V2, ArrayRef<int>{0, 1, 2, 3});
|
||
V = Builder.CreateInsertValue(V, V3, Idx);
|
||
}
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Slowpath
|
||
for (unsigned Idx = 0; Idx != RowNum; ++Idx) {
|
||
Value *Vec = PoisonValue::get(VTy);
|
||
|
||
for (unsigned Idx2 = 0; Idx2 != ColNum; ++Idx2) {
|
||
Value *S =
|
||
Builder.CreateExtractElement(MCache[Idx2], Builder.getInt32(Idx));
|
||
Vec = Builder.CreateInsertElement(Vec, S, Idx2);
|
||
}
|
||
|
||
V = Builder.CreateInsertValue(V, Vec, Idx);
|
||
}
|
||
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case OpCopyObject: {
|
||
SPIRVCopyObject *CO = static_cast<SPIRVCopyObject *>(BV);
|
||
auto *Ty = transType(CO->getOperand()->getType());
|
||
AllocaInst *AI =
|
||
new AllocaInst(Ty, M->getDataLayout().getAllocaAddrSpace(), "", BB);
|
||
new StoreInst(transValue(CO->getOperand(), F, BB), AI, BB);
|
||
LoadInst *LI = new LoadInst(Ty, AI, "", BB);
|
||
return mapValue(BV, LI);
|
||
}
|
||
case OpCopyLogical: {
|
||
SPIRVCopyLogical *CL = static_cast<SPIRVCopyLogical *>(BV);
|
||
|
||
auto *SrcTy = transType(CL->getOperand()->getType());
|
||
auto *DstTy = transType(CL->getType());
|
||
|
||
assert(M->getDataLayout().getTypeStoreSize(SrcTy).getFixedValue() ==
|
||
M->getDataLayout().getTypeStoreSize(DstTy).getFixedValue() &&
|
||
"Size mismatch in OpCopyLogical");
|
||
|
||
IRBuilder<> Builder(BB);
|
||
|
||
auto *SrcAI = Builder.CreateAlloca(SrcTy);
|
||
Builder.CreateAlignedStore(transValue(CL->getOperand(), F, BB), SrcAI,
|
||
SrcAI->getAlign());
|
||
|
||
auto *LI = Builder.CreateAlignedLoad(DstTy, SrcAI, SrcAI->getAlign());
|
||
return mapValue(BV, LI);
|
||
}
|
||
|
||
case OpAccessChain:
|
||
case OpInBoundsAccessChain:
|
||
case OpPtrAccessChain:
|
||
case OpInBoundsPtrAccessChain:
|
||
case OpUntypedAccessChainKHR:
|
||
case OpUntypedInBoundsAccessChainKHR:
|
||
case OpUntypedPtrAccessChainKHR:
|
||
case OpUntypedInBoundsPtrAccessChainKHR: {
|
||
auto *AC = static_cast<SPIRVAccessChainBase *>(BV);
|
||
auto *Base = transValue(AC->getBase(), F, BB);
|
||
SPIRVType *BaseSPVTy = AC->getBaseType();
|
||
if ((BaseSPVTy->isTypePointer() &&
|
||
BaseSPVTy->getPointerElementType()->isTypeCooperativeMatrixKHR()) ||
|
||
(isUntypedAccessChainOpCode(OC) &&
|
||
BaseSPVTy->isTypeCooperativeMatrixKHR())) {
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(AC, BB));
|
||
}
|
||
Type *BaseTy =
|
||
BaseSPVTy->isTypeVector()
|
||
? transType(
|
||
BaseSPVTy->getVectorComponentType()->getPointerElementType())
|
||
: BaseSPVTy->isTypePointer()
|
||
? transType(BaseSPVTy->getPointerElementType())
|
||
: transType(BaseSPVTy);
|
||
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 (GEPOrUseMap.count(Base)) {
|
||
auto IdxToInstMap = GEPOrUseMap[Base];
|
||
auto Idx = AC->getIndices();
|
||
|
||
// In transIntelFPGADecorations we generated GEPs only for the fields of
|
||
// structure, meaning that GEP to `0` accesses the Structure itself, and
|
||
// the second `Id` is a Key in the map.
|
||
if (Idx.size() == 2) {
|
||
unsigned Idx1 = static_cast<ConstantInt *>(getTranslatedValue(Idx[0]))
|
||
->getZExtValue();
|
||
if (Idx1 == 0) {
|
||
unsigned Idx2 = static_cast<ConstantInt *>(getTranslatedValue(Idx[1]))
|
||
->getZExtValue();
|
||
|
||
// If we already have the instruction in a map, use it.
|
||
if (IdxToInstMap.count(Idx2))
|
||
return mapValue(BV, IdxToInstMap[Idx2]);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (BB) {
|
||
auto *GEP =
|
||
GetElementPtrInst::Create(BaseTy, Base, Index, BV->getName(), BB);
|
||
GEP->setIsInBounds(IsInbound);
|
||
V = GEP;
|
||
} else {
|
||
auto *CT = cast<Constant>(Base);
|
||
V = ConstantExpr::getGetElementPtr(BaseTy, CT, Index, IsInbound);
|
||
}
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case OpPtrEqual:
|
||
case OpPtrNotEqual: {
|
||
auto *BC = static_cast<SPIRVBinary *>(BV);
|
||
auto Ops = transValue(BC->getOperands(), F, BB);
|
||
|
||
IRBuilder<> Builder(BB);
|
||
Value *Op1 = Builder.CreatePtrToInt(Ops[0], Type::getInt64Ty(*Context));
|
||
Value *Op2 = Builder.CreatePtrToInt(Ops[1], Type::getInt64Ty(*Context));
|
||
CmpInst::Predicate P =
|
||
OC == OpPtrEqual ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE;
|
||
Value *V = Builder.CreateICmp(P, Op1, Op2);
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case OpPtrDiff: {
|
||
auto *BC = static_cast<SPIRVBinary *>(BV);
|
||
auto SPVOps = BC->getOperands();
|
||
auto Ops = transValue(SPVOps, F, BB);
|
||
IRBuilder<> Builder(BB);
|
||
|
||
Type *ElemTy = nullptr;
|
||
if (SPVOps[0]->isUntypedVariable())
|
||
ElemTy = transType(
|
||
static_cast<SPIRVUntypedVariableKHR *>(SPVOps[0])->getDataType());
|
||
else
|
||
ElemTy = transType(SPVOps[0]->getType()->getPointerElementType());
|
||
|
||
Value *V = Builder.CreatePtrDiff(ElemTy, Ops[0], Ops[1]);
|
||
return mapValue(BV, V);
|
||
}
|
||
|
||
case OpCompositeConstruct: {
|
||
auto *CC = static_cast<SPIRVCompositeConstruct *>(BV);
|
||
auto Constituents = transValue(CC->getOperands(), F, BB);
|
||
std::vector<Constant *> CV;
|
||
bool HasRtValues = false;
|
||
for (const auto &I : Constituents) {
|
||
auto *C = dyn_cast<Constant>(I);
|
||
CV.push_back(C);
|
||
if (!HasRtValues && C == nullptr)
|
||
HasRtValues = true;
|
||
}
|
||
|
||
switch (static_cast<size_t>(BV->getType()->getOpCode())) {
|
||
case OpTypeVector: {
|
||
if (!HasRtValues)
|
||
return mapValue(BV, ConstantVector::get(CV));
|
||
|
||
auto *VT = cast<FixedVectorType>(transType(CC->getType()));
|
||
Value *NewVec = ConstantVector::getSplat(
|
||
VT->getElementCount(), PoisonValue::get(VT->getElementType()));
|
||
|
||
for (size_t I = 0; I < Constituents.size(); I++) {
|
||
NewVec = InsertElementInst::Create(NewVec, Constituents[I],
|
||
getInt32(M, I), "", BB);
|
||
}
|
||
return mapValue(BV, NewVec);
|
||
}
|
||
case OpTypeArray: {
|
||
auto *AT = cast<ArrayType>(transType(CC->getType()));
|
||
if (!HasRtValues)
|
||
return mapValue(BV, ConstantArray::get(AT, CV));
|
||
|
||
AllocaInst *Alloca =
|
||
new AllocaInst(AT, M->getDataLayout().getAllocaAddrSpace(), "", BB);
|
||
|
||
// get pointer to the element of the array
|
||
// store the result of argument
|
||
for (size_t I = 0; I < Constituents.size(); I++) {
|
||
auto *GEP = GetElementPtrInst::Create(
|
||
AT, Alloca, {getInt32(M, 0), getInt32(M, I)}, "gep", BB);
|
||
GEP->setIsInBounds(true);
|
||
new StoreInst(Constituents[I], GEP, false, BB);
|
||
}
|
||
|
||
auto *Load = new LoadInst(AT, Alloca, "load", false, BB);
|
||
return mapValue(BV, Load);
|
||
}
|
||
case OpTypeStruct: {
|
||
auto *ST = cast<StructType>(transType(CC->getType()));
|
||
if (!HasRtValues)
|
||
return mapValue(BV, ConstantStruct::get(ST, CV));
|
||
|
||
AllocaInst *Alloca =
|
||
new AllocaInst(ST, M->getDataLayout().getAllocaAddrSpace(), "", BB);
|
||
|
||
// get pointer to the element of structure
|
||
// store the result of argument
|
||
for (size_t I = 0; I < Constituents.size(); I++) {
|
||
auto *GEP = GetElementPtrInst::Create(
|
||
ST, Alloca, {getInt32(M, 0), getInt32(M, I)}, "gep", BB);
|
||
GEP->setIsInBounds(true);
|
||
new StoreInst(Constituents[I], GEP, false, BB);
|
||
}
|
||
|
||
auto *Load = new LoadInst(ST, Alloca, "load", false, BB);
|
||
return mapValue(BV, Load);
|
||
}
|
||
case internal::OpTypeJointMatrixINTEL:
|
||
case OpTypeCooperativeMatrixKHR:
|
||
case internal::OpTypeTaskSequenceINTEL:
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(CC, BB));
|
||
default:
|
||
llvm_unreachable("Unhandled type!");
|
||
}
|
||
}
|
||
|
||
case OpCompositeExtract: {
|
||
SPIRVCompositeExtract *CE = static_cast<SPIRVCompositeExtract *>(BV);
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
if (CE->getComposite()->getType()->isTypeVector()) {
|
||
assert(CE->getIndices().size() == 1 && "Invalid index");
|
||
return mapValue(
|
||
BV, Builder.CreateExtractElement(
|
||
transValue(CE->getComposite(), F, BB),
|
||
ConstantInt::get(*Context, APInt(32, CE->getIndices()[0])),
|
||
BV->getName()));
|
||
}
|
||
return mapValue(
|
||
BV, Builder.CreateExtractValue(transValue(CE->getComposite(), F, BB),
|
||
CE->getIndices(), BV->getName()));
|
||
}
|
||
|
||
case OpVectorExtractDynamic: {
|
||
auto *VED = static_cast<SPIRVVectorExtractDynamic *>(BV);
|
||
SPIRVValue *Vec = VED->getVector();
|
||
if (Vec->getType()->getOpCode() == internal::OpTypeJointMatrixINTEL) {
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(VED, BB));
|
||
}
|
||
return mapValue(
|
||
BV, ExtractElementInst::Create(transValue(Vec, F, BB),
|
||
transValue(VED->getIndex(), F, BB),
|
||
BV->getName(), BB));
|
||
}
|
||
|
||
case OpCompositeInsert: {
|
||
auto *CI = static_cast<SPIRVCompositeInsert *>(BV);
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
if (CI->getComposite()->getType()->isTypeVector()) {
|
||
assert(CI->getIndices().size() == 1 && "Invalid index");
|
||
return mapValue(
|
||
BV, Builder.CreateInsertElement(
|
||
transValue(CI->getComposite(), F, BB),
|
||
transValue(CI->getObject(), F, BB),
|
||
ConstantInt::get(*Context, APInt(32, CI->getIndices()[0])),
|
||
BV->getName()));
|
||
}
|
||
return mapValue(
|
||
BV, Builder.CreateInsertValue(transValue(CI->getComposite(), F, BB),
|
||
transValue(CI->getObject(), F, BB),
|
||
CI->getIndices(), BV->getName()));
|
||
}
|
||
|
||
case OpVectorInsertDynamic: {
|
||
auto *VID = static_cast<SPIRVVectorInsertDynamic *>(BV);
|
||
SPIRVValue *Vec = VID->getVector();
|
||
if (Vec->getType()->getOpCode() == internal::OpTypeJointMatrixINTEL) {
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(VID, BB));
|
||
}
|
||
return mapValue(
|
||
BV, InsertElementInst::Create(
|
||
transValue(Vec, F, BB), transValue(VID->getComponent(), F, BB),
|
||
transValue(VID->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(PoisonValue::get(Int32Ty));
|
||
else
|
||
Components.push_back(ConstantInt::get(Int32Ty, I));
|
||
}
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
Value *Vec1 = transValue(VS->getVector1(), F, BB);
|
||
Value *Vec2 = transValue(VS->getVector2(), F, BB);
|
||
auto *Vec1Ty = cast<FixedVectorType>(Vec1->getType());
|
||
auto *Vec2Ty = cast<FixedVectorType>(Vec2->getType());
|
||
if (Vec1Ty->getNumElements() != Vec2Ty->getNumElements()) {
|
||
// LLVM's shufflevector requires that the two vector operands have the
|
||
// same type; SPIR-V's OpVectorShuffle allows the vector operands to
|
||
// differ in the number of components. Adjust for that by extending
|
||
// the smaller vector.
|
||
if (Vec1Ty->getNumElements() < Vec2Ty->getNumElements()) {
|
||
Vec1 = extendVector(Vec1, Vec2Ty, Builder);
|
||
// Extending Vec1 requires offsetting any Vec2 indices in Components by
|
||
// the number of new elements.
|
||
unsigned Offset = Vec2Ty->getNumElements() - Vec1Ty->getNumElements();
|
||
unsigned Vec2Start = Vec1Ty->getNumElements();
|
||
for (auto &C : Components) {
|
||
if (auto *CI = dyn_cast<ConstantInt>(C)) {
|
||
uint64_t V = CI->getZExtValue();
|
||
if (V >= Vec2Start) {
|
||
// This is a Vec2 index; add the offset to it.
|
||
C = ConstantInt::get(Int32Ty, V + Offset);
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
Vec2 = extendVector(Vec2, Vec1Ty, Builder);
|
||
}
|
||
}
|
||
return mapValue(
|
||
BV, Builder.CreateShuffleVector(
|
||
Vec1, Vec2, ConstantVector::get(Components), BV->getName()));
|
||
}
|
||
|
||
case OpBitReverse: {
|
||
auto *BR = static_cast<SPIRVUnary *>(BV);
|
||
auto *Ty = transType(BV->getType());
|
||
Function *intr =
|
||
Intrinsic::getOrInsertDeclaration(M, llvm::Intrinsic::bitreverse, Ty);
|
||
auto *Call = CallInst::Create(intr, transValue(BR->getOperand(0), F, BB),
|
||
BR->getName(), BB);
|
||
return mapValue(BV, Call);
|
||
}
|
||
|
||
case OpFunctionCall: {
|
||
SPIRVFunctionCall *BC = static_cast<SPIRVFunctionCall *>(BV);
|
||
std::vector<Value *> Args = transValue(BC->getArgumentValues(), F, BB);
|
||
auto *Call = CallInst::Create(transFunction(BC->getFunction()), Args,
|
||
BC->getName(), BB);
|
||
setCallingConv(Call);
|
||
setAttrByCalledFunc(Call);
|
||
return mapValue(BV, Call);
|
||
}
|
||
|
||
case OpAsmCallINTEL:
|
||
return mapValue(
|
||
BV, transAsmCallINTEL(static_cast<SPIRVAsmCallINTEL *>(BV), F, BB));
|
||
|
||
case OpFunctionPointerCallINTEL: {
|
||
SPIRVFunctionPointerCallINTEL *BC =
|
||
static_cast<SPIRVFunctionPointerCallINTEL *>(BV);
|
||
auto *V = transValue(BC->getCalledValue(), F, BB);
|
||
auto *SpirvFnTy = BC->getCalledValue()->getType()->getPointerElementType();
|
||
auto *FnTy = cast<FunctionType>(transType(SpirvFnTy));
|
||
auto *Call = CallInst::Create(
|
||
FnTy, V, transValue(BC->getArgumentValues(), F, BB), BC->getName(), BB);
|
||
transFunctionPointerCallArgumentAttributes(
|
||
BV, Call, static_cast<SPIRVTypeFunction *>(SpirvFnTy));
|
||
// Assuming we are calling a regular device function
|
||
Call->setCallingConv(CallingConv::SPIR_FUNC);
|
||
// Don't set attributes, because at translation time we don't know which
|
||
// function exactly we are calling.
|
||
return mapValue(BV, Call);
|
||
}
|
||
|
||
case OpAssumeTrueKHR: {
|
||
IRBuilder<> Builder(BB);
|
||
SPIRVAssumeTrueKHR *BC = static_cast<SPIRVAssumeTrueKHR *>(BV);
|
||
Value *Condition = transValue(BC->getCondition(), F, BB);
|
||
return mapValue(BV, Builder.CreateAssumption(Condition));
|
||
}
|
||
|
||
case OpExpectKHR: {
|
||
IRBuilder<> Builder(BB);
|
||
SPIRVExpectKHRInstBase *BC = static_cast<SPIRVExpectKHRInstBase *>(BV);
|
||
Type *RetTy = transType(BC->getType());
|
||
Value *Val = transValue(BC->getOperand(0), F, BB);
|
||
Value *ExpVal = transValue(BC->getOperand(1), F, BB);
|
||
return mapValue(
|
||
BV, Builder.CreateIntrinsic(Intrinsic::expect, RetTy, {Val, ExpVal}));
|
||
}
|
||
|
||
case OpUntypedPrefetchKHR: {
|
||
// Do the same as transOCLBuiltinFromExtInst() but for OpUntypedPrefetchKHR.
|
||
auto *BC = static_cast<SPIRVUntypedPrefetchKHR *>(BV);
|
||
|
||
std::vector<Type *> ArgTypes =
|
||
transTypeVector(BC->getValueTypes(BC->getArguments()), true);
|
||
Type *RetTy = Type::getVoidTy(*Context);
|
||
|
||
std::string MangledName =
|
||
getSPIRVFriendlyIRFunctionName(OpenCLLIB::Prefetch, ArgTypes, RetTy);
|
||
opaquifyTypedPointers(ArgTypes);
|
||
|
||
FunctionType *FT = FunctionType::get(RetTy, ArgTypes, false);
|
||
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);
|
||
if (isFuncReadNone(OCLExtOpMap::map(OpenCLLIB::Prefetch)))
|
||
F->setDoesNotAccessMemory();
|
||
}
|
||
|
||
auto Args = transValue(BC->getValues(BC->getArguments()), F, BB);
|
||
CallInst *CI = CallInst::Create(F, Args, BC->getName(), BB);
|
||
setCallingConv(CI);
|
||
addFnAttr(CI, Attribute::NoUnwind);
|
||
|
||
return mapValue(BV, CI);
|
||
}
|
||
case OpExtInst: {
|
||
auto *ExtInst = static_cast<SPIRVExtInst *>(BV);
|
||
switch (ExtInst->getExtSetKind()) {
|
||
case SPIRVEIS_OpenCL: {
|
||
auto *V = mapValue(BV, transOCLBuiltinFromExtInst(ExtInst, BB));
|
||
applyFPFastMathModeDecorations(BV, static_cast<Instruction *>(V));
|
||
return V;
|
||
}
|
||
case SPIRVEIS_Debug:
|
||
case SPIRVEIS_OpenCL_DebugInfo_100:
|
||
case SPIRVEIS_NonSemantic_Shader_DebugInfo_100:
|
||
case SPIRVEIS_NonSemantic_Shader_DebugInfo_200:
|
||
DbgTran->transDebugIntrinsic(ExtInst, BB);
|
||
return mapValue(BV, nullptr);
|
||
default:
|
||
llvm_unreachable("Unknown extended instruction set!");
|
||
}
|
||
}
|
||
|
||
case OpSNegate: {
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
SPIRVUnary *BC = static_cast<SPIRVUnary *>(BV);
|
||
if (BV->getType()->isTypeCooperativeMatrixKHR()) {
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(BC, BB));
|
||
}
|
||
auto *Neg =
|
||
Builder.CreateNeg(transValue(BC->getOperand(0), F, BB), BV->getName());
|
||
if (auto *NegInst = dyn_cast<Instruction>(Neg)) {
|
||
applyNoIntegerWrapDecorations(BV, NegInst);
|
||
}
|
||
return mapValue(BV, Neg);
|
||
}
|
||
|
||
case OpFMod: {
|
||
// translate OpFMod(a, b) to:
|
||
// r = frem(a, b)
|
||
// c = copysign(r, b)
|
||
// needs_fixing = islessgreater(r, c)
|
||
// result = needs_fixing ? r + b : c
|
||
IRBuilder<> Builder(BB);
|
||
SPIRVFMod *FMod = static_cast<SPIRVFMod *>(BV);
|
||
auto *Dividend = transValue(FMod->getOperand(0), F, BB);
|
||
auto *Divisor = transValue(FMod->getOperand(1), F, BB);
|
||
auto *FRem = Builder.CreateFRem(Dividend, Divisor, "frem.res");
|
||
auto *CopySign = Builder.CreateBinaryIntrinsic(
|
||
llvm::Intrinsic::copysign, FRem, Divisor, nullptr, "copysign.res");
|
||
auto *FAdd = Builder.CreateFAdd(FRem, Divisor, "fadd.res");
|
||
auto *Cmp = Builder.CreateFCmpONE(FRem, CopySign, "cmp.res");
|
||
auto *Select = Builder.CreateSelect(Cmp, FAdd, CopySign);
|
||
return mapValue(BV, Select);
|
||
}
|
||
|
||
case OpSMod: {
|
||
// translate OpSMod(a, b) to:
|
||
// r = srem(a, b)
|
||
// needs_fixing = ((a < 0) != (b < 0) && r != 0)
|
||
// result = needs_fixing ? r + b : r
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
SPIRVSMod *SMod = static_cast<SPIRVSMod *>(BV);
|
||
auto *Dividend = transValue(SMod->getOperand(0), F, BB);
|
||
auto *Divisor = transValue(SMod->getOperand(1), F, BB);
|
||
auto *SRem = Builder.CreateSRem(Dividend, Divisor, "srem.res");
|
||
auto *Xor = Builder.CreateXor(Dividend, Divisor, "xor.res");
|
||
auto *Zero = ConstantInt::getNullValue(Dividend->getType());
|
||
auto *CmpSign = Builder.CreateICmpSLT(Xor, Zero, "cmpsign.res");
|
||
auto *CmpSRem = Builder.CreateICmpNE(SRem, Zero, "cmpsrem.res");
|
||
auto *Add = Builder.CreateNSWAdd(SRem, Divisor, "add.res");
|
||
auto *Cmp = Builder.CreateAnd(CmpSign, CmpSRem, "cmp.res");
|
||
auto *Select = Builder.CreateSelect(Cmp, Add, SRem);
|
||
return mapValue(BV, Select);
|
||
}
|
||
|
||
case OpFNegate: {
|
||
SPIRVUnary *BC = static_cast<SPIRVUnary *>(BV);
|
||
if (BV->getType()->isTypeCooperativeMatrixKHR()) {
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(BC, BB));
|
||
}
|
||
auto *Neg = UnaryOperator::CreateFNeg(transValue(BC->getOperand(0), F, BB),
|
||
BV->getName(), BB);
|
||
applyFPFastMathModeDecorations(BV, Neg);
|
||
return mapValue(BV, Neg);
|
||
}
|
||
|
||
case OpNot:
|
||
case OpLogicalNot: {
|
||
IRBuilder<> Builder(*Context);
|
||
if (BB) {
|
||
Builder.SetInsertPoint(BB);
|
||
}
|
||
SPIRVUnary *BC = static_cast<SPIRVUnary *>(BV);
|
||
return mapValue(BV, Builder.CreateNot(transValue(BC->getOperand(0), F, BB),
|
||
BV->getName()));
|
||
}
|
||
|
||
case OpAll:
|
||
case OpAny:
|
||
return mapValue(BV, transAllAny(static_cast<SPIRVInstruction *>(BV), BB));
|
||
|
||
case OpIsFinite:
|
||
case OpIsInf:
|
||
case OpIsNan:
|
||
case OpIsNormal:
|
||
case OpSignBitSet:
|
||
return mapValue(BV,
|
||
transRelational(static_cast<SPIRVInstruction *>(BV), BB));
|
||
case OpIAddCarry:
|
||
case OpISubBorrow: {
|
||
IRBuilder Builder(BB);
|
||
auto *BC = static_cast<SPIRVBinary *>(BV);
|
||
Intrinsic::ID ID = OC == OpIAddCarry ? Intrinsic::uadd_with_overflow
|
||
: Intrinsic::usub_with_overflow;
|
||
auto *Inst =
|
||
Builder.CreateBinaryIntrinsic(ID, transValue(BC->getOperand(0), F, BB),
|
||
transValue(BC->getOperand(1), F, BB));
|
||
|
||
// Extract components of the result.
|
||
auto *Result = Builder.CreateExtractValue(Inst, 0); // iN result
|
||
auto *Carry = Builder.CreateExtractValue(Inst, 1); // i1 overflow
|
||
|
||
// Convert {iN, i1} into {iN, iN} for SPIR-V compatibility.
|
||
Value *CarryInt;
|
||
if (Carry->getType()->isVectorTy()) {
|
||
CarryInt = Builder.CreateZExt(
|
||
Carry, VectorType::get(
|
||
cast<VectorType>(Result->getType())->getElementType(),
|
||
cast<VectorType>(Carry->getType())->getElementCount()));
|
||
} else {
|
||
CarryInt = Builder.CreateZExt(Carry, Result->getType());
|
||
}
|
||
auto *ResultStruct =
|
||
Builder.CreateInsertValue(PoisonValue::get(StructType::get(
|
||
Result->getType(), CarryInt->getType())),
|
||
Result, 0);
|
||
ResultStruct = Builder.CreateInsertValue(ResultStruct, CarryInt, 1);
|
||
|
||
return mapValue(BV, ResultStruct);
|
||
}
|
||
case OpSMulExtended: {
|
||
auto *BC = static_cast<SPIRVBinary *>(BV);
|
||
return mapValue(BV, transBuiltinFromInst("__spirv_SMulExtended", BC, BB));
|
||
}
|
||
case OpUMulExtended: {
|
||
auto *BC = static_cast<SPIRVBinary *>(BV);
|
||
return mapValue(BV, transBuiltinFromInst("__spirv_UMulExtended", BC, BB));
|
||
}
|
||
case OpGetKernelWorkGroupSize:
|
||
case OpGetKernelPreferredWorkGroupSizeMultiple:
|
||
return mapValue(
|
||
BV, transWGSizeQueryBI(static_cast<SPIRVInstruction *>(BV), BB));
|
||
case OpGetKernelNDrangeMaxSubGroupSize:
|
||
case OpGetKernelNDrangeSubGroupCount:
|
||
return mapValue(
|
||
BV, transSGSizeQueryBI(static_cast<SPIRVInstruction *>(BV), BB));
|
||
case OpFPGARegINTEL: {
|
||
IRBuilder<> Builder(BB);
|
||
|
||
SPIRVFPGARegINTELInstBase *BC =
|
||
static_cast<SPIRVFPGARegINTELInstBase *>(BV);
|
||
|
||
PointerType *Int8PtrTyPrivate = PointerType::get(*Context, SPIRAS_Private);
|
||
IntegerType *Int32Ty = Type::getInt32Ty(*Context);
|
||
|
||
Value *UndefInt8Ptr = PoisonValue::get(Int8PtrTyPrivate);
|
||
Value *UndefInt32 = PoisonValue::get(Int32Ty);
|
||
|
||
Constant *GS = Builder.CreateGlobalString(kOCLBuiltinName::FPGARegIntel);
|
||
|
||
Type *Ty = transType(BC->getType());
|
||
Value *Val = transValue(BC->getOperand(0), F, BB);
|
||
|
||
Value *ValAsArg = Val;
|
||
Type *RetTy = Ty;
|
||
auto IID = Intrinsic::annotation;
|
||
if (!isa<IntegerType>(Ty)) {
|
||
// All scalar types can be bitcasted to a same-sized integer
|
||
if (!isa<PointerType>(Ty) && !isa<StructType>(Ty)) {
|
||
RetTy = IntegerType::get(*Context, Ty->getPrimitiveSizeInBits());
|
||
ValAsArg = Builder.CreateBitCast(Val, RetTy);
|
||
}
|
||
// If pointer type or struct type
|
||
else {
|
||
IID = Intrinsic::ptr_annotation;
|
||
auto *PtrTy = dyn_cast<PointerType>(Ty);
|
||
if (PtrTy) {
|
||
RetTy = PtrTy;
|
||
} else {
|
||
// If a struct - bitcast to i8*
|
||
RetTy = Int8PtrTyPrivate;
|
||
ValAsArg = Builder.CreateBitCast(Val, RetTy);
|
||
}
|
||
Value *Args[] = {ValAsArg, GS, UndefInt8Ptr, UndefInt32, UndefInt8Ptr};
|
||
auto *IntrinsicCall =
|
||
Builder.CreateIntrinsic(IID, {RetTy, GS->getType()}, Args);
|
||
return mapValue(BV, IntrinsicCall);
|
||
}
|
||
}
|
||
|
||
Value *Args[] = {ValAsArg, GS, UndefInt8Ptr, UndefInt32};
|
||
auto *IntrinsicCall =
|
||
Builder.CreateIntrinsic(IID, {RetTy, GS->getType()}, Args);
|
||
return mapValue(BV, IntrinsicCall);
|
||
}
|
||
|
||
case OpFixedSqrtINTEL:
|
||
case OpFixedRecipINTEL:
|
||
case OpFixedRsqrtINTEL:
|
||
case OpFixedSinINTEL:
|
||
case OpFixedCosINTEL:
|
||
case OpFixedSinCosINTEL:
|
||
case OpFixedSinPiINTEL:
|
||
case OpFixedCosPiINTEL:
|
||
case OpFixedSinCosPiINTEL:
|
||
case OpFixedLogINTEL:
|
||
case OpFixedExpINTEL:
|
||
return mapValue(
|
||
BV, transFixedPointInst(static_cast<SPIRVInstruction *>(BV), BB));
|
||
|
||
case OpArbitraryFloatCastINTEL:
|
||
case OpArbitraryFloatCastFromIntINTEL:
|
||
case OpArbitraryFloatCastToIntINTEL:
|
||
case OpArbitraryFloatRecipINTEL:
|
||
case OpArbitraryFloatRSqrtINTEL:
|
||
case OpArbitraryFloatCbrtINTEL:
|
||
case OpArbitraryFloatSqrtINTEL:
|
||
case OpArbitraryFloatLogINTEL:
|
||
case OpArbitraryFloatLog2INTEL:
|
||
case OpArbitraryFloatLog10INTEL:
|
||
case OpArbitraryFloatLog1pINTEL:
|
||
case OpArbitraryFloatExpINTEL:
|
||
case OpArbitraryFloatExp2INTEL:
|
||
case OpArbitraryFloatExp10INTEL:
|
||
case OpArbitraryFloatExpm1INTEL:
|
||
case OpArbitraryFloatSinINTEL:
|
||
case OpArbitraryFloatCosINTEL:
|
||
case OpArbitraryFloatSinCosINTEL:
|
||
case OpArbitraryFloatSinPiINTEL:
|
||
case OpArbitraryFloatCosPiINTEL:
|
||
case OpArbitraryFloatSinCosPiINTEL:
|
||
case OpArbitraryFloatASinINTEL:
|
||
case OpArbitraryFloatASinPiINTEL:
|
||
case OpArbitraryFloatACosINTEL:
|
||
case OpArbitraryFloatACosPiINTEL:
|
||
case OpArbitraryFloatATanINTEL:
|
||
case OpArbitraryFloatATanPiINTEL:
|
||
return mapValue(BV,
|
||
transArbFloatInst(static_cast<SPIRVInstruction *>(BV), BB));
|
||
|
||
case OpArbitraryFloatAddINTEL:
|
||
case OpArbitraryFloatSubINTEL:
|
||
case OpArbitraryFloatMulINTEL:
|
||
case OpArbitraryFloatDivINTEL:
|
||
case OpArbitraryFloatGTINTEL:
|
||
case OpArbitraryFloatGEINTEL:
|
||
case OpArbitraryFloatLTINTEL:
|
||
case OpArbitraryFloatLEINTEL:
|
||
case OpArbitraryFloatEQINTEL:
|
||
case OpArbitraryFloatHypotINTEL:
|
||
case OpArbitraryFloatATan2INTEL:
|
||
case OpArbitraryFloatPowINTEL:
|
||
case OpArbitraryFloatPowRINTEL:
|
||
case OpArbitraryFloatPowNINTEL:
|
||
return mapValue(
|
||
BV, transArbFloatInst(static_cast<SPIRVInstruction *>(BV), BB, true));
|
||
|
||
case OpArithmeticFenceEXT: {
|
||
IRBuilder<> Builder(BB);
|
||
auto *BC = static_cast<SPIRVUnary *>(BV);
|
||
Type *RetTy = transType(BC->getType());
|
||
Value *Val = transValue(BC->getOperand(0), F, BB);
|
||
return mapValue(
|
||
BV, Builder.CreateIntrinsic(Intrinsic::arithmetic_fence, RetTy, Val));
|
||
}
|
||
case OpFmaKHR: {
|
||
IRBuilder<> Builder(BB);
|
||
auto *BC = static_cast<SPIRVFmaKHR *>(BV);
|
||
return mapValue(
|
||
BV, Builder.CreateIntrinsic(Intrinsic::fma, transType(BC->getType()),
|
||
{transValue(BC->getOperand(0), F, BB),
|
||
transValue(BC->getOperand(1), F, BB),
|
||
transValue(BC->getOperand(2), F, BB)}));
|
||
}
|
||
case internal::OpMaskedGatherINTEL: {
|
||
IRBuilder<> Builder(BB);
|
||
auto *Inst = static_cast<SPIRVMaskedGatherINTELInst *>(BV);
|
||
Type *RetTy = transType(Inst->getType());
|
||
Value *PtrVector = transValue(Inst->getOperand(0), F, BB);
|
||
uint32_t Alignment = Inst->getOpWord(1);
|
||
Value *Mask = transValue(Inst->getOperand(2), F, BB);
|
||
Value *FillEmpty = transValue(Inst->getOperand(3), F, BB);
|
||
return mapValue(BV, Builder.CreateMaskedGather(RetTy, PtrVector,
|
||
Align(Alignment), Mask,
|
||
FillEmpty));
|
||
}
|
||
|
||
case internal::OpMaskedScatterINTEL: {
|
||
IRBuilder<> Builder(BB);
|
||
auto *Inst = static_cast<SPIRVMaskedScatterINTELInst *>(BV);
|
||
Value *InputVector = transValue(Inst->getOperand(0), F, BB);
|
||
Value *PtrVector = transValue(Inst->getOperand(1), F, BB);
|
||
uint32_t Alignment = Inst->getOpWord(2);
|
||
Value *Mask = transValue(Inst->getOperand(3), F, BB);
|
||
return mapValue(BV, Builder.CreateMaskedScatter(InputVector, PtrVector,
|
||
Align(Alignment), Mask));
|
||
}
|
||
|
||
default: {
|
||
auto OC = BV->getOpCode();
|
||
if (isCmpOpCode(OC))
|
||
return mapValue(BV, transCmpInst(BV, BB, F));
|
||
|
||
if (OCLSPIRVBuiltinMap::rfind(OC, nullptr))
|
||
return mapValue(BV, transSPIRVBuiltinFromInst(
|
||
static_cast<SPIRVInstruction *>(BV), BB));
|
||
|
||
if (isBinaryShiftLogicalBitwiseOpCode(OC) || isLogicalOpCode(OC))
|
||
return mapValue(BV, transShiftLogicalBitwiseInst(BV, BB, F));
|
||
|
||
if (isCvtOpCode(OC) && OC != OpGenericCastToPtrExplicit) {
|
||
auto *BI = static_cast<SPIRVInstruction *>(BV);
|
||
Value *Inst = nullptr;
|
||
if (BI->hasFPRoundingMode() || BI->isSaturatedConversion()) {
|
||
Inst = transSPIRVBuiltinFromInst(BI, BB);
|
||
} else if (BI->getType()->isTypeCooperativeMatrixKHR()) {
|
||
// For cooperative matrix conversions generate __builtin_spirv
|
||
// conversions instead of __spirv_FConvert in case of mini-float
|
||
// type element type.
|
||
auto *OutMatrixElementTy =
|
||
static_cast<SPIRVTypeCooperativeMatrixKHR *>(BI->getType())
|
||
->getCompType();
|
||
auto *InMatrixElementTy =
|
||
static_cast<SPIRVTypeCooperativeMatrixKHR *>(
|
||
static_cast<SPIRVUnary *>(BI)->getOperand(0)->getType())
|
||
->getCompType();
|
||
if (OutMatrixElementTy->isTypeFloat(8, FPEncodingFloat8E4M3EXT) ||
|
||
OutMatrixElementTy->isTypeFloat(8, FPEncodingFloat8E5M2EXT) ||
|
||
InMatrixElementTy->isTypeFloat(8, FPEncodingFloat8E4M3EXT) ||
|
||
InMatrixElementTy->isTypeFloat(8, FPEncodingFloat8E5M2EXT) ||
|
||
OutMatrixElementTy->isTypeFloat(4, FPEncodingFloat4E2M1EXT) ||
|
||
InMatrixElementTy->isTypeFloat(4, FPEncodingFloat4E2M1EXT) ||
|
||
OutMatrixElementTy->isTypeFloat(
|
||
4, internal::FPEncodingFloat4E2M1INTEL) ||
|
||
InMatrixElementTy->isTypeFloat(4,
|
||
internal::FPEncodingFloat4E2M1INTEL))
|
||
Inst = transConvertInst(BV, F, BB);
|
||
else
|
||
Inst = transSPIRVBuiltinFromInst(BI, BB);
|
||
} else {
|
||
Inst = transConvertInst(BV, F, BB);
|
||
}
|
||
return mapValue(BV, Inst);
|
||
}
|
||
if (isIntelCvtOpCode(OC))
|
||
return mapValue(BV, transConvertInst(BV, F, BB));
|
||
return mapValue(
|
||
BV, transSPIRVBuiltinFromInst(static_cast<SPIRVInstruction *>(BV), BB));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Get meaningful suffix for adding at the end of the function name to avoid
|
||
// ascending numerical suffixes. It is useful in situations, where the same
|
||
// function is called twice or more in one basic block. So, the function name is
|
||
// formed in the following way: [FuncName].[ReturnTy].[InputTy]
|
||
static std::string getFuncAPIntSuffix(const Type *RetTy, const Type *In1Ty,
|
||
const Type *In2Ty = nullptr) {
|
||
std::stringstream Suffix;
|
||
Suffix << ".i" << RetTy->getIntegerBitWidth() << ".i"
|
||
<< In1Ty->getIntegerBitWidth();
|
||
if (In2Ty)
|
||
Suffix << ".i" << In2Ty->getIntegerBitWidth();
|
||
return Suffix.str();
|
||
}
|
||
|
||
Value *SPIRVToLLVM::transFixedPointInst(SPIRVInstruction *BI, BasicBlock *BB) {
|
||
// LLVM fixed point functions return value:
|
||
// iN (arbitrary precision integer of N bits length)
|
||
// Arguments:
|
||
// A(iN), S(i1), I(i32), rI(i32), Quantization(i32), Overflow(i32)
|
||
// If return value wider than 64 bit:
|
||
// iN addrspace(4)* sret(iN), A(iN), S(i1), I(i32), rI(i32),
|
||
// Quantization(i32), Overflow(i32)
|
||
|
||
// SPIR-V fixed point instruction contains:
|
||
// <id>ResTy Res<id> In<id> Literal S Literal I Literal rI Literal Q Literal O
|
||
|
||
Type *RetTy = transType(BI->getType());
|
||
|
||
auto *Inst = static_cast<SPIRVFixedPointIntelInst *>(BI);
|
||
Type *InTy = transType(Inst->getOperand(0)->getType());
|
||
|
||
IntegerType *Int32Ty = IntegerType::get(*Context, 32);
|
||
IntegerType *Int1Ty = IntegerType::get(*Context, 1);
|
||
|
||
SmallVector<Type *, 8> ArgTys;
|
||
std::vector<Value *> Args;
|
||
Args.reserve(8);
|
||
if (RetTy->getIntegerBitWidth() > 64) {
|
||
llvm::PointerType *RetPtrTy =
|
||
llvm::PointerType::get(*Context, SPIRAS_Generic);
|
||
Value *Alloca =
|
||
new AllocaInst(RetTy, M->getDataLayout().getAllocaAddrSpace(), "", BB);
|
||
Value *RetValPtr = new AddrSpaceCastInst(Alloca, RetPtrTy, "", BB);
|
||
ArgTys.emplace_back(RetPtrTy);
|
||
Args.emplace_back(RetValPtr);
|
||
}
|
||
|
||
ArgTys.insert(ArgTys.end(),
|
||
{InTy, Int1Ty, Int32Ty, Int32Ty, Int32Ty, Int32Ty});
|
||
|
||
auto Words = Inst->getOpWords();
|
||
Args.emplace_back(transValue(Inst->getOperand(0), BB->getParent(), BB));
|
||
Args.emplace_back(ConstantInt::get(Int1Ty, Words[1]));
|
||
for (int I = 2; I <= 5; I++)
|
||
Args.emplace_back(ConstantInt::get(Int32Ty, Words[I]));
|
||
|
||
Type *FuncRetTy =
|
||
(RetTy->getIntegerBitWidth() <= 64) ? RetTy : Type::getVoidTy(*Context);
|
||
FunctionType *FT = FunctionType::get(FuncRetTy, ArgTys, false);
|
||
|
||
Op OpCode = Inst->getOpCode();
|
||
std::string FuncName =
|
||
SPIRVFixedPointIntelMap::rmap(OpCode) + getFuncAPIntSuffix(RetTy, InTy);
|
||
|
||
FunctionCallee FCallee = M->getOrInsertFunction(FuncName, FT);
|
||
|
||
auto *Func = cast<Function>(FCallee.getCallee());
|
||
Func->setCallingConv(CallingConv::SPIR_FUNC);
|
||
if (isFuncNoUnwind())
|
||
Func->addFnAttr(Attribute::NoUnwind);
|
||
|
||
if (RetTy->getIntegerBitWidth() <= 64)
|
||
return CallInst::Create(FCallee, Args, "", BB);
|
||
|
||
Func->addParamAttr(
|
||
0, Attribute::get(*Context, Attribute::AttrKind::StructRet, RetTy));
|
||
CallInst *APIntInst = CallInst::Create(FCallee, Args, "", BB);
|
||
APIntInst->addParamAttr(
|
||
0, Attribute::get(*Context, Attribute::AttrKind::StructRet, RetTy));
|
||
|
||
return static_cast<Value *>(new LoadInst(RetTy, Args[0], "", false, BB));
|
||
}
|
||
|
||
Value *SPIRVToLLVM::transArbFloatInst(SPIRVInstruction *BI, BasicBlock *BB,
|
||
bool IsBinaryInst) {
|
||
// Format of instructions Add, Sub, Mul, Div, Hypot, ATan2, Pow, PowR:
|
||
// LLVM arbitrary floating point functions return value:
|
||
// iN (arbitrary precision integer of N bits length)
|
||
// Arguments: A(iN), MA(i32), B(iN), MB(i32), Mout(i32),
|
||
// EnableSubnormals(i32), RoundingMode(i32),
|
||
// RoundingAccuracy(i32)
|
||
// where A, B and return values are of arbitrary precision integer type.
|
||
// SPIR-V arbitrary floating point instruction layout:
|
||
// <id>ResTy Res<id> A<id> Literal MA B<id> Literal MB Literal Mout
|
||
// Literal EnableSubnormals Literal RoundingMode
|
||
// Literal RoundingAccuracy
|
||
|
||
// Format of instructions GT, GE, LT, LE, EQ:
|
||
// LLVM arbitrary floating point functions return value: Bool
|
||
// Arguments: A(iN), MA(i32), B(iN), MB(i32)
|
||
// where A and B are of arbitrary precision integer type.
|
||
// SPIR-V arbitrary floating point instruction layout:
|
||
// <id>ResTy Res<id> A<id> Literal MA B<id> Literal MB
|
||
|
||
// Format of instruction PowN:
|
||
// LLVM arbitrary floating point functions return value: iN
|
||
// Arguments: A(iN), MA(i32), B(iN), SignOfB(i1), Mout(i32),
|
||
// EnableSubnormals(i32), RoundingMode(i32),
|
||
// RoundingAccuracy(i32)
|
||
// where A, B and return values are of arbitrary precision integer type.
|
||
// SPIR-V arbitrary floating point instruction layout:
|
||
// <id>ResTy Res<id> A<id> Literal MA B<id> Literal SignOfB Literal Mout
|
||
// Literal EnableSubnormals Literal RoundingMode
|
||
// Literal RoundingAccuracy
|
||
|
||
// Format of instruction CastFromInt:
|
||
// LLVM arbitrary floating point functions return value: iN
|
||
// Arguments: A(iN), Mout(i32), FromSign(bool), EnableSubnormals(i32),
|
||
// RoundingMode(i32), RoundingAccuracy(i32)
|
||
// where A and return values are of arbitrary precision integer type.
|
||
// SPIR-V arbitrary floating point instruction layout:
|
||
// <id>ResTy Res<id> A<id> Literal Mout Literal FromSign
|
||
// Literal EnableSubnormals Literal RoundingMode
|
||
// Literal RoundingAccuracy
|
||
|
||
// Format of instruction CastToInt:
|
||
// LLVM arbitrary floating point functions return value: iN
|
||
// Arguments: A(iN), MA(i32), ToSign(bool), EnableSubnormals(i32),
|
||
// RoundingMode(i32), RoundingAccuracy(i32)
|
||
// where A and return values are of arbitrary precision integer type.
|
||
// SPIR-V arbitrary floating point instruction layout:
|
||
// <id>ResTy Res<id> A<id> Literal MA Literal ToSign
|
||
// Literal EnableSubnormals Literal RoundingMode
|
||
// Literal RoundingAccuracy
|
||
|
||
// Format of other instructions:
|
||
// LLVM arbitrary floating point functions return value: iN
|
||
// Arguments: A(iN), MA(i32), Mout(i32), EnableSubnormals(i32),
|
||
// RoundingMode(i32), RoundingAccuracy(i32)
|
||
// where A and return values are of arbitrary precision integer type.
|
||
// SPIR-V arbitrary floating point instruction layout:
|
||
// <id>ResTy Res<id> A<id> Literal MA Literal Mout Literal EnableSubnormals
|
||
// Literal RoundingMode Literal RoundingAccuracy
|
||
|
||
Type *RetTy = transType(BI->getType());
|
||
IntegerType *Int1Ty = Type::getInt1Ty(*Context);
|
||
IntegerType *Int32Ty = Type::getInt32Ty(*Context);
|
||
|
||
auto *Inst = static_cast<SPIRVArbFloatIntelInst *>(BI);
|
||
|
||
Type *ATy = transType(Inst->getOperand(0)->getType());
|
||
Type *BTy = nullptr;
|
||
|
||
// Words contain:
|
||
// A<id> [Literal MA] [B<id>] [Literal MB] [Literal Mout] [Literal Sign]
|
||
// [Literal EnableSubnormals Literal RoundingMode Literal RoundingAccuracy]
|
||
const std::vector<SPIRVWord> Words = Inst->getOpWords();
|
||
auto WordsItr = Words.begin() + 1; /* Skip word for A input id */
|
||
|
||
SmallVector<Type *, 8> ArgTys;
|
||
std::vector<Value *> Args;
|
||
|
||
if (RetTy->getIntegerBitWidth() > 64) {
|
||
llvm::PointerType *RetPtrTy =
|
||
llvm::PointerType::get(*Context, SPIRAS_Generic);
|
||
ArgTys.push_back(RetPtrTy);
|
||
Value *Alloca =
|
||
new AllocaInst(RetTy, M->getDataLayout().getAllocaAddrSpace(), "", BB);
|
||
Value *RetValPtr = new AddrSpaceCastInst(Alloca, RetPtrTy, "", BB);
|
||
Args.push_back(RetValPtr);
|
||
}
|
||
|
||
ArgTys.insert(ArgTys.end(), {ATy, Int32Ty});
|
||
// A - input
|
||
Args.emplace_back(transValue(Inst->getOperand(0), BB->getParent(), BB));
|
||
// MA/Mout - width of mantissa
|
||
Args.emplace_back(ConstantInt::get(Int32Ty, *WordsItr++));
|
||
|
||
Op OC = Inst->getOpCode();
|
||
if (OC == OpArbitraryFloatCastFromIntINTEL ||
|
||
OC == OpArbitraryFloatCastToIntINTEL) {
|
||
ArgTys.push_back(Int1Ty);
|
||
Args.push_back(ConstantInt::get(Int1Ty, *WordsItr++)); /* ToSign/FromSign */
|
||
}
|
||
|
||
if (IsBinaryInst) {
|
||
/* B - input */
|
||
BTy = transType(Inst->getOperand(2)->getType());
|
||
ArgTys.push_back(BTy);
|
||
Args.push_back(transValue(Inst->getOperand(2), BB->getParent(), BB));
|
||
++WordsItr; /* Skip word for B input id */
|
||
if (OC == OpArbitraryFloatPowNINTEL) {
|
||
ArgTys.push_back(Int1Ty);
|
||
Args.push_back(ConstantInt::get(Int1Ty, *WordsItr++)); /* SignOfB */
|
||
}
|
||
}
|
||
|
||
std::fill_n(std::back_inserter(ArgTys), Words.end() - WordsItr, Int32Ty);
|
||
std::transform(WordsItr, Words.end(), std::back_inserter(Args),
|
||
[Int32Ty](const SPIRVWord &Word) {
|
||
return ConstantInt::get(Int32Ty, Word);
|
||
});
|
||
|
||
std::string FuncName =
|
||
SPIRVArbFloatIntelMap::rmap(OC) + getFuncAPIntSuffix(RetTy, ATy, BTy);
|
||
|
||
Type *FuncRetTy =
|
||
(RetTy->getIntegerBitWidth() <= 64) ? RetTy : Type::getVoidTy(*Context);
|
||
FunctionType *FT = FunctionType::get(FuncRetTy, ArgTys, false);
|
||
FunctionCallee FCallee = M->getOrInsertFunction(FuncName, FT);
|
||
|
||
auto *Func = cast<Function>(FCallee.getCallee());
|
||
Func->setCallingConv(CallingConv::SPIR_FUNC);
|
||
if (isFuncNoUnwind())
|
||
Func->addFnAttr(Attribute::NoUnwind);
|
||
|
||
if (RetTy->getIntegerBitWidth() <= 64)
|
||
return CallInst::Create(Func, Args, "", BB);
|
||
|
||
Func->addParamAttr(
|
||
0, Attribute::get(*Context, Attribute::AttrKind::StructRet, RetTy));
|
||
CallInst *APFloatInst = CallInst::Create(FCallee, Args, "", BB);
|
||
APFloatInst->addParamAttr(
|
||
0, Attribute::get(*Context, Attribute::AttrKind::StructRet, RetTy));
|
||
|
||
return static_cast<Value *>(new LoadInst(RetTy, Args[0], "", false, BB));
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
void SPIRVToLLVM::transFunctionAttrs(SPIRVFunction *BF, Function *F) {
|
||
if (BF->hasDecorate(DecorationReferencedIndirectlyINTEL))
|
||
F->addFnAttr("referenced-indirectly");
|
||
if (isFuncNoUnwind())
|
||
F->addFnAttr(Attribute::NoUnwind);
|
||
foreachFuncCtlMask(BF, [&](Attribute::AttrKind Attr) { F->addFnAttr(Attr); });
|
||
|
||
for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
|
||
++I) {
|
||
auto *BA = BF->getArgument(I->getArgNo());
|
||
mapValue(BA, &(*I));
|
||
setName(&(*I), BA);
|
||
AttributeMask IllegalAttrs =
|
||
AttributeFuncs::typeIncompatible(I->getType(), I->getAttributes());
|
||
BA->foreachAttr([&](SPIRVFuncParamAttrKind Kind) {
|
||
// Skip this function parameter attribute as it will translated among
|
||
// OpenCL metadata
|
||
if (Kind == FunctionParameterAttributeRuntimeAlignedINTEL)
|
||
return;
|
||
if (Kind == FunctionParameterAttributeNoCapture) {
|
||
I->addAttr(Attribute::getWithCaptureInfo(F->getContext(),
|
||
CaptureInfo::none()));
|
||
return;
|
||
}
|
||
Attribute::AttrKind LLVMKind = SPIRSPIRVFuncParamAttrMap::rmap(Kind);
|
||
if (IllegalAttrs.contains(LLVMKind))
|
||
return;
|
||
Type *AttrTy = nullptr;
|
||
switch (LLVMKind) {
|
||
case Attribute::AttrKind::ByVal:
|
||
case Attribute::AttrKind::StructRet:
|
||
AttrTy = transType(BA->getType()->getPointerElementType());
|
||
break;
|
||
default:
|
||
break; // do nothing
|
||
}
|
||
// Make sure to use a correct constructor for a typed/typeless attribute
|
||
auto A = AttrTy ? Attribute::get(*Context, LLVMKind, AttrTy)
|
||
: Attribute::get(*Context, LLVMKind);
|
||
I->addAttr(A);
|
||
});
|
||
|
||
AttrBuilder Builder(*Context);
|
||
SPIRVWord MaxOffset = 0;
|
||
if (BA->hasDecorate(DecorationMaxByteOffset, 0, &MaxOffset))
|
||
Builder.addDereferenceableAttr(MaxOffset);
|
||
else {
|
||
SPIRVId MaxOffsetId;
|
||
if (BA->hasDecorateId(DecorationMaxByteOffsetId, 0, &MaxOffsetId)) {
|
||
if (auto MaxOffsetVal = transIdAsConstant(MaxOffsetId)) {
|
||
Builder.addDereferenceableAttr(*MaxOffsetVal);
|
||
}
|
||
}
|
||
}
|
||
if (auto Alignment = getAlignment(BA)) {
|
||
Builder.addAlignmentAttr(*Alignment);
|
||
}
|
||
I->addAttrs(Builder);
|
||
}
|
||
BF->foreachReturnValueAttr([&](SPIRVFuncParamAttrKind Kind) {
|
||
if (Kind == FunctionParameterAttributeNoWrite)
|
||
return;
|
||
F->addRetAttr(SPIRSPIRVFuncParamAttrMap::rmap(Kind));
|
||
});
|
||
}
|
||
|
||
namespace {
|
||
// One basic block can be a predecessor to another basic block more than
|
||
// once (https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/2702).
|
||
// This function fixes any PHIs that break this rule.
|
||
static void validatePhiPredecessors(Function *F) {
|
||
for (BasicBlock &BB : *F) {
|
||
bool UniquePreds = true;
|
||
DenseMap<BasicBlock *, unsigned> PredsCnt;
|
||
for (BasicBlock *PredBB : predecessors(&BB)) {
|
||
auto It = PredsCnt.try_emplace(PredBB, 1);
|
||
if (!It.second) {
|
||
UniquePreds = false;
|
||
++It.first->second;
|
||
}
|
||
}
|
||
if (UniquePreds)
|
||
continue;
|
||
// `phi` requires an incoming value per each predecessor instance, even
|
||
// it's the same basic block that has been already inserted as an incoming
|
||
// value of the `phi`.
|
||
for (PHINode &Phi : BB.phis()) {
|
||
SmallVector<Value *> Vs;
|
||
SmallVector<BasicBlock *> Bs;
|
||
SmallPtrSet<BasicBlock *, 8> UsedB;
|
||
for (auto [V, B] : zip(Phi.incoming_values(), Phi.blocks())) {
|
||
if (!UsedB.insert(B).second)
|
||
continue;
|
||
unsigned N = PredsCnt[B];
|
||
Vs.insert(Vs.end(), N, V);
|
||
Bs.insert(Bs.end(), N, B);
|
||
}
|
||
unsigned I = 0;
|
||
for (unsigned N = Phi.getNumIncomingValues(); I < N; ++I) {
|
||
Phi.setIncomingValue(I, Vs[I]);
|
||
Phi.setIncomingBlock(I, Bs[I]);
|
||
}
|
||
for (unsigned N = Vs.size(); I < N; ++I)
|
||
Phi.addIncoming(Vs[I], Bs[I]);
|
||
}
|
||
}
|
||
}
|
||
} // namespace
|
||
|
||
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF, unsigned AS) {
|
||
auto Loc = FuncMap.find(BF);
|
||
if (Loc != FuncMap.end())
|
||
return Loc->second;
|
||
|
||
auto IsKernel = isKernel(BF);
|
||
|
||
if (IsKernel) {
|
||
// search for a previous function with the same name
|
||
// upgrade it to a kernel and drop this if it's found
|
||
for (auto &I : FuncMap) {
|
||
const auto &BFName = I.getFirst()->getName();
|
||
if (BF->getName() == BFName) {
|
||
auto *F = I.getSecond();
|
||
F->setCallingConv(CallingConv::SPIR_KERNEL);
|
||
F->setLinkage(GlobalValue::ExternalLinkage);
|
||
F->setDSOLocal(false);
|
||
F = cast<Function>(mapValue(BF, F));
|
||
mapFunction(BF, F);
|
||
transFunctionAttrs(BF, F);
|
||
return F;
|
||
}
|
||
}
|
||
}
|
||
|
||
auto Linkage = IsKernel ? GlobalValue::ExternalLinkage : transLinkageType(BF);
|
||
FunctionType *FT = cast<FunctionType>(transType(BF->getFunctionType()));
|
||
std::string FuncName = BF->getName();
|
||
StringRef FuncNameRef(FuncName);
|
||
// Transform "@spirv.llvm_memset_p0i8_i32.volatile" to @llvm.memset.p0i8.i32
|
||
// assuming llvm.memset is supported by the device compiler. If this
|
||
// assumption is not safe, we should have a command line option to control
|
||
// this behavior.
|
||
if (FuncNameRef.starts_with("spirv.llvm_memset_p")) {
|
||
// We can't guarantee that the name is correctly mangled due to opaque
|
||
// pointers. Derive the correct name from the function type.
|
||
FuncName =
|
||
Intrinsic::getOrInsertDeclaration(
|
||
M, Intrinsic::memset, {FT->getParamType(0), FT->getParamType(2)})
|
||
->getName();
|
||
}
|
||
|
||
// Special handling for spirv.llvm_umul_with_overflow_* functions
|
||
// These were created during forward translation by lowering intrinsics.
|
||
// During reverse translation, we replace them with intrinsic calls.
|
||
if (FuncNameRef.starts_with("spirv.llvm_umul_with_overflow_")) {
|
||
Type *OverloadTy = FT->getParamType(0);
|
||
Function *F = Intrinsic::getOrInsertDeclaration(
|
||
M, Intrinsic::umul_with_overflow, {OverloadTy});
|
||
F = cast<Function>(mapValue(BF, F));
|
||
mapFunction(BF, F);
|
||
return F; // Skip body translation - intrinsic will be used instead
|
||
}
|
||
|
||
// Normal function handling.
|
||
if (FuncNameRef.consume_front("spirv.")) {
|
||
FuncNameRef.consume_back(".volatile");
|
||
FuncName = FuncNameRef.str();
|
||
std::replace(FuncName.begin(), FuncName.end(), '_', '.');
|
||
}
|
||
Function *F = M->getFunction(FuncName);
|
||
if (!F)
|
||
F = Function::Create(FT, Linkage, AS, FuncName, M);
|
||
|
||
F = cast<Function>(mapValue(BF, F));
|
||
mapFunction(BF, F);
|
||
|
||
if (F->isIntrinsic()) {
|
||
return F;
|
||
}
|
||
|
||
F->setCallingConv(IsKernel ? CallingConv::SPIR_KERNEL
|
||
: CallingConv::SPIR_FUNC);
|
||
transFunctionAttrs(BF, F);
|
||
|
||
// 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 = 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);
|
||
}
|
||
}
|
||
|
||
validatePhiPredecessors(F);
|
||
transLLVMLoopMetadata(F);
|
||
|
||
return F;
|
||
}
|
||
|
||
Value *SPIRVToLLVM::transAsmINTEL(SPIRVAsmINTEL *BA) {
|
||
assert(BA);
|
||
bool HasSideEffect = BA->hasDecorate(DecorationSideEffectsINTEL);
|
||
return InlineAsm::get(
|
||
cast<FunctionType>(transType(BA->getFunctionType())),
|
||
BA->getInstructions(), BA->getConstraints(), HasSideEffect,
|
||
/* IsAlignStack */ false, InlineAsm::AsmDialect::AD_ATT);
|
||
}
|
||
|
||
CallInst *SPIRVToLLVM::transAsmCallINTEL(SPIRVAsmCallINTEL *BI, Function *F,
|
||
BasicBlock *BB) {
|
||
assert(BI);
|
||
auto *IA = cast<InlineAsm>(transValue(BI->getAsm(), F, BB));
|
||
auto Args = transValue(BM->getValues(BI->getArguments()), F, BB);
|
||
return CallInst::Create(cast<FunctionType>(IA->getFunctionType()), IA, Args,
|
||
BI->getName(), BB);
|
||
}
|
||
|
||
/// LLVM convert builtin functions is translated to two instructions:
|
||
/// y = i32 islessgreater(float x, float z) ->
|
||
/// y = i32 ZExt(bool LessOrGreater(float x, float z))
|
||
/// When translating back, for simplicity, a trunc instruction is inserted
|
||
/// w = bool LessOrGreater(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();
|
||
if (isCmpOpCode(BI->getOpCode())) {
|
||
if (BT->isTypeBool())
|
||
RetTy = IntegerType::getInt32Ty(*Context);
|
||
else if (BT->isTypeVectorBool())
|
||
RetTy = FixedVectorType::get(
|
||
IntegerType::get(
|
||
*Context,
|
||
Args[0]->getType()->getVectorComponentType()->getBitWidth()),
|
||
BT->getVectorComponentCount());
|
||
else
|
||
llvm_unreachable("invalid compare instruction");
|
||
}
|
||
}
|
||
|
||
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 (SPIRVEnableStepExpansion &&
|
||
(DemangledName == "smoothstep" || DemangledName == "step"))
|
||
return expandOCLBuiltinWithScalarArg(CI, DemangledName);
|
||
return CI;
|
||
}
|
||
|
||
Value *SPIRVToLLVM::transBlockInvoke(SPIRVValue *Invoke, BasicBlock *BB) {
|
||
auto *TranslatedInvoke = transFunction(static_cast<SPIRVFunction *>(Invoke));
|
||
auto *Int8PtrTyGen = PointerType::get(*Context, SPIRAS_Generic);
|
||
return CastInst::CreatePointerBitCastOrAddrSpaceCast(TranslatedInvoke,
|
||
Int8PtrTyGen, "", BB);
|
||
}
|
||
|
||
Instruction *SPIRVToLLVM::transWGSizeQueryBI(SPIRVInstruction *BI,
|
||
BasicBlock *BB) {
|
||
std::string FName =
|
||
(BI->getOpCode() == OpGetKernelWorkGroupSize)
|
||
? "__get_kernel_work_group_size_impl"
|
||
: "__get_kernel_preferred_work_group_size_multiple_impl";
|
||
|
||
Function *F = M->getFunction(FName);
|
||
if (!F) {
|
||
auto *Int8PtrTyGen = PointerType::get(*Context, SPIRAS_Generic);
|
||
FunctionType *FT = FunctionType::get(Type::getInt32Ty(*Context),
|
||
{Int8PtrTyGen, Int8PtrTyGen}, false);
|
||
F = Function::Create(FT, GlobalValue::ExternalLinkage, FName, M);
|
||
if (isFuncNoUnwind())
|
||
F->addFnAttr(Attribute::NoUnwind);
|
||
}
|
||
auto Ops = BI->getOperands();
|
||
SmallVector<Value *, 2> Args = {transBlockInvoke(Ops[0], BB),
|
||
transValue(Ops[1], F, BB, false)};
|
||
auto *Call = CallInst::Create(F, Args, "", BB);
|
||
setName(Call, BI);
|
||
setAttrByCalledFunc(Call);
|
||
return Call;
|
||
}
|
||
|
||
Instruction *SPIRVToLLVM::transSGSizeQueryBI(SPIRVInstruction *BI,
|
||
BasicBlock *BB) {
|
||
std::string FName = (BI->getOpCode() == OpGetKernelNDrangeMaxSubGroupSize)
|
||
? "__get_kernel_max_sub_group_size_for_ndrange_impl"
|
||
: "__get_kernel_sub_group_count_for_ndrange_impl";
|
||
|
||
auto Ops = BI->getOperands();
|
||
Function *F = M->getFunction(FName);
|
||
if (!F) {
|
||
auto *Int8PtrTyGen = PointerType::get(*Context, SPIRAS_Generic);
|
||
SmallVector<Type *, 3> Tys = {
|
||
transType(Ops[0]->getType()), // ndrange
|
||
Int8PtrTyGen, // block_invoke
|
||
Int8PtrTyGen // block_literal
|
||
};
|
||
auto *FT = FunctionType::get(Type::getInt32Ty(*Context), Tys, false);
|
||
F = Function::Create(FT, GlobalValue::ExternalLinkage, FName, M);
|
||
if (isFuncNoUnwind())
|
||
F->addFnAttr(Attribute::NoUnwind);
|
||
}
|
||
SmallVector<Value *, 2> Args = {
|
||
transValue(Ops[0], F, BB, false), // ndrange
|
||
transBlockInvoke(Ops[1], BB), // block_invoke
|
||
transValue(Ops[2], F, BB, false) // block_literal
|
||
};
|
||
auto *Call = CallInst::Create(F, Args, "", BB);
|
||
setName(Call, BI);
|
||
setAttrByCalledFunc(Call);
|
||
return Call;
|
||
}
|
||
|
||
Instruction *SPIRVToLLVM::transBuiltinFromInst(const std::string &FuncName,
|
||
SPIRVInstruction *BI,
|
||
BasicBlock *BB) {
|
||
std::string MangledName;
|
||
auto Ops = BI->getOperands();
|
||
Op OC = BI->getOpCode();
|
||
if (isUntypedAccessChainOpCode(OC)) {
|
||
auto *AC = static_cast<SPIRVAccessChainBase *>(BI);
|
||
if (AC->getBaseType()->isTypeCooperativeMatrixKHR())
|
||
Ops.erase(Ops.begin());
|
||
}
|
||
Type *RetTy =
|
||
BI->hasType() ? transType(BI->getType()) : Type::getVoidTy(*Context);
|
||
transOCLBuiltinFromInstPreproc(BI, RetTy, Ops);
|
||
std::vector<Type *> ArgTys =
|
||
transTypeVector(SPIRVInstruction::getOperandTypes(Ops), true);
|
||
|
||
auto Ptr = findFirstPtrType(ArgTys);
|
||
if (Ptr < ArgTys.size() &&
|
||
BI->getValueType(Ops[Ptr]->getId())->isTypeUntypedPointerKHR()) {
|
||
// Special handling for "truly" untyped pointers to preserve correct
|
||
// builtin mangling of atomic and matrix operations.
|
||
if (isAtomicOpCodeUntypedPtrSupported(OC)) {
|
||
auto *AI = static_cast<SPIRVAtomicInstBase *>(BI);
|
||
ArgTys[Ptr] = TypedPointerType::get(
|
||
transType(AI->getSemanticType()),
|
||
SPIRSPIRVAddrSpaceMap::rmap(
|
||
BI->getValueType(Ops[Ptr]->getId())->getPointerStorageClass()));
|
||
}
|
||
}
|
||
|
||
for (unsigned I = 0; I < ArgTys.size(); I++) {
|
||
if (isa<PointerType>(ArgTys[I])) {
|
||
SPIRVType *OpTy = BI->getValueType(Ops[I]->getId());
|
||
// `Param` must be a pointer to an 8-bit integer type scalar.
|
||
// Avoid demangling for this argument if it's a pointer to get `Pc`
|
||
// mangling.
|
||
if (OC == OpEnqueueKernel && I == 7) {
|
||
if (ArgTys[I]->isPointerTy())
|
||
continue;
|
||
}
|
||
if (OpTy->isTypeUntypedPointerKHR()) {
|
||
auto *Val = transValue(Ops[I], BB->getParent(), BB);
|
||
Val = Val->stripPointerCasts();
|
||
if (isUntypedAccessChainOpCode(Ops[I]->getOpCode())) {
|
||
SPIRVType *BaseTy =
|
||
reinterpret_cast<SPIRVAccessChainBase *>(Ops[I])->getBaseType();
|
||
|
||
Type *Ty = nullptr;
|
||
if (BaseTy->isTypeArray())
|
||
Ty = transType(BaseTy->getArrayElementType());
|
||
else if (BaseTy->isTypeVector())
|
||
Ty = transType(BaseTy->getVectorComponentType());
|
||
else
|
||
Ty = transType(BaseTy);
|
||
ArgTys[I] = TypedPointerType::get(
|
||
Ty, SPIRSPIRVAddrSpaceMap::rmap(OpTy->getPointerStorageClass()));
|
||
} else if (auto *GEP = dyn_cast<GetElementPtrInst>(Val)) {
|
||
ArgTys[I] = TypedPointerType::get(
|
||
GEP->getSourceElementType(),
|
||
SPIRSPIRVAddrSpaceMap::rmap(OpTy->getPointerStorageClass()));
|
||
} else if (Ops[I]->getOpCode() == OpUntypedVariableKHR) {
|
||
SPIRVUntypedVariableKHR *UV =
|
||
static_cast<SPIRVUntypedVariableKHR *>(Ops[I]);
|
||
Type *Ty = transType(UV->getDataType());
|
||
ArgTys[I] = TypedPointerType::get(
|
||
Ty, SPIRSPIRVAddrSpaceMap::rmap(OpTy->getPointerStorageClass()));
|
||
} else if (auto *AI = dyn_cast<AllocaInst>(Val)) {
|
||
ArgTys[I] = TypedPointerType::get(
|
||
AI->getAllocatedType(),
|
||
SPIRSPIRVAddrSpaceMap::rmap(OpTy->getPointerStorageClass()));
|
||
} else if (Ops[I]->getOpCode() == OpFunctionParameter &&
|
||
!RetTy->isVoidTy()) {
|
||
// Pointer could be a function parameter. Assume that the type of
|
||
// the pointer is the same as the return type.
|
||
Type *Ty = nullptr;
|
||
// it return type is array type, assign its element type to Ty
|
||
if (RetTy->isArrayTy())
|
||
Ty = RetTy->getArrayElementType();
|
||
else if (RetTy->isVectorTy())
|
||
Ty = cast<VectorType>(RetTy)->getElementType();
|
||
else
|
||
Ty = RetTy;
|
||
|
||
ArgTys[I] = TypedPointerType::get(
|
||
Ty, SPIRSPIRVAddrSpaceMap::rmap(OpTy->getPointerStorageClass()));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for (auto &I : ArgTys) {
|
||
if (isa<FunctionType>(I)) {
|
||
I = TypedPointerType::get(I, SPIRAS_Private);
|
||
}
|
||
}
|
||
|
||
if (BM->getDesiredBIsRepresentation() != BIsRepresentation::SPIRVFriendlyIR)
|
||
mangleOpenClBuiltin(FuncName, ArgTys, MangledName);
|
||
else
|
||
MangledName = getSPIRVFriendlyIRFunctionName(FuncName, OC, ArgTys, Ops);
|
||
|
||
opaquifyTypedPointers(ArgTys);
|
||
|
||
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 (Func && Func->getFunctionType() != FT) {
|
||
dbgs() << "Warning: Function name conflict:\n"
|
||
<< *Func << '\n'
|
||
<< " => " << *FT << '\n';
|
||
})
|
||
if (!Func || Func->getFunctionType() != FT) {
|
||
LLVM_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);
|
||
if (isGroupOpCode(OC) || isGroupNonUniformOpcode(OC) ||
|
||
isIntelSubgroupOpCode(OC) || isSplitBarrierINTELOpCode(OC) ||
|
||
OC == OpControlBarrier)
|
||
Func->addFnAttr(Attribute::Convergent);
|
||
}
|
||
CallInst *Call;
|
||
// TODO: Remove the check for matrix type once drivers are updated.
|
||
if (OC == OpCooperativeMatrixLengthKHR &&
|
||
Ops[0]->getOpCode() == OpTypeCooperativeMatrixKHR) {
|
||
// OpCooperativeMatrixLengthKHR needs special handling as its operand is
|
||
// a Type instead of a Value.
|
||
llvm::Type *MatTy = transType(reinterpret_cast<SPIRVType *>(Ops[0]));
|
||
Call = CallInst::Create(Func, Constant::getNullValue(MatTy), "", BB);
|
||
} else {
|
||
Call = CallInst::Create(Func, transValue(Ops, BB->getParent(), BB), "", BB);
|
||
}
|
||
setName(Call, BI);
|
||
setAttrByCalledFunc(Call);
|
||
SPIRVDBG(spvdbgs() << "[transInstToBuiltinCall] " << *BI << " -> ";
|
||
dbgs() << *Call << '\n';)
|
||
Instruction *Inst = transOCLBuiltinPostproc(BI, Call, BB, FuncName);
|
||
return Inst;
|
||
}
|
||
|
||
SPIRVToLLVM::SPIRVToLLVM(Module *LLVMModule, SPIRVModule *TheSPIRVModule)
|
||
: BuiltinCallHelper(ManglingRules::OpenCL), M(LLVMModule),
|
||
BM(TheSPIRVModule) {
|
||
assert(M && "Initialization without an LLVM module is not allowed");
|
||
initialize(*M);
|
||
Context = &M->getContext();
|
||
if (BM->getDesiredBIsRepresentation() == BIsRepresentation::SPIRVFriendlyIR)
|
||
UseTargetTypes = true;
|
||
DbgTran.reset(new SPIRVToLLVMDbgTran(TheSPIRVModule, LLVMModule, this));
|
||
}
|
||
|
||
std::string getSPIRVFuncSuffix(SPIRVInstruction *BI) {
|
||
std::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()) {
|
||
default:
|
||
case AccessQualifierReadOnly:
|
||
Suffix = "_read";
|
||
break;
|
||
case AccessQualifierWriteOnly:
|
||
Suffix = "_write";
|
||
break;
|
||
case AccessQualifierReadWrite:
|
||
Suffix = "_read_write";
|
||
break;
|
||
}
|
||
}
|
||
if (BI->hasDecorate(DecorationSaturatedConversion)) {
|
||
Suffix += kSPIRVPostfix::Divider;
|
||
Suffix += kSPIRVPostfix::Sat;
|
||
}
|
||
SPIRVFPRoundingModeKind Kind;
|
||
if (BI->hasFPRoundingMode(&Kind)) {
|
||
Suffix += kSPIRVPostfix::Divider;
|
||
Suffix += SPIRSPIRVFPRoundingModeMap::rmap(Kind);
|
||
}
|
||
if (BI->getOpCode() == OpGenericCastToPtrExplicit) {
|
||
Suffix += kSPIRVPostfix::Divider;
|
||
auto *Ty = BI->getType();
|
||
auto GenericCastToPtrInst =
|
||
Ty->isTypeVectorPointer()
|
||
? Ty->getVectorComponentType()->getPointerStorageClass()
|
||
: Ty->getPointerStorageClass();
|
||
switch (GenericCastToPtrInst) {
|
||
case StorageClassCrossWorkgroup:
|
||
Suffix += std::string(kSPIRVPostfix::ToGlobal);
|
||
break;
|
||
case StorageClassWorkgroup:
|
||
Suffix += std::string(kSPIRVPostfix::ToLocal);
|
||
break;
|
||
case StorageClassFunction:
|
||
Suffix += std::string(kSPIRVPostfix::ToPrivate);
|
||
break;
|
||
default:
|
||
llvm_unreachable("Invalid address space");
|
||
}
|
||
}
|
||
if (BI->getOpCode() == OpBuildNDRange) {
|
||
Suffix += kSPIRVPostfix::Divider;
|
||
auto *NDRangeInst = static_cast<SPIRVBuildNDRange *>(BI);
|
||
auto *EleTy = ((NDRangeInst->getOperands())[0])->getType();
|
||
int Dim = EleTy->isTypeArray() ? EleTy->getArrayLength() : 1;
|
||
assert((EleTy->isTypeInt() && Dim == 1) ||
|
||
(EleTy->isTypeArray() && Dim >= 2 && Dim <= 3));
|
||
std::ostringstream OS;
|
||
OS << Dim;
|
||
Suffix += OS.str() + "D";
|
||
}
|
||
return Suffix;
|
||
}
|
||
|
||
Instruction *SPIRVToLLVM::transSPIRVBuiltinFromInst(SPIRVInstruction *BI,
|
||
BasicBlock *BB) {
|
||
assert(BB && "Invalid BB");
|
||
const auto OC = BI->getOpCode();
|
||
|
||
bool AddRetTypePostfix = false;
|
||
switch (static_cast<size_t>(OC)) {
|
||
case OpImageQuerySizeLod:
|
||
case OpImageQuerySize:
|
||
case OpImageRead:
|
||
case OpSubgroupImageBlockReadINTEL:
|
||
case OpSubgroupImageMediaBlockReadINTEL:
|
||
case OpSubgroupBlockReadINTEL:
|
||
case OpImageSampleExplicitLod:
|
||
case OpSDotKHR:
|
||
case OpUDotKHR:
|
||
case OpSUDotKHR:
|
||
case OpSDotAccSatKHR:
|
||
case OpUDotAccSatKHR:
|
||
case OpSUDotAccSatKHR:
|
||
case OpReadClockKHR:
|
||
case OpFmaKHR:
|
||
case internal::OpJointMatrixLoadINTEL:
|
||
case OpCooperativeMatrixLoadKHR:
|
||
case internal::OpCooperativeMatrixLoadCheckedINTEL:
|
||
case internal::OpCooperativeMatrixLoadOffsetINTEL:
|
||
case internal::OpTaskSequenceCreateINTEL:
|
||
case internal::OpConvertHandleToImageINTEL:
|
||
case internal::OpConvertHandleToSampledImageINTEL:
|
||
case internal::OpClampConvertFToSINTEL:
|
||
case internal::OpStochasticRoundFToFINTEL:
|
||
case internal::OpClampStochasticRoundFToSINTEL:
|
||
// Old opcodes, for backward compatibility.
|
||
case internal::OpClampConvertFToFINTEL:
|
||
case internal::OpClampStochasticRoundFToFINTEL:
|
||
AddRetTypePostfix = true;
|
||
break;
|
||
default: {
|
||
if (isCvtOpCode(OC) && OC != OpGenericCastToPtrExplicit)
|
||
AddRetTypePostfix = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
bool IsRetSigned = true;
|
||
switch (OC) {
|
||
case OpConvertFToU:
|
||
case OpSatConvertSToU:
|
||
case OpUConvert:
|
||
case OpUDotKHR:
|
||
case OpUDotAccSatKHR:
|
||
case OpReadClockKHR:
|
||
IsRetSigned = false;
|
||
break;
|
||
case OpImageRead:
|
||
case OpImageSampleExplicitLod: {
|
||
size_t Idx = getImageOperandsIndex(OC);
|
||
if (auto Ops = BI->getOperands(); Ops.size() > Idx) {
|
||
auto ImOp = static_cast<SPIRVConstant *>(Ops[Idx])->getZExtIntValue();
|
||
IsRetSigned = !(ImOp & ImageOperandsMask::ImageOperandsZeroExtendMask);
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
|
||
if (AddRetTypePostfix) {
|
||
const Type *RetTy = BI->hasType() ? transType(BI->getType(), true)
|
||
: Type::getVoidTy(*Context);
|
||
Type *PET = nullptr;
|
||
if (auto *TPT = dyn_cast<TypedPointerType>(RetTy))
|
||
PET = TPT->getElementType();
|
||
return transBuiltinFromInst(getSPIRVFuncName(OC, RetTy, IsRetSigned, PET) +
|
||
getSPIRVFuncSuffix(BI),
|
||
BI, BB);
|
||
}
|
||
return transBuiltinFromInst(getSPIRVFuncName(OC, getSPIRVFuncSuffix(BI)), BI,
|
||
BB);
|
||
}
|
||
|
||
bool SPIRVToLLVM::translate() {
|
||
if (!transAddressingModel())
|
||
return false;
|
||
|
||
// Entry Points should be translated before all debug intrinsics.
|
||
for (SPIRVExtInst *EI : BM->getDebugInstVec()) {
|
||
if (EI->getExtOp() == SPIRVDebug::EntryPoint)
|
||
DbgTran->transDebugInst(EI);
|
||
}
|
||
|
||
// Compile unit might be needed during translation of debug intrinsics.
|
||
for (SPIRVExtInst *EI : BM->getDebugInstVec()) {
|
||
// Translate Compile Units first.
|
||
if (EI->getExtOp() == SPIRVDebug::CompilationUnit)
|
||
DbgTran->transDebugInst(EI);
|
||
}
|
||
|
||
for (unsigned I = 0, E = BM->getNumVariables(); I != E; ++I) {
|
||
auto *BV = BM->getVariable(I);
|
||
if (BV->getStorageClass() != StorageClassFunction)
|
||
transValue(BV, nullptr, nullptr);
|
||
transGlobalCtorDtors(BV);
|
||
}
|
||
|
||
// Then translate all debug instructions.
|
||
for (SPIRVExtInst *EI : BM->getDebugInstVec()) {
|
||
DbgTran->transDebugInst(EI);
|
||
}
|
||
|
||
for (auto *FP : BM->getFunctionPointers()) {
|
||
SPIRVConstantFunctionPointerINTEL *BC =
|
||
static_cast<SPIRVConstantFunctionPointerINTEL *>(FP);
|
||
SPIRVFunction *F = BC->getFunction();
|
||
FP->setName(F->getName());
|
||
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
|
||
? SPIRAS_CodeSectionINTEL
|
||
: SPIRAS_Private;
|
||
mapValue(FP, transFunction(F, AS));
|
||
}
|
||
|
||
for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) {
|
||
transFunction(BM->getFunction(I));
|
||
transUserSemantic(BM->getFunction(I));
|
||
}
|
||
|
||
transGlobalAnnotations();
|
||
|
||
if (!transMetadata())
|
||
return false;
|
||
if (!transFPContractMetadata())
|
||
return false;
|
||
transSourceLanguage();
|
||
if (!transSourceExtension())
|
||
return false;
|
||
transGeneratorMD();
|
||
if (!lowerBuiltins(BM, M))
|
||
return false;
|
||
if (BM->getDesiredBIsRepresentation() == BIsRepresentation::SPIRVFriendlyIR) {
|
||
SPIRVWord SrcLangVer = 0;
|
||
BM->getSourceLanguage(&SrcLangVer);
|
||
bool IsCpp =
|
||
SrcLangVer == kOCLVer::CLCXX10 || SrcLangVer == kOCLVer::CLCXX2021;
|
||
if (!postProcessBuiltinsReturningStruct(M, IsCpp))
|
||
return false;
|
||
}
|
||
|
||
for (SPIRVExtInst *EI : BM->getAuxDataInstVec()) {
|
||
transAuxDataInst(EI);
|
||
}
|
||
|
||
eraseUselessFunctions(M);
|
||
|
||
DbgTran->addDbgInfoVersion();
|
||
DbgTran->finalize();
|
||
|
||
return true;
|
||
}
|
||
|
||
bool SPIRVToLLVM::transAddressingModel() {
|
||
switch (BM->getAddressingModel()) {
|
||
case AddressingModelPhysical64:
|
||
M->setTargetTriple(Triple(SPIR_TARGETTRIPLE64));
|
||
M->setDataLayout(SPIR_DATALAYOUT64);
|
||
break;
|
||
case AddressingModelPhysical32:
|
||
M->setTargetTriple(Triple(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 " +
|
||
std::to_string(BM->getAddressingModel()));
|
||
}
|
||
return true;
|
||
}
|
||
|
||
void generateIntelFPGAAnnotation(
|
||
const SPIRVEntry *E, std::vector<llvm::SmallString<256>> &AnnotStrVec) {
|
||
llvm::SmallString<256> AnnotStr;
|
||
llvm::raw_svector_ostream Out(AnnotStr);
|
||
if (E->hasDecorate(DecorationRegisterINTEL))
|
||
Out << "{register:1}";
|
||
|
||
SPIRVWord Result = 0;
|
||
if (E->hasDecorate(DecorationMemoryINTEL))
|
||
Out << "{memory:"
|
||
<< E->getDecorationStringLiteral(DecorationMemoryINTEL).front() << '}';
|
||
if (E->hasDecorate(DecorationBankwidthINTEL, 0, &Result))
|
||
Out << "{bankwidth:" << Result << '}';
|
||
if (E->hasDecorate(DecorationNumbanksINTEL, 0, &Result))
|
||
Out << "{numbanks:" << Result << '}';
|
||
if (E->hasDecorate(DecorationMaxPrivateCopiesINTEL, 0, &Result))
|
||
Out << "{private_copies:" << Result << '}';
|
||
if (E->hasDecorate(DecorationSinglepumpINTEL))
|
||
Out << "{pump:1}";
|
||
if (E->hasDecorate(DecorationDoublepumpINTEL))
|
||
Out << "{pump:2}";
|
||
if (E->hasDecorate(DecorationMaxReplicatesINTEL, 0, &Result))
|
||
Out << "{max_replicates:" << Result << '}';
|
||
if (E->hasDecorate(DecorationSimpleDualPortINTEL))
|
||
Out << "{simple_dual_port:1}";
|
||
if (E->hasDecorate(DecorationMergeINTEL)) {
|
||
Out << "{merge";
|
||
for (const auto &Str : E->getDecorationStringLiteral(DecorationMergeINTEL))
|
||
Out << ":" << Str;
|
||
Out << '}';
|
||
}
|
||
if (E->hasDecorate(DecorationBankBitsINTEL)) {
|
||
Out << "{bank_bits:";
|
||
auto Literals = E->getDecorationLiterals(DecorationBankBitsINTEL);
|
||
for (size_t I = 0; I < Literals.size() - 1; ++I)
|
||
Out << Literals[I] << ",";
|
||
Out << Literals.back() << '}';
|
||
}
|
||
if (E->hasDecorate(DecorationForcePow2DepthINTEL, 0, &Result))
|
||
Out << "{force_pow2_depth:" << Result << '}';
|
||
if (E->hasDecorate(DecorationStridesizeINTEL, 0, &Result))
|
||
Out << "{stride_size:" << Result << "}";
|
||
if (E->hasDecorate(DecorationWordsizeINTEL, 0, &Result))
|
||
Out << "{word_size:" << Result << "}";
|
||
if (E->hasDecorate(DecorationTrueDualPortINTEL))
|
||
Out << "{true_dual_port}";
|
||
if (E->hasDecorate(DecorationBufferLocationINTEL, 0, &Result))
|
||
Out << "{sycl-buffer-location:" << Result << '}';
|
||
if (E->hasDecorate(DecorationLatencyControlLabelINTEL, 0, &Result))
|
||
Out << "{sycl-latency-anchor-id:" << Result << '}';
|
||
if (E->hasDecorate(DecorationLatencyControlConstraintINTEL)) {
|
||
auto Literals =
|
||
E->getDecorationLiterals(DecorationLatencyControlConstraintINTEL);
|
||
assert(Literals.size() == 3 &&
|
||
"Latency Control Constraint decoration shall have 3 extra operands");
|
||
Out << "{sycl-latency-constraint:" << Literals[0] << "," << Literals[1]
|
||
<< "," << Literals[2] << '}';
|
||
}
|
||
|
||
unsigned LSUParamsBitmask = 0;
|
||
llvm::SmallString<32> AdditionalParamsStr;
|
||
llvm::raw_svector_ostream ParamsOut(AdditionalParamsStr);
|
||
if (E->hasDecorate(DecorationBurstCoalesceINTEL, 0))
|
||
LSUParamsBitmask |= IntelFPGAMemoryAccessesVal::BurstCoalesce;
|
||
if (E->hasDecorate(DecorationCacheSizeINTEL, 0, &Result)) {
|
||
LSUParamsBitmask |= IntelFPGAMemoryAccessesVal::CacheSizeFlag;
|
||
ParamsOut << "{cache-size:" << Result << "}";
|
||
}
|
||
if (E->hasDecorate(DecorationDontStaticallyCoalesceINTEL, 0))
|
||
LSUParamsBitmask |= IntelFPGAMemoryAccessesVal::DontStaticallyCoalesce;
|
||
if (E->hasDecorate(DecorationPrefetchINTEL, 0, &Result)) {
|
||
LSUParamsBitmask |= IntelFPGAMemoryAccessesVal::PrefetchFlag;
|
||
// TODO: Enable prefetch size backwards translation
|
||
// once it is supported
|
||
}
|
||
if (LSUParamsBitmask)
|
||
Out << "{params:" << LSUParamsBitmask << "}" << AdditionalParamsStr;
|
||
if (!AnnotStr.empty())
|
||
AnnotStrVec.emplace_back(AnnotStr);
|
||
|
||
if (E->hasDecorate(DecorationUserSemantic)) {
|
||
auto Annotations =
|
||
E->getAllDecorationStringLiterals(DecorationUserSemantic);
|
||
for (size_t I = 0; I != Annotations.size(); ++I) {
|
||
// UserSemantic has a single literal string
|
||
llvm::SmallString<256> UserSemanticStr;
|
||
llvm::raw_svector_ostream UserSemanticOut(UserSemanticStr);
|
||
for (const auto &Str : Annotations[I])
|
||
UserSemanticOut << Str;
|
||
AnnotStrVec.emplace_back(UserSemanticStr);
|
||
}
|
||
}
|
||
}
|
||
|
||
void generateIntelFPGAAnnotationForStructMember(
|
||
const SPIRVEntry *E, SPIRVWord MemberNumber,
|
||
std::vector<llvm::SmallString<256>> &AnnotStrVec) {
|
||
llvm::SmallString<256> AnnotStr;
|
||
llvm::raw_svector_ostream Out(AnnotStr);
|
||
if (E->hasMemberDecorate(DecorationRegisterINTEL, 0, MemberNumber))
|
||
Out << "{register:1}";
|
||
|
||
SPIRVWord Result = 0;
|
||
if (E->hasMemberDecorate(DecorationMemoryINTEL, 0, MemberNumber, &Result))
|
||
Out << "{memory:"
|
||
<< E->getMemberDecorationStringLiteral(DecorationMemoryINTEL,
|
||
MemberNumber)
|
||
.front()
|
||
<< '}';
|
||
if (E->hasMemberDecorate(DecorationBankwidthINTEL, 0, MemberNumber, &Result))
|
||
Out << "{bankwidth:" << Result << '}';
|
||
if (E->hasMemberDecorate(DecorationNumbanksINTEL, 0, MemberNumber, &Result))
|
||
Out << "{numbanks:" << Result << '}';
|
||
if (E->hasMemberDecorate(DecorationMaxPrivateCopiesINTEL, 0, MemberNumber,
|
||
&Result))
|
||
Out << "{private_copies:" << Result << '}';
|
||
if (E->hasMemberDecorate(DecorationSinglepumpINTEL, 0, MemberNumber))
|
||
Out << "{pump:1}";
|
||
if (E->hasMemberDecorate(DecorationDoublepumpINTEL, 0, MemberNumber))
|
||
Out << "{pump:2}";
|
||
if (E->hasMemberDecorate(DecorationMaxReplicatesINTEL, 0, MemberNumber,
|
||
&Result))
|
||
Out << "{max_replicates:" << Result << '}';
|
||
if (E->hasMemberDecorate(DecorationSimpleDualPortINTEL, 0, MemberNumber))
|
||
Out << "{simple_dual_port:1}";
|
||
if (E->hasMemberDecorate(DecorationMergeINTEL, 0, MemberNumber)) {
|
||
Out << "{merge";
|
||
for (const auto &Str : E->getMemberDecorationStringLiteral(
|
||
DecorationMergeINTEL, MemberNumber))
|
||
Out << ":" << Str;
|
||
Out << '}';
|
||
}
|
||
if (E->hasMemberDecorate(DecorationBankBitsINTEL, 0, MemberNumber)) {
|
||
Out << "{bank_bits:";
|
||
auto Literals =
|
||
E->getMemberDecorationLiterals(DecorationBankBitsINTEL, MemberNumber);
|
||
for (size_t I = 0; I < Literals.size() - 1; ++I)
|
||
Out << Literals[I] << ",";
|
||
Out << Literals.back() << '}';
|
||
}
|
||
if (E->hasMemberDecorate(DecorationForcePow2DepthINTEL, 0, MemberNumber,
|
||
&Result))
|
||
Out << "{force_pow2_depth:" << Result << '}';
|
||
if (E->hasMemberDecorate(DecorationStridesizeINTEL, 0, MemberNumber, &Result))
|
||
Out << "{stride_size:" << Result << "}";
|
||
if (E->hasMemberDecorate(DecorationWordsizeINTEL, 0, MemberNumber, &Result))
|
||
Out << "{word_size:" << Result << "}";
|
||
if (E->hasMemberDecorate(DecorationTrueDualPortINTEL, 0, MemberNumber))
|
||
Out << "{true_dual_port}";
|
||
if (!AnnotStr.empty())
|
||
AnnotStrVec.emplace_back(AnnotStr);
|
||
|
||
if (E->hasMemberDecorate(DecorationUserSemantic, 0, MemberNumber)) {
|
||
auto Annotations = E->getAllMemberDecorationStringLiterals(
|
||
DecorationUserSemantic, MemberNumber);
|
||
for (size_t I = 0; I != Annotations.size(); ++I) {
|
||
// UserSemantic has a single literal string
|
||
llvm::SmallString<256> UserSemanticStr;
|
||
llvm::raw_svector_ostream UserSemanticOut(UserSemanticStr);
|
||
for (const auto &Str : Annotations[I])
|
||
UserSemanticOut << Str;
|
||
AnnotStrVec.emplace_back(UserSemanticStr);
|
||
}
|
||
}
|
||
}
|
||
|
||
void SPIRVToLLVM::transIntelFPGADecorations(SPIRVValue *BV, Value *V) {
|
||
if (!BV->isVariable() && !BV->isInst())
|
||
return;
|
||
|
||
if (auto *Inst = dyn_cast<Instruction>(V)) {
|
||
auto *AL = dyn_cast<AllocaInst>(Inst);
|
||
Type *AllocatedTy = AL ? AL->getAllocatedType() : Inst->getType();
|
||
|
||
IRBuilder<> Builder(Inst->getParent());
|
||
|
||
Type *Int8PtrTyPrivate = PointerType::get(*Context, SPIRAS_Private);
|
||
IntegerType *Int32Ty = IntegerType::get(*Context, 32);
|
||
|
||
Value *UndefInt8Ptr = PoisonValue::get(Int8PtrTyPrivate);
|
||
Value *UndefInt32 = PoisonValue::get(Int32Ty);
|
||
|
||
if (AL && BV->getType()->getPointerElementType()->isTypeStruct()) {
|
||
auto *ST = BV->getType()->getPointerElementType();
|
||
SPIRVTypeStruct *STS = static_cast<SPIRVTypeStruct *>(ST);
|
||
|
||
for (SPIRVWord I = 0; I < STS->getMemberCount(); ++I) {
|
||
std::vector<SmallString<256>> AnnotStrVec;
|
||
generateIntelFPGAAnnotationForStructMember(ST, I, AnnotStrVec);
|
||
CallInst *AnnotationCall = nullptr;
|
||
for (const auto &AnnotStr : AnnotStrVec) {
|
||
auto *GS = Builder.CreateGlobalString(AnnotStr);
|
||
|
||
Instruction *PtrAnnFirstArg = nullptr;
|
||
|
||
if (GEPOrUseMap.count(AL)) {
|
||
auto IdxToInstMap = GEPOrUseMap[AL];
|
||
if (IdxToInstMap.count(I)) {
|
||
PtrAnnFirstArg = IdxToInstMap[I];
|
||
}
|
||
}
|
||
|
||
Type *IntTy = nullptr;
|
||
|
||
if (!PtrAnnFirstArg) {
|
||
GetElementPtrInst *GEP = cast<GetElementPtrInst>(
|
||
Builder.CreateConstInBoundsGEP2_32(AllocatedTy, AL, 0, I));
|
||
|
||
IntTy = GEP->getResultElementType()->isIntegerTy()
|
||
? GEP->getType()
|
||
: Int8PtrTyPrivate;
|
||
PtrAnnFirstArg = GEP;
|
||
} else {
|
||
IntTy = PtrAnnFirstArg->getType();
|
||
}
|
||
|
||
auto *AnnotationFn = llvm::Intrinsic::getOrInsertDeclaration(
|
||
M, Intrinsic::ptr_annotation, {IntTy, Int8PtrTyPrivate});
|
||
|
||
llvm::Value *Args[] = {
|
||
Builder.CreateBitCast(PtrAnnFirstArg, IntTy,
|
||
PtrAnnFirstArg->getName()),
|
||
Builder.CreateBitCast(GS, Int8PtrTyPrivate), UndefInt8Ptr,
|
||
UndefInt32, UndefInt8Ptr};
|
||
AnnotationCall = Builder.CreateCall(AnnotationFn, Args);
|
||
GEPOrUseMap[AL][I] = AnnotationCall;
|
||
}
|
||
if (AnnotationCall)
|
||
ValueMap[BV] = AnnotationCall;
|
||
}
|
||
}
|
||
|
||
std::vector<SmallString<256>> AnnotStrVec;
|
||
generateIntelFPGAAnnotation(BV, AnnotStrVec);
|
||
CallInst *AnnotationCall = nullptr;
|
||
for (const auto &AnnotStr : AnnotStrVec) {
|
||
Constant *GS = nullptr;
|
||
const auto StringAnnotStr = static_cast<std::string>(AnnotStr);
|
||
auto AnnotItr = AnnotationsMap.find(StringAnnotStr);
|
||
if (AnnotItr != AnnotationsMap.end()) {
|
||
GS = AnnotItr->second;
|
||
} else {
|
||
GS = Builder.CreateGlobalString(AnnotStr);
|
||
AnnotationsMap.emplace(std::move(StringAnnotStr), GS);
|
||
}
|
||
|
||
Value *BaseInst = nullptr;
|
||
if (AnnotationCall && !AnnotationCall->getType()->isVoidTy())
|
||
BaseInst = AnnotationCall;
|
||
else
|
||
BaseInst = AL ? Builder.CreateBitCast(V, Int8PtrTyPrivate, V->getName())
|
||
: Inst;
|
||
|
||
// Try to find alloca instruction for statically allocated variables.
|
||
// Alloca might be hidden by a couple of casts.
|
||
bool isStaticMemoryAttribute = AL ? true : false;
|
||
while (!isStaticMemoryAttribute && Inst &&
|
||
(isa<BitCastInst>(Inst) || isa<AddrSpaceCastInst>(Inst))) {
|
||
Inst = dyn_cast<Instruction>(Inst->getOperand(0));
|
||
isStaticMemoryAttribute = (Inst && isa<AllocaInst>(Inst));
|
||
}
|
||
auto *AnnotationFn = llvm::Intrinsic::getOrInsertDeclaration(
|
||
M,
|
||
isStaticMemoryAttribute ? Intrinsic::var_annotation
|
||
: Intrinsic::ptr_annotation,
|
||
{BaseInst->getType(), Int8PtrTyPrivate});
|
||
|
||
llvm::Value *Args[] = {BaseInst,
|
||
Builder.CreateBitCast(GS, Int8PtrTyPrivate),
|
||
UndefInt8Ptr, UndefInt32, UndefInt8Ptr};
|
||
AnnotationCall = Builder.CreateCall(AnnotationFn, Args);
|
||
}
|
||
if (AnnotationCall && !AnnotationCall->getType()->isVoidTy())
|
||
ValueMap[BV] = AnnotationCall;
|
||
} else if (auto *GV = dyn_cast<GlobalVariable>(V)) {
|
||
// Do not add annotations for builtin variables if they will be translated
|
||
// to function calls.
|
||
SPIRVBuiltinVariableKind Kind;
|
||
if (BM->getBuiltinFormat() == BuiltinFormat::Function &&
|
||
isSPIRVBuiltinVariable(GV, &Kind))
|
||
return;
|
||
|
||
std::vector<SmallString<256>> AnnotStrVec;
|
||
generateIntelFPGAAnnotation(BV, AnnotStrVec);
|
||
|
||
if (AnnotStrVec.empty()) {
|
||
// Check if IO pipe decoration is applied to the global
|
||
SPIRVWord ID;
|
||
if (BV->hasDecorate(DecorationIOPipeStorageINTEL, 0, &ID)) {
|
||
auto Literals = BV->getDecorationLiterals(DecorationIOPipeStorageINTEL);
|
||
assert(Literals.size() == 1 &&
|
||
"IO PipeStorage decoration shall have 1 extra operand");
|
||
GV->setMetadata("io_pipe_id", getMDNodeStringIntVec(Context, Literals));
|
||
}
|
||
return;
|
||
}
|
||
|
||
for (const auto &AnnotStr : AnnotStrVec) {
|
||
Constant *StrConstant =
|
||
ConstantDataArray::getString(*Context, StringRef(AnnotStr));
|
||
|
||
auto *GS = new GlobalVariable(
|
||
*GV->getParent(), StrConstant->getType(),
|
||
/*IsConstant*/ true, GlobalValue::PrivateLinkage, StrConstant, "");
|
||
|
||
GS->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
|
||
GS->setSection("llvm.metadata");
|
||
|
||
Type *ResType = PointerType::get(
|
||
GV->getContext(), M->getDataLayout().getDefaultGlobalsAddressSpace());
|
||
Constant *C = ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, ResType);
|
||
|
||
Type *Int8PtrTyPrivate = PointerType::get(*Context, SPIRAS_Private);
|
||
IntegerType *Int32Ty = Type::getInt32Ty(*Context);
|
||
|
||
llvm::Constant *Fields[5] = {
|
||
C, ConstantExpr::getBitCast(GS, Int8PtrTyPrivate),
|
||
PoisonValue::get(Int8PtrTyPrivate), PoisonValue::get(Int32Ty),
|
||
PoisonValue::get(Int8PtrTyPrivate)};
|
||
|
||
GlobalAnnotations.push_back(ConstantStruct::getAnon(Fields));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Translate aliasing decorations applied to instructions. These decorations
|
||
// are mapped on alias.scope and noalias metadata in LLVM. Translation of
|
||
// optional string operand isn't yet supported in the translator.
|
||
void SPIRVToLLVM::transMemAliasingINTELDecorations(SPIRVValue *BV, Value *V) {
|
||
if (!BV->isInst())
|
||
return;
|
||
Instruction *Inst = dyn_cast<Instruction>(V);
|
||
if (!Inst)
|
||
return;
|
||
if (BV->hasDecorateId(DecorationAliasScopeINTEL)) {
|
||
std::vector<SPIRVId> AliasListIds;
|
||
AliasListIds = BV->getDecorationIdLiterals(DecorationAliasScopeINTEL);
|
||
assert(AliasListIds.size() == 1 &&
|
||
"Memory aliasing decorations must have one argument");
|
||
addMemAliasMetadata(Inst, AliasListIds[0], LLVMContext::MD_alias_scope);
|
||
}
|
||
if (BV->hasDecorateId(DecorationNoAliasINTEL)) {
|
||
std::vector<SPIRVId> AliasListIds;
|
||
AliasListIds = BV->getDecorationIdLiterals(DecorationNoAliasINTEL);
|
||
assert(AliasListIds.size() == 1 &&
|
||
"Memory aliasing decorations must have one argument");
|
||
addMemAliasMetadata(Inst, AliasListIds[0], LLVMContext::MD_noalias);
|
||
}
|
||
}
|
||
|
||
// Having UserSemantic decoration on Function is against the spec, but we allow
|
||
// this for various purposes (like prototyping new features when we need to
|
||
// attach some information on function and propagate that through SPIR-V and
|
||
// ect.)
|
||
void SPIRVToLLVM::transUserSemantic(SPIRV::SPIRVFunction *Fun) {
|
||
auto *TransFun = transFunction(Fun);
|
||
for (const auto &UsSem :
|
||
Fun->getDecorationStringLiteral(DecorationUserSemantic)) {
|
||
auto *V = cast<Value>(TransFun);
|
||
Constant *StrConstant =
|
||
ConstantDataArray::getString(*Context, StringRef(UsSem));
|
||
auto *GS = new GlobalVariable(
|
||
*TransFun->getParent(), StrConstant->getType(),
|
||
/*IsConstant*/ true, GlobalValue::PrivateLinkage, StrConstant, "");
|
||
|
||
GS->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
|
||
GS->setSection("llvm.metadata");
|
||
|
||
Type *ResType = PointerType::get(
|
||
V->getContext(), M->getDataLayout().getDefaultGlobalsAddressSpace());
|
||
Constant *C =
|
||
ConstantExpr::getPointerBitCastOrAddrSpaceCast(TransFun, ResType);
|
||
|
||
Type *Int8PtrTyPrivate = PointerType::get(*Context, SPIRAS_Private);
|
||
IntegerType *Int32Ty = Type::getInt32Ty(*Context);
|
||
|
||
llvm::Constant *Fields[5] = {
|
||
C, ConstantExpr::getBitCast(GS, Int8PtrTyPrivate),
|
||
PoisonValue::get(Int8PtrTyPrivate), PoisonValue::get(Int32Ty),
|
||
PoisonValue::get(Int8PtrTyPrivate)};
|
||
GlobalAnnotations.push_back(ConstantStruct::getAnon(Fields));
|
||
}
|
||
}
|
||
|
||
void SPIRVToLLVM::transGlobalAnnotations() {
|
||
if (!GlobalAnnotations.empty()) {
|
||
Constant *Array =
|
||
ConstantArray::get(ArrayType::get(GlobalAnnotations[0]->getType(),
|
||
GlobalAnnotations.size()),
|
||
GlobalAnnotations);
|
||
auto *GV = new GlobalVariable(*M, Array->getType(), /*IsConstant*/ false,
|
||
GlobalValue::AppendingLinkage, Array,
|
||
"llvm.global.annotations");
|
||
GV->setSection("llvm.metadata");
|
||
}
|
||
}
|
||
|
||
static llvm::MDNode *
|
||
transDecorationsToMetadataList(llvm::LLVMContext *Context,
|
||
std::vector<SPIRVDecorate const *> Decorates) {
|
||
SmallVector<Metadata *, 4> MDs;
|
||
MDs.reserve(Decorates.size());
|
||
for (const auto *Deco : Decorates) {
|
||
std::vector<Metadata *> OPs;
|
||
auto *KindMD = ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt32Ty(*Context), Deco->getDecorateKind()));
|
||
OPs.push_back(KindMD);
|
||
switch (static_cast<size_t>(Deco->getDecorateKind())) {
|
||
case DecorationLinkageAttributes: {
|
||
const auto *const LinkAttrDeco =
|
||
static_cast<const SPIRVDecorateLinkageAttr *>(Deco);
|
||
auto *const LinkNameMD =
|
||
MDString::get(*Context, LinkAttrDeco->getLinkageName());
|
||
auto *const LinkTypeMD = ConstantAsMetadata::get(ConstantInt::get(
|
||
Type::getInt32Ty(*Context), LinkAttrDeco->getLinkageType()));
|
||
OPs.push_back(LinkNameMD);
|
||
OPs.push_back(LinkTypeMD);
|
||
break;
|
||
}
|
||
case spv::internal::DecorationHostAccessINTEL:
|
||
case DecorationHostAccessINTEL: {
|
||
const auto *const HostAccDeco =
|
||
static_cast<const SPIRVDecorateHostAccessINTEL *>(Deco);
|
||
auto *const AccModeMD = ConstantAsMetadata::get(ConstantInt::get(
|
||
Type::getInt32Ty(*Context), HostAccDeco->getAccessMode()));
|
||
auto *const NameMD = MDString::get(*Context, HostAccDeco->getVarName());
|
||
OPs.push_back(AccModeMD);
|
||
OPs.push_back(NameMD);
|
||
break;
|
||
}
|
||
case DecorationMergeINTEL: {
|
||
const auto MergeAttrLits = Deco->getVecLiteral();
|
||
std::string FirstString = getString(MergeAttrLits);
|
||
std::string SecondString =
|
||
getString(MergeAttrLits.cbegin() + getVec(FirstString).size(),
|
||
MergeAttrLits.cend());
|
||
OPs.push_back(MDString::get(*Context, FirstString));
|
||
OPs.push_back(MDString::get(*Context, SecondString));
|
||
break;
|
||
}
|
||
case DecorationMemoryINTEL:
|
||
case DecorationUserSemantic: {
|
||
auto *const StrMD =
|
||
MDString::get(*Context, getString(Deco->getVecLiteral()));
|
||
OPs.push_back(StrMD);
|
||
break;
|
||
}
|
||
default: {
|
||
for (const SPIRVWord Lit : Deco->getVecLiteral()) {
|
||
auto *const LitMD = ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt32Ty(*Context), Lit));
|
||
OPs.push_back(LitMD);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
MDs.push_back(MDNode::get(*Context, OPs));
|
||
}
|
||
return MDNode::get(*Context, MDs);
|
||
}
|
||
|
||
void SPIRVToLLVM::transDecorationsToMetadata(SPIRVValue *BV, Value *V) {
|
||
if (!BV->isVariable() && !BV->isInst())
|
||
return;
|
||
|
||
auto SetDecorationsMetadata = [&](auto V) {
|
||
std::vector<SPIRVDecorate const *> Decorates = BV->getDecorations();
|
||
if (!Decorates.empty()) {
|
||
MDNode *MDList = transDecorationsToMetadataList(Context, Decorates);
|
||
V->setMetadata(SPIRV_MD_DECORATIONS, MDList);
|
||
}
|
||
};
|
||
|
||
if (auto *GV = dyn_cast<GlobalVariable>(V))
|
||
SetDecorationsMetadata(GV);
|
||
else if (auto *I = dyn_cast<Instruction>(V))
|
||
SetDecorationsMetadata(I);
|
||
}
|
||
|
||
namespace {
|
||
|
||
static float convertSPIRVWordToFloat(SPIRVWord Spir) {
|
||
union {
|
||
float F;
|
||
SPIRVWord Spir;
|
||
} FPMaxError;
|
||
FPMaxError.Spir = Spir;
|
||
return FPMaxError.F;
|
||
}
|
||
|
||
static bool transFPMaxErrorDecoration(SPIRVValue *BV, Value *V,
|
||
LLVMContext *Context) {
|
||
SPIRVWord ID;
|
||
if (Instruction *I = dyn_cast<Instruction>(V))
|
||
if (BV->hasDecorate(DecorationFPMaxErrorDecorationINTEL, 0, &ID)) {
|
||
auto Literals =
|
||
BV->getDecorationLiterals(DecorationFPMaxErrorDecorationINTEL);
|
||
assert(Literals.size() == 1 &&
|
||
"FP Max Error decoration shall have 1 operand");
|
||
auto F = convertSPIRVWordToFloat(Literals[0]);
|
||
if (CallInst *CI = dyn_cast<CallInst>(I)) {
|
||
// Add attribute
|
||
auto A = llvm::Attribute::get(*Context, "fpbuiltin-max-error",
|
||
std::to_string(F));
|
||
CI->addFnAttr(A);
|
||
} else {
|
||
// Add metadata
|
||
MDNode *N =
|
||
MDNode::get(*Context, MDString::get(*Context, std::to_string(F)));
|
||
I->setMetadata("fpbuiltin-max-error", N);
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
} // namespace
|
||
|
||
bool SPIRVToLLVM::transDecoration(SPIRVValue *BV, Value *V) {
|
||
if (transFPMaxErrorDecoration(BV, V, Context))
|
||
return true;
|
||
|
||
if (!transAlign(BV, V))
|
||
return false;
|
||
|
||
transIntelFPGADecorations(BV, V);
|
||
transMemAliasingINTELDecorations(BV, V);
|
||
|
||
// Decoration metadata is only enabled in SPIR-V friendly mode
|
||
if (BM->getDesiredBIsRepresentation() == BIsRepresentation::SPIRVFriendlyIR)
|
||
transDecorationsToMetadata(BV, V);
|
||
|
||
DbgTran->transDbgInfo(BV, V);
|
||
return true;
|
||
}
|
||
|
||
void SPIRVToLLVM::transGlobalCtorDtors(SPIRVVariableBase *BV) {
|
||
if (BV->getName() != "llvm.global_ctors" &&
|
||
BV->getName() != "llvm.global_dtors")
|
||
return;
|
||
|
||
Value *V = transValue(BV, nullptr, nullptr);
|
||
cast<GlobalValue>(V)->setLinkage(GlobalValue::AppendingLinkage);
|
||
}
|
||
|
||
void SPIRVToLLVM::createCXXStructor(const char *ListName,
|
||
SmallVectorImpl<Function *> &Funcs) {
|
||
if (Funcs.empty())
|
||
return;
|
||
|
||
// If the SPIR-V input contained a variable for the structor list and it
|
||
// has already been translated, then don't interfere.
|
||
if (M->getGlobalVariable(ListName))
|
||
return;
|
||
|
||
// Type of a structor entry: { i32, void ()*, i8* }
|
||
Type *PriorityTy = Type::getInt32Ty(*Context);
|
||
PointerType *CtorTy = PointerType::getUnqual(*Context);
|
||
PointerType *ComdatTy = PointerType::getUnqual(*Context);
|
||
StructType *StructorTy = StructType::get(PriorityTy, CtorTy, ComdatTy);
|
||
|
||
ArrayType *ArrTy = ArrayType::get(StructorTy, Funcs.size());
|
||
|
||
GlobalVariable *GV =
|
||
cast<GlobalVariable>(M->getOrInsertGlobal(ListName, ArrTy));
|
||
GV->setLinkage(GlobalValue::AppendingLinkage);
|
||
|
||
// Build the initializer.
|
||
SmallVector<Constant *, 2> ArrayElts;
|
||
for (auto *F : Funcs) {
|
||
SmallVector<Constant *, 3> Elts;
|
||
// SPIR-V does not specify an order between Initializers, so set default
|
||
// priority.
|
||
Elts.push_back(ConstantInt::get(PriorityTy, 65535));
|
||
Elts.push_back(ConstantExpr::getBitCast(F, CtorTy));
|
||
Elts.push_back(ConstantPointerNull::get(ComdatTy));
|
||
ArrayElts.push_back(ConstantStruct::get(StructorTy, Elts));
|
||
}
|
||
|
||
Constant *NewArray = ConstantArray::get(ArrTy, ArrayElts);
|
||
GV->setInitializer(NewArray);
|
||
}
|
||
|
||
bool SPIRVToLLVM::transFPContractMetadata() {
|
||
bool ContractOff = false;
|
||
for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) {
|
||
SPIRVFunction *BF = BM->getFunction(I);
|
||
if (!isKernel(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->hasAccessQualifier()
|
||
? ST->getAccessQualifier()
|
||
: AccessQualifierReadOnly);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
// Information of types of kernel arguments may be additionally stored in
|
||
// 'OpString "kernel_arg_type.%kernel_name%.type1,type2,type3,..' instruction.
|
||
// Try to find such instruction and generate metadata based on it.
|
||
// Return 'true' if 'OpString' was found and 'kernel_arg_type' metadata
|
||
// generated and 'false' otherwise.
|
||
static bool transKernelArgTypeMedataFromString(LLVMContext *Ctx,
|
||
SPIRVModule *BM,
|
||
Function *Kernel,
|
||
std::string MDName) {
|
||
// Run W/A translation only if the appropriate option is passed
|
||
if (!BM->shouldPreserveOCLKernelArgTypeMetadataThroughString())
|
||
return false;
|
||
std::string ArgTypePrefix =
|
||
std::string(MDName) + "." + Kernel->getName().str() + ".";
|
||
auto ArgTypeStrIt = std::find_if(
|
||
BM->getStringVec().begin(), BM->getStringVec().end(),
|
||
[=](SPIRVString *S) { return S->getStr().find(ArgTypePrefix) == 0; });
|
||
|
||
if (ArgTypeStrIt == BM->getStringVec().end())
|
||
return false;
|
||
|
||
std::string ArgTypeStr =
|
||
(*ArgTypeStrIt)->getStr().substr(ArgTypePrefix.size());
|
||
std::vector<Metadata *> TypeMDs;
|
||
|
||
int CountBraces = 0;
|
||
std::string::size_type Start = 0;
|
||
|
||
for (std::string::size_type I = 0; I < ArgTypeStr.length(); I++) {
|
||
switch (ArgTypeStr[I]) {
|
||
case '<':
|
||
CountBraces++;
|
||
break;
|
||
case '>':
|
||
CountBraces--;
|
||
break;
|
||
case ',':
|
||
if (CountBraces == 0) {
|
||
TypeMDs.push_back(
|
||
MDString::get(*Ctx, ArgTypeStr.substr(Start, I - Start)));
|
||
Start = I + 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
Kernel->setMetadata(MDName, MDNode::get(*Ctx, TypeMDs));
|
||
return true;
|
||
}
|
||
|
||
void SPIRVToLLVM::transFunctionDecorationsToMetadata(SPIRVFunction *BF,
|
||
Function *F) {
|
||
size_t TotalParameterDecorations = 0;
|
||
BF->foreachArgument([&](SPIRVFunctionParameter *Arg) {
|
||
TotalParameterDecorations += Arg->getNumDecorations();
|
||
});
|
||
if (TotalParameterDecorations == 0)
|
||
return;
|
||
|
||
// Generate metadata for spirv.ParameterDecorations
|
||
addKernelArgumentMetadata(Context, SPIRV_MD_PARAMETER_DECORATIONS, BF, F,
|
||
[=](SPIRVFunctionParameter *Arg) {
|
||
return transDecorationsToMetadataList(
|
||
Context, Arg->getDecorations());
|
||
});
|
||
}
|
||
|
||
bool SPIRVToLLVM::transMetadata() {
|
||
SmallVector<Function *, 2> CtorKernels;
|
||
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");
|
||
|
||
transOCLMetadata(BF);
|
||
transVectorComputeMetadata(BF);
|
||
transFPGAFunctionMetadata(BF, F);
|
||
|
||
// Decoration metadata is only enabled in SPIR-V friendly mode
|
||
if (BM->getDesiredBIsRepresentation() == BIsRepresentation::SPIRVFriendlyIR)
|
||
transFunctionDecorationsToMetadata(BF, F);
|
||
|
||
if (F->getCallingConv() != CallingConv::SPIR_KERNEL)
|
||
continue;
|
||
|
||
// Generate metadata for reqd_work_group_size
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeLocalSize)) {
|
||
F->setMetadata(kSPIR2MD::WGSize,
|
||
getMDNodeStringIntVec(Context, EM->getLiterals()));
|
||
} else if (auto *EM = BF->getExecutionModeId(ExecutionModeLocalSizeId)) {
|
||
std::vector<SPIRVWord> Values;
|
||
for (const auto Id : EM->getLiterals()) {
|
||
if (auto Val = transIdAsConstant(Id)) {
|
||
Values.emplace_back(static_cast<SPIRVWord>(*Val));
|
||
}
|
||
}
|
||
F->setMetadata(kSPIR2MD::WGSize, getMDNodeStringIntVec(Context, Values));
|
||
}
|
||
// Generate metadata for work_group_size_hint
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeLocalSizeHint)) {
|
||
F->setMetadata(kSPIR2MD::WGSizeHint,
|
||
getMDNodeStringIntVec(Context, EM->getLiterals()));
|
||
} else if (auto *EM =
|
||
BF->getExecutionModeId(ExecutionModeLocalSizeHintId)) {
|
||
std::vector<SPIRVWord> Values;
|
||
for (const auto Id : EM->getLiterals()) {
|
||
if (auto Val = transIdAsConstant(Id)) {
|
||
Values.emplace_back(static_cast<SPIRVWord>(*Val));
|
||
}
|
||
}
|
||
F->setMetadata(kSPIR2MD::WGSizeHint,
|
||
getMDNodeStringIntVec(Context, Values));
|
||
}
|
||
// Generate metadata for vec_type_hint
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeVecTypeHint)) {
|
||
std::vector<Metadata *> MetadataVec;
|
||
Type *VecHintTy = decodeVecTypeHint(*Context, EM->getLiterals()[0]);
|
||
assert(VecHintTy);
|
||
MetadataVec.push_back(ValueAsMetadata::get(PoisonValue::get(VecHintTy)));
|
||
MetadataVec.push_back(ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt32Ty(*Context), 1)));
|
||
F->setMetadata(kSPIR2MD::VecTyHint, MDNode::get(*Context, MetadataVec));
|
||
}
|
||
// Generate metadata for Initializer.
|
||
if (BF->getExecutionMode(ExecutionModeInitializer)) {
|
||
CtorKernels.push_back(F);
|
||
}
|
||
// Generate metadata for intel_reqd_sub_group_size
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeSubgroupSize)) {
|
||
auto *SizeMD =
|
||
ConstantAsMetadata::get(getUInt32(M, EM->getLiterals()[0]));
|
||
F->setMetadata(kSPIR2MD::SubgroupSize, MDNode::get(*Context, SizeMD));
|
||
}
|
||
// Generate metadata for intel_reqd_sub_group_size
|
||
if (BF->getExecutionMode(internal::ExecutionModeNamedSubgroupSizeINTEL)) {
|
||
// For now, there is only one named sub group size: primary, which is
|
||
// represented as a value of 0 as the argument of the OpExecutionMode.
|
||
assert(BF->getExecutionMode(internal::ExecutionModeNamedSubgroupSizeINTEL)
|
||
->getLiterals()[0] == 0 &&
|
||
"Invalid named sub group size");
|
||
// On the LLVM IR side, this is represented as the metadata
|
||
// intel_reqd_sub_group_size with value -1.
|
||
auto *SizeMD = ConstantAsMetadata::get(getInt32(M, -1));
|
||
F->setMetadata(kSPIR2MD::SubgroupSize, MDNode::get(*Context, SizeMD));
|
||
}
|
||
// Generate metadata for SubgroupsPerWorkgroup/SubgroupsPerWorkgroupId.
|
||
auto EmitSubgroupsPerWorkgroupMD = [this, F](SPIRVExecutionModeKind EMK,
|
||
uint64_t Value) {
|
||
NamedMDNode *ExecModeMD =
|
||
M->getOrInsertNamedMetadata(kSPIRVMD::ExecutionMode);
|
||
SmallVector<Metadata *, 2> OperandVec;
|
||
OperandVec.push_back(ConstantAsMetadata::get(F));
|
||
OperandVec.push_back(ConstantAsMetadata::get(getUInt32(M, EMK)));
|
||
OperandVec.push_back(ConstantAsMetadata::get(getUInt32(M, Value)));
|
||
ExecModeMD->addOperand(MDNode::get(*Context, OperandVec));
|
||
};
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeSubgroupsPerWorkgroup)) {
|
||
EmitSubgroupsPerWorkgroupMD(EM->getExecutionMode(), EM->getLiterals()[0]);
|
||
} else if (auto *EM = BF->getExecutionModeId(
|
||
ExecutionModeSubgroupsPerWorkgroupId)) {
|
||
if (auto Val = transIdAsConstant(EM->getLiterals()[0])) {
|
||
EmitSubgroupsPerWorkgroupMD(EM->getExecutionMode(), *Val);
|
||
}
|
||
}
|
||
// Generate metadata for max_work_group_size
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeMaxWorkgroupSizeINTEL)) {
|
||
F->setMetadata(kSPIR2MD::MaxWGSize,
|
||
getMDNodeStringIntVec(Context, EM->getLiterals()));
|
||
}
|
||
// Generate metadata for no_global_work_offset
|
||
if (BF->getExecutionMode(ExecutionModeNoGlobalOffsetINTEL)) {
|
||
F->setMetadata(kSPIR2MD::NoGlobalOffset, MDNode::get(*Context, {}));
|
||
}
|
||
// Generate metadata for max_global_work_dim
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeMaxWorkDimINTEL)) {
|
||
F->setMetadata(kSPIR2MD::MaxWGDim,
|
||
getMDNodeStringIntVec(Context, EM->getLiterals()));
|
||
}
|
||
// Generate metadata for num_simd_work_items
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeNumSIMDWorkitemsINTEL)) {
|
||
F->setMetadata(kSPIR2MD::NumSIMD,
|
||
getMDNodeStringIntVec(Context, EM->getLiterals()));
|
||
}
|
||
// Generate metadata for scheduler_target_fmax_mhz
|
||
if (auto *EM =
|
||
BF->getExecutionMode(ExecutionModeSchedulerTargetFmaxMhzINTEL)) {
|
||
F->setMetadata(kSPIR2MD::FmaxMhz,
|
||
getMDNodeStringIntVec(Context, EM->getLiterals()));
|
||
}
|
||
// Generate metadata for Intel FPGA register map interface
|
||
if (auto *EM =
|
||
BF->getExecutionMode(ExecutionModeRegisterMapInterfaceINTEL)) {
|
||
std::vector<uint32_t> InterfaceVec = EM->getLiterals();
|
||
assert(InterfaceVec.size() == 1 &&
|
||
"Expected RegisterMapInterfaceINTEL to have exactly 1 literal");
|
||
std::vector<Metadata *> InterfaceMDVec =
|
||
[&]() -> std::vector<Metadata *> {
|
||
switch (InterfaceVec[0]) {
|
||
case 0:
|
||
return {MDString::get(*Context, "csr")};
|
||
case 1:
|
||
return {MDString::get(*Context, "csr"),
|
||
MDString::get(*Context, "wait_for_done_write")};
|
||
default:
|
||
llvm_unreachable("Invalid register map interface mode");
|
||
}
|
||
}();
|
||
F->setMetadata(kSPIR2MD::IntelFPGAIPInterface,
|
||
MDNode::get(*Context, InterfaceMDVec));
|
||
}
|
||
// Generate metadata for Intel FPGA streaming interface
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeStreamingInterfaceINTEL)) {
|
||
std::vector<uint32_t> InterfaceVec = EM->getLiterals();
|
||
assert(InterfaceVec.size() == 1 &&
|
||
"Expected StreamingInterfaceINTEL to have exactly 1 literal");
|
||
std::vector<Metadata *> InterfaceMDVec =
|
||
[&]() -> std::vector<Metadata *> {
|
||
switch (InterfaceVec[0]) {
|
||
case 0:
|
||
return {MDString::get(*Context, "streaming")};
|
||
case 1:
|
||
return {MDString::get(*Context, "streaming"),
|
||
MDString::get(*Context, "stall_free_return")};
|
||
default:
|
||
llvm_unreachable("Invalid streaming interface mode");
|
||
}
|
||
}();
|
||
F->setMetadata(kSPIR2MD::IntelFPGAIPInterface,
|
||
MDNode::get(*Context, InterfaceMDVec));
|
||
}
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeMaximumRegistersINTEL)) {
|
||
NamedMDNode *ExecModeMD =
|
||
M->getOrInsertNamedMetadata(kSPIRVMD::ExecutionMode);
|
||
|
||
SmallVector<Metadata *, 4> ValueVec;
|
||
ValueVec.push_back(ConstantAsMetadata::get(F));
|
||
ValueVec.push_back(
|
||
ConstantAsMetadata::get(getUInt32(M, EM->getExecutionMode())));
|
||
ValueVec.push_back(
|
||
ConstantAsMetadata::get(getUInt32(M, EM->getLiterals()[0])));
|
||
ExecModeMD->addOperand(MDNode::get(*Context, ValueVec));
|
||
}
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeMaximumRegistersIdINTEL)) {
|
||
NamedMDNode *ExecModeMD =
|
||
M->getOrInsertNamedMetadata(kSPIRVMD::ExecutionMode);
|
||
|
||
SmallVector<Metadata *, 4> ValueVec;
|
||
ValueVec.push_back(ConstantAsMetadata::get(F));
|
||
ValueVec.push_back(
|
||
ConstantAsMetadata::get(getUInt32(M, EM->getExecutionMode())));
|
||
|
||
auto *ExecOp = BF->getModule()->getValue(EM->getLiterals()[0]);
|
||
ValueVec.push_back(
|
||
MDNode::get(*Context, ConstantAsMetadata::get(cast<ConstantInt>(
|
||
transValue(ExecOp, nullptr, nullptr)))));
|
||
ExecModeMD->addOperand(MDNode::get(*Context, ValueVec));
|
||
}
|
||
if (auto *EM =
|
||
BF->getExecutionMode(ExecutionModeNamedMaximumRegistersINTEL)) {
|
||
NamedMDNode *ExecModeMD =
|
||
M->getOrInsertNamedMetadata(kSPIRVMD::ExecutionMode);
|
||
|
||
SmallVector<Metadata *, 4> ValueVec;
|
||
ValueVec.push_back(ConstantAsMetadata::get(F));
|
||
ValueVec.push_back(
|
||
ConstantAsMetadata::get(getUInt32(M, EM->getExecutionMode())));
|
||
|
||
assert(EM->getLiterals()[0] == 0 &&
|
||
"Invalid named maximum number of registers");
|
||
ValueVec.push_back(MDString::get(*Context, "AutoINTEL"));
|
||
ExecModeMD->addOperand(MDNode::get(*Context, ValueVec));
|
||
}
|
||
}
|
||
NamedMDNode *MemoryModelMD =
|
||
M->getOrInsertNamedMetadata(kSPIRVMD::MemoryModel);
|
||
MemoryModelMD->addOperand(
|
||
getMDTwoInt(Context, static_cast<unsigned>(BM->getAddressingModel()),
|
||
static_cast<unsigned>(BM->getMemoryModel())));
|
||
createCXXStructor("llvm.global_ctors", CtorKernels);
|
||
return true;
|
||
}
|
||
|
||
bool SPIRVToLLVM::transOCLMetadata(SPIRVFunction *BF) {
|
||
Function *F = static_cast<Function *>(getTranslatedValue(BF));
|
||
assert(F && "Invalid translated function");
|
||
if (F->getCallingConv() != CallingConv::SPIR_KERNEL)
|
||
return true;
|
||
|
||
if (BF->hasDecorate(DecorationVectorComputeFunctionINTEL))
|
||
return true;
|
||
|
||
// Generate metadata for kernel_arg_addr_space
|
||
addKernelArgumentMetadata(
|
||
Context, SPIR_MD_KERNEL_ARG_ADDR_SPACE, BF, F,
|
||
[=](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
|
||
addKernelArgumentMetadata(Context, SPIR_MD_KERNEL_ARG_ACCESS_QUAL, BF, F,
|
||
[=](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
|
||
if (!transKernelArgTypeMedataFromString(Context, BM, F,
|
||
SPIR_MD_KERNEL_ARG_TYPE))
|
||
addKernelArgumentMetadata(Context, SPIR_MD_KERNEL_ARG_TYPE, BF, F,
|
||
[=](SPIRVFunctionParameter *Arg) {
|
||
return transOCLKernelArgTypeName(Arg);
|
||
});
|
||
// Generate metadata for kernel_arg_type_qual
|
||
if (!transKernelArgTypeMedataFromString(Context, BM, F,
|
||
SPIR_MD_KERNEL_ARG_TYPE_QUAL))
|
||
addKernelArgumentMetadata(
|
||
Context, SPIR_MD_KERNEL_ARG_TYPE_QUAL, BF, F,
|
||
[=](SPIRVFunctionParameter *Arg) {
|
||
std::string Qual;
|
||
if (Arg->hasDecorate(DecorationVolatile))
|
||
Qual = kOCLTypeQualifierName::Volatile;
|
||
Arg->foreachAttr([&](SPIRVFuncParamAttrKind Kind) {
|
||
Qual += Qual.empty() ? "" : " ";
|
||
if (Kind == FunctionParameterAttributeNoAlias)
|
||
Qual += kOCLTypeQualifierName::Restrict;
|
||
});
|
||
if (Arg->getType()->isTypePipe()) {
|
||
Qual += Qual.empty() ? "" : " ";
|
||
Qual += kOCLTypeQualifierName::Pipe;
|
||
}
|
||
return MDString::get(*Context, Qual);
|
||
});
|
||
// Generate metadata for kernel_arg_base_type
|
||
addKernelArgumentMetadata(Context, SPIR_MD_KERNEL_ARG_BASE_TYPE, BF, F,
|
||
[=](SPIRVFunctionParameter *Arg) {
|
||
return transOCLKernelArgTypeName(Arg);
|
||
});
|
||
// Generate metadata for kernel_arg_name
|
||
if (BM->isGenArgNameMDEnabled()) {
|
||
addKernelArgumentMetadata(Context, SPIR_MD_KERNEL_ARG_NAME, BF, F,
|
||
[=](SPIRVFunctionParameter *Arg) {
|
||
return MDString::get(*Context, Arg->getName());
|
||
});
|
||
}
|
||
// Generate metadata for kernel_arg_buffer_location
|
||
addBufferLocationMetadata(Context, BF, F, [=](SPIRVFunctionParameter *Arg) {
|
||
auto Literals = Arg->getDecorationLiterals(DecorationBufferLocationINTEL);
|
||
assert(Literals.size() == 1 &&
|
||
"BufferLocationINTEL decoration shall have 1 ID literal");
|
||
|
||
return ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt32Ty(*Context), Literals[0]));
|
||
});
|
||
// Generate metadata for kernel_arg_runtime_aligned
|
||
addRuntimeAlignedMetadata(Context, BF, F, [=](SPIRVFunctionParameter *Arg) {
|
||
return ConstantAsMetadata::get(
|
||
ConstantInt::get(Type::getInt1Ty(*Context), 1));
|
||
});
|
||
// Generate metadata for spirv.ParameterDecorations.
|
||
// In the entry-point wrapper pattern two SPIR-V functions map to the same
|
||
// LLVM function; only one carries decorated parameters. Skip the
|
||
// undecorated function so it cannot overwrite the decorated one.
|
||
bool HasDecorations = false;
|
||
BF->foreachArgument([&](SPIRVFunctionParameter *Arg) {
|
||
HasDecorations |= Arg->getNumDecorations() > 0;
|
||
});
|
||
if (HasDecorations || !F->hasMetadata(SPIRV_MD_PARAMETER_DECORATIONS))
|
||
addKernelArgumentMetadata(Context, SPIRV_MD_PARAMETER_DECORATIONS, BF, F,
|
||
[=](SPIRVFunctionParameter *Arg) {
|
||
return transDecorationsToMetadataList(
|
||
Context, Arg->getDecorations());
|
||
});
|
||
return true;
|
||
}
|
||
|
||
bool SPIRVToLLVM::transVectorComputeMetadata(SPIRVFunction *BF) {
|
||
using namespace VectorComputeUtil;
|
||
Function *F = static_cast<Function *>(getTranslatedValue(BF));
|
||
assert(F && "Invalid translated function");
|
||
|
||
if (BF->hasDecorate(DecorationStackCallINTEL))
|
||
F->addFnAttr(kVCMetadata::VCStackCall);
|
||
|
||
if (BF->hasDecorate(DecorationVectorComputeFunctionINTEL))
|
||
F->addFnAttr(kVCMetadata::VCFunction);
|
||
|
||
SPIRVWord SIMTMode = 0;
|
||
if (BF->hasDecorate(DecorationSIMTCallINTEL, 0, &SIMTMode))
|
||
F->addFnAttr(kVCMetadata::VCSIMTCall, std::to_string(SIMTMode));
|
||
|
||
auto SEVAttr = translateSEVMetadata(BF, F->getContext());
|
||
|
||
if (SEVAttr)
|
||
F->addAttributeAtIndex(AttributeList::ReturnIndex, SEVAttr.value());
|
||
|
||
for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
|
||
++I) {
|
||
auto ArgNo = I->getArgNo();
|
||
SPIRVFunctionParameter *BA = BF->getArgument(ArgNo);
|
||
SPIRVWord Kind;
|
||
if (BA->hasDecorate(DecorationFuncParamIOKindINTEL, 0, &Kind)) {
|
||
Attribute Attr = Attribute::get(*Context, kVCMetadata::VCArgumentIOKind,
|
||
std::to_string(Kind));
|
||
F->addParamAttr(ArgNo, Attr);
|
||
}
|
||
SEVAttr = translateSEVMetadata(BA, F->getContext());
|
||
if (SEVAttr)
|
||
F->addParamAttr(ArgNo, SEVAttr.value());
|
||
if (BA->hasDecorate(DecorationMediaBlockIOINTEL)) {
|
||
assert(BA->getType()->isTypeImage() &&
|
||
"MediaBlockIOINTEL decoration is valid only on image parameters");
|
||
F->addParamAttr(ArgNo,
|
||
Attribute::get(*Context, kVCMetadata::VCMediaBlockIO));
|
||
}
|
||
}
|
||
|
||
// Do not add float control if there is no any
|
||
bool IsVCFloatControl = false;
|
||
unsigned FloatControl = 0;
|
||
// RoundMode and FloatMode are always same for all types in Cm
|
||
// While Denorm could be different for double, float and half
|
||
if (isKernel(BF)) {
|
||
FPRoundingModeExecModeMap::foreach (
|
||
[&](FPRoundingMode VCRM, ExecutionMode EM) {
|
||
if (BF->getExecutionMode(EM)) {
|
||
IsVCFloatControl = true;
|
||
FloatControl |= getVCFloatControl(VCRM);
|
||
}
|
||
});
|
||
FPOperationModeExecModeMap::foreach (
|
||
[&](FPOperationMode VCFM, ExecutionMode EM) {
|
||
if (BF->getExecutionMode(EM)) {
|
||
IsVCFloatControl = true;
|
||
FloatControl |= getVCFloatControl(VCFM);
|
||
}
|
||
});
|
||
FPDenormModeExecModeMap::foreach ([&](FPDenormMode VCDM, ExecutionMode EM) {
|
||
auto ExecModes = BF->getExecutionModeRange(EM);
|
||
for (auto It = ExecModes.first; It != ExecModes.second; It++) {
|
||
IsVCFloatControl = true;
|
||
unsigned TargetWidth = (*It).second->getLiterals()[0];
|
||
VCFloatType FloatType = VCFloatTypeSizeMap::rmap(TargetWidth);
|
||
FloatControl |= getVCFloatControl(VCDM, FloatType);
|
||
}
|
||
});
|
||
} else {
|
||
if (BF->hasDecorate(DecorationFunctionRoundingModeINTEL)) {
|
||
std::vector<SPIRVDecorate const *> RoundModes =
|
||
BF->getDecorations(DecorationFunctionRoundingModeINTEL);
|
||
|
||
assert(RoundModes.size() == 3 && "Function must have precisely 3 "
|
||
"FunctionRoundingModeINTEL decoration");
|
||
|
||
auto *DecRound =
|
||
static_cast<SPIRVDecorateFunctionRoundingModeINTEL const *>(
|
||
RoundModes.at(0));
|
||
auto RoundingMode = DecRound->getRoundingMode();
|
||
#ifndef NDEBUG
|
||
for (auto *DecPreCast : RoundModes) {
|
||
auto *Dec = static_cast<SPIRVDecorateFunctionRoundingModeINTEL const *>(
|
||
DecPreCast);
|
||
assert(Dec->getRoundingMode() == RoundingMode &&
|
||
"Rounding Mode must be equal within all targets");
|
||
}
|
||
#endif
|
||
IsVCFloatControl = true;
|
||
FloatControl |= getVCFloatControl(RoundingMode);
|
||
}
|
||
|
||
if (BF->hasDecorate(DecorationFunctionDenormModeINTEL)) {
|
||
std::vector<SPIRVDecorate const *> DenormModes =
|
||
BF->getDecorations(DecorationFunctionDenormModeINTEL);
|
||
IsVCFloatControl = true;
|
||
|
||
for (const auto *DecPtr : DenormModes) {
|
||
const auto *DecDenorm =
|
||
static_cast<SPIRVDecorateFunctionDenormModeINTEL const *>(DecPtr);
|
||
VCFloatType FType =
|
||
VCFloatTypeSizeMap::rmap(DecDenorm->getTargetWidth());
|
||
FloatControl |= getVCFloatControl(DecDenorm->getDenormMode(), FType);
|
||
}
|
||
}
|
||
|
||
if (BF->hasDecorate(DecorationFunctionFloatingPointModeINTEL)) {
|
||
std::vector<SPIRVDecorate const *> FloatModes =
|
||
BF->getDecorations(DecorationFunctionFloatingPointModeINTEL);
|
||
|
||
assert(FloatModes.size() == 3 &&
|
||
"Function must have precisely 3 FunctionFloatingPointModeINTEL "
|
||
"decoration");
|
||
|
||
auto *DecFlt =
|
||
static_cast<SPIRVDecorateFunctionFloatingPointModeINTEL const *>(
|
||
FloatModes.at(0));
|
||
auto FloatingMode = DecFlt->getOperationMode();
|
||
#ifndef NDEBUG
|
||
for (auto *DecPreCast : FloatModes) {
|
||
auto *Dec =
|
||
static_cast<SPIRVDecorateFunctionFloatingPointModeINTEL const *>(
|
||
DecPreCast);
|
||
assert(Dec->getOperationMode() == FloatingMode &&
|
||
"Rounding Mode must be equal within all targets");
|
||
}
|
||
#endif
|
||
|
||
IsVCFloatControl = true;
|
||
FloatControl |= getVCFloatControl(FloatingMode);
|
||
}
|
||
}
|
||
|
||
if (IsVCFloatControl) {
|
||
Attribute Attr = Attribute::get(*Context, kVCMetadata::VCFloatControl,
|
||
std::to_string(FloatControl));
|
||
F->addFnAttr(Attr);
|
||
}
|
||
|
||
if (auto *EM =
|
||
BF->getExecutionMode(ExecutionModeSharedLocalMemorySizeINTEL)) {
|
||
unsigned int SLMSize = EM->getLiterals()[0];
|
||
Attribute Attr = Attribute::get(*Context, kVCMetadata::VCSLMSize,
|
||
std::to_string(SLMSize));
|
||
F->addFnAttr(Attr);
|
||
}
|
||
|
||
if (auto *EM = BF->getExecutionMode(ExecutionModeNamedBarrierCountINTEL)) {
|
||
unsigned int NBarrierCnt = EM->getLiterals()[0];
|
||
Attribute Attr = Attribute::get(*Context, kVCMetadata::VCNamedBarrierCount,
|
||
std::to_string(NBarrierCnt));
|
||
F->addFnAttr(Attr);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool SPIRVToLLVM::transFPGAFunctionMetadata(SPIRVFunction *BF, Function *F) {
|
||
if (BF->hasDecorate(DecorationStallEnableINTEL)) {
|
||
std::vector<Metadata *> MetadataVec;
|
||
MetadataVec.push_back(ConstantAsMetadata::get(getInt32(M, 1)));
|
||
F->setMetadata(kSPIR2MD::StallEnable, MDNode::get(*Context, MetadataVec));
|
||
}
|
||
if (BF->hasDecorate(DecorationStallFreeINTEL)) {
|
||
std::vector<Metadata *> MetadataVec;
|
||
MetadataVec.push_back(ConstantAsMetadata::get(getInt32(M, 1)));
|
||
F->setMetadata(kSPIR2MD::StallFree, MDNode::get(*Context, MetadataVec));
|
||
}
|
||
if (BF->hasDecorate(DecorationFuseLoopsInFunctionINTEL)) {
|
||
std::vector<Metadata *> MetadataVec;
|
||
auto Literals =
|
||
BF->getDecorationLiterals(DecorationFuseLoopsInFunctionINTEL);
|
||
MetadataVec.push_back(ConstantAsMetadata::get(getUInt32(M, Literals[0])));
|
||
MetadataVec.push_back(ConstantAsMetadata::get(getUInt32(M, Literals[1])));
|
||
F->setMetadata(kSPIR2MD::LoopFuse, MDNode::get(*Context, MetadataVec));
|
||
}
|
||
if (BF->hasDecorate(DecorationMathOpDSPModeINTEL)) {
|
||
std::vector<SPIRVWord> Literals =
|
||
BF->getDecorationLiterals(DecorationMathOpDSPModeINTEL);
|
||
assert(Literals.size() == 2 &&
|
||
"MathOpDSPModeINTEL decoration shall have 2 literals");
|
||
F->setMetadata(kSPIR2MD::PreferDSP,
|
||
MDNode::get(*Context, ConstantAsMetadata::get(
|
||
getUInt32(M, Literals[0]))));
|
||
if (Literals[1] != 0) {
|
||
F->setMetadata(kSPIR2MD::PropDSPPref,
|
||
MDNode::get(*Context, ConstantAsMetadata::get(
|
||
getUInt32(M, Literals[1]))));
|
||
}
|
||
}
|
||
if (BF->hasDecorate(DecorationInitiationIntervalINTEL)) {
|
||
std::vector<Metadata *> MetadataVec;
|
||
auto Literals =
|
||
BF->getDecorationLiterals(DecorationInitiationIntervalINTEL);
|
||
MetadataVec.push_back(ConstantAsMetadata::get(getUInt32(M, Literals[0])));
|
||
F->setMetadata(kSPIR2MD::InitiationInterval,
|
||
MDNode::get(*Context, MetadataVec));
|
||
}
|
||
if (BF->hasDecorate(DecorationMaxConcurrencyINTEL)) {
|
||
std::vector<Metadata *> MetadataVec;
|
||
auto Literals = BF->getDecorationLiterals(DecorationMaxConcurrencyINTEL);
|
||
MetadataVec.push_back(ConstantAsMetadata::get(getUInt32(M, Literals[0])));
|
||
F->setMetadata(kSPIR2MD::MaxConcurrency,
|
||
MDNode::get(*Context, MetadataVec));
|
||
}
|
||
if (BF->hasDecorate(DecorationPipelineEnableINTEL)) {
|
||
auto Literals = BF->getDecorationLiterals(DecorationPipelineEnableINTEL);
|
||
std::vector<Metadata *> MetadataVec;
|
||
MetadataVec.push_back(ConstantAsMetadata::get(getInt32(M, Literals[0])));
|
||
F->setMetadata(kSPIR2MD::PipelineKernel,
|
||
MDNode::get(*Context, MetadataVec));
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool SPIRVToLLVM::transAlign(SPIRVValue *BV, Value *V) {
|
||
if (auto *AL = dyn_cast<AllocaInst>(V)) {
|
||
if (auto Align = getAlignment(BV))
|
||
AL->setAlignment(llvm::Align(*Align));
|
||
return true;
|
||
}
|
||
if (auto *GV = dyn_cast<GlobalVariable>(V)) {
|
||
if (auto Align = getAlignment(BV))
|
||
GV->setAlignment(MaybeAlign(*Align));
|
||
return true;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
Instruction *SPIRVToLLVM::transOCLBuiltinFromExtInst(SPIRVExtInst *BC,
|
||
BasicBlock *BB) {
|
||
assert(BB && "Invalid BB");
|
||
auto ExtOp = static_cast<OCLExtOpKind>(BC->getExtOp());
|
||
std::string UnmangledName = OCLExtOpMap::map(ExtOp);
|
||
|
||
assert(BM->getBuiltinSet(BC->getExtSetId()) == SPIRVEIS_OpenCL &&
|
||
"Not OpenCL extended instruction");
|
||
|
||
std::vector<Type *> ArgTypes = transTypeVector(BC->getArgTypes(), true);
|
||
for (unsigned I = 0; I < ArgTypes.size(); I++) {
|
||
// Special handling for "truly" untyped pointers to preserve correct OCL
|
||
// bultin mangling.
|
||
if (isa<PointerType>(ArgTypes[I]) &&
|
||
BC->getArgValue(I)->isUntypedVariable()) {
|
||
auto *BVar = static_cast<SPIRVUntypedVariableKHR *>(BC->getArgValue(I));
|
||
ArgTypes[I] = TypedPointerType::get(
|
||
transType(BVar->getDataType()),
|
||
SPIRSPIRVAddrSpaceMap::rmap(BVar->getStorageClass()));
|
||
}
|
||
}
|
||
|
||
Type *RetTy = transType(BC->getType());
|
||
std::string MangledName =
|
||
getSPIRVFriendlyIRFunctionName(ExtOp, ArgTypes, RetTy);
|
||
opaquifyTypedPointers(ArgTypes);
|
||
|
||
SPIRVDBG(spvdbgs() << "[transOCLBuiltinFromExtInst] UnmangledName: "
|
||
<< UnmangledName << " MangledName: " << MangledName
|
||
<< '\n');
|
||
|
||
FunctionType *FT = FunctionType::get(RetTy, ArgTypes, false);
|
||
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);
|
||
if (isFuncReadNone(UnmangledName))
|
||
F->setDoesNotAccessMemory();
|
||
}
|
||
auto Args = transValue(BC->getArgValues(), F, BB);
|
||
SPIRVDBG(dbgs() << "[transOCLBuiltinFromExtInst] Function: " << *F
|
||
<< ", Args: ";
|
||
for (auto &I : Args) dbgs() << *I << ", "; dbgs() << '\n');
|
||
CallInst *CI = CallInst::Create(F, Args, BC->getName(), BB);
|
||
setCallingConv(CI);
|
||
addFnAttr(CI, Attribute::NoUnwind);
|
||
return CI;
|
||
}
|
||
|
||
void SPIRVToLLVM::transAuxDataInst(SPIRVExtInst *BC) {
|
||
assert(BC->getExtSetKind() == SPIRV::SPIRVEIS_NonSemantic_AuxData);
|
||
if (!BC->getModule()->preserveAuxData())
|
||
return;
|
||
auto Args = BC->getArguments();
|
||
// Args 0 and 1 are common between attributes and metadata.
|
||
// 0 is the global object, 1 is the name of the attribute/metadata as a string
|
||
auto *Arg0 = BC->getModule()->getValue(Args[0]);
|
||
auto *GO = cast<GlobalObject>(getTranslatedValue(Arg0));
|
||
auto *F = dyn_cast<Function>(GO);
|
||
auto *GV = dyn_cast<GlobalVariable>(GO);
|
||
assert((F || GV) && "Value should already have been translated!");
|
||
auto AttrOrMDName = BC->getModule()->get<SPIRVString>(Args[1])->getStr();
|
||
switch (BC->getExtOp()) {
|
||
case NonSemanticAuxData::FunctionAttribute:
|
||
case NonSemanticAuxData::GlobalVariableAttribute: {
|
||
assert(Args.size() < 4 && "Unexpected FunctionAttribute Args");
|
||
// If this attr was specially handled and added elsewhere, skip it.
|
||
Attribute::AttrKind AsKind = Attribute::getAttrKindFromName(AttrOrMDName);
|
||
if (AsKind != Attribute::None)
|
||
if ((F && F->hasFnAttribute(AsKind)) || (GV && GV->hasAttribute(AsKind)))
|
||
return;
|
||
if (AsKind == Attribute::None)
|
||
if ((F && F->hasFnAttribute(AttrOrMDName)) ||
|
||
(GV && GV->hasAttribute(AttrOrMDName)))
|
||
return;
|
||
// For attributes, arg 2 is the attribute value as a string, which may not
|
||
// exist.
|
||
if (Args.size() == 3) {
|
||
auto AttrValue = BC->getModule()->get<SPIRVString>(Args[2])->getStr();
|
||
if (F)
|
||
F->addFnAttr(AttrOrMDName, AttrValue);
|
||
else
|
||
GV->addAttribute(AttrOrMDName, AttrValue);
|
||
} else {
|
||
if (AsKind != Attribute::None) {
|
||
if (F)
|
||
F->addFnAttr(AsKind);
|
||
else
|
||
GV->addAttribute(AsKind);
|
||
} else {
|
||
if (F)
|
||
F->addFnAttr(AttrOrMDName);
|
||
else
|
||
GV->addAttribute(AttrOrMDName);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case NonSemanticAuxData::FunctionMetadata:
|
||
case NonSemanticAuxData::GlobalVariableMetadata: {
|
||
// If this metadata was specially handled and added elsewhere, skip it.
|
||
if (GO->hasMetadata(AttrOrMDName))
|
||
return;
|
||
SmallVector<Metadata *> MetadataArgs;
|
||
// Process the metadata values.
|
||
for (size_t CurArg = 2; CurArg < Args.size(); CurArg++) {
|
||
auto *Arg = BC->getModule()->get<SPIRVEntry>(Args[CurArg]);
|
||
// For metadata, the metadata values can be either values or strings.
|
||
if (Arg->getOpCode() == OpString) {
|
||
auto *ArgAsStr = static_cast<SPIRVString *>(Arg);
|
||
MetadataArgs.push_back(
|
||
MDString::get(GO->getContext(), ArgAsStr->getStr()));
|
||
} else {
|
||
auto *ArgAsVal = static_cast<SPIRVValue *>(Arg);
|
||
auto *TranslatedMD = transValue(ArgAsVal, nullptr, nullptr);
|
||
MetadataArgs.push_back(ValueAsMetadata::get(TranslatedMD));
|
||
}
|
||
}
|
||
GO->setMetadata(AttrOrMDName, MDNode::get(*Context, MetadataArgs));
|
||
break;
|
||
}
|
||
default:
|
||
llvm_unreachable("Invalid op");
|
||
}
|
||
}
|
||
|
||
// SPIR-V only contains language version. Use OpenCL language version as
|
||
// SPIR version.
|
||
void SPIRVToLLVM::transSourceLanguage() {
|
||
SPIRVWord Ver = 0;
|
||
SourceLanguage Lang = BM->getSourceLanguage(&Ver);
|
||
if (Lang != SourceLanguageUnknown && // Allow unknown for debug info test
|
||
Lang != SourceLanguageOpenCL_C && Lang != SourceLanguageCPP_for_OpenCL &&
|
||
Lang != SourceLanguageOpenCL_CPP)
|
||
return;
|
||
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);
|
||
|
||
if (Lang == SourceLanguageOpenCL_C) {
|
||
addOCLVersionMetadata(Context, M, kSPIR2MD::OCLVer, Major, Minor);
|
||
return;
|
||
}
|
||
if (Lang == SourceLanguageCPP_for_OpenCL) {
|
||
addOCLVersionMetadata(Context, M, kSPIR2MD::OCLCXXVer, Major, Minor);
|
||
addOCLVersionMetadata(Context, M, kSPIR2MD::OCLVer,
|
||
Ver == kOCLVer::CLCXX10 ? 2 : 3, 0);
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
llvm::GlobalValue::LinkageTypes
|
||
SPIRVToLLVM::transLinkageType(const SPIRVValue *V) {
|
||
std::string ValueName = V->getName();
|
||
if (ValueName == "llvm.used" || ValueName == "llvm.compiler.used")
|
||
return GlobalValue::AppendingLinkage;
|
||
int LT = V->getLinkageType();
|
||
switch (LT) {
|
||
case internal::LinkageTypeInternal:
|
||
return GlobalValue::InternalLinkage;
|
||
case LinkageTypeImport:
|
||
// Function declaration
|
||
if (V->getOpCode() == OpFunction) {
|
||
if (static_cast<const SPIRVFunction *>(V)->getNumBasicBlock() == 0)
|
||
return GlobalValue::ExternalLinkage;
|
||
}
|
||
// Variable declaration
|
||
if (V->getOpCode() == OpVariable ||
|
||
V->getOpCode() == OpUntypedVariableKHR) {
|
||
if (static_cast<const SPIRVVariableBase *>(V)->getInitializer() == 0)
|
||
return GlobalValue::ExternalLinkage;
|
||
}
|
||
// Definition
|
||
return GlobalValue::AvailableExternallyLinkage;
|
||
case LinkageTypeExport:
|
||
if (V->getOpCode() == OpVariable ||
|
||
V->getOpCode() == OpUntypedVariableKHR) {
|
||
if (static_cast<const SPIRVVariableBase *>(V)->getInitializer() == 0)
|
||
// Tentative definition
|
||
return GlobalValue::CommonLinkage;
|
||
}
|
||
return GlobalValue::ExternalLinkage;
|
||
case LinkageTypeLinkOnceODR:
|
||
return GlobalValue::LinkOnceODRLinkage;
|
||
default:
|
||
llvm_unreachable("Invalid linkage type");
|
||
}
|
||
}
|
||
|
||
Instruction *SPIRVToLLVM::transAllAny(SPIRVInstruction *I, BasicBlock *BB) {
|
||
CallInst *CI = cast<CallInst>(transSPIRVBuiltinFromInst(I, BB));
|
||
auto Mutator = mutateCallInst(
|
||
CI, getSPIRVFuncName(I->getOpCode(), getSPIRVFuncSuffix(I)));
|
||
Mutator.mapArg(0, [](IRBuilder<> &Builder, Value *OldArg) {
|
||
auto *NewArgTy = OldArg->getType()->getWithNewBitWidth(8);
|
||
return Builder.CreateSExtOrBitCast(OldArg, NewArgTy);
|
||
});
|
||
return cast<Instruction>(Mutator.getMutated());
|
||
}
|
||
|
||
Instruction *SPIRVToLLVM::transRelational(SPIRVInstruction *I, BasicBlock *BB) {
|
||
CallInst *CI = cast<CallInst>(transSPIRVBuiltinFromInst(I, BB));
|
||
auto Mutator = mutateCallInst(
|
||
CI, getSPIRVFuncName(I->getOpCode(), getSPIRVFuncSuffix(I)));
|
||
if (CI->getType()->isVectorTy()) {
|
||
Type *RetTy = CI->getType()->getWithNewBitWidth(8);
|
||
Mutator.changeReturnType(RetTy, [=](IRBuilder<> &Builder, CallInst *NewCI) {
|
||
return Builder.CreateTruncOrBitCast(NewCI, CI->getType());
|
||
});
|
||
}
|
||
return cast<Instruction>(Mutator.getMutated());
|
||
}
|
||
|
||
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS) {
|
||
int IgnoreErrCode;
|
||
return getSpirvReport(IS, IgnoreErrCode);
|
||
}
|
||
|
||
std::optional<SPIRVModuleReport> getSpirvReport(std::istream &IS,
|
||
int &ErrCode) {
|
||
SPIRVWord Word;
|
||
std::string Name;
|
||
std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
|
||
SPIRVDecoder D(IS, *BM);
|
||
D >> Word;
|
||
if (Word != MagicNumber) {
|
||
ErrCode = SPIRVEC_InvalidMagicNumber;
|
||
return {};
|
||
}
|
||
D >> Word;
|
||
if (!isSPIRVVersionKnown(static_cast<VersionNumber>(Word))) {
|
||
ErrCode = SPIRVEC_InvalidVersionNumber;
|
||
return {};
|
||
}
|
||
SPIRVModuleReport Report;
|
||
Report.Version = static_cast<SPIRV::VersionNumber>(Word);
|
||
// Skip: Generator’s magic number, Bound and Reserved word
|
||
D.ignore(3);
|
||
|
||
bool IsReportGenCompleted = false, IsMemoryModelDefined = false;
|
||
while (!IS.bad() && !IsReportGenCompleted && D.getWordCountAndOpCode()) {
|
||
switch (D.OpCode) {
|
||
case OpCapability:
|
||
D >> Word;
|
||
Report.Capabilities.push_back(Word);
|
||
break;
|
||
case OpExtension:
|
||
Name.clear();
|
||
D >> Name;
|
||
Report.Extensions.push_back(Name);
|
||
break;
|
||
case OpExtInstImport:
|
||
Name.clear();
|
||
D >> Word >> Name;
|
||
Report.ExtendedInstructionSets.push_back(Name);
|
||
break;
|
||
case OpMemoryModel:
|
||
if (IsMemoryModelDefined) {
|
||
ErrCode = SPIRVEC_RepeatedMemoryModel;
|
||
return {};
|
||
}
|
||
SPIRVAddressingModelKind AddrModel;
|
||
SPIRVMemoryModelKind MemoryModel;
|
||
D >> AddrModel >> MemoryModel;
|
||
if (!isValid(AddrModel)) {
|
||
ErrCode = SPIRVEC_InvalidAddressingModel;
|
||
return {};
|
||
}
|
||
if (!isValid(MemoryModel)) {
|
||
ErrCode = SPIRVEC_InvalidMemoryModel;
|
||
return {};
|
||
}
|
||
Report.MemoryModel = MemoryModel;
|
||
Report.AddrModel = AddrModel;
|
||
IsMemoryModelDefined = true;
|
||
// In this report we don't analyze instructions after OpMemoryModel
|
||
IsReportGenCompleted = true;
|
||
break;
|
||
default:
|
||
// No more instructions to gather information about
|
||
IsReportGenCompleted = true;
|
||
}
|
||
}
|
||
if (IS.bad()) {
|
||
ErrCode = SPIRVEC_InvalidModule;
|
||
return {};
|
||
}
|
||
if (!IsMemoryModelDefined) {
|
||
ErrCode = SPIRVEC_UnspecifiedMemoryModel;
|
||
return {};
|
||
}
|
||
ErrCode = SPIRVEC_Success;
|
||
return std::make_optional(std::move(Report));
|
||
}
|
||
|
||
constexpr std::string_view formatAddressingModel(uint32_t AddrModel) {
|
||
switch (AddrModel) {
|
||
case AddressingModelLogical:
|
||
return "Logical";
|
||
case AddressingModelPhysical32:
|
||
return "Physical32";
|
||
case AddressingModelPhysical64:
|
||
return "Physical64";
|
||
case AddressingModelPhysicalStorageBuffer64:
|
||
return "PhysicalStorageBuffer64";
|
||
default:
|
||
return "Unknown";
|
||
}
|
||
}
|
||
|
||
constexpr std::string_view formatMemoryModel(uint32_t MemoryModel) {
|
||
switch (MemoryModel) {
|
||
case MemoryModelSimple:
|
||
return "Simple";
|
||
case MemoryModelGLSL450:
|
||
return "GLSL450";
|
||
case MemoryModelOpenCL:
|
||
return "OpenCL";
|
||
case MemoryModelVulkan:
|
||
return "Vulkan";
|
||
default:
|
||
return "Unknown";
|
||
}
|
||
}
|
||
|
||
SPIRVModuleTextReport formatSpirvReport(const SPIRVModuleReport &Report) {
|
||
SPIRVModuleTextReport TextReport;
|
||
TextReport.Version =
|
||
formatVersionNumber(static_cast<uint32_t>(Report.Version));
|
||
TextReport.AddrModel = formatAddressingModel(Report.AddrModel);
|
||
TextReport.MemoryModel = formatMemoryModel(Report.MemoryModel);
|
||
// format capability codes as strings
|
||
std::string Name;
|
||
for (auto Capability : Report.Capabilities) {
|
||
bool Found = SPIRVCapabilityNameMap::find(
|
||
static_cast<SPIRVCapabilityKind>(Capability), &Name);
|
||
TextReport.Capabilities.push_back(Found ? Name : "Unknown");
|
||
}
|
||
// other fields with string content can be copied as is
|
||
TextReport.Extensions = Report.Extensions;
|
||
TextReport.ExtendedInstructionSets = Report.ExtendedInstructionSets;
|
||
return TextReport;
|
||
}
|
||
|
||
std::unique_ptr<SPIRVModule> readSpirvModule(std::istream &IS,
|
||
const SPIRV::TranslatorOpts &Opts,
|
||
std::string &ErrMsg) {
|
||
std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule(Opts));
|
||
|
||
IS >> *BM;
|
||
if (!BM->isModuleValid()) {
|
||
BM->getError(ErrMsg);
|
||
return nullptr;
|
||
}
|
||
return BM;
|
||
}
|
||
|
||
std::unique_ptr<SPIRVModule> readSpirvModule(std::istream &IS,
|
||
std::string &ErrMsg) {
|
||
SPIRV::TranslatorOpts DefaultOpts;
|
||
return readSpirvModule(IS, DefaultOpts, ErrMsg);
|
||
}
|
||
|
||
} // namespace SPIRV
|
||
|
||
std::unique_ptr<Module>
|
||
llvm::convertSpirvToLLVM(LLVMContext &C, SPIRVModule &BM,
|
||
const SPIRV::TranslatorOpts &Opts,
|
||
std::string &ErrMsg) {
|
||
std::unique_ptr<Module> M(new Module("", C));
|
||
|
||
SPIRVToLLVM BTL(M.get(), &BM);
|
||
|
||
if (!BTL.translate()) {
|
||
BM.getError(ErrMsg);
|
||
return nullptr;
|
||
}
|
||
|
||
llvm::ModulePassManager PassMgr;
|
||
addSPIRVBIsLoweringPass(PassMgr, Opts.getDesiredBIsRepresentation());
|
||
llvm::ModuleAnalysisManager MAM;
|
||
MAM.registerPass([&] { return PassInstrumentationAnalysis(); });
|
||
PassMgr.run(*M, MAM);
|
||
|
||
return M;
|
||
}
|
||
|
||
std::unique_ptr<Module>
|
||
llvm::convertSpirvToLLVM(LLVMContext &C, SPIRVModule &BM, std::string &ErrMsg) {
|
||
SPIRV::TranslatorOpts DefaultOpts;
|
||
return llvm::convertSpirvToLLVM(C, BM, DefaultOpts, ErrMsg);
|
||
}
|
||
|
||
bool llvm::readSpirv(LLVMContext &C, std::istream &IS, Module *&M,
|
||
std::string &ErrMsg) {
|
||
SPIRV::TranslatorOpts DefaultOpts;
|
||
// As it is stated in the documentation, the translator accepts all SPIR-V
|
||
// extensions by default
|
||
DefaultOpts.enableAllExtensions();
|
||
return llvm::readSpirv(C, DefaultOpts, IS, M, ErrMsg);
|
||
}
|
||
|
||
bool llvm::readSpirv(LLVMContext &C, const SPIRV::TranslatorOpts &Opts,
|
||
std::istream &IS, Module *&M, std::string &ErrMsg) {
|
||
std::unique_ptr<SPIRVModule> BM(readSpirvModule(IS, Opts, ErrMsg));
|
||
|
||
if (!BM)
|
||
return false;
|
||
|
||
if (Opts.getFnVarSpecEnable()) {
|
||
if (!specializeFnVariants(BM.get(), ErrMsg)) {
|
||
return false;
|
||
}
|
||
|
||
// Write out the specialized/targeted module
|
||
if (!BM->getFnVarSpvOut().empty()) {
|
||
auto SaveOpt = SPIRVUseTextFormat;
|
||
auto OFSSpv = std::ofstream(BM->getFnVarSpvOut(), std::ios::binary);
|
||
SPIRVUseTextFormat = false;
|
||
OFSSpv << *BM;
|
||
if (BM->getError(ErrMsg) != SPIRVEC_Success) {
|
||
return false;
|
||
}
|
||
SPIRVUseTextFormat = SaveOpt;
|
||
}
|
||
}
|
||
|
||
if (BM->getExtension().find("SPV_INTEL_function_variants") !=
|
||
BM->getExtension().end()) {
|
||
ErrMsg = "Instructions from SPV_INTEL_function_variants are not "
|
||
"convertible to LLVM IR.";
|
||
return false;
|
||
}
|
||
|
||
M = convertSpirvToLLVM(C, *BM, Opts, ErrMsg).release();
|
||
|
||
if (!M)
|
||
return false;
|
||
|
||
if (DbgSaveTmpLLVM)
|
||
dumpLLVM(M, DbgTmpLLVMFileName);
|
||
|
||
return true;
|
||
}
|
||
|
||
bool llvm::getSpecConstInfo(std::istream &IS,
|
||
std::vector<SpecConstInfoTy> &SpecConstInfo) {
|
||
std::unique_ptr<SPIRVModule> BM(SPIRVModule::createSPIRVModule());
|
||
BM->setAutoAddExtensions(false);
|
||
SPIRVDecoder D(IS, *BM);
|
||
SPIRVWord Magic;
|
||
D >> Magic;
|
||
if (!BM->getErrorLog().checkError(Magic == MagicNumber, SPIRVEC_InvalidModule,
|
||
"invalid magic number")) {
|
||
return false;
|
||
}
|
||
// Skip the rest of the header
|
||
D.ignore(4);
|
||
|
||
// According to the logical layout of SPIRV module (p2.4 of the spec),
|
||
// all constant instructions must appear before function declarations.
|
||
while (D.OpCode != OpFunction && D.getWordCountAndOpCode()) {
|
||
switch (D.OpCode) {
|
||
case OpDecorate:
|
||
// The decoration is added to the module in scope of SPIRVDecorate::decode
|
||
D.getEntry();
|
||
break;
|
||
case OpTypeBool:
|
||
case OpTypeInt:
|
||
case OpTypeFloat:
|
||
BM->addEntry(D.getEntry());
|
||
break;
|
||
case OpSpecConstant:
|
||
case OpSpecConstantTrue:
|
||
case OpSpecConstantFalse: {
|
||
auto *C = BM->addConstant(static_cast<SPIRVValue *>(D.getEntry()));
|
||
SPIRVWord SpecConstIdLiteral = 0;
|
||
if (C->hasDecorate(DecorationSpecId, 0, &SpecConstIdLiteral)) {
|
||
SPIRVType *Ty = C->getType();
|
||
uint32_t SpecConstSize = Ty->isTypeBool() ? 1 : Ty->getBitWidth() / 8;
|
||
std::string TypeString = "";
|
||
if (Ty->isTypeBool()) {
|
||
TypeString = "i1";
|
||
} else if (Ty->isTypeInt()) {
|
||
switch (SpecConstSize) {
|
||
case 1:
|
||
TypeString = "i8";
|
||
break;
|
||
case 2:
|
||
TypeString = "i16";
|
||
break;
|
||
case 4:
|
||
TypeString = "i32";
|
||
break;
|
||
case 8:
|
||
TypeString = "i64";
|
||
break;
|
||
}
|
||
} else if (Ty->isTypeFloat()) {
|
||
switch (SpecConstSize) {
|
||
case 2:
|
||
TypeString = "f16";
|
||
break;
|
||
case 4:
|
||
TypeString = "f32";
|
||
break;
|
||
case 8:
|
||
TypeString = "f64";
|
||
break;
|
||
}
|
||
}
|
||
if (TypeString == "")
|
||
return false;
|
||
|
||
SpecConstInfo.emplace_back(
|
||
SpecConstInfoTy({SpecConstIdLiteral, SpecConstSize, TypeString}));
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
D.ignoreInstruction();
|
||
}
|
||
}
|
||
return !IS.bad();
|
||
}
|
||
|
||
// clang-format off
|
||
const StringSet<> SPIRVToLLVM::BuiltInConstFunc {
|
||
"convert", "get_work_dim", "get_global_size", "sub_group_ballot_bit_count",
|
||
"get_global_id", "get_local_size", "get_local_id", "get_num_groups",
|
||
"get_group_id", "get_global_offset", "acos", "acosh", "acospi",
|
||
"asin", "asinh", "asinpi", "atan", "atan2", "atanh", "atanpi",
|
||
"atan2pi", "cbrt", "ceil", "copysign", "cos", "cosh", "cospi",
|
||
"erfc", "erf", "exp", "exp2", "exp10", "expm1", "fabs", "fdim",
|
||
"floor", "fma", "fmax", "fmin", "fmod", "ilogb", "ldexp", "lgamma",
|
||
"log", "log2", "log10", "log1p", "logb", "mad", "maxmag", "minmag",
|
||
"nan", "nextafter", "pow", "pown", "powr", "remainder", "rint",
|
||
"rootn", "round", "rsqrt", "sin", "sinh", "sinpi", "sqrt", "tan",
|
||
"tanh", "tanpi", "tgamma", "trunc", "half_cos", "half_divide", "half_exp",
|
||
"half_exp2", "half_exp10", "half_log", "half_log2", "half_log10", "half_powr",
|
||
"half_recip", "half_rsqrt", "half_sin", "half_sqrt", "half_tan", "native_cos",
|
||
"native_divide", "native_exp", "native_exp2", "native_exp10", "native_log",
|
||
"native_log2", "native_log10", "native_powr", "native_recip", "native_rsqrt",
|
||
"native_sin", "native_sqrt", "native_tan", "abs", "abs_diff", "add_sat", "hadd",
|
||
"rhadd", "clamp", "clz", "mad_hi", "mad_sat", "max", "min", "mul_hi", "rotate",
|
||
"sub_sat", "upsample", "popcount", "mad24", "mul24", "degrees", "mix", "radians",
|
||
"step", "smoothstep", "sign", "cross", "dot", "distance", "length", "normalize",
|
||
"fast_distance", "fast_length", "fast_normalize", "isequal", "isnotequal",
|
||
"isgreater", "isgreaterequal", "isless", "islessequal", "islessgreater",
|
||
"isfinite", "isinf", "isnan", "isnormal", "isordered", "isunordered", "signbit",
|
||
"any", "all", "bitselect", "select", "shuffle", "shuffle2", "get_image_width",
|
||
"get_image_height", "get_image_depth", "get_image_channel_data_type",
|
||
"get_image_channel_order", "get_image_dim", "get_image_array_size",
|
||
"get_image_array_size", "sub_group_inverse_ballot", "sub_group_ballot_bit_extract",
|
||
};
|
||
// clang-format on
|