//===- llvm/CodeGen/GlobalISel/InstructionSelectorImpl.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 This file declares the API for the instruction selector. /// This class is responsible for selecting machine instructions. /// It's implemented by the target. It's used by the InstructionSelect pass. // //===----------------------------------------------------------------------===// #ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H #define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" #include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" #include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetOpcodes.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/IR/Constants.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include #include namespace llvm { /// GlobalISel PatFrag Predicates enum { GIPFP_I64_Invalid = 0, GIPFP_APInt_Invalid = 0, GIPFP_APFloat_Invalid = 0, GIPFP_MI_Invalid = 0, }; template bool InstructionSelector::executeMatchTable( TgtInstructionSelector &ISel, NewMIVector &OutMIs, MatcherState &State, const ISelInfoTy &ISelInfo, const int64_t *MatchTable, const TargetInstrInfo &TII, MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI, const RegisterBankInfo &RBI, const PredicateBitset &AvailableFeatures, CodeGenCoverage &CoverageInfo) const { uint64_t CurrentIdx = 0; SmallVector OnFailResumeAt; enum RejectAction { RejectAndGiveUp, RejectAndResume }; auto handleReject = [&]() -> RejectAction { DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": Rejected\n"); if (OnFailResumeAt.empty()) return RejectAndGiveUp; CurrentIdx = OnFailResumeAt.pop_back_val(); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": Resume at " << CurrentIdx << " (" << OnFailResumeAt.size() << " try-blocks remain)\n"); return RejectAndResume; }; while (true) { assert(CurrentIdx != ~0u && "Invalid MatchTable index"); int64_t MatcherOpcode = MatchTable[CurrentIdx++]; switch (MatcherOpcode) { case GIM_Try: { DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": Begin try-block\n"); OnFailResumeAt.push_back(MatchTable[CurrentIdx++]); break; } case GIM_RecordInsn: { int64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; // As an optimisation we require that MIs[0] is always the root. Refuse // any attempt to modify it. assert(NewInsnID != 0 && "Refusing to modify MIs[0]"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); if (!MO.isReg()) { DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": Not a register\n"); if (handleReject() == RejectAndGiveUp) return false; break; } if (TRI.isPhysicalRegister(MO.getReg())) { DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": Is a physical register\n"); if (handleReject() == RejectAndGiveUp) return false; break; } MachineInstr *NewMI = MRI.getVRegDef(MO.getReg()); if ((size_t)NewInsnID < State.MIs.size()) State.MIs[NewInsnID] = NewMI; else { assert((size_t)NewInsnID == State.MIs.size() && "Expected to store MIs in order"); State.MIs.push_back(NewMI); } DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": MIs[" << NewInsnID << "] = GIM_RecordInsn(" << InsnID << ", " << OpIdx << ")\n"); break; } case GIM_CheckFeatures: { int64_t ExpectedBitsetID = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckFeatures(ExpectedBitsetID=" << ExpectedBitsetID << ")\n"); if ((AvailableFeatures & ISelInfo.FeatureBitsets[ExpectedBitsetID]) != ISelInfo.FeatureBitsets[ExpectedBitsetID]) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_CheckOpcode: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Expected = MatchTable[CurrentIdx++]; assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); unsigned Opcode = State.MIs[InsnID]->getOpcode(); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckOpcode(MIs[" << InsnID << "], ExpectedOpcode=" << Expected << ") // Got=" << Opcode << "\n"); if (Opcode != Expected) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_SwitchOpcode: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t LowerBound = MatchTable[CurrentIdx++]; int64_t UpperBound = MatchTable[CurrentIdx++]; int64_t Default = MatchTable[CurrentIdx++]; assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); const int64_t Opcode = State.MIs[InsnID]->getOpcode(); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), { dbgs() << CurrentIdx << ": GIM_SwitchOpcode(MIs[" << InsnID << "], [" << LowerBound << ", " << UpperBound << "), Default=" << Default << ", JumpTable...) // Got=" << Opcode << "\n"; }); if (Opcode < LowerBound || UpperBound <= Opcode) { CurrentIdx = Default; break; } CurrentIdx = MatchTable[CurrentIdx + (Opcode - LowerBound)]; if (!CurrentIdx) { CurrentIdx = Default; break; } OnFailResumeAt.push_back(Default); break; } case GIM_SwitchType: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t LowerBound = MatchTable[CurrentIdx++]; int64_t UpperBound = MatchTable[CurrentIdx++]; int64_t Default = MatchTable[CurrentIdx++]; assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), { dbgs() << CurrentIdx << ": GIM_SwitchType(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), [" << LowerBound << ", " << UpperBound << "), Default=" << Default << ", JumpTable...) // Got="; if (!MO.isReg()) dbgs() << "Not a VReg\n"; else dbgs() << MRI.getType(MO.getReg()) << "\n"; }); if (!MO.isReg()) { CurrentIdx = Default; break; } const LLT Ty = MRI.getType(MO.getReg()); const auto TyI = ISelInfo.TypeIDMap.find(Ty); if (TyI == ISelInfo.TypeIDMap.end()) { CurrentIdx = Default; break; } const int64_t TypeID = TyI->second; if (TypeID < LowerBound || UpperBound <= TypeID) { CurrentIdx = Default; break; } CurrentIdx = MatchTable[CurrentIdx + (TypeID - LowerBound)]; if (!CurrentIdx) { CurrentIdx = Default; break; } OnFailResumeAt.push_back(Default); break; } case GIM_CheckNumOperands: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Expected = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckNumOperands(MIs[" << InsnID << "], Expected=" << Expected << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (State.MIs[InsnID]->getNumOperands() != Expected) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_CheckI64ImmPredicate: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Predicate = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckI64ImmPredicate(MIs[" << InsnID << "], Predicate=" << Predicate << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); assert(State.MIs[InsnID]->getOpcode() == TargetOpcode::G_CONSTANT && "Expected G_CONSTANT"); assert(Predicate > GIPFP_I64_Invalid && "Expected a valid predicate"); int64_t Value = 0; if (State.MIs[InsnID]->getOperand(1).isCImm()) Value = State.MIs[InsnID]->getOperand(1).getCImm()->getSExtValue(); else if (State.MIs[InsnID]->getOperand(1).isImm()) Value = State.MIs[InsnID]->getOperand(1).getImm(); else llvm_unreachable("Expected Imm or CImm operand"); if (!testImmPredicate_I64(Predicate, Value)) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckAPIntImmPredicate: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Predicate = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckAPIntImmPredicate(MIs[" << InsnID << "], Predicate=" << Predicate << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); assert(State.MIs[InsnID]->getOpcode() == TargetOpcode::G_CONSTANT && "Expected G_CONSTANT"); assert(Predicate > GIPFP_APInt_Invalid && "Expected a valid predicate"); APInt Value; if (State.MIs[InsnID]->getOperand(1).isCImm()) Value = State.MIs[InsnID]->getOperand(1).getCImm()->getValue(); else llvm_unreachable("Expected Imm or CImm operand"); if (!testImmPredicate_APInt(Predicate, Value)) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckAPFloatImmPredicate: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Predicate = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckAPFloatImmPredicate(MIs[" << InsnID << "], Predicate=" << Predicate << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); assert(State.MIs[InsnID]->getOpcode() == TargetOpcode::G_FCONSTANT && "Expected G_FCONSTANT"); assert(State.MIs[InsnID]->getOperand(1).isFPImm() && "Expected FPImm operand"); assert(Predicate > GIPFP_APFloat_Invalid && "Expected a valid predicate"); APFloat Value = State.MIs[InsnID]->getOperand(1).getFPImm()->getValueAPF(); if (!testImmPredicate_APFloat(Predicate, Value)) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckCxxInsnPredicate: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Predicate = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckCxxPredicate(MIs[" << InsnID << "], Predicate=" << Predicate << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); assert(Predicate > GIPFP_MI_Invalid && "Expected a valid predicate"); if (!testMIPredicate_MI(Predicate, *State.MIs[InsnID])) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckAtomicOrdering: { int64_t InsnID = MatchTable[CurrentIdx++]; AtomicOrdering Ordering = (AtomicOrdering)MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckAtomicOrdering(MIs[" << InsnID << "], " << (uint64_t)Ordering << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (!State.MIs[InsnID]->hasOneMemOperand()) if (handleReject() == RejectAndGiveUp) return false; for (const auto &MMO : State.MIs[InsnID]->memoperands()) if (MMO->getOrdering() != Ordering) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckAtomicOrderingOrStrongerThan: { int64_t InsnID = MatchTable[CurrentIdx++]; AtomicOrdering Ordering = (AtomicOrdering)MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckAtomicOrderingOrStrongerThan(MIs[" << InsnID << "], " << (uint64_t)Ordering << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (!State.MIs[InsnID]->hasOneMemOperand()) if (handleReject() == RejectAndGiveUp) return false; for (const auto &MMO : State.MIs[InsnID]->memoperands()) if (!isAtLeastOrStrongerThan(MMO->getOrdering(), Ordering)) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckAtomicOrderingWeakerThan: { int64_t InsnID = MatchTable[CurrentIdx++]; AtomicOrdering Ordering = (AtomicOrdering)MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckAtomicOrderingWeakerThan(MIs[" << InsnID << "], " << (uint64_t)Ordering << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (!State.MIs[InsnID]->hasOneMemOperand()) if (handleReject() == RejectAndGiveUp) return false; for (const auto &MMO : State.MIs[InsnID]->memoperands()) if (!isStrongerThan(Ordering, MMO->getOrdering())) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckMemoryAddressSpace: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t MMOIdx = MatchTable[CurrentIdx++]; // This accepts a list of possible address spaces. const int NumAddrSpace = MatchTable[CurrentIdx++]; if (State.MIs[InsnID]->getNumMemOperands() <= MMOIdx) { if (handleReject() == RejectAndGiveUp) return false; break; } // Need to still jump to the end of the list of address spaces if we find // a match earlier. const uint64_t LastIdx = CurrentIdx + NumAddrSpace; const MachineMemOperand *MMO = *(State.MIs[InsnID]->memoperands_begin() + MMOIdx); const unsigned MMOAddrSpace = MMO->getAddrSpace(); bool Success = false; for (int I = 0; I != NumAddrSpace; ++I) { unsigned AddrSpace = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE( TgtInstructionSelector::getName(), dbgs() << "addrspace(" << MMOAddrSpace << ") vs " << AddrSpace << '\n'); if (AddrSpace == MMOAddrSpace) { Success = true; break; } } CurrentIdx = LastIdx; if (!Success && handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckMemorySizeEqualTo: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t MMOIdx = MatchTable[CurrentIdx++]; uint64_t Size = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckMemorySizeEqual(MIs[" << InsnID << "]->memoperands() + " << MMOIdx << ", Size=" << Size << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (State.MIs[InsnID]->getNumMemOperands() <= MMOIdx) { if (handleReject() == RejectAndGiveUp) return false; break; } MachineMemOperand *MMO = *(State.MIs[InsnID]->memoperands_begin() + MMOIdx); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << MMO->getSize() << " bytes vs " << Size << " bytes\n"); if (MMO->getSize() != Size) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckMemorySizeEqualToLLT: case GIM_CheckMemorySizeLessThanLLT: case GIM_CheckMemorySizeGreaterThanLLT: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t MMOIdx = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE( TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckMemorySize" << (MatcherOpcode == GIM_CheckMemorySizeEqualToLLT ? "EqualTo" : MatcherOpcode == GIM_CheckMemorySizeGreaterThanLLT ? "GreaterThan" : "LessThan") << "LLT(MIs[" << InsnID << "]->memoperands() + " << MMOIdx << ", OpIdx=" << OpIdx << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); if (!MO.isReg()) { DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": Not a register\n"); if (handleReject() == RejectAndGiveUp) return false; break; } if (State.MIs[InsnID]->getNumMemOperands() <= MMOIdx) { if (handleReject() == RejectAndGiveUp) return false; break; } MachineMemOperand *MMO = *(State.MIs[InsnID]->memoperands_begin() + MMOIdx); unsigned Size = MRI.getType(MO.getReg()).getSizeInBits(); if (MatcherOpcode == GIM_CheckMemorySizeEqualToLLT && MMO->getSizeInBits() != Size) { if (handleReject() == RejectAndGiveUp) return false; } else if (MatcherOpcode == GIM_CheckMemorySizeLessThanLLT && MMO->getSizeInBits() >= Size) { if (handleReject() == RejectAndGiveUp) return false; } else if (MatcherOpcode == GIM_CheckMemorySizeGreaterThanLLT && MMO->getSizeInBits() <= Size) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckType: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t TypeID = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckType(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), TypeID=" << TypeID << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); if (!MO.isReg() || MRI.getType(MO.getReg()) != ISelInfo.TypeObjects[TypeID]) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_CheckPointerToAny: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t SizeInBits = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckPointerToAny(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), SizeInBits=" << SizeInBits << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); const LLT Ty = MRI.getType(MO.getReg()); // iPTR must be looked up in the target. if (SizeInBits == 0) { MachineFunction *MF = State.MIs[InsnID]->getParent()->getParent(); const unsigned AddrSpace = Ty.getAddressSpace(); SizeInBits = MF->getDataLayout().getPointerSizeInBits(AddrSpace); } assert(SizeInBits != 0 && "Pointer size must be known"); if (MO.isReg()) { if (!Ty.isPointer() || Ty.getSizeInBits() != SizeInBits) if (handleReject() == RejectAndGiveUp) return false; } else if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckRegBankForClass: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t RCEnum = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckRegBankForClass(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), RCEnum=" << RCEnum << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); if (!MO.isReg() || &RBI.getRegBankFromRegClass(*TRI.getRegClass(RCEnum)) != RBI.getRegBank(MO.getReg(), MRI, TRI)) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_CheckComplexPattern: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t RendererID = MatchTable[CurrentIdx++]; int64_t ComplexPredicateID = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": State.Renderers[" << RendererID << "] = GIM_CheckComplexPattern(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), ComplexPredicateID=" << ComplexPredicateID << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); // FIXME: Use std::invoke() when it's available. ComplexRendererFns Renderer = (ISel.*ISelInfo.ComplexPredicates[ComplexPredicateID])( State.MIs[InsnID]->getOperand(OpIdx)); if (Renderer.hasValue()) State.Renderers[RendererID] = Renderer.getValue(); else if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckConstantInt: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t Value = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckConstantInt(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), Value=" << Value << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); if (MO.isReg()) { // isOperandImmEqual() will sign-extend to 64-bits, so should we. LLT Ty = MRI.getType(MO.getReg()); Value = SignExtend64(Value, Ty.getSizeInBits()); if (!isOperandImmEqual(MO, Value, MRI)) { if (handleReject() == RejectAndGiveUp) return false; } } else if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckLiteralInt: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t Value = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckLiteralInt(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), Value=" << Value << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); if (!MO.isCImm() || !MO.getCImm()->equalsInt(Value)) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_CheckIntrinsicID: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t Value = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckIntrinsicID(MIs[" << InsnID << "]->getOperand(" << OpIdx << "), Value=" << Value << ")\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); MachineOperand &MO = State.MIs[InsnID]->getOperand(OpIdx); if (!MO.isIntrinsicID() || MO.getIntrinsicID() != Value) if (handleReject() == RejectAndGiveUp) return false; break; } case GIM_CheckIsMBB: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckIsMBB(MIs[" << InsnID << "]->getOperand(" << OpIdx << "))\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (!State.MIs[InsnID]->getOperand(OpIdx).isMBB()) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_CheckIsSafeToFold: { int64_t InsnID = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckIsSafeToFold(MIs[" << InsnID << "])\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); if (!isObviouslySafeToFold(*State.MIs[InsnID], *State.MIs[0])) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_CheckIsSameOperand: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t OtherInsnID = MatchTable[CurrentIdx++]; int64_t OtherOpIdx = MatchTable[CurrentIdx++]; DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_CheckIsSameOperand(MIs[" << InsnID << "][" << OpIdx << "], MIs[" << OtherInsnID << "][" << OtherOpIdx << "])\n"); assert(State.MIs[InsnID] != nullptr && "Used insn before defined"); assert(State.MIs[OtherInsnID] != nullptr && "Used insn before defined"); if (!State.MIs[InsnID]->getOperand(OpIdx).isIdenticalTo( State.MIs[OtherInsnID]->getOperand(OtherOpIdx))) { if (handleReject() == RejectAndGiveUp) return false; } break; } case GIM_Reject: DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIM_Reject\n"); if (handleReject() == RejectAndGiveUp) return false; break; case GIR_MutateOpcode: { int64_t OldInsnID = MatchTable[CurrentIdx++]; uint64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t NewOpcode = MatchTable[CurrentIdx++]; if (NewInsnID >= OutMIs.size()) OutMIs.resize(NewInsnID + 1); OutMIs[NewInsnID] = MachineInstrBuilder(*State.MIs[OldInsnID]->getMF(), State.MIs[OldInsnID]); OutMIs[NewInsnID]->setDesc(TII.get(NewOpcode)); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_MutateOpcode(OutMIs[" << NewInsnID << "], MIs[" << OldInsnID << "], " << NewOpcode << ")\n"); break; } case GIR_BuildMI: { uint64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t Opcode = MatchTable[CurrentIdx++]; if (NewInsnID >= OutMIs.size()) OutMIs.resize(NewInsnID + 1); OutMIs[NewInsnID] = BuildMI(*State.MIs[0]->getParent(), State.MIs[0], State.MIs[0]->getDebugLoc(), TII.get(Opcode)); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_BuildMI(OutMIs[" << NewInsnID << "], " << Opcode << ")\n"); break; } case GIR_Copy: { int64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t OldInsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction"); OutMIs[NewInsnID].add(State.MIs[OldInsnID]->getOperand(OpIdx)); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_Copy(OutMIs[" << NewInsnID << "], MIs[" << OldInsnID << "], " << OpIdx << ")\n"); break; } case GIR_CopyOrAddZeroReg: { int64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t OldInsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t ZeroReg = MatchTable[CurrentIdx++]; assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction"); MachineOperand &MO = State.MIs[OldInsnID]->getOperand(OpIdx); if (isOperandImmEqual(MO, 0, MRI)) OutMIs[NewInsnID].addReg(ZeroReg); else OutMIs[NewInsnID].add(MO); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_CopyOrAddZeroReg(OutMIs[" << NewInsnID << "], MIs[" << OldInsnID << "], " << OpIdx << ", " << ZeroReg << ")\n"); break; } case GIR_CopySubReg: { int64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t OldInsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t SubRegIdx = MatchTable[CurrentIdx++]; assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction"); OutMIs[NewInsnID].addReg(State.MIs[OldInsnID]->getOperand(OpIdx).getReg(), 0, SubRegIdx); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_CopySubReg(OutMIs[" << NewInsnID << "], MIs[" << OldInsnID << "], " << OpIdx << ", " << SubRegIdx << ")\n"); break; } case GIR_AddImplicitDef: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t RegNum = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); OutMIs[InsnID].addDef(RegNum, RegState::Implicit); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_AddImplicitDef(OutMIs[" << InsnID << "], " << RegNum << ")\n"); break; } case GIR_AddImplicitUse: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t RegNum = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); OutMIs[InsnID].addUse(RegNum, RegState::Implicit); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_AddImplicitUse(OutMIs[" << InsnID << "], " << RegNum << ")\n"); break; } case GIR_AddRegister: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t RegNum = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); OutMIs[InsnID].addReg(RegNum); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_AddRegister(OutMIs[" << InsnID << "], " << RegNum << ")\n"); break; } case GIR_AddTempRegister: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t TempRegID = MatchTable[CurrentIdx++]; uint64_t TempRegFlags = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); OutMIs[InsnID].addReg(State.TempRegisters[TempRegID], TempRegFlags); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_AddTempRegister(OutMIs[" << InsnID << "], TempRegisters[" << TempRegID << "], " << TempRegFlags << ")\n"); break; } case GIR_AddImm: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t Imm = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); OutMIs[InsnID].addImm(Imm); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_AddImm(OutMIs[" << InsnID << "], " << Imm << ")\n"); break; } case GIR_ComplexRenderer: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t RendererID = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); for (const auto &RenderOpFn : State.Renderers[RendererID]) RenderOpFn(OutMIs[InsnID]); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_ComplexRenderer(OutMIs[" << InsnID << "], " << RendererID << ")\n"); break; } case GIR_ComplexSubOperandRenderer: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t RendererID = MatchTable[CurrentIdx++]; int64_t RenderOpID = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); State.Renderers[RendererID][RenderOpID](OutMIs[InsnID]); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_ComplexSubOperandRenderer(OutMIs[" << InsnID << "], " << RendererID << ", " << RenderOpID << ")\n"); break; } case GIR_CopyConstantAsSImm: { int64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t OldInsnID = MatchTable[CurrentIdx++]; assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction"); assert(State.MIs[OldInsnID]->getOpcode() == TargetOpcode::G_CONSTANT && "Expected G_CONSTANT"); if (State.MIs[OldInsnID]->getOperand(1).isCImm()) { OutMIs[NewInsnID].addImm( State.MIs[OldInsnID]->getOperand(1).getCImm()->getSExtValue()); } else if (State.MIs[OldInsnID]->getOperand(1).isImm()) OutMIs[NewInsnID].add(State.MIs[OldInsnID]->getOperand(1)); else llvm_unreachable("Expected Imm or CImm operand"); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_CopyConstantAsSImm(OutMIs[" << NewInsnID << "], MIs[" << OldInsnID << "])\n"); break; } // TODO: Needs a test case once we have a pattern that uses this. case GIR_CopyFConstantAsFPImm: { int64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t OldInsnID = MatchTable[CurrentIdx++]; assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction"); assert(State.MIs[OldInsnID]->getOpcode() == TargetOpcode::G_FCONSTANT && "Expected G_FCONSTANT"); if (State.MIs[OldInsnID]->getOperand(1).isFPImm()) OutMIs[NewInsnID].addFPImm( State.MIs[OldInsnID]->getOperand(1).getFPImm()); else llvm_unreachable("Expected FPImm operand"); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_CopyFPConstantAsFPImm(OutMIs[" << NewInsnID << "], MIs[" << OldInsnID << "])\n"); break; } case GIR_CustomRenderer: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OldInsnID = MatchTable[CurrentIdx++]; int64_t RendererFnID = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_CustomRenderer(OutMIs[" << InsnID << "], MIs[" << OldInsnID << "], " << RendererFnID << ")\n"); (ISel.*ISelInfo.CustomRenderers[RendererFnID])(OutMIs[InsnID], *State.MIs[OldInsnID]); break; } case GIR_ConstrainOperandRC: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t OpIdx = MatchTable[CurrentIdx++]; int64_t RCEnum = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); constrainOperandRegToRegClass(*OutMIs[InsnID].getInstr(), OpIdx, *TRI.getRegClass(RCEnum), TII, TRI, RBI); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_ConstrainOperandRC(OutMIs[" << InsnID << "], " << OpIdx << ", " << RCEnum << ")\n"); break; } case GIR_ConstrainSelectedInstOperands: { int64_t InsnID = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); constrainSelectedInstRegOperands(*OutMIs[InsnID].getInstr(), TII, TRI, RBI); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_ConstrainSelectedInstOperands(OutMIs[" << InsnID << "])\n"); break; } case GIR_MergeMemOperands: { int64_t InsnID = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_MergeMemOperands(OutMIs[" << InsnID << "]"); int64_t MergeInsnID = GIU_MergeMemOperands_EndOfList; while ((MergeInsnID = MatchTable[CurrentIdx++]) != GIU_MergeMemOperands_EndOfList) { DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << ", MIs[" << MergeInsnID << "]"); for (const auto &MMO : State.MIs[MergeInsnID]->memoperands()) OutMIs[InsnID].addMemOperand(MMO); } DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << ")\n"); break; } case GIR_EraseFromParent: { int64_t InsnID = MatchTable[CurrentIdx++]; assert(State.MIs[InsnID] && "Attempted to erase an undefined instruction"); State.MIs[InsnID]->eraseFromParent(); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_EraseFromParent(MIs[" << InsnID << "])\n"); break; } case GIR_MakeTempReg: { int64_t TempRegID = MatchTable[CurrentIdx++]; int64_t TypeID = MatchTable[CurrentIdx++]; State.TempRegisters[TempRegID] = MRI.createGenericVirtualRegister(ISelInfo.TypeObjects[TypeID]); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": TempRegs[" << TempRegID << "] = GIR_MakeTempReg(" << TypeID << ")\n"); break; } case GIR_Coverage: { int64_t RuleID = MatchTable[CurrentIdx++]; CoverageInfo.setCovered(RuleID); DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_Coverage(" << RuleID << ")"); break; } case GIR_Done: DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), dbgs() << CurrentIdx << ": GIR_Done\n"); return true; default: llvm_unreachable("Unexpected command"); } } } } // end namespace llvm #endif // LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTORIMPL_H