2732 lines
98 KiB
C++
2732 lines
98 KiB
C++
//===- SPIRVUtil.cpp - SPIR-V Utilities -------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM/SPIRV Translator
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal with the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimers.
|
|
// Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimers in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// Neither the names of Advanced Micro Devices, Inc., nor the names of its
|
|
// contributors may be used to endorse or promote products derived from this
|
|
// Software without specific prior written permission.
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
|
|
// THE SOFTWARE.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
/// \file
|
|
///
|
|
/// This file defines utility classes and functions shared by SPIR-V
|
|
/// reader/writer.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// This file needs to be included before anything that declares
|
|
// llvm::PointerType to avoid a compilation bug on MSVC.
|
|
#include "llvm/Demangle/Demangle.h"
|
|
#include "llvm/Demangle/ItaniumDemangle.h"
|
|
|
|
#include "FunctionDescriptor.h"
|
|
#include "ManglingUtils.h"
|
|
#include "NameMangleAPI.h"
|
|
#include "OCLUtil.h"
|
|
#include "ParameterType.h"
|
|
#include "SPIRVInternal.h"
|
|
#include "SPIRVMDWalker.h"
|
|
#include "libSPIRV/SPIRVDecorate.h"
|
|
#include "libSPIRV/SPIRVValue.h"
|
|
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include "llvm/Bitcode/BitcodeWriter.h"
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/Metadata.h"
|
|
#include "llvm/IR/Operator.h"
|
|
#include "llvm/IR/TypedPointerType.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
|
|
#include <functional>
|
|
#include <sstream>
|
|
|
|
#define DEBUG_TYPE "spirv"
|
|
|
|
namespace SPIRV {
|
|
|
|
#ifdef _SPIRV_SUPPORT_TEXT_FMT
|
|
cl::opt<bool, true>
|
|
UseTextFormat("spirv-text",
|
|
cl::desc("Use text format for SPIR-V for debugging purpose"),
|
|
cl::location(SPIRVUseTextFormat));
|
|
#endif
|
|
|
|
#ifdef _SPIRVDBG
|
|
cl::opt<bool, true> EnableDbgOutput("spirv-debug",
|
|
cl::desc("Enable SPIR-V debug output"),
|
|
cl::location(SPIRVDbgEnable));
|
|
#endif
|
|
|
|
bool isSupportedTriple(Triple T) { return T.isSPIR() || T.isSPIRV(); }
|
|
|
|
void addFnAttr(CallInst *Call, Attribute::AttrKind Attr) {
|
|
Call->addFnAttr(Attr);
|
|
}
|
|
|
|
void removeFnAttr(CallInst *Call, Attribute::AttrKind Attr) {
|
|
Call->removeFnAttr(Attr);
|
|
}
|
|
|
|
Value *extendVector(Value *V, FixedVectorType *NewType,
|
|
IRBuilderBase &Builder) {
|
|
unsigned OldSize = cast<FixedVectorType>(V->getType())->getNumElements();
|
|
unsigned NewSize = NewType->getNumElements();
|
|
assert(OldSize < NewSize);
|
|
std::vector<Constant *> Components;
|
|
IntegerType *Int32Ty = Builder.getInt32Ty();
|
|
for (unsigned I = 0; I < NewSize; I++) {
|
|
if (I < OldSize)
|
|
Components.push_back(ConstantInt::get(Int32Ty, I));
|
|
else
|
|
Components.push_back(PoisonValue::get(Int32Ty));
|
|
}
|
|
|
|
return Builder.CreateShuffleVector(V, PoisonValue::get(V->getType()),
|
|
ConstantVector::get(Components), "vecext");
|
|
}
|
|
|
|
void saveLLVMModule(Module *M, const std::string &OutputFile) {
|
|
std::error_code EC;
|
|
ToolOutputFile Out(OutputFile.c_str(), EC, sys::fs::OF_None);
|
|
if (EC) {
|
|
SPIRVDBG(errs() << "Fails to open output file: " << EC.message();)
|
|
return;
|
|
}
|
|
|
|
WriteBitcodeToFile(*M, Out.os());
|
|
Out.keep();
|
|
}
|
|
|
|
std::string mapLLVMTypeToOCLType(const Type *Ty, bool Signed, Type *PET) {
|
|
if (Ty->isHalfTy())
|
|
return "half";
|
|
if (Ty->isFloatTy())
|
|
return "float";
|
|
if (Ty->isDoubleTy())
|
|
return "double";
|
|
if (const auto *IntTy = dyn_cast<IntegerType>(Ty)) {
|
|
std::string SignPrefix;
|
|
std::string Stem;
|
|
if (!Signed)
|
|
SignPrefix = "u";
|
|
switch (IntTy->getIntegerBitWidth()) {
|
|
case 8:
|
|
Stem = "char";
|
|
break;
|
|
case 16:
|
|
Stem = "short";
|
|
break;
|
|
case 32:
|
|
Stem = "int";
|
|
break;
|
|
case 64:
|
|
Stem = "long";
|
|
break;
|
|
default:
|
|
Stem = "invalid_type";
|
|
break;
|
|
}
|
|
return SignPrefix + Stem;
|
|
}
|
|
if (const auto *VecTy = dyn_cast<FixedVectorType>(Ty)) {
|
|
Type *EleTy = VecTy->getElementType();
|
|
unsigned Size = VecTy->getNumElements();
|
|
std::stringstream Ss;
|
|
Ss << mapLLVMTypeToOCLType(EleTy, Signed) << Size;
|
|
return Ss.str();
|
|
}
|
|
// It is expected that `Ty` can be mapped to `ReturnType` from "Optional
|
|
// Postfixes for SPIR-V Builtin Function Names" section of
|
|
// SPIRVRepresentationInLLVM.rst document (aka SPIRV-friendly IR).
|
|
// If `Ty` is not a scalar or vector type mentioned in the document (return
|
|
// value of some SPIR-V instructions may be represented as pointer to a struct
|
|
// in LLVM IR) we can mangle the type.
|
|
BuiltinFuncMangleInfo MangleInfo;
|
|
if (Ty->isPointerTy())
|
|
Ty = TypedPointerType::get(PET, Ty->getPointerAddressSpace());
|
|
std::string MangledName =
|
|
mangleBuiltin("", const_cast<Type *>(Ty), &MangleInfo);
|
|
// Remove "_Z0"(3 characters) from the front of the name
|
|
return MangledName.erase(0, 3);
|
|
}
|
|
|
|
StructType *getOrCreateOpaqueStructType(Module *M, StringRef Name) {
|
|
auto *OpaqueType = StructType::getTypeByName(M->getContext(), Name);
|
|
if (!OpaqueType)
|
|
OpaqueType = StructType::create(M->getContext(), Name);
|
|
return OpaqueType;
|
|
}
|
|
|
|
void getFunctionTypeParameterTypes(llvm::FunctionType *FT,
|
|
SmallVector<Type *> &ArgTys) {
|
|
for (auto I = FT->param_begin(), E = FT->param_end(); I != E; ++I) {
|
|
ArgTys.push_back(*I);
|
|
}
|
|
}
|
|
|
|
bool isVoidFuncTy(FunctionType *FT) { return FT->getReturnType()->isVoidTy(); }
|
|
|
|
bool isOCLImageType(llvm::Type *Ty, StringRef *Name) {
|
|
if (auto *TPT = dyn_cast_or_null<TypedPointerType>(Ty))
|
|
if (auto *ST = dyn_cast_or_null<StructType>(TPT->getElementType()))
|
|
if (ST->isOpaque()) {
|
|
auto FullName = ST->getName();
|
|
if (FullName.find(kSPR2TypeName::ImagePrefix) == 0) {
|
|
if (Name)
|
|
*Name = FullName.drop_front(strlen(kSPR2TypeName::OCLPrefix));
|
|
return true;
|
|
}
|
|
}
|
|
if (auto *TET = dyn_cast_or_null<TargetExtType>(Ty)) {
|
|
assert(!Name && "Cannot get the name for a target-extension type image");
|
|
return TET->getName() == "spirv.Image";
|
|
}
|
|
return false;
|
|
}
|
|
/// \param BaseTyName is the type Name as in spirv.BaseTyName.Postfixes
|
|
/// \param Postfix contains postfixes extracted from the SPIR-V image
|
|
/// type Name as spirv.BaseTyName.Postfixes.
|
|
bool isSPIRVStructType(llvm::Type *Ty, StringRef BaseTyName,
|
|
StringRef *Postfix) {
|
|
auto *ST = dyn_cast<StructType>(Ty);
|
|
if (!ST)
|
|
return false;
|
|
if (ST->isOpaque()) {
|
|
auto FullName = ST->getName();
|
|
std::string N =
|
|
std::string(kSPIRVTypeName::PrefixAndDelim) + BaseTyName.str();
|
|
if (FullName != N)
|
|
N = N + kSPIRVTypeName::Delimiter;
|
|
if (FullName.starts_with(N)) {
|
|
if (Postfix)
|
|
*Postfix = FullName.drop_front(N.size());
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isSYCLHalfType(llvm::Type *Ty) {
|
|
if (auto *ST = dyn_cast<StructType>(Ty)) {
|
|
if (!ST->hasName())
|
|
return false;
|
|
StringRef Name = ST->getName();
|
|
if (!Name.consume_front("class."))
|
|
return false;
|
|
if ((Name.starts_with("sycl::") || Name.starts_with("cl::sycl::") ||
|
|
Name.starts_with("__sycl_internal::")) &&
|
|
Name.ends_with("::half")) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isSYCLBfloat16Type(llvm::Type *Ty) {
|
|
if (auto *ST = dyn_cast<StructType>(Ty)) {
|
|
if (!ST->hasName())
|
|
return false;
|
|
StringRef Name = ST->getName();
|
|
if (!Name.consume_front("class."))
|
|
return false;
|
|
if ((Name.starts_with("sycl::") || Name.starts_with("cl::sycl::") ||
|
|
Name.starts_with("__sycl_internal::")) &&
|
|
Name.ends_with("::bfloat16")) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isLLVMCooperativeMatrixType(llvm::Type *Ty) {
|
|
if (auto *TargetTy = dyn_cast<TargetExtType>(Ty))
|
|
return TargetTy->getName() == "spirv.CooperativeMatrixKHR";
|
|
return false;
|
|
}
|
|
|
|
Function *getOrCreateFunction(Module *M, Type *RetTy, ArrayRef<Type *> ArgTypes,
|
|
StringRef Name, BuiltinFuncMangleInfo *Mangle,
|
|
AttributeList *Attrs, bool TakeName) {
|
|
std::string MangledName{Name};
|
|
bool IsVarArg = false;
|
|
if (Mangle) {
|
|
MangledName = mangleBuiltin(Name, ArgTypes, Mangle);
|
|
IsVarArg = 0 <= Mangle->getVarArg();
|
|
if (IsVarArg)
|
|
ArgTypes = ArgTypes.slice(0, Mangle->getVarArg());
|
|
}
|
|
FunctionType *FT = FunctionType::get(RetTy, ArgTypes, IsVarArg);
|
|
Function *F = M->getFunction(MangledName);
|
|
if (!TakeName && F && F->getFunctionType() != FT && Mangle != nullptr) {
|
|
std::string S;
|
|
raw_string_ostream SS(S);
|
|
SS << "Error: Attempt to redefine function: " << *F << " => " << *FT
|
|
<< '\n';
|
|
report_fatal_error(llvm::Twine(SS.str()), false);
|
|
}
|
|
if (!F || F->getFunctionType() != FT) {
|
|
auto *NewF =
|
|
Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M);
|
|
if (F && TakeName) {
|
|
NewF->takeName(F);
|
|
LLVM_DEBUG(
|
|
dbgs() << "[getOrCreateFunction] Warning: taking function Name\n");
|
|
}
|
|
if (NewF->getName() != MangledName) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "[getOrCreateFunction] Warning: function Name changed\n");
|
|
}
|
|
LLVM_DEBUG(dbgs() << "[getOrCreateFunction] ";
|
|
if (F) dbgs() << *F << " => "; dbgs() << *NewF << '\n';);
|
|
if (F)
|
|
NewF->setDSOLocal(F->isDSOLocal());
|
|
F = NewF;
|
|
F->setCallingConv(CallingConv::SPIR_FUNC);
|
|
if (Attrs)
|
|
F->setAttributes(*Attrs);
|
|
}
|
|
return F;
|
|
}
|
|
|
|
std::vector<Value *> getArguments(CallInst *CI, unsigned Start, unsigned End) {
|
|
std::vector<Value *> Args;
|
|
if (End == 0)
|
|
End = CI->arg_size();
|
|
for (; Start != End; ++Start) {
|
|
Args.push_back(CI->getArgOperand(Start));
|
|
}
|
|
return Args;
|
|
}
|
|
|
|
uint64_t getArgAsInt(CallInst *CI, unsigned I) {
|
|
return cast<ConstantInt>(CI->getArgOperand(I))->getZExtValue();
|
|
}
|
|
|
|
Scope getArgAsScope(CallInst *CI, unsigned I) {
|
|
return static_cast<Scope>(getArgAsInt(CI, I));
|
|
}
|
|
|
|
std::string prefixSPIRVName(const std::string &S) {
|
|
return std::string(kSPIRVName::Prefix) + S;
|
|
}
|
|
|
|
StringRef dePrefixSPIRVName(StringRef R, SmallVectorImpl<StringRef> &Postfix) {
|
|
const size_t Start = strlen(kSPIRVName::Prefix);
|
|
if (!R.starts_with(kSPIRVName::Prefix))
|
|
return R;
|
|
R = R.drop_front(Start);
|
|
R.split(Postfix, "_", -1, false);
|
|
auto Name = Postfix.front();
|
|
Postfix.erase(Postfix.begin());
|
|
return Name;
|
|
}
|
|
|
|
std::string getSPIRVFuncName(Op OC, StringRef PostFix) {
|
|
return prefixSPIRVName(getName(OC) + PostFix.str());
|
|
}
|
|
|
|
std::string getSPIRVFuncName(Op OC, const Type *PRetTy, bool IsSigned,
|
|
Type *PET) {
|
|
return prefixSPIRVName(getName(OC) + kSPIRVPostfix::Divider +
|
|
getPostfixForReturnType(PRetTy, IsSigned, PET));
|
|
}
|
|
|
|
std::string getSPIRVFuncName(SPIRVBuiltinVariableKind BVKind) {
|
|
return prefixSPIRVName(getName(BVKind));
|
|
}
|
|
|
|
std::string getSPIRVExtFuncName(SPIRVExtInstSetKind Set, unsigned ExtOp,
|
|
StringRef PostFix) {
|
|
std::string ExtOpName;
|
|
switch (Set) {
|
|
default:
|
|
llvm_unreachable("invalid extended instruction set");
|
|
ExtOpName = "unknown";
|
|
break;
|
|
case SPIRVEIS_OpenCL:
|
|
ExtOpName = getName(static_cast<OCLExtOpKind>(ExtOp));
|
|
break;
|
|
}
|
|
return prefixSPIRVName(SPIRVExtSetShortNameMap::map(Set) + '_' + ExtOpName +
|
|
PostFix.str());
|
|
}
|
|
|
|
SPIRVDecorate *mapPostfixToDecorate(StringRef Postfix, SPIRVEntry *Target) {
|
|
if (Postfix == kSPIRVPostfix::Sat)
|
|
return new SPIRVDecorate(spv::DecorationSaturatedConversion, Target);
|
|
|
|
if (Postfix.starts_with(kSPIRVPostfix::Rt))
|
|
return new SPIRVDecorate(spv::DecorationFPRoundingMode, Target,
|
|
map<SPIRVFPRoundingModeKind>(Postfix.str()));
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
SPIRVValue *addDecorations(SPIRVValue *Target,
|
|
const SmallVectorImpl<std::string> &Decs) {
|
|
for (auto &I : Decs)
|
|
if (auto *Dec = mapPostfixToDecorate(I, Target))
|
|
Target->addDecorate(Dec);
|
|
return Target;
|
|
}
|
|
|
|
std::string getPostfixForReturnType(CallInst *CI, bool IsSigned) {
|
|
return getPostfixForReturnType(CI->getType(), IsSigned);
|
|
}
|
|
|
|
std::string getPostfixForReturnType(const Type *PRetTy, bool IsSigned,
|
|
Type *PET) {
|
|
return std::string(kSPIRVPostfix::Return) +
|
|
mapLLVMTypeToOCLType(PRetTy, IsSigned, PET);
|
|
}
|
|
|
|
// Enqueue kernel, kernel query, pipe and address space cast built-ins
|
|
// are not mangled.
|
|
bool isNonMangledOCLBuiltin(StringRef Name) {
|
|
if (!Name.starts_with("__"))
|
|
return false;
|
|
|
|
return isEnqueueKernelBI(Name) || isKernelQueryBI(Name) ||
|
|
isPipeOrAddressSpaceCastBI(Name.drop_front(2));
|
|
}
|
|
|
|
Op getSPIRVFuncOC(StringRef S, SmallVectorImpl<std::string> *Dec) {
|
|
Op OC;
|
|
SmallVector<StringRef, 2> Postfix;
|
|
StringRef Name;
|
|
if (!oclIsBuiltin(S, Name))
|
|
Name = S;
|
|
StringRef R(Name);
|
|
if ((!Name.starts_with(kSPIRVName::Prefix) && !isNonMangledOCLBuiltin(S)) ||
|
|
!getByName(dePrefixSPIRVName(R, Postfix).str(), OC)) {
|
|
return OpNop;
|
|
}
|
|
if (Dec)
|
|
for (auto &I : Postfix)
|
|
Dec->push_back(I.str());
|
|
return OC;
|
|
}
|
|
|
|
bool getSPIRVBuiltin(const std::string &OrigName, spv::BuiltIn &B) {
|
|
SmallVector<StringRef, 2> Postfix;
|
|
StringRef R(OrigName);
|
|
R = dePrefixSPIRVName(R, Postfix);
|
|
if (!Postfix.empty())
|
|
return false;
|
|
return getByName(R.str(), B);
|
|
}
|
|
|
|
// DemangledName is a substring of Name. The DemangledName is updated only
|
|
// if true is returned
|
|
bool oclIsBuiltin(StringRef Name, StringRef &DemangledName, bool IsCpp) {
|
|
if (Name == "printf") {
|
|
DemangledName = "__spirv_ocl_printf";
|
|
return true;
|
|
}
|
|
if (isNonMangledOCLBuiltin(Name)) {
|
|
DemangledName = Name.drop_front(2);
|
|
return true;
|
|
}
|
|
if (!Name.starts_with("_Z"))
|
|
return false;
|
|
// OpenCL C++ built-ins are declared in cl namespace.
|
|
// TODO: consider using 'St' abbriviation for cl namespace mangling.
|
|
// Similar to ::std:: in C++.
|
|
if (IsCpp) {
|
|
if (!Name.starts_with("_ZN"))
|
|
return false;
|
|
// Skip CV and ref qualifiers.
|
|
size_t NameSpaceStart = Name.find_first_not_of("rVKRO", 3);
|
|
// All built-ins are in the ::cl:: namespace.
|
|
if (Name.substr(NameSpaceStart, 11) != "2cl7__spirv")
|
|
return false;
|
|
size_t DemangledNameLenStart = NameSpaceStart + 11;
|
|
size_t Start = Name.find_first_not_of("0123456789", DemangledNameLenStart);
|
|
size_t Len = 0;
|
|
if (!Name.substr(DemangledNameLenStart, Start - DemangledNameLenStart)
|
|
.getAsInteger(10, Len)) {
|
|
DemangledName = Name.substr(Start, Len);
|
|
return true;
|
|
}
|
|
SPIRVDBG(errs() << "Error in extracting integer value");
|
|
return false;
|
|
}
|
|
size_t Start = Name.find_first_not_of("0123456789", 2);
|
|
size_t Len = 0;
|
|
if (!Name.substr(2, Start - 2).getAsInteger(10, Len)) {
|
|
DemangledName = Name.substr(Start, Len);
|
|
return true;
|
|
}
|
|
SPIRVDBG(errs() << "Error in extracting integer value");
|
|
return false;
|
|
}
|
|
|
|
// DemangledName is a substring of Name. The DemangledName is updated only
|
|
// if true is returned.
|
|
bool isInternalSPIRVBuiltin(StringRef Name, StringRef &DemangledName) {
|
|
if (!Name.starts_with("_Z"))
|
|
return false;
|
|
constexpr unsigned DemangledNameLenStart = 2;
|
|
size_t Start = Name.find_first_not_of("0123456789", DemangledNameLenStart);
|
|
if (!Name.substr(Start, Name.size() - 1)
|
|
.starts_with(kSPIRVName::InternalBuiltinPrefix))
|
|
return false;
|
|
DemangledName = llvm::itaniumDemangle(Name.data(), false);
|
|
DemangledName.consume_front(kSPIRVName::InternalBuiltinPrefix);
|
|
return true;
|
|
}
|
|
|
|
// Check if a mangled type Name is unsigned
|
|
bool isMangledTypeUnsigned(char Mangled) {
|
|
return Mangled == 'h' /* uchar */
|
|
|| Mangled == 't' /* ushort */
|
|
|| Mangled == 'j' /* uint */
|
|
|| Mangled == 'm' /* ulong */;
|
|
}
|
|
|
|
// Check if a mangled type Name is signed
|
|
bool isMangledTypeSigned(char Mangled) {
|
|
return Mangled == 'c' /* char */
|
|
|| Mangled == 'a' /* signed char */
|
|
|| Mangled == 's' /* short */
|
|
|| Mangled == 'i' /* int */
|
|
|| Mangled == 'l' /* long */;
|
|
}
|
|
|
|
// Check if a mangled type Name is floating point (excludes half)
|
|
bool isMangledTypeFP(char Mangled) {
|
|
return Mangled == 'f' /* float */
|
|
|| Mangled == 'd'; /* double */
|
|
}
|
|
|
|
// Check if a mangled type Name is half
|
|
bool isMangledTypeHalf(std::string Mangled) {
|
|
return Mangled == "Dh"; /* half */
|
|
}
|
|
|
|
void eraseSubstitutionFromMangledName(std::string &MangledName) {
|
|
auto Len = MangledName.length();
|
|
while (Len >= 2 && MangledName.substr(Len - 2, 2) == "S_") {
|
|
Len -= 2;
|
|
MangledName.erase(Len, 2);
|
|
}
|
|
}
|
|
|
|
ParamType lastFuncParamType(StringRef MangledName) {
|
|
std::string Copy(MangledName);
|
|
eraseSubstitutionFromMangledName(Copy);
|
|
char Mangled = Copy.back();
|
|
std::string Mangled2 = Copy.substr(Copy.size() - 2);
|
|
|
|
std::string Mangled5 = Copy.substr(Copy.size() - 5);
|
|
if (Mangled5 == "DF16b") {
|
|
return ParamType::FLOAT;
|
|
}
|
|
|
|
if (isMangledTypeFP(Mangled) || isMangledTypeHalf(Mangled2)) {
|
|
return ParamType::FLOAT;
|
|
} else if (isMangledTypeUnsigned(Mangled)) {
|
|
return ParamType::UNSIGNED;
|
|
} else if (isMangledTypeSigned(Mangled)) {
|
|
return ParamType::SIGNED;
|
|
}
|
|
|
|
return ParamType::UNKNOWN;
|
|
}
|
|
|
|
// Check if the last argument is signed
|
|
bool isLastFuncParamSigned(StringRef MangledName) {
|
|
return lastFuncParamType(MangledName) == ParamType::SIGNED;
|
|
}
|
|
|
|
// Check if a mangled function Name contains unsigned atomic type
|
|
bool containsUnsignedAtomicType(StringRef Name) {
|
|
auto Loc = Name.find(kMangledName::AtomicPrefixIncoming);
|
|
if (Loc == StringRef::npos)
|
|
return false;
|
|
return isMangledTypeUnsigned(
|
|
Name[Loc + strlen(kMangledName::AtomicPrefixIncoming)]);
|
|
}
|
|
|
|
bool hasArrayArg(Function *F) {
|
|
for (auto I = F->arg_begin(), E = F->arg_end(); I != E; ++I) {
|
|
LLVM_DEBUG(dbgs() << "[hasArrayArg] " << *I << '\n');
|
|
if (I->getType()->isArrayTy()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Convert a struct name from the name given to it in Itanium name mangling to
|
|
/// the name given to it as an LLVM opaque struct.
|
|
static std::string demangleBuiltinOpenCLTypeName(StringRef MangledStructName) {
|
|
assert(MangledStructName.starts_with("ocl_") &&
|
|
"Not a valid builtin OpenCL mangled name");
|
|
// Bare structure type that starts with ocl_ is a builtin opencl type.
|
|
// See clang/lib/CodeGen/CGOpenCLRuntime for how these map to LLVM types
|
|
// and clang/lib/AST/ItaniumMangle for how they are mangled.
|
|
// In general, ocl_<foo> is mapped to pointer-to-%opencl.<foo>, but
|
|
// there is some variance around whether or not _t is included in the
|
|
// mangled name.
|
|
std::string LlvmStructName = StringSwitch<StringRef>(MangledStructName)
|
|
.Case("ocl_sampler", "opencl.sampler_t")
|
|
.Case("ocl_event", "opencl.event_t")
|
|
.Case("ocl_clkevent", "opencl.clk_event_t")
|
|
.Case("ocl_queue", "opencl.queue_t")
|
|
.Case("ocl_reserveid", "opencl.reserve_id_t")
|
|
.Default("")
|
|
.str();
|
|
if (LlvmStructName.empty()) {
|
|
LlvmStructName = "opencl.";
|
|
LlvmStructName += MangledStructName.substr(4); // Strip off ocl_
|
|
if (!MangledStructName.ends_with("_t"))
|
|
LlvmStructName += "_t";
|
|
}
|
|
return LlvmStructName;
|
|
}
|
|
|
|
/// Convert a C/C++ type name into an LLVM type, if it's a basic integer or
|
|
/// floating point type.
|
|
static Type *parsePrimitiveType(LLVMContext &Ctx, StringRef Name) {
|
|
return StringSwitch<Type *>(Name)
|
|
.Cases("char", "signed char", "unsigned char", Type::getInt8Ty(Ctx))
|
|
.Cases("short", "unsigned short", Type::getInt16Ty(Ctx))
|
|
.Cases("int", "unsigned int", Type::getInt32Ty(Ctx))
|
|
.Cases("long", "unsigned long", Type::getInt64Ty(Ctx))
|
|
.Cases("long long", "unsigned long long", Type::getInt64Ty(Ctx))
|
|
.Case("half", Type::getHalfTy(Ctx))
|
|
.Case("std::bfloat16_t", Type::getBFloatTy(Ctx))
|
|
.Case("float", Type::getFloatTy(Ctx))
|
|
.Case("double", Type::getDoubleTy(Ctx))
|
|
.Case("void", Type::getInt8Ty(Ctx))
|
|
.Default(nullptr);
|
|
}
|
|
|
|
} // namespace SPIRV
|
|
|
|
namespace {
|
|
|
|
// Return the value for when the dimension index of a builtin is out of range.
|
|
uint64_t getBuiltinOutOfRangeValue(StringRef VarName) {
|
|
assert(VarName.starts_with("__spirv_BuiltIn"));
|
|
return StringSwitch<uint64_t>(VarName)
|
|
.EndsWith("GlobalSize", 1)
|
|
.EndsWith("NumWorkgroups", 1)
|
|
.EndsWith("WorkgroupSize", 1)
|
|
.EndsWith("EnqueuedWorkgroupSize", 1)
|
|
.Default(0);
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
// The demangler node hierarchy doesn't use LLVM's RTTI helper functions (as it
|
|
// also needs to live in libcxxabi). By specializing this implementation here,
|
|
// we can add support for these functions.
|
|
#define NODE(X) \
|
|
template <typename From> struct llvm::isa_impl<itanium_demangle::X, From> { \
|
|
static inline bool doit(const From &Val) { \
|
|
return Val.getKind() == itanium_demangle::Node::K##X; \
|
|
} \
|
|
};
|
|
#include "llvm/Demangle/ItaniumNodes.def"
|
|
|
|
namespace SPIRV {
|
|
|
|
namespace {
|
|
// An allocator to use with the demangler API.
|
|
class DefaultAllocator {
|
|
BumpPtrAllocator Alloc;
|
|
|
|
public:
|
|
void reset() { Alloc.Reset(); }
|
|
|
|
template <typename T, typename... Args> T *makeNode(Args &&...ArgList) {
|
|
return new (Alloc.Allocate(sizeof(T), alignof(T)))
|
|
T(std::forward<Args>(ArgList)...);
|
|
}
|
|
|
|
void *allocateNodeArray(size_t Sz) {
|
|
using namespace llvm::itanium_demangle;
|
|
return Alloc.Allocate(sizeof(Node *) * Sz, alignof(Node *));
|
|
}
|
|
};
|
|
} // unnamed namespace
|
|
|
|
static StringRef stringify(const itanium_demangle::NameType *Node) {
|
|
return Node->getName();
|
|
}
|
|
|
|
/// Convert a mangled name that represents a basic integer, floating-point,
|
|
/// etc. type into the corresponding LLVM type.
|
|
static Type *getPrimitiveType(LLVMContext &Ctx,
|
|
const llvm::itanium_demangle::Node *N) {
|
|
using namespace llvm::itanium_demangle;
|
|
if (auto *Name = dyn_cast<NameType>(N)) {
|
|
return parsePrimitiveType(Ctx, stringify(Name));
|
|
}
|
|
if (auto *BitInt = dyn_cast<BitIntType>(N)) {
|
|
unsigned BitWidth = 0;
|
|
BitInt->match([&](const Node *NodeSize, bool) {
|
|
const StringRef SizeStr(stringify(cast<NameType>(NodeSize)));
|
|
SizeStr.getAsInteger(10, BitWidth);
|
|
});
|
|
return Type::getIntNTy(Ctx, BitWidth);
|
|
}
|
|
if (auto *FP = dyn_cast<BinaryFPType>(N)) {
|
|
StringRef SizeStr;
|
|
FP->match([&](const Node *NodeDimension) {
|
|
SizeStr = stringify(cast<NameType>(NodeDimension));
|
|
});
|
|
return StringSwitch<Type *>(SizeStr)
|
|
.Case("16", Type::getHalfTy(Ctx))
|
|
.Case("32", Type::getFloatTy(Ctx))
|
|
.Case("64", Type::getDoubleTy(Ctx))
|
|
.Case("128", Type::getFP128Ty(Ctx))
|
|
.Default(nullptr);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename FnType>
|
|
static TypedPointerType *
|
|
parseNode(Module *M, const llvm::itanium_demangle::Node *ParamType,
|
|
FnType GetStructType) {
|
|
using namespace llvm::itanium_demangle;
|
|
Type *PointeeTy = nullptr;
|
|
unsigned AS = 0;
|
|
|
|
if (auto *Name = dyn_cast<NameType>(ParamType)) {
|
|
// This corresponds to a simple class name. Since we only care about
|
|
// pointer element types, the only relevant names are those corresponding
|
|
// to the OpenCL special types (which all begin with "ocl_").
|
|
StringRef Arg(stringify(Name));
|
|
if (Arg.starts_with("ocl_")) {
|
|
const std::string StructName = demangleBuiltinOpenCLTypeName(Arg);
|
|
PointeeTy = GetStructType(StructName);
|
|
} else if (Arg.consume_front("__spirv_")) {
|
|
// This is a pointer to a SPIR-V OpType* opaque struct. In general,
|
|
// convert __spirv_<Type>[__Suffix] to %spirv.Type[._Suffix]
|
|
auto NameSuffixPair = Arg.split('_');
|
|
std::string StructName = "spirv.";
|
|
StructName += NameSuffixPair.first;
|
|
if (!NameSuffixPair.second.empty()) {
|
|
StructName += ".";
|
|
StructName += NameSuffixPair.second;
|
|
}
|
|
PointeeTy = GetStructType(StructName);
|
|
} else if (Arg == "ndrange_t") {
|
|
PointeeTy = GetStructType(Arg);
|
|
}
|
|
} else if (auto *P = dyn_cast<itanium_demangle::PointerType>(ParamType)) {
|
|
const Node *Pointee = P->getPointee();
|
|
|
|
// Strip through all of the qualifiers on the pointee type.
|
|
while (true) {
|
|
if (auto *VendorTy = dyn_cast<VendorExtQualType>(Pointee)) {
|
|
Pointee = VendorTy->getTy();
|
|
StringRef Qualifier(&*VendorTy->getExt().begin(),
|
|
VendorTy->getExt().size());
|
|
if (Qualifier.consume_front("AS")) {
|
|
Qualifier.getAsInteger(10, AS);
|
|
}
|
|
} else if (auto *Qual = dyn_cast<QualType>(Pointee)) {
|
|
Pointee = Qual->getChild();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (auto *Name = dyn_cast<NameType>(Pointee)) {
|
|
StringRef MangledStructName(stringify(Name));
|
|
if (MangledStructName.consume_front("__spirv_")) {
|
|
// This is a pointer to a SPIR-V OpType* opaque struct. In general,
|
|
// convert __spirv_<Type>[__Suffix] to %spirv.Type[._Suffix]
|
|
auto NameSuffixPair = MangledStructName.split('_');
|
|
std::string StructName = "spirv.";
|
|
StructName += NameSuffixPair.first;
|
|
if (!NameSuffixPair.second.empty()) {
|
|
StructName += ".";
|
|
StructName += NameSuffixPair.second;
|
|
}
|
|
PointeeTy = GetStructType(StructName);
|
|
} else if (MangledStructName.starts_with("opencl.")) {
|
|
PointeeTy = GetStructType(MangledStructName);
|
|
} else if (MangledStructName.starts_with("ocl_")) {
|
|
const std::string StructName =
|
|
demangleBuiltinOpenCLTypeName(MangledStructName);
|
|
PointeeTy = TypedPointerType::get(GetStructType(StructName), 0);
|
|
} else {
|
|
PointeeTy = parsePrimitiveType(M->getContext(), MangledStructName);
|
|
}
|
|
} else if (auto *Ty = getPrimitiveType(M->getContext(), Pointee)) {
|
|
PointeeTy = Ty;
|
|
} else if (auto *Vec = dyn_cast<itanium_demangle::VectorType>(Pointee)) {
|
|
unsigned ElemCount = 0;
|
|
const StringRef ElemCountStr(
|
|
stringify(cast<NameType>(Vec->getDimension())));
|
|
ElemCountStr.getAsInteger(10, ElemCount);
|
|
if (auto *Ty = getPrimitiveType(M->getContext(), Vec->getBaseType())) {
|
|
PointeeTy = llvm::VectorType::get(Ty, ElemCount, false);
|
|
}
|
|
} else if (llvm::isa<itanium_demangle::PointerType>(Pointee)) {
|
|
PointeeTy = parseNode(M, Pointee, GetStructType);
|
|
} else {
|
|
// Other possible pointee types do not correspond to any of the special
|
|
// struct types were are looking for here.
|
|
}
|
|
} else if (auto *VendorTy = dyn_cast<VendorExtQualType>(ParamType)) {
|
|
if (auto *NameTy = dyn_cast<NameType>(VendorTy->getTy())) {
|
|
if (NameTy->getName() == "std::bfloat16_t")
|
|
PointeeTy = llvm::Type::getBFloatTy(M->getContext());
|
|
}
|
|
// This is a block parameter. Decode the pointee type as if it were a
|
|
// void (*)(void) function pointer type.
|
|
if (VendorTy->getExt() == "block_pointer") {
|
|
PointeeTy =
|
|
llvm::FunctionType::get(Type::getVoidTy(M->getContext()), false);
|
|
}
|
|
} else {
|
|
// Other parameter types are not likely to be pointer types, so we can
|
|
// ignore these.
|
|
}
|
|
return PointeeTy ? TypedPointerType::get(PointeeTy, AS) : nullptr;
|
|
}
|
|
|
|
bool getParameterTypes(Function *F, SmallVectorImpl<Type *> &ArgTys,
|
|
std::function<std::string(StringRef)> NameMapFn) {
|
|
using namespace llvm::itanium_demangle;
|
|
// If there's no mangled name, we can't do anything. Also, if there's no
|
|
// parameters, do nothing.
|
|
StringRef Name = F->getName();
|
|
if (!Name.starts_with("_Z") || F->arg_empty())
|
|
return Name.starts_with("_Z");
|
|
|
|
Module *M = F->getParent();
|
|
auto GetStructType = [&](StringRef Name) {
|
|
return getOrCreateOpaqueStructType(M, NameMapFn ? NameMapFn(Name) : Name);
|
|
};
|
|
|
|
// Start by filling in a skeleton of information we can get from the LLVM type
|
|
// itself.
|
|
ArgTys.clear();
|
|
auto *FT = F->getFunctionType();
|
|
ArgTys.reserve(FT->getNumParams());
|
|
bool HasSret = false;
|
|
for (Argument &Arg : F->args()) {
|
|
if (!Arg.getType()->isPointerTy())
|
|
ArgTys.push_back(Arg.getType());
|
|
else if (Type *Ty = Arg.getParamStructRetType()) {
|
|
assert(!HasSret && &Arg == F->getArg(0) &&
|
|
"sret parameter should only appear on the first argument");
|
|
HasSret = true;
|
|
unsigned AS = Arg.getType()->getPointerAddressSpace();
|
|
if (auto *STy = dyn_cast<StructType>(Ty))
|
|
if (STy->hasName())
|
|
ArgTys.push_back(
|
|
TypedPointerType::get(GetStructType(STy->getName()), AS));
|
|
else
|
|
ArgTys.push_back(TypedPointerType::get(STy, AS));
|
|
else
|
|
ArgTys.push_back(TypedPointerType::get(Ty, AS));
|
|
} else {
|
|
ArgTys.push_back(Arg.getType());
|
|
}
|
|
}
|
|
|
|
// Skip the first argument if it's an sret parameter--this would be an
|
|
// implicit parameter not recognized as part of the function parameters.
|
|
auto *ArgIter = ArgTys.begin();
|
|
if (HasSret)
|
|
++ArgIter;
|
|
|
|
// Demangle the function arguments. If we get an input name of
|
|
// "_Z12write_imagei20ocl_image1d_array_woDv2_iiDv4_i", then we expect
|
|
// that Demangler.getFunctionParameters will return
|
|
// "(ocl_image1d_array_wo, int __vector(2), int, int __vector(4))" (in other
|
|
// words, the stuff between the parentheses if you ran C++ filt, including
|
|
// the parentheses itself).
|
|
const StringRef MangledName(F->getName());
|
|
ManglingParser<DefaultAllocator> Demangler(MangledName.begin(),
|
|
MangledName.end());
|
|
// We expect to see only function name encodings here. If it's not a function
|
|
// name encoding, bail out.
|
|
auto *RootNode = dyn_cast_or_null<FunctionEncoding>(Demangler.parse());
|
|
if (!RootNode)
|
|
return false;
|
|
|
|
// Get the parameter list. If the function is a vararg function, drop the last
|
|
// parameter.
|
|
NodeArray Params = RootNode->getParams();
|
|
if (F->isVarArg()) {
|
|
bool HasVarArgParam = false;
|
|
if (!Params.empty()) {
|
|
if (auto *Name = dyn_cast<NameType>(Params[Params.size() - 1])) {
|
|
if (stringify(Name) == "...")
|
|
HasVarArgParam = true;
|
|
}
|
|
}
|
|
if (HasVarArgParam) {
|
|
Params = NodeArray(Params.begin(), Params.size() - 1);
|
|
} else {
|
|
LLVM_DEBUG(dbgs() << "[getParameterTypes] function " << MangledName
|
|
<< " was expected to have a varargs parameter\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Sanity check that the name mangling matches up to the expected number of
|
|
// arguments.
|
|
if (Params.size() != (size_t)(ArgTys.end() - ArgIter)) {
|
|
LLVM_DEBUG(dbgs() << "[getParameterTypes] function " << MangledName
|
|
<< " appears to have " << Params.size()
|
|
<< " arguments but has " << (ArgTys.end() - ArgIter)
|
|
<< "\n");
|
|
return false;
|
|
}
|
|
|
|
// Overwrite the types of pointer-typed arguments with information from
|
|
// demangling.
|
|
bool DemangledSuccessfully = true;
|
|
for (auto *ParamType : Params) {
|
|
Type *ArgTy = *ArgIter;
|
|
Type *DemangledTy = parseNode(M, ParamType, GetStructType);
|
|
if (ArgTy->isPointerTy() && DemangledTy == nullptr) {
|
|
DemangledTy = TypedPointerType::get(Type::getInt8Ty(ArgTy->getContext()),
|
|
ArgTy->getPointerAddressSpace());
|
|
LLVM_DEBUG(dbgs() << "Failed to recover type of argument " << *ArgTy
|
|
<< " of function " << F->getName() << "\n");
|
|
DemangledSuccessfully = false;
|
|
} else if (ArgTy->isTargetExtTy() || !DemangledTy)
|
|
DemangledTy = ArgTy;
|
|
if (auto *TPT = dyn_cast<TypedPointerType>(DemangledTy))
|
|
if (ArgTy->isPointerTy() &&
|
|
TPT->getAddressSpace() != ArgTy->getPointerAddressSpace())
|
|
DemangledTy = TypedPointerType::get(TPT->getElementType(),
|
|
ArgTy->getPointerAddressSpace());
|
|
*ArgIter++ = DemangledTy;
|
|
}
|
|
return DemangledSuccessfully;
|
|
}
|
|
|
|
bool getRetParamSignedness(Function *F, ParamSignedness &RetSignedness,
|
|
SmallVectorImpl<ParamSignedness> &ArgSignedness) {
|
|
using namespace llvm::itanium_demangle;
|
|
StringRef Name = F->getName();
|
|
if (!Name.starts_with("_Z") || F->arg_empty())
|
|
return false;
|
|
|
|
ManglingParser<DefaultAllocator> Demangler(Name.begin(), Name.end());
|
|
// If it's not a function name encoding, bail out.
|
|
auto *RootNode = dyn_cast_or_null<FunctionEncoding>(Demangler.parse());
|
|
if (!RootNode)
|
|
return false;
|
|
|
|
auto GetSignedness = [](const itanium_demangle::Node *N) {
|
|
if (!N)
|
|
return ParamSignedness::Unknown;
|
|
if (const auto *Vec = dyn_cast<itanium_demangle::VectorType>(N))
|
|
N = Vec->getBaseType();
|
|
if (const auto *Name = dyn_cast<NameType>(N)) {
|
|
StringRef Arg(stringify(Name));
|
|
if (Arg.starts_with("unsigned"))
|
|
return ParamSignedness::Unsigned;
|
|
if (Arg == "char" || Arg == "short" || Arg == "int" || Arg == "long")
|
|
return ParamSignedness::Signed;
|
|
}
|
|
return ParamSignedness::Unknown;
|
|
};
|
|
RetSignedness = GetSignedness(RootNode->getReturnType());
|
|
ArgSignedness.resize(F->arg_size());
|
|
for (const auto &[I, ParamType] : llvm::enumerate(RootNode->getParams())) {
|
|
if (F->getArg(I)->getType()->isIntOrIntVectorTy())
|
|
ArgSignedness[I] = GetSignedness(ParamType);
|
|
else
|
|
ArgSignedness[I] = ParamSignedness::Unknown;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CallInst *mutateCallInst(
|
|
Module *M, CallInst *CI,
|
|
std::function<std::string(CallInst *, std::vector<Value *> &)> ArgMutate,
|
|
BuiltinFuncMangleInfo *Mangle, AttributeList *Attrs, bool TakeFuncName) {
|
|
LLVM_DEBUG(dbgs() << "[mutateCallInst] " << *CI);
|
|
|
|
auto Args = getArguments(CI);
|
|
auto NewName = ArgMutate(CI, Args);
|
|
std::string InstName;
|
|
if (!CI->getType()->isVoidTy() && CI->hasName()) {
|
|
InstName = CI->getName().str();
|
|
CI->setName(InstName + ".old");
|
|
}
|
|
auto *NewCI = addCallInst(M, NewName, CI->getType(), Args, Attrs, CI, Mangle,
|
|
InstName, TakeFuncName);
|
|
NewCI->setDebugLoc(CI->getDebugLoc());
|
|
LLVM_DEBUG(dbgs() << " => " << *NewCI << '\n');
|
|
CI->replaceAllUsesWith(NewCI);
|
|
CI->eraseFromParent();
|
|
return NewCI;
|
|
}
|
|
|
|
Instruction *mutateCallInst(
|
|
Module *M, CallInst *CI,
|
|
std::function<std::string(CallInst *, std::vector<Value *> &, Type *&RetTy)>
|
|
ArgMutate,
|
|
std::function<Instruction *(CallInst *)> RetMutate,
|
|
BuiltinFuncMangleInfo *Mangle, AttributeList *Attrs, bool TakeFuncName) {
|
|
LLVM_DEBUG(dbgs() << "[mutateCallInst] " << *CI);
|
|
|
|
auto Args = getArguments(CI);
|
|
Type *RetTy = CI->getType();
|
|
auto NewName = ArgMutate(CI, Args, RetTy);
|
|
StringRef InstName = CI->getName();
|
|
auto *NewCI = addCallInst(M, NewName, RetTy, Args, Attrs, CI, Mangle,
|
|
InstName, TakeFuncName);
|
|
auto *NewI = RetMutate(NewCI);
|
|
NewI->takeName(CI);
|
|
NewI->setDebugLoc(CI->getDebugLoc());
|
|
LLVM_DEBUG(dbgs() << " => " << *NewI << '\n');
|
|
if (!CI->getType()->isVoidTy())
|
|
CI->replaceAllUsesWith(NewI);
|
|
CI->eraseFromParent();
|
|
return NewI;
|
|
}
|
|
|
|
void mutateFunction(
|
|
Function *F,
|
|
std::function<std::string(CallInst *, std::vector<Value *> &)> ArgMutate,
|
|
BuiltinFuncMangleInfo *Mangle, AttributeList *Attrs, bool TakeFuncName) {
|
|
auto *M = F->getParent();
|
|
for (auto I = F->user_begin(), E = F->user_end(); I != E;) {
|
|
if (auto *CI = dyn_cast<CallInst>(*I++))
|
|
mutateCallInst(M, CI, ArgMutate, Mangle, Attrs, TakeFuncName);
|
|
}
|
|
if (F->use_empty())
|
|
F->eraseFromParent();
|
|
}
|
|
|
|
void mutateFunction(
|
|
Function *F,
|
|
std::function<std::string(CallInst *, std::vector<Value *> &, Type *&RetTy)>
|
|
ArgMutate,
|
|
std::function<Instruction *(CallInst *)> RetMutate,
|
|
BuiltinFuncMangleInfo *Mangle, AttributeList *Attrs, bool TakeName) {
|
|
auto *M = F->getParent();
|
|
for (auto I = F->user_begin(), E = F->user_end(); I != E;) {
|
|
if (auto *CI = dyn_cast<CallInst>(*I++))
|
|
mutateCallInst(M, CI, ArgMutate, RetMutate, Mangle, Attrs, TakeName);
|
|
}
|
|
if (F->use_empty())
|
|
F->eraseFromParent();
|
|
}
|
|
|
|
CallInst *addCallInst(Module *M, StringRef FuncName, Type *RetTy,
|
|
ArrayRef<Value *> Args, AttributeList *Attrs,
|
|
Instruction *Pos, BuiltinFuncMangleInfo *Mangle,
|
|
StringRef InstName, bool TakeFuncName) {
|
|
|
|
auto *F = getOrCreateFunction(M, RetTy, getTypes(Args), FuncName, Mangle,
|
|
Attrs, TakeFuncName);
|
|
InsertPosition InsertPos(nullptr);
|
|
if (Pos)
|
|
InsertPos = Pos->getIterator();
|
|
// Cannot assign a Name to void typed values
|
|
auto *CI =
|
|
CallInst::Create(F, Args, RetTy->isVoidTy() ? "" : InstName, InsertPos);
|
|
CI->setCallingConv(F->getCallingConv());
|
|
CI->setAttributes(F->getAttributes());
|
|
return CI;
|
|
}
|
|
|
|
CallInst *addCallInstSPIRV(Module *M, StringRef FuncName, Type *RetTy,
|
|
ArrayRef<Value *> Args, AttributeList *Attrs,
|
|
ArrayRef<Type *> PointerElementTypes,
|
|
Instruction *Pos, StringRef InstName) {
|
|
BuiltinFuncMangleInfo BtnInfo;
|
|
for (unsigned I = 0; I < PointerElementTypes.size(); I++) {
|
|
if (Args[I]->getType()->isPointerTy())
|
|
BtnInfo.getTypeMangleInfo(I).PointerTy = TypedPointerType::get(
|
|
PointerElementTypes[I], Args[I]->getType()->getPointerAddressSpace());
|
|
}
|
|
return addCallInst(M, FuncName, RetTy, Args, Attrs, Pos, &BtnInfo, InstName);
|
|
}
|
|
|
|
bool isValidVectorSize(unsigned I) {
|
|
return I == 2 || I == 3 || I == 4 || I == 8 || I == 16;
|
|
}
|
|
|
|
Value *addVector(Instruction *InsPos, ValueVecRange Range) {
|
|
size_t VecSize = Range.second - Range.first;
|
|
if (VecSize == 1)
|
|
return *Range.first;
|
|
assert(isValidVectorSize(VecSize) && "Invalid vector size");
|
|
IRBuilder<> Builder(InsPos);
|
|
auto *Vec = Builder.CreateVectorSplat(VecSize, *Range.first);
|
|
unsigned Index = 1;
|
|
for (++Range.first; Range.first != Range.second; ++Range.first, ++Index)
|
|
Vec = Builder.CreateInsertElement(
|
|
Vec, *Range.first,
|
|
ConstantInt::get(Type::getInt32Ty(InsPos->getContext()), Index, false));
|
|
return Vec;
|
|
}
|
|
|
|
void makeVector(Instruction *InsPos, std::vector<Value *> &Ops,
|
|
ValueVecRange Range) {
|
|
auto *Vec = addVector(InsPos, Range);
|
|
Ops.erase(Range.first, Range.second);
|
|
Ops.push_back(Vec);
|
|
}
|
|
|
|
PointerType *getInt8PtrTy(PointerType *T) {
|
|
return PointerType::get(T->getContext(), T->getAddressSpace());
|
|
}
|
|
|
|
Value *castToInt8Ptr(Value *V, BasicBlock::iterator Pos) {
|
|
return CastInst::CreatePointerCast(
|
|
V, getInt8PtrTy(cast<PointerType>(V->getType())), "", Pos);
|
|
}
|
|
|
|
IntegerType *getSizetType(Module *M) {
|
|
return IntegerType::getIntNTy(M->getContext(),
|
|
M->getDataLayout().getPointerSizeInBits(0));
|
|
}
|
|
|
|
ConstantInt *getInt64(Module *M, int64_t Value) {
|
|
return ConstantInt::getSigned(Type::getInt64Ty(M->getContext()), Value);
|
|
}
|
|
|
|
ConstantInt *getUInt64(Module *M, uint64_t Value) {
|
|
return ConstantInt::get(Type::getInt64Ty(M->getContext()), Value, false);
|
|
}
|
|
|
|
Constant *getFloat32(Module *M, float Value) {
|
|
return ConstantFP::get(Type::getFloatTy(M->getContext()), Value);
|
|
}
|
|
|
|
ConstantInt *getInt32(Module *M, int Value) {
|
|
return ConstantInt::get(Type::getInt32Ty(M->getContext()), Value, true);
|
|
}
|
|
|
|
ConstantInt *getUInt32(Module *M, unsigned Value) {
|
|
return ConstantInt::get(Type::getInt32Ty(M->getContext()), Value, false);
|
|
}
|
|
|
|
ConstantInt *getInt(Module *M, int64_t Value) {
|
|
return Value >> 32 ? getInt64(M, Value)
|
|
: getInt32(M, static_cast<int32_t>(Value));
|
|
}
|
|
|
|
ConstantInt *getUInt(Module *M, uint64_t Value) {
|
|
return Value >> 32 ? getUInt64(M, Value)
|
|
: getUInt32(M, static_cast<uint32_t>(Value));
|
|
}
|
|
|
|
ConstantInt *getUInt16(Module *M, unsigned short Value) {
|
|
return ConstantInt::get(Type::getInt16Ty(M->getContext()), Value, false);
|
|
}
|
|
|
|
std::vector<Value *> getInt32(Module *M, const std::vector<int> &Values) {
|
|
std::vector<Value *> V;
|
|
for (auto &I : Values)
|
|
V.push_back(getInt32(M, I));
|
|
return V;
|
|
}
|
|
|
|
ConstantInt *getSizet(Module *M, uint64_t Value) {
|
|
return ConstantInt::get(getSizetType(M), Value, false);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Functions for getting metadata
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
int64_t getMDOperandAsInt(MDNode *N, unsigned I) {
|
|
return mdconst::dyn_extract<ConstantInt>(N->getOperand(I))->getZExtValue();
|
|
}
|
|
|
|
// Additional helper function to be reused by getMDOperandAs* helpers
|
|
Metadata *getMDOperandOrNull(MDNode *N, unsigned I) {
|
|
if (!N)
|
|
return nullptr;
|
|
return N->getOperand(I);
|
|
}
|
|
|
|
StringRef getMDOperandAsString(MDNode *N, unsigned I) {
|
|
if (auto *Str = dyn_cast_or_null<MDString>(getMDOperandOrNull(N, I)))
|
|
return Str->getString();
|
|
return "";
|
|
}
|
|
|
|
MDNode *getMDOperandAsMDNode(MDNode *N, unsigned I) {
|
|
return dyn_cast_or_null<MDNode>(getMDOperandOrNull(N, I));
|
|
}
|
|
|
|
Type *getMDOperandAsType(MDNode *N, unsigned I) {
|
|
return cast<ValueAsMetadata>(N->getOperand(I))->getType();
|
|
}
|
|
|
|
std::set<std::string> getNamedMDAsStringSet(Module *M,
|
|
const std::string &MDName) {
|
|
NamedMDNode *NamedMD = M->getNamedMetadata(MDName);
|
|
std::set<std::string> StrSet;
|
|
if (!NamedMD)
|
|
return StrSet;
|
|
|
|
assert(NamedMD->getNumOperands() > 0 && "Invalid SPIR");
|
|
|
|
for (unsigned I = 0, E = NamedMD->getNumOperands(); I != E; ++I) {
|
|
MDNode *MD = NamedMD->getOperand(I);
|
|
if (!MD || MD->getNumOperands() == 0)
|
|
continue;
|
|
for (unsigned J = 0, N = MD->getNumOperands(); J != N; ++J)
|
|
StrSet.insert(getMDOperandAsString(MD, J).str());
|
|
}
|
|
|
|
return StrSet;
|
|
}
|
|
|
|
std::tuple<unsigned, unsigned, std::string> getSPIRVSource(Module *M) {
|
|
std::tuple<unsigned, unsigned, std::string> Tup;
|
|
if (auto N = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::Source).nextOp())
|
|
N.get(std::get<0>(Tup))
|
|
.get(std::get<1>(Tup))
|
|
.setQuiet(true)
|
|
.get(std::get<2>(Tup));
|
|
return Tup;
|
|
}
|
|
|
|
bool isDecoratedSPIRVFunc(const Function *F, StringRef &UndecoratedName) {
|
|
if (!F->hasName() || !F->getName().starts_with(kSPIRVName::Prefix))
|
|
return false;
|
|
UndecoratedName = F->getName();
|
|
return true;
|
|
}
|
|
|
|
/// Get TypePrimitiveEnum for special OpenCL type except opencl.block.
|
|
SPIR::TypePrimitiveEnum getOCLTypePrimitiveEnum(StringRef TyName) {
|
|
return StringSwitch<SPIR::TypePrimitiveEnum>(TyName)
|
|
.Case("opencl.image1d_ro_t", SPIR::PRIMITIVE_IMAGE1D_RO_T)
|
|
.Case("opencl.image1d_array_ro_t", SPIR::PRIMITIVE_IMAGE1D_ARRAY_RO_T)
|
|
.Case("opencl.image1d_buffer_ro_t", SPIR::PRIMITIVE_IMAGE1D_BUFFER_RO_T)
|
|
.Case("opencl.image2d_ro_t", SPIR::PRIMITIVE_IMAGE2D_RO_T)
|
|
.Case("opencl.image2d_array_ro_t", SPIR::PRIMITIVE_IMAGE2D_ARRAY_RO_T)
|
|
.Case("opencl.image2d_depth_ro_t", SPIR::PRIMITIVE_IMAGE2D_DEPTH_RO_T)
|
|
.Case("opencl.image2d_array_depth_ro_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RO_T)
|
|
.Case("opencl.image2d_msaa_ro_t", SPIR::PRIMITIVE_IMAGE2D_MSAA_RO_T)
|
|
.Case("opencl.image2d_array_msaa_ro_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_RO_T)
|
|
.Case("opencl.image2d_msaa_depth_ro_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_MSAA_DEPTH_RO_T)
|
|
.Case("opencl.image2d_array_msaa_depth_ro_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RO_T)
|
|
.Case("opencl.image3d_ro_t", SPIR::PRIMITIVE_IMAGE3D_RO_T)
|
|
.Case("opencl.image1d_wo_t", SPIR::PRIMITIVE_IMAGE1D_WO_T)
|
|
.Case("opencl.image1d_array_wo_t", SPIR::PRIMITIVE_IMAGE1D_ARRAY_WO_T)
|
|
.Case("opencl.image1d_buffer_wo_t", SPIR::PRIMITIVE_IMAGE1D_BUFFER_WO_T)
|
|
.Case("opencl.image2d_wo_t", SPIR::PRIMITIVE_IMAGE2D_WO_T)
|
|
.Case("opencl.image2d_array_wo_t", SPIR::PRIMITIVE_IMAGE2D_ARRAY_WO_T)
|
|
.Case("opencl.image2d_depth_wo_t", SPIR::PRIMITIVE_IMAGE2D_DEPTH_WO_T)
|
|
.Case("opencl.image2d_array_depth_wo_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_DEPTH_WO_T)
|
|
.Case("opencl.image2d_msaa_wo_t", SPIR::PRIMITIVE_IMAGE2D_MSAA_WO_T)
|
|
.Case("opencl.image2d_array_msaa_wo_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_WO_T)
|
|
.Case("opencl.image2d_msaa_depth_wo_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_MSAA_DEPTH_WO_T)
|
|
.Case("opencl.image2d_array_msaa_depth_wo_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_WO_T)
|
|
.Case("opencl.image3d_wo_t", SPIR::PRIMITIVE_IMAGE3D_WO_T)
|
|
.Case("opencl.image1d_rw_t", SPIR::PRIMITIVE_IMAGE1D_RW_T)
|
|
.Case("opencl.image1d_array_rw_t", SPIR::PRIMITIVE_IMAGE1D_ARRAY_RW_T)
|
|
.Case("opencl.image1d_buffer_rw_t", SPIR::PRIMITIVE_IMAGE1D_BUFFER_RW_T)
|
|
.Case("opencl.image2d_rw_t", SPIR::PRIMITIVE_IMAGE2D_RW_T)
|
|
.Case("opencl.image2d_array_rw_t", SPIR::PRIMITIVE_IMAGE2D_ARRAY_RW_T)
|
|
.Case("opencl.image2d_depth_rw_t", SPIR::PRIMITIVE_IMAGE2D_DEPTH_RW_T)
|
|
.Case("opencl.image2d_array_depth_rw_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_DEPTH_RW_T)
|
|
.Case("opencl.image2d_msaa_rw_t", SPIR::PRIMITIVE_IMAGE2D_MSAA_RW_T)
|
|
.Case("opencl.image2d_array_msaa_rw_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_RW_T)
|
|
.Case("opencl.image2d_msaa_depth_rw_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_MSAA_DEPTH_RW_T)
|
|
.Case("opencl.image2d_array_msaa_depth_rw_t",
|
|
SPIR::PRIMITIVE_IMAGE2D_ARRAY_MSAA_DEPTH_RW_T)
|
|
.Case("opencl.image3d_rw_t", SPIR::PRIMITIVE_IMAGE3D_RW_T)
|
|
.Case("opencl.event_t", SPIR::PRIMITIVE_EVENT_T)
|
|
.Case("opencl.pipe_ro_t", SPIR::PRIMITIVE_PIPE_RO_T)
|
|
.Case("opencl.pipe_wo_t", SPIR::PRIMITIVE_PIPE_WO_T)
|
|
.Case("opencl.reserve_id_t", SPIR::PRIMITIVE_RESERVE_ID_T)
|
|
.Case("opencl.queue_t", SPIR::PRIMITIVE_QUEUE_T)
|
|
.Case("opencl.clk_event_t", SPIR::PRIMITIVE_CLK_EVENT_T)
|
|
.Case("opencl.sampler_t", SPIR::PRIMITIVE_SAMPLER_T)
|
|
.Case("struct.ndrange_t", SPIR::PRIMITIVE_NDRANGE_T)
|
|
.Case("opencl.intel_sub_group_avc_mce_payload_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_MCE_PAYLOAD_T)
|
|
.Case("opencl.intel_sub_group_avc_ime_payload_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_IME_PAYLOAD_T)
|
|
.Case("opencl.intel_sub_group_avc_ref_payload_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_REF_PAYLOAD_T)
|
|
.Case("opencl.intel_sub_group_avc_sic_payload_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_SIC_PAYLOAD_T)
|
|
.Case("opencl.intel_sub_group_avc_mce_result_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_MCE_RESULT_T)
|
|
.Case("opencl.intel_sub_group_avc_ime_result_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_IME_RESULT_T)
|
|
.Case("opencl.intel_sub_group_avc_ref_result_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_REF_RESULT_T)
|
|
.Case("opencl.intel_sub_group_avc_sic_result_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_SIC_RESULT_T)
|
|
.Case(
|
|
"opencl.intel_sub_group_avc_ime_result_single_reference_streamout_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_IME_SINGLE_REF_STREAMOUT_T)
|
|
.Case("opencl.intel_sub_group_avc_ime_result_dual_reference_streamout_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_IME_DUAL_REF_STREAMOUT_T)
|
|
.Case("opencl.intel_sub_group_avc_ime_single_reference_streamin_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_IME_SINGLE_REF_STREAMIN_T)
|
|
.Case("opencl.intel_sub_group_avc_ime_dual_reference_streamin_t",
|
|
SPIR::PRIMITIVE_SUB_GROUP_AVC_IME_DUAL_REF_STREAMIN_T)
|
|
.Default(SPIR::PRIMITIVE_NONE);
|
|
}
|
|
/// Translates LLVM type to descriptor for mangler.
|
|
/// \param Signed indicates integer type should be translated as signed.
|
|
/// \param VoidPtr indicates i8* should be translated as void*.
|
|
static SPIR::RefParamType transTypeDesc(Type *Ty,
|
|
const BuiltinArgTypeMangleInfo &Info,
|
|
StringRef InstName = "") {
|
|
bool Signed = Info.IsSigned;
|
|
unsigned Attr = Info.Attr;
|
|
bool VoidPtr = Info.IsVoidPtr;
|
|
if (Info.IsEnum)
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(Info.Enum));
|
|
if (Info.IsSampler)
|
|
return SPIR::RefParamType(
|
|
new SPIR::PrimitiveType(SPIR::PRIMITIVE_SAMPLER_T));
|
|
if (Ty->isPointerTy())
|
|
Ty = TypedPointerType::get(Type::getInt8Ty(Ty->getContext()),
|
|
Ty->getPointerAddressSpace());
|
|
if (Info.IsAtomic && !isa<TypedPointerType>(Ty)) {
|
|
BuiltinArgTypeMangleInfo DTInfo = Info;
|
|
DTInfo.IsAtomic = false;
|
|
return SPIR::RefParamType(new SPIR::AtomicType(transTypeDesc(Ty, DTInfo)));
|
|
}
|
|
if (auto *IntTy = dyn_cast<IntegerType>(Ty)) {
|
|
switch (IntTy->getBitWidth()) {
|
|
case 1:
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_BOOL));
|
|
case 8:
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(
|
|
Signed ? SPIR::PRIMITIVE_CHAR : SPIR::PRIMITIVE_UCHAR));
|
|
case 16:
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(
|
|
Signed ? SPIR::PRIMITIVE_SHORT : SPIR::PRIMITIVE_USHORT));
|
|
case 32:
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(
|
|
Signed ? SPIR::PRIMITIVE_INT : SPIR::PRIMITIVE_UINT));
|
|
case 64:
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(
|
|
Signed ? SPIR::PRIMITIVE_LONG : SPIR::PRIMITIVE_ULONG));
|
|
default:
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_INT));
|
|
}
|
|
}
|
|
if (Ty->isVoidTy())
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_VOID));
|
|
if (Ty->isHalfTy())
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_HALF));
|
|
if (Ty->isFloatTy())
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_FLOAT));
|
|
if (Ty->isDoubleTy())
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_DOUBLE));
|
|
if (Ty->isBFloatTy())
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_BFLOAT));
|
|
if (auto *VecTy = dyn_cast<FixedVectorType>(Ty)) {
|
|
return SPIR::RefParamType(new SPIR::VectorType(
|
|
transTypeDesc(VecTy->getElementType(), Info), VecTy->getNumElements()));
|
|
}
|
|
if (Ty->isArrayTy()) {
|
|
return transTypeDesc(TypedPointerType::get(Ty->getArrayElementType(), 0),
|
|
Info);
|
|
}
|
|
if (Ty->isStructTy()) {
|
|
auto Name = Ty->getStructName();
|
|
std::string Tmp;
|
|
|
|
if (Name.starts_with(kLLVMTypeName::StructPrefix))
|
|
Name = Name.drop_front(strlen(kLLVMTypeName::StructPrefix));
|
|
if (Name.starts_with(kSPIRVTypeName::PrefixAndDelim)) {
|
|
Name = Name.substr(sizeof(kSPIRVTypeName::PrefixAndDelim) - 1);
|
|
Tmp = Name.str();
|
|
auto Pos = Tmp.find(kSPIRVTypeName::Delimiter); // first dot
|
|
while (Pos != std::string::npos) {
|
|
Tmp[Pos] = '_';
|
|
Pos = Tmp.find(kSPIRVTypeName::Delimiter, Pos);
|
|
}
|
|
Name = Tmp = kSPIRVName::Prefix + Tmp;
|
|
}
|
|
// ToDo: Create a better unique Name for struct without Name
|
|
if (Name.empty()) {
|
|
std::ostringstream OS;
|
|
OS << reinterpret_cast<size_t>(Ty);
|
|
Name = Tmp = std::string("struct_") + OS.str();
|
|
}
|
|
return SPIR::RefParamType(new SPIR::UserDefinedType(Name.str()));
|
|
}
|
|
if (auto *TargetTy = dyn_cast<TargetExtType>(Ty)) {
|
|
std::string FullName;
|
|
unsigned AS = 0;
|
|
{
|
|
raw_string_ostream OS(FullName);
|
|
StringRef Name = TargetTy->getName();
|
|
if (Name.consume_front(kSPIRVTypeName::PrefixAndDelim)) {
|
|
OS << "__spirv_" << Name;
|
|
AS = getOCLOpaqueTypeAddrSpace(
|
|
SPIRVOpaqueTypeOpCodeMap::map(Name.str()));
|
|
} else {
|
|
OS << Name;
|
|
}
|
|
if (!TargetTy->int_params().empty())
|
|
OS << "_";
|
|
for (Type *InnerTy : TargetTy->type_params())
|
|
OS << "_" << convertTypeToPostfix(InnerTy);
|
|
for (unsigned Param : TargetTy->int_params())
|
|
OS << "_" << Param;
|
|
}
|
|
// Translate as if it's a pointer to the named struct.
|
|
auto *Inner = new SPIR::UserDefinedType(FullName);
|
|
auto *PT = new SPIR::PointerType(Inner);
|
|
PT->setAddressSpace(static_cast<SPIR::TypeAttributeEnum>(
|
|
AS + (unsigned)SPIR::ATTR_ADDR_SPACE_FIRST));
|
|
return SPIR::RefParamType(PT);
|
|
}
|
|
|
|
if (auto *TPT = dyn_cast<TypedPointerType>(Ty)) {
|
|
auto *ET = TPT->getElementType();
|
|
SPIR::ParamType *EPT = nullptr;
|
|
if (isa<FunctionType>(ET)) {
|
|
FunctionType *FT = cast<FunctionType>(ET);
|
|
if (InstName.consume_front(kSPIRVName::Prefix) &&
|
|
InstName.starts_with("TaskSequence")) {
|
|
EPT = new SPIR::PointerType(transTypeDesc(FT->getReturnType(), Info));
|
|
} else {
|
|
assert((isVoidFuncTy(FT)) && "Not supported");
|
|
EPT = new SPIR::BlockType;
|
|
}
|
|
} else if (auto *StructTy = dyn_cast<StructType>(ET)) {
|
|
LLVM_DEBUG(dbgs() << "ptr to struct: " << *Ty << '\n');
|
|
auto TyName = StructTy->getStructName();
|
|
if (TyName.starts_with(kSPR2TypeName::OCLPrefix)) {
|
|
auto DelimPos = TyName.find_first_of(kSPR2TypeName::Delimiter,
|
|
strlen(kSPR2TypeName::OCLPrefix));
|
|
if (DelimPos != StringRef::npos)
|
|
TyName = TyName.substr(0, DelimPos);
|
|
}
|
|
LLVM_DEBUG(dbgs() << " type Name: " << TyName << '\n');
|
|
|
|
auto Prim = getOCLTypePrimitiveEnum(TyName);
|
|
if (StructTy->isOpaque()) {
|
|
if (TyName == "opencl.block") {
|
|
auto *BlockTy = new SPIR::BlockType;
|
|
// Handle block with local memory arguments according to OpenCL 2.0
|
|
// spec.
|
|
if (Info.IsLocalArgBlock) {
|
|
SPIR::RefParamType VoidTyRef(
|
|
new SPIR::PrimitiveType(SPIR::PRIMITIVE_VOID));
|
|
auto *VoidPtrTy = new SPIR::PointerType(VoidTyRef);
|
|
VoidPtrTy->setAddressSpace(SPIR::ATTR_LOCAL);
|
|
// "__local void *"
|
|
BlockTy->setParam(0, SPIR::RefParamType(VoidPtrTy));
|
|
// "..."
|
|
BlockTy->setParam(1, SPIR::RefParamType(new SPIR::PrimitiveType(
|
|
SPIR::PRIMITIVE_VAR_ARG)));
|
|
}
|
|
EPT = BlockTy;
|
|
} else if (Prim != SPIR::PRIMITIVE_NONE) {
|
|
if (Prim == SPIR::PRIMITIVE_PIPE_RO_T ||
|
|
Prim == SPIR::PRIMITIVE_PIPE_WO_T) {
|
|
SPIR::RefParamType OpaqueTyRef(new SPIR::PrimitiveType(Prim));
|
|
auto *OpaquePtrTy = new SPIR::PointerType(OpaqueTyRef);
|
|
OpaquePtrTy->setAddressSpace(getOCLOpaqueTypeAddrSpace(Prim));
|
|
EPT = OpaquePtrTy;
|
|
} else {
|
|
EPT = new SPIR::PrimitiveType(Prim);
|
|
}
|
|
}
|
|
} else if (Prim == SPIR::PRIMITIVE_NDRANGE_T)
|
|
// ndrange_t is not opaque type
|
|
EPT = new SPIR::PrimitiveType(SPIR::PRIMITIVE_NDRANGE_T);
|
|
}
|
|
if (EPT)
|
|
return SPIR::RefParamType(EPT);
|
|
|
|
if (VoidPtr && ET->isIntegerTy(8))
|
|
ET = Type::getVoidTy(ET->getContext());
|
|
auto *PT = new SPIR::PointerType(transTypeDesc(ET, Info));
|
|
PT->setAddressSpace(static_cast<SPIR::TypeAttributeEnum>(
|
|
TPT->getAddressSpace() + (unsigned)SPIR::ATTR_ADDR_SPACE_FIRST));
|
|
for (unsigned I = SPIR::ATTR_QUALIFIER_FIRST, E = SPIR::ATTR_QUALIFIER_LAST;
|
|
I <= E; ++I)
|
|
PT->setQualifier(static_cast<SPIR::TypeAttributeEnum>(I), I & Attr);
|
|
return SPIR::RefParamType(PT);
|
|
}
|
|
LLVM_DEBUG(dbgs() << "[transTypeDesc] " << *Ty << '\n');
|
|
assert(0 && "not implemented");
|
|
return SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_INT));
|
|
}
|
|
|
|
Value *getScalarOrArray(Value *V, unsigned Size, BasicBlock::iterator Pos) {
|
|
if (!V->getType()->isPointerTy())
|
|
return V;
|
|
Type *SourceTy;
|
|
Value *Addr;
|
|
if (auto *GV = dyn_cast<GlobalVariable>(V)) {
|
|
SourceTy = GV->getValueType();
|
|
Addr = GV;
|
|
} else if (auto *AI = dyn_cast<AllocaInst>(V)) {
|
|
SourceTy = AI->getAllocatedType();
|
|
Addr = AI;
|
|
} else if (auto *GEP = dyn_cast<GEPOperator>(V)) {
|
|
assert(GEP->getNumOperands() == 3 && "must be a GEP from an array");
|
|
SourceTy = GEP->getSourceElementType();
|
|
[[maybe_unused]] auto *OP1 = cast<ConstantInt>(GEP->getOperand(1));
|
|
[[maybe_unused]] auto *OP2 = cast<ConstantInt>(GEP->getOperand(2));
|
|
assert(OP1->getZExtValue() == 0);
|
|
assert(OP2->getZExtValue() == 0);
|
|
Addr = GEP->getOperand(0);
|
|
} else {
|
|
llvm_unreachable("Unknown array type");
|
|
}
|
|
assert(SourceTy->getArrayNumElements() == Size);
|
|
return new LoadInst(SourceTy, Addr, "", Pos);
|
|
}
|
|
|
|
Constant *getScalarOrVectorConstantInt(Type *T, uint64_t V, bool IsSigned) {
|
|
if (auto *IT = dyn_cast<IntegerType>(T))
|
|
return ConstantInt::get(IT, V);
|
|
if (auto *VT = dyn_cast<FixedVectorType>(T)) {
|
|
std::vector<Constant *> EV(
|
|
VT->getNumElements(),
|
|
getScalarOrVectorConstantInt(VT->getElementType(), V, IsSigned));
|
|
return ConstantVector::get(EV);
|
|
}
|
|
llvm_unreachable("Invalid type");
|
|
return nullptr;
|
|
}
|
|
|
|
Value *getScalarOrArrayConstantInt(BasicBlock::iterator Pos, Type *T,
|
|
unsigned Len, uint64_t V, bool IsSigned) {
|
|
if (auto *IT = dyn_cast<IntegerType>(T)) {
|
|
assert(Len == 1 && "Invalid length");
|
|
return ConstantInt::get(IT, V, IsSigned);
|
|
}
|
|
if (isa<PointerType>(T)) {
|
|
unsigned PointerSize =
|
|
Pos->getModule()->getDataLayout().getPointerTypeSizeInBits(T);
|
|
auto *ET = Type::getIntNTy(T->getContext(), PointerSize);
|
|
auto *AT = ArrayType::get(ET, Len);
|
|
std::vector<Constant *> EV(Len, ConstantInt::get(ET, V, IsSigned));
|
|
auto *CA = ConstantArray::get(AT, EV);
|
|
auto *Alloca = new AllocaInst(
|
|
AT, Pos->getParent()->getParent()->getDataLayout().getAllocaAddrSpace(),
|
|
"", Pos);
|
|
new StoreInst(CA, Alloca, Pos);
|
|
auto *Zero = ConstantInt::getNullValue(Type::getInt32Ty(T->getContext()));
|
|
Value *Index[] = {Zero, Zero};
|
|
auto *Ret = GetElementPtrInst::CreateInBounds(AT, Alloca, Index, "", Pos);
|
|
LLVM_DEBUG(dbgs() << "[getScalarOrArrayConstantInt] Alloca: " << *Alloca
|
|
<< ", Return: " << *Ret << '\n');
|
|
return Ret;
|
|
}
|
|
if (auto *AT = dyn_cast<ArrayType>(T)) {
|
|
auto *ET = AT->getArrayElementType();
|
|
assert(AT->getArrayNumElements() == Len);
|
|
std::vector<Constant *> EV(Len, ConstantInt::get(ET, V, IsSigned));
|
|
auto *Ret = ConstantArray::get(AT, EV);
|
|
LLVM_DEBUG(dbgs() << "[getScalarOrArrayConstantInt] Array type: " << *AT
|
|
<< ", Return: " << *Ret << '\n');
|
|
return Ret;
|
|
}
|
|
llvm_unreachable("Invalid type");
|
|
return nullptr;
|
|
}
|
|
|
|
void dumpUsers(Value *V, StringRef Prompt) {
|
|
if (!V)
|
|
return;
|
|
LLVM_DEBUG(dbgs() << Prompt << " Users of " << *V << " :\n");
|
|
for (auto UI = V->user_begin(), UE = V->user_end(); UI != UE; ++UI)
|
|
LLVM_DEBUG(dbgs() << " " << **UI << '\n');
|
|
}
|
|
|
|
std::string getSPIRVTypeName(StringRef BaseName, StringRef Postfixes) {
|
|
assert(!BaseName.empty() && "Invalid SPIR-V type Name");
|
|
auto TN = std::string(kSPIRVTypeName::PrefixAndDelim) + BaseName.str();
|
|
if (Postfixes.empty())
|
|
return TN;
|
|
return TN + kSPIRVTypeName::Delimiter + Postfixes.str();
|
|
}
|
|
|
|
bool isSPIRVConstantName(StringRef TyName) {
|
|
if (TyName == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler) ||
|
|
TyName == getSPIRVTypeName(kSPIRVTypeName::ConstantPipeStorage))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
// ToDo: Find a way to represent uint sampled type in LLVM, maybe an
|
|
// opaque type.
|
|
Type *getLLVMTypeForSPIRVImageSampledTypePostfix(StringRef Postfix,
|
|
LLVMContext &Ctx) {
|
|
if (Postfix == kSPIRVImageSampledTypeName::Void)
|
|
return Type::getVoidTy(Ctx);
|
|
if (Postfix == kSPIRVImageSampledTypeName::Float)
|
|
return Type::getFloatTy(Ctx);
|
|
if (Postfix == kSPIRVImageSampledTypeName::Half)
|
|
return Type::getHalfTy(Ctx);
|
|
if (Postfix == kSPIRVImageSampledTypeName::Int ||
|
|
Postfix == kSPIRVImageSampledTypeName::UInt)
|
|
return Type::getInt32Ty(Ctx);
|
|
if (Postfix == kSPIRVImageSampledTypeName::Long ||
|
|
Postfix == kSPIRVImageSampledTypeName::ULong)
|
|
return Type::getInt64Ty(Ctx);
|
|
llvm_unreachable("Invalid sampled type postfix");
|
|
return nullptr;
|
|
}
|
|
|
|
std::string convertTypeToPostfix(Type *Ty) {
|
|
if (Ty->isIntegerTy()) {
|
|
switch (Ty->getIntegerBitWidth()) {
|
|
case 8:
|
|
return "char";
|
|
case 16:
|
|
return "short";
|
|
case 32:
|
|
return "uint";
|
|
case 64:
|
|
return "long";
|
|
default:
|
|
return (Twine("i") + Twine(Ty->getIntegerBitWidth())).str();
|
|
}
|
|
} else if (Ty->isHalfTy()) {
|
|
return "half";
|
|
} else if (Ty->isFloatTy()) {
|
|
return "float";
|
|
} else if (Ty->isDoubleTy()) {
|
|
return "double";
|
|
} else if (Ty->isBFloatTy()) {
|
|
return "bfloat16";
|
|
} else if (Ty->isVoidTy()) {
|
|
return "void";
|
|
} else {
|
|
report_fatal_error("Unknown LLVM type for element type");
|
|
}
|
|
}
|
|
|
|
std::string getImageBaseTypeName(StringRef Name) {
|
|
|
|
SmallVector<StringRef, 4> SubStrs;
|
|
const char Delims[] = {kSPR2TypeName::Delimiter, 0};
|
|
Name.split(SubStrs, Delims);
|
|
if (Name.starts_with(kSPR2TypeName::OCLPrefix)) {
|
|
Name = SubStrs[1];
|
|
} else {
|
|
Name = SubStrs[0];
|
|
}
|
|
|
|
std::string ImageTyName{Name};
|
|
if (hasAccessQualifiedName(Name))
|
|
ImageTyName.erase(ImageTyName.size() - 5, 3);
|
|
|
|
return ImageTyName;
|
|
}
|
|
|
|
size_t getImageOperandsIndex(Op OpCode) {
|
|
switch (OpCode) {
|
|
case OpImageRead:
|
|
case OpImageSampleExplicitLod:
|
|
return 2;
|
|
case OpImageWrite:
|
|
return 3;
|
|
default:
|
|
return ~0U;
|
|
}
|
|
}
|
|
|
|
SPIRVTypeImageDescriptor getImageDescriptor(Type *Ty) {
|
|
if (auto *TET = dyn_cast_or_null<TargetExtType>(Ty)) {
|
|
auto IntParams = TET->int_params();
|
|
assert(IntParams.size() > 6 && "Expected type to be an image type");
|
|
return SPIRVTypeImageDescriptor(SPIRVImageDimKind(IntParams[0]),
|
|
IntParams[1], IntParams[2], IntParams[3],
|
|
IntParams[4], IntParams[5]);
|
|
}
|
|
StringRef TyName;
|
|
[[maybe_unused]] bool IsImg = isOCLImageType(Ty, &TyName);
|
|
assert(IsImg && "Must be an image type");
|
|
return map<SPIRVTypeImageDescriptor>(getImageBaseTypeName(TyName));
|
|
}
|
|
|
|
bool eraseIfNoUse(Function *F) {
|
|
bool Changed = false;
|
|
if (!F)
|
|
return Changed;
|
|
if (!GlobalValue::isInternalLinkage(F->getLinkage()) && !F->isDeclaration())
|
|
return Changed;
|
|
|
|
dumpUsers(F, "[eraseIfNoUse] ");
|
|
for (auto UI = F->user_begin(), UE = F->user_end(); UI != UE;) {
|
|
auto *U = *UI++;
|
|
if (auto *CE = dyn_cast<ConstantExpr>(U)) {
|
|
if (CE->use_empty()) {
|
|
CE->dropAllReferences();
|
|
Changed = true;
|
|
}
|
|
}
|
|
}
|
|
if (F->use_empty()) {
|
|
LLVM_DEBUG(dbgs() << "Erase "; F->printAsOperand(dbgs()); dbgs() << '\n');
|
|
F->eraseFromParent();
|
|
Changed = true;
|
|
}
|
|
return Changed;
|
|
}
|
|
|
|
bool eraseUselessFunctions(Module *M) {
|
|
bool Changed = false;
|
|
for (auto I = M->begin(), E = M->end(); I != E;)
|
|
Changed |= eraseIfNoUse(&(*I++));
|
|
return Changed;
|
|
}
|
|
|
|
// The mangling algorithm follows OpenCL pipe built-ins clang 3.8 CodeGen rules.
|
|
static SPIR::MangleError
|
|
manglePipeOrAddressSpaceCastBuiltin(const SPIR::FunctionDescriptor &Fd,
|
|
std::string &MangledName) {
|
|
assert(OCLUtil::isPipeOrAddressSpaceCastBI(Fd.Name) &&
|
|
"Method is expected to be called only for pipe and address space cast "
|
|
"builtins!");
|
|
if (Fd.isNull()) {
|
|
MangledName.assign(SPIR::FunctionDescriptor::nullString());
|
|
return SPIR::MANGLE_NULL_FUNC_DESCRIPTOR;
|
|
}
|
|
MangledName.assign("__" + Fd.Name);
|
|
return SPIR::MANGLE_SUCCESS;
|
|
}
|
|
|
|
std::string mangleBuiltin(StringRef UniqName, ArrayRef<Type *> ArgTypes,
|
|
BuiltinFuncMangleInfo *BtnInfo) {
|
|
if (!BtnInfo)
|
|
return std::string(UniqName);
|
|
BtnInfo->init(UniqName);
|
|
if (BtnInfo->avoidMangling())
|
|
return std::string(UniqName);
|
|
std::string MangledName;
|
|
LLVM_DEBUG(dbgs() << "[mangle] " << UniqName << " => ");
|
|
SPIR::FunctionDescriptor FD;
|
|
FD.Name = BtnInfo->getUnmangledName();
|
|
bool BIVarArgNegative = BtnInfo->getVarArg() < 0;
|
|
|
|
if (ArgTypes.empty()) {
|
|
// Function signature cannot be ()(void, ...) so if there is an ellipsis
|
|
// it must be ()(...)
|
|
if (BIVarArgNegative) {
|
|
FD.Parameters.emplace_back(
|
|
SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_VOID)));
|
|
}
|
|
} else {
|
|
for (unsigned I = 0, E = BIVarArgNegative ? ArgTypes.size()
|
|
: (unsigned)BtnInfo->getVarArg();
|
|
I != E; ++I) {
|
|
auto *T = ArgTypes[I];
|
|
auto MangleInfo = BtnInfo->getTypeMangleInfo(I);
|
|
if (MangleInfo.PointerTy && T->isPointerTy()) {
|
|
T = MangleInfo.PointerTy;
|
|
}
|
|
FD.Parameters.emplace_back(
|
|
transTypeDesc(T, BtnInfo->getTypeMangleInfo(I), UniqName));
|
|
}
|
|
}
|
|
// Ellipsis must be the last argument of any function
|
|
if (!BIVarArgNegative) {
|
|
assert((unsigned)BtnInfo->getVarArg() <= ArgTypes.size() &&
|
|
"invalid index of an ellipsis");
|
|
FD.Parameters.emplace_back(
|
|
SPIR::RefParamType(new SPIR::PrimitiveType(SPIR::PRIMITIVE_VAR_ARG)));
|
|
}
|
|
|
|
#if defined(SPIRV_SPIR20_MANGLING_REQUIREMENTS)
|
|
SPIR::NameMangler Mangler(SPIR::SPIR20);
|
|
Mangler.mangle(FD, MangledName);
|
|
#else
|
|
if (OCLUtil::isPipeOrAddressSpaceCastBI(BtnInfo->getUnmangledName())) {
|
|
manglePipeOrAddressSpaceCastBuiltin(FD, MangledName);
|
|
} else {
|
|
SPIR::NameMangler Mangler(SPIR::SPIR20);
|
|
Mangler.mangle(FD, MangledName);
|
|
}
|
|
#endif
|
|
|
|
LLVM_DEBUG(dbgs() << MangledName << '\n');
|
|
return MangledName;
|
|
}
|
|
|
|
/// Check if access qualifier is encoded in the type Name.
|
|
bool hasAccessQualifiedName(StringRef TyName) {
|
|
if (TyName.size() < 5)
|
|
return false;
|
|
auto Acc = TyName.substr(TyName.size() - 5, 3);
|
|
return llvm::StringSwitch<bool>(Acc)
|
|
.Case(kAccessQualPostfix::ReadOnly, true)
|
|
.Case(kAccessQualPostfix::WriteOnly, true)
|
|
.Case(kAccessQualPostfix::ReadWrite, true)
|
|
.Default(false);
|
|
}
|
|
|
|
SPIRVAccessQualifierKind getAccessQualifier(StringRef TyName) {
|
|
assert(hasAccessQualifiedName(TyName) &&
|
|
"Type is not qualified with access.");
|
|
auto Acc = TyName.substr(TyName.size() - 5, 3);
|
|
return llvm::StringSwitch<SPIRVAccessQualifierKind>(Acc)
|
|
.Case(kAccessQualPostfix::ReadOnly, AccessQualifierReadOnly)
|
|
.Case(kAccessQualPostfix::WriteOnly, AccessQualifierWriteOnly)
|
|
.Case(kAccessQualPostfix::ReadWrite, AccessQualifierReadWrite);
|
|
}
|
|
|
|
StringRef getAccessQualifierPostfix(SPIRVAccessQualifierKind Access) {
|
|
switch (Access) {
|
|
case AccessQualifierReadOnly:
|
|
return kAccessQualPostfix::ReadOnly;
|
|
case AccessQualifierWriteOnly:
|
|
return kAccessQualPostfix::WriteOnly;
|
|
case AccessQualifierReadWrite:
|
|
return kAccessQualPostfix::ReadWrite;
|
|
default:
|
|
assert(false && "Unrecognized access qualifier!");
|
|
return kAccessQualPostfix::ReadWrite;
|
|
}
|
|
}
|
|
|
|
bool hasLoopMetadata(const Module *M) {
|
|
for (const Function &F : *M)
|
|
for (const BasicBlock &BB : F) {
|
|
const Instruction *Term = BB.getTerminator();
|
|
if (Term && Term->getMetadata("llvm.loop"))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isSPIRVOCLExtInst(const CallInst *CI, OCLExtOpKind *ExtOp) {
|
|
StringRef DemangledName;
|
|
if (!oclIsBuiltin(CI->getCalledFunction()->getName(), DemangledName))
|
|
return false;
|
|
StringRef S = DemangledName;
|
|
if (!S.starts_with(kSPIRVName::Prefix))
|
|
return false;
|
|
S = S.drop_front(strlen(kSPIRVName::Prefix));
|
|
auto Loc = S.find(kSPIRVPostfix::Divider);
|
|
auto ExtSetName = S.substr(0, Loc);
|
|
SPIRVExtInstSetKind Set = SPIRVEIS_Count;
|
|
if (!SPIRVExtSetShortNameMap::rfind(ExtSetName.str(), &Set))
|
|
return false;
|
|
|
|
if (Set != SPIRVEIS_OpenCL)
|
|
return false;
|
|
|
|
auto ExtOpName = S.substr(Loc + 1);
|
|
auto PostFixPos = ExtOpName.find("_R");
|
|
ExtOpName = ExtOpName.substr(0, PostFixPos);
|
|
|
|
OCLExtOpKind EOC;
|
|
if (!OCLExtOpMap::rfind(ExtOpName.str(), &EOC))
|
|
return false;
|
|
|
|
*ExtOp = EOC;
|
|
return true;
|
|
}
|
|
|
|
std::string decodeSPIRVTypeName(StringRef Name,
|
|
SmallVectorImpl<std::string> &Strs) {
|
|
SmallVector<StringRef, 4> SubStrs;
|
|
const char Delim[] = {kSPIRVTypeName::Delimiter, 0};
|
|
Name.split(SubStrs, Delim, -1, true);
|
|
assert(SubStrs.size() >= 2 && "Invalid SPIRV type name");
|
|
assert(SubStrs[0] == kSPIRVTypeName::Prefix && "Invalid prefix");
|
|
assert((SubStrs.size() == 2 || !SubStrs[2].empty()) && "Invalid postfix");
|
|
|
|
if (SubStrs.size() > 2) {
|
|
const char PostDelim[] = {kSPIRVTypeName::PostfixDelim, 0};
|
|
SmallVector<StringRef, 4> Postfixes;
|
|
SubStrs[2].split(Postfixes, PostDelim, -1, true);
|
|
assert(Postfixes.size() > 1 && Postfixes[0].empty() && "Invalid postfix");
|
|
for (unsigned I = 1, E = Postfixes.size(); I != E; ++I)
|
|
Strs.push_back(std::string(Postfixes[I]).c_str());
|
|
}
|
|
return SubStrs[1].str();
|
|
}
|
|
|
|
// Returns true if type(s) and number of elements (if vector) is valid
|
|
bool checkTypeForSPIRVExtendedInstLowering(IntrinsicInst *II, SPIRVModule *BM) {
|
|
switch (II->getIntrinsicID()) {
|
|
case Intrinsic::acos:
|
|
case Intrinsic::asin:
|
|
case Intrinsic::atan:
|
|
case Intrinsic::atan2:
|
|
case Intrinsic::ceil:
|
|
case Intrinsic::copysign:
|
|
case Intrinsic::cos:
|
|
case Intrinsic::cosh:
|
|
case Intrinsic::exp:
|
|
case Intrinsic::exp2:
|
|
case Intrinsic::fabs:
|
|
case Intrinsic::floor:
|
|
case Intrinsic::fma:
|
|
case Intrinsic::log:
|
|
case Intrinsic::log10:
|
|
case Intrinsic::log2:
|
|
case Intrinsic::maximum:
|
|
case Intrinsic::maxnum:
|
|
case Intrinsic::minimum:
|
|
case Intrinsic::minnum:
|
|
case Intrinsic::nearbyint:
|
|
case Intrinsic::pow:
|
|
case Intrinsic::powi:
|
|
case Intrinsic::rint:
|
|
case Intrinsic::round:
|
|
case Intrinsic::roundeven:
|
|
case Intrinsic::sin:
|
|
case Intrinsic::sinh:
|
|
case Intrinsic::sqrt:
|
|
case Intrinsic::tan:
|
|
case Intrinsic::tanh:
|
|
case Intrinsic::trunc: {
|
|
// Although some of the intrinsics above take multiple arguments, it is
|
|
// sufficient to check arg 0 because the LLVM Verifier will have checked
|
|
// that all floating point operands have the same type and the second
|
|
// argument of powi is i32.
|
|
Type *Ty = II->getType();
|
|
if (II->getArgOperand(0)->getType() != Ty)
|
|
return false;
|
|
int NumElems = 1;
|
|
if (auto *VecTy = dyn_cast<FixedVectorType>(Ty)) {
|
|
NumElems = VecTy->getNumElements();
|
|
Ty = VecTy->getElementType();
|
|
}
|
|
if (Ty->isBFloatTy() &&
|
|
BM->hasCapability(internal::CapabilityBFloat16ArithmeticINTEL))
|
|
return true;
|
|
if ((!Ty->isFloatTy() && !Ty->isDoubleTy() && !Ty->isHalfTy()) ||
|
|
(!BM->hasCapability(CapabilityVectorAnyINTEL) &&
|
|
!BM->hasCapability(CapabilityLongVectorEXT) &&
|
|
((NumElems > 4) && (NumElems != 8) && (NumElems != 16)))) {
|
|
BM->SPIRVCK(false, InvalidFunctionCall,
|
|
II->getCalledOperand()->getName().str());
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case Intrinsic::abs: {
|
|
Type *Ty = II->getType();
|
|
int NumElems = 1;
|
|
if (auto *VecTy = dyn_cast<FixedVectorType>(Ty)) {
|
|
NumElems = VecTy->getNumElements();
|
|
Ty = VecTy->getElementType();
|
|
}
|
|
if (Ty->isBFloatTy() &&
|
|
BM->hasCapability(internal::CapabilityBFloat16ArithmeticINTEL))
|
|
return true;
|
|
if ((!Ty->isIntegerTy()) ||
|
|
(!BM->hasCapability(CapabilityVectorAnyINTEL) &&
|
|
!BM->hasCapability(CapabilityLongVectorEXT) &&
|
|
((NumElems > 4) && (NumElems != 8) && (NumElems != 16)))) {
|
|
BM->SPIRVCK(false, InvalidFunctionCall,
|
|
II->getCalledOperand()->getName().str());
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
CallInst *setAttrByCalledFunc(CallInst *Call) {
|
|
Function *F = Call->getCalledFunction();
|
|
assert(F);
|
|
if (F->isIntrinsic()) {
|
|
return Call;
|
|
}
|
|
Call->setCallingConv(F->getCallingConv());
|
|
Call->setAttributes(F->getAttributes());
|
|
return Call;
|
|
}
|
|
|
|
bool isSPIRVBuiltinVariable(GlobalVariable *GV,
|
|
SPIRVBuiltinVariableKind *Kind) {
|
|
if (!GV->hasName() || !getSPIRVBuiltin(GV->getName().str(), *Kind))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Variable like GlobalInvolcationId[x] -> get_global_id(x).
|
|
// Variable like WorkDim -> get_work_dim().
|
|
// Replace the following pattern:
|
|
// %a = addrspacecast i32 addrspace(1)* @__spirv_BuiltInSubgroupMaxSize to
|
|
// i32 addrspace(4)*
|
|
// %b = load i32, i32 addrspace(4)* %a, align 4
|
|
// %c = load i32, i32 addrspace(4)* %a, align 4
|
|
// With:
|
|
// %b = call spir_func i32 @_Z22get_max_sub_group_sizev()
|
|
// %c = call spir_func i32 @_Z22get_max_sub_group_sizev()
|
|
|
|
// And replace the following pattern:
|
|
// %a = addrspacecast <3 x i64> addrspace(1)* @__spirv_BuiltInWorkgroupId to
|
|
// <3 x i64> addrspace(4)*
|
|
// %b = load <3 x i64>, <3 x i64> addrspace(4)* %a, align 32
|
|
// %c = extractelement <3 x i64> %b, i32 idx
|
|
// %d = extractelement <3 x i64> %b, i32 idx
|
|
// With:
|
|
// %0 = call spir_func i64 @_Z13get_global_idj(i32 0) #1
|
|
// %1 = insertelement <3 x i64> poison, i64 %0, i32 0
|
|
// %2 = call spir_func i64 @_Z13get_global_idj(i32 1) #1
|
|
// %3 = insertelement <3 x i64> %1, i64 %2, i32 1
|
|
// %4 = call spir_func i64 @_Z13get_global_idj(i32 2) #1
|
|
// %5 = insertelement <3 x i64> %3, i64 %4, i32 2
|
|
// %c = extractelement <3 x i64> %5, i32 idx
|
|
// %d = extractelement <3 x i64> %5, i32 idx
|
|
//
|
|
// Replace the following pattern:
|
|
// %0 = addrspacecast <3 x i64> addrspace(1)* @__spirv_BuiltInWorkgroupSize to
|
|
// <3 x i64> addrspace(4)*
|
|
// %1 = getelementptr <3 x i64>, <3 x i64> addrspace(4)* %0, i64 0, i64 0
|
|
// %2 = load i64, i64 addrspace(4)* %1, align 32
|
|
// With:
|
|
// %0 = call spir_func i64 @_Z13get_global_idj(i32 0) #1
|
|
// %1 = insertelement <3 x i64> poison, i64 %0, i32 0
|
|
// %2 = call spir_func i64 @_Z13get_global_idj(i32 1) #1
|
|
// %3 = insertelement <3 x i64> %1, i64 %2, i32 1
|
|
// %4 = call spir_func i64 @_Z13get_global_idj(i32 2) #1
|
|
// %5 = insertelement <3 x i64> %3, i64 %4, i32 2
|
|
// %6 = extractelement <3 x i64> %5, i32 0
|
|
|
|
/// Recursively look through the uses of a global variable, including casts or
|
|
/// gep offsets, to find all loads of the variable. Gep offsets that are non-0
|
|
/// are accumulated in the AccumulatedOffset parameter, which will eventually be
|
|
/// used to figure out which index of a variable is being used.
|
|
static void replaceUsesOfBuiltinVar(Value *V, const APInt &AccumulatedOffset,
|
|
Function *ReplacementFunc,
|
|
GlobalVariable *GV) {
|
|
const DataLayout &DL = ReplacementFunc->getParent()->getDataLayout();
|
|
SmallVector<Instruction *, 4> InstsToRemove;
|
|
for (User *U : V->users()) {
|
|
if (auto *Cast = dyn_cast<CastInst>(U)) {
|
|
replaceUsesOfBuiltinVar(Cast, AccumulatedOffset, ReplacementFunc, GV);
|
|
InstsToRemove.push_back(Cast);
|
|
} else if (auto *GEP = dyn_cast<GEPOperator>(U)) {
|
|
APInt NewOffset = AccumulatedOffset.sextOrTrunc(
|
|
DL.getIndexSizeInBits(GEP->getPointerAddressSpace()));
|
|
if (!GEP->accumulateConstantOffset(DL, NewOffset))
|
|
llvm_unreachable("Illegal GEP of a SPIR-V builtin variable");
|
|
replaceUsesOfBuiltinVar(GEP, NewOffset, ReplacementFunc, GV);
|
|
if (auto *AsInst = dyn_cast<Instruction>(U))
|
|
InstsToRemove.push_back(AsInst);
|
|
} else if (auto *Load = dyn_cast<LoadInst>(U)) {
|
|
// Figure out which index the accumulated offset corresponds to. If we
|
|
// have a weird offset (e.g., trying to load byte 7), bail out.
|
|
APInt Index;
|
|
Type *ScalarTy = ReplacementFunc->getReturnType();
|
|
if (!ScalarTy->isIntegerTy(1)) {
|
|
uint64_t Remainder;
|
|
APInt::udivrem(AccumulatedOffset, ScalarTy->getScalarSizeInBits() / 8,
|
|
Index, Remainder);
|
|
if (Remainder != 0)
|
|
llvm_unreachable("Illegal GEP of a SPIR-V builtin variable");
|
|
}
|
|
|
|
IRBuilder<> Builder(Load);
|
|
Value *Replacement;
|
|
if (ReplacementFunc->getFunctionType()->getNumParams() == 0) {
|
|
if (Load->getType() != ScalarTy)
|
|
llvm_unreachable("Illegal use of a SPIR-V builtin variable");
|
|
Replacement =
|
|
setAttrByCalledFunc(Builder.CreateCall(ReplacementFunc, {}));
|
|
} else {
|
|
// The function has an index parameter.
|
|
if (auto *VecTy = dyn_cast<FixedVectorType>(Load->getType())) {
|
|
// Reconstruct the original global variable vector because
|
|
// the load type may not match.
|
|
// global <3 x i64>, load <6 x i32>
|
|
VecTy = cast<FixedVectorType>(GV->getValueType());
|
|
if (!Index.isZero() || DL.getTypeSizeInBits(VecTy) !=
|
|
DL.getTypeSizeInBits(Load->getType()))
|
|
llvm_unreachable("Illegal use of a SPIR-V builtin variable");
|
|
Replacement = PoisonValue::get(VecTy);
|
|
for (unsigned I = 0; I < VecTy->getNumElements(); I++) {
|
|
Replacement = Builder.CreateInsertElement(
|
|
Replacement,
|
|
setAttrByCalledFunc(
|
|
Builder.CreateCall(ReplacementFunc, {Builder.getInt32(I)})),
|
|
Builder.getInt32(I));
|
|
}
|
|
// Insert a bitcast from the reconstructed vector to the load vector
|
|
// type in case they are different.
|
|
// Input:
|
|
// %1 = load <6 x i32>, ptr addrspace(1) %0, align 32
|
|
// %2 = extractelement <6 x i32> %1, i32 0
|
|
// %3 = add i32 5, %2
|
|
// Modified:
|
|
// < reconstruct global vector elements 0 and 1 >
|
|
// %2 = insertelement <3 x i64> %0, i64 %1, i32 2
|
|
// %3 = bitcast <3 x i64> %2 to <6 x i32>
|
|
// %4 = extractelement <6 x i32> %3, i32 0
|
|
// %5 = add i32 5, %4
|
|
Replacement = Builder.CreateBitCast(Replacement, Load->getType());
|
|
} else if (Load->getType() == ScalarTy) {
|
|
Replacement = setAttrByCalledFunc(Builder.CreateCall(
|
|
ReplacementFunc, {Builder.getInt32(Index.getZExtValue())}));
|
|
} else {
|
|
llvm_unreachable("Illegal load type of a SPIR-V builtin variable");
|
|
}
|
|
}
|
|
Load->replaceAllUsesWith(Replacement);
|
|
InstsToRemove.push_back(Load);
|
|
} else {
|
|
llvm_unreachable("Illegal use of a SPIR-V builtin variable");
|
|
}
|
|
}
|
|
|
|
for (Instruction *I : InstsToRemove)
|
|
I->eraseFromParent();
|
|
}
|
|
|
|
bool lowerBuiltinVariableToCall(GlobalVariable *GV,
|
|
SPIRVBuiltinVariableKind Kind) {
|
|
// There might be dead constant users of GV (for example, SPIRVLowerConstExpr
|
|
// replaces ConstExpr uses but those ConstExprs are not deleted, since LLVM
|
|
// constants are created on demand as needed and never deleted).
|
|
// Remove them first!
|
|
GV->removeDeadConstantUsers();
|
|
|
|
Module *M = GV->getParent();
|
|
LLVMContext &C = M->getContext();
|
|
std::string FuncName = GV->getName().str();
|
|
Type *GVTy = GV->getValueType();
|
|
Type *ReturnTy = GVTy;
|
|
// Some SPIR-V builtin variables are translated to a function with an index
|
|
// argument.
|
|
bool HasIndexArg =
|
|
ReturnTy->isVectorTy() &&
|
|
!(BuiltInSubgroupEqMask <= Kind && Kind <= BuiltInSubgroupLtMask);
|
|
if (HasIndexArg)
|
|
ReturnTy = cast<VectorType>(ReturnTy)->getElementType();
|
|
std::vector<Type *> ArgTy;
|
|
if (HasIndexArg)
|
|
ArgTy.push_back(Type::getInt32Ty(C));
|
|
std::string MangledName;
|
|
mangleOpenClBuiltin(FuncName, ArgTy, MangledName);
|
|
Function *Func = M->getFunction(MangledName);
|
|
if (!Func) {
|
|
FunctionType *FT = FunctionType::get(ReturnTy, ArgTy, false);
|
|
Func = Function::Create(FT, GlobalValue::ExternalLinkage, MangledName, M);
|
|
Func->setCallingConv(CallingConv::SPIR_FUNC);
|
|
Func->addFnAttr(Attribute::NoUnwind);
|
|
Func->addFnAttr(Attribute::WillReturn);
|
|
Func->setDoesNotAccessMemory();
|
|
}
|
|
|
|
replaceUsesOfBuiltinVar(GV, APInt(64, 0), Func, GV);
|
|
return true;
|
|
}
|
|
|
|
bool lowerBuiltinVariablesToCalls(Module *M) {
|
|
std::vector<GlobalVariable *> WorkList;
|
|
for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) {
|
|
SPIRVBuiltinVariableKind Kind;
|
|
if (!isSPIRVBuiltinVariable(&(*I), &Kind))
|
|
continue;
|
|
if (!lowerBuiltinVariableToCall(&(*I), Kind))
|
|
return false;
|
|
WorkList.push_back(&(*I));
|
|
}
|
|
for (auto &I : WorkList) {
|
|
I->eraseFromParent();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Transforms SPV-IR work-item builtin calls to SPIRV builtin variables.
|
|
/// e.g.
|
|
/// SPV-IR: @_Z33__spirv_BuiltInGlobalInvocationIdi(i)
|
|
/// is transformed as:
|
|
/// x = load GlobalInvocationId; extract x, i
|
|
/// e.g.
|
|
/// SPV-IR: @_Z22__spirv_BuiltInWorkDim()
|
|
/// is transformed as:
|
|
/// load WorkDim
|
|
bool lowerBuiltinCallsToVariables(Module *M) {
|
|
LLVM_DEBUG(dbgs() << "Enter lowerBuiltinCallsToVariables\n");
|
|
// Store instructions and functions that need to be removed.
|
|
SmallVector<Value *, 16> ToRemove;
|
|
for (auto &F : *M) {
|
|
// Builtins should be declaration only.
|
|
if (!F.isDeclaration())
|
|
continue;
|
|
StringRef DemangledName;
|
|
if (!oclIsBuiltin(F.getName(), DemangledName))
|
|
continue;
|
|
LLVM_DEBUG(dbgs() << "Function demangled name: " << DemangledName << '\n');
|
|
SmallVector<StringRef, 2> Postfix;
|
|
// Deprefix "__spirv_"
|
|
StringRef Name = dePrefixSPIRVName(DemangledName, Postfix);
|
|
// Lookup SPIRV Builtin map.
|
|
if (!SPIRVBuiltInNameMap::rfind(Name.str(), nullptr))
|
|
continue;
|
|
std::string BuiltinVarName = DemangledName.str();
|
|
LLVM_DEBUG(dbgs() << "builtin variable name: " << BuiltinVarName << '\n');
|
|
bool IsVec = F.getFunctionType()->getNumParams() > 0;
|
|
Type *GVType =
|
|
IsVec ? FixedVectorType::get(F.getReturnType(), 3) : F.getReturnType();
|
|
GlobalVariable *BV = nullptr;
|
|
// Consider the following LLVM IR:
|
|
// @__spirv_BuiltInLocalInvocationId = <Global constant>
|
|
// .....
|
|
// define spir_kernel void @kernel1(....) {
|
|
// %3 = tail call i64 @_Z12get_local_idj(i32 0)
|
|
// .....
|
|
// return void
|
|
// }
|
|
// During the OCLToSPIRV pass, the opencl call will get lowered to
|
|
// yet another global variable with the name
|
|
// '@__spirv_BuiltInLocalInvocationId'. In such a case, we would want to
|
|
// create only a single global variable with this name.
|
|
if (GlobalVariable *GV = M->getGlobalVariable(BuiltinVarName))
|
|
BV = GV;
|
|
else
|
|
BV = new GlobalVariable(*M, GVType, /*isConstant=*/true,
|
|
GlobalValue::ExternalLinkage, nullptr,
|
|
BuiltinVarName, 0, GlobalVariable::NotThreadLocal,
|
|
SPIRAS_Input);
|
|
for (auto *U : F.users()) {
|
|
auto *CI = dyn_cast<CallInst>(U);
|
|
assert(CI && "invalid instruction");
|
|
IRBuilder<> Builder(CI);
|
|
Value *NewValue = Builder.CreateLoad(GVType, BV);
|
|
LLVM_DEBUG(dbgs() << "Transform: " << *CI << " => " << *NewValue << '\n');
|
|
if (IsVec) {
|
|
auto *GVVecTy = cast<FixedVectorType>(GVType);
|
|
ConstantInt *Bound = Builder.getInt32(GVVecTy->getNumElements());
|
|
// Create a select on the index first, to avoid undefined behaviour
|
|
// due to exceeding the vector size by the extractelement.
|
|
Value *IndexCmp = Builder.CreateICmpULT(CI->getArgOperand(0), Bound);
|
|
Constant *ZeroIndex =
|
|
ConstantInt::get(CI->getArgOperand(0)->getType(), 0);
|
|
Value *ExtractIndex =
|
|
Builder.CreateSelect(IndexCmp, CI->getArgOperand(0), ZeroIndex);
|
|
|
|
// Extract from builtin variable.
|
|
NewValue = Builder.CreateExtractElement(NewValue, ExtractIndex);
|
|
|
|
// Clamp to out-of-range value.
|
|
Constant *OutOfRangeVal = ConstantInt::get(
|
|
F.getReturnType(), getBuiltinOutOfRangeValue(BuiltinVarName));
|
|
NewValue = Builder.CreateSelect(IndexCmp, NewValue, OutOfRangeVal);
|
|
LLVM_DEBUG(dbgs() << *NewValue << '\n');
|
|
}
|
|
NewValue->takeName(CI);
|
|
CI->replaceAllUsesWith(NewValue);
|
|
ToRemove.push_back(CI);
|
|
}
|
|
ToRemove.push_back(&F);
|
|
}
|
|
for (auto *V : ToRemove) {
|
|
if (auto *I = dyn_cast<Instruction>(V))
|
|
I->eraseFromParent();
|
|
else if (auto *F = dyn_cast<Function>(V))
|
|
F->eraseFromParent();
|
|
else
|
|
llvm_unreachable("Unexpected value to remove!");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool lowerBuiltins(SPIRVModule *BM, Module *M) {
|
|
auto Format = BM->getBuiltinFormat();
|
|
if (Format == BuiltinFormat::Function && !lowerBuiltinVariablesToCalls(M))
|
|
return false;
|
|
if (Format == BuiltinFormat::Global && !lowerBuiltinCallsToVariables(M))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool postProcessBuiltinReturningStruct(Function *F) {
|
|
Module *M = F->getParent();
|
|
LLVMContext *Context = &M->getContext();
|
|
std::string Name = F->getName().str();
|
|
F->setName(Name + ".old");
|
|
SmallVector<Instruction *, 32> InstToRemove;
|
|
for (auto *U : F->users()) {
|
|
if (auto *CI = dyn_cast<CallInst>(U)) {
|
|
IRBuilder<> Builder(CI->getParent());
|
|
Builder.SetInsertPoint(CI);
|
|
SmallVector<User *> Users(CI->users());
|
|
Value *A = nullptr;
|
|
StoreInst *SI = nullptr;
|
|
for (auto *U : Users) {
|
|
if ((SI = dyn_cast<StoreInst>(U)) != nullptr) {
|
|
A = SI->getPointerOperand();
|
|
InstToRemove.push_back(SI);
|
|
break;
|
|
}
|
|
}
|
|
if (!A) {
|
|
A = Builder.CreateAlloca(F->getReturnType());
|
|
}
|
|
SmallVector<Type *> ArgTys;
|
|
getFunctionTypeParameterTypes(F->getFunctionType(), ArgTys);
|
|
ArgTys.insert(ArgTys.begin(), A->getType());
|
|
auto *NewF =
|
|
getOrCreateFunction(M, Type::getVoidTy(*Context), ArgTys, Name);
|
|
auto SretAttr = Attribute::get(*Context, Attribute::AttrKind::StructRet,
|
|
F->getReturnType());
|
|
NewF->addParamAttr(0, SretAttr);
|
|
NewF->setCallingConv(F->getCallingConv());
|
|
auto Args = getArguments(CI);
|
|
Args.insert(Args.begin(), A);
|
|
CallInst *NewCI = Builder.CreateCall(NewF, Args, CI->getName());
|
|
NewCI->addParamAttr(0, SretAttr);
|
|
NewCI->setCallingConv(CI->getCallingConv());
|
|
SmallVector<User *, 32> UsersToReplace;
|
|
for (auto *U : Users)
|
|
if (U != SI)
|
|
UsersToReplace.push_back(U);
|
|
if (UsersToReplace.size() > 0) {
|
|
auto *LI = Builder.CreateLoad(F->getReturnType(), A);
|
|
for (auto *U : UsersToReplace)
|
|
U->replaceUsesOfWith(CI, LI);
|
|
}
|
|
InstToRemove.push_back(CI);
|
|
}
|
|
}
|
|
for (auto *Inst : InstToRemove) {
|
|
Inst->dropAllReferences();
|
|
Inst->eraseFromParent();
|
|
}
|
|
F->dropAllReferences();
|
|
F->eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
bool postProcessBuiltinWithArrayArguments(Function *F,
|
|
StringRef DemangledName) {
|
|
LLVM_DEBUG(dbgs() << "[postProcessOCLBuiltinWithArrayArguments] " << *F
|
|
<< '\n');
|
|
auto Attrs = F->getAttributes();
|
|
auto Name = F->getName();
|
|
mutateFunction(
|
|
F,
|
|
[=](CallInst *CI, std::vector<Value *> &Args) {
|
|
auto FBegin = CI->getFunction()->begin()->getFirstInsertionPt();
|
|
for (auto &I : Args) {
|
|
auto *T = I->getType();
|
|
if (!T->isArrayTy())
|
|
continue;
|
|
auto *Alloca = new AllocaInst(
|
|
T, F->getParent()->getDataLayout().getAllocaAddrSpace(), "",
|
|
FBegin);
|
|
new StoreInst(I, Alloca, false, CI->getIterator());
|
|
auto *Zero =
|
|
ConstantInt::getNullValue(Type::getInt32Ty(T->getContext()));
|
|
Value *Index[] = {Zero, Zero};
|
|
I = GetElementPtrInst::CreateInBounds(T, Alloca, Index, "",
|
|
CI->getIterator());
|
|
}
|
|
return Name.str();
|
|
},
|
|
nullptr, &Attrs);
|
|
return true;
|
|
}
|
|
|
|
bool postProcessBuiltinsReturningStruct(Module *M, bool IsCpp) {
|
|
StringRef DemangledName;
|
|
// postProcessBuiltinReturningStruct may remove some functions from the
|
|
// module, so use make_early_inc_range
|
|
for (auto &F : make_early_inc_range(M->functions())) {
|
|
if (F.hasName() && F.isDeclaration()) {
|
|
LLVM_DEBUG(dbgs() << "[postProcess sret] " << F << '\n');
|
|
if (F.getReturnType()->isStructTy() &&
|
|
oclIsBuiltin(F.getName(), DemangledName, IsCpp)) {
|
|
if (!postProcessBuiltinReturningStruct(&F))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool postProcessBuiltinsWithArrayArguments(Module *M, bool IsCpp) {
|
|
StringRef DemangledName;
|
|
// postProcessBuiltinWithArrayArguments may remove some functions from the
|
|
// module, so use make_early_inc_range
|
|
for (auto &F : make_early_inc_range(M->functions())) {
|
|
if (F.hasName() && F.isDeclaration()) {
|
|
LLVM_DEBUG(dbgs() << "[postProcess array arg] " << F << '\n');
|
|
if (hasArrayArg(&F) && oclIsBuiltin(F.getName(), DemangledName, IsCpp))
|
|
if (!postProcessBuiltinWithArrayArguments(&F, DemangledName))
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace SPIRV
|
|
|
|
namespace {
|
|
class SPIRVFriendlyIRMangleInfo : public BuiltinFuncMangleInfo {
|
|
public:
|
|
SPIRVFriendlyIRMangleInfo(spv::Op OC, ArrayRef<Type *> ArgTys,
|
|
ArrayRef<SPIRVValue *> Ops)
|
|
: OC(OC), ArgTys(ArgTys), Ops(Ops) {}
|
|
|
|
void init(StringRef UniqUnmangledName) override {
|
|
UnmangledName = UniqUnmangledName.str();
|
|
switch (static_cast<unsigned>(OC)) {
|
|
case OpConvertUToF:
|
|
case OpUConvert:
|
|
case OpSatConvertUToS:
|
|
// Treat all arguments as unsigned
|
|
addUnsignedArg(-1);
|
|
break;
|
|
case OpSubgroupShuffleINTEL:
|
|
case OpSubgroupShuffleXorINTEL:
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpSubgroupShuffleDownINTEL:
|
|
case OpSubgroupShuffleUpINTEL:
|
|
addUnsignedArg(2);
|
|
break;
|
|
case OpSubgroupBlockWriteINTEL:
|
|
addUnsignedArg(0);
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpSubgroupImageBlockWriteINTEL:
|
|
addUnsignedArg(2);
|
|
break;
|
|
case OpSubgroupBlockReadINTEL:
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
addUnsignedArg(0);
|
|
break;
|
|
case OpSubgroupBlockPrefetchINTEL:
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
addUnsignedArg(0);
|
|
addUnsignedArg(1);
|
|
addUnsignedArg(2); // optional Memory Operands bitmask
|
|
break;
|
|
case OpAtomicUMax:
|
|
case OpAtomicUMin:
|
|
addUnsignedArg(0);
|
|
addUnsignedArg(3);
|
|
break;
|
|
case OpGroupAsyncCopy:
|
|
setArgAttr(2, SPIR::ATTR_CONST);
|
|
addUnsignedArg(3);
|
|
addUnsignedArg(4);
|
|
break;
|
|
case OpGroupUMax:
|
|
case OpGroupUMin:
|
|
case OpGroupBroadcast:
|
|
case OpGroupNonUniformBroadcast:
|
|
case OpGroupNonUniformBallotBitCount:
|
|
case OpGroupNonUniformShuffle:
|
|
case OpGroupNonUniformShuffleXor:
|
|
case OpGroupNonUniformShuffleUp:
|
|
case OpGroupNonUniformShuffleDown:
|
|
addUnsignedArg(2);
|
|
break;
|
|
case OpGroupNonUniformRotateKHR:
|
|
addUnsignedArg(2);
|
|
if (ArgTys.size() == 4)
|
|
addUnsignedArg(3);
|
|
break;
|
|
case OpGroupNonUniformInverseBallot:
|
|
case OpGroupNonUniformBallotFindLSB:
|
|
case OpGroupNonUniformBallotFindMSB:
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpBitFieldSExtract:
|
|
case OpGroupNonUniformBallotBitExtract:
|
|
addUnsignedArg(1);
|
|
addUnsignedArg(2);
|
|
break;
|
|
case OpGroupNonUniformIAdd:
|
|
case OpGroupNonUniformFAdd:
|
|
case OpGroupNonUniformIMul:
|
|
case OpGroupNonUniformFMul:
|
|
case OpGroupNonUniformSMin:
|
|
case OpGroupNonUniformFMin:
|
|
case OpGroupNonUniformSMax:
|
|
case OpGroupNonUniformFMax:
|
|
case OpGroupNonUniformBitwiseAnd:
|
|
case OpGroupNonUniformBitwiseOr:
|
|
case OpGroupNonUniformBitwiseXor:
|
|
case OpGroupNonUniformLogicalAnd:
|
|
case OpGroupNonUniformLogicalOr:
|
|
case OpGroupNonUniformLogicalXor:
|
|
addUnsignedArg(3);
|
|
break;
|
|
case OpBitFieldInsert:
|
|
case OpGroupNonUniformUMax:
|
|
case OpGroupNonUniformUMin:
|
|
addUnsignedArg(2);
|
|
addUnsignedArg(3);
|
|
break;
|
|
case OpEnqueueMarker:
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpSubgroupAvcBmeInitializeINTEL:
|
|
addUnsignedArgs(0, 7);
|
|
break;
|
|
case OpSubgroupAvcFmeInitializeINTEL:
|
|
case OpSubgroupAvcSicConfigureIpeLumaINTEL:
|
|
addUnsignedArgs(0, 6);
|
|
break;
|
|
case OpSubgroupAvcImeAdjustRefOffsetINTEL:
|
|
addUnsignedArgs(1, 3);
|
|
break;
|
|
case OpSubgroupAvcImeGetBorderReachedINTEL:
|
|
case OpSubgroupAvcImeRefWindowSizeINTEL:
|
|
case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL:
|
|
case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL:
|
|
case OpSubgroupAvcImeSetWeightedSadINTEL:
|
|
case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL:
|
|
case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL:
|
|
case OpSubgroupAvcMceSetInterShapePenaltyINTEL:
|
|
case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL:
|
|
case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL:
|
|
case OpSubgroupAvcSicInitializeINTEL:
|
|
case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL:
|
|
case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL:
|
|
case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL:
|
|
case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL:
|
|
addUnsignedArg(0);
|
|
break;
|
|
case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL:
|
|
case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL:
|
|
case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL:
|
|
case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL:
|
|
case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL:
|
|
case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL:
|
|
case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL:
|
|
addUnsignedArgs(1, 2);
|
|
break;
|
|
case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL:
|
|
case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL:
|
|
case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL:
|
|
case OpSubgroupAvcImeSetSingleReferenceINTEL:
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpBitFieldUExtract:
|
|
case OpSubgroupAvcImeInitializeINTEL:
|
|
case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL:
|
|
case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL:
|
|
addUnsignedArgs(0, 2);
|
|
break;
|
|
case OpSubgroupAvcImeSetDualReferenceINTEL:
|
|
addUnsignedArg(2);
|
|
break;
|
|
case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL:
|
|
case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL:
|
|
case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL:
|
|
case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL:
|
|
case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL:
|
|
case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL:
|
|
case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL:
|
|
case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL:
|
|
case OpSubgroupAvcSicGetMotionVectorMaskINTEL:
|
|
addUnsignedArgs(0, 1);
|
|
break;
|
|
case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL:
|
|
addUnsignedArgs(0, 9);
|
|
break;
|
|
case OpSubgroupAvcSicConfigureSkcINTEL:
|
|
addUnsignedArgs(0, 4);
|
|
break;
|
|
case OpUDotKHR:
|
|
case OpUDotAccSatKHR:
|
|
addUnsignedArg(-1);
|
|
break;
|
|
case OpSUDotKHR:
|
|
case OpSUDotAccSatKHR:
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpImageWrite: {
|
|
size_t Idx = getImageOperandsIndex(OC);
|
|
if (Ops.size() > Idx) {
|
|
auto ImOp = static_cast<SPIRVConstant *>(Ops[Idx])->getZExtIntValue();
|
|
if (ImOp & ImageOperandsMask::ImageOperandsZeroExtendMask)
|
|
addUnsignedArg(2);
|
|
}
|
|
break;
|
|
}
|
|
case internal::OpConvertHandleToImageINTEL:
|
|
case internal::OpConvertHandleToSamplerINTEL:
|
|
case internal::OpConvertHandleToSampledImageINTEL:
|
|
addUnsignedArg(0);
|
|
break;
|
|
case OpGenericPtrMemSemantics:
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
break;
|
|
default:;
|
|
// No special handling is needed
|
|
}
|
|
}
|
|
|
|
private:
|
|
spv::Op OC;
|
|
ArrayRef<Type *> ArgTys;
|
|
ArrayRef<SPIRVValue *> Ops;
|
|
};
|
|
class OpenCLStdToSPIRVFriendlyIRMangleInfo : public BuiltinFuncMangleInfo {
|
|
public:
|
|
OpenCLStdToSPIRVFriendlyIRMangleInfo(OCLExtOpKind ExtOpId,
|
|
ArrayRef<Type *> ArgTys, Type *RetTy)
|
|
: ExtOpId(ExtOpId), ArgTys(ArgTys), RetTy(RetTy) {
|
|
|
|
std::string Postfix = "";
|
|
if (needRetTypePostfix())
|
|
Postfix = kSPIRVPostfix::Divider + getPostfixForReturnType(RetTy, true);
|
|
|
|
UnmangledName = getSPIRVExtFuncName(SPIRVEIS_OpenCL, ExtOpId, Postfix);
|
|
}
|
|
|
|
bool needRetTypePostfix() {
|
|
switch (ExtOpId) {
|
|
case OpenCLLIB::Vload_half:
|
|
case OpenCLLIB::Vload_halfn:
|
|
case OpenCLLIB::Vloada_halfn:
|
|
case OpenCLLIB::Vloadn:
|
|
return true;
|
|
case OpenCLLIB::Nan:
|
|
// Only add return type mangling for bfloat16 to disambiguate from half
|
|
// (both are represented as i16 in LLVM). Float and half use traditional
|
|
// naming for backward compatibility.
|
|
return RetTy->getScalarType()->isBFloatTy();
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void init(StringRef) override {
|
|
switch (ExtOpId) {
|
|
case OpenCLLIB::UAbs:
|
|
case OpenCLLIB::UAbs_diff:
|
|
case OpenCLLIB::UAdd_sat:
|
|
case OpenCLLIB::UHadd:
|
|
case OpenCLLIB::URhadd:
|
|
case OpenCLLIB::UClamp:
|
|
case OpenCLLIB::UMad_hi:
|
|
case OpenCLLIB::UMad_sat:
|
|
case OpenCLLIB::UMax:
|
|
case OpenCLLIB::UMin:
|
|
case OpenCLLIB::UMul_hi:
|
|
case OpenCLLIB::USub_sat:
|
|
case OpenCLLIB::U_Upsample:
|
|
case OpenCLLIB::UMad24:
|
|
case OpenCLLIB::UMul24:
|
|
// Treat all arguments as unsigned
|
|
addUnsignedArg(-1);
|
|
break;
|
|
case OpenCLLIB::S_Upsample:
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpenCLLIB::Nan:
|
|
addUnsignedArg(0);
|
|
break;
|
|
case OpenCLLIB::Prefetch:
|
|
setArgAttr(0, SPIR::ATTR_CONST);
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpenCLLIB::Shuffle:
|
|
addUnsignedArg(1);
|
|
break;
|
|
case OpenCLLIB::Shuffle2:
|
|
addUnsignedArg(2);
|
|
break;
|
|
case OpenCLLIB::Vloadn:
|
|
case OpenCLLIB::Vload_half:
|
|
case OpenCLLIB::Vload_halfn:
|
|
case OpenCLLIB::Vloada_halfn:
|
|
addUnsignedArg(0);
|
|
setArgAttr(1, SPIR::ATTR_CONST);
|
|
break;
|
|
case OpenCLLIB::Vstoren:
|
|
case OpenCLLIB::Vstore_half:
|
|
case OpenCLLIB::Vstore_half_r:
|
|
case OpenCLLIB::Vstore_halfn:
|
|
case OpenCLLIB::Vstore_halfn_r:
|
|
case OpenCLLIB::Vstorea_halfn:
|
|
case OpenCLLIB::Vstorea_halfn_r:
|
|
addUnsignedArg(1);
|
|
break;
|
|
default:;
|
|
// No special handling is needed
|
|
}
|
|
}
|
|
|
|
private:
|
|
OCLExtOpKind ExtOpId;
|
|
ArrayRef<Type *> ArgTys;
|
|
Type *RetTy;
|
|
};
|
|
} // namespace
|
|
|
|
namespace SPIRV {
|
|
std::string getSPIRVFriendlyIRFunctionName(OCLExtOpKind ExtOpId,
|
|
ArrayRef<Type *> ArgTys,
|
|
Type *RetTy) {
|
|
OpenCLStdToSPIRVFriendlyIRMangleInfo MangleInfo(ExtOpId, ArgTys, RetTy);
|
|
return mangleBuiltin(MangleInfo.getUnmangledName(), ArgTys, &MangleInfo);
|
|
}
|
|
|
|
std::string getSPIRVFriendlyIRFunctionName(const std::string &UniqName,
|
|
spv::Op OC, ArrayRef<Type *> ArgTys,
|
|
ArrayRef<SPIRVValue *> Ops) {
|
|
SPIRVFriendlyIRMangleInfo MangleInfo(OC, ArgTys, Ops);
|
|
return mangleBuiltin(UniqName, ArgTys, &MangleInfo);
|
|
}
|
|
|
|
} // namespace SPIRV
|