574 lines
22 KiB
C++
574 lines
22 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Control flow graph (CFG)
|
|
//
|
|
// 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) and related data structures.
|
|
//
|
|
// A control flow graph is a directed graph with with basic blocks as
|
|
// vertices connected by control flow edges. A basic block is a sequence
|
|
// of statements that are always executed as a unit (there are no branches
|
|
// targeting the middle of a basic block). The last statement in a basic
|
|
// block is called the terminator statemet. The terminator statements are
|
|
// the control flow transfer statements (branches) in the program. In this
|
|
// implementation, an unconditoinal jump (goto) is implicit and not stored
|
|
// in the basic block. A consequence of the implicit jump is that a basic
|
|
// block might be empty (not contain any statements). Conditional branches
|
|
// are represented by storing the corresponding conditional AstNodeStmt as
|
|
// the last statement in the basic block. Most importantly, only the actual
|
|
// condition check (e.g.: the 'condp' part of an AstIf) and branch is
|
|
// executed in the host basic block, but not any of the body statements of
|
|
// the conditoinal. The control flow graph has 2 unique basic blocks. The
|
|
// 'enter' block is the unique entry point of the represented program. It
|
|
// has no predecessors and itself dominates all other basic blocks. The
|
|
// 'exit' block is the unique exit point. It has no successors, and itself
|
|
// post-dominates all other basic blocks.
|
|
//
|
|
// The current implementation is designed to be immutable after
|
|
// construction. This can be relaxed in the future if necessary, however
|
|
// it is unlikely to be necessary as we cannot as of now convert a control
|
|
// flow graph back to Ast or any other form of output. We can also only
|
|
// represent 2-way conditionals, therefore all basic blocks have up to 2
|
|
// successors. The exit block has 0, blocks terminated by an unconditional
|
|
// implicit jump have exactly 1, and blocks terminated by a conditional
|
|
// branch have exactly 2 successors.
|
|
//
|
|
// Basic blocks have a unique ID, these are assigned based on the reverse
|
|
// post-ordering of the basic blocks within the control flow graph, and
|
|
// therefore they define a topological ordering of the basic blocks. The
|
|
// basic blocks within the graph are stored in this order. This means that
|
|
// in control flow graphs without back edges (loops), a basic block is
|
|
// always stored after its predecessors.
|
|
//
|
|
//*************************************************************************
|
|
|
|
#ifndef VERILATOR_V3CFG_H_
|
|
#define VERILATOR_V3CFG_H_
|
|
|
|
#include "config_build.h"
|
|
#include "verilatedos.h"
|
|
|
|
#include "V3Ast.h"
|
|
#include "V3Graph.h"
|
|
|
|
#include <functional>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
//######################################################################
|
|
// Control Flow Graph (CFG) and related data structures
|
|
|
|
class CfgGraph;
|
|
class CfgBlock;
|
|
class CfgEdge;
|
|
|
|
template <typename T_Key, typename T_Value>
|
|
class CfgMap;
|
|
template <typename T_Value>
|
|
class CfgBlockMap;
|
|
template <typename T_Value>
|
|
class CfgEdgeMap;
|
|
|
|
//######################################################################
|
|
// CfgBlock - A basic block (verticies of the control flow graph)
|
|
|
|
class CfgBlock final : public V3GraphVertex {
|
|
friend class CfgGraph;
|
|
template <typename T_Key, typename T_Value>
|
|
friend class CfgMap;
|
|
friend class CfgBuilder;
|
|
|
|
// STATE
|
|
CfgGraph* const m_cfgp; // The control flow graph this CfgBlock is under
|
|
size_t m_rpoNumber = 0; // Reverse post-order number and unique ID of this CfgBlock
|
|
|
|
// V3GraphEdge::user() is set to the unique id by CfgBuilder
|
|
std::vector<AstNodeStmt*> m_stmtps; // statements in this CfgBlock
|
|
|
|
// PRIVATE METHODS
|
|
|
|
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
|
inline explicit CfgBlock(CfgGraph* cfgp);
|
|
~CfgBlock() override = default;
|
|
|
|
public:
|
|
// PUBLIC METHODS
|
|
|
|
// ID (reverse post-order number) of this block
|
|
inline size_t id();
|
|
inline size_t id() const;
|
|
|
|
// Is this the entry block of the CFG?
|
|
bool isEnter() const { return inEmpty(); }
|
|
// Is this the exit block of the CFG?
|
|
bool isExit() const { return outEmpty(); }
|
|
// Is this a branching block (multiple successors)?
|
|
bool isBranch() const { return outEdges().hasMultipleElements(); }
|
|
// Is this a control flow convergence block (multiple predecessors)?
|
|
bool isJoin() const { return inEdges().hasMultipleElements(); }
|
|
// Is this a join of exactly 2 paths?
|
|
bool isTwoWayJoin() const { return inEdges().hasTwoElements(); }
|
|
|
|
// The edge going to the taken (or uncinditional) successor, or nullptr if exit block
|
|
inline CfgEdge* takenEdgep();
|
|
inline const CfgEdge* takenEdgep() const;
|
|
// The edge going to the untaken successor, or nullptr if not a branch, or exit block
|
|
inline CfgEdge* untknEdgep();
|
|
inline const CfgEdge* untknEdgep() const;
|
|
// The taken successor block, or nullptr if exit block
|
|
inline CfgBlock* takenp();
|
|
inline const CfgBlock* takenp() const;
|
|
// The untakens successor block, or nullptr if not a branch, or exit block
|
|
inline CfgBlock* untknp();
|
|
inline const CfgBlock* untknp() const;
|
|
|
|
// The first predecessor edge, or nullptr if enter block
|
|
inline CfgEdge* firstPredecessorEdgep();
|
|
inline const CfgEdge* firstPredecessorEdgep() const;
|
|
// The last predecessor edge, or nullptr if enter block
|
|
inline CfgEdge* lastPredecessorEdgep();
|
|
inline const CfgEdge* lastPredecessorEdgep() const;
|
|
// The first predecessor block, or nullptr if enter block
|
|
inline CfgBlock* firstPredecessorp();
|
|
inline const CfgBlock* firstPredecessorp() const;
|
|
// The last predecessor block, or nullptr if enter block
|
|
inline CfgBlock* lastPredecessorp();
|
|
inline const CfgBlock* lastPredecessorp() const;
|
|
|
|
// Statements in this CfgBlock
|
|
const std::vector<AstNodeStmt*>& stmtps() const { return m_stmtps; }
|
|
|
|
// Ordering is done on reverese post-order numbering. For loop-free graph
|
|
// this ensures that a block that compares less than another is not a
|
|
// successor of the other block (it is an ancestor, or sibling).
|
|
bool operator<(const CfgBlock& that) const { return id() < that.id(); }
|
|
bool operator>(const CfgBlock& that) const { return id() > that.id(); }
|
|
bool operator==(const CfgBlock& that) const { return this == &that; }
|
|
|
|
// Iterators
|
|
void forEachSuccessor(std::function<void(const CfgBlock&)> f) const {
|
|
for (const V3GraphEdge& edge : outEdges()) f(*static_cast<CfgBlock*>(edge.top()));
|
|
}
|
|
void forEachPredecessor(std::function<void(const CfgBlock&)> f) const {
|
|
for (const V3GraphEdge& edge : inEdges()) f(*static_cast<CfgBlock*>(edge.fromp()));
|
|
}
|
|
|
|
// Source location for debugging
|
|
FileLine* fileline() const override {
|
|
return !m_stmtps.empty() ? m_stmtps.front()->fileline() : nullptr;
|
|
}
|
|
|
|
// For Graphviz dumps only
|
|
std::string name() const override;
|
|
std::string dotShape() const override;
|
|
std::string dotRank() const override;
|
|
};
|
|
|
|
//######################################################################
|
|
// CfgEdge - An edges of the control flow graph
|
|
|
|
class CfgEdge final : public V3GraphEdge {
|
|
friend class CfgGraph;
|
|
template <typename T_Key, typename T_Value>
|
|
friend class CfgMap;
|
|
|
|
// STATE - Immutable after construction, set by CfgBuilder
|
|
CfgGraph* const m_cfgp; // The control flow graph this CfgEdge is under
|
|
size_t m_id = 0; // Unique ID of this vertex
|
|
|
|
// PRIVATE METHODS
|
|
|
|
// CONSTRUCTOR/DESTRUCTOR - via CfgGraph only
|
|
inline CfgEdge(CfgGraph* graphp, CfgBlock* srcp, CfgBlock* dstp);
|
|
~CfgEdge() override = default;
|
|
|
|
public:
|
|
// METHODS
|
|
|
|
// Unique ID of this CfgEdge - no particular meaning
|
|
inline size_t id();
|
|
inline size_t id() const;
|
|
|
|
// Source/destination CfgBlock
|
|
const CfgBlock* srcp() const { return static_cast<const CfgBlock*>(fromp()); }
|
|
const CfgBlock* dstp() const { return static_cast<const CfgBlock*>(top()); }
|
|
CfgBlock* srcp() { return static_cast<CfgBlock*>(fromp()); }
|
|
CfgBlock* dstp() { return static_cast<CfgBlock*>(top()); }
|
|
|
|
// For Graphviz dumps only
|
|
std::string dotLabel() const override;
|
|
};
|
|
|
|
//######################################################################
|
|
// CfgGraph - The control flow graph
|
|
|
|
class CfgGraph final : public V3Graph {
|
|
friend class CfgBlock;
|
|
friend class CfgEdge;
|
|
template <typename T_Key, typename T_Value>
|
|
friend class CfgMap;
|
|
friend class CfgBuilder;
|
|
|
|
// STATE
|
|
size_t m_nEdits = 0; // Edit count of this graph
|
|
size_t m_nLastOrdered = 0; // Last edit count blocks were ordered
|
|
CfgBlock* m_enterp = nullptr; // The singular entry vertex
|
|
CfgBlock* m_exitp = nullptr; // The singular exit vertex
|
|
size_t m_nBlocks = 0; // Number of CfgBlocks in this CfgGraph
|
|
size_t m_nEdges = 0; // Number of CfgEdges in this CfgGraph
|
|
|
|
// PRIVATE METHODS
|
|
|
|
// Compute reverse post-order enumeration of blocks, and sort them
|
|
// accordingly. Assign blocks, and edge IDs. Invalidates all previous IDs.
|
|
void rpoBlocks();
|
|
|
|
// Add a new CfgBlock to this graph
|
|
CfgBlock* addBlock() {
|
|
++m_nEdits;
|
|
++m_nBlocks;
|
|
return new CfgBlock{this};
|
|
}
|
|
|
|
// Add a new taken (or unconditional) CfgEdge to this CFG
|
|
void addTakenEdge(CfgBlock* srcp, CfgBlock* dstp) {
|
|
UASSERT_OBJ(srcp->m_cfgp == this, srcp, "'srcp' is not in this graph");
|
|
UASSERT_OBJ(dstp->m_cfgp == this, dstp, "'dstp' is not in this graph");
|
|
UASSERT_OBJ(!srcp->takenEdgep(), srcp, "Taken edge should be added first");
|
|
//
|
|
UASSERT_OBJ(dstp != m_enterp, dstp, "Enter block cannot have a predecessor");
|
|
UASSERT_OBJ(srcp != m_exitp, srcp, "Exit block cannot have a successor");
|
|
++m_nEdits;
|
|
++m_nEdges;
|
|
new CfgEdge{this, srcp, dstp};
|
|
}
|
|
|
|
// Add a new untaken CfgEdge to this CFG
|
|
void addUntknEdge(CfgBlock* srcp, CfgBlock* dstp) {
|
|
UASSERT_OBJ(srcp->m_cfgp == this, srcp, "'srcp' is not in this graph");
|
|
UASSERT_OBJ(dstp->m_cfgp == this, dstp, "'dstp' is not in this graph");
|
|
UASSERT_OBJ(srcp->takenEdgep(), srcp, "Untaken edge shold be added second");
|
|
UASSERT_OBJ(srcp->takenp() != dstp, srcp, "Untaken branch targets the same block");
|
|
//
|
|
UASSERT_OBJ(dstp != m_enterp, dstp, "Enter block cannot have a predecessor");
|
|
UASSERT_OBJ(srcp != m_exitp, srcp, "Exit block cannot have a successor");
|
|
++m_nEdits;
|
|
++m_nEdges;
|
|
new CfgEdge{this, srcp, dstp};
|
|
}
|
|
|
|
size_t idOf(const CfgBlock* bbp) const {
|
|
UASSERT_OBJ(m_nEdits == m_nLastOrdered, m_enterp, "Cfg was edited but not re-ordered");
|
|
return bbp->m_rpoNumber;
|
|
}
|
|
|
|
size_t idOf(const CfgEdge* edgep) const {
|
|
UASSERT_OBJ(m_nEdits == m_nLastOrdered, m_enterp, "Cfg was edited but not re-ordered");
|
|
return edgep->m_id;
|
|
}
|
|
|
|
// Implementation for insertTwoWayJoins
|
|
CfgBlock* getOrCreateTwoWayJoinFor(CfgBlock* bbp);
|
|
|
|
// CONSTRUCTOR - use CfgGraph::build, which might fail, so this can't be public
|
|
CfgGraph() = default;
|
|
|
|
public:
|
|
~CfgGraph() override = default;
|
|
|
|
// STATIC FUNCTIONS
|
|
|
|
// Build CFG for the given list of statements
|
|
static std::unique_ptr<CfgGraph> build(AstNode* stmtsp);
|
|
|
|
// PUBLIC METHODS
|
|
|
|
// Accessors
|
|
const CfgBlock& enter() const { return *m_enterp; }
|
|
const CfgBlock& exit() const { return *m_exitp; }
|
|
|
|
// Number of basic blocks in this graph
|
|
size_t nBlocks() const { return m_nBlocks; }
|
|
// Number of control flow edges in this graph
|
|
size_t nEdges() const { return m_nEdges; }
|
|
|
|
// Create a CfgBlock map for this graph
|
|
template <typename T_Value>
|
|
inline CfgBlockMap<T_Value> makeBlockMap() const;
|
|
// Create a CfgEdgeMap map for this graph
|
|
template <typename T_Value>
|
|
inline CfgEdgeMap<T_Value> makeEdgeMap() const;
|
|
|
|
// Returns true iff the graph contains a loop (back-edge)
|
|
bool containsLoop() const;
|
|
|
|
//------------------------------------------------------------------
|
|
// The following methods mutate this CFG and invalidate CfgBlock and
|
|
// CfgEdge IDs and associated CfgBlockMap, CfgEdgeMap and other
|
|
// query instances.
|
|
|
|
// Remove empty blocks, combine sequential blocks. Keeps the enter/exit block, even if empty.
|
|
void minimize();
|
|
|
|
// Insert empty blocks to fix critical edges (edges that have a source with
|
|
// multiple successors, and a destination with multiple predecessors)
|
|
void breakCriticalEdges();
|
|
|
|
bool insertTwoWayJoins();
|
|
};
|
|
|
|
//######################################################################
|
|
// CfgMap - Map from CfgBlock or CfgEdge to generic values
|
|
|
|
template <typename T_Key, typename T_Value>
|
|
class CfgMap VL_NOT_FINAL {
|
|
// As opposed to a std::map/std::unordered_map, all entries are value
|
|
// initialized at construction, and the map is always dense. This can
|
|
// be improved if necessary but is sufficient for our current purposes.
|
|
|
|
const CfgGraph* m_cfgp; // The control flow graph this map is for
|
|
size_t m_created; // Edit count of CFG this map was created at
|
|
std::vector<T_Value> m_map; // The map, stored as a vector
|
|
|
|
protected:
|
|
// CONSTRUCTOR
|
|
explicit CfgMap(const CfgGraph* cfgp, size_t size)
|
|
: m_cfgp{cfgp}
|
|
, m_created{cfgp->m_nEdits}
|
|
, m_map{size} {}
|
|
|
|
public:
|
|
// Can create an empty map
|
|
CfgMap()
|
|
: m_cfgp{nullptr}
|
|
, m_created{0} {}
|
|
|
|
// Copyable, movable
|
|
CfgMap(const CfgMap<T_Key, T_Value>&) = default;
|
|
CfgMap(CfgMap<T_Key, T_Value>&&) = default;
|
|
CfgMap<T_Key, T_Value>& operator=(const CfgMap<T_Key, T_Value>&) = default;
|
|
CfgMap<T_Key, T_Value>& operator=(CfgMap<T_Key, T_Value>&&) = default;
|
|
|
|
T_Value& operator[](const T_Key& key) {
|
|
UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale");
|
|
UASSERT_OBJ(m_cfgp == key.m_cfgp, m_cfgp->m_enterp, "Key not in this CFG");
|
|
return m_map.at(key.id());
|
|
}
|
|
const T_Value& operator[](const T_Key& key) const {
|
|
UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale");
|
|
UASSERT_OBJ(m_cfgp == key.m_cfgp, m_cfgp->m_enterp, "Key not in this CFG");
|
|
return m_map.at(key.id());
|
|
}
|
|
T_Value& operator[](const T_Key* keyp) {
|
|
UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale");
|
|
UASSERT_OBJ(m_cfgp == keyp->m_cfgp, m_cfgp->m_enterp, "Key not in this CFG");
|
|
return m_map.at(keyp->id());
|
|
}
|
|
const T_Value& operator[](const T_Key* keyp) const {
|
|
UASSERT_OBJ(m_created == m_cfgp->m_nEdits, m_cfgp->m_enterp, "Map is stale");
|
|
UASSERT_OBJ(m_cfgp == keyp->m_cfgp, m_cfgp->m_enterp, "Key not in this CFG");
|
|
return m_map.at(keyp->id());
|
|
}
|
|
};
|
|
|
|
template <typename T_Value>
|
|
class CfgBlockMap final : public CfgMap<CfgBlock, T_Value> {
|
|
friend class CfgGraph;
|
|
// CONSTRUCTOR - Create one via CfgGraph::makeBlockMap
|
|
explicit CfgBlockMap(const CfgGraph* cfgp)
|
|
: CfgMap<CfgBlock, T_Value>{cfgp, cfgp->nBlocks()} {}
|
|
|
|
public:
|
|
// Can create an empty map
|
|
CfgBlockMap() = default;
|
|
// Copyable, movable
|
|
CfgBlockMap(const CfgBlockMap&) = default;
|
|
CfgBlockMap(CfgBlockMap&&) = default;
|
|
CfgBlockMap& operator=(const CfgBlockMap&) = default;
|
|
CfgBlockMap& operator=(CfgBlockMap&&) = default;
|
|
};
|
|
|
|
template <typename T_Value>
|
|
class CfgEdgeMap final : public CfgMap<CfgEdge, T_Value> {
|
|
friend class CfgGraph;
|
|
// CONSTRUCTOR - Create one via CfgGraph::makeEdgeMap
|
|
explicit CfgEdgeMap(const CfgGraph* cfgp)
|
|
: CfgMap<CfgEdge, T_Value>{cfgp, cfgp->nEdges()} {}
|
|
|
|
public:
|
|
// Can create an empty map
|
|
CfgEdgeMap() = default;
|
|
// Copyable, movable
|
|
CfgEdgeMap(const CfgEdgeMap&) = default;
|
|
CfgEdgeMap(CfgEdgeMap&&) = default;
|
|
CfgEdgeMap& operator=(const CfgEdgeMap&) = default;
|
|
CfgEdgeMap& operator=(CfgEdgeMap&&) = default;
|
|
};
|
|
|
|
//######################################################################
|
|
// Innline method definitions
|
|
|
|
// --- CfgBlock ---
|
|
|
|
CfgBlock::CfgBlock(CfgGraph* cfgp)
|
|
: V3GraphVertex{cfgp}
|
|
, m_cfgp{cfgp} {}
|
|
|
|
size_t CfgBlock::id() { return m_cfgp->idOf(this); }
|
|
size_t CfgBlock::id() const { return m_cfgp->idOf(this); }
|
|
|
|
// Successor edges
|
|
CfgEdge* CfgBlock::takenEdgep() { // It's always the first edge
|
|
return isExit() ? nullptr : static_cast<CfgEdge*>(outEdges().frontp());
|
|
}
|
|
const CfgEdge* CfgBlock::takenEdgep() const { // It's always the first edge
|
|
return isExit() ? nullptr : static_cast<const CfgEdge*>(outEdges().frontp());
|
|
}
|
|
CfgEdge* CfgBlock::untknEdgep() { // It's always the second (last) edge
|
|
return isBranch() ? static_cast<CfgEdge*>(outEdges().backp()) : nullptr;
|
|
}
|
|
const CfgEdge* CfgBlock::untknEdgep() const { // It's always the second (last) edge
|
|
return isBranch() ? static_cast<const CfgEdge*>(outEdges().backp()) : nullptr;
|
|
}
|
|
CfgBlock* CfgBlock::takenp() {
|
|
return isExit() ? nullptr : static_cast<CfgBlock*>(outEdges().frontp()->top());
|
|
}
|
|
const CfgBlock* CfgBlock::takenp() const {
|
|
return isExit() ? nullptr : static_cast<CfgBlock*>(outEdges().frontp()->top());
|
|
}
|
|
CfgBlock* CfgBlock::untknp() {
|
|
return isBranch() ? static_cast<CfgBlock*>(outEdges().backp()->top()) : nullptr;
|
|
}
|
|
const CfgBlock* CfgBlock::untknp() const {
|
|
return isBranch() ? static_cast<const CfgBlock*>(outEdges().backp()->top()) : nullptr;
|
|
}
|
|
|
|
// Predecessor edges
|
|
CfgEdge* CfgBlock::firstPredecessorEdgep() { //
|
|
return static_cast<CfgEdge*>(inEdges().frontp());
|
|
}
|
|
const CfgEdge* CfgBlock::firstPredecessorEdgep() const { //
|
|
return static_cast<const CfgEdge*>(inEdges().frontp());
|
|
}
|
|
CfgEdge* CfgBlock::lastPredecessorEdgep() { //
|
|
return static_cast<CfgEdge*>(inEdges().backp());
|
|
}
|
|
const CfgEdge* CfgBlock::lastPredecessorEdgep() const { //
|
|
return static_cast<const CfgEdge*>(inEdges().backp());
|
|
}
|
|
CfgBlock* CfgBlock::firstPredecessorp() { //
|
|
return isEnter() ? nullptr : firstPredecessorEdgep()->srcp();
|
|
}
|
|
const CfgBlock* CfgBlock::firstPredecessorp() const { //
|
|
return isEnter() ? nullptr : firstPredecessorEdgep()->srcp();
|
|
}
|
|
CfgBlock* CfgBlock::lastPredecessorp() { //
|
|
return isEnter() ? nullptr : lastPredecessorEdgep()->srcp();
|
|
}
|
|
const CfgBlock* CfgBlock::lastPredecessorp() const { //
|
|
return isEnter() ? nullptr : lastPredecessorEdgep()->srcp();
|
|
}
|
|
|
|
// --- CfgEdge ---
|
|
|
|
CfgEdge::CfgEdge(CfgGraph* cfgp, CfgBlock* srcp, CfgBlock* dstp)
|
|
: V3GraphEdge{cfgp, srcp, dstp, 1, false}
|
|
, m_cfgp{cfgp} {}
|
|
|
|
size_t CfgEdge::id() { return m_cfgp->idOf(this); }
|
|
size_t CfgEdge::id() const { return m_cfgp->idOf(this); }
|
|
|
|
// --- CfgGraph ---
|
|
|
|
template <typename T_Value>
|
|
CfgBlockMap<T_Value> CfgGraph::makeBlockMap() const {
|
|
return CfgBlockMap<T_Value>{this};
|
|
}
|
|
template <typename T_Value>
|
|
CfgEdgeMap<T_Value> CfgGraph::makeEdgeMap() const {
|
|
return CfgEdgeMap<T_Value>{this};
|
|
}
|
|
|
|
//######################################################################
|
|
// CfgDominatorTree
|
|
|
|
class CfgDominatorTree final {
|
|
// STATE
|
|
CfgBlockMap<const CfgBlock*> m_bb2Idom; // Map from CfgBlock to its immediate dominator
|
|
|
|
// PRIVATE METHODS
|
|
|
|
// Part of algorithm to compute m_bb2Idom, see consructor
|
|
const CfgBlock* intersect(const CfgBlock* ap, const CfgBlock* bp);
|
|
|
|
public:
|
|
// CONSTRUCTOR
|
|
explicit CfgDominatorTree(const CfgGraph& cfg);
|
|
// Can create an empty map
|
|
CfgDominatorTree() = default;
|
|
// Copyable, movable
|
|
CfgDominatorTree(const CfgDominatorTree&) = default;
|
|
CfgDominatorTree(CfgDominatorTree&&) = default;
|
|
CfgDominatorTree& operator=(const CfgDominatorTree&) = default;
|
|
CfgDominatorTree& operator=(CfgDominatorTree&&) = default;
|
|
|
|
// PUBLIC METHODS
|
|
|
|
// Return unique CfgBlock that dominates both of the given blocks, but does
|
|
// not strictly dominate any other block that dominates both blocks. It
|
|
// will return 'ap' or 'bp' if one dominates the other (or are the same)
|
|
const CfgBlock* closestCommonDominator(const CfgBlock* ap, const CfgBlock* bp) const {
|
|
while (ap != bp) {
|
|
if (*ap < *bp) {
|
|
bp = m_bb2Idom[bp];
|
|
} else {
|
|
ap = m_bb2Idom[ap];
|
|
}
|
|
}
|
|
return ap;
|
|
}
|
|
|
|
// Returns true if 'ap' dominates 'bp'
|
|
bool dominates(const CfgBlock* const ap, const CfgBlock* bp) {
|
|
// Walk up the dominator tree from 'bp' until reaching 'ap' or the root
|
|
while (true) {
|
|
// True if 'ap' is above (or same as) 'bp'
|
|
if (ap == bp) return true;
|
|
// Step up the dominator tree
|
|
bp = m_bb2Idom[bp];
|
|
// False if reached the root
|
|
if (!bp) return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
//######################################################################
|
|
// V3Cfg
|
|
|
|
namespace V3Cfg {
|
|
|
|
// Compute AstVars live on entry to given CFG. That is, variables that might
|
|
// be read before wholly assigned in the CFG. Returns nullptr if the analysis
|
|
// failed due to unhandled statements or data types involved in the CFG.
|
|
// On success, returns a vector of AstVar or AstVarScope nodes live on entry.
|
|
std::unique_ptr<std::vector<AstVar*>> liveVars(const CfgGraph&);
|
|
|
|
// Same as liveVars, but return AstVarScopes insted
|
|
std::unique_ptr<std::vector<AstVarScope*>> liveVarScopes(const CfgGraph&);
|
|
|
|
} //namespace V3Cfg
|
|
|
|
#endif // VERILATOR_V3CFG_H_
|