diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 2cdd75f82962c3..bf90aa0c04022c 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 892b3a015f5346..5655662bd766dc 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp @@ -1611,5 +1611,236 @@ Optional 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 +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(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 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(new SPIRV::ImageType{ + Name, SampledType, SPIRV::AccessQualifier::AccessQualifier(AccessQual), + SPIRV::Dim::Dim(Dim), static_cast(Arrayed), + static_cast(Depth), static_cast(Multisampled), + static_cast(Sampled), SPIRV::ImageFormat::ImageFormat(Format)}); + } + llvm_unreachable("Unknown builtin image type name/literal"); +} + +static std::unique_ptr +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(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( + new SPIRV::PipeType{Name, SPIRV::AccessQualifier::ReadOnly}); + if (Name.endswith("_1")) + return std::unique_ptr( + new SPIRV::PipeType{Name, SPIRV::AccessQualifier::WriteOnly}); + if (Name.endswith("_2")) + return std::unique_ptr( + 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 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 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 8b105f7a81cfd9..2f622e74433078 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 lowerBuiltin(const StringRef DemangledCall, InstructionSet::InstructionSet Set, MachineIRBuilder &MIRBuilder, const Register OrigRet, const Type *OrigRetTy, const SmallVectorImpl &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 763ae7d3615034..da63ef53a53be3 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 = 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 { !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 0075f547b6d67e..7b7455a53325d8 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(Op) || isa(Op) || // Check GetElementPtrConstantExpr case. (isa(Op) && isa(Op))) { - IRB->SetInsertPoint(I); + setInsertPointSkippingPhis(*IRB, I); if (isa(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 03f8fa6f61b48d..3131794eed2d0b 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(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(Ty)) { + assert(!PType->isOpaque()); + Ty = PType->getNonOpaquePointerElementType(); + } + auto SType = cast(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(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 5c19b2735d52c8..667802a84ee475 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 &ArgTypes, MachineIRBuilder &MIRBuilder); + + SPIRVType * + getOrCreateSpecialType(const Type *Ty, MachineIRBuilder &MIRBuilder, + SPIRV::AccessQualifier::AccessQualifier AccQual); + std::tuple 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 &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 07e1158677862a..5ebec6b8fa137e 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>; 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(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 87f9e9545dd399..3d12077cd2c039 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 347193505a739e..1b36c10df15b16 100644 --- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td +++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td @@ -734,7 +734,7 @@ class ImageFormat value> { } multiclass ImageFormatOperand value, list reqCapabilities> { - def : ImageFormat; + def NAME : ImageFormat; defm : SymbolicOperandWithRequirements; } diff --git a/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll b/llvm/test/CodeGen/SPIRV/instructions/intrinsics.ll new file mode 100644 index 00000000000000..04dadc58599c01 --- /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 00000000000000..0a4538c6de2804 --- /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 00000000000000..8f14e8cf272ba7 --- /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 00000000000000..7b8c05d9c98eda --- /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 00000000000000..43fcda71e83006 --- /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]]