diff --git a/include/revng/Yield/Graph.h b/include/revng/Yield/Graph.h index de19850c8..7508fc94c 100644 --- a/include/revng/Yield/Graph.h +++ b/include/revng/Yield/Graph.h @@ -50,4 +50,27 @@ public: using EdgeType = detail::EdgeType; }; +namespace layout { + +template<> +struct LayoutableGraphTraits { + static_assert(SpecializationOfGenericGraph); + using LLVMTrait = llvm::GraphTraits; + + static const Size &getNodeSize(typename LLVMTrait::NodeRef Node) { + return Node->Size; + } + + static void setNodePosition(typename LLVMTrait::NodeRef Node, Point &&Point) { + Node->Center = std::move(Point); + } + static void setEdgePath(typename LLVMTrait::EdgeRef Edge, Path &&Path) { + Edge.Label->Path.reserve(Path.size()); + for (layout::Point &Point : Path) + Edge.Label->Path.emplace_back(std::move(Point)); + } +}; + +} // namespace layout + } // namespace yield diff --git a/include/revng/Yield/Support/GraphLayout/SugiyamaStyle/Compute.h b/include/revng/Yield/Support/GraphLayout/SugiyamaStyle/Compute.h index c83304c51..59cc76e8b 100644 --- a/include/revng/Yield/Support/GraphLayout/SugiyamaStyle/Compute.h +++ b/include/revng/Yield/Support/GraphLayout/SugiyamaStyle/Compute.h @@ -4,7 +4,8 @@ // This file is distributed under the MIT License. See LICENSE.md for details. // -#include "revng/Yield/Graph.h" +#include "revng/Yield/Support/GraphLayout/Graphs.h" +#include "revng/Yield/Support/GraphLayout/SugiyamaStyle/InternalGraph.h" namespace yield::layout::sugiyama { @@ -71,8 +72,47 @@ public: layout::Dimension EdgeMarginSize; }; +namespace detail { + +bool computeImpl(InternalGraph &Internal, const Configuration &Configuration); + +} // namespace detail + +template +inline bool +computeInPlace(GraphType &&Graph, const Configuration &Configuration) { + using IG = InternalGraph; + auto [Internal, InputNodeLookup] = IG::make(std::forward(Graph)); + if (!detail::computeImpl(Internal, Configuration)) + return false; + + Internal.template exportInto(InputNodeLookup); + return true; +} + /// A custom graph layering algorithm designed for pre-calculating majority of /// the expensive stuff needed for graph rendering. -bool compute(Graph &Graph, const Configuration &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 Configuration An object configuring the specifics of the layout +/// +/// \return The laid out version of the graph corresponding to \ref Graph +template +inline std::optional> +compute(const layout::InputGraph &Graph, + const Configuration &Configuration) { + // TODO: rename into `compute` once `llvm::GraphTraits` has a concept support + // for checking whether it's defined for a given type or not. + + using OutputGraph = layout::OutputGraph; + std::optional Result = layout::detail::convert(Graph); + if (!computeInPlace(&Result.value(), Configuration)) + Result = std::nullopt; + + return Result; +} } // namespace yield::layout::sugiyama diff --git a/lib/Yield/ControlFlow/SVG.cpp b/lib/Yield/ControlFlow/SVG.cpp index 14dd17676..7e5295dc2 100644 --- a/lib/Yield/ControlFlow/SVG.cpp +++ b/lib/Yield/ControlFlow/SVG.cpp @@ -320,17 +320,17 @@ compute(Graph &Graph, const cfg::Configuration &CFG, Orientation LayoutOrientation = Orientation::TopToBottom, RankingStrategy Ranking = RankingStrategy::DisjointDepthFirstSearch, - 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 }); + 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 }); } } // namespace yield::layout::sugiyama diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/Compute.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/Compute.cpp index e02dd1c31..852f75488 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/Compute.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/Compute.cpp @@ -10,8 +10,8 @@ #include "InternalCompute.h" namespace sugiyama = yield::layout::sugiyama; -bool sugiyama::compute(yield::Graph &Graph, - const Configuration &Configuration) { +bool sugiyama::detail::computeImpl(InternalGraph &Graph, + const Configuration &Configuration) { using RS = sugiyama::RankingStrategy; if (Configuration.Orientation == sugiyama::Orientation::LeftToRight @@ -48,8 +48,9 @@ bool sugiyama::compute(yield::Graph &Graph, std::swap(Node->Center.X, Node->Center.Y); for (auto [_, Edge] : Node->successor_edges()) - for (auto &[X, Y] : Edge->Path) - std::swap(X, Y); + if (!Edge->isVirtual()) + for (auto &[X, Y] : Edge->getPath()) + std::swap(X, Y); } } @@ -58,16 +59,18 @@ bool sugiyama::compute(yield::Graph &Graph, Node->Center.Y = -Node->Center.Y; for (auto [_, Edge] : Node->successor_edges()) - for (auto &[X, Y] : Edge->Path) - Y = -Y; + if (!Edge->isVirtual()) + for (auto &[X, Y] : Edge->getPath()) + Y = -Y; } } else if (Configuration.Orientation == sugiyama::Orientation::LeftToRight) { for (auto *Node : Graph.nodes()) { Node->Center.X = -Node->Center.X; for (auto [_, Edge] : Node->successor_edges()) - for (auto &[X, Y] : Edge->Path) - X = -X; + if (!Edge->isVirtual()) + for (auto &[X, Y] : Edge->getPath()) + X = -X; } } diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/EdgeRouting.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/EdgeRouting.cpp index 200f61798..a2e15fa89 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/EdgeRouting.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/EdgeRouting.cpp @@ -14,14 +14,14 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, const LaneContainer &Lanes, float MarginSize, float EdgeDistance) { - std::vector CornerEdges; + std::vector CornerEdges; // To keep the hierarchy consistent, V-shapes were added using forward // direction. So that's what we're going to use to detect them. for (auto *From : Graph.nodes()) for (auto [To, Label] : From->successor_edges()) - if (!From->isVirtual() != !To->isVirtual() && !Label->IsBackwards) - CornerEdges.emplace_back(From, To, Label->Pointer, false); + if (!From->IsVirtual != !To->IsVirtual && !Label->IsBackwards) + CornerEdges.emplace_back(From, To, *Label); CornerContainer Corners; for (auto &Edge : CornerEdges) { @@ -34,7 +34,7 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, LaneIndex = Iterator->second; } - if (Edge.From->isVirtual() && !Edge.To->isVirtual()) { + if (Edge.From->IsVirtual && !Edge.To->IsVirtual) { if (Edge.From->successorCount() != 2 || Edge.From->hasPredecessors()) continue; @@ -45,35 +45,36 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, auto *Second = *std::next(Edge.From->successors().begin()); // Make sure there are no self-loops, otherwise it's not a corner. - if (First->Index == Edge.From->Index || Second->Index == Edge.From->Index) + if (First->index() == Edge.From->index() + || Second->index() == Edge.From->index()) continue; - auto ToUpperEdge = First->center().Y + First->size().H / 2; - auto FromUpperEdge = Second->center().Y + Second->size().H / 2; - Edge.From->center().X = (First->center().X + Second->center().X) / 2; - Edge.From->center().Y = std::min(ToUpperEdge, FromUpperEdge) + MarginSize - + LaneIndex * EdgeDistance; + auto ToUpperEdge = First->Center.Y + First->Size.H / 2; + auto FromUpperEdge = Second->Center.Y + Second->Size.H / 2; + Edge.From->Center.X = (First->Center.X + Second->Center.X) / 2; + Edge.From->Center.Y = std::min(ToUpperEdge, FromUpperEdge) + MarginSize + + LaneIndex * EdgeDistance; auto &From = Edge.From; for (auto [To, Label] : From->successor_edges()) { - auto FromTop = From->center().Y + From->size().H / 2; - auto ToTop = To->center().Y + To->size().H / 2; + auto FromTop = From->Center.Y + From->Size.H / 2; + auto ToTop = To->Center.Y + To->Size.H / 2; if (Label->IsBackwards) { revng_assert(!Corners.contains({ To, From })); - auto FromPoint = Point{ To->center().X, ToTop }; - auto CenterPoint = Point{ To->center().X, From->center().Y }; - auto ToPoint = Point{ From->center().X, FromTop }; + auto FromPoint = Point{ To->Center.X, ToTop }; + auto CenterPoint = Point{ To->Center.X, From->Center.Y }; + auto ToPoint = Point{ From->Center.X, FromTop }; Corners.emplace(NodePair{ To, From }, Corner{ FromPoint, CenterPoint, ToPoint }); } else { revng_assert(!Corners.contains({ From, To })); - auto ToLane = To->center().X; + auto ToLane = To->Center.X; if (auto It = Lanes.Entries.find(To); It != Lanes.Entries.end()) { - auto View = DirectedEdgeView{ From, To, Label->Pointer, false }; + EdgeDestinationView View(From, *Label); revng_assert(It->second.contains(View)); auto EntryIndex = float(It->second.at(View)); @@ -81,7 +82,7 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, auto ToLaneGap = EdgeDistance / 2; if (It->second.size() != 0) { - auto AlternativeGap = To->size().W / 2 / It->second.size(); + auto AlternativeGap = To->Size.W / 2 / It->second.size(); if (AlternativeGap < ToLaneGap) ToLaneGap = AlternativeGap; } @@ -89,8 +90,8 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, ToLane += ToLaneGap * CenteredIndex; } - auto FromPoint = Point{ From->center().X, FromTop }; - auto CenterPoint = Point{ ToLane, From->center().Y }; + auto FromPoint = Point{ From->Center.X, FromTop }; + auto CenterPoint = Point{ ToLane, From->Center.Y }; auto ToPoint = Point{ ToLane, ToTop }; Corners.emplace(NodePair{ From, To }, @@ -108,32 +109,33 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, auto *Second = *std::next(Edge.To->predecessors().begin()); // Make sure there are no self-loops, otherwise it's not a corner. - if (First->Index == Edge.To->Index || Second->Index == Edge.To->Index) + if (First->index() == Edge.To->index() + || Second->index() == Edge.To->index()) continue; - Edge.To->center().X = (First->center().X + Second->center().X) / 2; - Edge.To->center().Y += MarginSize + LaneIndex * EdgeDistance; + Edge.To->Center.X = (First->Center.X + Second->Center.X) / 2; + Edge.To->Center.Y += MarginSize + LaneIndex * EdgeDistance; auto &To = Edge.To; for (auto [From, Label] : To->predecessor_edges()) { - auto FromBottom = From->center().Y - From->size().H / 2; - auto ToBottom = To->center().Y - To->size().H / 2; + auto FromBottom = From->Center.Y - From->Size.H / 2; + auto ToBottom = To->Center.Y - To->Size.H / 2; if (Label->IsBackwards) { revng_assert(!Corners.contains({ To, From })); - auto FromPoint = Point{ To->center().X, ToBottom }; - auto CenterPoint = Point{ From->center().X, To->center().Y }; - auto ToPoint = Point{ From->center().X, FromBottom }; + auto FromPoint = Point{ To->Center.X, ToBottom }; + auto CenterPoint = Point{ From->Center.X, To->Center.Y }; + auto ToPoint = Point{ From->Center.X, FromBottom }; Corners.emplace(NodePair{ To, From }, Corner{ FromPoint, CenterPoint, ToPoint }); } else { revng_assert(!Corners.contains({ From, To })); - auto FromLane = From->center().X; + auto FromLane = From->Center.X; if (auto It = Lanes.Exits.find(From); It != Lanes.Exits.end()) { - auto View = DirectedEdgeView{ From, To, Label->Pointer, false }; + EdgeDestinationView View(To, *Label); revng_assert(It->second.contains(View)); auto ExitIndex = float(It->second.at(View)); @@ -141,7 +143,7 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, auto FromLaneGap = EdgeDistance / 2; if (It->second.size() != 0) { - auto AlternativeGap = From->size().W / 2 / It->second.size(); + auto AlternativeGap = From->Size.W / 2 / It->second.size(); if (AlternativeGap < FromLaneGap) FromLaneGap = AlternativeGap; } @@ -150,8 +152,8 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, } auto FromPoint = Point{ FromLane, FromBottom }; - auto CenterPoint = Point{ FromLane, To->center().Y }; - auto ToPoint = Point{ To->center().X, ToBottom }; + auto CenterPoint = Point{ FromLane, To->Center.Y }; + auto ToPoint = Point{ To->Center.X, ToBottom }; Corners.emplace(NodePair{ From, To }, Corner{ FromPoint, CenterPoint, ToPoint }); @@ -171,33 +173,30 @@ public: CornerContainer &&Prerouted) : Ranks(Ranks), Lanes(Lanes), Prerouted(std::move(Prerouted)) {} - RoutableEdge make(NodeView From, NodeView To, ExternalLabel *Label) { - auto View = DirectedEdgeView{ From, To, Label, false }; + RoutableEdge make(NodeView From, NodeView To, InternalEdge &Label) { + revng_assert(Label.IsBackwards == false); Rank ExitIndex = 0; Rank ExitCount = 1; if (auto It = Lanes.Exits.find(From); It != Lanes.Exits.end()) { - if (It->second.size() != 0) { - revng_assert(It->second.contains(View)); - ExitIndex = It->second.at(View); - ExitCount = It->second.size(); - } + revng_assert(It->second.contains({ To, Label })); + ExitIndex = It->second.at({ To, Label }); + ExitCount = It->second.size(); } Rank EntryIndex = 0; Rank EntryCount = 1; if (auto It = Lanes.Entries.find(To); It != Lanes.Entries.end()) { - if (It->second.size() != 0) { - revng_assert(It->second.contains(View)); - EntryIndex = It->second.at(View); - EntryCount = It->second.size(); - } + revng_assert(It->second.contains({ From, Label })); + EntryIndex = It->second.at({ From, Label }); + EntryCount = It->second.size(); } Rank LaneIndex = 0; if (auto LayerIndex = std::min(Ranks.at(From), Ranks.at(To)); LayerIndex < Lanes.Horizontal.size() && Lanes.Horizontal[LayerIndex].size()) { + EdgeView View(From, To, Label); if (auto Iterator = Lanes.Horizontal[LayerIndex].find(View); Iterator != Lanes.Horizontal[LayerIndex].end()) LaneIndex = Iterator->second; @@ -209,11 +208,11 @@ public: CurrentRoute = std::move(Iterator->second); return RoutableEdge{ - .Label = Label, - .FromCenter = From->center(), - .ToCenter = To->center(), - .FromSize = From->size(), - .ToSize = To->size(), + .Label = &Label, + .FromCenter = From->Center, + .ToCenter = To->Center, + .FromSize = From->Size, + .ToSize = To->Size, .LaneIndex = LaneIndex, .ExitCount = ExitCount, .EntryCount = EntryCount, @@ -229,7 +228,7 @@ private: CornerContainer &&Prerouted; }; -OrderedEdgeContainer orderEdges(InternalGraph &&Graph, +OrderedEdgeContainer orderEdges(InternalGraph &Graph, CornerContainer &&Prerouted, const RankContainer &Ranks, const LaneContainer &Lanes) { @@ -249,64 +248,41 @@ OrderedEdgeContainer orderEdges(InternalGraph &&Graph, OrderedEdgeContainer Result; RoutableEdgeMaker Maker(Ranks, Lanes, std::move(Prerouted)); for (auto *From : Graph.nodes()) { - if (!From->isVirtual()) { + if (!From->IsVirtual) { for (auto [To, Label] : From->successor_edges()) { - revng_assert(Label->IsBackwards == false); - Result.emplace_back(Maker.make(From, To, Label->Pointer)); - if (To->isVirtual()) { + Result.emplace_back(Maker.make(From, To, *Label)); + if (To->IsVirtual) { for (auto *Current : llvm::depth_first(To)) { - if (!Current->isVirtual()) + if (!Current->IsVirtual) break; revng_assert(Current->successorCount() == 1); revng_assert(Current->predecessorCount() == 1 || (Current->predecessorCount() == 2 && Graph.hasEntryNode && Graph.getEntryNode() - && Graph.getEntryNode()->isVirtual())); + && Graph.getEntryNode()->IsVirtual)); auto [Next, NextLabel] = *Current->successor_edges().begin(); - revng_assert(NextLabel->IsBackwards == false); - Result.emplace_back(Maker.make(Current, Next, NextLabel->Pointer)); + Result.emplace_back(Maker.make(Current, Next, *NextLabel)); } } } } } - // Move the graph out of an input parameter so that it gets deleted at - // the end of the scope of this function. - auto GraphOnLocalStack = std::move(Graph); - return Result; } -/// Adds a point to the edge path or replaces its last point based -/// on their coordinates. -template -void appendPoint(PathType &Path, const Point &P) { - if (Path.size() > 1) { - auto &First = *std::prev(std::prev(Path.end())); - auto &Second = *std::prev(Path.end()); - - auto LHS = (P.Y - Second.Y) * (Second.X - First.X); - auto RHS = (Second.Y - First.Y) * (P.X - Second.X); - if (LHS == RHS) - Path.pop_back(); - } - Path.push_back(P); -} - void route(const OrderedEdgeContainer &OrderedListOfEdges, float MarginSize, float EdgeDistance) { for (auto &Edge : OrderedListOfEdges) { - if (Edge.Label->Status == ExternalGraph::EdgeStatus::Hidden) - continue; + revng_assert(Edge.Label->IsRouted == false); if (Edge.Prerouted != std::nullopt) { - appendPoint(Edge.Label->Path, Edge.Prerouted->Start); - appendPoint(Edge.Label->Path, Edge.Prerouted->Center); - appendPoint(Edge.Label->Path, Edge.Prerouted->End); + Edge.Label->appendPoint(Edge.Prerouted->Start); + Edge.Label->appendPoint(Edge.Prerouted->Center); + Edge.Label->appendPoint(Edge.Prerouted->End); } else { // Looking for the lowest point of the edge auto ToUpperEdge = Edge.ToCenter.Y + Edge.ToSize.H / 2, @@ -327,33 +303,29 @@ void route(const OrderedEdgeContainer &OrderedListOfEdges, float ToLane = Edge.ToCenter.X + ToDisplacement, ToTop = Edge.ToCenter.Y + Edge.ToSize.H / 2; - appendPoint(Edge.Label->Path, - Point{ Edge.FromCenter.X + FromDisplacement, - Edge.FromCenter.Y - Edge.FromSize.H / 2 }); - appendPoint(Edge.Label->Path, - Point{ Edge.FromCenter.X + FromDisplacement, Corner }); - appendPoint(Edge.Label->Path, Point{ ToLane, Corner }); - appendPoint(Edge.Label->Path, Point{ ToLane, ToTop }); + Edge.Label->appendPoint(Edge.FromCenter.X + FromDisplacement, + Edge.FromCenter.Y - Edge.FromSize.H / 2); + Edge.Label->appendPoint(Edge.FromCenter.X + FromDisplacement, Corner); + Edge.Label->appendPoint(ToLane, Corner); + Edge.Label->appendPoint(ToLane, ToTop); } - Edge.Label->Status = ExternalGraph::EdgeStatus::Routed; + Edge.Label->IsRouted = true; } } void routeWithStraightLines(const OrderedEdgeContainer &OrderedListOfEdges) { for (auto &Edge : OrderedListOfEdges) { - if (Edge.Label->Status == ExternalGraph::EdgeStatus::Hidden) - continue; + revng_assert(Edge.Label->IsRouted == false); revng_assert(Edge.Prerouted == std::nullopt, "Straight line routing doesn't support prerouted corners"); - appendPoint(Edge.Label->Path, - Point{ Edge.FromCenter.X, - Edge.FromCenter.Y - Edge.FromSize.H / 2 }); - appendPoint(Edge.Label->Path, - Point{ Edge.ToCenter.X, Edge.ToCenter.Y + Edge.ToSize.H / 2 }); + Edge.Label->appendPoint(Edge.FromCenter.X, + Edge.FromCenter.Y - Edge.FromSize.H / 2); + Edge.Label->appendPoint(Edge.ToCenter.X, + Edge.ToCenter.Y + Edge.ToSize.H / 2); - Edge.Label->Status = ExternalGraph::EdgeStatus::Routed; + Edge.Label->IsRouted = true; } } diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/GraphPreparation.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/GraphPreparation.cpp index 9705111f6..c81e8b42e 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/GraphPreparation.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/GraphPreparation.cpp @@ -13,31 +13,13 @@ #include "InternalCompute.h" #include "NodeRanking.h" -// Converts an external graph into an internal one. -static InternalGraph convertToInternal(ExternalGraph &Graph) { - std::unordered_map LookupTable; - - // Add all the "external" nodes to the "internal" graph. - InternalGraph Result; - for (auto *Node : Graph.nodes()) - LookupTable.emplace(Node, Result.addNode(Node)); - - // Add all the "external" edges to the "internal" graph. - for (auto *From : Graph.nodes()) - for (auto [To, Label] : From->successor_edges()) - LookupTable.at(From)->addSuccessor(LookupTable.at(To), - InternalLabel{ Label }); - - return Result; -} - // A simple container that's used to indicate a self-loop. struct SelfLoop { InternalNode *Node; - ExternalLabel *Label; + InternalEdge Edge; - SelfLoop(InternalNode *Node, ExternalLabel *Label) : - Node(Node), Label(Label) {} + SelfLoop(InternalNode *Node, InternalEdge &&Edge) : + Node(Node), Edge(std::move(Edge)) {} }; using SelfLoopContainer = llvm::SmallVector; @@ -48,8 +30,8 @@ static SelfLoopContainer extractSelfLoops(InternalGraph &Graph) { for (auto *Node : Graph.nodes()) { for (auto Iterator = Node->successor_edges().begin(); Iterator != Node->successor_edges().end();) { - if (Iterator->Neighbor->Index == Node->Index) { - Result.emplace_back(Node, Iterator->Label->Pointer); + if (Iterator->Neighbor->index() == Node->index()) { + Result.emplace_back(Node, std::move(*Iterator->Label)); Iterator = Node->removeSuccessor(Iterator); } else { ++Iterator; @@ -79,17 +61,17 @@ ensureSingleEntry(InternalGraph &Graph, RankContainer *MaybeRanks = nullptr) { // this prevents the possibility of chaining virtual entry nodes when this // function is invoked on a slightly-modified graph multiple times. if (Graph.getEntryNode() != nullptr) { - if (Graph.getEntryNode()->isVirtual()) { + if (Graph.getEntryNode()->IsVirtual) { Graph.removeNode(Graph.getEntryNode()); if (MaybeRanks != nullptr) MaybeRanks->erase(Graph.getEntryNode()); } } - auto EntryPoint = Graph.addNode(nullptr); - for (auto *Node : Graph.nodes()) - if (!Node->hasPredecessors() && Node->Index != EntryPoint->Index) - EntryPoint->addSuccessor(Node, nullptr); + InternalNode *EntryPoint = Graph.makeVirtualNode(); + for (InternalNode *Node : Graph.nodes()) + if (!Node->hasPredecessors() && Node->index() != EntryPoint->index()) + Graph.makeEdge(EntryPoint, Node); Graph.setEntryNode(EntryPoint); } } @@ -133,26 +115,25 @@ pickLongEdges(InternalGraph &Graph, const RankContainer &Ranks) { for (auto *From : Graph.nodes()) for (auto [To, Label] : From->successor_edges()) if (delta(From, To, Ranks) > RankDelta(1)) - Result.emplace_back(From, To, Label); + Result.emplace_back(From, To, *Label); return Result; } template -void partition(const std::vector &Edges, +void partition(std::vector &Edges, InternalGraph &Graph, const RankContainer &Ranks, MaybeClassifier &Classifier) { for (auto &Edge : Edges) { size_t PartitionCount = delta(Edge.From, Edge.To, Ranks); + InternalEdge &Label = Edge.label(); auto Current = Edge.From; if (PartitionCount != 0) { for (size_t Partition = 0; Partition < PartitionCount - 1; ++Partition) { - auto NewNode = Graph.addNode(nullptr); - - const InternalLabel &LabelCopy = Edge.label(); - Current->addSuccessor(NewNode, LabelCopy); + auto *NewNode = Graph.makeVirtualNode(); + Current->addSuccessor(NewNode, Graph.makeVirtualEdge(Label)); if (Classifier.has_value()) Classifier->addLongEdgePartition(Current, NewNode); @@ -161,8 +142,7 @@ void partition(const std::vector &Edges, } } - const InternalLabel &LabelCopy = Edge.label(); - Current->addSuccessor(Edge.To, LabelCopy); + Current->addSuccessor(Edge.To, std::move(Label)); if (Classifier.has_value()) Classifier->addLongEdgePartition(Current, Edge.To); @@ -204,17 +184,23 @@ RankContainer partitionLongEdges(InternalGraph &Graph, revng_assert(HasSingleEntryPoint(Graph)); auto Ranks = rankNodes(Graph); - /// A copy of an edge label. - using EdgeCopy = detail::GenericEdgeView; - // Temporary save them outside of the graph. - std::vector SavedLongEdges; + struct SavedEdge : EdgeView { + InternalEdge Label; + + SavedEdge(NodeView From, NodeView To, InternalEdge &&Label) : + EdgeView(From, To, Label), Label(std::move(Label)) {} + + InternalEdge &label() { return Label; } + const InternalEdge &label() const { return Label; } + }; + std::vector SavedLongEdges; for (auto *From : Graph.nodes()) { for (auto Iterator = From->successor_edges_rbegin(); Iterator != From->successor_edges_rend();) { - if (auto [To, Label] = *Iterator; delta(From, To, Ranks) > RankDelta(1)) { - revng_assert(Label != nullptr); - SavedLongEdges.emplace_back(From, To, std::move(*Label)); + if (auto [To, Edge] = *Iterator; delta(From, To, Ranks) > RankDelta(1)) { + revng_assert(Edge != nullptr); + SavedLongEdges.emplace_back(From, To, std::move(*Edge)); Iterator = From->removeSuccessor(Iterator); } else { ++Iterator; @@ -257,7 +243,7 @@ RankContainer partitionLongEdges(InternalGraph &Graph, // Remove an artificial entry node if it was ever added. revng_assert(HasSingleEntryPoint(Graph)); if (Graph.getEntryNode() != nullptr) { - if (Graph.getEntryNode()->isVirtual()) { + if (Graph.getEntryNode()->IsVirtual) { Ranks.erase(Graph.getEntryNode()); Graph.removeNode(Graph.getEntryNode()); } @@ -275,19 +261,23 @@ void partitionArtificialBackwardsEdges(InternalGraph &Graph, auto *From = *std::next(Graph.nodes().begin(), NodeIndex); for (auto EdgeIterator = From->successor_edges_rbegin(); EdgeIterator != From->successor_edges_rend();) { - auto [To, Label] = *EdgeIterator; - if (From->isVirtual() != To->isVirtual() && Label->IsBackwards == true) { - auto *LabelPointer = Label->Pointer; + auto [To, Original] = *EdgeIterator; + if (From->IsVirtual != To->IsVirtual && Original->IsBackwards == true) { + // Move the label out, so that the original edge can be deleted right + // away. If this is not done, inserting new edges might cause + // the reallocation of the underlying vector - leading to invalidation + // of `EdgeIterator`. + InternalEdge Label = std::move(*Original); EdgeIterator = From->removeSuccessor(EdgeIterator); + auto *NewNode1 = Graph.makeVirtualNode(); + auto *NewNode2 = Graph.makeVirtualNode(); - auto *NewNode1 = Graph.addNode(nullptr); - auto *NewNode2 = Graph.addNode(nullptr); - - if (From->isVirtual() && !To->isVirtual()) { - // Fix the low point of a backwards edge - From->addSuccessor(NewNode2, InternalLabel(LabelPointer, true)); - NewNode2->addSuccessor(NewNode1, InternalLabel(LabelPointer, true)); - To->addSuccessor(NewNode1, InternalLabel(LabelPointer, false)); + if (From->IsVirtual && !To->IsVirtual) { + // Make sure the "low" corner of the backwards facing edge looks good + // by splitting it into three nodes building a "v"-shape. + To->addSuccessor(NewNode1, Graph.makeVirtualEdge(Label, false)); + NewNode2->addSuccessor(NewNode1, Graph.makeVirtualEdge(Label, true)); + From->addSuccessor(NewNode2, std::move(Label)); if (Classifier.has_value()) { Classifier->addBackwardsEdgePartition(From, NewNode2); @@ -298,10 +288,11 @@ void partitionArtificialBackwardsEdges(InternalGraph &Graph, Ranks[NewNode1] = Ranks.at(To) + 1; Ranks[NewNode2] = Ranks.at(To); } else { - // Fix the high point of a backwards edge - NewNode1->addSuccessor(From, InternalLabel(LabelPointer, false)); - NewNode1->addSuccessor(NewNode2, InternalLabel(LabelPointer, true)); - NewNode2->addSuccessor(To, InternalLabel(LabelPointer, true)); + // Make sure the "high" corner of the backwards facing edge looks good + // by splitting it into three nodes building a "v"-shape. + NewNode1->addSuccessor(From, Graph.makeVirtualEdge(Label, false)); + NewNode1->addSuccessor(NewNode2, Graph.makeVirtualEdge(Label, true)); + NewNode2->addSuccessor(To, std::move(Label)); if (Classifier.has_value()) { Classifier->addBackwardsEdgePartition(NewNode1, From); @@ -327,22 +318,22 @@ void partitionOriginalBackwardsEdges(InternalGraph &Graph, auto *From = *std::next(Graph.nodes().begin(), NodeIndex); for (auto EdgeIterator = From->successor_edges_rbegin(); EdgeIterator != From->successor_edges_rend();) { - auto [To, Label] = *EdgeIterator; - if (!From->isVirtual() && !To->isVirtual() - && Label->IsBackwards == true) { - auto *LabelPointer = Label->Pointer; + auto [To, Original] = *EdgeIterator; + if (!From->IsVirtual && !To->IsVirtual && Original->IsBackwards == true) { + InternalEdge Label = std::move(*Original); EdgeIterator = From->removeSuccessor(EdgeIterator); - auto *NewNode1 = Graph.addNode(nullptr); - auto *NewNode2 = Graph.addNode(nullptr); - auto *NewNode3 = Graph.addNode(nullptr); - auto *NewNode4 = Graph.addNode(nullptr); + auto *NewNode1 = Graph.makeVirtualNode(); + auto *NewNode2 = Graph.makeVirtualNode(); + auto *NewNode3 = Graph.makeVirtualNode(); + auto *NewNode4 = Graph.makeVirtualNode(); - NewNode1->addSuccessor(From, InternalLabel(LabelPointer, false)); - NewNode1->addSuccessor(NewNode2, InternalLabel(LabelPointer, true)); - NewNode2->addSuccessor(NewNode3, InternalLabel(LabelPointer, true)); - NewNode3->addSuccessor(NewNode4, InternalLabel(LabelPointer, true)); - To->addSuccessor(NewNode4, InternalLabel(LabelPointer, false)); + Label.IsBackwards = false; + NewNode1->addSuccessor(From, Graph.makeVirtualEdge(Label, false)); + NewNode1->addSuccessor(NewNode2, Graph.makeVirtualEdge(Label, true)); + NewNode2->addSuccessor(NewNode3, Graph.makeVirtualEdge(Label, true)); + NewNode3->addSuccessor(NewNode4, Graph.makeVirtualEdge(Label, true)); + To->addSuccessor(NewNode4, std::move(Label)); if (Classifier.has_value()) { Classifier->addBackwardsEdgePartition(NewNode1, From); @@ -368,45 +359,38 @@ void partitionSelfLoops(InternalGraph &Graph, RankContainer &Ranks, SelfLoopContainer &SelfLoops, MaybeClassifier &Classifier) { - for (auto &Edge : SelfLoops) { - auto *NewNode1 = Graph.addNode(nullptr); - auto *NewNode2 = Graph.addNode(nullptr); - auto *NewNode3 = Graph.addNode(nullptr); + for (auto &&[Node, Edge] : SelfLoops) { + auto *NewNode1 = Graph.makeVirtualNode(); + auto *NewNode2 = Graph.makeVirtualNode(); + auto *NewNode3 = Graph.makeVirtualNode(); - NewNode1->addSuccessor(Edge.Node, InternalLabel(Edge.Label, false)); - NewNode1->addSuccessor(NewNode2, InternalLabel(Edge.Label, true)); - NewNode2->addSuccessor(NewNode3, InternalLabel(Edge.Label, true)); - Edge.Node->addSuccessor(NewNode3, InternalLabel(Edge.Label, false)); + Edge.IsBackwards = false; + NewNode1->addSuccessor(Node, Graph.makeVirtualEdge(Edge, false)); + NewNode1->addSuccessor(NewNode2, Graph.makeVirtualEdge(Edge, true)); + NewNode2->addSuccessor(NewNode3, Graph.makeVirtualEdge(Edge, true)); + Node->addSuccessor(NewNode3, std::move(Edge)); if (Classifier.has_value()) { - Classifier->addBackwardsEdgePartition(NewNode1, Edge.Node); + Classifier->addBackwardsEdgePartition(NewNode1, Node); Classifier->addBackwardsEdgePartition(NewNode1, NewNode2); Classifier->addBackwardsEdgePartition(NewNode2, NewNode3); - Classifier->addBackwardsEdgePartition(Edge.Node, NewNode3); + Classifier->addBackwardsEdgePartition(Node, NewNode3); } - Ranks[NewNode1] = Ranks.at(Edge.Node) - 1; - Ranks[NewNode2] = Ranks.at(Edge.Node); - Ranks[NewNode3] = Ranks.at(Edge.Node) + 1; + Ranks[NewNode1] = Ranks.at(Node) - 1; + Ranks[NewNode2] = Ranks.at(Node); + Ranks[NewNode3] = Ranks.at(Node) + 1; } } -// clang-format off template -std::tuple> -prepareGraph(ExternalGraph &Graph, bool ShouldOmitClassification) { - // clang-format on - - // Get the internal representation of the graph. - InternalGraph Result = convertToInternal(Graph); - +std::tuple> +prepareGraph(InternalGraph &Graph, bool ShouldOmitClassification) { // Temporarily remove self-loops from the graph. - auto SelfLoops = extractSelfLoops(Result); + auto SelfLoops = extractSelfLoops(Graph); // Temporarily reverse some of the edges so the graph doesn't contain loops. - convertToDAG(Result); + convertToDAG(Graph); // Use a robust node classification to speed the permutation selection up. MaybeClassifier Classifier; @@ -414,31 +398,31 @@ prepareGraph(ExternalGraph &Graph, bool ShouldOmitClassification) { Classifier = NodeClassifier{}; // Split long edges into one rank wide partitions. - auto Ranks = partitionLongEdges(Result, Classifier); + auto Ranks = partitionLongEdges(Graph, Classifier); // Split backwards facing edges created when partitioning the long edges up. - partitionArtificialBackwardsEdges(Result, Ranks, Classifier); + partitionArtificialBackwardsEdges(Graph, Ranks, Classifier); // Split the backwards facing edges from the "external" graph into partitions. - partitionOriginalBackwardsEdges(Result, Ranks, Classifier); + partitionOriginalBackwardsEdges(Graph, Ranks, Classifier); // Add the self-loops back in a partitioned form. - partitionSelfLoops(Result, Ranks, SelfLoops, Classifier); + partitionSelfLoops(Graph, Ranks, SelfLoops, Classifier); - return { std::move(Result), std::move(Ranks), std::move(Classifier) }; + return { std::move(Ranks), std::move(Classifier) }; } template -using RV = std::tuple>; +using ResultTuple = std::tuple>; -template RV -prepareGraph(ExternalGraph &, bool); +template ResultTuple +prepareGraph(InternalGraph &, bool); -template RV -prepareGraph(ExternalGraph &, bool); +template ResultTuple +prepareGraph(InternalGraph &, bool); -template RV -prepareGraph(ExternalGraph &, bool); +template ResultTuple +prepareGraph(InternalGraph &, bool); -template RV -prepareGraph(ExternalGraph &, bool); +template ResultTuple +prepareGraph(InternalGraph &, bool); diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/Helpers.h b/lib/Yield/Support/GraphLayout/SugiyamaStyle/Helpers.h index 541e6f38e..d0fdbf479 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/Helpers.h +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/Helpers.h @@ -14,85 +14,16 @@ using RankingStrategy = yield::layout::sugiyama::RankingStrategy; using Configuration = yield::layout::sugiyama::Configuration; -using ExternalGraph = yield::Graph; -using ExternalNode = ExternalGraph::Node; -using ExternalLabel = ExternalNode::Edge; - using Point = yield::layout::Point; using Size = yield::layout::Size; -using Index = size_t; -using Rank = size_t; -using RankDelta = int64_t; +using Index = std::size_t; +using Rank = std::size_t; +using RankDelta = std::ptrdiff_t; -namespace detail { -/// A simple class for keeping a count. -class SimpleIndexCounter { -public: - Index next() { return Count++; } - -private: - Index Count = 0; -}; -} // namespace detail - -/// An internal node representation. Contains a pointer to an external node, -/// an index and center/size helper members. -struct InternalNodeBase { - using Indexer = detail::SimpleIndexCounter; - - explicit InternalNodeBase(ExternalNode *Node, Indexer &IRef) : - Pointer(Node), Index(IRef.next()), LocalCenter{ 0, 0 }, LocalSize{ 0, 0 } {} - - // NOLINTNEXTLINE(readability-identifier-naming) - auto operator<=>(const InternalNodeBase &Another) const { - return Index <=> Another.Index; - } - - Point ¢er() { return Pointer ? Pointer->Center : LocalCenter; } - Point const ¢er() const { - return Pointer ? Pointer->Center : LocalCenter; - } - Size &size() { return Pointer ? Pointer->Size : LocalSize; } - Size const &size() const { return Pointer ? Pointer->Size : LocalSize; } - - bool isVirtual() const { return Pointer == nullptr; } - -public: - ExternalNode *Pointer; - const Index Index; - -private: - Point LocalCenter; // Used to temporarily store positions of "fake" nodes. - Size LocalSize; // Used to temporarily store size of "fake" nodes. -}; - -/// An internal edge label representation. Contains two pointers to external -/// labels: a forward-facing one and a backward-facing one. -struct InternalLabelBase { -public: - InternalLabelBase(ExternalLabel *Label, bool IsBackwards = false) : - Pointer(Label), IsBackwards(IsBackwards) {} - -public: - ExternalLabel *Pointer = nullptr; - bool IsBackwards = false; -}; - -using InternalNode = MutableEdgeNode; -using InternalLabel = InternalNode::Edge; -class InternalGraph : public GenericGraph { - using Base = GenericGraph; - -public: - template - InternalNode *addNode(Args &&...A) { - return Base::addNode(A..., Indexer); - } - -private: - typename InternalNode::Indexer Indexer; -}; +using InternalGraph = yield::layout::sugiyama::InternalGraph; +using InternalNode = InternalGraph::Node; +using InternalEdge = InternalNode::Edge; /// A wrapper around an `InternalNode` pointer used for comparison overloading. class NodeView { @@ -115,7 +46,8 @@ public: // NOLINTNEXTLINE(readability-identifier-naming) auto operator<=>(const NodeView &Another) const { revng_assert(Pointer != nullptr); - return Pointer->Index <=> Another.Pointer->Index; + revng_assert(Another.Pointer != nullptr); + return Pointer->index() <=> Another.Pointer->index(); } private: @@ -131,59 +63,55 @@ struct hash<::NodeView> { }; } // namespace std -namespace detail { +/// A view onto an edge as a extension of a known node. +/// This is useful as a `second` value for a map where the `first` one +/// is the `From` node. +struct EdgeDestinationView { + NodeView To; + std::size_t EdgeIndex; -/// A generic view onto an edge. It stores views onto the `From` and `To` -/// nodes as well as free information about the edge label. -template -struct GenericEdgeView { -protected: - static constexpr bool OwnsLabel = !std::is_pointer_v>; - using ParamType = std::conditional_t &&, - std::decay_t>; - -public: - NodeView From, To; - LabelType Label; - - GenericEdgeView(NodeView From, NodeView To, ParamType Label) : - From(From), To(To), Label(std::move(Label)) {} + EdgeDestinationView(NodeView To, const InternalEdge &Label) : + To(To), EdgeIndex(Label.index()) {} // NOLINTNEXTLINE(readability-identifier-naming) - auto operator<=>(const GenericEdgeView &) const = default; - - std::remove_pointer_t> &label() { - if constexpr (OwnsLabel) - return Label; - else - return *Label; - } - const std::remove_pointer_t> &label() const { - if constexpr (OwnsLabel) - return Label; - else - return *Label; - } + auto operator<=>(const EdgeDestinationView &) const = default; }; -} // namespace detail +/// A generic view onto an edge. It's the same as the `EdgeDestinationView` +/// but stores the views onto both of the nodes. +struct EdgeView : EdgeDestinationView { +public: + NodeView From; -/// A view onto an edge. It stores `From` and `To` node views as well as a -/// label pointer. -using EdgeView = detail::GenericEdgeView; + EdgeView(NodeView From, NodeView To, const InternalEdge &Label) : + EdgeDestinationView(To, Label), From(From) {} -/// A view onto one of the edge labels. It stores `From` and `To` node views -/// as well as a pointer to an external label. -using DirectionlessEdgeView = detail::GenericEdgeView; + // NOLINTNEXTLINE(readability-identifier-naming) + auto operator<=>(const EdgeView &) const = default; -/// A view onto an edge. It stores `From` and `To` node views, a pointer to -/// an external label and a flag declaring the direction of the edge. -struct DirectedEdgeView : public DirectionlessEdgeView { - bool IsBackwards = false; + InternalEdge &label() { + auto Lambda = [this](auto Edge) { + return Edge.Neighbor == To && Edge.Label->index() == EdgeIndex; + }; + revng_assert(llvm::count_if(From->successor_edges(), Lambda) == 1); - DirectedEdgeView(NodeView From, NodeView To, ParamType L, bool IsBackwards) : - DirectionlessEdgeView(From, To, L), IsBackwards(IsBackwards) {} + auto Iterator = llvm::find_if(From->successor_edges(), Lambda); + revng_assert(Iterator != From->successor_edges().end()); + revng_assert(Iterator->Label != nullptr); + return *Iterator->Label; + } + + const InternalEdge &label() const { + auto Lambda = [this](auto Edge) { + return Edge.Neighbor == To && Edge.Label->index() == EdgeIndex; + }; + revng_assert(llvm::count_if(From->successor_edges(), Lambda) == 1); + + auto Iterator = llvm::find_if(From->successor_edges(), Lambda); + revng_assert(Iterator != From->successor_edges().end()); + revng_assert(Iterator->Label != nullptr); + return *Iterator->Label; + } }; /// An internal data structure used to pass node ranks around. @@ -211,13 +139,13 @@ using LayoutContainer = std::unordered_map; /// used to route edges. struct LaneContainer { /// Stores edges that require a horizontal section grouped by layer. - std::vector> Horizontal; + std::vector> Horizontal; /// Stores edges entering a node groped by the node they enter. - std::unordered_map> Entries; + std::unordered_map> Entries; /// Stores edges leaving a node grouped by the node they leave. - std::unordered_map> Exits; + std::unordered_map> Exits; }; /// An internal data structure used to represent a corner. It stores three @@ -242,7 +170,7 @@ using CornerContainer = std::map; /// for an edge to be routed. This data is usable even after the internal graph /// was destroyed. struct RoutableEdge { - ExternalLabel *Label; + InternalEdge *Label; Point FromCenter, ToCenter; Size FromSize, ToSize; Rank LaneIndex, ExitCount, EntryCount; diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/HorizontalPositions.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/HorizontalPositions.cpp index 5cb546b18..07f8ea908 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/HorizontalPositions.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/HorizontalPositions.cpp @@ -28,7 +28,7 @@ void setHorizontalCoordinates(const LayerContainer &Layers, // Clear the existing coordinates so they don't interfere for (auto Node : Order) - Node->center() = { 0, 0 }; + Node->Center = { 0, 0 }; // First, the minimal horizontal width for each layer is computed. // @@ -38,14 +38,14 @@ void setHorizontalCoordinates(const LayerContainer &Layers, MinimalLayerWidths.resize(Layers.size()); for (size_t Index = 0; Index < Layers.size(); ++Index) for (auto Node : Layers[Index]) - MinimalLayerWidths[Index] += Node->size().W + MarginSize; + MinimalLayerWidths[Index] += Node->Size.W + MarginSize; for (size_t Iteration = 0; Iteration < IterationCount; ++Iteration) { for (auto [Child, Parent] : LinearSegments) - if (Child->center().X > Parent->center().X) - Parent->center().X = Child->center().X; + if (Child->Center.X > Parent->Center.X) + Parent->Center.X = Child->Center.X; else - Child->center().X = Parent->center().X; + Child->Center.X = Parent->Center.X; // NOTE: I feel like this loop can be A LOT simpler. std::map MinimumX; @@ -57,10 +57,10 @@ void setHorizontalCoordinates(const LayerContainer &Layers, Iterator = MinimumX.insert(Iterator, { LayerIndex, Offset }); } - auto LeftX = Iterator->second + Node->size().W / 2 + MarginSize; - if (LeftX > Node->center().X) - Node->center().X = LeftX; - Iterator->second = Node->center().X + Node->size().W / 2 + MarginSize; + auto LeftX = Iterator->second + Node->Size.W / 2 + MarginSize; + if (LeftX > Node->Center.X) + Node->Center.X = LeftX; + Iterator->second = Node->Center.X + Node->Size.W / 2 + MarginSize; } } @@ -85,7 +85,7 @@ void setHorizontalCoordinates(const LayerContainer &Layers, if (ChildLayerSize == ChildPosition.Index + 1) { auto Iterator = FreeSegments.lower_bound(Parent); if (Iterator == FreeSegments.end() - || Iterator->first->Index > Parent->Index) + || Iterator->first->index() > Parent->index()) FreeSegments.insert(Iterator, { Parent, true }); } else { FreeSegments[Parent] = false; @@ -96,7 +96,7 @@ void setHorizontalCoordinates(const LayerContainer &Layers, for (size_t Index = 0; Index < Layers.size(); ++Index) { if (Layers[Index].size() != 0) { for (auto Node : Layers[Index]) - Barycenters[Index] += Node->center().X; + Barycenters[Index] += Node->Center.X; Barycenters[Index] /= Layers[Index].size(); } } @@ -106,12 +106,12 @@ void setHorizontalCoordinates(const LayerContainer &Layers, for (size_t J = 0; J < Layers[Index].size() - 1; ++J) { auto Current = Layers[Index][J]; auto Next = Layers[Index][J + 1]; - double RightMargin = Next->center().X - Next->size().W / 2 - - MarginSize * 2 - Current->size().W / 2; + double RightMargin = Next->Center.X - Next->Size.W / 2 + - MarginSize * 2 - Current->Size.W / 2; auto Parent = LinearSegments.at(Current); auto Iterator = RightSegments.lower_bound(Parent); if (Iterator == RightSegments.end() - || Iterator->first->Index > Parent->Index) + || Iterator->first->index() > Parent->index()) RightSegments.insert(Iterator, { Parent, RightMargin }); else if (RightMargin < Iterator->second) Iterator->second = RightMargin; @@ -122,7 +122,7 @@ void setHorizontalCoordinates(const LayerContainer &Layers, // Any coordinate is safe for the rightmost node, but the barycenters // of both layers should be taken into the account, so that // the layout does not become skewed - float RightMargin = Layers[Index].back()->center().X; + float RightMargin = Layers[Index].back()->Center.X; if (Index + 1 < Layers.size()) if (auto Dlt = Barycenters[Index + 1] - Barycenters[Index]; Dlt > 0) RightMargin += Dlt; @@ -130,7 +130,7 @@ void setHorizontalCoordinates(const LayerContainer &Layers, auto Parent = LinearSegments.at(Layers[Index].back()); auto Iterator = RightSegments.lower_bound(Parent); if (Iterator == RightSegments.end() - || Iterator->first->Index > Parent->Index) + || Iterator->first->index() > Parent->index()) RightSegments.insert(Iterator, { Parent, RightMargin }); else if (FreeSegments.at(Parent)) { if (RightMargin > Iterator->second) @@ -148,21 +148,21 @@ void setHorizontalCoordinates(const LayerContainer &Layers, continue; // First move the rightmost node. - if (auto Node = Layers[Index].back(); !Node->isVirtual()) { + if (auto Node = Layers[Index].back(); !Node->IsVirtual) { if (Node->successorCount() > 0) { double Barycenter = 0, TotalWeight = 0; for (auto *Next : Node->successors()) { - float Weight = Next->isVirtual() ? 1 : VirtualNodeWeight; + float Weight = Next->IsVirtual ? 1 : VirtualNodeWeight; // This weight as an arbitrary number used to help the algorithm // prioritize putting a node closer to its non-virtual successors. - Barycenter += Next->center().X / Weight; + Barycenter += Next->Center.X / Weight; TotalWeight += 1.f / Weight; } Barycenter /= TotalWeight; - if (Barycenter > Node->center().X) - Node->center().X = Barycenter; + if (Barycenter > Node->Center.X) + Node->Center.X = Barycenter; } } @@ -175,11 +175,11 @@ void setHorizontalCoordinates(const LayerContainer &Layers, double Barycenter = 0; double TotalWeight = 0; for (auto *Next : Node->successors()) { - float Weight = Next->isVirtual() ? 1 : VirtualNodeWeight; + float Weight = Next->IsVirtual ? 1 : VirtualNodeWeight; // This weight as an arbitrary number used to help the algorithm // prioritize putting a node closer to its non-virtual successors. - Barycenter += Next->center().X / Weight; + Barycenter += Next->Center.X / Weight; TotalWeight += 1.f / Weight; } Barycenter /= TotalWeight; @@ -188,8 +188,8 @@ void setHorizontalCoordinates(const LayerContainer &Layers, LayerMargin = Barycenter; } - if (LayerMargin > Node->center().X) - Node->center().X = LayerMargin; + if (LayerMargin > Node->Center.X) + Node->Center.X = LayerMargin; } } } @@ -300,7 +300,7 @@ void setStaticOffsetHorizontalCoordinates(const LayerContainer &Layers, // coordinate. CurrentSubtree.ActualPosition = CurrentPosition; auto Width = CurrentSubtree.LogicalWidth; - NodeView->center().X = (Width / 2 + CurrentPosition) * MarginSize; + NodeView->Center.X = (Width / 2 + CurrentPosition) * MarginSize; // Update the current position, so that no sibling tree occupies the same // space. diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/InternalCompute.h b/lib/Yield/Support/GraphLayout/SugiyamaStyle/InternalCompute.h index 3fe83fb0a..23ed2aae1 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/InternalCompute.h +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/InternalCompute.h @@ -9,8 +9,8 @@ /// Prepares the graph for further processing. template -std::tuple> -prepareGraph(ExternalGraph &Graph, bool OmitClassification); +std::tuple> +prepareGraph(InternalGraph &Graph, bool OmitClassification); /// Approximates an optimal permutation selection. template @@ -81,8 +81,8 @@ CornerContainer routeBackwardsCorners(InternalGraph &Graph, float MarginSize, float EdgeDistance); -/// Consumes a DAG to produce the optimal routing order. -OrderedEdgeContainer orderEdges(InternalGraph &&Graph, +/// Consumes a Graph to produce the optimal routing order. +OrderedEdgeContainer orderEdges(InternalGraph &Graph, CornerContainer &&Prerouted, const RankContainer &Ranks, const LaneContainer &Lanes); @@ -97,7 +97,7 @@ void routeWithStraightLines(const OrderedEdgeContainer &OrderedListOfEdges); /// /// \note: it only works with `MutableEdgeNode`s. template -bool computeInternal(ExternalGraph &Graph, const Configuration &Configuration) { +bool computeInternal(InternalGraph &Graph, const Configuration &Configuration) { static_assert(StrictSpecializationOfMutableEdgeNode, "LayouterSugiyama requires mutable edge nodes."); @@ -112,7 +112,7 @@ bool computeInternal(ExternalGraph &Graph, const Configuration &Configuration) { // long edges and backwards facing edges are split up into into chunks // that span at most one layer at a time. bool ShouldClassify = !Configuration.UseSimpleTreeOptimization; - auto [DAG, Ranks, Classified] = prepareGraph(Graph, !ShouldClassify); + auto [Ranks, Classified] = prepareGraph(Graph, !ShouldClassify); // Try to select an optimal node permutation per layer. // NOTE: since this is the part with the highest complexity, it needs extra @@ -120,19 +120,19 @@ bool computeInternal(ExternalGraph &Graph, const Configuration &Configuration) { // Maybe we should consider something more optimal instead of a simple hill // climbing algorithm. auto Layers = Configuration.UseSimpleTreeOptimization ? - selectSimpleTreePermutation(DAG, Ranks) : - selectPermutation(DAG, Ranks, *Classified); + selectSimpleTreePermutation(Graph, Ranks) : + selectPermutation(Graph, Ranks, *Classified); // Compute an augmented topological ordering of the nodes of the graph. - auto Order = extractAugmentedTopologicalOrder(DAG, Layers); + auto Order = extractAugmentedTopologicalOrder(Graph, Layers); // Decide on which segments of the graph can be made linear, e.g. each edge // within the same linear segment is a straight line. SegmentContainer LinearSegments; if (Configuration.PreserveLinearSegments) - LinearSegments = selectLinearSegments(DAG, Ranks, Layers, Order); + LinearSegments = selectLinearSegments(Graph, Ranks, Layers, Order); else - LinearSegments = emptyLinearSegments(DAG); + LinearSegments = emptyLinearSegments(Graph); // Finalize the logical positions for each of the nodes. const auto Final = convertToLayout(Layers); @@ -151,7 +151,7 @@ bool computeInternal(ExternalGraph &Graph, const Configuration &Configuration) { } // Distribute edge lanes in a way that minimizes the number of crossings. - auto Lanes = assignLanes(DAG, LinearSegments, Final); + auto Lanes = assignLanes(Graph, LinearSegments, Final); // Set the rest of the coordinates. Node layouting is complete after this. const auto &EdgeGap = Configuration.EdgeMarginSize; @@ -160,13 +160,13 @@ bool computeInternal(ExternalGraph &Graph, const Configuration &Configuration) { // Route edges forming backwards facing corners. CornerContainer Prerouted; if (Configuration.UseOrthogonalBends) - Prerouted = routeBackwardsCorners(DAG, Ranks, Lanes, Margin, EdgeGap); + Prerouted = routeBackwardsCorners(Graph, Ranks, Lanes, Margin, EdgeGap); - // Now that the corners are routed, the DAG representation is not needed + // Now that the corners are routed, the Graph representation is not needed // anymore, both the graph and the routed corners get consumed to construct // an ordered list of edges with all the information necessary for them // to get routed (see `OrderedEdgeContainer`). - auto Edges = orderEdges(std::move(DAG), std::move(Prerouted), Ranks, Lanes); + auto Edges = orderEdges(Graph, std::move(Prerouted), Ranks, Lanes); // Route the edges. if (Configuration.UseOrthogonalBends) diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/LaneDistribution.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/LaneDistribution.cpp index df810ade3..8f85285db 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/LaneDistribution.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/LaneDistribution.cpp @@ -7,70 +7,75 @@ #include "InternalCompute.h" -/// Detects whether Edge faces left, right or neither. -/// \note: Legacy function, could possibly be merged into its only user. -static auto getFacingDirection(const DirectedEdgeView &Edge) { - auto &LHS = Edge.To->center().X; - auto &RHS = Edge.From->center().X; - return (LHS == RHS ? 0 : (Edge.IsBackwards ? 1 : -1) * (LHS > RHS ? 1 : -1)); -} +struct SortableEdge { +private: + EdgeView Edge; + bool IsFacingRight; -/// Returns `true` if `Edge` is facing left-to-right, `false` otherwise. -static bool facesRight(const DirectedEdgeView &Edge) { - return getFacingDirection(Edge) > 0; -} + static bool + isFacingRight(NodeView From, NodeView To, const InternalEdge &Label) { + if (To->Center.X == From->Center.X) + return false; -/// Compares two edges. This is used as a comparator for horizontal edge lane -/// sorting. The direction of an edge is the most most important characteristic -/// since we want to split "left-to-right" edges from "right-to-left" ones - -/// that helps to minimize the number of crossings. -/// When directions are the same, edges are sorted based on the horizontal -/// coordinates of their ends (the edge that needs to go further is placed -/// closer to the outside of the lane section). -static bool compareHorizontalLanes(const DirectedEdgeView &LHS, - const DirectedEdgeView &RHS) { - bool LHSFacesRight = facesRight(LHS); - bool RHSFacesRight = facesRight(RHS); - if (LHSFacesRight == RHSFacesRight) { - auto LHSFromX = LHS.From->center().X; - auto RHSFromX = RHS.From->center().X; - auto LHSToX = LHS.To->center().X; - auto RHSToX = RHS.To->center().X; - auto [LHSMin, LHSMax] = std::minmax(LHSFromX, LHSToX); - auto [RHSMin, RHSMax] = std::minmax(RHSFromX, RHSToX); - - if (LHSFacesRight) - return LHSMax == RHSMax ? LHSMin < RHSMin : LHSMax < RHSMax; - else - return LHSMax == RHSMax ? LHSMin > RHSMin : LHSMax > RHSMax; - } else { - return LHSFacesRight < RHSFacesRight; + return Label.IsBackwards != (From->Center.X > To->Center.X); } -} -/// Returns the entry node of an edge taking into account whether the edge was -/// reversed or not. -static NodeView getEntry(const DirectedEdgeView &Edge) { - return Edge.IsBackwards ? Edge.To : Edge.From; -} +public: + SortableEdge(NodeView From, NodeView To, const InternalEdge &Label) : + Edge(From, To, Label), IsFacingRight(isFacingRight(From, To, Label)) {} -/// Returns the exit node of an edge taking into account whether the edge was -/// reversed or not. -static NodeView getExit(const DirectedEdgeView &Edge) { - return Edge.IsBackwards ? Edge.From : Edge.To; -} + const EdgeView &view() const { return Edge; } + EdgeView &&view() { return std::move(Edge); } + + /// Compares two edges. This is used as a comparator for horizontal edge lane + /// sorting. The direction of an edge is the most most important + /// characteristic since we want to split "left-to-right" edges from + /// "right-to-left" ones - that helps to minimize the number of crossings. + /// When directions are the same, edges are sorted based on the horizontal + /// coordinates of their ends (the edge that needs to go further is placed + /// closer to the outside of the lane section). + bool operator<(const SortableEdge &Another) const { + if (IsFacingRight == Another.IsFacingRight) { + // Both edges face in the same direction. + auto [LHSMin, LHSMax] = std::minmax(Edge.From->Center.X, + Edge.To->Center.X); + auto [RHSMin, RHSMax] = std::minmax(Another.Edge.From->Center.X, + Another.Edge.To->Center.X); + + if (IsFacingRight) + return LHSMax == RHSMax ? LHSMin < RHSMin : LHSMax < RHSMax; + else + return LHSMax == RHSMax ? LHSMin > RHSMin : LHSMax > RHSMax; + } else { + // Both edges face in different directions. + return IsFacingRight < Another.IsFacingRight; + } + } +}; + +struct EdgeDestination { + NodeView Neighbor; + InternalEdge *Label; + + EdgeDestination(NodeView Neighbor, InternalEdge &Label) : + Neighbor(Neighbor), Label(&Label) {} + + EdgeDestinationView view() const { + return EdgeDestinationView(Neighbor, *Label); + } +}; LaneContainer assignLanes(InternalGraph &Graph, const SegmentContainer &LinearSegments, const LayoutContainer &Layout) { // Stores edges that require a horizontal section grouped by the layer rank. - std::vector> Horizontal; + std::vector> Horizontal; // Stores edges entering a node grouped by the node they enter. - std::unordered_map> Entries; + std::unordered_map> Entries; // Stores edges leaving a node grouped by the node they leave. - std::unordered_map> Exits; + std::unordered_map> Exits; // Calculate the number of lanes needed for each layer for (auto *From : Graph.nodes()) { @@ -78,48 +83,57 @@ LaneContainer assignLanes(InternalGraph &Graph, // If the ends of an edge are not a part of the same linear segment or // their horizontal coordinates are not aligned, a bend is necessary. if (LinearSegments.at(From) != LinearSegments.at(To) - || From->center().X != To->center().X) { + || From->Center.X != To->Center.X) { auto LayerIndex = std::min(Layout.at(From).Layer, Layout.at(To).Layer); if (LayerIndex >= Horizontal.size()) Horizontal.resize(LayerIndex + 1); - if (Label->IsBackwards) { - Horizontal[LayerIndex].emplace_back(To, From, Label->Pointer, true); - Entries[From].emplace_back(To, From, Label->Pointer, true); - Exits[To].emplace_back(To, From, Label->Pointer, true); - } else { - Horizontal[LayerIndex].emplace_back(From, To, Label->Pointer, false); - Entries[To].emplace_back(From, To, Label->Pointer, false); - Exits[From].emplace_back(From, To, Label->Pointer, false); - } + NodeView Entry = Label->IsBackwards ? To : From; + NodeView Exit = Label->IsBackwards ? From : To; + Horizontal[LayerIndex].emplace_back(Entry, Exit, *Label); + Entries[Exit].emplace_back(Entry, *Label); + Exits[Entry].emplace_back(Exit, *Label); } } } LaneContainer Result; - // Sort edges when they leave nodes - auto ExitComparator = [&Layout](const DirectedEdgeView &LHS, - const DirectedEdgeView &RHS) { - return Layout.at(getExit(LHS)).Index < Layout.at(getExit(RHS)).Index; + // Define a comparator used for sorting entries and exits. + struct Comparator { + const LayoutContainer &Layout; + NodeView FromNode; + + bool + operator()(const EdgeDestination &LHS, const EdgeDestination &RHS) const { + const auto &Left = LHS.Label->IsBackwards ? FromNode : LHS.Neighbor; + const auto &Right = RHS.Label->IsBackwards ? FromNode : RHS.Neighbor; + return Layout.at(Left).Index < Layout.at(Right).Index; + } }; - for (auto &[Node, Edges] : Exits) { - std::sort(Edges.begin(), Edges.end(), ExitComparator); + + // Sort edges when they leave nodes + for (auto &[Node, Neighbors] : Exits) { + std::sort(Neighbors.begin(), Neighbors.end(), Comparator{ Layout, Node }); + auto &NodeExits = Result.Exits[Node]; - for (size_t ExitRank = 0; ExitRank < Edges.size(); ExitRank++) - NodeExits.try_emplace(Edges[ExitRank], ExitRank); + for (size_t ExitRank = 0; ExitRank < Neighbors.size(); ExitRank++) { + auto [_, Success] = NodeExits.try_emplace(Neighbors[ExitRank].view(), + ExitRank); + revng_assert(Success); + } } // Sort edges where they enter nodes - auto EntryComparator = [&Layout](const DirectedEdgeView &LHS, - const DirectedEdgeView &RHS) { - return Layout.at(getEntry(LHS)).Index < Layout.at(getEntry(RHS)).Index; - }; - for (auto &[Node, Edges] : Entries) { - std::sort(Edges.begin(), Edges.end(), EntryComparator); + for (auto &[Node, Neighbors] : Entries) { + std::sort(Neighbors.begin(), Neighbors.end(), Comparator{ Layout, Node }); + auto &NodeEntries = Result.Entries[Node]; - for (size_t EntryRank = 0; EntryRank < Edges.size(); EntryRank++) - NodeEntries.try_emplace(Edges[EntryRank], EntryRank); + for (size_t EntryRank = 0; EntryRank < Neighbors.size(); EntryRank++) { + auto [_, Success] = NodeEntries.try_emplace(Neighbors[EntryRank].view(), + EntryRank); + revng_assert(Success); + } } // Sort horizontal lanes @@ -131,9 +145,9 @@ LaneContainer assignLanes(InternalGraph &Graph, // // "left-to-right" edges need to be layered from the closest to the most // distant one, while "right-to-left" edges - in the opposite order. - std::sort(CurrentLane.begin(), CurrentLane.end(), compareHorizontalLanes); + std::sort(CurrentLane.begin(), CurrentLane.end()); for (size_t I = 0; I < CurrentLane.size(); I++) - Result.Horizontal[Index][CurrentLane[I]] = CurrentLane.size() - I; + Result.Horizontal[Index][CurrentLane[I].view()] = CurrentLane.size() - I; } return Result; diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/NodeRanking.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/NodeRanking.cpp index d8d034f8a..4cafd1d0b 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/NodeRanking.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/NodeRanking.cpp @@ -189,7 +189,7 @@ RankContainer rankNodes(InternalGraph &Graph) { RankContainer &updateRanks(InternalGraph &Graph, RankContainer &Ranks) { Ranks.try_emplace(Graph.getEntryNode(), - Graph.getEntryNode()->isVirtual() ? Rank(-1) : Rank(0)); + Graph.getEntryNode()->IsVirtual ? Rank(-1) : Rank(0)); for (auto *Current : llvm::ReversePostOrderTraversal(Graph.getEntryNode())) { auto &CurrentRank = Ranks[Current]; diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/PermutationSelection.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/PermutationSelection.cpp index 37aa6dc53..f03b866f8 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/PermutationSelection.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/PermutationSelection.cpp @@ -32,7 +32,7 @@ optimizeLayers(InternalGraph &Graph, RankContainer &Ranks) { // // Such nodes always have a single predecessor and a single successor. // Additionally, the ranks of those two neighbors have to be different. - if (!Node->isVirtual()) { + if (!Node->IsVirtual) { IsLayerRequired = true; break; } @@ -305,7 +305,7 @@ public: if (int A = Cluster(LHS), B = Cluster(RHS); A == B) { auto BarycenterA = get(LHS), BarycenterB = get(RHS); if (std::isnan(BarycenterA) || std::isnan(BarycenterB)) - return LHS->Index < RHS->Index; + return LHS->index() < RHS->index(); else return BarycenterA < BarycenterB; } else { diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/TopologicalOrdering.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/TopologicalOrdering.cpp index 11a020444..ab79fbb72 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/TopologicalOrdering.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/TopologicalOrdering.cpp @@ -22,20 +22,20 @@ extractAugmentedTopologicalOrder(InternalGraph &Graph, std::unordered_map LookupTable; for (auto *Node : Graph.nodes()) { auto NewNode = Augmented.addNode(Node); - LookupTable.emplace(Node->Index, NewNode); + LookupTable.emplace(Node->index(), NewNode); } // Add original edges in. for (auto *From : Graph.nodes()) for (auto *To : From->successors()) - LookupTable.at(From->Index)->addSuccessor(LookupTable.at(To->Index)); + LookupTable.at(From->index())->addSuccessor(LookupTable.at(To->index())); // Add extra edges. for (size_t Layer = 0; Layer < Layers.size(); ++Layer) { for (size_t From = 0; From < Layers[Layer].size(); ++From) { for (size_t To = From + 1; To < Layers[Layer].size(); ++To) { - auto *FromNode = LookupTable.at(Layers[Layer][From]->Index); - auto *ToNode = LookupTable.at(Layers[Layer][To]->Index); + auto *FromNode = LookupTable.at(Layers[Layer][From]->index()); + auto *ToNode = LookupTable.at(Layers[Layer][To]->index()); FromNode->addSuccessor(ToNode); } } diff --git a/lib/Yield/Support/GraphLayout/SugiyamaStyle/VerticalPositions.cpp b/lib/Yield/Support/GraphLayout/SugiyamaStyle/VerticalPositions.cpp index 06420fb43..bae793559 100644 --- a/lib/Yield/Support/GraphLayout/SugiyamaStyle/VerticalPositions.cpp +++ b/lib/Yield/Support/GraphLayout/SugiyamaStyle/VerticalPositions.cpp @@ -15,10 +15,10 @@ void setVerticalCoordinates(const LayerContainer &Layers, for (size_t Index = 0; Index < Layers.size(); ++Index) { float MaxHeight = 0; for (auto Node : Layers[Index]) { - auto NodeHeight = Node->size().H; + auto NodeHeight = Node->Size.H; if (MaxHeight < NodeHeight) MaxHeight = NodeHeight; - Node->center().Y = LastY - Node->size().H / 2; + Node->Center.Y = LastY - Node->Size.H / 2; } auto LaneCount = Index < Lanes.Horizontal.size() ?