
When assigning a bundle to a DAG Node that is already assigned to a SchedBundle we need to remove the node from the old bundle.
666 lines
23 KiB
C++
666 lines
23 KiB
C++
//===- DependencyGraph.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 "llvm/Transforms/Vectorize/SandboxVectorizer/DependencyGraph.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/SandboxIR/Instruction.h"
|
|
#include "llvm/SandboxIR/Utils.h"
|
|
#include "llvm/Transforms/Vectorize/SandboxVectorizer/Scheduler.h"
|
|
|
|
namespace llvm::sandboxir {
|
|
|
|
User::op_iterator PredIterator::skipBadIt(User::op_iterator OpIt,
|
|
User::op_iterator OpItE,
|
|
const DependencyGraph &DAG) {
|
|
auto Skip = [&DAG](auto OpIt) {
|
|
auto *I = dyn_cast<Instruction>((*OpIt).get());
|
|
return I == nullptr || DAG.getNode(I) == nullptr;
|
|
};
|
|
while (OpIt != OpItE && Skip(OpIt))
|
|
++OpIt;
|
|
return OpIt;
|
|
}
|
|
|
|
PredIterator::value_type PredIterator::operator*() {
|
|
// If it's a DGNode then we dereference the operand iterator.
|
|
if (!isa<MemDGNode>(N)) {
|
|
assert(OpIt != OpItE && "Can't dereference end iterator!");
|
|
return DAG->getNode(cast<Instruction>((Value *)*OpIt));
|
|
}
|
|
// It's a MemDGNode, so we check if we return either the use-def operand,
|
|
// or a mem predecessor.
|
|
if (OpIt != OpItE)
|
|
return DAG->getNode(cast<Instruction>((Value *)*OpIt));
|
|
// It's a MemDGNode with OpIt == end, so we need to use MemIt.
|
|
assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() &&
|
|
"Cant' dereference end iterator!");
|
|
return *MemIt;
|
|
}
|
|
|
|
PredIterator &PredIterator::operator++() {
|
|
// If it's a DGNode then we increment the use-def iterator.
|
|
if (!isa<MemDGNode>(N)) {
|
|
assert(OpIt != OpItE && "Already at end!");
|
|
++OpIt;
|
|
// Skip operands that are not instructions or are outside the DAG.
|
|
OpIt = PredIterator::skipBadIt(OpIt, OpItE, *DAG);
|
|
return *this;
|
|
}
|
|
// It's a MemDGNode, so if we are not at the end of the use-def iterator we
|
|
// need to first increment that.
|
|
if (OpIt != OpItE) {
|
|
++OpIt;
|
|
// Skip operands that are not instructions or are outside the DAG.
|
|
OpIt = PredIterator::skipBadIt(OpIt, OpItE, *DAG);
|
|
return *this;
|
|
}
|
|
// It's a MemDGNode with OpIt == end, so we need to increment MemIt.
|
|
assert(MemIt != cast<MemDGNode>(N)->MemPreds.end() && "Already at end!");
|
|
++MemIt;
|
|
return *this;
|
|
}
|
|
|
|
bool PredIterator::operator==(const PredIterator &Other) const {
|
|
assert(DAG == Other.DAG && "Iterators of different DAGs!");
|
|
assert(N == Other.N && "Iterators of different nodes!");
|
|
return OpIt == Other.OpIt && MemIt == Other.MemIt;
|
|
}
|
|
|
|
void DGNode::setSchedBundle(SchedBundle &SB) {
|
|
if (this->SB != nullptr)
|
|
this->SB->eraseFromBundle(this);
|
|
this->SB = &SB;
|
|
}
|
|
|
|
DGNode::~DGNode() {
|
|
if (SB == nullptr)
|
|
return;
|
|
SB->eraseFromBundle(this);
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
void DGNode::print(raw_ostream &OS, bool PrintDeps) const {
|
|
OS << *I << " USuccs:" << UnscheduledSuccs << " Sched:" << Scheduled << "\n";
|
|
}
|
|
void DGNode::dump() const { print(dbgs()); }
|
|
void MemDGNode::print(raw_ostream &OS, bool PrintDeps) const {
|
|
DGNode::print(OS, false);
|
|
if (PrintDeps) {
|
|
// Print memory preds.
|
|
static constexpr const unsigned Indent = 4;
|
|
for (auto *Pred : MemPreds)
|
|
OS.indent(Indent) << "<-" << *Pred->getInstruction() << "\n";
|
|
}
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
MemDGNode *
|
|
MemDGNodeIntervalBuilder::getTopMemDGNode(const Interval<Instruction> &Intvl,
|
|
const DependencyGraph &DAG) {
|
|
Instruction *I = Intvl.top();
|
|
Instruction *BeforeI = Intvl.bottom();
|
|
// Walk down the chain looking for a mem-dep candidate instruction.
|
|
while (!DGNode::isMemDepNodeCandidate(I) && I != BeforeI)
|
|
I = I->getNextNode();
|
|
if (!DGNode::isMemDepNodeCandidate(I))
|
|
return nullptr;
|
|
return cast<MemDGNode>(DAG.getNode(I));
|
|
}
|
|
|
|
MemDGNode *
|
|
MemDGNodeIntervalBuilder::getBotMemDGNode(const Interval<Instruction> &Intvl,
|
|
const DependencyGraph &DAG) {
|
|
Instruction *I = Intvl.bottom();
|
|
Instruction *AfterI = Intvl.top();
|
|
// Walk up the chain looking for a mem-dep candidate instruction.
|
|
while (!DGNode::isMemDepNodeCandidate(I) && I != AfterI)
|
|
I = I->getPrevNode();
|
|
if (!DGNode::isMemDepNodeCandidate(I))
|
|
return nullptr;
|
|
return cast<MemDGNode>(DAG.getNode(I));
|
|
}
|
|
|
|
Interval<MemDGNode>
|
|
MemDGNodeIntervalBuilder::make(const Interval<Instruction> &Instrs,
|
|
DependencyGraph &DAG) {
|
|
if (Instrs.empty())
|
|
return {};
|
|
auto *TopMemN = getTopMemDGNode(Instrs, DAG);
|
|
// If we couldn't find a mem node in range TopN - BotN then it's empty.
|
|
if (TopMemN == nullptr)
|
|
return {};
|
|
auto *BotMemN = getBotMemDGNode(Instrs, DAG);
|
|
assert(BotMemN != nullptr && "TopMemN should be null too!");
|
|
// Now that we have the mem-dep nodes, create and return the range.
|
|
return Interval<MemDGNode>(TopMemN, BotMemN);
|
|
}
|
|
|
|
DependencyGraph::DependencyType
|
|
DependencyGraph::getRoughDepType(Instruction *FromI, Instruction *ToI) {
|
|
// TODO: Perhaps compile-time improvement by skipping if neither is mem?
|
|
if (FromI->mayWriteToMemory()) {
|
|
if (ToI->mayReadFromMemory())
|
|
return DependencyType::ReadAfterWrite;
|
|
if (ToI->mayWriteToMemory())
|
|
return DependencyType::WriteAfterWrite;
|
|
} else if (FromI->mayReadFromMemory()) {
|
|
if (ToI->mayWriteToMemory())
|
|
return DependencyType::WriteAfterRead;
|
|
}
|
|
if (isa<sandboxir::PHINode>(FromI) || isa<sandboxir::PHINode>(ToI))
|
|
return DependencyType::Control;
|
|
if (ToI->isTerminator())
|
|
return DependencyType::Control;
|
|
if (DGNode::isStackSaveOrRestoreIntrinsic(FromI) ||
|
|
DGNode::isStackSaveOrRestoreIntrinsic(ToI))
|
|
return DependencyType::Other;
|
|
return DependencyType::None;
|
|
}
|
|
|
|
static bool isOrdered(Instruction *I) {
|
|
auto IsOrdered = [](Instruction *I) {
|
|
if (auto *LI = dyn_cast<LoadInst>(I))
|
|
return !LI->isUnordered();
|
|
if (auto *SI = dyn_cast<StoreInst>(I))
|
|
return !SI->isUnordered();
|
|
if (DGNode::isFenceLike(I))
|
|
return true;
|
|
return false;
|
|
};
|
|
bool Is = IsOrdered(I);
|
|
assert((!Is || DGNode::isMemDepCandidate(I)) &&
|
|
"An ordered instruction must be a MemDepCandidate!");
|
|
return Is;
|
|
}
|
|
|
|
bool DependencyGraph::alias(Instruction *SrcI, Instruction *DstI,
|
|
DependencyType DepType) {
|
|
std::optional<MemoryLocation> DstLocOpt =
|
|
Utils::memoryLocationGetOrNone(DstI);
|
|
if (!DstLocOpt)
|
|
return true;
|
|
// Check aliasing.
|
|
assert((SrcI->mayReadFromMemory() || SrcI->mayWriteToMemory()) &&
|
|
"Expected a mem instr");
|
|
// TODO: Check AABudget
|
|
ModRefInfo SrcModRef =
|
|
isOrdered(SrcI)
|
|
? ModRefInfo::ModRef
|
|
: Utils::aliasAnalysisGetModRefInfo(*BatchAA, SrcI, *DstLocOpt);
|
|
switch (DepType) {
|
|
case DependencyType::ReadAfterWrite:
|
|
case DependencyType::WriteAfterWrite:
|
|
return isModSet(SrcModRef);
|
|
case DependencyType::WriteAfterRead:
|
|
return isRefSet(SrcModRef);
|
|
default:
|
|
llvm_unreachable("Expected only RAW, WAW and WAR!");
|
|
}
|
|
}
|
|
|
|
bool DependencyGraph::hasDep(Instruction *SrcI, Instruction *DstI) {
|
|
DependencyType RoughDepType = getRoughDepType(SrcI, DstI);
|
|
switch (RoughDepType) {
|
|
case DependencyType::ReadAfterWrite:
|
|
case DependencyType::WriteAfterWrite:
|
|
case DependencyType::WriteAfterRead:
|
|
return alias(SrcI, DstI, RoughDepType);
|
|
case DependencyType::Control:
|
|
// Adding actual dep edges from PHIs/to terminator would just create too
|
|
// many edges, which would be bad for compile-time.
|
|
// So we ignore them in the DAG formation but handle them in the
|
|
// scheduler, while sorting the ready list.
|
|
return false;
|
|
case DependencyType::Other:
|
|
return true;
|
|
case DependencyType::None:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Unknown DependencyType enum");
|
|
}
|
|
|
|
void DependencyGraph::scanAndAddDeps(MemDGNode &DstN,
|
|
const Interval<MemDGNode> &SrcScanRange) {
|
|
assert(isa<MemDGNode>(DstN) &&
|
|
"DstN is the mem dep destination, so it must be mem");
|
|
Instruction *DstI = DstN.getInstruction();
|
|
// Walk up the instruction chain from ScanRange bottom to top, looking for
|
|
// memory instrs that may alias.
|
|
for (MemDGNode &SrcN : reverse(SrcScanRange)) {
|
|
Instruction *SrcI = SrcN.getInstruction();
|
|
if (hasDep(SrcI, DstI))
|
|
DstN.addMemPred(&SrcN);
|
|
}
|
|
}
|
|
|
|
void DependencyGraph::setDefUseUnscheduledSuccs(
|
|
const Interval<Instruction> &NewInterval) {
|
|
// +---+
|
|
// | | Def
|
|
// | | |
|
|
// | | v
|
|
// | | Use
|
|
// +---+
|
|
// Set the intra-interval counters in NewInterval.
|
|
for (Instruction &I : NewInterval) {
|
|
for (Value *Op : I.operands()) {
|
|
auto *OpI = dyn_cast<Instruction>(Op);
|
|
if (OpI == nullptr)
|
|
continue;
|
|
// TODO: For now don't cross BBs.
|
|
if (OpI->getParent() != I.getParent())
|
|
continue;
|
|
if (!NewInterval.contains(OpI))
|
|
continue;
|
|
auto *OpN = getNode(OpI);
|
|
if (OpN == nullptr)
|
|
continue;
|
|
++OpN->UnscheduledSuccs;
|
|
}
|
|
}
|
|
|
|
// Now handle the cross-interval edges.
|
|
bool NewIsAbove = DAGInterval.empty() || NewInterval.comesBefore(DAGInterval);
|
|
const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
|
|
const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
|
|
// +---+
|
|
// |Top|
|
|
// | | Def
|
|
// +---+ |
|
|
// | | v
|
|
// |Bot| Use
|
|
// | |
|
|
// +---+
|
|
// Walk over all instructions in "BotInterval" and update the counter
|
|
// of operands that are in "TopInterval".
|
|
for (Instruction &BotI : BotInterval) {
|
|
auto *BotN = getNode(&BotI);
|
|
// Skip scheduled nodes.
|
|
if (BotN->scheduled())
|
|
continue;
|
|
for (Value *Op : BotI.operands()) {
|
|
auto *OpI = dyn_cast<Instruction>(Op);
|
|
if (OpI == nullptr)
|
|
continue;
|
|
auto *OpN = getNode(OpI);
|
|
if (OpN == nullptr)
|
|
continue;
|
|
if (!TopInterval.contains(OpI))
|
|
continue;
|
|
++OpN->UnscheduledSuccs;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DependencyGraph::createNewNodes(const Interval<Instruction> &NewInterval) {
|
|
// Create Nodes only for the new sections of the DAG.
|
|
DGNode *LastN = getOrCreateNode(NewInterval.top());
|
|
MemDGNode *LastMemN = dyn_cast<MemDGNode>(LastN);
|
|
for (Instruction &I : drop_begin(NewInterval)) {
|
|
auto *N = getOrCreateNode(&I);
|
|
// Build the Mem node chain.
|
|
if (auto *MemN = dyn_cast<MemDGNode>(N)) {
|
|
MemN->setPrevNode(LastMemN);
|
|
LastMemN = MemN;
|
|
}
|
|
}
|
|
// Link new MemDGNode chain with the old one, if any.
|
|
if (!DAGInterval.empty()) {
|
|
bool NewIsAbove = NewInterval.comesBefore(DAGInterval);
|
|
const auto &TopInterval = NewIsAbove ? NewInterval : DAGInterval;
|
|
const auto &BotInterval = NewIsAbove ? DAGInterval : NewInterval;
|
|
MemDGNode *LinkTopN =
|
|
MemDGNodeIntervalBuilder::getBotMemDGNode(TopInterval, *this);
|
|
MemDGNode *LinkBotN =
|
|
MemDGNodeIntervalBuilder::getTopMemDGNode(BotInterval, *this);
|
|
assert((LinkTopN == nullptr || LinkBotN == nullptr ||
|
|
LinkTopN->comesBefore(LinkBotN)) &&
|
|
"Wrong order!");
|
|
if (LinkTopN != nullptr && LinkBotN != nullptr) {
|
|
LinkTopN->setNextNode(LinkBotN);
|
|
}
|
|
#ifndef NDEBUG
|
|
// TODO: Remove this once we've done enough testing.
|
|
// Check that the chain is well formed.
|
|
auto UnionIntvl = DAGInterval.getUnionInterval(NewInterval);
|
|
MemDGNode *ChainTopN =
|
|
MemDGNodeIntervalBuilder::getTopMemDGNode(UnionIntvl, *this);
|
|
MemDGNode *ChainBotN =
|
|
MemDGNodeIntervalBuilder::getBotMemDGNode(UnionIntvl, *this);
|
|
if (ChainTopN != nullptr && ChainBotN != nullptr) {
|
|
for (auto *N = ChainTopN->getNextNode(), *LastN = ChainTopN; N != nullptr;
|
|
LastN = N, N = N->getNextNode()) {
|
|
assert(N == LastN->getNextNode() && "Bad chain!");
|
|
assert(N->getPrevNode() == LastN && "Bad chain!");
|
|
}
|
|
}
|
|
#endif // NDEBUG
|
|
}
|
|
|
|
setDefUseUnscheduledSuccs(NewInterval);
|
|
}
|
|
|
|
MemDGNode *DependencyGraph::getMemDGNodeBefore(DGNode *N, bool IncludingN,
|
|
MemDGNode *SkipN) const {
|
|
auto *I = N->getInstruction();
|
|
for (auto *PrevI = IncludingN ? I : I->getPrevNode(); PrevI != nullptr;
|
|
PrevI = PrevI->getPrevNode()) {
|
|
auto *PrevN = getNodeOrNull(PrevI);
|
|
if (PrevN == nullptr)
|
|
return nullptr;
|
|
auto *PrevMemN = dyn_cast<MemDGNode>(PrevN);
|
|
if (PrevMemN != nullptr && PrevMemN != SkipN)
|
|
return PrevMemN;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
MemDGNode *DependencyGraph::getMemDGNodeAfter(DGNode *N, bool IncludingN,
|
|
MemDGNode *SkipN) const {
|
|
auto *I = N->getInstruction();
|
|
for (auto *NextI = IncludingN ? I : I->getNextNode(); NextI != nullptr;
|
|
NextI = NextI->getNextNode()) {
|
|
auto *NextN = getNodeOrNull(NextI);
|
|
if (NextN == nullptr)
|
|
return nullptr;
|
|
auto *NextMemN = dyn_cast<MemDGNode>(NextN);
|
|
if (NextMemN != nullptr && NextMemN != SkipN)
|
|
return NextMemN;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void DependencyGraph::notifyCreateInstr(Instruction *I) {
|
|
if (Ctx->getTracker().getState() == Tracker::TrackerState::Reverting)
|
|
// We don't maintain the DAG while reverting.
|
|
return;
|
|
// Nothing to do if the node is not in the focus range of the DAG.
|
|
if (!(DAGInterval.contains(I) || DAGInterval.touches(I)))
|
|
return;
|
|
// Include `I` into the interval.
|
|
DAGInterval = DAGInterval.getUnionInterval({I, I});
|
|
auto *N = getOrCreateNode(I);
|
|
auto *MemN = dyn_cast<MemDGNode>(N);
|
|
|
|
// Update the MemDGNode chain if this is a memory node.
|
|
if (MemN != nullptr) {
|
|
if (auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false)) {
|
|
PrevMemN->NextMemN = MemN;
|
|
MemN->PrevMemN = PrevMemN;
|
|
}
|
|
if (auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false)) {
|
|
NextMemN->PrevMemN = MemN;
|
|
MemN->NextMemN = NextMemN;
|
|
}
|
|
|
|
// Add Mem dependencies.
|
|
// 1. Scan for deps above `I` for deps to `I`: AboveN->MemN.
|
|
if (DAGInterval.top()->comesBefore(I)) {
|
|
Interval<Instruction> AboveIntvl(DAGInterval.top(), I->getPrevNode());
|
|
auto SrcInterval = MemDGNodeIntervalBuilder::make(AboveIntvl, *this);
|
|
scanAndAddDeps(*MemN, SrcInterval);
|
|
}
|
|
// 2. Scan for deps below `I` for deps from `I`: MemN->BelowN.
|
|
if (I->comesBefore(DAGInterval.bottom())) {
|
|
Interval<Instruction> BelowIntvl(I->getNextNode(), DAGInterval.bottom());
|
|
for (MemDGNode &BelowN :
|
|
MemDGNodeIntervalBuilder::make(BelowIntvl, *this))
|
|
scanAndAddDeps(BelowN, Interval<MemDGNode>(MemN, MemN));
|
|
}
|
|
}
|
|
}
|
|
|
|
void DependencyGraph::notifyMoveInstr(Instruction *I, const BBIterator &To) {
|
|
if (Ctx->getTracker().getState() == Tracker::TrackerState::Reverting)
|
|
// We don't maintain the DAG while reverting.
|
|
return;
|
|
// NOTE: This function runs before `I` moves to its new destination.
|
|
BasicBlock *BB = To.getNodeParent();
|
|
assert(!(To != BB->end() && &*To == I->getNextNode()) &&
|
|
!(To == BB->end() && std::next(I->getIterator()) == BB->end()) &&
|
|
"Should not have been called if destination is same as origin.");
|
|
|
|
// TODO: We can only handle fully internal movements within DAGInterval or at
|
|
// the borders, i.e., right before the top or right after the bottom.
|
|
assert(To.getNodeParent() == I->getParent() &&
|
|
"TODO: We don't support movement across BBs!");
|
|
assert(
|
|
(To == std::next(DAGInterval.bottom()->getIterator()) ||
|
|
(To != BB->end() && std::next(To) == DAGInterval.top()->getIterator()) ||
|
|
(To != BB->end() && DAGInterval.contains(&*To))) &&
|
|
"TODO: To should be either within the DAGInterval or right "
|
|
"before/after it.");
|
|
|
|
// Make a copy of the DAGInterval before we update it.
|
|
auto OrigDAGInterval = DAGInterval;
|
|
|
|
// Maintain the DAGInterval.
|
|
DAGInterval.notifyMoveInstr(I, To);
|
|
|
|
// TODO: Perhaps check if this is legal by checking the dependencies?
|
|
|
|
// Update the MemDGNode chain to reflect the instr movement if necessary.
|
|
DGNode *N = getNodeOrNull(I);
|
|
if (N == nullptr)
|
|
return;
|
|
MemDGNode *MemN = dyn_cast<MemDGNode>(N);
|
|
if (MemN == nullptr)
|
|
return;
|
|
|
|
// First safely detach it from the existing chain.
|
|
MemN->detachFromChain();
|
|
|
|
// Now insert it back into the chain at the new location.
|
|
//
|
|
// We won't always have a DGNode to insert before it. If `To` is BB->end() or
|
|
// if it points to an instr after DAGInterval.bottom() then we will have to
|
|
// find a node to insert *after*.
|
|
//
|
|
// BB: BB:
|
|
// I1 I1 ^
|
|
// I2 I2 | DAGInteval [I1 to I3]
|
|
// I3 I3 V
|
|
// I4 I4 <- `To` == right after DAGInterval
|
|
// <- `To` == BB->end()
|
|
//
|
|
if (To == BB->end() ||
|
|
To == std::next(OrigDAGInterval.bottom()->getIterator())) {
|
|
// If we don't have a node to insert before, find a node to insert after and
|
|
// update the chain.
|
|
DGNode *InsertAfterN = getNode(&*std::prev(To));
|
|
MemN->setPrevNode(
|
|
getMemDGNodeBefore(InsertAfterN, /*IncludingN=*/true, /*SkipN=*/MemN));
|
|
} else {
|
|
// We have a node to insert before, so update the chain.
|
|
DGNode *BeforeToN = getNode(&*To);
|
|
MemN->setPrevNode(
|
|
getMemDGNodeBefore(BeforeToN, /*IncludingN=*/false, /*SkipN=*/MemN));
|
|
MemN->setNextNode(
|
|
getMemDGNodeAfter(BeforeToN, /*IncludingN=*/true, /*SkipN=*/MemN));
|
|
}
|
|
}
|
|
|
|
void DependencyGraph::notifyEraseInstr(Instruction *I) {
|
|
if (Ctx->getTracker().getState() == Tracker::TrackerState::Reverting)
|
|
// We don't maintain the DAG while reverting.
|
|
return;
|
|
auto *N = getNode(I);
|
|
if (N == nullptr)
|
|
// Early return if there is no DAG node for `I`.
|
|
return;
|
|
if (auto *MemN = dyn_cast<MemDGNode>(getNode(I))) {
|
|
// Update the MemDGNode chain if this is a memory node.
|
|
auto *PrevMemN = getMemDGNodeBefore(MemN, /*IncludingN=*/false);
|
|
auto *NextMemN = getMemDGNodeAfter(MemN, /*IncludingN=*/false);
|
|
if (PrevMemN != nullptr)
|
|
PrevMemN->NextMemN = NextMemN;
|
|
if (NextMemN != nullptr)
|
|
NextMemN->PrevMemN = PrevMemN;
|
|
|
|
// Drop the memory dependencies from both predecessors and successors.
|
|
while (!MemN->memPreds().empty()) {
|
|
auto *PredN = *MemN->memPreds().begin();
|
|
MemN->removeMemPred(PredN);
|
|
}
|
|
while (!MemN->memSuccs().empty()) {
|
|
auto *SuccN = *MemN->memSuccs().begin();
|
|
SuccN->removeMemPred(MemN);
|
|
}
|
|
// NOTE: The unscheduled succs for MemNodes get updated be setMemPred().
|
|
} else {
|
|
// If this is a non-mem node we only need to update UnscheduledSuccs.
|
|
if (!N->scheduled())
|
|
for (auto *PredN : N->preds(*this))
|
|
PredN->decrUnscheduledSuccs();
|
|
}
|
|
// Finally erase the Node.
|
|
InstrToNodeMap.erase(I);
|
|
}
|
|
|
|
void DependencyGraph::notifySetUse(const Use &U, Value *NewSrc) {
|
|
// Update the UnscheduledSuccs counter for both the current source and NewSrc
|
|
// if needed.
|
|
if (auto *CurrSrcI = dyn_cast<Instruction>(U.get())) {
|
|
if (auto *CurrSrcN = getNode(CurrSrcI)) {
|
|
CurrSrcN->decrUnscheduledSuccs();
|
|
}
|
|
}
|
|
if (auto *NewSrcI = dyn_cast<Instruction>(NewSrc)) {
|
|
if (auto *NewSrcN = getNode(NewSrcI)) {
|
|
++NewSrcN->UnscheduledSuccs;
|
|
}
|
|
}
|
|
}
|
|
|
|
Interval<Instruction> DependencyGraph::extend(ArrayRef<Instruction *> Instrs) {
|
|
if (Instrs.empty())
|
|
return {};
|
|
|
|
Interval<Instruction> InstrsInterval(Instrs);
|
|
Interval<Instruction> Union = DAGInterval.getUnionInterval(InstrsInterval);
|
|
auto NewInterval = Union.getSingleDiff(DAGInterval);
|
|
if (NewInterval.empty())
|
|
return {};
|
|
|
|
createNewNodes(NewInterval);
|
|
|
|
// Create the dependencies.
|
|
//
|
|
// 1. This is a new DAG, DAGInterval is empty. Fully scan the whole interval.
|
|
// +---+ - -
|
|
// | | SrcN | |
|
|
// | | | | SrcRange |
|
|
// |New| v | | DstRange
|
|
// | | DstN - |
|
|
// | | |
|
|
// +---+ -
|
|
// We are scanning for deps with destination in NewInterval and sources in
|
|
// NewInterval until DstN, for each DstN.
|
|
auto FullScan = [this](const Interval<Instruction> Intvl) {
|
|
auto DstRange = MemDGNodeIntervalBuilder::make(Intvl, *this);
|
|
if (!DstRange.empty()) {
|
|
for (MemDGNode &DstN : drop_begin(DstRange)) {
|
|
auto SrcRange = Interval<MemDGNode>(DstRange.top(), DstN.getPrevNode());
|
|
scanAndAddDeps(DstN, SrcRange);
|
|
}
|
|
}
|
|
};
|
|
auto MemDAGInterval = MemDGNodeIntervalBuilder::make(DAGInterval, *this);
|
|
if (MemDAGInterval.empty()) {
|
|
FullScan(NewInterval);
|
|
}
|
|
// 2. The new section is below the old section.
|
|
// +---+ -
|
|
// | | |
|
|
// |Old| SrcN |
|
|
// | | | |
|
|
// +---+ | | SrcRange
|
|
// +---+ | | -
|
|
// | | | | |
|
|
// |New| v | | DstRange
|
|
// | | DstN - |
|
|
// | | |
|
|
// +---+ -
|
|
// We are scanning for deps with destination in NewInterval because the deps
|
|
// in DAGInterval have already been computed. We consider sources in the whole
|
|
// range including both NewInterval and DAGInterval until DstN, for each DstN.
|
|
else if (DAGInterval.bottom()->comesBefore(NewInterval.top())) {
|
|
auto DstRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
|
|
auto SrcRangeFull = MemDAGInterval.getUnionInterval(DstRange);
|
|
for (MemDGNode &DstN : DstRange) {
|
|
auto SrcRange =
|
|
Interval<MemDGNode>(SrcRangeFull.top(), DstN.getPrevNode());
|
|
scanAndAddDeps(DstN, SrcRange);
|
|
}
|
|
}
|
|
// 3. The new section is above the old section.
|
|
else if (NewInterval.bottom()->comesBefore(DAGInterval.top())) {
|
|
// +---+ - -
|
|
// | | SrcN | |
|
|
// |New| | | SrcRange | DstRange
|
|
// | | v | |
|
|
// | | DstN - |
|
|
// | | |
|
|
// +---+ -
|
|
// +---+
|
|
// |Old|
|
|
// | |
|
|
// +---+
|
|
// When scanning for deps with destination in NewInterval we need to fully
|
|
// scan the interval. This is the same as the scanning for a new DAG.
|
|
FullScan(NewInterval);
|
|
|
|
// +---+ -
|
|
// | | |
|
|
// |New| SrcN | SrcRange
|
|
// | | | |
|
|
// | | | |
|
|
// | | | |
|
|
// +---+ | -
|
|
// +---+ | -
|
|
// |Old| v | DstRange
|
|
// | | DstN |
|
|
// +---+ -
|
|
// When scanning for deps with destination in DAGInterval we need to
|
|
// consider sources from the NewInterval only, because all intra-DAGInterval
|
|
// dependencies have already been created.
|
|
auto DstRangeOld = MemDAGInterval;
|
|
auto SrcRange = MemDGNodeIntervalBuilder::make(NewInterval, *this);
|
|
for (MemDGNode &DstN : DstRangeOld)
|
|
scanAndAddDeps(DstN, SrcRange);
|
|
} else {
|
|
llvm_unreachable("We don't expect extending in both directions!");
|
|
}
|
|
|
|
DAGInterval = Union;
|
|
return NewInterval;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
void DependencyGraph::print(raw_ostream &OS) const {
|
|
// InstrToNodeMap is unordered so we need to create an ordered vector.
|
|
SmallVector<DGNode *> Nodes;
|
|
Nodes.reserve(InstrToNodeMap.size());
|
|
for (const auto &Pair : InstrToNodeMap)
|
|
Nodes.push_back(Pair.second.get());
|
|
// Sort them based on which one comes first in the BB.
|
|
sort(Nodes, [](DGNode *N1, DGNode *N2) {
|
|
return N1->getInstruction()->comesBefore(N2->getInstruction());
|
|
});
|
|
for (auto *N : Nodes)
|
|
N->print(OS, /*PrintDeps=*/true);
|
|
}
|
|
|
|
void DependencyGraph::dump() const {
|
|
print(dbgs());
|
|
dbgs() << "\n";
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
} // namespace llvm::sandboxir
|