From a532f738e2662d782906eabb8f95106aa65283bd Mon Sep 17 00:00:00 2001 From: Ivan Krysak Date: Thu, 16 Mar 2023 11:37:53 +0200 Subject: [PATCH] Yield: switch to new layouter invocation style --- .../revng/Yield/CallGraphs/CallGraphSlices.h | 12 +- .../revng/Yield/ControlFlow/Configuration.h | 2 +- include/revng/Yield/ControlFlow/Extraction.h | 11 +- .../Yield/ControlFlow/NodeSizeCalculation.h | 7 +- .../Yield/CrossRelations/CrossRelations.h | 4 +- lib/Yield/CallGraphs/CallGraphSlices.cpp | 55 +++--- lib/Yield/ControlFlow/Extraction.cpp | 33 ++-- lib/Yield/ControlFlow/NodeSizeCalculation.cpp | 6 +- lib/Yield/ControlFlow/SVG.cpp | 181 +++++++++++------- lib/Yield/CrossRelations.cpp | 6 +- 10 files changed, 180 insertions(+), 137 deletions(-) diff --git a/include/revng/Yield/CallGraphs/CallGraphSlices.h b/include/revng/Yield/CallGraphs/CallGraphSlices.h index ae6076668..1a2082af9 100644 --- a/include/revng/Yield/CallGraphs/CallGraphSlices.h +++ b/include/revng/Yield/CallGraphs/CallGraphSlices.h @@ -4,7 +4,7 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // -#include "revng/Yield/Graph.h" +#include "revng/Yield/CallGraphs/Graph.h" class MetaAddress; @@ -36,14 +36,16 @@ namespace yield::calls { /// replaced by fake "reference" nodes). /// /// \note: this makes a copy of the graph, as such `Input` is not affected. -Graph makeCalleeTree(const Graph &Input, - const BasicBlockID &SlicePoint = BasicBlockID()); +calls::PreLayoutGraph +makeCalleeTree(const calls::PreLayoutGraph &Input, + const BasicBlockID &SlicePoint = BasicBlockID::invalid()); /// Produces a backwards facing slice of the graph starting from a single node. /// /// It is exactly the same as \see makeCalleeTree except it works in /// the opposite direction (it makes sure all the predecessors are preserved). -Graph makeCallerTree(const Graph &Input, - const BasicBlockID &SlicePoint = BasicBlockID()); +calls::PreLayoutGraph +makeCallerTree(const calls::PreLayoutGraph &Input, + const BasicBlockID &SlicePoint = BasicBlockID::invalid()); } // namespace yield::calls diff --git a/include/revng/Yield/ControlFlow/Configuration.h b/include/revng/Yield/ControlFlow/Configuration.h index 1a97946d2..e03662bd3 100644 --- a/include/revng/Yield/ControlFlow/Configuration.h +++ b/include/revng/Yield/ControlFlow/Configuration.h @@ -4,7 +4,7 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // -#include "revng/Yield/Graph.h" +#include "revng/Yield/Support/GraphLayout/Traits.h" namespace yield::cfg { diff --git a/include/revng/Yield/ControlFlow/Extraction.h b/include/revng/Yield/ControlFlow/Extraction.h index ded406cb3..4c927f1d2 100644 --- a/include/revng/Yield/ControlFlow/Extraction.h +++ b/include/revng/Yield/ControlFlow/Extraction.h @@ -4,21 +4,20 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // +#include "revng/Yield/ControlFlow/Graph.h" + namespace model { class Binary; } namespace yield { class Function; } -namespace yield { -class Graph; -} namespace yield::cfg { struct Configuration; -Graph extractFromInternal(const yield::Function &Function, - const model::Binary &Binary, - const Configuration &Configuration); +PreLayoutGraph extractFromInternal(const yield::Function &Function, + const model::Binary &Binary, + const Configuration &Configuration); } // namespace yield::cfg diff --git a/include/revng/Yield/ControlFlow/NodeSizeCalculation.h b/include/revng/Yield/ControlFlow/NodeSizeCalculation.h index a2e83b5a3..61dd1a26f 100644 --- a/include/revng/Yield/ControlFlow/NodeSizeCalculation.h +++ b/include/revng/Yield/ControlFlow/NodeSizeCalculation.h @@ -4,21 +4,20 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // +#include "revng/Yield/ControlFlow/Graph.h" + namespace model { class Binary; } namespace yield { class Function; } -namespace yield { -class Graph; -} namespace yield::cfg { struct Configuration; -void calculateNodeSizes(Graph &Graph, +void calculateNodeSizes(PreLayoutGraph &Graph, const yield::Function &Function, const model::Binary &Binary, const Configuration &Configuration); diff --git a/include/revng/Yield/CrossRelations/CrossRelations.h b/include/revng/Yield/CrossRelations/CrossRelations.h index 0155961a3..e3eea647b 100644 --- a/include/revng/Yield/CrossRelations/CrossRelations.h +++ b/include/revng/Yield/CrossRelations/CrossRelations.h @@ -7,8 +7,8 @@ #include "revng/ADT/GenericGraph.h" #include "revng/EarlyFunctionAnalysis/FunctionMetadata.h" #include "revng/Model/Binary.h" +#include "revng/Yield/CallGraphs/Graph.h" #include "revng/Yield/CrossRelations/RelationDescription.h" -#include "revng/Yield/Graph.h" /* TUPLE-TREE-YAML name: CrossRelations @@ -37,7 +37,7 @@ public: const model::Binary &Binary); GenericGraph toCallGraph() const; - yield::Graph toYieldGraph() const; + yield::calls::PreLayoutGraph toYieldGraph() const; }; } // namespace yield::crossrelations diff --git a/lib/Yield/CallGraphs/CallGraphSlices.cpp b/lib/Yield/CallGraphs/CallGraphSlices.cpp index e0152ed86..77077b068 100644 --- a/lib/Yield/CallGraphs/CallGraphSlices.cpp +++ b/lib/Yield/CallGraphs/CallGraphSlices.cpp @@ -12,20 +12,18 @@ #include "revng/Yield/CallGraphs/CallGraphSlices.h" -using NodeView = const yield::Graph::Node *; +using Graph = yield::calls::PreLayoutGraph; +using Node = yield::calls::PreLayoutNode; -static yield::Graph::Node * -copyNode(yield::Graph &Graph, const yield::Graph::Node *Source) { - auto New = std::make_unique(Source->data()); - return Graph.addNode(std::move(New)); +static Node *copyNode(yield::calls::PreLayoutGraph &Graph, const Node *Source) { + return Graph.addNode(std::make_unique(Source->data())); } /// \tparam NV local `NodeView` specialization /// \tparam INV inverted location `NodeView` specialization template -yield::Graph -makeTreeImpl(const yield::Graph &Input, const BasicBlockID &SlicePoint) { - auto SlicePointPredicate = [&SlicePoint](NodeView Node) { +Graph makeTreeImpl(const Graph &Input, const BasicBlockID &SlicePoint) { + auto SlicePointPredicate = [&SlicePoint](const Node *Node) { return Node->Address == SlicePoint; }; auto Entry = llvm::find_if(Input.nodes(), SlicePointPredicate); @@ -34,8 +32,8 @@ makeTreeImpl(const yield::Graph &Input, const BasicBlockID &SlicePoint) { // Find the rank of each node, such that for any node its rank is equal to // the highest rank among its children plus one. llvm::ReversePostOrderTraversal ReversePostOrder(NV{ *Entry }); - std::unordered_map Ranks; - for (NodeView CurrentNode : ReversePostOrder) { + std::unordered_map Ranks; + for (const Node *CurrentNode : ReversePostOrder) { uint64_t &CurrentRank = Ranks[CurrentNode]; CurrentRank = 0; @@ -48,16 +46,16 @@ makeTreeImpl(const yield::Graph &Input, const BasicBlockID &SlicePoint) { // The ranks calculated earlier are used to choose a specific one. // // TODO: We should consider a better selection algorithm. - std::unordered_map RealEdges; - for (NodeView Node : ReversePostOrder) { - auto NodeIt = Ranks.find(Node); + std::unordered_map RealEdges; + for (const Node *Current : ReversePostOrder) { + auto NodeIt = Ranks.find(Current); revng_assert(NodeIt != Ranks.end()); // Select the neighbour with the highest possible rank that is still // lower than the current node's rank. - NodeView SelectedNeighbour = nullptr; + const Node *SelectedNeighbour = nullptr; size_t SelectedNeighbourRank = 0; - for (NodeView Neighbour : llvm::children(Node)) { + for (const Node *Neighbour : llvm::children(Current)) { if (auto Iterator = Ranks.find(Neighbour); Iterator != Ranks.end()) { // If an inverse neighbour is not present in the `Ranks` table, it's not // a part of the desired slice, as such we can safely ignore it. @@ -69,16 +67,16 @@ makeTreeImpl(const yield::Graph &Input, const BasicBlockID &SlicePoint) { } } - auto [_, Success] = RealEdges.try_emplace(Node, SelectedNeighbour); + auto [_, Success] = RealEdges.try_emplace(Current, SelectedNeighbour); revng_assert(Success); } - yield::Graph Result; - std::unordered_map Lookup; + Graph Result; + std::unordered_map Lookup; // Returns the version of the node from the new graph if it exists, // or adds a new one to if it does not. - auto FindOrAddHelper = [&Result, &Lookup](NodeView OldNode) { + auto FindOrAddHelper = [&Result, &Lookup](const Node *OldNode) { if (auto NewNode = Lookup.find(OldNode); NewNode != Lookup.end()) return NewNode->second; else @@ -91,7 +89,7 @@ makeTreeImpl(const yield::Graph &Input, const BasicBlockID &SlicePoint) { Result.setEntryNode(FindOrAddHelper(*Entry)); // Fill in the `Result` graph. - for (NodeView Node : llvm::breadth_first(NV{ *Entry })) { + for (const Node *Node : llvm::breadth_first(NV{ *Entry })) { for (auto Neighbour : llvm::children(Node)) { if (Ranks.contains(Neighbour)) { auto *NewNeighbour = FindOrAddHelper(Neighbour); @@ -111,13 +109,18 @@ makeTreeImpl(const yield::Graph &Input, const BasicBlockID &SlicePoint) { return Result; } -yield::Graph yield::calls::makeCalleeTree(const yield::Graph &Input, - const BasicBlockID &SlicePoint) { +yield::calls::PreLayoutGraph +yield::calls::makeCalleeTree(const PreLayoutGraph &Input, + const BasicBlockID &SlicePoint) { // Forwards direction, makes sure no successor relation ever gets lost. - return makeTreeImpl>(Input, SlicePoint); + return makeTreeImpl>(Input, SlicePoint); } -yield::Graph yield::calls::makeCallerTree(const yield::Graph &Input, - const BasicBlockID &SlicePoint) { + +yield::calls::PreLayoutGraph +yield::calls::makeCallerTree(const PreLayoutGraph &Input, + const BasicBlockID &SlicePoint) { // Backwards direction, makes sure no predecessor relation ever gets lost. - return makeTreeImpl, NodeView>(Input, SlicePoint); + return makeTreeImpl, + const PreLayoutNode *>(Input, SlicePoint); } diff --git a/lib/Yield/ControlFlow/Extraction.cpp b/lib/Yield/ControlFlow/Extraction.cpp index 2c0d4e7cd..77f10d28c 100644 --- a/lib/Yield/ControlFlow/Extraction.cpp +++ b/lib/Yield/ControlFlow/Extraction.cpp @@ -11,27 +11,28 @@ #include "revng/Yield/ControlFlow/Configuration.h" #include "revng/Yield/ControlFlow/Extraction.h" #include "revng/Yield/ControlFlow/FallthroughDetection.h" +#include "revng/Yield/ControlFlow/Graph.h" #include "revng/Yield/Function.h" -#include "revng/Yield/Graph.h" -yield::Graph +yield::cfg::PreLayoutGraph yield::cfg::extractFromInternal(const yield::Function &Function, const model::Binary &Binary, const Configuration &Configuration) { + using PLG = PreLayoutGraph; const auto &ControlFlowGraph = Function.ControlFlowGraph(); - auto [Result, Table] = efa::buildControlFlowGraph(ControlFlowGraph, - Function.Entry(), - Binary); + auto [Result, Lookup] = efa::buildControlFlowGraph(ControlFlowGraph, + Function.Entry(), + Binary); if (!Configuration.AddExitNode) { - auto ExitNodeIterator = Table.find(BasicBlockID::invalid()); - if (ExitNodeIterator != Table.end()) + auto ExitNodeIterator = Lookup.find(BasicBlockID::invalid()); + if (ExitNodeIterator != Lookup.end()) Result.removeNode(ExitNodeIterator->second); } if (Configuration.AddEntryNode) { - auto EntryIterator = Table.find(BasicBlockID(Function.Entry())); - revng_assert(EntryIterator != Table.end()); + auto EntryIterator = Lookup.find(BasicBlockID(Function.Entry())); + revng_assert(EntryIterator != Lookup.end()); auto *RootNode = Result.addNode(); RootNode->Address = BasicBlockID::invalid(); RootNode->addSuccessor(EntryIterator->second); @@ -40,8 +41,8 @@ yield::cfg::extractFromInternal(const yield::Function &Function, // Colour 'taken' and 'refused' edges. for (const auto &BasicBlock : Function.ControlFlowGraph()) { - auto NodeIterator = Table.find(BasicBlock.ID()); - revng_assert(NodeIterator != Table.end()); + auto NodeIterator = Lookup.find(BasicBlock.ID()); + revng_assert(NodeIterator != Lookup.end()); auto &CurrentNode = *NodeIterator->second; CurrentNode.NextAddress = BasicBlock.nextBlock(); @@ -51,11 +52,11 @@ yield::cfg::extractFromInternal(const yield::Function &Function, auto Front = *CurrentNode.successor_edges_begin(); auto Back = *std::next(CurrentNode.successor_edges_begin()); if (Front.Neighbor->Address == BasicBlock.nextBlock()) { - Front.Label->Type = yield::Graph::EdgeType::Refused; - Back.Label->Type = yield::Graph::EdgeType::Taken; + Front.Label->Type = yield::cfg::EdgeType::Refused; + Back.Label->Type = yield::cfg::EdgeType::Taken; } else if (Back.Neighbor->Address == BasicBlock.nextBlock()) { - Front.Label->Type = yield::Graph::EdgeType::Taken; - Back.Label->Type = yield::Graph::EdgeType::Refused; + Front.Label->Type = yield::cfg::EdgeType::Taken; + Back.Label->Type = yield::cfg::EdgeType::Refused; } } else if (CurrentNode.successorCount() == 1) { for (const auto &Successor : BasicBlock.Successors()) @@ -64,7 +65,7 @@ yield::cfg::extractFromInternal(const yield::Function &Function, auto Edge = *CurrentNode.successor_edges_begin(); if (Edge.Neighbor->Address == BasicBlock.nextBlock()) - Edge.Label->Type = yield::Graph::EdgeType::Refused; + Edge.Label->Type = yield::cfg::EdgeType::Refused; } } } diff --git a/lib/Yield/ControlFlow/NodeSizeCalculation.cpp b/lib/Yield/ControlFlow/NodeSizeCalculation.cpp index e9e696424..bcf6d4aef 100644 --- a/lib/Yield/ControlFlow/NodeSizeCalculation.cpp +++ b/lib/Yield/ControlFlow/NodeSizeCalculation.cpp @@ -11,7 +11,7 @@ #include "revng/Yield/ControlFlow/FallthroughDetection.h" #include "revng/Yield/ControlFlow/NodeSizeCalculation.h" #include "revng/Yield/Function.h" -#include "revng/Yield/Graph.h" +#include "revng/Yield/Support/GraphLayout/Graphs.h" static yield::layout::Size operator+(const yield::layout::Size &LHS, const yield::layout::Size &RHS) { @@ -216,11 +216,11 @@ basicBlockSize(const yield::BasicBlock &BasicBlock, return Result; } -void yield::cfg::calculateNodeSizes(Graph &Graph, +void yield::cfg::calculateNodeSizes(PreLayoutGraph &Graph, const yield::Function &Function, const model::Binary &Binary, const Configuration &Configuration) { - for (yield::Node *Node : Graph.nodes()) { + for (PreLayoutNode *Node : Graph.nodes()) { revng_assert(Node != nullptr); if (Node->Address.isValid()) { diff --git a/lib/Yield/ControlFlow/SVG.cpp b/lib/Yield/ControlFlow/SVG.cpp index 7e5295dc2..a9ff6e3e9 100644 --- a/lib/Yield/ControlFlow/SVG.cpp +++ b/lib/Yield/ControlFlow/SVG.cpp @@ -18,7 +18,6 @@ #include "revng/Yield/ControlFlow/Extraction.h" #include "revng/Yield/ControlFlow/NodeSizeCalculation.h" #include "revng/Yield/CrossRelations/CrossRelations.h" -#include "revng/Yield/Graph.h" #include "revng/Yield/PTML.h" #include "revng/Yield/SVG.h" #include "revng/Yield/Support/GraphLayout/SugiyamaStyle/Compute.h" @@ -42,21 +41,26 @@ static constexpr auto RefusedArrowHead = "refused-arrow-head"; } // namespace tags -static std::string_view edgeTypeAsString(yield::Graph::EdgeType Type) { - switch (Type) { - case yield::Graph::EdgeType::Unconditional: +static std::string_view edgeTypeAsString(const yield::cfg::Edge &Edge) { + switch (Edge.Type) { + case yield::cfg::EdgeType::Unconditional: return tags::UnconditionalEdge; - case yield::Graph::EdgeType::Call: + case yield::cfg::EdgeType::Call: return tags::CallEdge; - case yield::Graph::EdgeType::Taken: + case yield::cfg::EdgeType::Taken: return tags::TakenEdge; - case yield::Graph::EdgeType::Refused: + case yield::cfg::EdgeType::Refused: return tags::RefusedEdge; default: revng_abort("Unknown edge type"); } } +static std::string_view edgeTypeAsString(const yield::calls::Edge &Edge) { + // TODO: we might want to use separate set of tags for call graphs. + return Edge.IsBackwards ? tags::RefusedEdge : tags::TakenEdge; +} + // clang-format off template static std::string cubicBend(const yield::layout::Point &From, @@ -84,8 +88,8 @@ static std::string cubicBend(const yield::layout::Point &From, -To.Y); } -static std::string edge(const std::vector &Path, - const yield::Graph::EdgeType &Type, +static std::string edge(const yield::layout::Path &Path, + const std::string_view Type, bool UseOrthogonalBends = true, bool UseVerticalCurves = false) { std::string Points; @@ -107,19 +111,19 @@ static std::string edge(const std::vector &Path, revng_assert(Points.back() == ' '); Points.pop_back(); // Remove an extra space at the end. - std::string Marker = llvm::formatv("url(#{0}-arrow-head)", - edgeTypeAsString(Type)); + std::string Marker = llvm::formatv("url(#{0}-arrow-head)", Type); return Tag("path") - .addAttribute("class", std::string(edgeTypeAsString(Type)) += "-edge") + .addAttribute("class", std::string(Type) += "-edge") .addAttribute("d", std::move(Points)) .addAttribute("marker-end", std::move(Marker)) .addAttribute("fill", "none") .serialize(); } -static std::string node(const yield::Node *Node, - std::string &&Content, - const yield::cfg::Configuration &Configuration) { +template +std::string node(const yield::layout::OutputNode *Node, + std::string &&Content, + const yield::cfg::Configuration &Configuration) { yield::layout::Size HalfSize{ Node->Size.W / 2, Node->Size.H / 2 }; yield::layout::Point TopLeft{ Node->Center.X - HalfSize.W, -Node->Center.Y - HalfSize.H }; @@ -151,7 +155,9 @@ struct Viewbox { yield::layout::Point BottomRight = { +1, +1 }; }; -static Viewbox makeViewbox(const yield::Node *Node) { +template +static Viewbox +makeViewbox(const yield::layout::OutputNode *Node) { yield::layout::Size HalfSize{ Node->Size.W / 2, Node->Size.H / 2 }; yield::layout::Point TopLeft{ Node->Center.X - HalfSize.W, -Node->Center.Y - HalfSize.H }; @@ -183,7 +189,8 @@ static void expandViewbox(Viewbox &Box, const yield::layout::Point &Point) { Box.BottomRight.Y = -Point.Y; } -static Viewbox calculateViewbox(const yield::Graph &Graph) { +template GraphType> +Viewbox calculateViewbox(const GraphType &Graph) { revng_assert(Graph.size() != 0); // Ensure every node fits. @@ -254,22 +261,23 @@ defaultArrowHeads(const yield::cfg::Configuration &Configuration) { return duplicateArrowHeadsImpl(8, 3, 2); } -template -concept NodeExporter = requires(CallableType &&Callable, - const yield::Graph::Node &Node) { - { Callable(Node) } -> convertible_to; -}; - constexpr bool isVertical(yield::layout::sugiyama::Orientation Orientation) { return Orientation == yield::layout::sugiyama::Orientation::TopToBottom || Orientation == yield::layout::sugiyama::Orientation::BottomToTop; } -template -static std::string exportGraph(const yield::Graph &Graph, +template +concept NodeExporter = requires(CallableType &&Callable, const NodeType &Node) { + { Callable(Node) } -> convertible_to; +}; + +template PostLayoutGraph, + NodeExporter ContentsLambda> +static std::string exportGraph(const PostLayoutGraph &Graph, const yield::cfg::Configuration &Configuration, yield::layout::sugiyama::Orientation Orientation, - NodeExporter auto &&NodeContents) { + ContentsLambda &&NodeContents) { std::string Result; // Short circuit the execution for an empty graph. @@ -282,9 +290,8 @@ static std::string exportGraph(const yield::Graph &Graph, for (const auto [To, Edge] : From->successor_edges()) { if (ShouldEmitEmptyNodes || To->Address.isValid()) { revng_assert(Edge != nullptr); - revng_assert(Edge->Status != yield::Graph::EdgeStatus::Unrouted); Result += edge(Edge->Path, - Edge->Type, + edgeTypeAsString(*Edge), Configuration.UseOrthogonalBends, isVertical(Orientation)); } @@ -315,22 +322,37 @@ static std::string exportGraph(const yield::Graph &Graph, namespace yield::layout::sugiyama { -inline bool -compute(Graph &Graph, +/// A helper for invoking sugiyama style layouter with the configuration +/// filled in based on the relevant cfg::Configuration. +/// +/// \tparam Node The type of the data attached to each graph node +/// \tparam Edge The type of the data attached to each graph edge +/// +/// \param Graph An input graph +/// \param CFG An object describing the desired CFG configuration +/// \param LayoutOrientation The direction of the desired layout +/// \param Ranking The ranking strategy +/// \param UseSimpleTreeOptimization A flag deciding whether simple tree +/// optimization should be used. +/// +/// \return The laid out version of the graph corresponding to \ref Graph +template +inline std::optional> +compute(const InputGraph &Graph, const cfg::Configuration &CFG, Orientation LayoutOrientation = Orientation::TopToBottom, RankingStrategy Ranking = RankingStrategy::DisjointDepthFirstSearch, - bool SimpleTreeOptimization = false) { - return computeInPlace(&Graph, - Configuration{ - .Ranking = Ranking, - .Orientation = LayoutOrientation, - .UseOrthogonalBends = CFG.UseOrthogonalBends, - .PreserveLinearSegments = CFG.PreserveLinearSegments, - .UseSimpleTreeOptimization = SimpleTreeOptimization, - .VirtualNodeWeight = CFG.VirtualNodeWeight, - .NodeMarginSize = CFG.ExternalNodeMarginSize, - .EdgeMarginSize = CFG.EdgeMarginSize }); + bool UseSimpleTreeOptimization = false) { + return compute(Graph, + Configuration{ + .Ranking = Ranking, + .Orientation = LayoutOrientation, + .UseOrthogonalBends = CFG.UseOrthogonalBends, + .PreserveLinearSegments = CFG.PreserveLinearSegments, + .UseSimpleTreeOptimization = UseSimpleTreeOptimization, + .VirtualNodeWeight = CFG.VirtualNodeWeight, + .NodeMarginSize = CFG.ExternalNodeMarginSize, + .EdgeMarginSize = CFG.EdgeMarginSize }); } } // namespace yield::layout::sugiyama @@ -340,16 +362,18 @@ yield::svg::controlFlowGraph(const yield::Function &InternalFunction, const model::Binary &Binary) { constexpr auto Configuration = cfg::Configuration::getDefault(); - yield::Graph Graph = cfg::extractFromInternal(InternalFunction, - Binary, - Configuration); + using Pre = cfg::PreLayoutGraph; + Pre Graph = cfg::extractFromInternal(InternalFunction, Binary, Configuration); cfg::calculateNodeSizes(Graph, InternalFunction, Binary, Configuration); - auto TopToBottom = yield::layout::sugiyama::Orientation::TopToBottom; - layout::sugiyama::compute(Graph, Configuration, TopToBottom); + constexpr auto TopToBottom = layout::sugiyama::Orientation::TopToBottom; - auto Content = [&](const yield::Graph::Node &Node) { + using Post = std::optional; + Post Result = layout::sugiyama::compute(Graph, Configuration, TopToBottom); + revng_assert(Result.has_value()); + + auto Content = [&](const yield::cfg::PostLayoutNode &Node) { if (Node.Address.isValid()) return yield::ptml::controlFlowNode(Node.Address, InternalFunction, @@ -357,7 +381,7 @@ yield::svg::controlFlowGraph(const yield::Function &InternalFunction, else return std::string{}; }; - return exportGraph(Graph, Configuration, TopToBottom, Content); + return exportGraph(*Result, Configuration, TopToBottom, Content); } struct LabelNodeHelper { @@ -365,7 +389,7 @@ struct LabelNodeHelper { const yield::cfg::Configuration Configuration; std::optional RootNodeLocation = std::nullopt; - void computeSizes(yield::Graph &Graph) { + void computeSizes(yield::calls::PreLayoutGraph &Graph) { for (auto *Node : Graph.nodes()) { if (Node->Address.isValid()) { // A normal node @@ -391,7 +415,7 @@ struct LabelNodeHelper { } } - std::string operator()(const yield::Graph::Node &Node) const { + std::string operator()(const yield::calls::PostLayoutNode &Node) const { revng_assert(Node.Address.isValid()); if (Node.NextAddress.isValid()) { revng_assert(Node.Address == Node.NextAddress); @@ -421,24 +445,27 @@ std::string yield::svg::callGraph(const CrossRelations &Relations, LabelNodeHelper Helper{ Binary, Configuration }; - auto Result = Relations.toYieldGraph(); + yield::calls::PreLayoutGraph Result = Relations.toYieldGraph(); auto EntryPoints = entryPoints(&Result); revng_assert(!EntryPoints.empty()); if (EntryPoints.size() > 1) { // Add an artificial "root" node to make sure there's a single entry point. - yield::Graph::Node *Root = Result.addNode(); - for (yield::Graph::Node *Entry : EntryPoints) + yield::calls::PreLayoutNode *Root = Result.addNode(); + for (yield::calls::PreLayoutNode *Entry : EntryPoints) Root->addSuccessor(Entry); Result.setEntryNode(Root); } else { Result.setEntryNode(EntryPoints.front()); } - auto CalleeTree = calls::makeCalleeTree(Result); - Helper.computeSizes(CalleeTree); + auto Tree = calls::makeCalleeTree(Result); + Helper.computeSizes(Tree); - layout::sugiyama::compute(CalleeTree, Configuration, LeftToRight, BFS, true); - return exportGraph(CalleeTree, Configuration, LeftToRight, Helper); + namespace sugiyama = layout::sugiyama; + auto LT = sugiyama::compute(Tree, Configuration, LeftToRight, BFS, true); + revng_assert(LT.has_value()); + + return exportGraph(*LT, Configuration, LeftToRight, Helper); } static auto flipPoint(yield::layout::Point const &Point) { @@ -457,9 +484,10 @@ static auto convertPoint(yield::layout::Point const &Point, return translatePoint(flipPoint(Point), Delta); } -static yield::Graph combineHalvesHelper(const BasicBlockID &SlicePoint, - yield::Graph &&ForwardsSlice, - yield::Graph &&BackwardsSlice) { +static yield::calls::PostLayoutGraph +combineHalvesHelper(const BasicBlockID &SlicePoint, + yield::calls::PostLayoutGraph &&ForwardsSlice, + yield::calls::PostLayoutGraph &&BackwardsSlice) { revng_assert(ForwardsSlice.size() != 0 && BackwardsSlice.size() != 0); auto IsSlicePoint = [&SlicePoint](const auto *Node) { @@ -490,8 +518,9 @@ static yield::Graph combineHalvesHelper(const BasicBlockID &SlicePoint, } // Define a map for faster node lookup. - llvm::DenseMap Lookup; - auto AccessLookup = [&Lookup](yield::Graph::Node *Key) { + using PostNode = yield::calls::PostLayoutGraph::Node; + llvm::DenseMap Lookup; + auto AccessLookup = [&Lookup](PostNode *Key) { auto Iterator = Lookup.find(Key); revng_assert(Iterator != Lookup.end() && Iterator->second != nullptr); return Iterator->second; @@ -534,24 +563,34 @@ std::string yield::svg::callGraphSlice(const BasicBlockID &SlicePoint, LabelNodeHelper Helper{ Binary, Configuration, SlicePoint }; // Ready the forwards facing part of the slice - auto Forwards = calls::makeCalleeTree(Relations.toYieldGraph(), SlicePoint); - for (auto *From : Forwards.nodes()) + auto Forward = calls::makeCalleeTree(Relations.toYieldGraph(), SlicePoint); + for (auto *From : Forward.nodes()) for (auto [To, Label] : From->successor_edges()) - Label->Type = yield::Graph::EdgeType::Taken; - Helper.computeSizes(Forwards); - layout::sugiyama::compute(Forwards, Configuration, LeftToRight, BFS, true); + Label->IsBackwards = false; + Helper.computeSizes(Forward); + auto LaidOutForwardsGraph = layout::sugiyama::compute(Forward, + Configuration, + LeftToRight, + BFS, + true); + revng_assert(LaidOutForwardsGraph.has_value()); // Ready the backwards facing part of the slice auto Backwards = calls::makeCallerTree(Relations.toYieldGraph(), SlicePoint); for (auto *From : Backwards.nodes()) for (auto [To, Label] : From->successor_edges()) - Label->Type = yield::Graph::EdgeType::Refused; + Label->IsBackwards = true; Helper.computeSizes(Backwards); - layout::sugiyama::compute(Backwards, Configuration, LeftToRight, BFS, true); + auto LaidOutBackwardsGraph = layout::sugiyama::compute(Backwards, + Configuration, + LeftToRight, + BFS, + true); + revng_assert(LaidOutBackwardsGraph.has_value()); // Consume the halves to produce a combined graph and export it. auto CombinedGraph = combineHalvesHelper(SlicePoint, - std::move(Forwards), - std::move(Backwards)); + std::move(*LaidOutForwardsGraph), + std::move(*LaidOutBackwardsGraph)); return exportGraph(CombinedGraph, Configuration, LeftToRight, Helper); } diff --git a/lib/Yield/CrossRelations.cpp b/lib/Yield/CrossRelations.cpp index 348330010..ebffa4d5a 100644 --- a/lib/Yield/CrossRelations.cpp +++ b/lib/Yield/CrossRelations.cpp @@ -141,10 +141,10 @@ CR::CrossRelations::toCallGraph() const { return Result; } -yield::Graph CR::CrossRelations::toYieldGraph() const { - yield::Graph Result; +yield::calls::PreLayoutGraph CR::CrossRelations::toYieldGraph() const { + yield::calls::PreLayoutGraph Result; - std::map LookupHelper; + std::map LookupHelper; namespace ranks = revng::ranks; using namespace pipeline;