cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
323 lines
11 KiB
C++
323 lines
11 KiB
C++
//===- DDG.cpp - Data Dependence Graph -------------------------------------==//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// The implementation for the data dependence graph.
|
|
//===----------------------------------------------------------------------===//
|
|
#include "llvm/Analysis/DDG.h"
|
|
#include "llvm/ADT/SCCIterator.h"
|
|
#include "llvm/Analysis/LoopInfo.h"
|
|
#include "llvm/Analysis/LoopIterator.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static cl::opt<bool> SimplifyDDG(
|
|
"ddg-simplify", cl::init(true), cl::Hidden,
|
|
cl::desc(
|
|
"Simplify DDG by merging nodes that have less interesting edges."));
|
|
|
|
static cl::opt<bool> CreatePiBlocks("ddg-pi-blocks", cl::init(true), cl::Hidden,
|
|
cl::desc("Create pi-block nodes."));
|
|
|
|
#define DEBUG_TYPE "ddg"
|
|
|
|
template class llvm::DGEdge<DDGNode, DDGEdge>;
|
|
template class llvm::DGNode<DDGNode, DDGEdge>;
|
|
template class llvm::DirectedGraph<DDGNode, DDGEdge>;
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// DDGNode implementation
|
|
//===--------------------------------------------------------------------===//
|
|
DDGNode::~DDGNode() = default;
|
|
|
|
bool DDGNode::collectInstructions(
|
|
llvm::function_ref<bool(Instruction *)> const &Pred,
|
|
InstructionListType &IList) const {
|
|
assert(IList.empty() && "Expected the IList to be empty on entry.");
|
|
if (isa<SimpleDDGNode>(this)) {
|
|
for (Instruction *I : cast<const SimpleDDGNode>(this)->getInstructions())
|
|
if (Pred(I))
|
|
IList.push_back(I);
|
|
} else if (isa<PiBlockDDGNode>(this)) {
|
|
for (const DDGNode *PN : cast<const PiBlockDDGNode>(this)->getNodes()) {
|
|
assert(!isa<PiBlockDDGNode>(PN) && "Nested PiBlocks are not supported.");
|
|
SmallVector<Instruction *, 8> TmpIList;
|
|
PN->collectInstructions(Pred, TmpIList);
|
|
llvm::append_range(IList, TmpIList);
|
|
}
|
|
} else
|
|
llvm_unreachable("unimplemented type of node");
|
|
return !IList.empty();
|
|
}
|
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode::NodeKind K) {
|
|
const char *Out;
|
|
switch (K) {
|
|
case DDGNode::NodeKind::SingleInstruction:
|
|
Out = "single-instruction";
|
|
break;
|
|
case DDGNode::NodeKind::MultiInstruction:
|
|
Out = "multi-instruction";
|
|
break;
|
|
case DDGNode::NodeKind::PiBlock:
|
|
Out = "pi-block";
|
|
break;
|
|
case DDGNode::NodeKind::Root:
|
|
Out = "root";
|
|
break;
|
|
case DDGNode::NodeKind::Unknown:
|
|
Out = "?? (error)";
|
|
break;
|
|
}
|
|
OS << Out;
|
|
return OS;
|
|
}
|
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGNode &N) {
|
|
OS << "Node Address:" << &N << ":" << N.getKind() << "\n";
|
|
if (isa<SimpleDDGNode>(N)) {
|
|
OS << " Instructions:\n";
|
|
for (const Instruction *I : cast<const SimpleDDGNode>(N).getInstructions())
|
|
OS.indent(2) << *I << "\n";
|
|
} else if (isa<PiBlockDDGNode>(&N)) {
|
|
OS << "--- start of nodes in pi-block ---\n";
|
|
auto &Nodes = cast<const PiBlockDDGNode>(&N)->getNodes();
|
|
unsigned Count = 0;
|
|
for (const DDGNode *N : Nodes)
|
|
OS << *N << (++Count == Nodes.size() ? "" : "\n");
|
|
OS << "--- end of nodes in pi-block ---\n";
|
|
} else if (!isa<RootDDGNode>(N))
|
|
llvm_unreachable("unimplemented type of node");
|
|
|
|
OS << (N.getEdges().empty() ? " Edges:none!\n" : " Edges:\n");
|
|
for (const auto &E : N.getEdges())
|
|
OS.indent(2) << *E;
|
|
return OS;
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// SimpleDDGNode implementation
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
SimpleDDGNode::SimpleDDGNode(Instruction &I)
|
|
: DDGNode(NodeKind::SingleInstruction) {
|
|
assert(InstList.empty() && "Expected empty list.");
|
|
InstList.push_back(&I);
|
|
}
|
|
|
|
SimpleDDGNode::SimpleDDGNode(const SimpleDDGNode &N)
|
|
: DDGNode(N), InstList(N.InstList) {
|
|
assert(((getKind() == NodeKind::SingleInstruction && InstList.size() == 1) ||
|
|
(getKind() == NodeKind::MultiInstruction && InstList.size() > 1)) &&
|
|
"constructing from invalid simple node.");
|
|
}
|
|
|
|
SimpleDDGNode::SimpleDDGNode(SimpleDDGNode &&N)
|
|
: DDGNode(std::move(N)), InstList(std::move(N.InstList)) {
|
|
assert(((getKind() == NodeKind::SingleInstruction && InstList.size() == 1) ||
|
|
(getKind() == NodeKind::MultiInstruction && InstList.size() > 1)) &&
|
|
"constructing from invalid simple node.");
|
|
}
|
|
|
|
SimpleDDGNode::~SimpleDDGNode() { InstList.clear(); }
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// PiBlockDDGNode implementation
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
PiBlockDDGNode::PiBlockDDGNode(const PiNodeList &List)
|
|
: DDGNode(NodeKind::PiBlock), NodeList(List) {
|
|
assert(!NodeList.empty() && "pi-block node constructed with an empty list.");
|
|
}
|
|
|
|
PiBlockDDGNode::PiBlockDDGNode(const PiBlockDDGNode &N)
|
|
: DDGNode(N), NodeList(N.NodeList) {
|
|
assert(getKind() == NodeKind::PiBlock && !NodeList.empty() &&
|
|
"constructing from invalid pi-block node.");
|
|
}
|
|
|
|
PiBlockDDGNode::PiBlockDDGNode(PiBlockDDGNode &&N)
|
|
: DDGNode(std::move(N)), NodeList(std::move(N.NodeList)) {
|
|
assert(getKind() == NodeKind::PiBlock && !NodeList.empty() &&
|
|
"constructing from invalid pi-block node.");
|
|
}
|
|
|
|
PiBlockDDGNode::~PiBlockDDGNode() { NodeList.clear(); }
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// DDGEdge implementation
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGEdge::EdgeKind K) {
|
|
const char *Out;
|
|
switch (K) {
|
|
case DDGEdge::EdgeKind::RegisterDefUse:
|
|
Out = "def-use";
|
|
break;
|
|
case DDGEdge::EdgeKind::MemoryDependence:
|
|
Out = "memory";
|
|
break;
|
|
case DDGEdge::EdgeKind::Rooted:
|
|
Out = "rooted";
|
|
break;
|
|
case DDGEdge::EdgeKind::Unknown:
|
|
Out = "?? (error)";
|
|
break;
|
|
}
|
|
OS << Out;
|
|
return OS;
|
|
}
|
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const DDGEdge &E) {
|
|
OS << "[" << E.getKind() << "] to " << &E.getTargetNode() << "\n";
|
|
return OS;
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// DataDependenceGraph implementation
|
|
//===--------------------------------------------------------------------===//
|
|
using BasicBlockListType = SmallVector<BasicBlock *, 8>;
|
|
|
|
DataDependenceGraph::DataDependenceGraph(Function &F, DependenceInfo &D)
|
|
: DependenceGraphInfo(F.getName().str(), D) {
|
|
// Put the basic blocks in program order for correct dependence
|
|
// directions.
|
|
BasicBlockListType BBList;
|
|
for (const auto &SCC : make_range(scc_begin(&F), scc_end(&F)))
|
|
append_range(BBList, SCC);
|
|
std::reverse(BBList.begin(), BBList.end());
|
|
DDGBuilder(*this, D, BBList).populate();
|
|
}
|
|
|
|
DataDependenceGraph::DataDependenceGraph(Loop &L, LoopInfo &LI,
|
|
DependenceInfo &D)
|
|
: DependenceGraphInfo(Twine(L.getHeader()->getParent()->getName() + "." +
|
|
L.getHeader()->getName())
|
|
.str(),
|
|
D) {
|
|
// Put the basic blocks in program order for correct dependence
|
|
// directions.
|
|
LoopBlocksDFS DFS(&L);
|
|
DFS.perform(&LI);
|
|
BasicBlockListType BBList;
|
|
append_range(BBList, make_range(DFS.beginRPO(), DFS.endRPO()));
|
|
DDGBuilder(*this, D, BBList).populate();
|
|
}
|
|
|
|
DataDependenceGraph::~DataDependenceGraph() {
|
|
for (auto *N : Nodes) {
|
|
for (auto *E : *N)
|
|
delete E;
|
|
delete N;
|
|
}
|
|
}
|
|
|
|
bool DataDependenceGraph::addNode(DDGNode &N) {
|
|
if (!DDGBase::addNode(N))
|
|
return false;
|
|
|
|
// In general, if the root node is already created and linked, it is not safe
|
|
// to add new nodes since they may be unreachable by the root. However,
|
|
// pi-block nodes need to be added after the root node is linked, and they are
|
|
// always reachable by the root, because they represent components that are
|
|
// already reachable by root.
|
|
auto *Pi = dyn_cast<PiBlockDDGNode>(&N);
|
|
assert((!Root || Pi) &&
|
|
"Root node is already added. No more nodes can be added.");
|
|
|
|
if (isa<RootDDGNode>(N))
|
|
Root = &N;
|
|
|
|
if (Pi)
|
|
for (DDGNode *NI : Pi->getNodes())
|
|
PiBlockMap.insert(std::make_pair(NI, Pi));
|
|
|
|
return true;
|
|
}
|
|
|
|
const PiBlockDDGNode *DataDependenceGraph::getPiBlock(const NodeType &N) const {
|
|
auto It = PiBlockMap.find(&N);
|
|
if (It == PiBlockMap.end())
|
|
return nullptr;
|
|
auto *Pi = It->second;
|
|
assert(!PiBlockMap.contains(Pi) && "Nested pi-blocks detected.");
|
|
return Pi;
|
|
}
|
|
|
|
raw_ostream &llvm::operator<<(raw_ostream &OS, const DataDependenceGraph &G) {
|
|
for (DDGNode *Node : G)
|
|
// Avoid printing nodes that are part of a pi-block twice. They will get
|
|
// printed when the pi-block is printed.
|
|
if (!G.getPiBlock(*Node))
|
|
OS << *Node << "\n";
|
|
OS << "\n";
|
|
return OS;
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// DDGBuilder implementation
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
bool DDGBuilder::areNodesMergeable(const DDGNode &Src,
|
|
const DDGNode &Tgt) const {
|
|
// Only merge two nodes if they are both simple nodes and the consecutive
|
|
// instructions after merging belong to the same BB.
|
|
const auto *SimpleSrc = dyn_cast<const SimpleDDGNode>(&Src);
|
|
const auto *SimpleTgt = dyn_cast<const SimpleDDGNode>(&Tgt);
|
|
if (!SimpleSrc || !SimpleTgt)
|
|
return false;
|
|
|
|
return SimpleSrc->getLastInstruction()->getParent() ==
|
|
SimpleTgt->getFirstInstruction()->getParent();
|
|
}
|
|
|
|
void DDGBuilder::mergeNodes(DDGNode &A, DDGNode &B) {
|
|
DDGEdge &EdgeToFold = A.back();
|
|
assert(A.getEdges().size() == 1 && EdgeToFold.getTargetNode() == B &&
|
|
"Expected A to have a single edge to B.");
|
|
assert(isa<SimpleDDGNode>(&A) && isa<SimpleDDGNode>(&B) &&
|
|
"Expected simple nodes");
|
|
|
|
// Copy instructions from B to the end of A.
|
|
cast<SimpleDDGNode>(&A)->appendInstructions(*cast<SimpleDDGNode>(&B));
|
|
|
|
// Move to A any outgoing edges from B.
|
|
for (DDGEdge *BE : B)
|
|
Graph.connect(A, BE->getTargetNode(), *BE);
|
|
|
|
A.removeEdge(EdgeToFold);
|
|
destroyEdge(EdgeToFold);
|
|
Graph.removeNode(B);
|
|
destroyNode(B);
|
|
}
|
|
|
|
bool DDGBuilder::shouldSimplify() const { return SimplifyDDG; }
|
|
|
|
bool DDGBuilder::shouldCreatePiBlocks() const { return CreatePiBlocks; }
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// DDG Analysis Passes
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
/// DDG as a loop pass.
|
|
DDGAnalysis::Result DDGAnalysis::run(Loop &L, LoopAnalysisManager &AM,
|
|
LoopStandardAnalysisResults &AR) {
|
|
Function *F = L.getHeader()->getParent();
|
|
DependenceInfo DI(F, &AR.AA, &AR.SE, &AR.LI);
|
|
return std::make_unique<DataDependenceGraph>(L, AR.LI, DI);
|
|
}
|
|
AnalysisKey DDGAnalysis::Key;
|
|
|
|
PreservedAnalyses DDGAnalysisPrinterPass::run(Loop &L, LoopAnalysisManager &AM,
|
|
LoopStandardAnalysisResults &AR,
|
|
LPMUpdater &U) {
|
|
OS << "'DDG' for loop '" << L.getHeader()->getName() << "':\n";
|
|
OS << *AM.getResult<DDGAnalysis>(L, AR);
|
|
return PreservedAnalyses::all();
|
|
}
|