Files
RedBear-OS/local/recipes/dev/spirv-llvm-translator/source/lib/SPIRV/SPIRVToOCL.cpp
T

1257 lines
44 KiB
C++

//===- SPIRVToOCL.cpp - Transform SPIR-V builtins to OCL builtins------===//
//
// 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 common transform of SPIR-V builtins to OCL builtins.
//
// Some of the visit functions are translations to OCL2.0 builtins, but they
// are currently used also for OCL1.2, so theirs implementations are placed
// in this pass as a common functionality for both versions.
//
//===----------------------------------------------------------------------===//
#include "SPIRVToOCL.h"
#include "llvm/IR/TypedPointerType.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/CommandLine.h"
#define DEBUG_TYPE "spvtocl"
namespace SPIRV {
void SPIRVToOCLBase::visitCallInst(CallInst &CI) {
LLVM_DEBUG(dbgs() << "[visistCallInst] " << CI << '\n');
auto *F = CI.getCalledFunction();
if (!F)
return;
OCLExtOpKind ExtOp;
if (isSPIRVOCLExtInst(&CI, &ExtOp)) {
switch (ExtOp) {
case OpenCLLIB::Vloadn:
case OpenCLLIB::Vloada_halfn:
case OpenCLLIB::Vload_halfn:
visitCallSPIRVVLoadn(&CI, ExtOp);
break;
case OpenCLLIB::Vstoren:
case OpenCLLIB::Vstore_halfn:
case OpenCLLIB::Vstorea_halfn:
case OpenCLLIB::Vstore_half_r:
case OpenCLLIB::Vstore_halfn_r:
case OpenCLLIB::Vstorea_halfn_r:
visitCallSPIRVVStore(&CI, ExtOp);
break;
case OpenCLLIB::Printf: {
// TODO: Lower the printf instruction with the non-constant address space
// format string to suitable for OpenCL representation
auto *PT = dyn_cast<PointerType>(CI.getOperand(0)->getType());
if (PT && PT->getAddressSpace() == SPIR::TypeAttributeEnum::ATTR_CONST)
visitCallSPIRVPrintf(&CI, ExtOp);
break;
}
default:
visitCallSPIRVOCLExt(&CI, ExtOp);
break;
}
return;
}
auto MangledName = F->getName();
StringRef DemangledName;
Op OC = OpNop;
SPIRVBuiltinVariableKind BuiltinKind = SPIRVBuiltinVariableKind::BuiltInMax;
if (!oclIsBuiltin(MangledName, DemangledName) ||
((OC = getSPIRVFuncOC(DemangledName)) == OpNop &&
!getSPIRVBuiltin(DemangledName.str(), BuiltinKind)))
return;
LLVM_DEBUG(dbgs() << "DemangledName = " << DemangledName.str() << '\n'
<< "OpCode = " << OC << '\n'
<< "BuiltinKind = " << BuiltinKind << '\n');
if (BuiltinKind != SPIRVBuiltinVariableKind::BuiltInMax) {
if (static_cast<uint32_t>(BuiltinKind) >=
internal::BuiltInSubDeviceIDINTEL &&
static_cast<uint32_t>(BuiltinKind) <=
internal::BuiltInGlobalHWThreadIDINTEL)
return;
visitCallSPIRVBuiltin(&CI, BuiltinKind);
return;
}
if (OC == OpImageQuerySize || OC == OpImageQuerySizeLod) {
visitCallSPIRVImageQuerySize(&CI);
return;
}
if (OC == OpMemoryBarrier) {
visitCallSPIRVMemoryBarrier(&CI);
return;
}
if (OC == OpControlBarrier) {
visitCallSPIRVControlBarrier(&CI);
}
if (isSplitBarrierINTELOpCode(OC)) {
visitCallSPIRVSplitBarrierINTEL(&CI, OC);
return;
}
if (isAtomicOpCode(OC)) {
visitCallSPIRVAtomicBuiltin(&CI, OC);
return;
}
if (isGroupOpCode(OC) || isGroupNonUniformOpcode(OC)) {
visitCallSPIRVGroupBuiltin(&CI, OC);
return;
}
if (isPipeOpCode(OC)) {
visitCallSPIRVPipeBuiltin(&CI, OC);
return;
}
if (isMediaBlockINTELOpcode(OC)) {
visitCallSPIRVImageMediaBlockBuiltin(&CI, OC);
return;
}
if (isIntelSubgroupOpCode(OC)) {
visitCallSPIRVSubgroupINTELBuiltIn(&CI, OC);
return;
}
if (isSubgroupAvcINTELEvaluateOpcode(OC)) {
visitCallSPIRVAvcINTELEvaluateBuiltIn(&CI, OC);
return;
}
if (isSubgroupAvcINTELInstructionOpCode(OC)) {
visitCallSPIRVAvcINTELInstructionBuiltin(&CI, OC);
return;
}
if (OC == OpBuildNDRange) {
visitCallBuildNDRangeBuiltIn(&CI, OC, DemangledName);
return;
}
if (OC == OpGenericCastToPtr) {
visitCallGenericCastToPtrBuiltIn(&CI, OC);
return;
}
if (OC == OpGenericCastToPtrExplicit) {
visitCallGenericCastToPtrExplicitBuiltIn(&CI, OC);
return;
}
if (isCvtOpCode(OC)) {
visitCallSPIRVCvtBuiltin(&CI, OC, DemangledName);
return;
}
if (OC == OpGroupAsyncCopy) {
visitCallAsyncWorkGroupCopy(&CI, OC);
return;
}
if (OC == OpGroupWaitEvents) {
visitCallGroupWaitEvents(&CI, OC);
return;
}
if (OC == OpImageSampleExplicitLod) {
visitCallSPIRVImageSampleExplicitLodBuiltIn(&CI, OC);
return;
}
if (OC == OpImageWrite) {
visitCallSPIRVImageWriteBuiltIn(&CI, OC);
return;
}
if (OC == OpImageRead) {
visitCallSPIRVImageReadBuiltIn(&CI, OC);
return;
}
if (OC == OpImageQueryOrder || OC == OpImageQueryFormat) {
visitCallSPIRVImageQueryBuiltIn(&CI, OC);
return;
}
if (OC == OpEnqueueKernel) {
visitCallSPIRVEnqueueKernel(&CI, OC);
return;
}
if (OC == OpGenericPtrMemSemantics) {
visitCallSPIRVGenericPtrMemSemantics(&CI);
return;
}
// Check if OC is OpenCL relational builtin except bitselect and select.
auto IsOclRelationalOp = [](Op OC) {
return isUnaryPredicateOpCode(OC) || OC == OpOrdered || OC == OpUnordered ||
OC == OpFOrdEqual || OC == OpFUnordNotEqual ||
OC == OpFOrdGreaterThan || OC == OpFOrdGreaterThanEqual ||
OC == OpFOrdLessThan || OC == OpFOrdLessThanEqual ||
OC == OpFOrdNotEqual;
};
if (IsOclRelationalOp(OC)) {
if (OC == OpAny || OC == OpAll)
visitCallSPIRVAnyAll(&CI, OC);
else
visitCallSPIRVRelational(&CI, OC);
return;
}
if (OC == OpReadClockKHR) {
visitCallSPIRVReadClockKHR(&CI);
return;
}
if (OC == OpConvertFToBF16INTEL || OC == OpConvertBF16ToFINTEL) {
visitCallSPIRVBFloat16Conversions(&CI, OC);
return;
}
if (OC == OpSDot || OC == OpUDot || OC == OpSUDot || OC == OpSDotAccSat ||
OC == OpUDotAccSat || OC == OpSUDotAccSat) {
visitCallSPIRVDot(&CI, OC, DemangledName);
return;
}
if (OCLSPIRVBuiltinMap::rfind(OC))
visitCallSPIRVBuiltin(&CI, OC);
}
void SPIRVToOCLBase::visitCastInst(CastInst &Cast) {
if (!isa<ZExtInst>(Cast) && !isa<SExtInst>(Cast) && !isa<TruncInst>(Cast) &&
!isa<FPTruncInst>(Cast) && !isa<FPExtInst>(Cast) &&
!isa<FPToUIInst>(Cast) && !isa<FPToSIInst>(Cast) &&
!isa<UIToFPInst>(Cast) && !isa<SIToFPInst>(Cast))
return;
Type const *SrcTy = Cast.getSrcTy();
Type *DstVecTy = Cast.getDestTy();
// Leave scalar casts as is. Skip boolean vector casts becase there
// are no suitable OCL built-ins.
if (!DstVecTy->isVectorTy() || SrcTy->getScalarSizeInBits() == 1 ||
DstVecTy->getScalarSizeInBits() == 1)
return;
// We don't have OpenCL builtins for 4-bit conversions.
if (DstVecTy->getScalarSizeInBits() == 4 || SrcTy->getScalarSizeInBits() == 4)
return;
// Assemble built-in name -> convert_gentypeN
std::string CastBuiltInName(kOCLBuiltinName::ConvertPrefix);
// Check if this is 'floating point -> unsigned integer' cast
CastBuiltInName += mapLLVMTypeToOCLType(DstVecTy, !isa<FPToUIInst>(Cast));
// Replace LLVM conversion instruction with call to conversion built-in
BuiltinFuncMangleInfo Mangle;
// It does matter if the source is unsigned integer or not. SExt is for
// signed source, ZExt and UIToFPInst are for unsigned source.
if (isa<ZExtInst>(Cast) || isa<UIToFPInst>(Cast))
Mangle.addUnsignedArg(0);
AttributeList Attributes;
CallInst *Call =
addCallInst(M, CastBuiltInName, DstVecTy, Cast.getOperand(0), &Attributes,
&Cast, &Mangle, Cast.getName(), false);
Cast.replaceAllUsesWith(Call);
Cast.eraseFromParent();
}
void SPIRVToOCLBase::visitCallSPIRVImageQuerySize(CallInst *CI) {
// Get image type
Type *ImgTy = getCallValueType(CI, 0);
auto Desc = getImageDescriptor(ImgTy);
unsigned ImgDim = getImageDimension(Desc.Dim);
bool ImgArray = Desc.Arrayed;
AttributeList Attributes = CI->getCalledFunction()->getAttributes();
BuiltinFuncMangleInfo Mangle;
Mangle.getTypeMangleInfo(0).PointerTy = ImgTy;
Type *Int32Ty = Type::getInt32Ty(*Ctx);
Instruction *GetImageSize = nullptr;
if (ImgDim == 1) {
// OpImageQuerySize from non-arrayed 1d image is always translated
// into get_image_width returning scalar argument
GetImageSize = addCallInst(M, kOCLBuiltinName::GetImageWidth, Int32Ty,
CI->getArgOperand(0), &Attributes, CI, &Mangle,
CI->getName(), false);
// The width of integer type returning by OpImageQuerySize[Lod] may
// differ from i32
if (CI->getType()->getScalarType() != Int32Ty) {
GetImageSize = CastInst::CreateIntegerCast(
GetImageSize, CI->getType()->getScalarType(), false, CI->getName(),
CI->getIterator());
}
} else {
assert((ImgDim == 2 || ImgDim == 3) && "invalid image type");
assert(CI->getType()->isVectorTy() &&
"this code can handle vector result type only");
// get_image_dim returns int2 and int4 for 2d and 3d images respecitvely.
const unsigned ImgDimRetEls = ImgDim == 2 ? 2 : 4;
VectorType *RetTy = FixedVectorType::get(Int32Ty, ImgDimRetEls);
GetImageSize = addCallInst(M, kOCLBuiltinName::GetImageDim, RetTy,
CI->getArgOperand(0), &Attributes, CI, &Mangle,
CI->getName(), false);
// The width of integer type returning by OpImageQuerySize[Lod] may
// differ from i32
if (CI->getType()->getScalarType() != Int32Ty) {
GetImageSize = CastInst::CreateIntegerCast(
GetImageSize,
FixedVectorType::get(
CI->getType()->getScalarType(),
cast<FixedVectorType>(GetImageSize->getType())->getNumElements()),
false, CI->getName(), CI->getIterator());
}
}
if (ImgArray || ImgDim == 3) {
auto *VecTy = cast<FixedVectorType>(CI->getType());
const unsigned ImgQuerySizeRetEls = VecTy->getNumElements();
if (ImgDim == 1) {
// get_image_width returns scalar result while OpImageQuerySize
// for image1d_array_t returns <2 x i32> vector.
assert(ImgQuerySizeRetEls == 2 &&
"OpImageQuerySize[Lod] must return <2 x iN> vector type");
GetImageSize = InsertElementInst::Create(
PoisonValue::get(VecTy), GetImageSize, ConstantInt::get(Int32Ty, 0),
CI->getName(), CI->getIterator());
} else {
// get_image_dim and OpImageQuerySize returns different vector
// types for arrayed and 3d images.
SmallVector<Constant *, 4> MaskEls;
for (unsigned Idx = 0; Idx < ImgQuerySizeRetEls; ++Idx)
MaskEls.push_back(ConstantInt::get(Int32Ty, Idx));
Constant *Mask = ConstantVector::get(MaskEls);
GetImageSize = new ShuffleVectorInst(
GetImageSize, PoisonValue::get(GetImageSize->getType()), Mask,
CI->getName(), CI->getIterator());
}
}
if (ImgArray) {
assert((ImgDim == 1 || ImgDim == 2) && "invalid image array type");
// Insert get_image_array_size to the last position of the resulting vector.
auto *VecTy = cast<FixedVectorType>(CI->getType());
Type *SizeTy =
Type::getIntNTy(*Ctx, M->getDataLayout().getPointerSizeInBits(0));
Instruction *GetImageArraySize = addCallInst(
M, kOCLBuiltinName::GetImageArraySize, SizeTy, CI->getArgOperand(0),
&Attributes, CI, &Mangle, CI->getName(), false);
// The width of integer type returning by OpImageQuerySize[Lod] may
// differ from size_t which is returned by get_image_array_size
if (GetImageArraySize->getType() != VecTy->getElementType()) {
GetImageArraySize = CastInst::CreateIntegerCast(
GetImageArraySize, VecTy->getElementType(), false, CI->getName(),
CI->getIterator());
}
GetImageSize = InsertElementInst::Create(
GetImageSize, GetImageArraySize,
ConstantInt::get(Int32Ty, VecTy->getNumElements() - 1), CI->getName(),
CI->getIterator());
}
assert(GetImageSize && "must not be null");
CI->replaceAllUsesWith(GetImageSize);
CI->eraseFromParent();
}
std::string SPIRVToOCLBase::getUniformArithmeticBuiltinName(CallInst *CI,
Op OC) {
assert(isUniformArithmeticOpCode(OC) &&
"Not intended to handle other than uniform arithmetic opcodes!");
auto FuncName = OCLSPIRVBuiltinMap::rmap(OC);
std::string Prefix = getGroupBuiltinPrefix(CI);
std::string Op = FuncName;
Op.erase(0, strlen(kSPIRVName::GroupPrefix));
// unsigned prefix cannot be removed yet, as it is necessary to properly
// mangle the function
bool Unsigned = Op.front() == 'u';
if (!Unsigned)
Op = Op.erase(0, 1);
std::string GroupOp;
auto GO = getArgAs<spv::GroupOperation>(CI, 1);
switch (GO) {
case GroupOperationReduce:
GroupOp = "reduce";
break;
case GroupOperationInclusiveScan:
GroupOp = "scan_inclusive";
break;
case GroupOperationExclusiveScan:
GroupOp = "scan_exclusive";
break;
default:
llvm_unreachable("Unsupported group operation!");
break;
}
return Prefix + kSPIRVName::GroupPrefix + GroupOp + "_" + Op;
}
std::string SPIRVToOCLBase::getNonUniformArithmeticBuiltinName(CallInst *CI,
Op OC) {
assert(isNonUniformArithmeticOpCode(OC) &&
"Not intended to handle other than non uniform arithmetic opcodes!");
std::string Prefix = getGroupBuiltinPrefix(CI);
assert((Prefix == kOCLBuiltinName::SubPrefix) &&
"Workgroup scope is not supported for OpGroupNonUniform opcodes");
auto FuncName = OCLSPIRVBuiltinMap::rmap(OC);
std::string Op = FuncName;
Op.erase(0, strlen(kSPIRVName::GroupNonUniformPrefix));
if (!isGroupLogicalOpCode(OC)) {
// unsigned prefix cannot be removed yet, as it is necessary to properly
// mangle the function
const char Sign = Op.front();
bool Signed = (Sign == 'i' || Sign == 'f' || Sign == 's');
if (Signed)
Op = Op.erase(0, 1);
else
assert((Sign == 'u') && "Incorrect sign!");
} else { // LogicalOpcode
assert(
(Op == "logical_iand" || Op == "logical_ior" || Op == "logical_ixor") &&
"Incorrect logical operation");
Op = Op.erase(8, 1);
}
std::string GroupOp;
std::string GroupPrefix = kSPIRVName::GroupNonUniformPrefix;
auto GO = getArgAs<spv::GroupOperation>(CI, 1);
switch (GO) {
case GroupOperationReduce:
GroupOp = "reduce";
break;
case GroupOperationInclusiveScan:
GroupOp = "scan_inclusive";
break;
case GroupOperationExclusiveScan:
GroupOp = "scan_exclusive";
break;
case GroupOperationClusteredReduce:
GroupOp = "clustered_reduce";
// OpenCL clustered builtin has no non_uniform prefix, ex.
// sub_group_reduce_clustered_logical_and
GroupPrefix = kSPIRVName::GroupPrefix;
break;
default:
llvm_unreachable("Unsupported group operation!");
break;
}
return Prefix + GroupPrefix + GroupOp + "_" + Op;
}
std::string SPIRVToOCLBase::getBallotBuiltinName(CallInst *CI, Op OC) {
assert((OC == OpGroupNonUniformBallotBitCount) &&
"Not inteded to handle other opcodes than "
"OpGroupNonUniformBallotBitCount!");
std::string Prefix = getGroupBuiltinPrefix(CI);
assert(
(Prefix == kOCLBuiltinName::SubPrefix) &&
"Workgroup scope is not supported for OpGroupNonUniformBallotBitCount");
std::string GroupOp;
auto GO = getArgAs<spv::GroupOperation>(CI, 1);
switch (GO) {
case GroupOperationReduce:
GroupOp = "bit_count";
break;
case GroupOperationInclusiveScan:
GroupOp = "inclusive_scan";
break;
case GroupOperationExclusiveScan:
GroupOp = "exclusive_scan";
break;
default:
llvm_unreachable("Unsupported group operation!");
break;
}
return Prefix + kSPIRVName::GroupPrefix + "ballot_" + GroupOp;
}
std::string SPIRVToOCLBase::getRotateBuiltinName(CallInst *CI, Op OC) {
assert((OC == OpGroupNonUniformRotateKHR) &&
"Not intended to handle other opcodes");
std::string Prefix = getGroupBuiltinPrefix(CI);
assert((Prefix == kOCLBuiltinName::SubPrefix) &&
"Workgroup scope is not supported for OpGroupNonUniformRotateKHR");
std::string OptionalClustered;
if (CI->arg_size() == 4)
OptionalClustered = "clustered_";
return Prefix + kSPIRVName::GroupPrefix + OptionalClustered + "rotate";
}
std::string SPIRVToOCLBase::groupOCToOCLBuiltinName(CallInst *CI, Op OC) {
if (OC == OpGroupNonUniformRotateKHR)
return getRotateBuiltinName(CI, OC);
auto FuncName = OCLSPIRVBuiltinMap::rmap(OC);
assert(FuncName.find(kSPIRVName::GroupPrefix) == 0);
if (!hasGroupOperation(OC)) {
/// Transform OpenCL group builtin function names from group_
/// to work_group_ and sub_group_.
FuncName = getGroupBuiltinPrefix(CI) + FuncName;
} else { // Opcodes with group operation parameter
if (isUniformArithmeticOpCode(OC))
FuncName = getUniformArithmeticBuiltinName(CI, OC);
else if (isNonUniformArithmeticOpCode(OC))
FuncName = getNonUniformArithmeticBuiltinName(CI, OC);
else if (OC == OpGroupNonUniformBallotBitCount)
FuncName = getBallotBuiltinName(CI, OC);
else
llvm_unreachable("Unsupported opcode!");
}
return FuncName;
}
/// Return true if the original boolean return type needs to be changed to i32
/// when mapping the SPIR-V op to an OpenCL builtin.
static bool needsInt32RetTy(Op OC) {
return OC == OpGroupAny || OC == OpGroupAll || OC == OpGroupNonUniformAny ||
OC == OpGroupNonUniformAll || OC == OpGroupNonUniformAllEqual ||
OC == OpGroupNonUniformElect || OC == OpGroupNonUniformInverseBallot ||
OC == OpGroupNonUniformBallotBitExtract || isGroupLogicalOpCode(OC);
}
void SPIRVToOCLBase::visitCallSPIRVGroupBuiltin(CallInst *CI, Op OC) {
auto FuncName = groupOCToOCLBuiltinName(CI, OC);
auto Mutator = mutateCallInst(CI, FuncName);
/// Remove Group Operation argument,
/// as in OpenCL representation this is included in the function name
Mutator.removeArgs(0, (hasGroupOperation(OC) ? 2 : 1));
Type *Int32Ty = Type::getInt32Ty(*Ctx);
bool HasArg0ExtendedToi32 =
OC == OpGroupAny || OC == OpGroupAll || OC == OpGroupNonUniformAny ||
OC == OpGroupNonUniformAll || OC == OpGroupNonUniformBallot ||
isGroupLogicalOpCode(OC);
// Handle function arguments
if (OC == OpGroupBroadcast) {
Value *VecArg = Mutator.getArg(1);
if (auto *VT = dyn_cast<FixedVectorType>(VecArg->getType())) {
unsigned NumElements = VT->getNumElements();
for (unsigned I = 0; I < NumElements; I++)
Mutator.insertArg(1 + I, Mutator.Builder.CreateExtractElement(
VecArg, Mutator.Builder.getInt32(I)));
Mutator.removeArg(1 + NumElements);
}
} else if (HasArg0ExtendedToi32)
Mutator.mapArg(0, [](IRBuilder<> &Builder, Value *V) {
return Builder.CreateZExt(V, Builder.getInt32Ty());
});
// Handle function return type
if (needsInt32RetTy(OC))
Mutator.changeReturnType(Int32Ty, [](IRBuilder<> &Builder, CallInst *CI) {
// The OpenCL builtin returns a non-zero integer value. Convert to a
// boolean value.
return Builder.CreateICmpNE(CI, Builder.getInt32(0));
});
}
void SPIRVToOCLBase::visitCallSPIRVPipeBuiltin(CallInst *CI, Op OC) {
auto DemangledName = OCLSPIRVBuiltinMap::rmap(OC);
bool HasScope = DemangledName.find(kSPIRVName::GroupPrefix) == 0;
if (HasScope)
DemangledName = getGroupBuiltinPrefix(CI) + DemangledName;
assert(CI->getCalledFunction() && "Unexpected indirect call");
auto Mutator = mutateCallInst(CI, DemangledName);
if (HasScope)
Mutator.removeArg(0);
if (OC == OpReadPipe || OC == OpWritePipe || OC == OpReservedReadPipe ||
OC == OpReservedWritePipe || OC == OpReadPipeBlockingINTEL ||
OC == OpWritePipeBlockingINTEL) {
Mutator.mapArg(Mutator.arg_size() - 3, [](IRBuilder<> &Builder, Value *P) {
Type *T = P->getType();
assert(isa<PointerType>(T));
auto *NewTy = Builder.getPtrTy(SPIRAS_Generic);
if (T != NewTy) {
P = Builder.CreateAddrSpaceCast(P, NewTy);
}
return std::make_pair(
P, TypedPointerType::get(Builder.getInt8Ty(), SPIRAS_Generic));
});
}
}
void SPIRVToOCLBase::visitCallSPIRVImageMediaBlockBuiltin(CallInst *CI, Op OC) {
Type *RetType = CI->getType();
if (OC == OpSubgroupImageMediaBlockWriteINTEL) {
assert(CI->arg_size() >= 5 && "Wrong media block write signature");
RetType = CI->getArgOperand(4)->getType(); // texel type
}
unsigned int BitWidth = RetType->getScalarSizeInBits();
std::string FuncPostfix;
if (BitWidth == 8)
FuncPostfix = "_uc";
else if (BitWidth == 16)
FuncPostfix = "_us";
else if (BitWidth == 32)
FuncPostfix = "_ui";
else
assert(0 && "Unsupported texel type!");
if (auto *VecTy = dyn_cast<FixedVectorType>(RetType)) {
unsigned int NumEl = VecTy->getNumElements();
assert((NumEl == 2 || NumEl == 4 || NumEl == 8 || NumEl == 16) &&
"Wrong function type!");
FuncPostfix += std::to_string(NumEl);
}
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC) + FuncPostfix)
.moveArg(0, CI->arg_size() - 1);
}
void SPIRVToOCLBase::visitCallBuildNDRangeBuiltIn(CallInst *CI, Op OC,
StringRef DemangledName) {
assert(CI->getCalledFunction() && "Unexpected indirect call");
// __spirv_BuildNDRange_nD, drop __spirv_
StringRef S = DemangledName;
S = S.drop_front(strlen(kSPIRVName::Prefix));
SmallVector<StringRef, 8> Split;
// BuildNDRange_nD
S.split(Split, kSPIRVPostfix::Divider,
/*MaxSplit=*/-1, /*KeepEmpty=*/false);
assert(Split.size() >= 2 && "Invalid SPIRV function name");
// Cut _nD and add it to function name.
mutateCallInst(CI, std::string(kOCLBuiltinName::NDRangePrefix) +
Split[1].substr(0, 3).str())
// OpenCL built-in has another order of parameters.
.moveArg(2, 0);
}
void SPIRVToOCLBase::visitCallGenericCastToPtrBuiltIn(CallInst *CI, Op OC) {
assert(CI->getCalledFunction() && "Unexpected indirect call");
IRBuilder<> Builder(CI);
Value *PtrArg = CI->getArgOperand(0);
auto AddrSpace =
static_cast<SPIRAddressSpace>(CI->getType()->getPointerAddressSpace());
Type *NewTy = PointerType::get(CI->getContext(), AddrSpace);
Value *ASC = Builder.CreateAddrSpaceCast(PtrArg, NewTy);
CI->replaceAllUsesWith(ASC);
CI->eraseFromParent();
}
void SPIRVToOCLBase::visitCallGenericCastToPtrExplicitBuiltIn(CallInst *CI,
Op OC) {
assert(CI->getCalledFunction() && "Unexpected indirect call");
StringRef Name;
auto AddrSpace =
static_cast<SPIRAddressSpace>(CI->getType()->getPointerAddressSpace());
switch (AddrSpace) {
case SPIRAS_Global:
Name = kOCLBuiltinName::ToGlobal;
break;
case SPIRAS_Local:
Name = kOCLBuiltinName::ToLocal;
break;
case SPIRAS_Private:
Name = kOCLBuiltinName::ToPrivate;
break;
default:
llvm_unreachable("Invalid address space");
}
mutateCallInst(CI, Name.str())
// The instruction has two arguments, whereas ocl built-in has only one
// argument.
.removeArg(1);
}
void SPIRVToOCLBase::visitCallSPIRVCvtBuiltin(CallInst *CI, Op OC,
StringRef DemangledName) {
if (auto *TET =
dyn_cast<TargetExtType>(CI->getFunctionType()->getReturnType())) {
// Preserve any cooperative matrix type conversions as SPIR-V calls.
if (TET->getName() == "spirv.CooperativeMatrixKHR") {
return;
}
}
std::string CastBuiltInName;
if (isCvtFromUnsignedOpCode(OC))
CastBuiltInName = "u";
CastBuiltInName += kOCLBuiltinName::ConvertPrefix;
Type *DstTy = CI->getType();
CastBuiltInName += mapLLVMTypeToOCLType(DstTy, !isCvtToUnsignedOpCode(OC));
if (DemangledName.find("_sat") != StringRef::npos || isSatCvtOpCode(OC))
CastBuiltInName += "_sat";
Value *Src = CI->getOperand(0);
assert(Src && "Invalid SPIRV convert builtin call");
Type *SrcTy = Src->getType();
auto Loc = DemangledName.find("_rt");
if (Loc != StringRef::npos &&
!(isa<IntegerType>(SrcTy) && isa<IntegerType>(DstTy)))
CastBuiltInName += DemangledName.substr(Loc, 4).str();
mutateCallInst(CI, CastBuiltInName);
}
void SPIRVToOCLBase::visitCallAsyncWorkGroupCopy(CallInst *CI, Op OC) {
// First argument of AsyncWorkGroupCopy instruction is Scope, OCL
// built-in async_work_group_strided_copy doesn't have this argument
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC)).removeArg(0);
}
void SPIRVToOCLBase::visitCallGroupWaitEvents(CallInst *CI, Op OC) {
// First argument of GroupWaitEvents instruction is Scope, OCL
// built-in wait_group_events doesn't have this argument
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC)).removeArg(0);
}
static std::string getTypeSuffix(Type *T, bool IsSigned) {
std::string Suffix;
Type *ST = T->getScalarType();
if (ST->isHalfTy())
Suffix = "h";
else if (ST->isFloatTy())
Suffix = "f";
else if (IsSigned)
Suffix = "i";
else
Suffix = "ui";
return Suffix;
}
BuiltinCallMutator
SPIRVToOCLBase::mutateCallImageOperands(CallInst *CI, StringRef NewFuncName,
Type *T, unsigned ImOpArgIndex) {
// Default to signed.
bool IsSigned = true;
uint64_t ImOpValue = 0;
if (CI->arg_size() > ImOpArgIndex) {
ConstantInt *ImOp = dyn_cast<ConstantInt>(CI->getArgOperand(ImOpArgIndex));
if (ImOp)
ImOpValue = ImOp->getZExtValue();
unsigned SignZeroExtMasks = ImageOperandsMask::ImageOperandsSignExtendMask |
ImageOperandsMask::ImageOperandsZeroExtendMask;
// If one of the SPIR-V 1.4 SignExtend/ZeroExtend operands is present, take
// it into account and drop the mask.
if (ImOpValue & SignZeroExtMasks) {
if (ImOpValue & ImageOperandsMask::ImageOperandsZeroExtendMask)
IsSigned = false;
ImOpValue &= ~SignZeroExtMasks;
}
}
auto Mutator =
mutateCallInst(CI, NewFuncName.str() + getTypeSuffix(T, IsSigned));
if (ImOpArgIndex < Mutator.arg_size()) {
// Drop "Image Operands" argument.
Mutator.removeArg(ImOpArgIndex);
if (ImOpArgIndex < Mutator.arg_size()) {
ConstantFP *LodVal = dyn_cast<ConstantFP>(Mutator.getArg(ImOpArgIndex));
// If the image operand is LOD and its value is zero, drop it too.
if (LodVal && LodVal->isNullValue() &&
ImOpValue & ImageOperandsMask::ImageOperandsLodMask) {
Mutator.removeArgs(ImOpArgIndex, Mutator.arg_size() - ImOpArgIndex);
ImOpValue &= ~ImageOperandsMask::ImageOperandsLodMask;
}
}
}
return Mutator;
}
void SPIRVToOCLBase::visitCallSPIRVImageSampleExplicitLodBuiltIn(CallInst *CI,
Op OC) {
Type *T = CI->getType();
if (auto *VT = dyn_cast<VectorType>(T))
T = VT->getElementType();
auto Mutator =
mutateCallImageOperands(CI, kOCLBuiltinName::SampledReadImage, T, 2);
CallInst *CallSampledImg = cast<CallInst>(CI->getArgOperand(0));
auto Img = getCallValue(CallSampledImg, 0);
auto Sampler = getCallValue(CallSampledImg, 1);
bool IsDepthImage = false;
Mutator.mapArg(0, [&](Value *SampledImg) {
StringRef ImageTypeName;
if (isOCLImageType(Img.second, &ImageTypeName))
IsDepthImage = ImageTypeName.contains("_depth_");
if (CallSampledImg->hasOneUse()) {
CallSampledImg->replaceAllUsesWith(
PoisonValue::get(CallSampledImg->getType()));
CallSampledImg->dropAllReferences();
CallSampledImg->eraseFromParent();
}
return Img;
});
Mutator.insertArg(1, Sampler);
if (IsDepthImage)
Mutator.changeReturnType(T, [&](IRBuilder<> &Builder, CallInst *NewCI) {
return Builder.CreateInsertElement(
FixedVectorType::get(NewCI->getType(), 4), NewCI, uint64_t(0));
});
}
void SPIRVToOCLBase::visitCallSPIRVImageWriteBuiltIn(CallInst *CI, Op OC) {
auto Mutator = mutateCallImageOperands(CI, kOCLBuiltinName::WriteImage,
CI->getArgOperand(2)->getType(), 3);
if (Mutator.arg_size() > 3)
Mutator.moveArg(3, 2);
}
void SPIRVToOCLBase::visitCallSPIRVImageReadBuiltIn(CallInst *CI, Op OC) {
mutateCallImageOperands(CI, kOCLBuiltinName::ReadImage, CI->getType(), 2);
}
void SPIRVToOCLBase::visitCallSPIRVImageQueryBuiltIn(CallInst *CI, Op OC) {
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC))
.changeReturnType(CI->getType(), [=](IRBuilder<> &Builder, CallInst *CI) {
unsigned int Offset = 0;
if (OC == OpImageQueryFormat)
Offset = OCLImageChannelDataTypeOffset;
else if (OC == OpImageQueryOrder)
Offset = OCLImageChannelOrderOffset;
else
llvm_unreachable("Unsupported opcode");
return Builder.CreateSub(CI, Builder.getInt32(Offset));
});
}
void SPIRVToOCLBase::visitCallSPIRVSubgroupINTELBuiltIn(CallInst *CI, Op OC) {
std::stringstream Name;
Type *DataTy = nullptr;
switch (OC) {
case OpSubgroupBlockReadINTEL:
case OpSubgroupImageBlockReadINTEL:
Name << "intel_sub_group_block_read";
DataTy = CI->getType();
break;
case OpSubgroupBlockWriteINTEL:
Name << "intel_sub_group_block_write";
DataTy = CI->getOperand(1)->getType();
break;
case OpSubgroupImageBlockWriteINTEL:
Name << "intel_sub_group_block_write";
DataTy = CI->getOperand(2)->getType();
break;
default:
Name << OCLSPIRVBuiltinMap::rmap(OC);
break;
}
if (DataTy) {
unsigned VectorNumElements = 1;
if (FixedVectorType *VT = dyn_cast<FixedVectorType>(DataTy))
VectorNumElements = VT->getNumElements();
unsigned ElementBitSize = DataTy->getScalarSizeInBits();
Name << getIntelSubgroupBlockDataPostfix(ElementBitSize, VectorNumElements);
}
mutateCallInst(CI, Name.str());
}
void SPIRVToOCLBase::visitCallSPIRVAvcINTELEvaluateBuiltIn(CallInst *CI,
Op OC) {
// There are three types of AVC Intel Evaluate opcodes:
// 1. With multi reference images - does not use OpVmeImageINTEL opcode
// for reference images
// 2. With dual reference images - uses two OpVmeImageINTEL opcodes for
// reference image
// 3. With single reference image - uses one OpVmeImageINTEL opcode for
// reference image
StringRef FnName = CI->getCalledFunction()->getName();
int NumImages = 0;
if (FnName.contains("SingleReference"))
NumImages = 2;
else if (FnName.contains("DualReference"))
NumImages = 3;
else if (FnName.contains("MultiReference"))
NumImages = 1;
else if (FnName.contains("EvaluateIpe"))
NumImages = 1;
auto EraseVmeImageCall = [](CallInst *CI) {
if (CI->hasOneUse()) {
CI->replaceAllUsesWith(PoisonValue::get(CI->getType()));
CI->dropAllReferences();
CI->eraseFromParent();
}
};
auto Mutator =
mutateCallInst(CI, OCLSPIRVSubgroupAVCIntelBuiltinMap::rmap(OC));
if (NumImages) {
CallInst *SrcImage = cast<CallInst>(Mutator.getArg(0));
if (NumImages == 1) {
// Multi reference opcode - remove src image OpVmeImageINTEL opcode
// and replace it with corresponding OpImage and OpSampler arguments
size_t SamplerPos = Mutator.arg_size() - 1;
Mutator.replaceArg(0, getCallValue(SrcImage, 0));
Mutator.insertArg(SamplerPos, getCallValue(SrcImage, 1));
} else {
CallInst *FwdRefImage = cast<CallInst>(Mutator.getArg(1));
CallInst *BwdRefImage =
NumImages == 3 ? cast<CallInst>(Mutator.getArg(2)) : nullptr;
// Single reference opcode - remove src and ref image
// OpVmeImageINTEL opcodes and replace them with src and ref OpImage
// opcodes and OpSampler
Mutator.removeArgs(0, NumImages);
// insert source OpImage and OpSampler
Mutator.insertArg(0, getCallValue(SrcImage, 0));
Mutator.insertArg(1, getCallValue(SrcImage, 1));
// insert reference OpImage
Mutator.insertArg(1, getCallValue(FwdRefImage, 0));
EraseVmeImageCall(SrcImage);
EraseVmeImageCall(FwdRefImage);
if (BwdRefImage) {
// Dual reference opcode - insert second reference OpImage argument
Mutator.insertArg(2, getCallValue(BwdRefImage, 0));
EraseVmeImageCall(BwdRefImage);
}
}
} else
llvm_unreachable("invalid avc instruction");
}
void SPIRVToOCLBase::visitCallSPIRVGenericPtrMemSemantics(CallInst *CI) {
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OpGenericPtrMemSemantics))
.changeReturnType(CI->getType(),
[](IRBuilder<> &Builder, CallInst *NewCI) {
return Builder.CreateShl(NewCI, Builder.getInt32(8));
});
}
void SPIRVToOCLBase::visitCallSPIRVBFloat16Conversions(CallInst *CI, Op OC) {
Type *ArgTy = CI->getOperand(0)->getType();
std::string N =
ArgTy->isVectorTy()
? std::to_string(cast<FixedVectorType>(ArgTy)->getNumElements())
: "";
std::string Name;
switch (static_cast<uint32_t>(OC)) {
case OpConvertFToBF16INTEL:
Name = "intel_convert_bfloat16" + N + "_as_ushort" + N;
break;
case OpConvertBF16ToFINTEL:
Name = "intel_convert_as_bfloat16" + N + "_float" + N;
break;
default:
break; // do nothing
}
mutateCallInst(CI, Name);
}
void SPIRVToOCLBase::visitCallSPIRVDot(CallInst *CI, Op OC,
StringRef DemangledName) {
// OpenCL only supports integer dot product builtins that have return types
// of int and uint.
if (!(DemangledName.contains("_Rint") || DemangledName.contains("_Ruint")))
return;
bool IsPacked = !CI->getOperand(0)->getType()->isVectorTy();
std::stringstream Name;
switch (OC) {
case OpSDot:
if (IsPacked)
Name << kOCLBuiltinName::Dot4x8PackedPrefix << "ss_int";
else
// Add an extra suffix to help determine signed/unsigned arguments
Name << kOCLBuiltinName::Dot << "_ss";
break;
case OpUDot:
if (IsPacked)
Name << kOCLBuiltinName::Dot4x8PackedPrefix << "uu_uint";
else
Name << kOCLBuiltinName::Dot << "_uu";
break;
case OpSUDot:
if (IsPacked)
Name << kOCLBuiltinName::Dot4x8PackedPrefix << "su_int";
else
Name << kOCLBuiltinName::Dot << "_su";
break;
case OpSDotAccSat:
if (IsPacked)
Name << kOCLBuiltinName::DotAccSat4x8PackedPrefix << "ss_int";
else
Name << kOCLBuiltinName::DotAccSat << "_ss";
break;
case OpUDotAccSat:
if (IsPacked)
Name << kOCLBuiltinName::DotAccSat4x8PackedPrefix << "uu_uint";
else
Name << kOCLBuiltinName::DotAccSat << "_uu";
break;
case OpSUDotAccSat:
if (IsPacked)
Name << kOCLBuiltinName::DotAccSat4x8PackedPrefix << "su_int";
else
Name << kOCLBuiltinName::DotAccSat << "_su";
break;
default:
break; // do nothing
}
auto Mutator = mutateCallInst(CI, Name.str());
if (IsPacked)
Mutator.removeArg(CI->arg_size() - 1);
}
void SPIRVToOCLBase::visitCallSPIRVBuiltin(CallInst *CI, Op OC) {
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC));
}
void SPIRVToOCLBase::visitCallSPIRVBuiltin(CallInst *CI,
SPIRVBuiltinVariableKind Kind) {
mutateCallInst(CI, SPIRSPIRVBuiltinVariableMap::rmap(Kind));
}
void SPIRVToOCLBase::visitCallSPIRVAvcINTELInstructionBuiltin(CallInst *CI,
Op OC) {
mutateCallInst(CI, OCLSPIRVSubgroupAVCIntelBuiltinMap::rmap(OC));
}
void SPIRVToOCLBase::visitCallSPIRVOCLExt(CallInst *CI, OCLExtOpKind Kind) {
mutateCallInst(CI, OCLExtOpMap::map(Kind));
}
void SPIRVToOCLBase::visitCallSPIRVVLoadn(CallInst *CI, OCLExtOpKind Kind) {
std::string Name = OCLExtOpMap::map(Kind);
unsigned LastArg = CI->arg_size() - 1;
if (ConstantInt *C = dyn_cast<ConstantInt>(CI->getArgOperand(LastArg))) {
uint64_t NumComponents = C->getZExtValue();
std::stringstream SS;
SS << NumComponents;
Name.replace(Name.find("n"), 1, SS.str());
}
mutateCallInst(CI, Name).removeArg(LastArg);
}
void SPIRVToOCLBase::visitCallSPIRVVStore(CallInst *CI, OCLExtOpKind Kind) {
std::string Name = OCLExtOpMap::map(Kind);
bool DropLastArg = false;
if (Kind == OpenCLLIB::Vstore_half_r || Kind == OpenCLLIB::Vstore_halfn_r ||
Kind == OpenCLLIB::Vstorea_halfn_r) {
auto *C = cast<ConstantInt>(CI->getArgOperand(CI->arg_size() - 1));
auto RoundingMode = static_cast<SPIRVFPRoundingModeKind>(C->getZExtValue());
Name.replace(Name.find("_r"), 2,
std::string("_") +
SPIRSPIRVFPRoundingModeMap::rmap(RoundingMode));
DropLastArg = true;
}
if (Kind == OpenCLLIB::Vstore_halfn || Kind == OpenCLLIB::Vstore_halfn_r ||
Kind == OpenCLLIB::Vstorea_halfn || Kind == OpenCLLIB::Vstorea_halfn_r ||
Kind == OpenCLLIB::Vstoren) {
if (auto *DataType =
dyn_cast<VectorType>(CI->getArgOperand(0)->getType())) {
uint64_t NumElements = DataType->getElementCount().getFixedValue();
assert((NumElements == 2 || NumElements == 3 || NumElements == 4 ||
NumElements == 8 || NumElements == 16) &&
"Unsupported vector size for vstore instruction!");
std::stringstream SS;
SS << NumElements;
Name.replace(Name.find("n"), 1, SS.str());
}
}
auto Mutator = mutateCallInst(CI, Name);
if (DropLastArg)
Mutator.removeArg(Mutator.arg_size() - 1);
}
void SPIRVToOCLBase::visitCallSPIRVPrintf(CallInst *CI, OCLExtOpKind Kind) {
CallInst *NewCI = cast<CallInst>(
mutateCallInst(CI, OCLExtOpMap::map(OpenCLLIB::Printf)).getMutated());
// Clang represents printf function without mangling
std::string TargetName = "printf";
if (Function *F = M->getFunction(TargetName))
NewCI->setCalledFunction(F);
else
NewCI->getCalledFunction()->setName(TargetName);
}
void SPIRVToOCLBase::visitCallSPIRVAnyAll(CallInst *CI, Op OC) {
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC))
.mapArg(0,
[](IRBuilder<> &Builder, Value *V) {
Type *NewArgTy = V->getType()->getWithNewBitWidth(8);
return Builder.CreateSExt(V, NewArgTy);
})
.changeReturnType(Type::getInt32Ty(*Ctx),
[=](IRBuilder<> &Builder, CallInst *NewCI) {
return Builder.CreateTrunc(NewCI, CI->getType());
});
}
void SPIRVToOCLBase::visitCallSPIRVRelational(CallInst *CI, Op OC) {
Type *IntTy = Type::getInt32Ty(*Ctx);
Type *RetTy = IntTy;
if (CI->getType()->isVectorTy()) {
auto *OpElemTy =
cast<FixedVectorType>(CI->getOperand(0)->getType())->getElementType();
if (OpElemTy->isDoubleTy())
IntTy = Type::getInt64Ty(*Ctx);
if (OpElemTy->isHalfTy())
IntTy = Type::getInt16Ty(*Ctx);
RetTy = FixedVectorType::get(
IntTy, cast<FixedVectorType>(CI->getType())->getNumElements());
}
mutateCallInst(CI, OCLSPIRVBuiltinMap::rmap(OC))
.changeReturnType(RetTy, [=](IRBuilder<> &Builder, CallInst *NewCI) {
return Builder.CreateTruncOrBitCast(NewCI, CI->getType());
});
}
void SPIRVToOCLBase::visitCallSPIRVReadClockKHR(CallInst *CI) {
std::ostringstream Name;
Name << "clock_read_";
if (CI->getType()->isVectorTy())
Name << "hilo_";
// Encode the scope (taken from the argument) in the function name.
ConstantInt *ScopeOp = cast<ConstantInt>(CI->getArgOperand(0));
switch (static_cast<Scope>(ScopeOp->getZExtValue())) {
case ScopeDevice:
Name << "device";
break;
case ScopeWorkgroup:
Name << "work_group";
break;
case ScopeSubgroup:
Name << "sub_group";
break;
default:
break;
}
auto Mutator = mutateCallInst(CI, Name.str());
Mutator.removeArg(0);
}
std::string SPIRVToOCLBase::getGroupBuiltinPrefix(CallInst *CI) {
std::string Prefix;
auto ES = getArgAsScope(CI, 0);
switch (ES) {
case ScopeWorkgroup:
Prefix = kOCLBuiltinName::WorkPrefix;
break;
case ScopeSubgroup:
Prefix = kOCLBuiltinName::SubPrefix;
break;
default:
llvm_unreachable("Invalid execution scope");
}
return Prefix;
}
std::string
SPIRVToOCLBase::getOCLImageOpaqueType(SmallVector<std::string, 8> &Postfixes) {
SmallVector<int, 7> Ops;
for (unsigned I = 1; I < 8; ++I)
Ops.push_back(atoi(Postfixes[I].c_str()));
SPIRVTypeImageDescriptor Desc(static_cast<SPIRVImageDimKind>(Ops[0]), Ops[1],
Ops[2], Ops[3], Ops[4], Ops[5]);
std::string OCLStructName =
std::string(kSPR2TypeName::OCLPrefix) + rmap<std::string>(Desc);
SPIRVAccessQualifierKind Acc = static_cast<SPIRVAccessQualifierKind>(Ops[6]);
insertImageNameAccessQualifier(Acc, OCLStructName);
return OCLStructName;
}
std::string
SPIRVToOCLBase::getOCLPipeOpaqueType(SmallVector<std::string, 8> &Postfixes) {
assert(Postfixes.size() == 1);
unsigned PipeAccess = atoi(Postfixes[0].c_str());
assert((PipeAccess == AccessQualifierReadOnly ||
PipeAccess == AccessQualifierWriteOnly) &&
"Invalid access qualifier");
return PipeAccess ? kSPR2TypeName::PipeWO : kSPR2TypeName::PipeRO;
}
void SPIRVToOCLBase::translateOpaqueTypes() {
for (auto *S : M->getIdentifiedStructTypes()) {
StringRef STName = S->getStructName();
bool IsSPIRVOpaque =
S->isOpaque() && STName.starts_with(kSPIRVTypeName::PrefixAndDelim);
if (!IsSPIRVOpaque)
continue;
S->setName(translateOpaqueType(STName));
}
}
std::string SPIRVToOCLBase::translateOpaqueType(StringRef STName) {
if (!STName.starts_with(kSPIRVTypeName::PrefixAndDelim))
return STName.str();
SmallVector<std::string, 8> Postfixes;
std::string DecodedST = decodeSPIRVTypeName(STName, Postfixes);
if (!SPIRVOpaqueTypeOpCodeMap::find(DecodedST))
return STName.str();
Op OP = SPIRVOpaqueTypeOpCodeMap::map(DecodedST);
std::string OCLOpaqueName;
if (OP == OpTypeImage)
OCLOpaqueName = getOCLImageOpaqueType(Postfixes);
else if (OP == OpTypePipe)
OCLOpaqueName = getOCLPipeOpaqueType(Postfixes);
else if (isSubgroupAvcINTELTypeOpCode(OP))
OCLOpaqueName = OCLSubgroupINTELTypeOpCodeMap::rmap(OP);
else if (isOpaqueGenericTypeOpCode(OP))
OCLOpaqueName = OCLOpaqueTypeOpCodeMap::rmap(OP);
else
return STName.str();
return OCLOpaqueName;
}
void addSPIRVBIsLoweringPass(ModulePassManager &PassMgr,
SPIRV::BIsRepresentation BIsRep) {
switch (BIsRep) {
case SPIRV::BIsRepresentation::OpenCL12:
PassMgr.addPass(SPIRVToOCL12Pass());
break;
case SPIRV::BIsRepresentation::OpenCL20:
PassMgr.addPass(SPIRVToOCL20Pass());
break;
case SPIRV::BIsRepresentation::SPIRVFriendlyIR:
// nothing to do, already done
break;
}
}
} // namespace SPIRV
ModulePass *
llvm::createSPIRVBIsLoweringPass(Module &M,
SPIRV::BIsRepresentation BIsRepresentation) {
switch (BIsRepresentation) {
case SPIRV::BIsRepresentation::OpenCL12:
return createSPIRVToOCL12Legacy();
case SPIRV::BIsRepresentation::OpenCL20:
return createSPIRVToOCL20Legacy();
case SPIRV::BIsRepresentation::SPIRVFriendlyIR:
// nothing to do, already done
return nullptr;
}
llvm_unreachable("Unsupported built-ins representation");
return nullptr;
}