Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Control flow graph (CFG) implementation
|
|
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
|
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
|
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Control flow graph (CFG) implementation
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
|
|
|
|
#include "V3Cfg.h"
|
|
|
|
|
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3EmitV.h"
|
|
|
|
|
|
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2025-08-25 14:47:45 +02:00
|
|
|
// CfgBlock method definitions
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
|
2025-08-25 14:47:45 +02:00
|
|
|
std::string CfgBlock::name() const {
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << "BB " + std::to_string(id()) + ":\n";
|
|
|
|
|
for (AstNode* nodep : m_stmtps) {
|
|
|
|
|
if (const AstIf* const ifp = VN_CAST(nodep, If)) {
|
|
|
|
|
ss << "if (";
|
|
|
|
|
V3EmitV::debugVerilogForTree(ifp->condp(), ss);
|
|
|
|
|
ss << ") ...";
|
|
|
|
|
} else if (const AstWhile* const whilep = VN_CAST(nodep, While)) {
|
|
|
|
|
ss << "while (";
|
|
|
|
|
V3EmitV::debugVerilogForTree(whilep->condp(), ss);
|
|
|
|
|
ss << ") ...";
|
|
|
|
|
} else {
|
|
|
|
|
V3EmitV::debugVerilogForTree(nodep, ss);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-25 14:47:45 +02:00
|
|
|
std::string text = VString::replaceSubstr(
|
|
|
|
|
VString::replaceSubstr(ss.str(), "\n", "\\l "), "\"", "\\\"");
|
|
|
|
|
if (isEnter()) text = "**ENTER**\n" + text;
|
|
|
|
|
if (isExit()) text = text + "\n**EXIT**";
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
return text;
|
|
|
|
|
}
|
2025-08-25 14:47:45 +02:00
|
|
|
std::string CfgBlock::dotShape() const { return "rect"; }
|
|
|
|
|
std::string CfgBlock::dotRank() const {
|
|
|
|
|
if (isEnter()) return "source";
|
|
|
|
|
if (isExit()) return "sink";
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2025-08-25 14:47:45 +02:00
|
|
|
// CfgEdge method definitions
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
|
2025-08-25 14:47:45 +02:00
|
|
|
std::string CfgEdge::dotLabel() const {
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
std::string label = "E" + std::to_string(id());
|
2025-08-25 14:47:45 +02:00
|
|
|
const CfgEdge* const untknp = srcp()->untknEdgep();
|
Optimize complex combinational logic in DFG (#6298)
This patch adds DfgLogic, which is a vertex that represents a whole,
arbitrarily complex combinational AstAlways or AstAssignW in the
DfgGraph.
Implementing this requires computing the variables live at entry to the
AstAlways (variables read by the block), so there is a new
ControlFlowGraph data structure and a classical data-flow analysis based
live variable analysis to do that at the variable level (as opposed to
bit/element level).
The actual CFG construction and live variable analysis is best effort,
and might fail for currently unhandled constructs or data types. This
can be extended later.
V3DfgAstToDfg is changed to convert the Ast into an initial DfgGraph
containing only DfgLogic, DfgVertexSplice and DfgVertexVar vertices.
The DfgLogic are then subsequently synthesized into primitive operations
by the new V3DfgSynthesize pass, which is a combination of the old
V3DfgAstToDfg conversion and new code to handle AstAlways blocks with
complex flow control.
V3DfgSynthesize by default will synthesize roughly the same constructs
as V3DfgAstToDfg used to handle before, plus any logic that is part of a
combinational cycle within the DfgGraph. This enables breaking up these
cycles, for which there are extensions to V3DfgBreakCycles in this patch
as well. V3DfgSynthesize will then delete all non synthesized or non
synthesizable DfgLogic vertices and the rest of the Dfg pipeline is
identical, with minor changes to adjust for the changed representation.
Because with this change we can now eliminate many more UNOPTFLAT, DFG
has been disabled in all the tests that specifically target testing the
scheduling and reporting of circular combinational logic.
2025-08-19 16:06:38 +02:00
|
|
|
if (this == untknp) {
|
|
|
|
|
label += " / F";
|
|
|
|
|
} else if (untknp) {
|
|
|
|
|
label += " / T";
|
|
|
|
|
}
|
|
|
|
|
return label;
|
|
|
|
|
}
|
2025-08-25 14:47:45 +02:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// CfgGraph method definitions
|
|
|
|
|
|
|
|
|
|
static void cfgOrderVisitBlock(std::vector<CfgBlock*>& postOrderEnumeration, CfgBlock* bbp) {
|
|
|
|
|
// Mark visited
|
|
|
|
|
bbp->user(1);
|
|
|
|
|
// Visit un-visited successors
|
|
|
|
|
if (CfgBlock* const takenp = bbp->takenp()) {
|
|
|
|
|
if (!takenp->user()) cfgOrderVisitBlock(postOrderEnumeration, takenp);
|
|
|
|
|
if (CfgBlock* const untknp = bbp->untknp()) {
|
|
|
|
|
if (!untknp->user()) cfgOrderVisitBlock(postOrderEnumeration, untknp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Add to post order enumeration
|
|
|
|
|
postOrderEnumeration.emplace_back(bbp);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void CfgGraph::rpoBlocks() {
|
|
|
|
|
UASSERT_OBJ(m_nEdits != m_nLastOrdered, m_enterp, "Redundant 'CfgGraph::order' call");
|
|
|
|
|
m_nLastOrdered = m_nEdits;
|
|
|
|
|
|
|
|
|
|
// Reset marks
|
|
|
|
|
for (V3GraphVertex& v : vertices()) v.user(0);
|
|
|
|
|
|
|
|
|
|
// Compute post-order enumeration. Simple recursive algorith will do.
|
|
|
|
|
std::vector<CfgBlock*> postOrderEnumeration;
|
|
|
|
|
postOrderEnumeration.reserve(m_nBlocks);
|
|
|
|
|
cfgOrderVisitBlock(postOrderEnumeration, m_enterp);
|
|
|
|
|
UASSERT_OBJ(postOrderEnumeration.size() == m_nBlocks, m_enterp, "Inconsistent block count");
|
|
|
|
|
|
|
|
|
|
// Assign block IDs equal to the reverse post-order number and sort vertices
|
|
|
|
|
for (size_t i = 0; i < postOrderEnumeration.size(); ++i) {
|
|
|
|
|
CfgBlock* const bbp = postOrderEnumeration[m_nBlocks - 1 - i];
|
|
|
|
|
bbp->m_rpoNumber = i;
|
|
|
|
|
vertices().unlink(bbp);
|
|
|
|
|
vertices().linkBack(bbp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Assign edge IDs
|
|
|
|
|
size_t edgeCount = 0;
|
|
|
|
|
for (V3GraphVertex& v : vertices()) {
|
2025-09-02 17:50:40 +02:00
|
|
|
// cppcheck-suppress constVariableReference // cppcheck is wrong
|
2025-08-25 14:47:45 +02:00
|
|
|
for (V3GraphEdge& e : v.outEdges()) static_cast<CfgEdge&>(e).m_id = edgeCount++;
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(edgeCount == m_nEdges, m_enterp, "Inconsistent edge count");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CfgGraph::containsLoop() const {
|
|
|
|
|
for (const V3GraphVertex& vtx : vertices()) {
|
|
|
|
|
const CfgBlock& current = static_cast<const CfgBlock&>(vtx);
|
|
|
|
|
for (const V3GraphEdge& edge : current.outEdges()) {
|
|
|
|
|
const CfgBlock& successor = *static_cast<const CfgBlock*>(edge.top());
|
|
|
|
|
// IDs are the reverse post-order numbering, so easy to check for a back-edge
|
|
|
|
|
if (successor.id() < current.id()) return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CfgGraph::minimize() {
|
|
|
|
|
// Remove empty blocks (except enter and exit)
|
|
|
|
|
for (V3GraphVertex* const vtxp : vertices().unlinkable()) {
|
|
|
|
|
CfgBlock* const bbp = static_cast<CfgBlock*>(vtxp);
|
|
|
|
|
if (bbp->isEnter()) continue;
|
|
|
|
|
if (bbp->isExit()) continue;
|
|
|
|
|
if (!bbp->stmtps().empty()) continue;
|
|
|
|
|
UASSERT(!bbp->isBranch(), "Empty block should have a single successor");
|
|
|
|
|
CfgBlock* const succp = bbp->takenp();
|
|
|
|
|
for (V3GraphEdge* const edgep : bbp->inEdges().unlinkable()) edgep->relinkTop(succp);
|
|
|
|
|
++m_nEdits;
|
|
|
|
|
--m_nEdges;
|
|
|
|
|
--m_nBlocks;
|
|
|
|
|
VL_DO_DANGLING(bbp->unlinkDelete(this), bbp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Combine sequential blocks
|
|
|
|
|
for (V3GraphVertex* const vtxp : vertices().unlinkable()) {
|
|
|
|
|
CfgBlock* const srcp = static_cast<CfgBlock*>(vtxp);
|
|
|
|
|
if (srcp->isExit()) continue;
|
|
|
|
|
if (srcp->isBranch()) continue;
|
|
|
|
|
CfgBlock* const dstp = srcp->takenp();
|
|
|
|
|
if (dstp->isJoin()) continue;
|
|
|
|
|
// Combine them
|
|
|
|
|
if (srcp->isEnter()) m_enterp = dstp;
|
|
|
|
|
std::vector<AstNodeStmt*> stmtps{std::move(srcp->m_stmtps)};
|
|
|
|
|
stmtps.reserve(stmtps.size() + dstp->m_stmtps.size());
|
|
|
|
|
stmtps.insert(stmtps.end(), dstp->m_stmtps.begin(), dstp->m_stmtps.end());
|
|
|
|
|
dstp->m_stmtps = std::move(stmtps);
|
|
|
|
|
for (V3GraphEdge* const edgep : srcp->inEdges().unlinkable()) edgep->relinkTop(dstp);
|
|
|
|
|
++m_nEdits;
|
|
|
|
|
--m_nEdges;
|
|
|
|
|
--m_nBlocks;
|
|
|
|
|
VL_DO_DANGLING(srcp->unlinkDelete(this), srcp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_nEdits != m_nLastOrdered) rpoBlocks();
|
|
|
|
|
if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("cfg-minimize");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CfgGraph::breakCriticalEdges() {
|
|
|
|
|
// Gather critical edges
|
|
|
|
|
std::vector<CfgEdge*> criticalEdges;
|
|
|
|
|
criticalEdges.reserve(m_nEdges);
|
|
|
|
|
for (V3GraphVertex& vtx : vertices()) {
|
|
|
|
|
const CfgBlock& bb = static_cast<const CfgBlock&>(vtx);
|
|
|
|
|
if (!bb.isBranch()) continue;
|
|
|
|
|
for (V3GraphEdge& edge : vtx.outEdges()) {
|
|
|
|
|
const CfgBlock& succ = static_cast<const CfgBlock&>(*edge.top());
|
|
|
|
|
if (!succ.isJoin()) continue;
|
|
|
|
|
criticalEdges.emplace_back(static_cast<CfgEdge*>(&edge));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Insert blocks
|
|
|
|
|
for (CfgEdge* const edgep : criticalEdges) {
|
|
|
|
|
CfgBlock* const newp = addBlock();
|
|
|
|
|
addTakenEdge(newp, edgep->dstp());
|
|
|
|
|
edgep->relinkTop(newp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_nEdits != m_nLastOrdered) rpoBlocks();
|
|
|
|
|
if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("cfg-breakCriticalEdges");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Given a branching basic block, if the sub-graph below this branch, up until
|
|
|
|
|
// the point where all of its control flow path convertes is series-parallel,
|
|
|
|
|
// then return the (potentially newly created) basic block with exactly 2
|
|
|
|
|
// predecessors where the two control flow paths from this branch have joined.
|
|
|
|
|
// If the relevant sub-graph is not series-parallel (there is a control flow
|
|
|
|
|
// path between the branches, or to a path not dominated by the given branch),
|
|
|
|
|
// then return nullptr. Cached results in the given map
|
|
|
|
|
CfgBlock* CfgGraph::getOrCreateTwoWayJoinFor(CfgBlock* bbp) {
|
|
|
|
|
UASSERT_OBJ(bbp->isBranch(), bbp, "Not a branch");
|
|
|
|
|
|
|
|
|
|
// Mark visited
|
|
|
|
|
UASSERT_OBJ(!bbp->user(), bbp, "Should not visit twice");
|
|
|
|
|
bbp->user(1);
|
|
|
|
|
|
|
|
|
|
// We need the edge converting to a join block along both path. This is how we find it:
|
|
|
|
|
const auto chaseEdge = [&](CfgEdge* edgep) -> CfgEdge* {
|
|
|
|
|
while (true) {
|
|
|
|
|
CfgBlock* dstp = edgep->dstp();
|
|
|
|
|
// Stop if found the joining block along this path
|
|
|
|
|
if (dstp->isJoin()) return edgep;
|
|
|
|
|
// If the successor is a branch, recursively get it's 2-way join block
|
|
|
|
|
while (dstp->isBranch()) {
|
|
|
|
|
dstp = getOrCreateTwoWayJoinFor(dstp);
|
|
|
|
|
// If the subgarph below dstp is not series-parallel, then no solution
|
|
|
|
|
if (!dstp) return nullptr;
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(!dstp->isExit(), bbp, "Non-convergent branch - multiple Exit blocks?");
|
|
|
|
|
edgep = dstp->takenEdgep();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Walk down both paths
|
|
|
|
|
CfgEdge* const takenEdgep = chaseEdge(bbp->takenEdgep());
|
|
|
|
|
if (!takenEdgep) return nullptr;
|
|
|
|
|
CfgEdge* const untknEdgep = chaseEdge(bbp->untknEdgep());
|
|
|
|
|
if (!untknEdgep) return nullptr;
|
|
|
|
|
// If we ended up at different joining blocks, then there is a path from one
|
|
|
|
|
// of the branches into a path of another branch before 'bbp', no solution
|
|
|
|
|
if (takenEdgep->dstp() != untknEdgep->dstp()) return nullptr;
|
|
|
|
|
|
|
|
|
|
// Pick up the common successor
|
|
|
|
|
CfgBlock* const succp = takenEdgep->dstp();
|
|
|
|
|
// If the common successor is a 2-way join, we can use it directly
|
|
|
|
|
if (succp->isTwoWayJoin()) return succp;
|
|
|
|
|
// Otherwise insert a new block to join the 2 paths of the original block
|
|
|
|
|
CfgBlock* const joinp = addBlock();
|
|
|
|
|
addTakenEdge(joinp, succp);
|
|
|
|
|
takenEdgep->relinkTop(joinp);
|
|
|
|
|
untknEdgep->relinkTop(joinp);
|
|
|
|
|
return joinp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool CfgGraph::insertTwoWayJoins() {
|
|
|
|
|
// Reset marks
|
|
|
|
|
for (V3GraphVertex& v : vertices()) v.user(0);
|
|
|
|
|
|
|
|
|
|
bool isSeriesParallel = true;
|
|
|
|
|
|
|
|
|
|
// We will be adding vertices at the end. That's OK, they don't need to be visited again
|
|
|
|
|
for (V3GraphVertex& v : vertices()) {
|
|
|
|
|
CfgBlock& bb = static_cast<CfgBlock&>(v);
|
|
|
|
|
// Skip if already visited
|
|
|
|
|
if (bb.user()) continue;
|
|
|
|
|
// Skip if not a branch
|
|
|
|
|
if (!bb.isBranch()) continue;
|
|
|
|
|
// Fix it up, record if failed
|
|
|
|
|
if (!getOrCreateTwoWayJoinFor(&bb)) isSeriesParallel = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_nEdits != m_nLastOrdered) rpoBlocks();
|
|
|
|
|
if (dumpGraphLevel() >= 9) dumpDotFilePrefixed("cfg-insertTwoWayJoins");
|
|
|
|
|
|
|
|
|
|
return isSeriesParallel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// CfgDominatorTree
|
|
|
|
|
|
|
|
|
|
const CfgBlock* CfgDominatorTree::intersect(const CfgBlock* ap, const CfgBlock* bp) {
|
|
|
|
|
while (ap != bp) {
|
|
|
|
|
while (*ap > *bp) ap = m_bb2Idom[*ap];
|
|
|
|
|
while (*bp > *ap) bp = m_bb2Idom[*bp];
|
|
|
|
|
}
|
|
|
|
|
return ap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CfgDominatorTree::CfgDominatorTree(const CfgGraph& cfg)
|
|
|
|
|
: m_bb2Idom{cfg.makeBlockMap<const CfgBlock*>()} {
|
|
|
|
|
|
|
|
|
|
// Build the immediate dominator map, using algorithm from:
|
|
|
|
|
// "A Simple, Fast Dominance Algorithm", Keith D. Cooper et al., 2006
|
|
|
|
|
// Immediate dominator of the enter block
|
|
|
|
|
|
|
|
|
|
// Point enteer block to itself, while computing below
|
|
|
|
|
m_bb2Idom[cfg.enter()] = &cfg.enter();
|
|
|
|
|
// Iterate until settled
|
|
|
|
|
for (bool changed = true; changed;) {
|
|
|
|
|
changed = false;
|
|
|
|
|
// For each vertex except enter block
|
|
|
|
|
for (const V3GraphVertex& vtx : cfg.vertices()) {
|
|
|
|
|
const CfgBlock& curr = static_cast<const CfgBlock&>(vtx);
|
|
|
|
|
if (curr.isEnter()) continue; // Skip entry block
|
|
|
|
|
|
|
|
|
|
// For each predecessor of current block
|
|
|
|
|
const CfgBlock* idom = nullptr;
|
|
|
|
|
for (const V3GraphEdge& edge : curr.inEdges()) {
|
|
|
|
|
const CfgBlock& pred = static_cast<const CfgBlock&>(*edge.fromp());
|
|
|
|
|
// Skip if perdecessor not yet processed
|
|
|
|
|
if (!m_bb2Idom[pred]) continue;
|
|
|
|
|
// Pick first, then use intersect
|
|
|
|
|
idom = !idom ? &pred : intersect(&pred, idom);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If chenged, record it, else move on
|
|
|
|
|
if (idom == m_bb2Idom[curr]) continue;
|
|
|
|
|
m_bb2Idom[curr] = idom;
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The enter block is the root of the tree and does not itself have an immediate dominator
|
|
|
|
|
m_bb2Idom[cfg.enter()] = nullptr;
|
|
|
|
|
}
|