blob: 08d63c56a9a730c367f5ef7b2ae15f177b511edf [file] [edit]
//===--- 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 <cassert>
#include <cstdint>
#include <utility>
using namespace llvm;
using namespace rdf;
#ifndef NDEBUG
extern cl::opt<unsigned> 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<StmtNode *> 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<DefNode *> 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<UseNode *>(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<InstrNode *> IA) {
for (NodeAddr<UseNode *> 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<InstrNode *> 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<RegisterRef, 4> 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<BlockNode *> BA = DFG.findBlock(B);
DFG.markBlock(BA.Id, DefM);
for (NodeAddr<InstrNode *> IA : BA.Addr->members(DFG)) {
if (DFG.IsCode<NodeAttrs::Stmt>(IA)) {
NodeAddr<StmtNode *> 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<StmtNode *>(C.first).Addr->getCode();
dbgs() << " eq: {";
for (auto J : C.second)
dbgs() << ' ' << Print<RegisterRef>(J.first, DFG) << '='
<< Print<RegisterRef>(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<UseNode *>(U.first).Addr->getOwner(DFG);
dbgs() << "Copy def: " << *DFG.addr<StmtNode *>(DefI.Id).Addr->getCode();
dbgs() << "Copy use: " << *DFG.addr<StmtNode *>(UseI.Id).Addr->getCode();
}
dbgs() << "\nRDef map:\n";
for (auto R : RDefMap) {
dbgs() << Print<RegisterRef>(R.first, DFG) << " -> {";
for (auto &M : R.second)
dbgs() << ' ' << Print<NodeId>(M.first, DFG) << ':'
<< Print<NodeId>(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<UseNode *> UA = P.first;
SmallVector<RegisterRef, 4> 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<RegisterRef>(DR, DFG) << " with "
<< Print<RegisterRef>(SR, DFG) << " in "
<< *NodeAddr<StmtNode *>(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<DefNode *>(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;
}