//===- llvm/CodeGen/GlobalISel/GenericMachineInstrs.h -----------*- 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 // //===----------------------------------------------------------------------===// /// \file /// Declares convenience wrapper classes for interpreting MachineInstr instances /// as specific generic operations. /// //===----------------------------------------------------------------------===// #ifndef LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H #define LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H #include "llvm/IR/Instructions.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/CodeGen/TargetOpcodes.h" #include "llvm/Support/Casting.h" namespace llvm { /// A base class for all GenericMachineInstrs. class GenericMachineInstr : public MachineInstr { public: GenericMachineInstr() = delete; /// Access the Idx'th operand as a register and return it. /// This assumes that the Idx'th operand is a Register type. Register getReg(unsigned Idx) const { return getOperand(Idx).getReg(); } static bool classof(const MachineInstr *MI) { return isPreISelGenericOpcode(MI->getOpcode()); } }; /// Provides common memory operand functionality. class GMemOperation : public GenericMachineInstr { public: /// Get the MachineMemOperand on this instruction. MachineMemOperand &getMMO() const { return **memoperands_begin(); } /// Returns true if the attached MachineMemOperand has the atomic flag set. bool isAtomic() const { return getMMO().isAtomic(); } /// Returns true if the attached MachineMemOpeand as the volatile flag set. bool isVolatile() const { return getMMO().isVolatile(); } /// Returns true if the memory operation is neither atomic or volatile. bool isSimple() const { return !isAtomic() && !isVolatile(); } /// Returns true if this memory operation doesn't have any ordering /// constraints other than normal aliasing. Volatile and (ordered) atomic /// memory operations can't be reordered. bool isUnordered() const { return getMMO().isUnordered(); } /// Returns the size in bytes of the memory access. uint64_t getMemSize() const { return getMMO().getSize(); } /// Returns the size in bits of the memory access. uint64_t getMemSizeInBits() const { return getMMO().getSizeInBits(); } static bool classof(const MachineInstr *MI) { return GenericMachineInstr::classof(MI) && MI->hasOneMemOperand(); } }; /// Represents any type of generic load or store. /// G_LOAD, G_STORE, G_ZEXTLOAD, G_SEXTLOAD. class GLoadStore : public GMemOperation { public: /// Get the source register of the pointer value. Register getPointerReg() const { return getOperand(1).getReg(); } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_LOAD: case TargetOpcode::G_STORE: case TargetOpcode::G_ZEXTLOAD: case TargetOpcode::G_SEXTLOAD: return true; default: return false; } } }; /// Represents indexed loads. These are different enough from regular loads /// that they get their own class. Including them in GAnyLoad would probably /// make a footgun for someone. class GIndexedLoad : public GMemOperation { public: /// Get the definition register of the loaded value. Register getDstReg() const { return getOperand(0).getReg(); } /// Get the def register of the writeback value. Register getWritebackReg() const { return getOperand(1).getReg(); } /// Get the base register of the pointer value. Register getBaseReg() const { return getOperand(2).getReg(); } /// Get the offset register of the pointer value. Register getOffsetReg() const { return getOperand(3).getReg(); } bool isPre() const { return getOperand(4).getImm() == 1; } bool isPost() const { return !isPre(); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_INDEXED_LOAD; } }; /// Represents a G_INDEX_ZEXTLOAD/G_INDEXED_SEXTLOAD. class GIndexedExtLoad : public GIndexedLoad { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_INDEXED_SEXTLOAD || MI->getOpcode() == TargetOpcode::G_INDEXED_ZEXTLOAD; } }; /// Represents either G_INDEXED_LOAD, G_INDEXED_ZEXTLOAD or G_INDEXED_SEXTLOAD. class GIndexedAnyExtLoad : public GIndexedLoad { public: static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_INDEXED_LOAD: case TargetOpcode::G_INDEXED_ZEXTLOAD: case TargetOpcode::G_INDEXED_SEXTLOAD: return true; default: return false; } } }; /// Represents a G_ZEXTLOAD. class GIndexedZExtLoad : GIndexedExtLoad { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_INDEXED_ZEXTLOAD; } }; /// Represents a G_SEXTLOAD. class GIndexedSExtLoad : GIndexedExtLoad { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_INDEXED_SEXTLOAD; } }; /// Represents indexed stores. class GIndexedStore : public GMemOperation { public: /// Get the def register of the writeback value. Register getWritebackReg() const { return getOperand(0).getReg(); } /// Get the stored value register. Register getValueReg() const { return getOperand(1).getReg(); } /// Get the base register of the pointer value. Register getBaseReg() const { return getOperand(2).getReg(); } /// Get the offset register of the pointer value. Register getOffsetReg() const { return getOperand(3).getReg(); } bool isPre() const { return getOperand(4).getImm() == 1; } bool isPost() const { return !isPre(); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_INDEXED_STORE; } }; /// Represents any generic load, including sign/zero extending variants. class GAnyLoad : public GLoadStore { public: /// Get the definition register of the loaded value. Register getDstReg() const { return getOperand(0).getReg(); } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_LOAD: case TargetOpcode::G_ZEXTLOAD: case TargetOpcode::G_SEXTLOAD: return true; default: return false; } } }; /// Represents a G_LOAD. class GLoad : public GAnyLoad { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_LOAD; } }; /// Represents either a G_SEXTLOAD or G_ZEXTLOAD. class GExtLoad : public GAnyLoad { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_SEXTLOAD || MI->getOpcode() == TargetOpcode::G_ZEXTLOAD; } }; /// Represents a G_SEXTLOAD. class GSExtLoad : public GExtLoad { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_SEXTLOAD; } }; /// Represents a G_ZEXTLOAD. class GZExtLoad : public GExtLoad { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_ZEXTLOAD; } }; /// Represents a G_STORE. class GStore : public GLoadStore { public: /// Get the stored value register. Register getValueReg() const { return getOperand(0).getReg(); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_STORE; } }; /// Represents a G_UNMERGE_VALUES. class GUnmerge : public GenericMachineInstr { public: /// Returns the number of def registers. unsigned getNumDefs() const { return getNumOperands() - 1; } /// Get the unmerge source register. Register getSourceReg() const { return getOperand(getNumDefs()).getReg(); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_UNMERGE_VALUES; } }; /// Represents G_BUILD_VECTOR, G_CONCAT_VECTORS or G_MERGE_VALUES. /// All these have the common property of generating a single value from /// multiple sources. class GMergeLikeInstr : public GenericMachineInstr { public: /// Returns the number of source registers. unsigned getNumSources() const { return getNumOperands() - 1; } /// Returns the I'th source register. Register getSourceReg(unsigned I) const { return getReg(I + 1); } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_MERGE_VALUES: case TargetOpcode::G_CONCAT_VECTORS: case TargetOpcode::G_BUILD_VECTOR: return true; default: return false; } } }; /// Represents a G_MERGE_VALUES. class GMerge : public GMergeLikeInstr { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_MERGE_VALUES; } }; /// Represents a G_CONCAT_VECTORS. class GConcatVectors : public GMergeLikeInstr { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_CONCAT_VECTORS; } }; /// Represents a G_BUILD_VECTOR. class GBuildVector : public GMergeLikeInstr { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_BUILD_VECTOR; } }; /// Represents a G_PTR_ADD. class GPtrAdd : public GenericMachineInstr { public: Register getBaseReg() const { return getReg(1); } Register getOffsetReg() const { return getReg(2); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_PTR_ADD; } }; /// Represents a G_IMPLICIT_DEF. class GImplicitDef : public GenericMachineInstr { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF; } }; /// Represents a G_SELECT. class GSelect : public GenericMachineInstr { public: Register getCondReg() const { return getReg(1); } Register getTrueReg() const { return getReg(2); } Register getFalseReg() const { return getReg(3); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_SELECT; } }; /// Represent a G_ICMP or G_FCMP. class GAnyCmp : public GenericMachineInstr { public: CmpInst::Predicate getCond() const { return static_cast(getOperand(1).getPredicate()); } Register getLHSReg() const { return getReg(2); } Register getRHSReg() const { return getReg(3); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_ICMP || MI->getOpcode() == TargetOpcode::G_FCMP; } }; /// Represent a G_ICMP. class GICmp : public GAnyCmp { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_ICMP; } }; /// Represent a G_FCMP. class GFCmp : public GAnyCmp { public: static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_FCMP; } }; /// Represents overflowing binary operations. /// Only carry-out: /// G_UADDO, G_SADDO, G_USUBO, G_SSUBO, G_UMULO, G_SMULO /// Carry-in and carry-out: /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE class GBinOpCarryOut : public GenericMachineInstr { public: Register getDstReg() const { return getReg(0); } Register getCarryOutReg() const { return getReg(1); } MachineOperand &getLHS() { return getOperand(2); } MachineOperand &getRHS() { return getOperand(3); } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_UADDO: case TargetOpcode::G_SADDO: case TargetOpcode::G_USUBO: case TargetOpcode::G_SSUBO: case TargetOpcode::G_UADDE: case TargetOpcode::G_SADDE: case TargetOpcode::G_USUBE: case TargetOpcode::G_SSUBE: case TargetOpcode::G_UMULO: case TargetOpcode::G_SMULO: return true; default: return false; } } }; /// Represents overflowing add/sub operations. /// Only carry-out: /// G_UADDO, G_SADDO, G_USUBO, G_SSUBO /// Carry-in and carry-out: /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE class GAddSubCarryOut : public GBinOpCarryOut { public: bool isAdd() const { switch (getOpcode()) { case TargetOpcode::G_UADDO: case TargetOpcode::G_SADDO: case TargetOpcode::G_UADDE: case TargetOpcode::G_SADDE: return true; default: return false; } } bool isSub() const { return !isAdd(); } bool isSigned() const { switch (getOpcode()) { case TargetOpcode::G_SADDO: case TargetOpcode::G_SSUBO: case TargetOpcode::G_SADDE: case TargetOpcode::G_SSUBE: return true; default: return false; } } bool isUnsigned() const { return !isSigned(); } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_UADDO: case TargetOpcode::G_SADDO: case TargetOpcode::G_USUBO: case TargetOpcode::G_SSUBO: case TargetOpcode::G_UADDE: case TargetOpcode::G_SADDE: case TargetOpcode::G_USUBE: case TargetOpcode::G_SSUBE: return true; default: return false; } } }; /// Represents overflowing add/sub operations that also consume a carry-in. /// G_UADDE, G_SADDE, G_USUBE, G_SSUBE class GAddSubCarryInOut : public GAddSubCarryOut { public: Register getCarryInReg() const { return getReg(4); } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_UADDE: case TargetOpcode::G_SADDE: case TargetOpcode::G_USUBE: case TargetOpcode::G_SSUBE: return true; default: return false; } } }; /// Represents a call to an intrinsic. class GIntrinsic final : public GenericMachineInstr { public: Intrinsic::ID getIntrinsicID() const { return getOperand(getNumExplicitDefs()).getIntrinsicID(); } bool is(Intrinsic::ID ID) const { return getIntrinsicID() == ID; } bool hasSideEffects() const { switch (getOpcode()) { case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: return true; default: return false; } } bool isConvergent() const { switch (getOpcode()) { case TargetOpcode::G_INTRINSIC_CONVERGENT: case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: return true; default: return false; } } static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_INTRINSIC: case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS: case TargetOpcode::G_INTRINSIC_CONVERGENT: case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS: return true; default: return false; } } }; // Represents a (non-sequential) vector reduction operation. class GVecReduce : public GenericMachineInstr { public: static bool classof(const MachineInstr *MI) { switch (MI->getOpcode()) { case TargetOpcode::G_VECREDUCE_FADD: case TargetOpcode::G_VECREDUCE_FMUL: case TargetOpcode::G_VECREDUCE_FMAX: case TargetOpcode::G_VECREDUCE_FMIN: case TargetOpcode::G_VECREDUCE_FMAXIMUM: case TargetOpcode::G_VECREDUCE_FMINIMUM: case TargetOpcode::G_VECREDUCE_ADD: case TargetOpcode::G_VECREDUCE_MUL: case TargetOpcode::G_VECREDUCE_AND: case TargetOpcode::G_VECREDUCE_OR: case TargetOpcode::G_VECREDUCE_XOR: case TargetOpcode::G_VECREDUCE_SMAX: case TargetOpcode::G_VECREDUCE_SMIN: case TargetOpcode::G_VECREDUCE_UMAX: case TargetOpcode::G_VECREDUCE_UMIN: return true; default: return false; } } /// Get the opcode for the equivalent scalar operation for this reduction. /// E.g. for G_VECREDUCE_FADD, this returns G_FADD. unsigned getScalarOpcForReduction() { unsigned ScalarOpc; switch (getOpcode()) { case TargetOpcode::G_VECREDUCE_FADD: ScalarOpc = TargetOpcode::G_FADD; break; case TargetOpcode::G_VECREDUCE_FMUL: ScalarOpc = TargetOpcode::G_FMUL; break; case TargetOpcode::G_VECREDUCE_FMAX: ScalarOpc = TargetOpcode::G_FMAXNUM; break; case TargetOpcode::G_VECREDUCE_FMIN: ScalarOpc = TargetOpcode::G_FMINNUM; break; case TargetOpcode::G_VECREDUCE_FMAXIMUM: ScalarOpc = TargetOpcode::G_FMAXIMUM; break; case TargetOpcode::G_VECREDUCE_FMINIMUM: ScalarOpc = TargetOpcode::G_FMINIMUM; break; case TargetOpcode::G_VECREDUCE_ADD: ScalarOpc = TargetOpcode::G_ADD; break; case TargetOpcode::G_VECREDUCE_MUL: ScalarOpc = TargetOpcode::G_MUL; break; case TargetOpcode::G_VECREDUCE_AND: ScalarOpc = TargetOpcode::G_AND; break; case TargetOpcode::G_VECREDUCE_OR: ScalarOpc = TargetOpcode::G_OR; break; case TargetOpcode::G_VECREDUCE_XOR: ScalarOpc = TargetOpcode::G_XOR; break; case TargetOpcode::G_VECREDUCE_SMAX: ScalarOpc = TargetOpcode::G_SMAX; break; case TargetOpcode::G_VECREDUCE_SMIN: ScalarOpc = TargetOpcode::G_SMIN; break; case TargetOpcode::G_VECREDUCE_UMAX: ScalarOpc = TargetOpcode::G_UMAX; break; case TargetOpcode::G_VECREDUCE_UMIN: ScalarOpc = TargetOpcode::G_UMIN; break; default: llvm_unreachable("Unhandled reduction"); } return ScalarOpc; } }; /// Represents a G_PHI. class GPhi : public GenericMachineInstr { public: /// Returns the number of incoming values. unsigned getNumIncomingValues() const { return (getNumOperands() - 1) / 2; } /// Returns the I'th incoming vreg. Register getIncomingValue(unsigned I) const { return getOperand(I * 2 + 1).getReg(); } /// Returns the I'th incoming basic block. MachineBasicBlock *getIncomingBlock(unsigned I) const { return getOperand(I * 2 + 2).getMBB(); } static bool classof(const MachineInstr *MI) { return MI->getOpcode() == TargetOpcode::G_PHI; } }; } // namespace llvm #endif // LLVM_CODEGEN_GLOBALISEL_GENERICMACHINEINSTRS_H