| //===-- 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; |
| } |