| //===-- SPIRVInstrInfo.cpp - SPIR-V Instruction Information ------*- 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains the SPIR-V implementation of the TargetInstrInfo class. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "SPIRVInstrInfo.h" | |
| #include "SPIRV.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" | |
| #include "llvm/CodeGen/MachineBasicBlock.h" | |
| #include "llvm/IR/DebugLoc.h" | |
| #include "llvm/Support/ErrorHandling.h" | |
| #define GET_INSTRINFO_CTOR_DTOR | |
| #include "SPIRVGenInstrInfo.inc" | |
| using namespace llvm; | |
| SPIRVInstrInfo::SPIRVInstrInfo() : SPIRVGenInstrInfo() {} | |
| bool SPIRVInstrInfo::isConstantInstr(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpConstantTrue: | |
| case SPIRV::OpConstantFalse: | |
| case SPIRV::OpConstantI: | |
| case SPIRV::OpConstantF: | |
| case SPIRV::OpConstantComposite: | |
| case SPIRV::OpConstantSampler: | |
| case SPIRV::OpConstantNull: | |
| case SPIRV::OpSpecConstantTrue: | |
| case SPIRV::OpSpecConstantFalse: | |
| case SPIRV::OpSpecConstant: | |
| case SPIRV::OpSpecConstantComposite: | |
| case SPIRV::OpSpecConstantOp: | |
| case SPIRV::OpUndef: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool SPIRVInstrInfo::isTypeDeclInstr(const MachineInstr &MI) const { | |
| auto &MRI = MI.getMF()->getRegInfo(); | |
| if (MI.getNumDefs() >= 1 && MI.getOperand(0).isReg()) { | |
| auto DefRegClass = MRI.getRegClassOrNull(MI.getOperand(0).getReg()); | |
| return DefRegClass && DefRegClass->getID() == SPIRV::TYPERegClass.getID(); | |
| } else { | |
| return MI.getOpcode() == SPIRV::OpTypeForwardPointer; | |
| } | |
| } | |
| bool SPIRVInstrInfo::isDecorationInstr(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpDecorate: | |
| case SPIRV::OpDecorateId: | |
| case SPIRV::OpDecorateString: | |
| case SPIRV::OpMemberDecorate: | |
| case SPIRV::OpMemberDecorateString: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool SPIRVInstrInfo::isHeaderInstr(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpCapability: | |
| case SPIRV::OpExtension: | |
| case SPIRV::OpExtInstImport: | |
| case SPIRV::OpMemoryModel: | |
| case SPIRV::OpEntryPoint: | |
| case SPIRV::OpExecutionMode: | |
| case SPIRV::OpExecutionModeId: | |
| case SPIRV::OpString: | |
| case SPIRV::OpSourceExtension: | |
| case SPIRV::OpSource: | |
| case SPIRV::OpSourceContinued: | |
| case SPIRV::OpName: | |
| case SPIRV::OpMemberName: | |
| case SPIRV::OpModuleProcessed: | |
| return true; | |
| default: | |
| return isTypeDeclInstr(MI) || isConstantInstr(MI) || isDecorationInstr(MI); | |
| } | |
| } | |
| bool SPIRVInstrInfo::canUseFastMathFlags(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpFAddS: | |
| case SPIRV::OpFSubS: | |
| case SPIRV::OpFMulS: | |
| case SPIRV::OpFDivS: | |
| case SPIRV::OpFRemS: | |
| case SPIRV::OpFAddV: | |
| case SPIRV::OpFSubV: | |
| case SPIRV::OpFMulV: | |
| case SPIRV::OpFDivV: | |
| case SPIRV::OpFRemV: | |
| case SPIRV::OpFMod: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool SPIRVInstrInfo::canUseNSW(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpIAddS: | |
| case SPIRV::OpIAddV: | |
| case SPIRV::OpISubS: | |
| case SPIRV::OpISubV: | |
| case SPIRV::OpIMulS: | |
| case SPIRV::OpIMulV: | |
| case SPIRV::OpShiftLeftLogicalS: | |
| case SPIRV::OpShiftLeftLogicalV: | |
| case SPIRV::OpSNegate: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool SPIRVInstrInfo::canUseNUW(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpIAddS: | |
| case SPIRV::OpIAddV: | |
| case SPIRV::OpISubS: | |
| case SPIRV::OpISubV: | |
| case SPIRV::OpIMulS: | |
| case SPIRV::OpIMulV: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| // Analyze the branching code at the end of MBB, returning | |
| // true if it cannot be understood (e.g. it's a switch dispatch or isn't | |
| // implemented for a target). Upon success, this returns false and returns | |
| // with the following information in various cases: | |
| // | |
| // 1. If this block ends with no branches (it just falls through to its succ) | |
| // just return false, leaving TBB/FBB null. | |
| // 2. If this block ends with only an unconditional branch, it sets TBB to be | |
| // the destination block. | |
| // 3. If this block ends with a conditional branch and it falls through to a | |
| // successor block, it sets TBB to be the branch destination block and a | |
| // list of operands that evaluate the condition. These operands can be | |
| // passed to other TargetInstrInfo methods to create new branches. | |
| // 4. If this block ends with a conditional branch followed by an | |
| // unconditional branch, it returns the 'true' destination in TBB, the | |
| // 'false' destination in FBB, and a list of operands that evaluate the | |
| // condition. These operands can be passed to other TargetInstrInfo | |
| // methods to create new branches. | |
| // | |
| // Note that removeBranch and insertBranch must be implemented to support | |
| // cases where this method returns success. | |
| // | |
| // If AllowModify is true, then this routine is allowed to modify the basic | |
| // block (e.g. delete instructions after the unconditional branch). | |
| // | |
| // The CFG information in MBB.Predecessors and MBB.Successors must be valid | |
| // before calling this function. | |
| bool SPIRVInstrInfo::analyzeBranch(MachineBasicBlock &MBB, | |
| MachineBasicBlock *&TBB, | |
| MachineBasicBlock *&FBB, | |
| SmallVectorImpl<MachineOperand> &Cond, | |
| bool AllowModify) const { | |
| TBB = nullptr; | |
| FBB = nullptr; | |
| if (MBB.empty()) | |
| return false; | |
| auto MI = MBB.getLastNonDebugInstr(); | |
| if (!MI.isValid()) | |
| return false; | |
| if (MI->getOpcode() == SPIRV::OpBranch) { | |
| TBB = MI->getOperand(0).getMBB(); | |
| return false; | |
| } else if (MI->getOpcode() == SPIRV::OpBranchConditional) { | |
| Cond.push_back(MI->getOperand(0)); | |
| TBB = MI->getOperand(1).getMBB(); | |
| if (MI->getNumOperands() == 3) { | |
| FBB = MI->getOperand(2).getMBB(); | |
| } | |
| return false; | |
| } else { | |
| return true; | |
| } | |
| } | |
| // Remove the branching code at the end of the specific MBB. | |
| // This is only invoked in cases where analyzeBranch returns success. It | |
| // returns the number of instructions that were removed. | |
| // If \p BytesRemoved is non-null, report the change in code size from the | |
| // removed instructions. | |
| unsigned SPIRVInstrInfo::removeBranch(MachineBasicBlock &MBB, | |
| int *BytesRemoved) const { | |
| report_fatal_error("Branch removal not supported, as MBB info not propagated" | |
| " to OpPhi instructions. Try using -O0 instead."); | |
| } | |
| // Insert branch code into the end of the specified MachineBasicBlock. The | |
| // operands to this method are the same as those returned by analyzeBranch. | |
| // This is only invoked in cases where analyzeBranch returns success. It | |
| // returns the number of instructions inserted. If \p BytesAdded is non-null, | |
| // report the change in code size from the added instructions. | |
| // | |
| // It is also invoked by tail merging to add unconditional branches in | |
| // cases where analyzeBranch doesn't apply because there was no original | |
| // branch to analyze. At least this much must be implemented, else tail | |
| // merging needs to be disabled. | |
| // | |
| // The CFG information in MBB.Predecessors and MBB.Successors must be valid | |
| // before calling this function. | |
| unsigned SPIRVInstrInfo::insertBranch( | |
| MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, | |
| ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const { | |
| report_fatal_error("Branch insertion not supported, as MBB info not " | |
| "propagated to OpPhi instructions. Try using " | |
| "-O0 instead."); | |
| } | |
| void SPIRVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator I, | |
| const DebugLoc &DL, MCRegister DestReg, | |
| MCRegister SrcReg, bool KillSrc) const { | |
| // Actually we don't need this COPY instruction. However if we do nothing with | |
| // it, post RA pseudo instrs expansion just removes it and we get the code | |
| // with undef registers. Therefore, we need to replace all uses of dst with | |
| // the src register. COPY instr itself will be safely removed later. | |
| assert(I->isCopy() && "Copy instruction is expected"); | |
| auto DstOp = I->getOperand(0); | |
| auto SrcOp = I->getOperand(1); | |
| assert(DstOp.isReg() && SrcOp.isReg() && | |
| "Register operands are expected in COPY"); | |
| auto &MRI = I->getMF()->getRegInfo(); | |
| MRI.replaceRegWith(DstOp.getReg(), SrcOp.getReg()); | |
| } | |
| bool SPIRVInstrInfo::expandPostRAPseudo(MachineInstr &MI) const { | |
| if (MI.getOpcode() == SPIRV::GET_ID || MI.getOpcode() == SPIRV::GET_fID || | |
| MI.getOpcode() == SPIRV::GET_pID || MI.getOpcode() == SPIRV::GET_vfID || | |
| MI.getOpcode() == SPIRV::GET_vID) { | |
| auto &MRI = MI.getMF()->getRegInfo(); | |
| MRI.replaceRegWith(MI.getOperand(0).getReg(), MI.getOperand(1).getReg()); | |
| MI.eraseFromParent(); | |
| return true; | |
| } | |
| return false; | |
| } |