598 lines
20 KiB
C++
598 lines
20 KiB
C++
//===- 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);
|
|
}
|