CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
373 lines
14 KiB
C++
373 lines
14 KiB
C++
//===--- 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;
|
|
}
|