| //===- HexagonGenPredicate.cpp --------------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "HexagonInstrInfo.h" |
| #include "HexagonSubtarget.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/CodeGen/MachineBasicBlock.h" |
| #include "llvm/CodeGen/MachineDominators.h" |
| #include "llvm/CodeGen/MachineFunction.h" |
| #include "llvm/CodeGen/MachineFunctionPass.h" |
| #include "llvm/CodeGen/MachineInstr.h" |
| #include "llvm/CodeGen/MachineInstrBuilder.h" |
| #include "llvm/CodeGen/MachineOperand.h" |
| #include "llvm/CodeGen/MachineRegisterInfo.h" |
| #include "llvm/CodeGen/TargetRegisterInfo.h" |
| #include "llvm/IR/DebugLoc.h" |
| #include "llvm/InitializePasses.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/ErrorHandling.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <cassert> |
| #include <iterator> |
| #include <map> |
| #include <queue> |
| #include <set> |
| #include <utility> |
| |
| #define DEBUG_TYPE "gen-pred" |
| |
| using namespace llvm; |
| |
| namespace llvm { |
| |
| void initializeHexagonGenPredicatePass(PassRegistry& Registry); |
| FunctionPass *createHexagonGenPredicate(); |
| |
| } // end namespace llvm |
| |
| namespace { |
| |
| // FIXME: Use TargetInstrInfo::RegSubRegPair |
| struct RegisterSubReg { |
| unsigned R, S; |
| |
| RegisterSubReg(unsigned r = 0, unsigned s = 0) : R(r), S(s) {} |
| RegisterSubReg(const MachineOperand &MO) : R(MO.getReg()), S(MO.getSubReg()) {} |
| RegisterSubReg(const Register &Reg) : R(Reg), S(0) {} |
| |
| bool operator== (const RegisterSubReg &Reg) const { |
| return R == Reg.R && S == Reg.S; |
| } |
| |
| bool operator< (const RegisterSubReg &Reg) const { |
| return R < Reg.R || (R == Reg.R && S < Reg.S); |
| } |
| }; |
| |
| struct PrintRegister { |
| friend raw_ostream &operator<< (raw_ostream &OS, const PrintRegister &PR); |
| |
| PrintRegister(RegisterSubReg R, const TargetRegisterInfo &I) : Reg(R), TRI(I) {} |
| |
| private: |
| RegisterSubReg Reg; |
| const TargetRegisterInfo &TRI; |
| }; |
| |
| raw_ostream &operator<< (raw_ostream &OS, const PrintRegister &PR) |
| LLVM_ATTRIBUTE_UNUSED; |
| raw_ostream &operator<< (raw_ostream &OS, const PrintRegister &PR) { |
| return OS << printReg(PR.Reg.R, &PR.TRI, PR.Reg.S); |
| } |
| |
| class HexagonGenPredicate : public MachineFunctionPass { |
| public: |
| static char ID; |
| |
| HexagonGenPredicate() : MachineFunctionPass(ID) { |
| initializeHexagonGenPredicatePass(*PassRegistry::getPassRegistry()); |
| } |
| |
| StringRef getPassName() const override { |
| return "Hexagon generate predicate operations"; |
| } |
| |
| void getAnalysisUsage(AnalysisUsage &AU) const override { |
| AU.addRequired<MachineDominatorTree>(); |
| AU.addPreserved<MachineDominatorTree>(); |
| MachineFunctionPass::getAnalysisUsage(AU); |
| } |
| |
| bool runOnMachineFunction(MachineFunction &MF) override; |
| |
| private: |
| using VectOfInst = SetVector<MachineInstr *>; |
| using SetOfReg = std::set<RegisterSubReg>; |
| using RegToRegMap = std::map<RegisterSubReg, RegisterSubReg>; |
| |
| const HexagonInstrInfo *TII = nullptr; |
| const HexagonRegisterInfo *TRI = nullptr; |
| MachineRegisterInfo *MRI = nullptr; |
| SetOfReg PredGPRs; |
| VectOfInst PUsers; |
| RegToRegMap G2P; |
| |
| bool isPredReg(unsigned R); |
| void collectPredicateGPR(MachineFunction &MF); |
| void processPredicateGPR(const RegisterSubReg &Reg); |
| unsigned getPredForm(unsigned Opc); |
| bool isConvertibleToPredForm(const MachineInstr *MI); |
| bool isScalarCmp(unsigned Opc); |
| bool isScalarPred(RegisterSubReg PredReg); |
| RegisterSubReg getPredRegFor(const RegisterSubReg &Reg); |
| bool convertToPredForm(MachineInstr *MI); |
| bool eliminatePredCopies(MachineFunction &MF); |
| }; |
| |
| } // end anonymous namespace |
| |
| char HexagonGenPredicate::ID = 0; |
| |
| INITIALIZE_PASS_BEGIN(HexagonGenPredicate, "hexagon-gen-pred", |
| "Hexagon generate predicate operations", false, false) |
| INITIALIZE_PASS_DEPENDENCY(MachineDominatorTree) |
| INITIALIZE_PASS_END(HexagonGenPredicate, "hexagon-gen-pred", |
| "Hexagon generate predicate operations", false, false) |
| |
| bool HexagonGenPredicate::isPredReg(unsigned R) { |
| if (!Register::isVirtualRegister(R)) |
| return false; |
| const TargetRegisterClass *RC = MRI->getRegClass(R); |
| return RC == &Hexagon::PredRegsRegClass; |
| } |
| |
| unsigned HexagonGenPredicate::getPredForm(unsigned Opc) { |
| using namespace Hexagon; |
| |
| switch (Opc) { |
| case A2_and: |
| case A2_andp: |
| return C2_and; |
| case A4_andn: |
| case A4_andnp: |
| return C2_andn; |
| case M4_and_and: |
| return C4_and_and; |
| case M4_and_andn: |
| return C4_and_andn; |
| case M4_and_or: |
| return C4_and_or; |
| |
| case A2_or: |
| case A2_orp: |
| return C2_or; |
| case A4_orn: |
| case A4_ornp: |
| return C2_orn; |
| case M4_or_and: |
| return C4_or_and; |
| case M4_or_andn: |
| return C4_or_andn; |
| case M4_or_or: |
| return C4_or_or; |
| |
| case A2_xor: |
| case A2_xorp: |
| return C2_xor; |
| |
| case C2_tfrrp: |
| return COPY; |
| } |
| // The opcode corresponding to 0 is TargetOpcode::PHI. We can use 0 here |
| // to denote "none", but we need to make sure that none of the valid opcodes |
| // that we return will ever be 0. |
| static_assert(PHI == 0, "Use different value for <none>"); |
| return 0; |
| } |
| |
| bool HexagonGenPredicate::isConvertibleToPredForm(const MachineInstr *MI) { |
| unsigned Opc = MI->getOpcode(); |
| if (getPredForm(Opc) != 0) |
| return true; |
| |
| // Comparisons against 0 are also convertible. This does not apply to |
| // A4_rcmpeqi or A4_rcmpneqi, since they produce values 0 or 1, which |
| // may not match the value that the predicate register would have if |
| // it was converted to a predicate form. |
| switch (Opc) { |
| case Hexagon::C2_cmpeqi: |
| case Hexagon::C4_cmpneqi: |
| if (MI->getOperand(2).isImm() && MI->getOperand(2).getImm() == 0) |
| return true; |
| break; |
| } |
| return false; |
| } |
| |
| void HexagonGenPredicate::collectPredicateGPR(MachineFunction &MF) { |
| for (MachineFunction::iterator A = MF.begin(), Z = MF.end(); A != Z; ++A) { |
| MachineBasicBlock &B = *A; |
| for (MachineBasicBlock::iterator I = B.begin(), E = B.end(); I != E; ++I) { |
| MachineInstr *MI = &*I; |
| unsigned Opc = MI->getOpcode(); |
| switch (Opc) { |
| case Hexagon::C2_tfrpr: |
| case TargetOpcode::COPY: |
| if (isPredReg(MI->getOperand(1).getReg())) { |
| RegisterSubReg RD = MI->getOperand(0); |
| if (Register::isVirtualRegister(RD.R)) |
| PredGPRs.insert(RD); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| void HexagonGenPredicate::processPredicateGPR(const RegisterSubReg &Reg) { |
| LLVM_DEBUG(dbgs() << __func__ << ": " << printReg(Reg.R, TRI, Reg.S) << "\n"); |
| using use_iterator = MachineRegisterInfo::use_iterator; |
| |
| use_iterator I = MRI->use_begin(Reg.R), E = MRI->use_end(); |
| if (I == E) { |
| LLVM_DEBUG(dbgs() << "Dead reg: " << printReg(Reg.R, TRI, Reg.S) << '\n'); |
| MachineInstr *DefI = MRI->getVRegDef(Reg.R); |
| DefI->eraseFromParent(); |
| return; |
| } |
| |
| for (; I != E; ++I) { |
| MachineInstr *UseI = I->getParent(); |
| if (isConvertibleToPredForm(UseI)) |
| PUsers.insert(UseI); |
| } |
| } |
| |
| RegisterSubReg HexagonGenPredicate::getPredRegFor(const RegisterSubReg &Reg) { |
| // Create a predicate register for a given Reg. The newly created register |
| // will have its value copied from Reg, so that it can be later used as |
| // an operand in other instructions. |
| assert(Register::isVirtualRegister(Reg.R)); |
| RegToRegMap::iterator F = G2P.find(Reg); |
| if (F != G2P.end()) |
| return F->second; |
| |
| LLVM_DEBUG(dbgs() << __func__ << ": " << PrintRegister(Reg, *TRI)); |
| MachineInstr *DefI = MRI->getVRegDef(Reg.R); |
| assert(DefI); |
| unsigned Opc = DefI->getOpcode(); |
| if (Opc == Hexagon::C2_tfrpr || Opc == TargetOpcode::COPY) { |
| assert(DefI->getOperand(0).isDef() && DefI->getOperand(1).isUse()); |
| RegisterSubReg PR = DefI->getOperand(1); |
| G2P.insert(std::make_pair(Reg, PR)); |
| LLVM_DEBUG(dbgs() << " -> " << PrintRegister(PR, *TRI) << '\n'); |
| return PR; |
| } |
| |
| MachineBasicBlock &B = *DefI->getParent(); |
| DebugLoc DL = DefI->getDebugLoc(); |
| const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass; |
| Register NewPR = MRI->createVirtualRegister(PredRC); |
| |
| // For convertible instructions, do not modify them, so that they can |
| // be converted later. Generate a copy from Reg to NewPR. |
| if (isConvertibleToPredForm(DefI)) { |
| MachineBasicBlock::iterator DefIt = DefI; |
| BuildMI(B, std::next(DefIt), DL, TII->get(TargetOpcode::COPY), NewPR) |
| .addReg(Reg.R, 0, Reg.S); |
| G2P.insert(std::make_pair(Reg, RegisterSubReg(NewPR))); |
| LLVM_DEBUG(dbgs() << " -> !" << PrintRegister(RegisterSubReg(NewPR), *TRI) |
| << '\n'); |
| return RegisterSubReg(NewPR); |
| } |
| |
| llvm_unreachable("Invalid argument"); |
| } |
| |
| bool HexagonGenPredicate::isScalarCmp(unsigned Opc) { |
| switch (Opc) { |
| case Hexagon::C2_cmpeq: |
| case Hexagon::C2_cmpgt: |
| case Hexagon::C2_cmpgtu: |
| case Hexagon::C2_cmpeqp: |
| case Hexagon::C2_cmpgtp: |
| case Hexagon::C2_cmpgtup: |
| case Hexagon::C2_cmpeqi: |
| case Hexagon::C2_cmpgti: |
| case Hexagon::C2_cmpgtui: |
| case Hexagon::C2_cmpgei: |
| case Hexagon::C2_cmpgeui: |
| case Hexagon::C4_cmpneqi: |
| case Hexagon::C4_cmpltei: |
| case Hexagon::C4_cmplteui: |
| case Hexagon::C4_cmpneq: |
| case Hexagon::C4_cmplte: |
| case Hexagon::C4_cmplteu: |
| case Hexagon::A4_cmpbeq: |
| case Hexagon::A4_cmpbeqi: |
| case Hexagon::A4_cmpbgtu: |
| case Hexagon::A4_cmpbgtui: |
| case Hexagon::A4_cmpbgt: |
| case Hexagon::A4_cmpbgti: |
| case Hexagon::A4_cmpheq: |
| case Hexagon::A4_cmphgt: |
| case Hexagon::A4_cmphgtu: |
| case Hexagon::A4_cmpheqi: |
| case Hexagon::A4_cmphgti: |
| case Hexagon::A4_cmphgtui: |
| return true; |
| } |
| return false; |
| } |
| |
| bool HexagonGenPredicate::isScalarPred(RegisterSubReg PredReg) { |
| std::queue<RegisterSubReg> WorkQ; |
| WorkQ.push(PredReg); |
| |
| while (!WorkQ.empty()) { |
| RegisterSubReg PR = WorkQ.front(); |
| WorkQ.pop(); |
| const MachineInstr *DefI = MRI->getVRegDef(PR.R); |
| if (!DefI) |
| return false; |
| unsigned DefOpc = DefI->getOpcode(); |
| switch (DefOpc) { |
| case TargetOpcode::COPY: { |
| const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass; |
| if (MRI->getRegClass(PR.R) != PredRC) |
| return false; |
| // If it is a copy between two predicate registers, fall through. |
| LLVM_FALLTHROUGH; |
| } |
| case Hexagon::C2_and: |
| case Hexagon::C2_andn: |
| case Hexagon::C4_and_and: |
| case Hexagon::C4_and_andn: |
| case Hexagon::C4_and_or: |
| case Hexagon::C2_or: |
| case Hexagon::C2_orn: |
| case Hexagon::C4_or_and: |
| case Hexagon::C4_or_andn: |
| case Hexagon::C4_or_or: |
| case Hexagon::C4_or_orn: |
| case Hexagon::C2_xor: |
| // Add operands to the queue. |
| for (const MachineOperand &MO : DefI->operands()) |
| if (MO.isReg() && MO.isUse()) |
| WorkQ.push(RegisterSubReg(MO.getReg())); |
| break; |
| |
| // All non-vector compares are ok, everything else is bad. |
| default: |
| return isScalarCmp(DefOpc); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool HexagonGenPredicate::convertToPredForm(MachineInstr *MI) { |
| LLVM_DEBUG(dbgs() << __func__ << ": " << MI << " " << *MI); |
| |
| unsigned Opc = MI->getOpcode(); |
| assert(isConvertibleToPredForm(MI)); |
| unsigned NumOps = MI->getNumOperands(); |
| for (unsigned i = 0; i < NumOps; ++i) { |
| MachineOperand &MO = MI->getOperand(i); |
| if (!MO.isReg() || !MO.isUse()) |
| continue; |
| RegisterSubReg Reg(MO); |
| if (Reg.S && Reg.S != Hexagon::isub_lo) |
| return false; |
| if (!PredGPRs.count(Reg)) |
| return false; |
| } |
| |
| MachineBasicBlock &B = *MI->getParent(); |
| DebugLoc DL = MI->getDebugLoc(); |
| |
| unsigned NewOpc = getPredForm(Opc); |
| // Special case for comparisons against 0. |
| if (NewOpc == 0) { |
| switch (Opc) { |
| case Hexagon::C2_cmpeqi: |
| NewOpc = Hexagon::C2_not; |
| break; |
| case Hexagon::C4_cmpneqi: |
| NewOpc = TargetOpcode::COPY; |
| break; |
| default: |
| return false; |
| } |
| |
| // If it's a scalar predicate register, then all bits in it are |
| // the same. Otherwise, to determine whether all bits are 0 or not |
| // we would need to use any8. |
| RegisterSubReg PR = getPredRegFor(MI->getOperand(1)); |
| if (!isScalarPred(PR)) |
| return false; |
| // This will skip the immediate argument when creating the predicate |
| // version instruction. |
| NumOps = 2; |
| } |
| |
| // Some sanity: check that def is in operand #0. |
| MachineOperand &Op0 = MI->getOperand(0); |
| assert(Op0.isDef()); |
| RegisterSubReg OutR(Op0); |
| |
| // Don't use getPredRegFor, since it will create an association between |
| // the argument and a created predicate register (i.e. it will insert a |
| // copy if a new predicate register is created). |
| const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass; |
| RegisterSubReg NewPR = MRI->createVirtualRegister(PredRC); |
| MachineInstrBuilder MIB = BuildMI(B, MI, DL, TII->get(NewOpc), NewPR.R); |
| |
| // Add predicate counterparts of the GPRs. |
| for (unsigned i = 1; i < NumOps; ++i) { |
| RegisterSubReg GPR = MI->getOperand(i); |
| RegisterSubReg Pred = getPredRegFor(GPR); |
| MIB.addReg(Pred.R, 0, Pred.S); |
| } |
| LLVM_DEBUG(dbgs() << "generated: " << *MIB); |
| |
| // Generate a copy-out: NewGPR = NewPR, and replace all uses of OutR |
| // with NewGPR. |
| const TargetRegisterClass *RC = MRI->getRegClass(OutR.R); |
| Register NewOutR = MRI->createVirtualRegister(RC); |
| BuildMI(B, MI, DL, TII->get(TargetOpcode::COPY), NewOutR) |
| .addReg(NewPR.R, 0, NewPR.S); |
| MRI->replaceRegWith(OutR.R, NewOutR); |
| MI->eraseFromParent(); |
| |
| // If the processed instruction was C2_tfrrp (i.e. Rn = Pm; Pk = Rn), |
| // then the output will be a predicate register. Do not visit the |
| // users of it. |
| if (!isPredReg(NewOutR)) { |
| RegisterSubReg R(NewOutR); |
| PredGPRs.insert(R); |
| processPredicateGPR(R); |
| } |
| return true; |
| } |
| |
| bool HexagonGenPredicate::eliminatePredCopies(MachineFunction &MF) { |
| LLVM_DEBUG(dbgs() << __func__ << "\n"); |
| const TargetRegisterClass *PredRC = &Hexagon::PredRegsRegClass; |
| bool Changed = false; |
| VectOfInst Erase; |
| |
| // First, replace copies |
| // IntR = PredR1 |
| // PredR2 = IntR |
| // with |
| // PredR2 = PredR1 |
| // Such sequences can be generated when a copy-into-pred is generated from |
| // a gpr register holding a result of a convertible instruction. After |
| // the convertible instruction is converted, its predicate result will be |
| // copied back into the original gpr. |
| |
| for (MachineBasicBlock &MBB : MF) { |
| for (MachineInstr &MI : MBB) { |
| if (MI.getOpcode() != TargetOpcode::COPY) |
| continue; |
| RegisterSubReg DR = MI.getOperand(0); |
| RegisterSubReg SR = MI.getOperand(1); |
| if (!Register::isVirtualRegister(DR.R)) |
| continue; |
| if (!Register::isVirtualRegister(SR.R)) |
| continue; |
| if (MRI->getRegClass(DR.R) != PredRC) |
| continue; |
| if (MRI->getRegClass(SR.R) != PredRC) |
| continue; |
| assert(!DR.S && !SR.S && "Unexpected subregister"); |
| MRI->replaceRegWith(DR.R, SR.R); |
| Erase.insert(&MI); |
| Changed = true; |
| } |
| } |
| |
| for (VectOfInst::iterator I = Erase.begin(), E = Erase.end(); I != E; ++I) |
| (*I)->eraseFromParent(); |
| |
| return Changed; |
| } |
| |
| bool HexagonGenPredicate::runOnMachineFunction(MachineFunction &MF) { |
| if (skipFunction(MF.getFunction())) |
| return false; |
| |
| TII = MF.getSubtarget<HexagonSubtarget>().getInstrInfo(); |
| TRI = MF.getSubtarget<HexagonSubtarget>().getRegisterInfo(); |
| MRI = &MF.getRegInfo(); |
| PredGPRs.clear(); |
| PUsers.clear(); |
| G2P.clear(); |
| |
| bool Changed = false; |
| collectPredicateGPR(MF); |
| for (SetOfReg::iterator I = PredGPRs.begin(), E = PredGPRs.end(); I != E; ++I) |
| processPredicateGPR(*I); |
| |
| bool Again; |
| do { |
| Again = false; |
| VectOfInst Processed, Copy; |
| |
| using iterator = VectOfInst::iterator; |
| |
| Copy = PUsers; |
| for (iterator I = Copy.begin(), E = Copy.end(); I != E; ++I) { |
| MachineInstr *MI = *I; |
| bool Done = convertToPredForm(MI); |
| if (Done) { |
| Processed.insert(MI); |
| Again = true; |
| } |
| } |
| Changed |= Again; |
| |
| auto Done = [Processed] (MachineInstr *MI) -> bool { |
| return Processed.count(MI); |
| }; |
| PUsers.remove_if(Done); |
| } while (Again); |
| |
| Changed |= eliminatePredCopies(MF); |
| return Changed; |
| } |
| |
| FunctionPass *llvm::createHexagonGenPredicate() { |
| return new HexagonGenPredicate(); |
| } |