[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:
Ilia Diachkov 2022-08-31 14:53:02 +03:00
parent c3d1504d63
commit 698c800142
15 changed files with 1412 additions and 27 deletions

View File

@ -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], []>;
}

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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()));

View File

@ -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) {

View File

@ -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

View File

@ -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");
}

View File

@ -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());
}

View File

@ -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>;
}

View File

@ -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
}

View File

@ -0,0 +1,6 @@
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
; CHECK: OpUnreachable
define void @test_unreachable() {
unreachable
}

View File

@ -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)

View File

@ -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)

View File

@ -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]]