[llvm] 698c800 - [SPIRV] support builtin types and ExtInsts selection
Ilia Diachkov via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 1 06:01:44 PDT 2022
Author: Ilia Diachkov
Date: 2022-09-01T16:44:54+03:00
New Revision: 698c800142d84d32471f36c7e0d33e68db07e06f
URL: https://github.com/llvm/llvm-project/commit/698c800142d84d32471f36c7e0d33e68db07e06f
DIFF: https://github.com/llvm/llvm-project/commit/698c800142d84d32471f36c7e0d33e68db07e06f.diff
LOG: [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 at gmail.com>
Co-authored-by: Michal Paszkowski <michal.paszkowski at outlook.com>
Co-authored-by: Andrey Tretyakov <andrey1.tretyakov at intel.com>
Co-authored-by: Konrad Trifunovic <konrad.trifunovic at intel.com>
Added:
llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll
llvm/test/CodeGen/SPIRV/instructions/unreachable.ll
llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll
llvm/test/CodeGen/SPIRV/opencl/image.ll
Modified:
llvm/include/llvm/IR/IntrinsicsSPIRV.td
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
llvm/lib/Target/SPIRV/SPIRVBuiltins.h
llvm/lib/Target/SPIRV/SPIRVBuiltins.td
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
Removed:
################################################################################
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 2cdd75f82962c..bf90aa0c04022 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -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], []>;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index 892b3a015f534..5655662bd766d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -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
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
index 8b105f7a81cfd..2f622e7443307 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.h
@@ -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
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
index 763ae7d361503..da63ef53a53be 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.td
@@ -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.
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index 0075f547b6d67..7b7455a53325d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -88,6 +88,7 @@ class SPIRVEmitIntrinsics
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()));
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 03f8fa6f61b48..3131794eed2d0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -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) {
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index 5c19b2735d52c..667802a84ee47 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -208,6 +208,11 @@ class SPIRVGlobalRegistry {
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 @@ class SPIRVGlobalRegistry {
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 @@ class SPIRVGlobalRegistry {
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
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 07e1158677862..5ebec6b8fa137 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -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 @@ class SPIRVInstructionSelector : public InstructionSelector {
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 @@ class SPIRVInstructionSelector : public InstructionSelector {
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");
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 87f9e9545dd39..3d12077cd2c03 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -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());
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index 347193505a739..1b36c10df15b1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -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>;
}
diff --git a/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll b/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll
new file mode 100644
index 0000000000000..04dadc58599c0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll
@@ -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
+}
diff --git a/llvm/test/CodeGen/SPIRV/instructions/unreachable.ll b/llvm/test/CodeGen/SPIRV/instructions/unreachable.ll
new file mode 100644
index 0000000000000..0a4538c6de280
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/instructions/unreachable.ll
@@ -0,0 +1,6 @@
+; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
+
+; CHECK: OpUnreachable
+define void @test_unreachable() {
+ unreachable
+}
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
new file mode 100644
index 0000000000000..8f14e8cf272ba
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
@@ -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)
diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll
new file mode 100644
index 0000000000000..7b8c05d9c98ed
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/maxnum.ll
@@ -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)
diff --git a/llvm/test/CodeGen/SPIRV/opencl/image.ll b/llvm/test/CodeGen/SPIRV/opencl/image.ll
new file mode 100644
index 0000000000000..43fcda71e8300
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/opencl/image.ll
@@ -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]]
More information about the llvm-commits
mailing list