| //===-- 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 "SPIRVSubtarget.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" | |
| #include "llvm/CodeGen/MachineBasicBlock.h" | |
| #include "llvm/IR/DebugLoc.h" | |
| #define GET_INSTRINFO_CTOR_DTOR | |
| #include "SPIRVGenInstrInfo.inc" | |
| using namespace llvm; | |
| SPIRVInstrInfo::SPIRVInstrInfo(const SPIRVSubtarget &STI) | |
| : SPIRVGenInstrInfo(STI, RI) {} | |
| 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::OpConstantCompositeContinuedINTEL: | |
| case SPIRV::OpConstantSampler: | |
| case SPIRV::OpConstantNull: | |
| case SPIRV::OpSpecConstantTrue: | |
| case SPIRV::OpSpecConstantFalse: | |
| case SPIRV::OpSpecConstant: | |
| case SPIRV::OpSpecConstantComposite: | |
| case SPIRV::OpSpecConstantCompositeContinuedINTEL: | |
| case SPIRV::OpSpecConstantOp: | |
| case SPIRV::OpUndef: | |
| case SPIRV::OpPoisonKHR: | |
| case SPIRV::OpConstantFunctionPointerINTEL: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool SPIRVInstrInfo::isSpecConstantInstr(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpSpecConstantTrue: | |
| case SPIRV::OpSpecConstantFalse: | |
| case SPIRV::OpSpecConstant: | |
| case SPIRV::OpSpecConstantComposite: | |
| case SPIRV::OpSpecConstantCompositeContinuedINTEL: | |
| case SPIRV::OpSpecConstantOp: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool SPIRVInstrInfo::isInlineAsmDefInstr(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpAsmTargetINTEL: | |
| case SPIRV::OpAsmINTEL: | |
| 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 || | |
| MI.getOpcode() == SPIRV::OpTypeStructContinuedINTEL; | |
| } | |
| } | |
| 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::isAliasingInstr(const MachineInstr &MI) const { | |
| switch (MI.getOpcode()) { | |
| case SPIRV::OpAliasDomainDeclINTEL: | |
| case SPIRV::OpAliasScopeDeclINTEL: | |
| case SPIRV::OpAliasScopeListDeclINTEL: | |
| 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) || isAliasingInstr(MI); | |
| } | |
| } | |
| bool SPIRVInstrInfo::canUseFastMathFlags(const MachineInstr &MI, | |
| bool KHRFloatControls2) 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: | |
| case SPIRV::OpFmaKHR: | |
| return true; | |
| case SPIRV::OpFNegateV: | |
| case SPIRV::OpFNegate: | |
| case SPIRV::OpOrdered: | |
| case SPIRV::OpUnordered: | |
| case SPIRV::OpFOrdEqual: | |
| case SPIRV::OpFOrdNotEqual: | |
| case SPIRV::OpFOrdLessThan: | |
| case SPIRV::OpFOrdLessThanEqual: | |
| case SPIRV::OpFOrdGreaterThan: | |
| case SPIRV::OpFOrdGreaterThanEqual: | |
| case SPIRV::OpFUnordEqual: | |
| case SPIRV::OpFUnordNotEqual: | |
| case SPIRV::OpFUnordLessThan: | |
| case SPIRV::OpFUnordLessThanEqual: | |
| case SPIRV::OpFUnordGreaterThan: | |
| case SPIRV::OpFUnordGreaterThanEqual: | |
| case SPIRV::OpExtInst: | |
| case SPIRV::OpFunctionCall: | |
| return KHRFloatControls2 ? true : false; | |
| default: | |
| return false; | |
| } | |
| } | |
| bool SPIRVInstrInfo::canUseIntegerWrapDecoration(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; | |
| } | |
| } | |
| // 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 { | |
| // We do not allow to restructure blocks by results of analyzeBranch(), | |
| // because it may potentially break structured control flow and anyway | |
| // doubtedly may be useful in SPIRV, including such reasons as, e.g.: | |
| // 1) there is no way to encode `if (Cond) then Stmt` logic, only full | |
| // if-then-else is supported by OpBranchConditional, so if we supported | |
| // splitting of blocks ending with OpBranchConditional MachineBasicBlock.cpp | |
| // would expect successfull implementation of calls to insertBranch() setting | |
| // FBB to null that is not feasible; 2) it's not possible to delete | |
| // instructions after the unconditional branch, because this instruction must | |
| // be the last instruction in a block. | |
| 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 { | |
| MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr(); | |
| if (I == MBB.end()) | |
| return 0; | |
| if (I->getOpcode() == SPIRV::OpBranch) { | |
| I->eraseFromParent(); | |
| return 1; | |
| } | |
| return 0; | |
| } | |
| // 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 { | |
| if (!TBB) | |
| return 0; | |
| BuildMI(&MBB, DL, get(SPIRV::OpBranch)).addMBB(TBB); | |
| return 1; | |
| } | |
| void SPIRVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, | |
| MachineBasicBlock::iterator I, | |
| const DebugLoc &DL, Register DestReg, | |
| Register SrcReg, bool KillSrc, | |
| bool RenamableDest, bool RenamableSrc) 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()); | |
| } |