[SPIRV] support builtin types and ExtInsts selection
The patch adds the support of OpenCL and SPIR-V built-in types. It also implements ExtInst selection and adds spv_unreachable and spv_alloca intrinsics which improve the generation of the corresponding SPIR-V code. Five LIT tests are included to demonstrate the improvement. Differential Revision: https://reviews.llvm.org/D132648 Co-authored-by: Aleksandr Bezzubikov <zuban32s@gmail.com> Co-authored-by: Michal Paszkowski <michal.paszkowski@outlook.com> Co-authored-by: Andrey Tretyakov <andrey1.tretyakov@intel.com> Co-authored-by: Konrad Trifunovic <konrad.trifunovic@intel.com>
This commit is contained in:
parent
c3d1504d63
commit
698c800142
|
@ -29,4 +29,6 @@ let TargetPrefix = "spv" in {
|
|||
def int_spv_bitcast : Intrinsic<[llvm_any_ty], [llvm_any_ty]>;
|
||||
def int_spv_switch : Intrinsic<[], [llvm_any_ty, llvm_vararg_ty]>;
|
||||
def int_spv_cmpxchg : Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_vararg_ty]>;
|
||||
def int_spv_unreachable : Intrinsic<[], []>;
|
||||
def int_spv_alloca : Intrinsic<[llvm_any_ty], []>;
|
||||
}
|
||||
|
|
|
@ -1611,5 +1611,236 @@ Optional<bool> lowerBuiltin(const StringRef DemangledCall,
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct DemangledType {
|
||||
StringRef Name;
|
||||
uint32_t Opcode;
|
||||
};
|
||||
|
||||
#define GET_DemangledTypes_DECL
|
||||
#define GET_DemangledTypes_IMPL
|
||||
|
||||
struct ImageType {
|
||||
StringRef Name;
|
||||
StringRef SampledType;
|
||||
AccessQualifier::AccessQualifier Qualifier;
|
||||
Dim::Dim Dimensionality;
|
||||
bool Arrayed;
|
||||
bool Depth;
|
||||
bool Multisampled;
|
||||
bool Sampled;
|
||||
ImageFormat::ImageFormat Format;
|
||||
};
|
||||
|
||||
struct PipeType {
|
||||
StringRef Name;
|
||||
AccessQualifier::AccessQualifier Qualifier;
|
||||
};
|
||||
|
||||
using namespace AccessQualifier;
|
||||
using namespace Dim;
|
||||
using namespace ImageFormat;
|
||||
#define GET_ImageTypes_DECL
|
||||
#define GET_ImageTypes_IMPL
|
||||
#define GET_PipeTypes_DECL
|
||||
#define GET_PipeTypes_IMPL
|
||||
#include "SPIRVGenTables.inc"
|
||||
} // namespace SPIRV
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Misc functions for parsing builtin types and looking up implementation
|
||||
// details in TableGenerated tables.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
static const SPIRV::DemangledType *findBuiltinType(StringRef Name) {
|
||||
if (Name.startswith("opencl."))
|
||||
return SPIRV::lookupBuiltinType(Name);
|
||||
if (Name.startswith("spirv.")) {
|
||||
// Some SPIR-V builtin types have a complex list of parameters as part of
|
||||
// their name (e.g. spirv.Image._void_1_0_0_0_0_0_0). Those parameters often
|
||||
// are numeric literals which cannot be easily represented by TableGen
|
||||
// records and should be parsed instead.
|
||||
unsigned BaseTypeNameLength =
|
||||
Name.contains('_') ? Name.find('_') - 1 : Name.size();
|
||||
return SPIRV::lookupBuiltinType(Name.substr(0, BaseTypeNameLength).str());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static std::unique_ptr<const SPIRV::ImageType>
|
||||
lookupOrParseBuiltinImageType(StringRef Name) {
|
||||
if (Name.startswith("opencl.")) {
|
||||
// Lookup OpenCL builtin image type lowering details in TableGen records.
|
||||
const SPIRV::ImageType *Record = SPIRV::lookupImageType(Name);
|
||||
return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType(*Record));
|
||||
}
|
||||
if (Name.startswith("spirv.")) {
|
||||
// Parse the literals of SPIR-V image builtin parameters. The name should
|
||||
// have the following format:
|
||||
// spirv.Image._Type_Dim_Depth_Arrayed_MS_Sampled_ImageFormat_AccessQualifier
|
||||
// e.g. %spirv.Image._void_1_0_0_0_0_0_0
|
||||
StringRef TypeParametersString = Name.substr(strlen("spirv.Image."));
|
||||
SmallVector<StringRef> TypeParameters;
|
||||
SplitString(TypeParametersString, TypeParameters, "_");
|
||||
assert(TypeParameters.size() == 8 &&
|
||||
"Wrong number of literals in SPIR-V builtin image type");
|
||||
|
||||
StringRef SampledType = TypeParameters[0];
|
||||
unsigned Dim, Depth, Arrayed, Multisampled, Sampled, Format, AccessQual;
|
||||
bool AreParameterLiteralsValid =
|
||||
!(TypeParameters[1].getAsInteger(10, Dim) ||
|
||||
TypeParameters[2].getAsInteger(10, Depth) ||
|
||||
TypeParameters[3].getAsInteger(10, Arrayed) ||
|
||||
TypeParameters[4].getAsInteger(10, Multisampled) ||
|
||||
TypeParameters[5].getAsInteger(10, Sampled) ||
|
||||
TypeParameters[6].getAsInteger(10, Format) ||
|
||||
TypeParameters[7].getAsInteger(10, AccessQual));
|
||||
assert(AreParameterLiteralsValid &&
|
||||
"Invalid format of SPIR-V image type parameter literals.");
|
||||
|
||||
return std::unique_ptr<SPIRV::ImageType>(new SPIRV::ImageType{
|
||||
Name, SampledType, SPIRV::AccessQualifier::AccessQualifier(AccessQual),
|
||||
SPIRV::Dim::Dim(Dim), static_cast<bool>(Arrayed),
|
||||
static_cast<bool>(Depth), static_cast<bool>(Multisampled),
|
||||
static_cast<bool>(Sampled), SPIRV::ImageFormat::ImageFormat(Format)});
|
||||
}
|
||||
llvm_unreachable("Unknown builtin image type name/literal");
|
||||
}
|
||||
|
||||
static std::unique_ptr<const SPIRV::PipeType>
|
||||
lookupOrParseBuiltinPipeType(StringRef Name) {
|
||||
if (Name.startswith("opencl.")) {
|
||||
// Lookup OpenCL builtin pipe type lowering details in TableGen records.
|
||||
const SPIRV::PipeType *Record = SPIRV::lookupPipeType(Name);
|
||||
return std::unique_ptr<SPIRV::PipeType>(new SPIRV::PipeType(*Record));
|
||||
}
|
||||
if (Name.startswith("spirv.")) {
|
||||
// Parse the access qualifier literal in the name of the SPIR-V pipe type.
|
||||
// The name should have the following format:
|
||||
// spirv.Pipe._AccessQualifier
|
||||
// e.g. %spirv.Pipe._1
|
||||
if (Name.endswith("_0"))
|
||||
return std::unique_ptr<SPIRV::PipeType>(
|
||||
new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadOnly});
|
||||
if (Name.endswith("_1"))
|
||||
return std::unique_ptr<SPIRV::PipeType>(
|
||||
new SPIRV::PipeType{Name, SPIRV::AccessQualifier::WriteOnly});
|
||||
if (Name.endswith("_2"))
|
||||
return std::unique_ptr<SPIRV::PipeType>(
|
||||
new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadWrite});
|
||||
llvm_unreachable("Unknown pipe type access qualifier literal");
|
||||
}
|
||||
llvm_unreachable("Unknown builtin pipe type name/literal");
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Implementation functions for builtin types.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
SPIRVType *getNonParametrizedType(const StructType *OpaqueType,
|
||||
const SPIRV::DemangledType *TypeRecord,
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
SPIRVGlobalRegistry *GR) {
|
||||
unsigned Opcode = TypeRecord->Opcode;
|
||||
// Create or get an existing type from GlobalRegistry.
|
||||
return GR->getOrCreateOpTypeByOpcode(OpaqueType, MIRBuilder, Opcode);
|
||||
}
|
||||
|
||||
SPIRVType *getSamplerType(MachineIRBuilder &MIRBuilder,
|
||||
SPIRVGlobalRegistry *GR) {
|
||||
// Create or get an existing type from GlobalRegistry.
|
||||
return GR->getOrCreateOpTypeSampler(MIRBuilder);
|
||||
}
|
||||
|
||||
SPIRVType *getPipeType(const StructType *OpaqueType,
|
||||
MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
|
||||
// Lookup pipe type lowering details in TableGen records or parse the
|
||||
// name/literal for details.
|
||||
std::unique_ptr<const SPIRV::PipeType> Record =
|
||||
lookupOrParseBuiltinPipeType(OpaqueType->getName());
|
||||
// Create or get an existing type from GlobalRegistry.
|
||||
return GR->getOrCreateOpTypePipe(MIRBuilder, Record.get()->Qualifier);
|
||||
}
|
||||
|
||||
SPIRVType *getImageType(const StructType *OpaqueType,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccessQual,
|
||||
MachineIRBuilder &MIRBuilder, SPIRVGlobalRegistry *GR) {
|
||||
// Lookup image type lowering details in TableGen records or parse the
|
||||
// name/literal for details.
|
||||
std::unique_ptr<const SPIRV::ImageType> Record =
|
||||
lookupOrParseBuiltinImageType(OpaqueType->getName());
|
||||
|
||||
SPIRVType *SampledType =
|
||||
GR->getOrCreateSPIRVTypeByName(Record.get()->SampledType, MIRBuilder);
|
||||
return GR->getOrCreateOpTypeImage(
|
||||
MIRBuilder, SampledType, Record.get()->Dimensionality,
|
||||
Record.get()->Depth, Record.get()->Arrayed, Record.get()->Multisampled,
|
||||
Record.get()->Sampled, Record.get()->Format,
|
||||
AccessQual == SPIRV::AccessQualifier::WriteOnly
|
||||
? SPIRV::AccessQualifier::WriteOnly
|
||||
: Record.get()->Qualifier);
|
||||
}
|
||||
|
||||
SPIRVType *getSampledImageType(const StructType *OpaqueType,
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
SPIRVGlobalRegistry *GR) {
|
||||
StringRef TypeParametersString =
|
||||
OpaqueType->getName().substr(strlen("spirv.SampledImage."));
|
||||
LLVMContext &Context = MIRBuilder.getMF().getFunction().getContext();
|
||||
Type *ImageOpaqueType = StructType::getTypeByName(
|
||||
Context, "spirv.Image." + TypeParametersString.str());
|
||||
SPIRVType *TargetImageType =
|
||||
GR->getOrCreateSPIRVType(ImageOpaqueType, MIRBuilder);
|
||||
return GR->getOrCreateOpTypeSampledImage(TargetImageType, MIRBuilder);
|
||||
}
|
||||
|
||||
namespace SPIRV {
|
||||
SPIRVType *lowerBuiltinType(const StructType *OpaqueType,
|
||||
AccessQualifier::AccessQualifier AccessQual,
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
SPIRVGlobalRegistry *GR) {
|
||||
assert(OpaqueType->hasName() &&
|
||||
"Structs representing builtin types must have a parsable name");
|
||||
unsigned NumStartingVRegs = MIRBuilder.getMRI()->getNumVirtRegs();
|
||||
|
||||
const StringRef Name = OpaqueType->getName();
|
||||
LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n");
|
||||
|
||||
// Lookup the demangled builtin type in the TableGen records.
|
||||
const SPIRV::DemangledType *TypeRecord = findBuiltinType(Name);
|
||||
if (!TypeRecord)
|
||||
report_fatal_error("Missing TableGen record for builtin type: " + Name);
|
||||
|
||||
// "Lower" the BuiltinType into TargetType. The following get<...>Type methods
|
||||
// use the implementation details from TableGen records to either create a new
|
||||
// OpType<...> machine instruction or get an existing equivalent SPIRVType
|
||||
// from GlobalRegistry.
|
||||
SPIRVType *TargetType;
|
||||
switch (TypeRecord->Opcode) {
|
||||
case SPIRV::OpTypeImage:
|
||||
TargetType = getImageType(OpaqueType, AccessQual, MIRBuilder, GR);
|
||||
break;
|
||||
case SPIRV::OpTypePipe:
|
||||
TargetType = getPipeType(OpaqueType, MIRBuilder, GR);
|
||||
break;
|
||||
case SPIRV::OpTypeSampler:
|
||||
TargetType = getSamplerType(MIRBuilder, GR);
|
||||
break;
|
||||
case SPIRV::OpTypeSampledImage:
|
||||
TargetType = getSampledImageType(OpaqueType, MIRBuilder, GR);
|
||||
break;
|
||||
default:
|
||||
TargetType = getNonParametrizedType(OpaqueType, TypeRecord, MIRBuilder, GR);
|
||||
break;
|
||||
}
|
||||
|
||||
// Emit OpName instruction if a new OpType<...> instruction was added
|
||||
// (equivalent type was not found in GlobalRegistry).
|
||||
if (NumStartingVRegs < MIRBuilder.getMRI()->getNumVirtRegs())
|
||||
buildOpName(GR->getSPIRVTypeID(TargetType), OpaqueType->getName(),
|
||||
MIRBuilder);
|
||||
|
||||
return TargetType;
|
||||
}
|
||||
} // namespace SPIRV
|
||||
} // namespace llvm
|
||||
|
|
|
@ -28,14 +28,27 @@ namespace SPIRV {
|
|||
/// \p DemangledCall is the skeleton of the lowered builtin function call.
|
||||
/// \p Set is the external instruction set containing the given builtin.
|
||||
/// \p OrigRet is the single original virtual return register if defined,
|
||||
/// Register(0) otherwise. \p OrigRetTy is the type of the \p OrigRet. \p Args
|
||||
/// are the arguments of the lowered builtin call.
|
||||
/// Register(0) otherwise.
|
||||
/// \p OrigRetTy is the type of the \p OrigRet.
|
||||
/// \p Args are the arguments of the lowered builtin call.
|
||||
Optional<bool> lowerBuiltin(const StringRef DemangledCall,
|
||||
InstructionSet::InstructionSet Set,
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
const Register OrigRet, const Type *OrigRetTy,
|
||||
const SmallVectorImpl<Register> &Args,
|
||||
SPIRVGlobalRegistry *GR);
|
||||
/// Handles the translation of the provided special opaque/builtin type \p Type
|
||||
/// to SPIR-V type. Generates the corresponding machine instructions for the
|
||||
/// target type or gets the already existing OpType<...> register from the
|
||||
/// global registry \p GR.
|
||||
///
|
||||
/// \return A machine instruction representing the OpType<...> SPIR-V type.
|
||||
///
|
||||
/// \p Type is the special opaque/builtin type to be lowered.
|
||||
SPIRVType *lowerBuiltinType(const StructType *Type,
|
||||
AccessQualifier::AccessQualifier AccessQual,
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
SPIRVGlobalRegistry *GR);
|
||||
} // namespace SPIRV
|
||||
} // namespace llvm
|
||||
#endif // LLVM_LIB_TARGET_SPIRV_SPIRVBUILTINS_H
|
||||
|
|
|
@ -1104,22 +1104,36 @@ def DemangledTypes : GenericTable {
|
|||
}
|
||||
|
||||
// Function to lookup builtin types by their demangled name.
|
||||
def lookupType : SearchIndex {
|
||||
def lookupBuiltinType : SearchIndex {
|
||||
let Table = DemangledTypes;
|
||||
let Key = ["Name"];
|
||||
}
|
||||
|
||||
// OpenCL builtin types:
|
||||
def : DemangledType<"opencl.reserve_id_t", OpTypeReserveId>;
|
||||
def : DemangledType<"opencl.event_t", OpTypeEvent>;
|
||||
def : DemangledType<"opencl.queue_t", OpTypeQueue>;
|
||||
def : DemangledType<"opencl.sampler_t", OpTypeSampler>;
|
||||
def : DemangledType<"opencl.clk_event_t", OpTypeDeviceEvent>;
|
||||
def : DemangledType<"opencl.clk_event_t", OpTypeDeviceEvent>;
|
||||
|
||||
def : DemangledType<"spirv.ReserveId", OpTypeReserveId>;
|
||||
def : DemangledType<"spirv.PipeStorage", OpTypePipeStorage>;
|
||||
def : DemangledType<"spirv.Queue", OpTypeQueue>;
|
||||
def : DemangledType<"spirv.Event", OpTypeEvent>;
|
||||
def : DemangledType<"spirv.Sampler", OpTypeSampler>;
|
||||
def : DemangledType<"spirv.DeviceEvent", OpTypeDeviceEvent>;
|
||||
|
||||
// Some SPIR-V builtin types (e.g. spirv.Image) have a complex list of
|
||||
// parameters as part of their name. Some of those parameters should be treated
|
||||
// as numeric literals and therefore they cannot be represented in TableGen and
|
||||
// should be parsed instead.
|
||||
def : DemangledType<"spirv.Image", OpTypeImage>;
|
||||
def : DemangledType<"spirv.SampledImage", OpTypeSampledImage>;
|
||||
def : DemangledType<"spirv.Pipe", OpTypePipe>;
|
||||
|
||||
// Class definining lowering details for various variants of image type indentifiers.
|
||||
class ImageType<string name> {
|
||||
string Name = name;
|
||||
string Type = "void";
|
||||
AccessQualifier Qualifier = !cond(!not(!eq(!find(name, "_ro_t"), -1)) : ReadOnly,
|
||||
!not(!eq(!find(name, "_wo_t"), -1)) : WriteOnly,
|
||||
!not(!eq(!find(name, "_rw_t"), -1)) : ReadWrite,
|
||||
|
@ -1130,14 +1144,19 @@ class ImageType<string name> {
|
|||
!not(!eq(!find(name, "image3"), -1)) : DIM_3D);
|
||||
bit Arrayed = !not(!eq(!find(name, "array"), -1));
|
||||
bit Depth = !not(!eq(!find(name, "depth"), -1));
|
||||
bit Multisampled = false;
|
||||
bit Sampled = false;
|
||||
ImageFormat Format = Unknown;
|
||||
}
|
||||
|
||||
// Table gathering all the image type records.
|
||||
def ImageTypes : GenericTable {
|
||||
let FilterClass = "ImageType";
|
||||
let Fields = ["Name", "Qualifier", "Dimensionality", "Arrayed", "Depth"];
|
||||
let Fields = ["Name", "Type", "Qualifier", "Dimensionality", "Arrayed",
|
||||
"Depth", "Multisampled", "Sampled", "Format"];
|
||||
string TypeOf_Qualifier = "AccessQualifier";
|
||||
string TypeOf_Dimensionality = "Dim";
|
||||
string TypeOf_Format = "ImageFormat";
|
||||
}
|
||||
|
||||
// Function to lookup builtin image types by their demangled name.
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
Instruction *visitStoreInst(StoreInst &I);
|
||||
Instruction *visitAllocaInst(AllocaInst &I);
|
||||
Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
|
||||
Instruction *visitUnreachableInst(UnreachableInst &I);
|
||||
bool runOnFunction(Function &F) override;
|
||||
};
|
||||
} // namespace
|
||||
|
@ -313,7 +314,13 @@ Instruction *SPIRVEmitIntrinsics::visitStoreInst(StoreInst &I) {
|
|||
|
||||
Instruction *SPIRVEmitIntrinsics::visitAllocaInst(AllocaInst &I) {
|
||||
TrackConstants = false;
|
||||
return &I;
|
||||
Type *PtrTy = I.getType();
|
||||
auto *NewI = IRB->CreateIntrinsic(Intrinsic::spv_alloca, {PtrTy}, {});
|
||||
std::string InstName = I.hasName() ? I.getName().str() : "";
|
||||
I.replaceAllUsesWith(NewI);
|
||||
I.eraseFromParent();
|
||||
NewI->setName(InstName);
|
||||
return NewI;
|
||||
}
|
||||
|
||||
Instruction *SPIRVEmitIntrinsics::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
|
||||
|
@ -332,6 +339,12 @@ Instruction *SPIRVEmitIntrinsics::visitAtomicCmpXchgInst(AtomicCmpXchgInst &I) {
|
|||
return NewI;
|
||||
}
|
||||
|
||||
Instruction *SPIRVEmitIntrinsics::visitUnreachableInst(UnreachableInst &I) {
|
||||
IRB->SetInsertPoint(&I);
|
||||
IRB->CreateIntrinsic(Intrinsic::spv_unreachable, {}, {});
|
||||
return &I;
|
||||
}
|
||||
|
||||
void SPIRVEmitIntrinsics::processGlobalValue(GlobalVariable &GV) {
|
||||
// Skip special artifical variable llvm.global.annotations.
|
||||
if (GV.getName() == "llvm.global.annotations")
|
||||
|
@ -368,7 +381,7 @@ void SPIRVEmitIntrinsics::insertAssignTypeIntrs(Instruction *I) {
|
|||
if (isa<ConstantPointerNull>(Op) || isa<UndefValue>(Op) ||
|
||||
// Check GetElementPtrConstantExpr case.
|
||||
(isa<ConstantExpr>(Op) && isa<GEPOperator>(Op))) {
|
||||
IRB->SetInsertPoint(I);
|
||||
setInsertPointSkippingPhis(*IRB, I);
|
||||
if (isa<UndefValue>(Op) && Op->getType()->isAggregateType())
|
||||
buildIntrWithMD(Intrinsic::spv_assign_type, {IRB->getInt32Ty()}, Op,
|
||||
UndefValue::get(IRB->getInt32Ty()));
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "SPIRVGlobalRegistry.h"
|
||||
#include "SPIRV.h"
|
||||
#include "SPIRVBuiltins.h"
|
||||
#include "SPIRVSubtarget.h"
|
||||
#include "SPIRVTargetMachine.h"
|
||||
#include "SPIRVUtils.h"
|
||||
|
@ -479,10 +480,10 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
|
|||
if (IsConst)
|
||||
buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Constant, {});
|
||||
|
||||
if (GVar && GVar->getAlign().valueOrOne().value() != 1)
|
||||
buildOpDecorate(
|
||||
Reg, MIRBuilder, SPIRV::Decoration::Alignment,
|
||||
{static_cast<uint32_t>(GVar->getAlign().valueOrOne().value())});
|
||||
if (GVar && GVar->getAlign().valueOrOne().value() != 1) {
|
||||
unsigned Alignment = (unsigned)GVar->getAlign().valueOrOne().value();
|
||||
buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::Alignment, {Alignment});
|
||||
}
|
||||
|
||||
if (HasLinkageTy)
|
||||
buildOpDecorate(Reg, MIRBuilder, SPIRV::Decoration::LinkageAttributes,
|
||||
|
@ -563,6 +564,20 @@ static bool isSpecialType(const Type *Ty) {
|
|||
return false;
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOrCreateSpecialType(
|
||||
const Type *Ty, MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccQual) {
|
||||
// Some OpenCL and SPIRV builtins like image2d_t are passed in as
|
||||
// pointers, but should be treated as custom types like OpTypeImage.
|
||||
if (auto PType = dyn_cast<PointerType>(Ty)) {
|
||||
assert(!PType->isOpaque());
|
||||
Ty = PType->getNonOpaquePointerElementType();
|
||||
}
|
||||
auto SType = cast<StructType>(Ty);
|
||||
assert(isOpenCLBuiltinType(SType) || isSPIRVBuiltinType(SType));
|
||||
return SPIRV::lowerBuiltinType(SType, AccQual, MIRBuilder, this);
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOpTypePointer(
|
||||
SPIRV::StorageClass::StorageClass SC, SPIRVType *ElemType,
|
||||
MachineIRBuilder &MIRBuilder, Register Reg) {
|
||||
|
@ -624,7 +639,8 @@ Register SPIRVGlobalRegistry::getSPIRVTypeID(const SPIRVType *SpirvType) const {
|
|||
SPIRVType *SPIRVGlobalRegistry::createSPIRVType(
|
||||
const Type *Ty, MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccQual, bool EmitIR) {
|
||||
assert(!isSpecialType(Ty));
|
||||
if (isSpecialType(Ty))
|
||||
return getOrCreateSpecialType(Ty, MIRBuilder, AccQual);
|
||||
auto &TypeToSPIRVTypeMap = DT.getTypes()->getAllUses();
|
||||
auto t = TypeToSPIRVTypeMap.find(Ty);
|
||||
if (t != TypeToSPIRVTypeMap.end()) {
|
||||
|
@ -729,7 +745,7 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType(
|
|||
const Type *Ty, MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccessQual, bool EmitIR) {
|
||||
Register Reg = DT.find(Ty, &MIRBuilder.getMF());
|
||||
if (Reg.isValid())
|
||||
if (Reg.isValid() && !isSpecialType(Ty))
|
||||
return getSPIRVTypeForVReg(Reg);
|
||||
TypesInProcessing.clear();
|
||||
SPIRVType *STy = restOfCreateSPIRVType(Ty, MIRBuilder, AccessQual, EmitIR);
|
||||
|
@ -804,6 +820,53 @@ SPIRVGlobalRegistry::getPointerStorageClass(Register VReg) const {
|
|||
Type->getOperand(1).getImm());
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage(
|
||||
MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim,
|
||||
uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled,
|
||||
SPIRV::ImageFormat::ImageFormat ImageFormat,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccessQual) {
|
||||
SPIRV::ImageTypeDescriptor TD(SPIRVToLLVMType.lookup(SampledType), Dim, Depth,
|
||||
Arrayed, Multisampled, Sampled, ImageFormat,
|
||||
AccessQual);
|
||||
if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
|
||||
return Res;
|
||||
Register ResVReg = createTypeVReg(MIRBuilder);
|
||||
DT.add(TD, &MIRBuilder.getMF(), ResVReg);
|
||||
return MIRBuilder.buildInstr(SPIRV::OpTypeImage)
|
||||
.addDef(ResVReg)
|
||||
.addUse(getSPIRVTypeID(SampledType))
|
||||
.addImm(Dim)
|
||||
.addImm(Depth) // Depth (whether or not it is a Depth image).
|
||||
.addImm(Arrayed) // Arrayed.
|
||||
.addImm(Multisampled) // Multisampled (0 = only single-sample).
|
||||
.addImm(Sampled) // Sampled (0 = usage known at runtime).
|
||||
.addImm(ImageFormat)
|
||||
.addImm(AccessQual);
|
||||
}
|
||||
|
||||
SPIRVType *
|
||||
SPIRVGlobalRegistry::getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder) {
|
||||
SPIRV::SamplerTypeDescriptor TD;
|
||||
if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
|
||||
return Res;
|
||||
Register ResVReg = createTypeVReg(MIRBuilder);
|
||||
DT.add(TD, &MIRBuilder.getMF(), ResVReg);
|
||||
return MIRBuilder.buildInstr(SPIRV::OpTypeSampler).addDef(ResVReg);
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypePipe(
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccessQual) {
|
||||
SPIRV::PipeTypeDescriptor TD(AccessQual);
|
||||
if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
|
||||
return Res;
|
||||
Register ResVReg = createTypeVReg(MIRBuilder);
|
||||
DT.add(TD, &MIRBuilder.getMF(), ResVReg);
|
||||
return MIRBuilder.buildInstr(SPIRV::OpTypePipe)
|
||||
.addDef(ResVReg)
|
||||
.addImm(AccessQual);
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeSampledImage(
|
||||
SPIRVType *ImageType, MachineIRBuilder &MIRBuilder) {
|
||||
SPIRV::SampledImageTypeDescriptor TD(
|
||||
|
@ -813,11 +876,20 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeSampledImage(
|
|||
if (auto *Res = checkSpecialInstr(TD, MIRBuilder))
|
||||
return Res;
|
||||
Register ResVReg = createTypeVReg(MIRBuilder);
|
||||
auto MIB = MIRBuilder.buildInstr(SPIRV::OpTypeSampledImage)
|
||||
.addDef(ResVReg)
|
||||
.addUse(getSPIRVTypeID(ImageType));
|
||||
DT.add(TD, &MIRBuilder.getMF(), ResVReg);
|
||||
return MIB;
|
||||
return MIRBuilder.buildInstr(SPIRV::OpTypeSampledImage)
|
||||
.addDef(ResVReg)
|
||||
.addUse(getSPIRVTypeID(ImageType));
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeByOpcode(
|
||||
const Type *Ty, MachineIRBuilder &MIRBuilder, unsigned Opcode) {
|
||||
Register ResVReg = DT.find(Ty, &MIRBuilder.getMF());
|
||||
if (ResVReg.isValid())
|
||||
return MIRBuilder.getMF().getRegInfo().getUniqueVRegDef(ResVReg);
|
||||
ResVReg = createTypeVReg(MIRBuilder);
|
||||
DT.add(Ty, &MIRBuilder.getMF(), ResVReg);
|
||||
return MIRBuilder.buildInstr(Opcode).addDef(ResVReg);
|
||||
}
|
||||
|
||||
const MachineInstr *
|
||||
|
@ -942,6 +1014,24 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVVectorType(
|
|||
return finishCreatingSPIRVType(LLVMTy, MIB);
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVArrayType(
|
||||
SPIRVType *BaseType, unsigned NumElements, MachineInstr &I,
|
||||
const SPIRVInstrInfo &TII) {
|
||||
Type *LLVMTy = ArrayType::get(
|
||||
const_cast<Type *>(getTypeForSPIRVType(BaseType)), NumElements);
|
||||
Register Reg = DT.find(LLVMTy, CurMF);
|
||||
if (Reg.isValid())
|
||||
return getSPIRVTypeForVReg(Reg);
|
||||
MachineBasicBlock &BB = *I.getParent();
|
||||
SPIRVType *SpirvType = getOrCreateSPIRVIntegerType(32, I, TII);
|
||||
Register Len = getOrCreateConstInt(NumElements, I, SpirvType, TII);
|
||||
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpTypeArray))
|
||||
.addDef(createTypeVReg(CurMF->getRegInfo()))
|
||||
.addUse(getSPIRVTypeID(BaseType))
|
||||
.addUse(Len);
|
||||
return finishCreatingSPIRVType(LLVMTy, MIB);
|
||||
}
|
||||
|
||||
SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVPointerType(
|
||||
SPIRVType *BaseType, MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::StorageClass::StorageClass SClass) {
|
||||
|
|
|
@ -208,6 +208,11 @@ private:
|
|||
SPIRVType *getOpTypeFunction(SPIRVType *RetType,
|
||||
const SmallVectorImpl<SPIRVType *> &ArgTypes,
|
||||
MachineIRBuilder &MIRBuilder);
|
||||
|
||||
SPIRVType *
|
||||
getOrCreateSpecialType(const Type *Ty, MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccQual);
|
||||
|
||||
std::tuple<Register, ConstantInt *, bool> getOrCreateConstIntReg(
|
||||
uint64_t Val, SPIRVType *SpvType, MachineIRBuilder *MIRBuilder,
|
||||
MachineInstr *I = nullptr, const SPIRVInstrInfo *TII = nullptr);
|
||||
|
@ -240,7 +245,6 @@ public:
|
|||
SPIRVType *SpvType, bool EmitIR = true);
|
||||
Register getOrCreateConsIntArray(uint64_t Val, MachineIRBuilder &MIRBuilder,
|
||||
SPIRVType *SpvType, bool EmitIR = true);
|
||||
|
||||
Register buildConstantSampler(Register Res, unsigned AddrMode, unsigned Param,
|
||||
unsigned FilerMode,
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
|
@ -270,19 +274,39 @@ public:
|
|||
SPIRVType *getOrCreateSPIRVVectorType(SPIRVType *BaseType,
|
||||
unsigned NumElements, MachineInstr &I,
|
||||
const SPIRVInstrInfo &TII);
|
||||
SPIRVType *getOrCreateSPIRVArrayType(SPIRVType *BaseType,
|
||||
unsigned NumElements, MachineInstr &I,
|
||||
const SPIRVInstrInfo &TII);
|
||||
|
||||
SPIRVType *getOrCreateSPIRVPointerType(
|
||||
SPIRVType *BaseType, MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::StorageClass::StorageClass SClass = SPIRV::StorageClass::Function);
|
||||
SPIRVType *getOrCreateSPIRVPointerType(
|
||||
SPIRVType *BaseType, MachineInstr &I, const SPIRVInstrInfo &TII,
|
||||
SPIRV::StorageClass::StorageClass SClass = SPIRV::StorageClass::Function);
|
||||
|
||||
SPIRVType *
|
||||
getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType,
|
||||
SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed,
|
||||
uint32_t Multisampled, uint32_t Sampled,
|
||||
SPIRV::ImageFormat::ImageFormat ImageFormat,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccQual);
|
||||
|
||||
SPIRVType *getOrCreateOpTypeSampler(MachineIRBuilder &MIRBuilder);
|
||||
|
||||
SPIRVType *getOrCreateOpTypeSampledImage(SPIRVType *ImageType,
|
||||
MachineIRBuilder &MIRBuilder);
|
||||
|
||||
SPIRVType *
|
||||
getOrCreateOpTypePipe(MachineIRBuilder &MIRBuilder,
|
||||
SPIRV::AccessQualifier::AccessQualifier AccQual);
|
||||
SPIRVType *getOrCreateOpTypeFunctionWithArgs(
|
||||
const Type *Ty, SPIRVType *RetType,
|
||||
const SmallVectorImpl<SPIRVType *> &ArgTypes,
|
||||
MachineIRBuilder &MIRBuilder);
|
||||
SPIRVType *getOrCreateOpTypeByOpcode(const Type *Ty,
|
||||
MachineIRBuilder &MIRBuilder,
|
||||
unsigned Opcode);
|
||||
};
|
||||
} // end namespace llvm
|
||||
#endif // LLLVM_LIB_TARGET_SPIRV_SPIRVTYPEMANAGER_H
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
#define DEBUG_TYPE "spirv-isel"
|
||||
|
||||
using namespace llvm;
|
||||
namespace CL = SPIRV::OpenCLExtInst;
|
||||
namespace GL = SPIRV::GLSLExtInst;
|
||||
|
||||
using ExtInstList =
|
||||
std::vector<std::pair<SPIRV::InstructionSet::InstructionSet, uint32_t>>;
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -132,9 +137,8 @@ private:
|
|||
bool selectTrunc(Register ResVReg, const SPIRVType *ResType,
|
||||
MachineInstr &I) const;
|
||||
|
||||
bool selectIntToBool(Register IntReg, Register ResVReg,
|
||||
const SPIRVType *intTy, const SPIRVType *boolTy,
|
||||
MachineInstr &I) const;
|
||||
bool selectIntToBool(Register IntReg, Register ResVReg, MachineInstr &I,
|
||||
const SPIRVType *intTy, const SPIRVType *boolTy) const;
|
||||
|
||||
bool selectOpUndef(Register ResVReg, const SPIRVType *ResType,
|
||||
MachineInstr &I) const;
|
||||
|
@ -160,6 +164,14 @@ private:
|
|||
bool selectPhi(Register ResVReg, const SPIRVType *ResType,
|
||||
MachineInstr &I) const;
|
||||
|
||||
bool selectExtInst(Register ResVReg, const SPIRVType *ResType,
|
||||
MachineInstr &I, CL::OpenCLExtInst CLInst) const;
|
||||
bool selectExtInst(Register ResVReg, const SPIRVType *ResType,
|
||||
MachineInstr &I, CL::OpenCLExtInst CLInst,
|
||||
GL::GLSLExtInst GLInst) const;
|
||||
bool selectExtInst(Register ResVReg, const SPIRVType *ResType,
|
||||
MachineInstr &I, const ExtInstList &ExtInsts) const;
|
||||
|
||||
Register buildI32Constant(uint32_t Val, MachineInstr &I,
|
||||
const SPIRVType *ResType = nullptr) const;
|
||||
|
||||
|
@ -283,6 +295,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
|
|||
}
|
||||
case TargetOpcode::G_MEMMOVE:
|
||||
case TargetOpcode::G_MEMCPY:
|
||||
case TargetOpcode::G_MEMSET:
|
||||
return selectMemOperation(ResVReg, I);
|
||||
|
||||
case TargetOpcode::G_ICMP:
|
||||
|
@ -318,6 +331,85 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
|
|||
|
||||
case TargetOpcode::G_CTPOP:
|
||||
return selectUnOp(ResVReg, ResType, I, SPIRV::OpBitCount);
|
||||
case TargetOpcode::G_SMIN:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::s_min, GL::SMin);
|
||||
case TargetOpcode::G_UMIN:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::u_min, GL::UMin);
|
||||
|
||||
case TargetOpcode::G_SMAX:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::s_max, GL::SMax);
|
||||
case TargetOpcode::G_UMAX:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::u_max, GL::UMax);
|
||||
|
||||
case TargetOpcode::G_FMA:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::fma, GL::Fma);
|
||||
|
||||
case TargetOpcode::G_FPOW:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::pow, GL::Pow);
|
||||
case TargetOpcode::G_FPOWI:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::pown);
|
||||
|
||||
case TargetOpcode::G_FEXP:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::exp, GL::Exp);
|
||||
case TargetOpcode::G_FEXP2:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::exp2, GL::Exp2);
|
||||
|
||||
case TargetOpcode::G_FLOG:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::log, GL::Log);
|
||||
case TargetOpcode::G_FLOG2:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::log2, GL::Log2);
|
||||
case TargetOpcode::G_FLOG10:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::log10);
|
||||
|
||||
case TargetOpcode::G_FABS:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::fabs, GL::FAbs);
|
||||
case TargetOpcode::G_ABS:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::s_abs, GL::SAbs);
|
||||
|
||||
case TargetOpcode::G_FMINNUM:
|
||||
case TargetOpcode::G_FMINIMUM:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::fmin, GL::FMin);
|
||||
case TargetOpcode::G_FMAXNUM:
|
||||
case TargetOpcode::G_FMAXIMUM:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::fmax, GL::FMax);
|
||||
|
||||
case TargetOpcode::G_FCOPYSIGN:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::copysign);
|
||||
|
||||
case TargetOpcode::G_FCEIL:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::ceil, GL::Ceil);
|
||||
case TargetOpcode::G_FFLOOR:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::floor, GL::Floor);
|
||||
|
||||
case TargetOpcode::G_FCOS:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::cos, GL::Cos);
|
||||
case TargetOpcode::G_FSIN:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::sin, GL::Sin);
|
||||
|
||||
case TargetOpcode::G_FSQRT:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::sqrt, GL::Sqrt);
|
||||
|
||||
case TargetOpcode::G_CTTZ:
|
||||
case TargetOpcode::G_CTTZ_ZERO_UNDEF:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::ctz);
|
||||
case TargetOpcode::G_CTLZ:
|
||||
case TargetOpcode::G_CTLZ_ZERO_UNDEF:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::clz);
|
||||
|
||||
case TargetOpcode::G_INTRINSIC_ROUND:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::round, GL::Round);
|
||||
case TargetOpcode::G_INTRINSIC_ROUNDEVEN:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::rint, GL::RoundEven);
|
||||
case TargetOpcode::G_INTRINSIC_TRUNC:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::trunc, GL::Trunc);
|
||||
case TargetOpcode::G_FRINT:
|
||||
case TargetOpcode::G_FNEARBYINT:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::rint, GL::RoundEven);
|
||||
|
||||
case TargetOpcode::G_SMULH:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::s_mul_hi);
|
||||
case TargetOpcode::G_UMULH:
|
||||
return selectExtInst(ResVReg, ResType, I, CL::u_mul_hi);
|
||||
|
||||
case TargetOpcode::G_SEXT:
|
||||
return selectExt(ResVReg, ResType, I, true);
|
||||
|
@ -394,6 +486,48 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
|
|||
}
|
||||
}
|
||||
|
||||
bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
|
||||
const SPIRVType *ResType,
|
||||
MachineInstr &I,
|
||||
CL::OpenCLExtInst CLInst) const {
|
||||
return selectExtInst(ResVReg, ResType, I,
|
||||
{{SPIRV::InstructionSet::OpenCL_std, CLInst}});
|
||||
}
|
||||
|
||||
bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
|
||||
const SPIRVType *ResType,
|
||||
MachineInstr &I,
|
||||
CL::OpenCLExtInst CLInst,
|
||||
GL::GLSLExtInst GLInst) const {
|
||||
ExtInstList ExtInsts = {{SPIRV::InstructionSet::OpenCL_std, CLInst},
|
||||
{SPIRV::InstructionSet::GLSL_std_450, GLInst}};
|
||||
return selectExtInst(ResVReg, ResType, I, ExtInsts);
|
||||
}
|
||||
|
||||
bool SPIRVInstructionSelector::selectExtInst(Register ResVReg,
|
||||
const SPIRVType *ResType,
|
||||
MachineInstr &I,
|
||||
const ExtInstList &Insts) const {
|
||||
|
||||
for (const auto &Ex : Insts) {
|
||||
SPIRV::InstructionSet::InstructionSet Set = Ex.first;
|
||||
uint32_t Opcode = Ex.second;
|
||||
if (STI.canUseExtInstSet(Set)) {
|
||||
MachineBasicBlock &BB = *I.getParent();
|
||||
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
|
||||
.addDef(ResVReg)
|
||||
.addUse(GR.getSPIRVTypeID(ResType))
|
||||
.addImm(static_cast<uint32_t>(Set))
|
||||
.addImm(Opcode);
|
||||
const unsigned NumOps = I.getNumOperands();
|
||||
for (unsigned i = 1; i < NumOps; ++i)
|
||||
MIB.add(I.getOperand(i));
|
||||
return MIB.constrainAllUses(TII, TRI, RBI);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SPIRVInstructionSelector::selectUnOpWithSrc(Register ResVReg,
|
||||
const SPIRVType *ResType,
|
||||
MachineInstr &I,
|
||||
|
@ -493,9 +627,39 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const {
|
|||
bool SPIRVInstructionSelector::selectMemOperation(Register ResVReg,
|
||||
MachineInstr &I) const {
|
||||
MachineBasicBlock &BB = *I.getParent();
|
||||
Register SrcReg = I.getOperand(1).getReg();
|
||||
if (I.getOpcode() == TargetOpcode::G_MEMSET) {
|
||||
assert(I.getOperand(1).isReg() && I.getOperand(2).isReg());
|
||||
unsigned Val = getIConstVal(I.getOperand(1).getReg(), MRI);
|
||||
unsigned Num = getIConstVal(I.getOperand(2).getReg(), MRI);
|
||||
SPIRVType *ValTy = GR.getOrCreateSPIRVIntegerType(8, I, TII);
|
||||
SPIRVType *ArrTy = GR.getOrCreateSPIRVArrayType(ValTy, Num, I, TII);
|
||||
Register Const = GR.getOrCreateConsIntArray(Val, I, ArrTy, TII);
|
||||
SPIRVType *VarTy = GR.getOrCreateSPIRVPointerType(
|
||||
ArrTy, I, TII, SPIRV::StorageClass::UniformConstant);
|
||||
// TODO: check if we have such GV, add init, use buildGlobalVariable.
|
||||
Type *LLVMArrTy = ArrayType::get(
|
||||
IntegerType::get(GR.CurMF->getFunction().getContext(), 8), Num);
|
||||
GlobalVariable *GV =
|
||||
new GlobalVariable(LLVMArrTy, true, GlobalValue::InternalLinkage);
|
||||
Register VarReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
|
||||
GR.add(GV, GR.CurMF, VarReg);
|
||||
|
||||
buildOpDecorate(VarReg, I, TII, SPIRV::Decoration::Constant, {});
|
||||
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpVariable))
|
||||
.addDef(VarReg)
|
||||
.addUse(GR.getSPIRVTypeID(VarTy))
|
||||
.addImm(SPIRV::StorageClass::UniformConstant)
|
||||
.addUse(Const)
|
||||
.constrainAllUses(TII, TRI, RBI);
|
||||
SPIRVType *SourceTy = GR.getOrCreateSPIRVPointerType(
|
||||
ValTy, I, TII, SPIRV::StorageClass::UniformConstant);
|
||||
SrcReg = MRI->createGenericVirtualRegister(LLT::scalar(32));
|
||||
selectUnOpWithSrc(SrcReg, SourceTy, I, VarReg, SPIRV::OpBitcast);
|
||||
}
|
||||
auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpCopyMemorySized))
|
||||
.addUse(I.getOperand(0).getReg())
|
||||
.addUse(I.getOperand(1).getReg())
|
||||
.addUse(SrcReg)
|
||||
.addUse(I.getOperand(2).getReg());
|
||||
if (I.getNumMemOperands())
|
||||
addMemoryOperands(*I.memoperands_begin(), MIB);
|
||||
|
@ -974,9 +1138,9 @@ bool SPIRVInstructionSelector::selectExt(Register ResVReg,
|
|||
|
||||
bool SPIRVInstructionSelector::selectIntToBool(Register IntReg,
|
||||
Register ResVReg,
|
||||
MachineInstr &I,
|
||||
const SPIRVType *IntTy,
|
||||
const SPIRVType *BoolTy,
|
||||
MachineInstr &I) const {
|
||||
const SPIRVType *BoolTy) const {
|
||||
// To truncate to a bool, we use OpBitwiseAnd 1 and OpINotEqual to zero.
|
||||
Register BitIntReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
|
||||
bool IsVectorTy = IntTy->getOpcode() == SPIRV::OpTypeVector;
|
||||
|
@ -1004,7 +1168,7 @@ bool SPIRVInstructionSelector::selectTrunc(Register ResVReg,
|
|||
if (GR.isScalarOrVectorOfType(ResVReg, SPIRV::OpTypeBool)) {
|
||||
Register IntReg = I.getOperand(1).getReg();
|
||||
const SPIRVType *ArgType = GR.getSPIRVTypeForVReg(IntReg);
|
||||
return selectIntToBool(IntReg, ResVReg, ArgType, ResType, I);
|
||||
return selectIntToBool(IntReg, ResVReg, I, ArgType, ResType);
|
||||
}
|
||||
bool IsSigned = GR.isScalarOrVectorSigned(ResType);
|
||||
unsigned Opcode = IsSigned ? SPIRV::OpSConvert : SPIRV::OpUConvert;
|
||||
|
@ -1223,6 +1387,12 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
|
|||
case Intrinsic::spv_cmpxchg:
|
||||
return selectAtomicCmpXchg(ResVReg, ResType, I);
|
||||
break;
|
||||
case Intrinsic::spv_unreachable:
|
||||
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpUnreachable));
|
||||
break;
|
||||
case Intrinsic::spv_alloca:
|
||||
return selectFrameIndex(ResVReg, ResType, I);
|
||||
break;
|
||||
default:
|
||||
llvm_unreachable("Intrinsic selection not implemented");
|
||||
}
|
||||
|
|
|
@ -255,6 +255,18 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
|
|||
getActionDefinitionsBuilder(G_FPOWI).legalForCartesianProduct(
|
||||
allFloatScalarsAndVectors, allIntScalarsAndVectors);
|
||||
|
||||
if (ST.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
|
||||
getActionDefinitionsBuilder(G_FLOG10).legalFor(allFloatScalarsAndVectors);
|
||||
|
||||
getActionDefinitionsBuilder(
|
||||
{G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTLZ, G_CTLZ_ZERO_UNDEF})
|
||||
.legalForCartesianProduct(allIntScalarsAndVectors,
|
||||
allIntScalarsAndVectors);
|
||||
|
||||
// Struct return types become a single scalar, so cannot easily legalize.
|
||||
getActionDefinitionsBuilder({G_SMULH, G_UMULH}).alwaysLegal();
|
||||
}
|
||||
|
||||
getLegacyLegalizerInfo().computeTables();
|
||||
verify(*ST.getInstrInfo());
|
||||
}
|
||||
|
|
|
@ -734,7 +734,7 @@ class ImageFormat<string name, bits<32> value> {
|
|||
}
|
||||
|
||||
multiclass ImageFormatOperand<bits<32> value, list<Capability> reqCapabilities> {
|
||||
def : ImageFormat<NAME, value>;
|
||||
def NAME : ImageFormat<NAME, value>;
|
||||
defm : SymbolicOperandWithRequirements<ImageFormatOperand, value, NAME, 0, 0, [], reqCapabilities>;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,397 @@
|
|||
; RUN: llc %s -mtriple=spirv32-unknown-unknown -o - | FileCheck %s
|
||||
|
||||
declare float @llvm.fabs.f32(float)
|
||||
declare float @llvm.rint.f32(float)
|
||||
declare float @llvm.nearbyint.f32(float)
|
||||
declare float @llvm.floor.f32(float)
|
||||
declare float @llvm.ceil.f32(float)
|
||||
declare float @llvm.round.f32(float)
|
||||
declare float @llvm.trunc.f32(float)
|
||||
declare float @llvm.sqrt.f32(float)
|
||||
declare float @llvm.sin.f32(float)
|
||||
declare float @llvm.cos.f32(float)
|
||||
declare float @llvm.exp2.f32(float)
|
||||
declare float @llvm.log.f32(float)
|
||||
declare float @llvm.log10.f32(float)
|
||||
declare float @llvm.log2.f32(float)
|
||||
declare float @llvm.minnum.f32(float, float)
|
||||
declare float @llvm.maxnum.f32(float, float)
|
||||
declare <2 x half> @llvm.fabs.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.rint.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.nearbyint.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.floor.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.ceil.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.round.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.trunc.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.sqrt.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.sin.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.cos.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.exp2.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.log.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.log10.v2f16(<2 x half>)
|
||||
declare <2 x half> @llvm.log2.v2f16(<2 x half>)
|
||||
|
||||
; CHECK-DAG: OpName %[[#SCALAR_FABS:]] "scalar_fabs"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_RINT:]] "scalar_rint"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_NEARBYINT:]] "scalar_nearbyint"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_FLOOR:]] "scalar_floor"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_CEIL:]] "scalar_ceil"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_ROUND:]] "scalar_round"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_TRUNC:]] "scalar_trunc"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_SQRT:]] "scalar_sqrt"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_SIN:]] "scalar_sin"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_COS:]] "scalar_cos"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_EXP2:]] "scalar_exp2"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_LOG:]] "scalar_log"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_LOG10:]] "scalar_log10"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_LOG2:]] "scalar_log2"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_MINNUM:]] "scalar_minnum"
|
||||
; CHECK-DAG: OpName %[[#SCALAR_MAXNUM:]] "scalar_maxnum"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_FABS:]] "vector_fabs"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_RINT:]] "vector_rint"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_NEARBYINT:]] "vector_nearbyint"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_FLOOR:]] "vector_floor"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_CEIL:]] "vector_ceil"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_ROUND:]] "vector_round"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_TRUNC:]] "vector_trunc"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_SQRT:]] "vector_sqrt"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_SIN:]] "vector_sin"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_COS:]] "vector_cos"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_EXP2:]] "vector_exp2"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_LOG:]] "vector_log"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_LOG10:]] "vector_log10"
|
||||
; CHECK-DAG: OpName %[[#VECTOR_LOG2:]] "vector_log2"
|
||||
|
||||
; CHECK-DAG: %[[#CLEXT:]] = OpExtInstImport "OpenCL.std"
|
||||
|
||||
; CHECK: %[[#SCALAR_FABS]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fabs %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_fabs(float %a) {
|
||||
%r = call float @llvm.fabs.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_RINT]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_rint(float %a) {
|
||||
%r = call float @llvm.rint.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_NEARBYINT]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_nearbyint(float %a) {
|
||||
%r = call float @llvm.nearbyint.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_FLOOR]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] floor %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_floor(float %a) {
|
||||
%r = call float @llvm.floor.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_CEIL]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] ceil %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_ceil(float %a) {
|
||||
%r = call float @llvm.ceil.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_ROUND]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] round %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_round(float %a) {
|
||||
%r = call float @llvm.round.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_TRUNC]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] trunc %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_trunc(float %a) {
|
||||
%r = call float @llvm.trunc.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_SQRT]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sqrt %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_sqrt(float %a) {
|
||||
%r = call float @llvm.sqrt.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_SIN]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sin %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_sin(float %a) {
|
||||
%r = call float @llvm.sin.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_COS]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] cos %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_cos(float %a) {
|
||||
%r = call float @llvm.cos.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_EXP2]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] exp2 %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_exp2(float %a) {
|
||||
%r = call float @llvm.exp2.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_LOG]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_log(float %a) {
|
||||
%r = call float @llvm.log.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_LOG10]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log10 %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_log10(float %a) {
|
||||
%r = call float @llvm.log10.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_LOG2]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log2 %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_log2(float %a) {
|
||||
%r = call float @llvm.log2.f32(float %a)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_FABS]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fabs %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_fabs(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.fabs.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_RINT]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_rint(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.rint.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_NEARBYINT]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] rint %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_nearbyint(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.nearbyint.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_FLOOR]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] floor %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_floor(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.floor.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_CEIL]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] ceil %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_ceil(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.ceil.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_ROUND]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] round %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_round(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.round.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_TRUNC]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] trunc %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_trunc(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.trunc.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_SQRT]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sqrt %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_sqrt(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.sqrt.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_SIN]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] sin %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_sin(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.sin.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_COS]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] cos %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_cos(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.cos.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_EXP2]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] exp2 %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_exp2(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.exp2.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_LOG]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_log(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.log.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_LOG10]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log10 %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_log10(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.log10.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#VECTOR_LOG2]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] log2 %[[#A]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define <2 x half> @vector_log2(<2 x half> %a) {
|
||||
%r = call <2 x half> @llvm.log2.v2f16(<2 x half> %a)
|
||||
ret <2 x half> %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_MINNUM]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK-NEXT: %[[#B:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fmin %[[#A]] %[[#B]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_minnum(float %A, float %B) {
|
||||
%r = call float @llvm.minnum.f32(float %A, float %B)
|
||||
ret float %r
|
||||
}
|
||||
|
||||
; CHECK: %[[#SCALAR_MAXNUM]] = OpFunction
|
||||
; CHECK-NEXT: %[[#A:]] = OpFunctionParameter
|
||||
; CHECK-NEXT: %[[#B:]] = OpFunctionParameter
|
||||
; CHECK: OpLabel
|
||||
; CHECK: %[[#R:]] = OpExtInst %[[#]] %[[#CLEXT]] fmax %[[#A]] %[[#B]]
|
||||
; CHECK: OpReturnValue %[[#R]]
|
||||
; CHECK-NEXT: OpFunctionEnd
|
||||
define float @scalar_maxnum(float %A, float %B) {
|
||||
%r = call float @llvm.maxnum.f32(float %A, float %B)
|
||||
ret float %r
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
|
||||
|
||||
; CHECK: OpUnreachable
|
||||
define void @test_unreachable() {
|
||||
unreachable
|
||||
}
|
|
@ -0,0 +1,339 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
|
||||
|
||||
; CHECK: %[[#extinst_id:]] = OpExtInstImport "OpenCL.std"
|
||||
|
||||
; CHECK: %[[#var0:]] = OpTypeFloat 16
|
||||
; CHECK: %[[#var1:]] = OpTypeFloat 32
|
||||
; CHECK: %[[#var2:]] = OpTypeFloat 64
|
||||
; CHECK: %[[#var3:]] = OpTypeVector %[[#var1]] 4
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var0]] %[[#extinst_id]] fabs
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func half @TestFabs16(half %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call half @llvm.fabs.f16(half %x)
|
||||
ret half %t
|
||||
}
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] fabs
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestFabs32(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.fabs.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var2]] %[[#extinst_id]] fabs
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func double @TestFabs64(double %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call double @llvm.fabs.f64(double %x)
|
||||
ret double %t
|
||||
}
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var3]] %[[#extinst_id]] fabs
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func <4 x float> @TestFabsVec(<4 x float> %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call <4 x float> @llvm.fabs.v4f32(<4 x float> %x)
|
||||
ret <4 x float> %t
|
||||
}
|
||||
|
||||
declare half @llvm.fabs.f16(half)
|
||||
declare float @llvm.fabs.f32(float)
|
||||
declare double @llvm.fabs.f64(double)
|
||||
declare <4 x float> @llvm.fabs.v4f32(<4 x float>)
|
||||
|
||||
;; We checked several types with fabs, but the type check works the same for
|
||||
;; all intrinsics being translated, so for the rest we'll just test one type.
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] ceil
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestCeil(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.ceil.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.ceil.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#n:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] pown %[[#x]] %[[#n]]
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestPowi(float %x, i32 %n) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.powi.f32(float %x, i32 %n)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.powi.f32(float, i32)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] sin
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestSin(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.sin.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.sin.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] cos
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestCos(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.cos.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.cos.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] pow %[[#x]] %[[#y]]
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestPow(float %x, float %y) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.pow.f32(float %x, float %y)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.pow.f32(float, float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] exp
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestExp(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.exp.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.exp.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] exp2
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestExp2(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.exp2.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.exp2.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] log
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestLog(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.log.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.log.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] log10
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestLog10(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.log10.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.log10.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] log2
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestLog2(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.log2.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.log2.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmin %[[#x]] %[[#y]]
|
||||
; CHECK: OpReturnValue %[[#res]]
|
||||
|
||||
define spir_func float @TestMinNum(float %x, float %y) {
|
||||
entry:
|
||||
%t = call float @llvm.minnum.f32(float %x, float %y)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.minnum.f32(float, float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmax %[[#x]] %[[#y]]
|
||||
; CHECK: OpReturnValue %[[#res]]
|
||||
|
||||
define spir_func float @TestMaxNum(float %x, float %y) {
|
||||
entry:
|
||||
%t = call float @llvm.maxnum.f32(float %x, float %y)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.maxnum.f32(float, float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmin %[[#x]] %[[#y]]
|
||||
; CHECK: OpReturnValue %[[#res]]
|
||||
|
||||
define spir_func float @TestMinimum(float %x, float %y) {
|
||||
entry:
|
||||
%t = call float @llvm.minimum.f32(float %x, float %y)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.minimum.f32(float, float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmax %[[#x]] %[[#y]]
|
||||
; CHECK: OpReturnValue %[[#res]]
|
||||
|
||||
define spir_func float @TestMaximum(float %x, float %y) {
|
||||
entry:
|
||||
%t = call float @llvm.maximum.f32(float %x, float %y)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.maximum.f32(float, float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] copysign %[[#x]] %[[#y]]
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestCopysign(float %x, float %y) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.copysign.f32(float %x, float %y)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.copysign.f32(float, float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] floor
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestFloor(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.floor.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.floor.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] trunc
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestTrunc(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.trunc.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.trunc.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] rint
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestRint(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.rint.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.rint.f32(float)
|
||||
|
||||
;; It is intentional that nearbyint translates to rint.
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] rint
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestNearbyint(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.nearbyint.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.nearbyint.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] round
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestRound(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.round.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.round.f32(float)
|
||||
|
||||
;; It is intentional that roundeven translates to rint.
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] rint
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestRoundEven(float %x) local_unnamed_addr {
|
||||
entry:
|
||||
%t = tail call float @llvm.roundeven.f32(float %x)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.roundeven.f32(float)
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#z:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#]] = OpExtInst %[[#var1]] %[[#extinst_id]] fma %[[#x]] %[[#y]] %[[#z]]
|
||||
; CHECK: OpFunctionEnd
|
||||
|
||||
define spir_func float @TestFma(float %x, float %y, float %z) {
|
||||
entry:
|
||||
%t = tail call float @llvm.fma.f32(float %x, float %y, float %z)
|
||||
ret float %t
|
||||
}
|
||||
|
||||
declare float @llvm.fma.f32(float, float, float)
|
|
@ -0,0 +1,15 @@
|
|||
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
|
||||
|
||||
define spir_func float @Test(float %x, float %y) {
|
||||
entry:
|
||||
%0 = call float @llvm.maxnum.f32(float %x, float %y)
|
||||
ret float %0
|
||||
}
|
||||
|
||||
; CHECK: OpFunction
|
||||
; CHECK: %[[#x:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#y:]] = OpFunctionParameter %[[#]]
|
||||
; CHECK: %[[#res:]] = OpExtInst %[[#]] %[[#]] fmax %[[#x]] %[[#y]]
|
||||
; CHECK: OpReturnValue %[[#res]]
|
||||
|
||||
declare float @llvm.maxnum.f32(float, float)
|
|
@ -0,0 +1,54 @@
|
|||
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
|
||||
|
||||
;; FIXME: Write tests to ensure invalid usage of image are rejected, such as:
|
||||
;; - invalid AS (only global is allowed);
|
||||
;; - used in struct, union, array, pointer or return types;
|
||||
;; - used with invalid CV-qualifiers (const or volatile in C99).
|
||||
;; FIXME: Write further tests to cover _array, _buffer, _depth, ... types.
|
||||
|
||||
%opencl.image1d_ro_t = type opaque ;; read_only image1d_t
|
||||
%opencl.image2d_wo_t = type opaque ;; write_only image2d_t
|
||||
%opencl.image3d_rw_t = type opaque ;; read_write image3d_t
|
||||
|
||||
define void @foo(
|
||||
%opencl.image1d_ro_t addrspace(1)* %a,
|
||||
%opencl.image2d_wo_t addrspace(1)* %b,
|
||||
%opencl.image3d_rw_t addrspace(1)* %c,
|
||||
i32 addrspace(1)* %d
|
||||
) {
|
||||
%pixel = call <4 x i32> @_Z11read_imagei14ocl_image1d_roi(%opencl.image1d_ro_t addrspace(1)* %a, i32 0)
|
||||
call void @_Z12write_imagei14ocl_image2d_woDv2_iDv4_i(%opencl.image2d_wo_t addrspace(1)* %b, <2 x i32> zeroinitializer, <4 x i32> %pixel)
|
||||
%size = call i32 @_Z15get_image_width14ocl_image3d_rw(%opencl.image3d_rw_t addrspace(1)* %c)
|
||||
store i32 %size, i32 addrspace(1)* %d
|
||||
ret void
|
||||
}
|
||||
|
||||
declare <4 x i32> @_Z11read_imagei14ocl_image1d_roi(%opencl.image1d_ro_t addrspace(1)*, i32)
|
||||
|
||||
declare void @_Z12write_imagei14ocl_image2d_woDv2_iDv4_i(%opencl.image2d_wo_t addrspace(1)*, <2 x i32>, <4 x i32>)
|
||||
|
||||
declare i32 @_Z15get_image_width14ocl_image3d_rw(%opencl.image3d_rw_t addrspace(1)*)
|
||||
|
||||
|
||||
;; Capabilities:
|
||||
; CHECK-DAG: OpCapability ImageReadWrite
|
||||
; CHECK-NOT: DAG-FENCE
|
||||
|
||||
;; Types, Constants and Variables:
|
||||
;; FIXME: The values should be double checked here.
|
||||
; CHECK-DAG: %[[#IMG_1D:]] = OpTypeImage %[[#VOID:]] 1D 0 0 0 0 Unknown ReadOnly
|
||||
; CHECK-DAG: %[[#IMG_2D:]] = OpTypeImage %[[#VOID]] 2D 0 0 0 0 Unknown WriteOnly
|
||||
; CHECK-DAG: %[[#IMG_3D:]] = OpTypeImage %[[#VOID]] 3D 0 0 0 0 Unknown ReadWrite
|
||||
; CHECK-DAG: %[[#PTR:]] = OpTypePointer CrossWorkgroup %[[#I32:]]
|
||||
; CHECK-DAG: %[[#FN:]] = OpTypeFunction %[[#VOID]] %[[#IMG_1D]] %[[#IMG_2D]] %[[#IMG_3D]] %[[#PTR]]
|
||||
|
||||
;; Functions:
|
||||
; CHECK: OpFunction %[[#VOID]] None %[[#FN]]
|
||||
; CHECK: %[[#A:]] = OpFunctionParameter %[[#IMG_1D]]
|
||||
; CHECK: %[[#B:]] = OpFunctionParameter %[[#IMG_2D]]
|
||||
; CHECK: %[[#C:]] = OpFunctionParameter %[[#IMG_3D]]
|
||||
; CHECK: %[[#D:]] = OpFunctionParameter %[[#PTR]]
|
||||
; CHECK: %[[#PIXEL:]] = OpImageRead %[[#VEC:]] %[[#A]] %[[#]]
|
||||
; CHECK: OpImageWrite %[[#B]] %[[#]] %[[#PIXEL]]
|
||||
;; FIXME: It is unclear which of OpImageQuerySize and OpImageQuerySizeLod should be used.
|
||||
; CHECK: %[[#SIZE:]] = OpImageQuerySize{{(Lod)?}} %[[#]] %[[#C]]
|
Loading…
Reference in New Issue