diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt --- a/llvm/lib/Target/SPIRV/CMakeLists.txt +++ b/llvm/lib/Target/SPIRV/CMakeLists.txt @@ -15,6 +15,7 @@ add_llvm_target(SPIRVCodeGen SPIRVAsmPrinter.cpp SPIRVCallLowering.cpp + SPIRVDuplicatesTracker.cpp SPIRVEmitIntrinsics.cpp SPIRVGlobalRegistry.cpp SPIRVInstrInfo.cpp diff --git a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp --- a/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVCallLowering.cpp @@ -68,6 +68,7 @@ ArrayRef> VRegs, FunctionLoweringInfo &FLI) const { assert(GR && "Must initialize the SPIRV type registry before lowering args."); + GR->setCurrentFunc(MIRBuilder.getMF()); // Assign types and names to all args, and store their types for later. SmallVector ArgTypeVRegs; @@ -114,6 +115,8 @@ auto MRI = MIRBuilder.getMRI(); Register FuncVReg = MRI->createGenericVirtualRegister(LLT::scalar(32)); MRI->setRegClass(FuncVReg, &SPIRV::IDRegClass); + if (F.isDeclaration()) + GR->add(&F, &MIRBuilder.getMF(), FuncVReg); auto *FTy = F.getFunctionType(); auto FuncTy = GR->assignTypeToVReg(FTy, FuncVReg, MIRBuilder); @@ -136,6 +139,8 @@ MIRBuilder.buildInstr(SPIRV::OpFunctionParameter) .addDef(VRegs[i][0]) .addUse(ArgTypeVRegs[i]); + if (F.isDeclaration()) + GR->add(F.getArg(i), &MIRBuilder.getMF(), VRegs[i][0]); } // Name the function. if (F.hasName()) @@ -165,6 +170,7 @@ if (Info.OrigRet.Regs.size() > 1) return false; + GR->setCurrentFunc(MIRBuilder.getMF()); Register ResVReg = Info.OrigRet.Regs.empty() ? Register(0) : Info.OrigRet.Regs[0]; // Emit a regular OpFunctionCall. If it's an externally declared function, diff --git a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h @@ -0,0 +1,174 @@ +//===-- SPIRVDuplicatesTracker.h - SPIR-V Duplicates Tracker ----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// General infrastructure for keeping track of the values that according to +// the SPIR-V binary layout should be global to the whole module. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVDUPLICATESTRACKER_H +#define LLVM_LIB_TARGET_SPIRV_SPIRVDUPLICATESTRACKER_H + +#include "MCTargetDesc/SPIRVBaseInfo.h" +#include "MCTargetDesc/SPIRVMCTargetDesc.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/MapVector.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" + +#include + +namespace llvm { +namespace SPIRV { +// NOTE: using MapVector instead of DenseMap because it helps getting +// everything ordered in a stable manner for a price of extra (NumKeys)*PtrSize +// memory and expensive removals which do not happen anyway. +class DTSortableEntry : public MapVector { + SmallVector Deps; + + struct FlagsTy { + unsigned IsFunc : 1; + unsigned IsGV : 1; + // NOTE: bit-field default init is a C++20 feature. + FlagsTy() : IsFunc(0), IsGV(0) {} + }; + FlagsTy Flags; + +public: + // Common hoisting utility doesn't support function, because their hoisting + // require hoisting of params as well. + bool getIsFunc() const { return Flags.IsFunc; } + bool getIsGV() const { return Flags.IsGV; } + void setIsFunc(bool V) { Flags.IsFunc = V; } + void setIsGV(bool V) { Flags.IsGV = V; } + + const SmallVector &getDeps() const { return Deps; } + void addDep(DTSortableEntry *E) { Deps.push_back(E); } +}; +} // namespace SPIRV + +template class SPIRVDuplicatesTrackerBase { +public: + // NOTE: using MapVector instead of DenseMap helps getting everything ordered + // in a stable manner for a price of extra (NumKeys)*PtrSize memory and + // expensive removals which don't happen anyway. + using StorageTy = MapVector; + +private: + StorageTy Storage; + +public: + void add(KeyTy V, const MachineFunction *MF, Register R) { + if (find(V, MF).isValid()) + return; + + Storage[V][MF] = R; + if (std::is_same::type>::type>() || + std::is_same::type>::type>()) + Storage[V].setIsFunc(true); + if (std::is_same::type>::type>()) + Storage[V].setIsGV(true); + } + + Register find(KeyTy V, const MachineFunction *MF) const { + auto iter = Storage.find(V); + if (iter != Storage.end()) { + auto Map = iter->second; + auto iter2 = Map.find(MF); + if (iter2 != Map.end()) + return iter2->second; + } + return Register(); + } + + const StorageTy &getAllUses() const { return Storage; } + +private: + StorageTy &getAllUses() { return Storage; } + + // The friend class needs to have access to the internal storage + // to be able to build dependency graph, can't declare only one + // function a 'friend' due to the incomplete declaration at this point + // and mutual dependency problems. + friend class SPIRVGeneralDuplicatesTracker; +}; + +template +class SPIRVDuplicatesTracker : public SPIRVDuplicatesTrackerBase {}; + +class SPIRVGeneralDuplicatesTracker { + SPIRVDuplicatesTracker TT; + SPIRVDuplicatesTracker CT; + SPIRVDuplicatesTracker GT; + SPIRVDuplicatesTracker FT; + SPIRVDuplicatesTracker AT; + + // NOTE: using MOs instead of regs to get rid of MF dependency to be able + // to use flat data structure. + // NOTE: replacing DenseMap with MapVector doesn't affect overall correctness + // but makes LITs more stable, should prefer DenseMap still due to + // significant perf difference. + using SPIRVReg2EntryTy = + MapVector; + + template + void prebuildReg2Entry(SPIRVDuplicatesTracker &DT, + SPIRVReg2EntryTy &Reg2Entry); + +public: + void buildDepsGraph(std::vector &Graph, + MachineModuleInfo *MMI); + + void add(const Type *T, const MachineFunction *MF, Register R) { + TT.add(T, MF, R); + } + + void add(const Constant *C, const MachineFunction *MF, Register R) { + CT.add(C, MF, R); + } + + void add(const GlobalVariable *GV, const MachineFunction *MF, Register R) { + GT.add(GV, MF, R); + } + + void add(const Function *F, const MachineFunction *MF, Register R) { + FT.add(F, MF, R); + } + + void add(const Argument *Arg, const MachineFunction *MF, Register R) { + AT.add(Arg, MF, R); + } + + Register find(const Type *T, const MachineFunction *MF) { + return TT.find(const_cast(T), MF); + } + + Register find(const Constant *C, const MachineFunction *MF) { + return CT.find(const_cast(C), MF); + } + + Register find(const GlobalVariable *GV, const MachineFunction *MF) { + return GT.find(const_cast(GV), MF); + } + + Register find(const Function *F, const MachineFunction *MF) { + return FT.find(const_cast(F), MF); + } + + Register find(const Argument *Arg, const MachineFunction *MF) { + return AT.find(const_cast(Arg), MF); + } +}; +} // namespace llvm +#endif \ No newline at end of file diff --git a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp @@ -0,0 +1,95 @@ +//===-- SPIRVDuplicatesTracker.cpp - SPIR-V Duplicates Tracker --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// General infrastructure for keeping track of the values that according to +// the SPIR-V binary layout should be global to the whole module. +// +//===----------------------------------------------------------------------===// + +#include "SPIRVDuplicatesTracker.h" + +using namespace llvm; + +template +void SPIRVGeneralDuplicatesTracker::prebuildReg2Entry( + SPIRVDuplicatesTracker &DT, SPIRVReg2EntryTy &Reg2Entry) { + for (auto &TPair : DT.getAllUses()) { + for (auto &RegPair : TPair.second) { + const MachineFunction *MF = RegPair.first; + Register R = RegPair.second; + MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(R); + if (!MI) + continue; + Reg2Entry[&MI->getOperand(0)] = &TPair.second; + } + } +} + +void SPIRVGeneralDuplicatesTracker::buildDepsGraph( + std::vector &Graph, + MachineModuleInfo *MMI = nullptr) { + SPIRVReg2EntryTy Reg2Entry; + prebuildReg2Entry(TT, Reg2Entry); + prebuildReg2Entry(CT, Reg2Entry); + prebuildReg2Entry(GT, Reg2Entry); + prebuildReg2Entry(FT, Reg2Entry); + prebuildReg2Entry(AT, Reg2Entry); + + for (auto &Op2E : Reg2Entry) { + SPIRV::DTSortableEntry *E = Op2E.second; + Graph.push_back(E); + for (auto &U : *E) { + const MachineRegisterInfo &MRI = U.first->getRegInfo(); + MachineInstr *MI = MRI.getUniqueVRegDef(U.second); + if (!MI) + continue; + assert(MI && MI->getParent() && "No MachineInstr created yet"); + for (auto i = MI->getNumDefs(); i < MI->getNumOperands(); i++) { + MachineOperand &Op = MI->getOperand(i); + if (!Op.isReg()) + continue; + MachineOperand *RegOp = &MRI.getVRegDef(Op.getReg())->getOperand(0); + assert((MI->getOpcode() == SPIRV::OpVariable && i == 3) || + Reg2Entry.count(RegOp)); + if (Reg2Entry.count(RegOp)) + E->addDep(Reg2Entry[RegOp]); + } + + if (E->getIsFunc()) { + MachineInstr *Next = MI->getNextNode(); + if (Next && (Next->getOpcode() == SPIRV::OpFunction || + Next->getOpcode() == SPIRV::OpFunctionParameter)) { + E->addDep(Reg2Entry[&Next->getOperand(0)]); + } + } + } + } + + if (MMI) { + const Module *M = MMI->getModule(); + for (auto F = M->begin(), E = M->end(); F != E; ++F) { + const MachineFunction *MF = MMI->getMachineFunction(*F); + if (!MF) + continue; + for (const MachineBasicBlock &MBB : *MF) { + for (const MachineInstr &CMI : MBB) { + MachineInstr &MI = const_cast(CMI); + MI.dump(); + if (MI.getNumExplicitDefs() > 0 && + Reg2Entry.count(&MI.getOperand(0))) { + dbgs() << "\t["; + for (SPIRV::DTSortableEntry *D : + Reg2Entry.lookup(&MI.getOperand(0))->getDeps()) + dbgs() << Register::virtReg2Index(D->lookup(MF)) << ", "; + dbgs() << "]\n"; + } + } + } + } + } +} \ No newline at end of file diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h @@ -17,6 +17,7 @@ #define LLVM_LIB_TARGET_SPIRV_SPIRVTYPEMANAGER_H #include "MCTargetDesc/SPIRVBaseInfo.h" +#include "SPIRVDuplicatesTracker.h" #include "SPIRVInstrInfo.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" @@ -30,7 +31,10 @@ // where Reg = OpType... // while VRegToTypeMap tracks SPIR-V type assigned to other regs (i.e. not // type-declaring ones) - DenseMap> VRegToTypeMap; + DenseMap> + VRegToTypeMap; + + SPIRVGeneralDuplicatesTracker DT; DenseMap SPIRVToLLVMType; @@ -48,6 +52,39 @@ MachineFunction *CurMF; + void add(const Constant *C, MachineFunction *MF, Register R) { + DT.add(C, MF, R); + } + + void add(const GlobalVariable *GV, MachineFunction *MF, Register R) { + DT.add(GV, MF, R); + } + + void add(const Function *F, MachineFunction *MF, Register R) { + DT.add(F, MF, R); + } + + void add(const Argument *Arg, MachineFunction *MF, Register R) { + DT.add(Arg, MF, R); + } + + Register find(const Constant *C, MachineFunction *MF) { + return DT.find(C, MF); + } + + Register find(const GlobalVariable *GV, MachineFunction *MF) { + return DT.find(GV, MF); + } + + Register find(const Function *F, MachineFunction *MF) { + return DT.find(F, MF); + } + + void buildDepsGraph(std::vector &Graph, + MachineModuleInfo *MMI = nullptr) { + DT.buildDepsGraph(Graph, MMI); + } + // Get or create a SPIR-V type corresponding the given LLVM IR type, // and map it to the given VReg by creating an ASSIGN_TYPE instruction. SPIRVType *assignTypeToVReg( @@ -136,7 +173,7 @@ SPIRVType *getOpTypeFunction(SPIRVType *RetType, const SmallVectorImpl &ArgTypes, MachineIRBuilder &MIRBuilder); - SPIRVType *restOfCreateSPIRVType(Type *LLVMTy, MachineInstrBuilder MIB); + SPIRVType *restOfCreateSPIRVType(const Type *LLVMTy, SPIRVType *SpirvType); public: Register buildConstantInt(uint64_t Val, MachineIRBuilder &MIRBuilder, diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp @@ -101,7 +101,6 @@ SPIRVType *SpvType, bool EmitIR) { auto &MF = MIRBuilder.getMF(); - Register Res; const IntegerType *LLVMIntTy; if (SpvType) LLVMIntTy = cast(getTypeForSPIRVType(SpvType)); @@ -110,15 +109,18 @@ // Find a constant in DT or build a new one. const auto ConstInt = ConstantInt::get(const_cast(LLVMIntTy), Val); - unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32; - Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth)); - assignTypeToVReg(LLVMIntTy, Res, MIRBuilder); - if (EmitIR) - MIRBuilder.buildConstant(Res, *ConstInt); - else - MIRBuilder.buildInstr(SPIRV::OpConstantI) - .addDef(Res) - .addImm(ConstInt->getSExtValue()); + Register Res = DT.find(ConstInt, &MF); + if (!Res.isValid()) { + unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32; + Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth)); + assignTypeToVReg(LLVMIntTy, Res, MIRBuilder); + if (EmitIR) + MIRBuilder.buildConstant(Res, *ConstInt); + else + MIRBuilder.buildInstr(SPIRV::OpConstantI) + .addDef(Res) + .addImm(ConstInt->getSExtValue()); + } return Res; } @@ -126,7 +128,6 @@ MachineIRBuilder &MIRBuilder, SPIRVType *SpvType) { auto &MF = MIRBuilder.getMF(); - Register Res; const Type *LLVMFPTy; if (SpvType) { LLVMFPTy = getTypeForSPIRVType(SpvType); @@ -136,10 +137,13 @@ } // Find a constant in DT or build a new one. const auto ConstFP = ConstantFP::get(LLVMFPTy->getContext(), Val); - unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32; - Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth)); - assignTypeToVReg(LLVMFPTy, Res, MIRBuilder); - MIRBuilder.buildFConstant(Res, *ConstFP); + Register Res = DT.find(ConstFP, &MF); + if (!Res.isValid()) { + unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32; + Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth)); + assignTypeToVReg(LLVMFPTy, Res, MIRBuilder); + MIRBuilder.buildFConstant(Res, *ConstFP); + } return Res; } @@ -184,6 +188,7 @@ *Subtarget.getRegBankInfo()); } Reg = MIB->getOperand(0).getReg(); + DT.add(GVar, &MIRBuilder.getMF(), Reg); // Set to Reg the same type as ResVReg has. auto MRI = MIRBuilder.getMRI(); @@ -318,10 +323,11 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVType( const Type *Type, MachineIRBuilder &MIRBuilder, SPIRV::AccessQualifier AccessQual, bool EmitIR) { + Register Reg = DT.find(Type, &MIRBuilder.getMF()); + if (Reg.isValid()) + return getSPIRVTypeForVReg(Reg); SPIRVType *SpirvType = createSPIRVType(Type, MIRBuilder, AccessQual, EmitIR); - VRegToTypeMap[&MIRBuilder.getMF()][getSPIRVTypeID(SpirvType)] = SpirvType; - SPIRVToLLVMType[SpirvType] = Type; - return SpirvType; + return restOfCreateSPIRVType(Type, SpirvType); } bool SPIRVGlobalRegistry::isScalarOfType(Register VReg, @@ -387,17 +393,21 @@ MIRBuilder); } -SPIRVType *SPIRVGlobalRegistry::restOfCreateSPIRVType(Type *LLVMTy, - MachineInstrBuilder MIB) { - SPIRVType *SpirvType = MIB; +SPIRVType *SPIRVGlobalRegistry::restOfCreateSPIRVType(const Type *LLVMTy, + SPIRVType *SpirvType) { + assert(CurMF == SpirvType->getMF()); VRegToTypeMap[CurMF][getSPIRVTypeID(SpirvType)] = SpirvType; SPIRVToLLVMType[SpirvType] = LLVMTy; + DT.add(LLVMTy, CurMF, getSPIRVTypeID(SpirvType)); return SpirvType; } SPIRVType *SPIRVGlobalRegistry::getOrCreateSPIRVIntegerType( unsigned BitWidth, MachineInstr &I, const SPIRVInstrInfo &TII) { Type *LLVMTy = IntegerType::get(CurMF->getFunction().getContext(), BitWidth); + Register Reg = DT.find(LLVMTy, CurMF); + if (Reg.isValid()) + return getSPIRVTypeForVReg(Reg); MachineBasicBlock &BB = *I.getParent(); auto MIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpTypeInt)) .addDef(createTypeVReg(CurMF->getRegInfo())) diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -807,23 +807,29 @@ Register SPIRVInstructionSelector::buildI32Constant(uint32_t Val, MachineInstr &I, const SPIRVType *ResType) const { + Type *LLVMTy = IntegerType::get(GR.CurMF->getFunction().getContext(), 32); const SPIRVType *SpvI32Ty = ResType ? ResType : GR.getOrCreateSPIRVIntegerType(32, I, TII); - Register NewReg; - NewReg = MRI->createGenericVirtualRegister(LLT::scalar(32)); - MachineInstr *MI; - MachineBasicBlock &BB = *I.getParent(); - if (Val == 0) { - MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull)) - .addDef(NewReg) - .addUse(GR.getSPIRVTypeID(SpvI32Ty)); - } else { - MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantI)) - .addDef(NewReg) - .addUse(GR.getSPIRVTypeID(SpvI32Ty)) - .addImm(APInt(32, Val).getZExtValue()); + // Find a constant in DT or build a new one. + auto ConstInt = ConstantInt::get(LLVMTy, Val); + Register NewReg = GR.find(ConstInt, GR.CurMF); + if (!NewReg.isValid()) { + NewReg = MRI->createGenericVirtualRegister(LLT::scalar(32)); + GR.add(ConstInt, GR.CurMF, NewReg); + MachineInstr *MI; + MachineBasicBlock &BB = *I.getParent(); + if (Val == 0) { + MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantNull)) + .addDef(NewReg) + .addUse(GR.getSPIRVTypeID(SpvI32Ty)); + } else { + MI = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpConstantI)) + .addDef(NewReg) + .addUse(GR.getSPIRVTypeID(SpvI32Ty)) + .addImm(APInt(32, Val).getZExtValue()); + } + constrainSelectedInstRegOperands(*MI, TII, TRI, RBI); } - constrainSelectedInstRegOperands(*MI, TII, TRI, RBI); return NewReg; } diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h --- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h @@ -15,6 +15,7 @@ #define LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H #include "MCTargetDesc/SPIRVBaseInfo.h" +#include "SPIRVDuplicatesTracker.h" #include "SPIRVSubtarget.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" @@ -123,6 +124,11 @@ private: void setBaseInfo(const Module &M); template void collectTypesConstsVars(); + void collectGlobalEntities( + const std::vector &DepsGraph, + SPIRV::ModuleSectionType MSType, + std::function Pred, + bool UsePreOrder); void processDefInstrs(const Module &M); void collectFuncNames(MachineInstr &MI, const Function &F); void processOtherInstrs(const Module &M); diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp --- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp @@ -28,6 +28,11 @@ #define DEBUG_TYPE "spirv-module-analysis" +static cl::opt + SPVDumpDeps("spv-dump-deps", + cl::desc("Dump MIR with SPIR-V dependencies info"), + cl::Optional, cl::init(false)); + char llvm::SPIRVModuleAnalysis::ID = 0; namespace llvm { @@ -113,6 +118,83 @@ return false; } +// Collect MI which defines the register in the given machine function. +static void collectDefInstr(Register Reg, const MachineFunction *MF, + SPIRV::ModuleAnalysisInfo *MAI, + SPIRV::ModuleSectionType MSType, + bool DoInsert = true) { + assert(MAI->hasRegisterAlias(MF, Reg) && "Cannot find register alias"); + MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(Reg); + assert(MI && "There should be an instruction that defines the register"); + MAI->setSkipEmission(MI); + if (DoInsert) + MAI->MS[MSType].push_back(MI); +} + +void SPIRVModuleAnalysis::collectGlobalEntities( + const std::vector &DepsGraph, + SPIRV::ModuleSectionType MSType, + std::function Pred, + bool UsePreOrder) { + DenseSet Visited; + for (const auto *E : DepsGraph) { + std::function RecHoistUtil; + // NOTE: here we prefer recursive approach over iterative because + // we don't expect depchains long enough to cause SO. + RecHoistUtil = [MSType, UsePreOrder, &Visited, &Pred, + &RecHoistUtil](const SPIRV::DTSortableEntry *E) { + if (Visited.count(E) || !Pred(E)) + return; + Visited.insert(E); + + // Traversing deps graph in post-order allows us to get rid of + // register aliases preprocessing. + // But pre-order is required for correct processing of function + // declaration and arguments processing. + if (!UsePreOrder) + for (auto *S : E->getDeps()) + RecHoistUtil(S); + + Register GlobalReg = Register::index2VirtReg(MAI.getNextID()); + bool IsFirst = true; + for (auto &U : *E) { + const MachineFunction *MF = U.first; + Register Reg = U.second; + MAI.setRegisterAlias(MF, Reg, GlobalReg); + if (!MF->getRegInfo().getUniqueVRegDef(Reg)) + continue; + collectDefInstr(Reg, MF, &MAI, MSType, IsFirst); + IsFirst = false; + if (E->getIsGV()) + MAI.GlobalVarList.push_back(MF->getRegInfo().getUniqueVRegDef(Reg)); + } + + if (UsePreOrder) + for (auto *S : E->getDeps()) + RecHoistUtil(S); + }; + RecHoistUtil(E); + } +} + +// The function initializes global register alias table for types, consts, +// global vars and func decls and collects these instruction for output +// at module level. Also it collects explicit OpExtension/OpCapability +// instructions. +void SPIRVModuleAnalysis::processDefInstrs(const Module &M) { + std::vector DepsGraph; + + GR->buildDepsGraph(DepsGraph, SPVDumpDeps ? MMI : nullptr); + + collectGlobalEntities( + DepsGraph, SPIRV::MB_TypeConstVars, + [](const SPIRV::DTSortableEntry *E) { return !E->getIsFunc(); }, false); + + collectGlobalEntities( + DepsGraph, SPIRV::MB_ExtFuncDecls, + [](const SPIRV::DTSortableEntry *E) { return E->getIsFunc(); }, true); +} + // Look for IDs declared with Import linkage, and map the imported name string // to the register defining that variable (which will usually be the result of // an OpFunction). This lets us call externally imported functions using @@ -146,10 +228,9 @@ // numbering has already occurred by this point. We can directly compare reg // arguments when detecting duplicates. static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI, - SPIRV::ModuleSectionType MSType, - bool IsConstOrType = false) { + SPIRV::ModuleSectionType MSType) { MAI.setSkipEmission(&MI); - if (findSameInstrInMS(MI, MSType, MAI, IsConstOrType, IsConstOrType ? 1 : 0)) + if (findSameInstrInMS(MI, MSType, MAI, false)) return; // Found a duplicate, so don't add it. // No duplicates, so add it. MAI.MS[MSType].push_back(&MI); @@ -163,18 +244,11 @@ continue; MachineFunction *MF = MMI->getMachineFunction(*F); assert(MF); - unsigned FCounter = 0; for (MachineBasicBlock &MBB : *MF) for (MachineInstr &MI : MBB) { - if (MI.getOpcode() == SPIRV::OpFunction) - FCounter++; if (MAI.getSkipEmission(&MI)) continue; const unsigned OpCode = MI.getOpcode(); - const bool IsFuncOrParm = - OpCode == SPIRV::OpFunction || OpCode == SPIRV::OpFunctionParameter; - const bool IsConstOrType = - TII->isConstantInstr(MI) || TII->isTypeDeclInstr(MI); if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) { collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames); } else if (OpCode == SPIRV::OpEntryPoint) { @@ -182,12 +256,6 @@ } else if (TII->isDecorationInstr(MI)) { collectOtherInstr(MI, MAI, SPIRV::MB_Annotations); collectFuncNames(MI, *F); - } else if (IsConstOrType || (FCounter > 1 && IsFuncOrParm)) { - // Now OpSpecConstant*s are not in DT, - // but they need to be collected anyway. - enum SPIRV::ModuleSectionType Type = - IsFuncOrParm ? SPIRV::MB_ExtFuncDecls : SPIRV::MB_TypeConstVars; - collectOtherInstr(MI, MAI, Type, IsConstOrType); } else if (OpCode == SPIRV::OpFunction) { collectFuncNames(MI, *F); } @@ -239,6 +307,7 @@ // TODO: Process type/const/global var/func decl instructions, number their // destination registers from 0 to N, collect Extensions and Capabilities. + processDefInstrs(M); // Number rest of registers from N+1 onwards. numberRegistersGlobally(M); diff --git a/llvm/test/CodeGen/SPIRV/transcoding/RelationalOperators.ll b/llvm/test/CodeGen/SPIRV/transcoding/RelationalOperators.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/transcoding/RelationalOperators.ll @@ -0,0 +1,298 @@ +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV + +; ModuleID = 'RelationalOperators.cl' +source_filename = "RelationalOperators.cl" +target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024" +target triple = "spirv32-unknown-unknown" + +; CHECK-SPIRV: %[[bool:[0-9]+]] = OpTypeBool +; CHECK-SPIRV: %[[bool2:[0-9]+]] = OpTypeVector %[[bool]] 2 + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpUGreaterThan %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testUGreaterThan(uint2 a, uint2 b, global int2 *res) { +; res[0] = a > b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testUGreaterThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp ugt <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpSGreaterThan %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testSGreaterThan(int2 a, int2 b, global int2 *res) { +; res[0] = a > b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testSGreaterThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp sgt <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpUGreaterThanEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testUGreaterThanEqual(uint2 a, uint2 b, global int2 *res) { +; res[0] = a >= b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testUGreaterThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp uge <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpSGreaterThanEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testSGreaterThanEqual(int2 a, int2 b, global int2 *res) { +; res[0] = a >= b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testSGreaterThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp sge <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpULessThan %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testULessThan(uint2 a, uint2 b, global int2 *res) { +; res[0] = a < b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testULessThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp ult <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpSLessThan %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testSLessThan(int2 a, int2 b, global int2 *res) { +; res[0] = a < b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testSLessThan(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp slt <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpULessThanEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testULessThanEqual(uint2 a, uint2 b, global int2 *res) { +; res[0] = a <= b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testULessThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !5 !kernel_arg_base_type !6 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp ule <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpSLessThanEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testSLessThanEqual(int2 a, int2 b, global int2 *res) { +; res[0] = a <= b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testSLessThanEqual(<2 x i32> noundef %a, <2 x i32> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !11 !kernel_arg_base_type !12 !kernel_arg_type_qual !7 { +entry: + %cmp = icmp sle <2 x i32> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testFOrdEqual(float2 a, float2 b, global int2 *res) { +; res[0] = a == b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testFOrdEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 { +entry: + %cmp = fcmp oeq <2 x float> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpFUnordNotEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testFUnordNotEqual(float2 a, float2 b, global int2 *res) { +; res[0] = a != b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testFUnordNotEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 { +entry: + %cmp = fcmp une <2 x float> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdGreaterThan %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testFOrdGreaterThan(float2 a, float2 b, global int2 *res) { +; res[0] = a > b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testFOrdGreaterThan(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 { +entry: + %cmp = fcmp ogt <2 x float> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdGreaterThanEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testFOrdGreaterThanEqual(float2 a, float2 b, global int2 *res) { +; res[0] = a >= b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testFOrdGreaterThanEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 { +entry: + %cmp = fcmp oge <2 x float> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdLessThan %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testFOrdLessThan(float2 a, float2 b, global int2 *res) { +; res[0] = a < b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testFOrdLessThan(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 { +entry: + %cmp = fcmp olt <2 x float> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +; CHECK-SPIRV: OpFunction +; CHECK-SPIRV-NEXT: %[[A:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV-NEXT: %[[B:[0-9]+]] = OpFunctionParameter %{{[0-9]+}} +; CHECK-SPIRV: %{{[0-9]+}} = OpFOrdLessThanEqual %[[bool2]] %[[A]] %[[B]] +; CHECK-SPIRV: OpFunctionEnd + +; kernel void testFOrdLessThanEqual(float2 a, float2 b, global int2 *res) { +; res[0] = a <= b; +; } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn writeonly +define dso_local spir_kernel void @testFOrdLessThanEqual(<2 x float> noundef %a, <2 x float> noundef %b, <2 x i32> addrspace(1)* nocapture noundef writeonly %res) local_unnamed_addr #0 !kernel_arg_addr_space !3 !kernel_arg_access_qual !4 !kernel_arg_type !13 !kernel_arg_base_type !14 !kernel_arg_type_qual !7 { +entry: + %cmp = fcmp ole <2 x float> %a, %b + %sext = sext <2 x i1> %cmp to <2 x i32> + store <2 x i32> %sext, <2 x i32> addrspace(1)* %res, align 8, !tbaa !8 + ret void +} + +attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn writeonly "frame-pointer"="none" "min-legal-vector-width"="64" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" } + +!llvm.module.flags = !{!0} +!opencl.ocl.version = !{!1} +!opencl.spir.version = !{!1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 2, i32 0} +!2 = !{!"clang version 14.0.0 (https://github.com/llvm/llvm-project.git 881b6a009fb6e2dd5fb924524cd6eacd14148a08)"} +!3 = !{i32 0, i32 0, i32 1} +!4 = !{!"none", !"none", !"none"} +!5 = !{!"uint2", !"uint2", !"int2*"} +!6 = !{!"uint __attribute__((ext_vector_type(2)))", !"uint __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))*"} +!7 = !{!"", !"", !""} +!8 = !{!9, !9, i64 0} +!9 = !{!"omnipotent char", !10, i64 0} +!10 = !{!"Simple C/C++ TBAA"} +!11 = !{!"int2", !"int2", !"int2*"} +!12 = !{!"int __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))*"} +!13 = !{!"float2", !"float2", !"int2*"} +!14 = !{!"float __attribute__((ext_vector_type(2)))", !"float __attribute__((ext_vector_type(2)))", !"int __attribute__((ext_vector_type(2)))*"} diff --git a/llvm/test/CodeGen/SPIRV/transcoding/fcmp.ll b/llvm/test/CodeGen/SPIRV/transcoding/fcmp.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/transcoding/fcmp.ll @@ -0,0 +1,296 @@ +; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV + +; CHECK-SPIRV: OpName %[[#r1:]] "r1" +; CHECK-SPIRV: OpName %[[#r2:]] "r2" +; CHECK-SPIRV: OpName %[[#r3:]] "r3" +; CHECK-SPIRV: OpName %[[#r4:]] "r4" +; CHECK-SPIRV: OpName %[[#r5:]] "r5" +; CHECK-SPIRV: OpName %[[#r6:]] "r6" +; CHECK-SPIRV: OpName %[[#r7:]] "r7" +; CHECK-SPIRV: OpName %[[#r8:]] "r8" +; CHECK-SPIRV: OpName %[[#r9:]] "r9" +; CHECK-SPIRV: OpName %[[#r10:]] "r10" +; CHECK-SPIRV: OpName %[[#r11:]] "r11" +; CHECK-SPIRV: OpName %[[#r12:]] "r12" +; CHECK-SPIRV: OpName %[[#r13:]] "r13" +; CHECK-SPIRV: OpName %[[#r14:]] "r14" +; CHECK-SPIRV: OpName %[[#r15:]] "r15" +; CHECK-SPIRV: OpName %[[#r16:]] "r16" +; CHECK-SPIRV: OpName %[[#r17:]] "r17" +; CHECK-SPIRV: OpName %[[#r18:]] "r18" +; CHECK-SPIRV: OpName %[[#r19:]] "r19" +; CHECK-SPIRV: OpName %[[#r20:]] "r20" +; CHECK-SPIRV: OpName %[[#r21:]] "r21" +; CHECK-SPIRV: OpName %[[#r22:]] "r22" +; CHECK-SPIRV: OpName %[[#r23:]] "r23" +; CHECK-SPIRV: OpName %[[#r24:]] "r24" +; CHECK-SPIRV: OpName %[[#r25:]] "r25" +; CHECK-SPIRV: OpName %[[#r26:]] "r26" +; CHECK-SPIRV: OpName %[[#r27:]] "r27" +; CHECK-SPIRV: OpName %[[#r28:]] "r28" +; CHECK-SPIRV: OpName %[[#r29:]] "r29" +; CHECK-SPIRV: OpName %[[#r30:]] "r30" +; CHECK-SPIRV: OpName %[[#r31:]] "r31" +; CHECK-SPIRV: OpName %[[#r32:]] "r32" +; CHECK-SPIRV: OpName %[[#r33:]] "r33" +; CHECK-SPIRV: OpName %[[#r34:]] "r34" +; CHECK-SPIRV: OpName %[[#r35:]] "r35" +; CHECK-SPIRV: OpName %[[#r36:]] "r36" +; CHECK-SPIRV: OpName %[[#r37:]] "r37" +; CHECK-SPIRV: OpName %[[#r38:]] "r38" +; CHECK-SPIRV: OpName %[[#r39:]] "r39" +; CHECK-SPIRV: OpName %[[#r40:]] "r40" +; CHECK-SPIRV: OpName %[[#r41:]] "r41" +; CHECK-SPIRV: OpName %[[#r42:]] "r42" +; CHECK-SPIRV: OpName %[[#r43:]] "r43" +; CHECK-SPIRV: OpName %[[#r44:]] "r44" +; CHECK-SPIRV: OpName %[[#r45:]] "r45" +; CHECK-SPIRV: OpName %[[#r46:]] "r46" +; CHECK-SPIRV: OpName %[[#r47:]] "r47" +; CHECK-SPIRV: OpName %[[#r48:]] "r48" +; CHECK-SPIRV: OpName %[[#r49:]] "r49" +; CHECK-SPIRV: OpName %[[#r50:]] "r50" +; CHECK-SPIRV: OpName %[[#r51:]] "r51" +; CHECK-SPIRV: OpName %[[#r52:]] "r52" +; CHECK-SPIRV: OpName %[[#r53:]] "r53" +; CHECK-SPIRV: OpName %[[#r54:]] "r54" +; CHECK-SPIRV: OpName %[[#r55:]] "r55" +; CHECK-SPIRV: OpName %[[#r56:]] "r56" +; CHECK-SPIRV: OpName %[[#r57:]] "r57" +; CHECK-SPIRV: OpName %[[#r58:]] "r58" +; CHECK-SPIRV: OpName %[[#r59:]] "r59" +; CHECK-SPIRV: OpName %[[#r60:]] "r60" +; CHECK-SPIRV: OpName %[[#r61:]] "r61" +; CHECK-SPIRV: OpName %[[#r62:]] "r62" +; CHECK-SPIRV: OpName %[[#r63:]] "r63" +; CHECK-SPIRV: OpName %[[#r64:]] "r64" +; CHECK-SPIRV: OpName %[[#r65:]] "r65" +; CHECK-SPIRV: OpName %[[#r66:]] "r66" +; CHECK-SPIRV: OpName %[[#r67:]] "r67" +; CHECK-SPIRV: OpName %[[#r68:]] "r68" +; CHECK-SPIRV: OpName %[[#r69:]] "r69" +; CHECK-SPIRV: OpName %[[#r70:]] "r70" +; CHECK-SPIRV: OpName %[[#r71:]] "r71" +; CHECK-SPIRV: OpName %[[#r72:]] "r72" +; CHECK-SPIRV: OpName %[[#r73:]] "r73" +; CHECK-SPIRV: OpName %[[#r74:]] "r74" +; CHECK-SPIRV: OpName %[[#r75:]] "r75" +; CHECK-SPIRV: OpName %[[#r76:]] "r76" +; CHECK-SPIRV: OpName %[[#r77:]] "r77" +; CHECK-SPIRV: OpName %[[#r78:]] "r78" +; CHECK-SPIRV: OpName %[[#r79:]] "r79" +; CHECK-SPIRV: OpName %[[#r80:]] "r80" +; CHECK-SPIRV: OpName %[[#r81:]] "r81" +; CHECK-SPIRV: OpName %[[#r82:]] "r82" +; CHECK-SPIRV: OpName %[[#r83:]] "r83" +; CHECK-SPIRV: OpName %[[#r84:]] "r84" +; CHECK-SPIRV: OpName %[[#r85:]] "r85" +; CHECK-SPIRV: OpName %[[#r86:]] "r86" +; CHECK-SPIRV: OpName %[[#r87:]] "r87" +; CHECK-SPIRV: OpName %[[#r88:]] "r88" +; CHECK-SPIRV: OpName %[[#r89:]] "r89" +; CHECK-SPIRV: OpName %[[#r90:]] "r90" +; CHECK-SPIRV-NOT: OpDecorate %{{.*}} FPFastMathMode +; CHECK-SPIRV: %[[#bool:]] = OpTypeBool +; CHECK-SPIRV: %[[#r1]] = OpFOrdEqual %[[#bool]] +; CHECK-SPIRV: %[[#r2]] = OpFOrdEqual %[[#bool]] +; CHECK-SPIRV: %[[#r3]] = OpFOrdEqual %[[#bool]] +; CHECK-SPIRV: %[[#r4]] = OpFOrdEqual %[[#bool]] +; CHECK-SPIRV: %[[#r5]] = OpFOrdEqual %[[#bool]] +; CHECK-SPIRV: %[[#r6]] = OpFOrdEqual %[[#bool]] +; CHECK-SPIRV: %[[#r7]] = OpFOrdEqual %[[#bool]] +; CHECK-SPIRV: %[[#r8]] = OpFOrdNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r9]] = OpFOrdNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r10]] = OpFOrdNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r11]] = OpFOrdNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r12]] = OpFOrdNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r13]] = OpFOrdNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r14]] = OpFOrdNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r15]] = OpFOrdLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r16]] = OpFOrdLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r17]] = OpFOrdLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r18]] = OpFOrdLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r19]] = OpFOrdLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r20]] = OpFOrdLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r21]] = OpFOrdLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r22]] = OpFOrdGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r23]] = OpFOrdGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r24]] = OpFOrdGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r25]] = OpFOrdGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r26]] = OpFOrdGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r27]] = OpFOrdGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r28]] = OpFOrdGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r29]] = OpFOrdLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r30]] = OpFOrdLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r31]] = OpFOrdLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r32]] = OpFOrdLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r33]] = OpFOrdLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r34]] = OpFOrdLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r35]] = OpFOrdLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r36]] = OpFOrdGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r37]] = OpFOrdGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r38]] = OpFOrdGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r39]] = OpFOrdGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r40]] = OpFOrdGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r41]] = OpFOrdGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r42]] = OpFOrdGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r43]] = OpOrdered %[[#bool]] +; CHECK-SPIRV: %[[#r44]] = OpOrdered %[[#bool]] +; CHECK-SPIRV: %[[#r45]] = OpOrdered %[[#bool]] +; CHECK-SPIRV: %[[#r46]] = OpFUnordEqual %[[#bool]] +; CHECK-SPIRV: %[[#r47]] = OpFUnordEqual %[[#bool]] +; CHECK-SPIRV: %[[#r48]] = OpFUnordEqual %[[#bool]] +; CHECK-SPIRV: %[[#r49]] = OpFUnordEqual %[[#bool]] +; CHECK-SPIRV: %[[#r50]] = OpFUnordEqual %[[#bool]] +; CHECK-SPIRV: %[[#r51]] = OpFUnordEqual %[[#bool]] +; CHECK-SPIRV: %[[#r52]] = OpFUnordEqual %[[#bool]] +; CHECK-SPIRV: %[[#r53]] = OpFUnordNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r54]] = OpFUnordNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r55]] = OpFUnordNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r56]] = OpFUnordNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r57]] = OpFUnordNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r58]] = OpFUnordNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r59]] = OpFUnordNotEqual %[[#bool]] +; CHECK-SPIRV: %[[#r60]] = OpFUnordLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r61]] = OpFUnordLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r62]] = OpFUnordLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r63]] = OpFUnordLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r64]] = OpFUnordLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r65]] = OpFUnordLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r66]] = OpFUnordLessThan %[[#bool]] +; CHECK-SPIRV: %[[#r67]] = OpFUnordGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r68]] = OpFUnordGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r69]] = OpFUnordGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r70]] = OpFUnordGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r71]] = OpFUnordGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r72]] = OpFUnordGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r73]] = OpFUnordGreaterThan %[[#bool]] +; CHECK-SPIRV: %[[#r74]] = OpFUnordLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r75]] = OpFUnordLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r76]] = OpFUnordLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r77]] = OpFUnordLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r78]] = OpFUnordLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r79]] = OpFUnordLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r80]] = OpFUnordLessThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r81]] = OpFUnordGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r82]] = OpFUnordGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r83]] = OpFUnordGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r84]] = OpFUnordGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r85]] = OpFUnordGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r86]] = OpFUnordGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r87]] = OpFUnordGreaterThanEqual %[[#bool]] +; CHECK-SPIRV: %[[#r88]] = OpUnordered %[[#bool]] +; CHECK-SPIRV: %[[#r89]] = OpUnordered %[[#bool]] +; CHECK-SPIRV: %[[#r90]] = OpUnordered %[[#bool]] + +target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024" +target triple = "spirv32-unknown-unknown" + +; Function Attrs: nounwind +define spir_kernel void @testFCmp(float %a, float %b) local_unnamed_addr #0 !kernel_arg_addr_space !2 !kernel_arg_access_qual !3 !kernel_arg_type !4 !kernel_arg_base_type !4 !kernel_arg_type_qual !5 { +entry: + %r1 = fcmp oeq float %a, %b + %r2 = fcmp nnan oeq float %a, %b + %r3 = fcmp ninf oeq float %a, %b + %r4 = fcmp nsz oeq float %a, %b + %r5 = fcmp arcp oeq float %a, %b + %r6 = fcmp fast oeq float %a, %b + %r7 = fcmp nnan ninf oeq float %a, %b + %r8 = fcmp one float %a, %b + %r9 = fcmp nnan one float %a, %b + %r10 = fcmp ninf one float %a, %b + %r11 = fcmp nsz one float %a, %b + %r12 = fcmp arcp one float %a, %b + %r13 = fcmp fast one float %a, %b + %r14 = fcmp nnan ninf one float %a, %b + %r15 = fcmp olt float %a, %b + %r16 = fcmp nnan olt float %a, %b + %r17 = fcmp ninf olt float %a, %b + %r18 = fcmp nsz olt float %a, %b + %r19 = fcmp arcp olt float %a, %b + %r20 = fcmp fast olt float %a, %b + %r21 = fcmp nnan ninf olt float %a, %b + %r22 = fcmp ogt float %a, %b + %r23 = fcmp nnan ogt float %a, %b + %r24 = fcmp ninf ogt float %a, %b + %r25 = fcmp nsz ogt float %a, %b + %r26 = fcmp arcp ogt float %a, %b + %r27 = fcmp fast ogt float %a, %b + %r28 = fcmp nnan ninf ogt float %a, %b + %r29 = fcmp ole float %a, %b + %r30 = fcmp nnan ole float %a, %b + %r31 = fcmp ninf ole float %a, %b + %r32 = fcmp nsz ole float %a, %b + %r33 = fcmp arcp ole float %a, %b + %r34 = fcmp fast ole float %a, %b + %r35 = fcmp nnan ninf ole float %a, %b + %r36 = fcmp oge float %a, %b + %r37 = fcmp nnan oge float %a, %b + %r38 = fcmp ninf oge float %a, %b + %r39 = fcmp nsz oge float %a, %b + %r40 = fcmp arcp oge float %a, %b + %r41 = fcmp fast oge float %a, %b + %r42 = fcmp nnan ninf oge float %a, %b + %r43 = fcmp ord float %a, %b + %r44 = fcmp ninf ord float %a, %b + %r45 = fcmp nsz ord float %a, %b + %r46 = fcmp ueq float %a, %b + %r47 = fcmp nnan ueq float %a, %b + %r48 = fcmp ninf ueq float %a, %b + %r49 = fcmp nsz ueq float %a, %b + %r50 = fcmp arcp ueq float %a, %b + %r51 = fcmp fast ueq float %a, %b + %r52 = fcmp nnan ninf ueq float %a, %b + %r53 = fcmp une float %a, %b + %r54 = fcmp nnan une float %a, %b + %r55 = fcmp ninf une float %a, %b + %r56 = fcmp nsz une float %a, %b + %r57 = fcmp arcp une float %a, %b + %r58 = fcmp fast une float %a, %b + %r59 = fcmp nnan ninf une float %a, %b + %r60 = fcmp ult float %a, %b + %r61 = fcmp nnan ult float %a, %b + %r62 = fcmp ninf ult float %a, %b + %r63 = fcmp nsz ult float %a, %b + %r64 = fcmp arcp ult float %a, %b + %r65 = fcmp fast ult float %a, %b + %r66 = fcmp nnan ninf ult float %a, %b + %r67 = fcmp ugt float %a, %b + %r68 = fcmp nnan ugt float %a, %b + %r69 = fcmp ninf ugt float %a, %b + %r70 = fcmp nsz ugt float %a, %b + %r71 = fcmp arcp ugt float %a, %b + %r72 = fcmp fast ugt float %a, %b + %r73 = fcmp nnan ninf ugt float %a, %b + %r74 = fcmp ule float %a, %b + %r75 = fcmp nnan ule float %a, %b + %r76 = fcmp ninf ule float %a, %b + %r77 = fcmp nsz ule float %a, %b + %r78 = fcmp arcp ule float %a, %b + %r79 = fcmp fast ule float %a, %b + %r80 = fcmp nnan ninf ule float %a, %b + %r81 = fcmp uge float %a, %b + %r82 = fcmp nnan uge float %a, %b + %r83 = fcmp ninf uge float %a, %b + %r84 = fcmp nsz uge float %a, %b + %r85 = fcmp arcp uge float %a, %b + %r86 = fcmp fast uge float %a, %b + %r87 = fcmp nnan ninf uge float %a, %b + %r88 = fcmp uno float %a, %b + %r89 = fcmp ninf uno float %a, %b + %r90 = fcmp nsz uno float %a, %b + ret void +} + +attributes #0 = { convergent nounwind writeonly "correctly-rounded-divide-sqrt-fp-math"="false" "denorms-are-zero"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="false" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.module.flags = !{!0} +!opencl.ocl.version = !{!1} +!opencl.spir.version = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 2, i32 0} +!2 = !{i32 0, i32 0} +!3 = !{!"none", !"none"} +!4 = !{!"float", !"float"} +!5 = !{!"", !""}