//===--- HexagonAggressiveRDFCopy.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 // //===----------------------------------------------------------------------===// // // RDF-based aggressive copy propagation. // // This optimization extends the standard RDF copy propagation with support for // super-register and sub-register copy propagation. It determines candidates // for copy propagation by verifying that both the copy instruction and all // reached uses have the same reaching definitions for the source register(s). // // Key differences: // 1. Super-register handling: Can propagate copies involving super-registers // and their sub-registers (e.g., double-register pairs on Hexagon). // 2. Sub-register coverage: Verifies that all sub-registers of the source // register have consistent reaching definitions before propagating. // 3. Combine instruction support: Handles A2_combinew instructions that // combine two 32-bit registers into a 64-bit register pair. // // Algorithm: // 1. Scan all basic blocks in dominator tree order, maintaining a stack of // reaching definitions for each register. // 2. For each copy instruction: // a. Record the copy and its source/destination register mapping. // b. Find the reaching definition for the source register at the copy. // c. For each use reached by the copy's destination register: // - Check if all sub-registers of the source have the same reaching // definition at both the copy and the use. // - If yes, mark the use as replaceable. // 3. Replace all marked uses with the source register of the copy. // // Example: // BB1: // R1 = ... // Def1 // D0 = A2_combinew R1, R0 // Copy: D0 = {R1, R0} // ... = D0 // Use of D0 // // If R1 and R0 have the same reaching definitions at both the copy and the // use, the use of D0 can be replaced with the original source registers. // D0 is super-register corresponding to R1:0. // //===----------------------------------------------------------------------===// #include "HexagonAggressiveRDFCopy.h" #include "llvm/CodeGen/MachineDominators.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/RDFGraph.h" #include "llvm/CodeGen/RDFLiveness.h" #include "llvm/CodeGen/RDFRegisters.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetOpcodes.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include #include using namespace llvm; using namespace rdf; #ifndef NDEBUG extern cl::opt RDFCpLimit; static unsigned RDFCpCount = 0; #endif // Record destination and source registers in EqualityMap // if this is a copy instruction bool AggressiveCopyPropagation::interpretAsCopy(const MachineInstr *MI, EqualityMap &EM) { unsigned Opc = MI->getOpcode(); switch (Opc) { case TargetOpcode::COPY: { const MachineOperand &Dst = MI->getOperand(0); const MachineOperand &Src = MI->getOperand(1); RegisterRef DstR = DFG.makeRegRef(Dst.getReg(), Dst.getSubReg()); RegisterRef SrcR = DFG.makeRegRef(Src.getReg(), Src.getSubReg()); assert(Register::isPhysicalRegister(DstR.Id)); assert(Register::isPhysicalRegister(SrcR.Id)); if (HRI.isFakeReg(DstR.Id) || HRI.isFakeReg(SrcR.Id)) return false; if (TRI.getMinimalPhysRegClass(DstR.Id) != TRI.getMinimalPhysRegClass(SrcR.Id)) return false; if (!DFG.isTracked(SrcR) || !DFG.isTracked(DstR)) return false; EM.insert(std::make_pair(DstR, SrcR)); return true; } case TargetOpcode::REG_SEQUENCE: llvm_unreachable("Unexpected REG_SEQUENCE"); } return false; } // Track instructions determined to be copies along with their uses. // The register, sub-register copy pairs are given by EqualityMap // Find the reaching def from DefM stack for source (LHS) registers in each // copy. ReachedUseToCopyMap stores each reached use (UseNode) of a copy along // with the copy DefNode and source register void AggressiveCopyPropagation::recordCopy(NodeAddr SA, EqualityMap &EM) { if (trace()) CopyMap.insert(std::make_pair(SA.Id, EM)); // Find and store reaching def for each source register // EqualityMap should also contain subregs for (auto I : EM) { if (PRI.equal_to(I.first, I.second)) continue; NodeId RDefId = 0; auto FS = DefM.find(I.second.Id); if (FS != DefM.end() && !FS->second.empty()) { auto Def = FS->second.top()->Addr->getRegRef(DFG); // Avoid adding subreg as reaching def for superreg if (Register::isPhysicalRegister(Def.Id) && TRI.isSuperRegister(Def.Id, I.second.Id)) continue; RDefId = FS->second.top()->Id; } RDefMap[I.second][SA.Id] = RDefId; } for (NodeAddr DA : SA.Addr->members_if(DFG.IsDef, DFG)) { RegisterRef DR = DA.Addr->getRegRef(DFG); auto FR = EM.find(DR); if (FR == EM.end()) continue; // Iterate over DR and its subregisters // if present in EqualityMap, find its reached uses for (MCPhysReg SubDReg : TRI.subregs_inclusive(DR.Id)) { auto SubDR = DFG.makeRegRef(SubDReg, 0); auto FR = EM.find(SubDR); if (FR == EM.end()) continue; RegisterRef SR = FR->second; // Redundant copy if (PRI.equal_to(SubDR, SR)) continue; for (NodeId N = DA.Addr->getReachedUse(), NextN; N; N = NextN) { auto UA = DFG.addr(N); NextN = UA.Addr->getSibling(); uint16_t F = UA.Addr->getFlags(); // Skip phi node uses // Skip shadow uses. When shadow nodes are present, the register has // multiple reaching defs. if ((F & NodeAttrs::PhiRef) || (F & NodeAttrs::Fixed) || (F & NodeAttrs::Shadow)) continue; if (!PRI.equal_to(UA.Addr->getRegRef(DFG), SubDR)) continue; MachineOperand &Op = UA.Addr->getOp(); // Skip operand if def and use of a register happens in same instruction if (Op.isTied()) continue; if (ReachedUseToCopyMap.find(UA.Id) != ReachedUseToCopyMap.end()) llvm_unreachable("Multiple copy instructions reach this use"); ReachedUseToCopyMap.insert( std::make_pair(UA.Id, std::make_pair(DA, SR))); } } } } // Now that we can obtain reaching def for uses from DefM, // check that reaching defs for source register and subregisters at the use // instruction, are the same as reaching defs for the copy. Uses that satisfy // this check can be replaced with the source registers of the copy. void AggressiveCopyPropagation::recordReplacableUses(NodeAddr IA) { for (NodeAddr UA : IA.Addr->members_if(DFG.IsUse, DFG)) { // Check if any uses of the instruction are reached by a copy auto CopyUseIt = ReachedUseToCopyMap.find(UA.Id); if (CopyUseIt == ReachedUseToCopyMap.end()) continue; [[maybe_unused]] auto UseReg = UA.Addr->getRegRef(DFG); auto DA = CopyUseIt->second.first; auto SR = CopyUseIt->second.second; [[maybe_unused]] auto DefReg = DA.Addr->getRegRef(DFG); assert(PRI.equal_to(DefReg, UseReg)); NodeAddr DefI = DA.Addr->getOwner(DFG); // Aggr of subregs that have same reaching def (at IA) as DefI RegisterAggr RRs(PRI); // Registers that need to be added as use nodes in updated IA SmallVector UseRefs; for (MCPhysReg S : TRI.subregs_inclusive(SR.Id)) { auto SRef = DFG.makeRegRef(S, 0); NodeId RDefId = 0; // If there is no reaching def for SRef at DefI, // do not check if SRef can be propagated auto RDefIt = RDefMap.find(SRef); if (RDefIt == RDefMap.end()) continue; auto DefIIt = RDefIt->second.find(DefI.Id); if (DefIIt == RDefIt->second.end()) continue; // If we already have reaching def for SRef at IA, // use it instead of searching DefM. auto IAIt = RDefIt->second.find(IA.Id); if (IAIt != RDefIt->second.end()) { RDefId = IAIt->second; } else { auto F = DefM.find(S); if (F != DefM.end() && !F->second.empty()) { auto Def = F->second.top()->Addr->getRegRef(DFG); // Avoid adding subreg as reaching def for superreg if (Register::isPhysicalRegister(Def.Id) && TRI.isSuperRegister(Def.Id, S)) continue; RDefId = F->second.top()->Id; } } // If reaching def for SRef at DefI is not same as IA, // SRef can not propagated to IA. if (DefIIt->second != RDefId) continue; RRs.insert(SRef); UseRefs.push_back(SRef); RDefIt->second[IA.Id] = RDefId; // If registers or sub-registers that can be propagated cover SR, // the use node is a candidate for copy propagation if (RRs.hasCoverOf(SR)) break; } // Use node can be replaced with new use nodes created from UseRefs if (RRs.hasCoverOf(SR)) ReplacableUses.push_back(std::make_pair(UA, UseRefs)); } } // Recursively process all children in the dominator tree. // Find copy instructions and reached uses that are candidates for propagation void AggressiveCopyPropagation::scanBlock(MachineBasicBlock *B) { NodeAddr BA = DFG.findBlock(B); DFG.markBlock(BA.Id, DefM); for (NodeAddr IA : BA.Addr->members(DFG)) { if (DFG.IsCode(IA)) { NodeAddr SA = IA; EqualityMap EM(RegisterRefLess(DFG.getPRI())); if (interpretAsCopy(SA.Addr->getCode(), EM)) recordCopy(SA, EM); recordReplacableUses(IA); } DFG.pushAllDefs(IA, DefM); } MachineDomTreeNode *N = MDT.getNode(B); for (auto *I : *N) scanBlock(I->getBlock()); DFG.releaseBlock(BA.Id, DefM); return; } bool AggressiveCopyPropagation::run() { scanBlock(MDT.getRootNode()->getBlock()); if (trace()) { dbgs() << "Copies:\n"; for (auto &C : CopyMap) { dbgs() << "Instr: " << *DFG.addr(C.first).Addr->getCode(); dbgs() << " eq: {"; for (auto J : C.second) dbgs() << ' ' << Print(J.first, DFG) << '=' << Print(J.second, DFG); dbgs() << " }\n"; } dbgs() << "\nCopy def-use:\n"; for (auto &U : ReachedUseToCopyMap) { auto DA = U.second.first; auto DefI = DA.Addr->getOwner(DFG); auto UseI = DFG.addr(U.first).Addr->getOwner(DFG); dbgs() << "Copy def: " << *DFG.addr(DefI.Id).Addr->getCode(); dbgs() << "Copy use: " << *DFG.addr(UseI.Id).Addr->getCode(); } dbgs() << "\nRDef map:\n"; for (auto R : RDefMap) { dbgs() << Print(R.first, DFG) << " -> {"; for (auto &M : R.second) dbgs() << ' ' << Print(M.first, DFG) << ':' << Print(M.second, DFG); dbgs() << " }\n"; } } bool Changed = false; #ifndef NDEBUG bool HasLimit = RDFCpLimit.getNumOccurrences() > 0; #endif auto MinPhysReg = [this](RegisterRef RR) -> unsigned { const TargetRegisterClass &RC = *TRI.getMinimalPhysRegClass(RR.Id); if ((RC.LaneMask & RR.Mask) == RC.LaneMask) return RR.Id; for (MCSubRegIndexIterator S(RR.Id, &TRI); S.isValid(); ++S) if (RR.Mask == TRI.getSubRegIndexLaneMask(S.getSubRegIndex())) return S.getSubReg(); llvm_unreachable("Should have found a register"); return 0; }; // Iterate over all candidate uses found and replace with source register of // copy for (auto P : ReplacableUses) { #ifndef NDEBUG if (HasLimit && RDFCpCount >= RDFCpLimit) break; #endif NodeAddr UA = P.first; SmallVector UseRefs = P.second; // UseRefs should never be empty if RRs.hasCoverOf(SR) was true assert(!UseRefs.empty() && "UseRefs should not be empty for replaceable use"); auto IA = UA.Addr->getOwner(DFG); auto DR = UA.Addr->getRegRef(DFG); auto SR = ReachedUseToCopyMap[UA.Id].second; if (HRI.isFakeReg(SR.Id)) continue; if (trace()) { dbgs() << "Can replace " << Print(DR, DFG) << " with " << Print(SR, DFG) << " in " << *NodeAddr(IA).Addr->getCode(); } // Update existing use node to use the source register MachineOperand &Op = UA.Addr->getOp(); unsigned NewReg = MinPhysReg(SR); Op.setReg(NewReg); Op.setSubReg(0); DFG.unlinkUse(UA, false); bool firstUseNode = true; for (auto UR : UseRefs) { // If we have more than one use (such as multiple subregs), // add a new shadow use node if (!firstUseNode) { UA.Addr->setFlags(UA.Addr->getFlags() | NodeAttrs::Shadow); UA = DFG.getNextShadow(IA, UA, true); } if (RDefMap[UR][IA.Id] != 0) { UA.Addr->linkToDef(UA.Id, DFG.addr(RDefMap[UR][IA.Id])); } else { // No reaching def present UA.Addr->setReachingDef(0); UA.Addr->setSibling(0); } firstUseNode = false; } Changed = true; #ifndef NDEBUG if (HasLimit && RDFCpCount >= RDFCpLimit) break; RDFCpCount++; #endif } // for (UA in replacable uses) return Changed; }