//===- OCLUtil.cpp - OCL Utilities ----------------------------------------===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal with the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimers in the documentation
// and/or other materials provided with the distribution.
// Neither the names of Advanced Micro Devices, Inc., nor the names of its
// contributors may be used to endorse or promote products derived from this
// Software without specific prior written permission.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
// THE SOFTWARE.
//
//===----------------------------------------------------------------------===//
//
// This file implements OCL utility functions.
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "oclutil"
#include "SPIRVInternal.h"
#include "OCLUtil.h"
#include "SPIRVEntry.h"
#include "SPIRVFunction.h"
#include "SPIRVInstruction.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Pass.h"
#include "llvm/PassSupport.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace SPIRV;
namespace OCLUtil {
#ifndef SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
#define SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE SPIRAS_Private
#endif
#ifndef SPIRV_QUEUE_T_ADDR_SPACE
#define SPIRV_QUEUE_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
#endif
#ifndef SPIRV_EVENT_T_ADDR_SPACE
#define SPIRV_EVENT_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
#endif
#ifndef SPIRV_CLK_EVENT_T_ADDR_SPACE
#define SPIRV_CLK_EVENT_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
#endif
#ifndef SPIRV_RESERVE_ID_T_ADDR_SPACE
#define SPIRV_RESERVE_ID_T_ADDR_SPACE SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE
#endif
// Excerpt from SPIR 2.0 spec.:
// Pipe objects are represented using pointers to the opaque %opencl.pipe LLVM structure type
// which reside in the global address space.
#ifndef SPIRV_PIPE_ADDR_SPACE
#define SPIRV_PIPE_ADDR_SPACE SPIRAS_Global
#endif
// Excerpt from SPIR 2.0 spec.:
// Note: Images data types reside in global memory and hence should be marked as such in the
// "kernel arg addr space" metadata.
#ifndef SPIRV_IMAGE_ADDR_SPACE
#define SPIRV_IMAGE_ADDR_SPACE SPIRAS_Global
#endif
///////////////////////////////////////////////////////////////////////////////
//
// Functions for getting builtin call info
//
///////////////////////////////////////////////////////////////////////////////
AtomicWorkItemFenceLiterals getAtomicWorkItemFenceLiterals(CallInst* CI) {
return std::make_tuple(getArgAsInt(CI, 0),
static_cast<OCLMemOrderKind>(getArgAsInt(CI, 1)),
static_cast<OCLScopeKind>(getArgAsInt(CI, 2)));
}
size_t getAtomicBuiltinNumMemoryOrderArgs(StringRef Name) {
if (Name.startswith("atomic_compare_exchange"))
return 2;
return 1;
}
BarrierLiterals getBarrierLiterals(CallInst* CI){
auto N = CI->getNumArgOperands();
assert (N == 1 || N == 2);
std::string DemangledName;
if (!oclIsBuiltin(CI->getCalledFunction()->getName(), &DemangledName)) {
assert(0 && "call must a builtin (work_group_barrier or sub_group_barrier)");
}
OCLScopeKind scope = OCLMS_work_group;
if (DemangledName == kOCLBuiltinName::SubGroupBarrier) {
scope = OCLMS_sub_group;
}
return std::make_tuple(getArgAsInt(CI, 0),
N == 1 ? OCLMS_work_group : static_cast<OCLScopeKind>(getArgAsInt(CI, 1)),
scope);
}
unsigned
getExtOp(StringRef OrigName, const std::string &GivenDemangledName) {
std::string DemangledName = GivenDemangledName;
if (!oclIsBuiltin(OrigName, DemangledName.empty() ? &DemangledName : nullptr))
return ~0U;
DEBUG(dbgs() << "getExtOp: demangled name: " << DemangledName << '\n');
OCLExtOpKind EOC;
bool Found = OCLExtOpMap::rfind(DemangledName, &EOC);
if (!Found) {
std::string Prefix;
switch (LastFuncParamType(OrigName)) {
case ParamType::UNSIGNED:
Prefix = "u_";
break;
case ParamType::SIGNED:
Prefix = "s_";
break;
case ParamType::FLOAT:
Prefix = "f";
break;
default:
llvm_unreachable("unknown mangling!");
}
Found = OCLExtOpMap::rfind(Prefix + DemangledName, &EOC);
}
if (Found)
return EOC;
else
return ~0U;
}
std::unique_ptr<SPIRVEntry>
getSPIRVInst(const OCLBuiltinTransInfo &Info) {
Op OC = OpNop;
unsigned ExtOp = ~0U;
SPIRVEntry *Entry = nullptr;
if (OCLSPIRVBuiltinMap::find(Info.UniqName, &OC))
Entry = SPIRVEntry::create(OC);
else if ((ExtOp = getExtOp(Info.MangledName, Info.UniqName)) != ~0U)
Entry = static_cast<SPIRVEntry*>(
SPIRVEntry::create_unique(SPIRVEIS_OpenCL, ExtOp).get());
return std::unique_ptr<SPIRVEntry>(Entry);
}
///////////////////////////////////////////////////////////////////////////////
//
// Functions for getting module info
//
///////////////////////////////////////////////////////////////////////////////
unsigned
encodeOCLVer(unsigned short Major,
unsigned char Minor, unsigned char Rev) {
return (Major * 100 + Minor) * 1000 + Rev;
}
std::tuple<unsigned short, unsigned char, unsigned char>
decodeOCLVer(unsigned Ver) {
unsigned short Major = Ver / 100000;
unsigned char Minor = (Ver % 100000) / 1000;
unsigned char Rev = Ver % 1000;
return std::make_tuple(Major, Minor, Rev);
}
unsigned getOCLVersion(Module *M, bool AllowMulti) {
NamedMDNode *NamedMD = M->getNamedMetadata(kSPIR2MD::OCLVer);
if (!NamedMD)
return 0;
assert (NamedMD->getNumOperands() > 0 && "Invalid SPIR");
if (!AllowMulti && NamedMD->getNumOperands() != 1)
report_fatal_error("Multiple OCL version metadata not allowed");
// If the module was linked with another module, there may be multiple
// operands.
auto getVer = [=](unsigned I) {
auto MD = NamedMD->getOperand(I);
return std::make_pair(getMDOperandAsInt(MD, 0), getMDOperandAsInt(MD, 1));
};
auto Ver = getVer(0);
for (unsigned I = 1, E = NamedMD->getNumOperands(); I != E; ++I)
if (Ver != getVer(I))
report_fatal_error("OCL version mismatch");
return encodeOCLVer(Ver.first, Ver.second, 0);
}
void
decodeMDNode(MDNode* N, unsigned& X, unsigned& Y, unsigned& Z) {
if (N == NULL)
return;
X = getMDOperandAsInt(N, 1);
Y = getMDOperandAsInt(N, 2);
Z = getMDOperandAsInt(N, 3);
}
/// Encode LLVM type by SPIR-V execution mode VecTypeHint
unsigned
encodeVecTypeHint(Type *Ty){
if (Ty->isHalfTy())
return 4;
if (Ty->isFloatTy())
return 5;
if (Ty->isDoubleTy())
return 6;
if (IntegerType* intTy = dyn_cast<IntegerType>(Ty)) {
switch (intTy->getIntegerBitWidth()) {
case 8:
return 0;
case 16:
return 1;
case 32:
return 2;
case 64:
return 3;
default:
llvm_unreachable("invalid integer type");
}
}
if (VectorType* VecTy = dyn_cast<VectorType>(Ty)) {
Type* EleTy = VecTy->getElementType();
unsigned Size = VecTy->getVectorNumElements();
return Size << 16 | encodeVecTypeHint(EleTy);
}
llvm_unreachable("invalid type");
}
Type *
decodeVecTypeHint(LLVMContext &C, unsigned code) {
unsigned VecWidth = code >> 16;
unsigned Scalar = code & 0xFFFF;
Type *ST = nullptr;
switch(Scalar) {
case 0:
case 1:
case 2:
case 3:
ST = IntegerType::get(C, 1 << (3 + Scalar));
break;
case 4:
ST = Type::getHalfTy(C);
break;
case 5:
ST = Type::getFloatTy(C);
break;
case 6:
ST = Type::getDoubleTy(C);
break;
default:
llvm_unreachable("Invalid vec type hint");
}
if (VecWidth < 1)
return ST;
return VectorType::get(ST, VecWidth);
}
unsigned
transVecTypeHint(MDNode* Node) {
return encodeVecTypeHint(getMDOperandAsType(Node, 1));
}
SPIRAddressSpace
getOCLOpaqueTypeAddrSpace(Op OpCode) {
switch (OpCode) {
case OpTypeQueue:
return SPIRV_QUEUE_T_ADDR_SPACE;
case OpTypeEvent:
return SPIRV_EVENT_T_ADDR_SPACE;
case OpTypeDeviceEvent:
return SPIRV_CLK_EVENT_T_ADDR_SPACE;
case OpTypeReserveId:
return SPIRV_RESERVE_ID_T_ADDR_SPACE;
case OpTypePipe:
case OpTypePipeStorage:
return SPIRV_PIPE_ADDR_SPACE;
case OpTypeImage:
case OpTypeSampledImage:
return SPIRV_IMAGE_ADDR_SPACE;
default:
assert(false && "No address space is determined for some OCL type");
return SPIRV_OCL_SPECIAL_TYPES_DEFAULT_ADDR_SPACE;
}
}
static SPIR::TypeAttributeEnum
mapAddrSpaceEnums(SPIRAddressSpace addrspace)
{
switch (addrspace) {
case SPIRAS_Private:
return SPIR::ATTR_PRIVATE;
case SPIRAS_Global:
return SPIR::ATTR_GLOBAL;
case SPIRAS_Constant:
return SPIR::ATTR_CONSTANT;
case SPIRAS_Local:
return SPIR::ATTR_LOCAL;
case SPIRAS_Generic:
return SPIR::ATTR_GENERIC;
default:
llvm_unreachable("Invalid addrspace enum member");
}
}
SPIR::TypeAttributeEnum
getOCLOpaqueTypeAddrSpace(SPIR::TypePrimitiveEnum prim) {
switch (prim) {
case SPIR::PRIMITIVE_QUEUE_T:
return mapAddrSpaceEnums(SPIRV_QUEUE_T_ADDR_SPACE);
case SPIR::PRIMITIVE_EVENT_T:
return mapAddrSpaceEnums(SPIRV_EVENT_T_ADDR_SPACE);
case SPIR::PRIMITIVE_CLK_EVENT_T:
return mapAddrSpaceEnums(SPIRV_CLK_EVENT_T_ADDR_SPACE);
case SPIR::PRIMITIVE_RESERVE_ID_T:
return mapAddrSpaceEnums(SPIRV_RESERVE_ID_T_ADDR_SPACE);
case SPIR::PRIMITIVE_PIPE_T:
return mapAddrSpaceEnums(SPIRV_PIPE_ADDR_SPACE);
case SPIR::PRIMITIVE_IMAGE_1D_T:
case SPIR::PRIMITIVE_IMAGE_1D_ARRAY_T:
case SPIR::PRIMITIVE_IMAGE_1D_BUFFER_T:
case SPIR::PRIMITIVE_IMAGE_2D_T:
case SPIR::PRIMITIVE_IMAGE_2D_ARRAY_T:
case SPIR::PRIMITIVE_IMAGE_3D_T:
case SPIR::PRIMITIVE_IMAGE_2D_MSAA_T:
case SPIR::PRIMITIVE_IMAGE_2D_ARRAY_MSAA_T:
case SPIR::PRIMITIVE_IMAGE_2D_MSAA_DEPTH_T:
case SPIR::PRIMITIVE_IMAGE_2D_ARRAY_MSAA_DEPTH_T:
case SPIR::PRIMITIVE_IMAGE_2D_DEPTH_T:
case SPIR::PRIMITIVE_IMAGE_2D_ARRAY_DEPTH_T:
return mapAddrSpaceEnums(SPIRV_IMAGE_ADDR_SPACE);
default:
llvm_unreachable("No address space is determined for a SPIR primitive");
}
}
// Fetch type of invoke function passed to device execution built-ins
static FunctionType *
getBlockInvokeTy(Function * F, unsigned blockIdx) {
auto params = F->getFunctionType()->params();
PointerType * funcPtr = cast<PointerType>(params[blockIdx]);
return cast<FunctionType>(funcPtr->getElementType());
}
class OCLBuiltinFuncMangleInfo : public SPIRV::BuiltinFuncMangleInfo {
public:
OCLBuiltinFuncMangleInfo(Function * f) : F(f) {}
void init(const std::string &UniqName) {
UnmangledName = UniqName;
size_t Pos = std::string::npos;
if (UnmangledName.find("async_work_group") == 0) {
addUnsignedArg(-1);
setArgAttr(1, SPIR::ATTR_CONST);
} else if (UnmangledName.find("write_imageui") == 0)
addUnsignedArg(2);
else if (UnmangledName == "prefetch") {
addUnsignedArg(1);
setArgAttr(0, SPIR::ATTR_CONST);
} else if(UnmangledName == "get_kernel_work_group_size" ||
UnmangledName == "get_kernel_preferred_work_group_size_multiple") {
assert(F && "lack of necessary information");
const size_t blockArgIdx = 0;
FunctionType * InvokeTy = getBlockInvokeTy(F, blockArgIdx);
if(InvokeTy->getNumParams() > 1) setLocalArgBlock(blockArgIdx);
} else if (UnmangledName == "enqueue_kernel") {
assert(F && "lack of necessary information");
setEnumArg(1, SPIR::PRIMITIVE_KERNEL_ENQUEUE_FLAGS_T);
addUnsignedArg(3);
setArgAttr(4, SPIR::ATTR_CONST);
// If there are arguments other then block context then these are pointers
// to local memory so this built-in must be mangled accordingly.
const size_t blockArgIdx = 6;
FunctionType * InvokeTy = getBlockInvokeTy(F, blockArgIdx);
if(InvokeTy->getNumParams() > 1) {
setLocalArgBlock(blockArgIdx);
addUnsignedArg(blockArgIdx + 1);
setVarArg(blockArgIdx + 2);
}
} else if (UnmangledName.find("get_") == 0 ||
UnmangledName == "nan" ||
UnmangledName == "mem_fence" ||
UnmangledName.find("shuffle") == 0){
addUnsignedArg(-1);
if (UnmangledName.find(kOCLBuiltinName::GetFence) == 0){
setArgAttr(0, SPIR::ATTR_CONST);
addVoidPtrArg(0);
}
} else if (UnmangledName.find("barrier") == 0 ||
UnmangledName.find("work_group_barrier") == 0 ||
UnmangledName.find("sub_group_barrier") == 0) {
addUnsignedArg(0);
} else if (UnmangledName.find("atomic_work_item_fence") == 0) {
addUnsignedArg(0);
} else if (UnmangledName.find("atomic") == 0) {
setArgAttr(0, SPIR::ATTR_VOLATILE);
if (UnmangledName.find("atomic_umax") == 0 ||
UnmangledName.find("atomic_umin") == 0) {
addUnsignedArg(0);
addUnsignedArg(1);
UnmangledName.erase(7, 1);
} else if (UnmangledName.find("atomic_fetch_umin") == 0 ||
UnmangledName.find("atomic_fetch_umax") == 0) {
addUnsignedArg(0);
addUnsignedArg(1);
UnmangledName.erase(13, 1);
}
// Don't set atomic property to the first argument of 1.2 atomic built-ins.
if(UnmangledName.find("atomic_add") != 0 && UnmangledName.find("atomic_sub") != 0 &&
UnmangledName.find("atomic_xchg") != 0 && UnmangledName.find("atomic_inc") != 0 &&
UnmangledName.find("atomic_dec") != 0 && UnmangledName.find("atomic_cmpxchg") != 0 &&
UnmangledName.find("atomic_min") != 0 && UnmangledName.find("atomic_max") != 0 &&
UnmangledName.find("atomic_and") != 0 && UnmangledName.find("atomic_or") != 0 &&
UnmangledName.find("atomic_xor") != 0 && UnmangledName.find("atom_") != 0) {
addAtomicArg(0);
}
} else if (UnmangledName.find("uconvert_") == 0) {
addUnsignedArg(0);
UnmangledName.erase(0, 1);
} else if (UnmangledName.find("s_") == 0) {
UnmangledName.erase(0, 2);
} else if (UnmangledName.find("u_") == 0) {
addUnsignedArg(-1);
UnmangledName.erase(0, 2);
} else if (UnmangledName == "fclamp") {
UnmangledName.erase(0, 1);
} else if (UnmangledName == "read_pipe" || UnmangledName == "write_pipe") {
assert(F && "lack of necessary information");
// handle [read|write]pipe builtins (plus two i32 literal args
// required by SPIR 2.0 provisional specification):
if (F->getArgumentList().size() == 6) {
// with 4 arguments (plus two i32 literals):
// int read_pipe (read_only pipe gentype p, reserve_id_t reserve_id, uint index, gentype *ptr)
// int write_pipe (write_only pipe gentype p, reserve_id_t reserve_id, uint index, const gentype *ptr)
addUnsignedArg(2);
addVoidPtrArg(3);
addUnsignedArg(4);
addUnsignedArg(5);
} else if (F->getArgumentList().size() == 4) {
// with 2 arguments (plus two i32 literals):
// int read_pipe (read_only pipe gentype p, gentype *ptr)
// int write_pipe (write_only pipe gentype p, const gentype *ptr)
addVoidPtrArg(1);
addUnsignedArg(2);
addUnsignedArg(3);
} else {
llvm_unreachable("read/write pipe builtin with unexpected number of arguments");
}
} else if (UnmangledName.find("reserve_read_pipe") != std::string::npos ||
UnmangledName.find("reserve_write_pipe") != std::string::npos) {
// process [|work_group|sub_group]reserve[read|write]pipe builtins
addUnsignedArg(1);
addUnsignedArg(2);
addUnsignedArg(3);
} else if (UnmangledName.find("commit_read_pipe") != std::string::npos ||
UnmangledName.find("commit_write_pipe") != std::string::npos) {
// process [|work_group|sub_group]commit[read|write]pipe builtins
addUnsignedArg(2);
addUnsignedArg(3);
} else if (UnmangledName == "capture_event_profiling_info") {
addVoidPtrArg(2);
setEnumArg(1, SPIR::PRIMITIVE_CLK_PROFILING_INFO);
} else if (UnmangledName == "enqueue_marker") {
setArgAttr(2, SPIR::ATTR_CONST);
addUnsignedArg(1);
} else if (UnmangledName.find("vload") == 0) {
addUnsignedArg(0);
setArgAttr(1, SPIR::ATTR_CONST);
} else if (UnmangledName.find("vstore") == 0 ){
addUnsignedArg(1);
} else if (UnmangledName.find("ndrange_") == 0) {
addUnsignedArg(-1);
if (UnmangledName[8] == '2' || UnmangledName[8] == '3') {
setArgAttr(-1, SPIR::ATTR_CONST);
}
} else if ((Pos = UnmangledName.find("umax")) != std::string::npos ||
(Pos = UnmangledName.find("umin")) != std::string::npos) {
addUnsignedArg(-1);
UnmangledName.erase(Pos, 1);
} else if (UnmangledName.find("broadcast") != std::string::npos)
addUnsignedArg(-1);
else if (UnmangledName.find(kOCLBuiltinName::SampledReadImage) == 0) {
UnmangledName.erase(0, strlen(kOCLBuiltinName::Sampled));
addSamplerArg(1);
}
}
// Auxiliarry information, it is expected what it is relevant at the moment
// the init method is called.
Function * F; // SPIRV decorated function
};
CallInst *
mutateCallInstOCL(Module *M, CallInst *CI,
std::function<std::string (CallInst *, std::vector<Value *> &)>ArgMutate,
AttributeSet *Attrs) {
OCLBuiltinFuncMangleInfo BtnInfo(CI->getCalledFunction());
return mutateCallInst(M, CI, ArgMutate, &BtnInfo, Attrs);
}
Instruction *
mutateCallInstOCL(Module *M, CallInst *CI,
std::function<std::string (CallInst *, std::vector<Value *> &,
Type *&RetTy)> ArgMutate,
std::function<Instruction *(CallInst *)> RetMutate,
AttributeSet *Attrs) {
OCLBuiltinFuncMangleInfo BtnInfo(CI->getCalledFunction());
return mutateCallInst(M, CI, ArgMutate, RetMutate, &BtnInfo, Attrs);
}
void
mutateFunctionOCL(Function *F,
std::function<std::string (CallInst *, std::vector<Value *> &)>ArgMutate,
AttributeSet *Attrs) {
OCLBuiltinFuncMangleInfo BtnInfo(F);
return mutateFunction(F, ArgMutate, &BtnInfo, Attrs, false);
}
static std::pair<StringRef, StringRef>
getSrcAndDstElememntTypeName(BitCastInst* BIC) {
if (!BIC)
return std::pair<StringRef, StringRef>("", "");
Type *SrcTy = BIC->getSrcTy();
Type *DstTy = BIC->getDestTy();
if (SrcTy->isPointerTy())
SrcTy = SrcTy->getPointerElementType();
if (DstTy->isPointerTy())
DstTy = DstTy->getPointerElementType();
auto SrcST = dyn_cast<StructType>(SrcTy);
auto DstST = dyn_cast<StructType>(DstTy);
if (!DstST || !DstST->hasName() || !SrcST || !SrcST->hasName())
return std::pair<StringRef, StringRef>("", "");
return std::make_pair(SrcST->getName(), DstST->getName());
}
bool
isSamplerInitializer(Instruction *Inst) {
BitCastInst *BIC = dyn_cast<BitCastInst>(Inst);
auto Names = getSrcAndDstElememntTypeName(BIC);
if (Names.second == getSPIRVTypeName(kSPIRVTypeName::Sampler) &&
Names.first == getSPIRVTypeName(kSPIRVTypeName::ConstantSampler))
return true;
return false;
}
bool
isPipeStorageInitializer(Instruction *Inst) {
BitCastInst *BIC = dyn_cast<BitCastInst>(Inst);
auto Names = getSrcAndDstElememntTypeName(BIC);
if (Names.second == getSPIRVTypeName(kSPIRVTypeName::PipeStorage) &&
Names.first == getSPIRVTypeName(kSPIRVTypeName::ConstantPipeStorage))
return true;
return false;
}
bool
isSpecialTypeInitializer(Instruction* Inst) {
return isSamplerInitializer(Inst) || isPipeStorageInitializer(Inst);
}
} // namespace OCLUtil
void
llvm::MangleOpenCLBuiltin(const std::string &UniqName,
ArrayRef<Type*> ArgTypes, std::string &MangledName) {
OCLUtil::OCLBuiltinFuncMangleInfo BtnInfo(nullptr);
MangledName = SPIRV::mangleBuiltin(UniqName, ArgTypes, &BtnInfo);
}