Merge branch 'master' into develop-v5
This commit is contained in:
commit
7403226a97
|
|
@ -334,7 +334,7 @@ clang-tidy: $(CLANGTIDY_DEP)
|
|||
%.cpp.tidy: %.cpp
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
%.h.tidy: %.h
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) | 2>&1 tee $@
|
||||
$(CLANGTIDY) $(CLANGTIDY_FLAGS) $< -- $(CLANGTIDY_DEFS) $(CPPCHECK_INC) -x c++-header | 2>&1 tee $@
|
||||
|
||||
analyzer-src:
|
||||
-rm -rf src/obj_dbg
|
||||
|
|
|
|||
|
|
@ -1293,3 +1293,8 @@ void VNDeleter::doDeletes() {
|
|||
for (AstNode* const nodep : m_deleteps) nodep->deleteTree();
|
||||
m_deleteps.clear();
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
// VNVisitor
|
||||
|
||||
#include "V3Ast__gen_visitor_defns.h" // From ./astgen
|
||||
|
|
|
|||
|
|
@ -1308,10 +1308,8 @@ public:
|
|||
/// Return edited nodep; see comments in V3Ast.cpp
|
||||
AstNode* iterateSubtreeReturnEdits(AstNode* nodep);
|
||||
|
||||
#include "V3Ast__gen_visitor.h" // From ./astgen
|
||||
// Things like:
|
||||
// virtual void visit(AstBreak* nodep) { visit((AstNodeStmt*)(nodep)); }
|
||||
// virtual void visit(AstNodeStmt* nodep) { visit((AstNode*)(nodep)); }
|
||||
virtual void visit(AstNode* nodep) = 0;
|
||||
#include "V3Ast__gen_visitor_decls.h" // From ./astgen
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
|
|||
|
|
@ -3603,7 +3603,11 @@ public:
|
|||
}
|
||||
virtual ~ConstVisitor() override {
|
||||
if (m_doCpp) {
|
||||
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
||||
if (m_globalPass) {
|
||||
V3Stats::addStat("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
||||
} else {
|
||||
V3Stats::addStatSum("Optimizations, Const bit op reduction", m_statBitOpReduction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3696,6 +3700,12 @@ AstNode* V3Const::constifyEdit(AstNode* nodep) {
|
|||
return nodep;
|
||||
}
|
||||
|
||||
AstNode* V3Const::constifyEditCpp(AstNode* nodep) {
|
||||
ConstVisitor visitor{ConstVisitor::PROC_CPP, /* globalPass: */ false};
|
||||
nodep = visitor.mainAcceptEdit(nodep);
|
||||
return nodep;
|
||||
}
|
||||
|
||||
void V3Const::constifyAllLive(AstNetlist* nodep) {
|
||||
// Only call from Verilator.cpp, as it uses user#'s
|
||||
// This only pushes constants up, doesn't make any other edits
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ public:
|
|||
static void constifyCpp(AstNetlist* nodep);
|
||||
// Only the current node and lower
|
||||
// Return new node that may have replaced nodep
|
||||
static AstNode* constifyEditCpp(AstNode* nodep);
|
||||
// Only the current node and lower
|
||||
// Return new node that may have replaced nodep
|
||||
static AstNode* constifyEdit(AstNode* nodep);
|
||||
// Only the current node and lower, with special SenTree optimization
|
||||
// Return new node that may have replaced nodep
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "V3Expand.h"
|
||||
#include "V3Stats.h"
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
|
@ -160,6 +161,7 @@ private:
|
|||
new AstShiftL{fl, llowp,
|
||||
new AstConst{fl, static_cast<uint32_t>(loffset)},
|
||||
VL_EDATASIZE}}};
|
||||
newp = V3Const::constifyEditCpp(newp);
|
||||
} else {
|
||||
newp = llowp;
|
||||
}
|
||||
|
|
@ -520,8 +522,9 @@ private:
|
|||
cleanmask.setMask(VL_BITBIT_E(destp->widthMin()));
|
||||
newp = new AstAnd{lfl, newp, new AstConst{lfl, cleanmask}};
|
||||
}
|
||||
|
||||
addWordAssign(nodep, w, destp, new AstOr{lfl, oldvalp, newp});
|
||||
AstNode* const orp
|
||||
= V3Const::constifyEditCpp(new AstOr{lfl, oldvalp, newp});
|
||||
addWordAssign(nodep, w, destp, orp);
|
||||
}
|
||||
}
|
||||
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
|
||||
|
|
@ -541,7 +544,8 @@ private:
|
|||
AstNode* const shifted = new AstShiftL{
|
||||
lfl, rhsp, new AstConst{lfl, static_cast<uint32_t>(lsb)}, destp->width()};
|
||||
AstNode* const cleaned = new AstAnd{lfl, shifted, new AstConst{lfl, cleanmask}};
|
||||
AstNode* const newp = new AstAssign{nfl, destp, new AstOr{lfl, oldvalp, cleaned}};
|
||||
AstNode* const orp = V3Const::constifyEditCpp(new AstOr{lfl, oldvalp, cleaned});
|
||||
AstNode* newp = new AstAssign{nfl, destp, orp};
|
||||
insertBefore(nodep, newp);
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -195,12 +195,9 @@ public:
|
|||
// longest !wayward edge. Schedule that to be resolved.
|
||||
const uint32_t newPendingVal
|
||||
= newInclusiveCp - m_accessp->critPathCost(relativep, m_way);
|
||||
if (m_pending.has(relativep)) {
|
||||
if (newPendingVal > m_pending.at(relativep)) {
|
||||
m_pending.set(relativep, newPendingVal);
|
||||
}
|
||||
} else {
|
||||
m_pending.set(relativep, newPendingVal);
|
||||
const auto pair = m_pending.emplace(relativep, newPendingVal);
|
||||
if (!pair.second && (newPendingVal > pair.first->second)) {
|
||||
m_pending.update(pair.first, newPendingVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -225,8 +222,8 @@ public:
|
|||
// This generalizes to multiple seed nodes also.
|
||||
while (!m_pending.empty()) {
|
||||
const auto it = m_pending.rbegin();
|
||||
V3GraphVertex* const updateMep = (*it).key();
|
||||
const uint32_t cpGrowBy = (*it).value();
|
||||
V3GraphVertex* const updateMep = it->first;
|
||||
const uint32_t cpGrowBy = it->second;
|
||||
m_pending.erase(it);
|
||||
|
||||
// For *updateMep, whose critPathCost was out-of-date with respect
|
||||
|
|
@ -375,17 +372,17 @@ public:
|
|||
~CpCostAccessor() = default;
|
||||
// Return cost of this node
|
||||
uint32_t cost(const V3GraphVertex* vxp) const {
|
||||
const LogicMTask* const mtaskp = dynamic_cast<const LogicMTask*>(vxp);
|
||||
const LogicMTask* const mtaskp = static_cast<const LogicMTask*>(vxp);
|
||||
return mtaskp->stepCost();
|
||||
}
|
||||
// Return stored CP to this node
|
||||
uint32_t critPathCost(const V3GraphVertex* vxp, GraphWay way) const {
|
||||
const LogicMTask* const mtaskp = dynamic_cast<const LogicMTask*>(vxp);
|
||||
const LogicMTask* const mtaskp = static_cast<const LogicMTask*>(vxp);
|
||||
return mtaskp->critPathCost(way);
|
||||
}
|
||||
// Store a new CP to this node
|
||||
void setCritPathCost(V3GraphVertex* vxp, GraphWay way, uint32_t cost) const {
|
||||
LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(vxp);
|
||||
LogicMTask* const mtaskp = static_cast<LogicMTask*>(vxp);
|
||||
mtaskp->setCritPathCost(way, cost);
|
||||
}
|
||||
// Notify vxp that the wayward CP at the throughp-->vxp edge
|
||||
|
|
@ -393,19 +390,19 @@ public:
|
|||
// This is our cue to update vxp's m_edges[!way][throughp].
|
||||
void notifyEdgeCp(V3GraphVertex* vxp, GraphWay way, V3GraphVertex* throuvhVxp,
|
||||
uint32_t cp) const {
|
||||
LogicMTask* const updateVxp = dynamic_cast<LogicMTask*>(vxp);
|
||||
LogicMTask* const lthrouvhVxp = dynamic_cast<LogicMTask*>(throuvhVxp);
|
||||
LogicMTask* const updateVxp = static_cast<LogicMTask*>(vxp);
|
||||
LogicMTask* const lthrouvhVxp = static_cast<LogicMTask*>(throuvhVxp);
|
||||
EdgeSet& edges = updateVxp->m_edges[way.invert()];
|
||||
const uint32_t edgeCp = edges.at(lthrouvhVxp);
|
||||
if (cp > edgeCp) edges.set(lthrouvhVxp, cp);
|
||||
const auto it = edges.find(lthrouvhVxp);
|
||||
if (cp > it->second) edges.update(it, cp);
|
||||
}
|
||||
// Check that CP matches that of the longest edge wayward of vxp.
|
||||
void checkNewCpVersusEdges(V3GraphVertex* vxp, GraphWay way, uint32_t cp) const {
|
||||
LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(vxp);
|
||||
LogicMTask* const mtaskp = static_cast<LogicMTask*>(vxp);
|
||||
const EdgeSet& edges = mtaskp->m_edges[way.invert()];
|
||||
// This is mtaskp's relative with longest !wayward inclusive CP:
|
||||
const auto edgeIt = edges.rbegin();
|
||||
const uint32_t edgeCp = (*edgeIt).value();
|
||||
const uint32_t edgeCp = edgeIt->second;
|
||||
UASSERT_OBJ(edgeCp == cp, vxp, "CP doesn't match longest wayward edge");
|
||||
}
|
||||
|
||||
|
|
@ -512,26 +509,21 @@ public:
|
|||
}
|
||||
|
||||
void addRelative(GraphWay way, LogicMTask* relativep) {
|
||||
EdgeSet& edges = m_edges[way];
|
||||
UASSERT(!edges.has(relativep), "Adding existing edge");
|
||||
// value is !way cp to this edge
|
||||
edges.set(relativep, relativep->stepCost() + relativep->critPathCost(way.invert()));
|
||||
}
|
||||
void removeRelative(GraphWay way, LogicMTask* relativep) {
|
||||
EdgeSet& edges = m_edges[way];
|
||||
edges.erase(relativep);
|
||||
}
|
||||
bool hasRelative(GraphWay way, LogicMTask* relativep) {
|
||||
const EdgeSet& edges = m_edges[way];
|
||||
return edges.has(relativep);
|
||||
const uint32_t cp = relativep->stepCost() + relativep->critPathCost(way.invert());
|
||||
VL_ATTR_UNUSED const bool exits = !m_edges[way].emplace(relativep, cp).second;
|
||||
#if VL_DEBUG
|
||||
UASSERT(!exits, "Adding existing edge");
|
||||
#endif
|
||||
}
|
||||
void removeRelative(GraphWay way, LogicMTask* relativep) { m_edges[way].erase(relativep); }
|
||||
bool hasRelative(GraphWay way, LogicMTask* relativep) { return m_edges[way].has(relativep); }
|
||||
void checkRelativesCp(GraphWay way) const {
|
||||
const EdgeSet& edges = m_edges[way];
|
||||
for (const auto& edge : vlstd::reverse_view(edges)) {
|
||||
const LogicMTask* const relativep = edge.key();
|
||||
const uint32_t cachedCp = edge.value();
|
||||
partCheckCachedScoreVsActual(cachedCp, relativep->critPathCost(way.invert())
|
||||
+ relativep->stepCost());
|
||||
for (const auto& edge : vlstd::reverse_view(m_edges[way])) {
|
||||
const LogicMTask* const relativep = edge.first;
|
||||
const uint32_t cachedCp = edge.second;
|
||||
const uint32_t cp = relativep->critPathCost(way.invert()) + relativep->stepCost();
|
||||
partCheckCachedScoreVsActual(cachedCp, cp);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -557,11 +549,11 @@ public:
|
|||
const EdgeSet& edges = m_edges[way.invert()];
|
||||
uint32_t result = 0;
|
||||
for (const auto& edge : vlstd::reverse_view(edges)) {
|
||||
if (edge.key() != withoutp->furtherp(way.invert())) {
|
||||
if (edge.first != withoutp->furtherp(way.invert())) {
|
||||
// Use the cached cost. It could be a small overestimate
|
||||
// due to stepping. This is consistent with critPathCost()
|
||||
// which also returns the cached cost.
|
||||
result = edge.value();
|
||||
result = edge.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -603,7 +595,7 @@ private:
|
|||
for (const V3GraphEdge* followp = fromp->outBeginp(); followp;
|
||||
followp = followp->outNextp()) {
|
||||
if (followp == excludedEdgep) continue;
|
||||
LogicMTask* const nextp = dynamic_cast<LogicMTask*>(followp->top());
|
||||
LogicMTask* const nextp = static_cast<LogicMTask*>(followp->top());
|
||||
if (pathExistsFromInternal(nextp, top, nullptr, generation)) return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -634,7 +626,7 @@ public:
|
|||
const LogicMTask* startp = nullptr;
|
||||
for (const V3GraphVertex* vxp = graphp->verticesBeginp(); vxp;
|
||||
vxp = vxp->verticesNextp()) {
|
||||
const LogicMTask* const mtaskp = dynamic_cast<const LogicMTask*>(vxp);
|
||||
const LogicMTask* const mtaskp = static_cast<const LogicMTask*>(vxp);
|
||||
if (!startp) {
|
||||
startp = mtaskp;
|
||||
continue;
|
||||
|
|
@ -657,7 +649,7 @@ public:
|
|||
if (it == children.rend()) {
|
||||
nextp = nullptr;
|
||||
} else {
|
||||
nextp = (*it).key();
|
||||
nextp = it->first;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -805,10 +797,10 @@ public:
|
|||
}
|
||||
// METHODS
|
||||
LogicMTask* furtherMTaskp(GraphWay way) const {
|
||||
return dynamic_cast<LogicMTask*>(this->furtherp(way));
|
||||
return static_cast<LogicMTask*>(this->furtherp(way));
|
||||
}
|
||||
LogicMTask* fromMTaskp() const { return dynamic_cast<LogicMTask*>(fromp()); }
|
||||
LogicMTask* toMTaskp() const { return dynamic_cast<LogicMTask*>(top()); }
|
||||
LogicMTask* fromMTaskp() const { return static_cast<LogicMTask*>(fromp()); }
|
||||
LogicMTask* toMTaskp() const { return static_cast<LogicMTask*>(top()); }
|
||||
bool mergeWouldCreateCycle() const {
|
||||
return LogicMTask::pathExistsFrom(fromMTaskp(), toMTaskp(), this);
|
||||
}
|
||||
|
|
@ -962,7 +954,7 @@ static void partInitHalfCriticalPaths(GraphWay way, V3Graph* mtasksp, bool check
|
|||
GraphStreamUnordered order(mtasksp, way);
|
||||
const GraphWay rev = way.invert();
|
||||
for (const V3GraphVertex* vertexp; (vertexp = order.nextp());) {
|
||||
const LogicMTask* const mtaskcp = dynamic_cast<const LogicMTask*>(vertexp);
|
||||
const LogicMTask* const mtaskcp = static_cast<const LogicMTask*>(vertexp);
|
||||
LogicMTask* const mtaskp = const_cast<LogicMTask*>(mtaskcp);
|
||||
uint32_t cpCost = 0;
|
||||
#if VL_DEBUG
|
||||
|
|
@ -977,7 +969,7 @@ static void partInitHalfCriticalPaths(GraphWay way, V3Graph* mtasksp, bool check
|
|||
"Should be no redundant edges in mtasks graph");
|
||||
relatives.insert(edgep->furtherp(rev));
|
||||
#endif
|
||||
const LogicMTask* const relativep = dynamic_cast<LogicMTask*>(edgep->furtherp(rev));
|
||||
const LogicMTask* const relativep = static_cast<LogicMTask*>(edgep->furtherp(rev));
|
||||
cpCost = std::max(cpCost, (relativep->critPathCost(way)
|
||||
+ static_cast<uint32_t>(relativep->stepCost())));
|
||||
}
|
||||
|
|
@ -1010,7 +1002,7 @@ static void partCheckCriticalPaths(V3Graph* mtasksp) {
|
|||
partInitHalfCriticalPaths(GraphWay::FORWARD, mtasksp, true);
|
||||
partInitHalfCriticalPaths(GraphWay::REVERSE, mtasksp, true);
|
||||
for (V3GraphVertex* vxp = mtasksp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
const LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(vxp);
|
||||
const LogicMTask* const mtaskp = static_cast<LogicMTask*>(vxp);
|
||||
mtaskp->checkRelativesCp(GraphWay::FORWARD);
|
||||
mtaskp->checkRelativesCp(GraphWay::REVERSE);
|
||||
}
|
||||
|
|
@ -1326,8 +1318,8 @@ private:
|
|||
MTaskEdge* mergeEdgep = mergeCanp->toMTaskEdge();
|
||||
const SiblingMC* mergeSibsp = nullptr;
|
||||
if (mergeEdgep) {
|
||||
top = dynamic_cast<LogicMTask*>(mergeEdgep->top());
|
||||
fromp = dynamic_cast<LogicMTask*>(mergeEdgep->fromp());
|
||||
top = static_cast<LogicMTask*>(mergeEdgep->top());
|
||||
fromp = static_cast<LogicMTask*>(mergeEdgep->fromp());
|
||||
} else {
|
||||
mergeSibsp = mergeCanp->toSiblingMC();
|
||||
UASSERT(mergeSibsp, "Failed to cast mergeCanp to either MTaskEdge or SiblingMC");
|
||||
|
|
@ -1437,14 +1429,14 @@ private:
|
|||
siblingPairFromRelatives(GraphWay::FORWARD, recipientp, true);
|
||||
unsigned edges = 0;
|
||||
for (V3GraphEdge* edgep = recipientp->outBeginp(); edgep; edgep = edgep->outNextp()) {
|
||||
LogicMTask* const postreqp = dynamic_cast<LogicMTask*>(edgep->top());
|
||||
LogicMTask* const postreqp = static_cast<LogicMTask*>(edgep->top());
|
||||
siblingPairFromRelatives(GraphWay::REVERSE, postreqp, false);
|
||||
++edges;
|
||||
if (edges > PART_SIBLING_EDGE_LIMIT) break;
|
||||
}
|
||||
edges = 0;
|
||||
for (V3GraphEdge* edgep = recipientp->inBeginp(); edgep; edgep = edgep->inNextp()) {
|
||||
LogicMTask* const prereqp = dynamic_cast<LogicMTask*>(edgep->fromp());
|
||||
LogicMTask* const prereqp = static_cast<LogicMTask*>(edgep->fromp());
|
||||
siblingPairFromRelatives(GraphWay::FORWARD, prereqp, false);
|
||||
++edges;
|
||||
if (edges > PART_SIBLING_EDGE_LIMIT) break;
|
||||
|
|
@ -1477,6 +1469,7 @@ private:
|
|||
return 0;
|
||||
}
|
||||
|
||||
VL_ATTR_NOINLINE
|
||||
static uint32_t siblingScore(const SiblingMC* sibsp) {
|
||||
const LogicMTask* const ap = sibsp->ap();
|
||||
const LogicMTask* const bp = sibsp->bp();
|
||||
|
|
@ -1487,12 +1480,13 @@ private:
|
|||
return mergedCpCostRev + mergedCpCostFwd + LogicMTask::stepCost(ap->cost() + bp->cost());
|
||||
}
|
||||
|
||||
VL_ATTR_NOINLINE
|
||||
static uint32_t edgeScore(const V3GraphEdge* edgep) {
|
||||
// Score this edge. Lower is better. The score is the new local CP
|
||||
// length if we merge these mtasks. ("Local" means the longest
|
||||
// critical path running through the merged node.)
|
||||
const LogicMTask* const top = dynamic_cast<LogicMTask*>(edgep->top());
|
||||
const LogicMTask* const fromp = dynamic_cast<LogicMTask*>(edgep->fromp());
|
||||
const LogicMTask* const top = static_cast<LogicMTask*>(edgep->top());
|
||||
const LogicMTask* const fromp = static_cast<LogicMTask*>(edgep->fromp());
|
||||
const uint32_t mergedCpCostFwd
|
||||
= std::max(fromp->critPathCost(GraphWay::FORWARD),
|
||||
top->critPathCostWithout(GraphWay::FORWARD, edgep));
|
||||
|
|
@ -1534,7 +1528,7 @@ private:
|
|||
std::vector<LogicMTask*> shortestPrereqs;
|
||||
|
||||
for (V3GraphEdge* edgep = mtaskp->beginp(way); edgep; edgep = edgep->nextp(way)) {
|
||||
LogicMTask* const prereqp = dynamic_cast<LogicMTask*>(edgep->furtherp(way));
|
||||
LogicMTask* const prereqp = static_cast<LogicMTask*>(edgep->furtherp(way));
|
||||
shortestPrereqs.push_back(prereqp);
|
||||
// Prevent nodes with huge numbers of edges from massively
|
||||
// slowing down the partitioner:
|
||||
|
|
@ -1930,7 +1924,7 @@ public:
|
|||
OvvSet ovvSetSystemC(ovvOrder);
|
||||
|
||||
for (V3GraphVertex* vxp = m_mtasksp->verticesBeginp(); vxp; vxp = vxp->verticesNextp()) {
|
||||
LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(vxp);
|
||||
LogicMTask* const mtaskp = static_cast<LogicMTask*>(vxp);
|
||||
// Should be only one MTaskMoveVertex in each mtask at this
|
||||
// stage, but whatever, write it as a loop:
|
||||
for (LogicMTask::VxList::const_iterator it = mtaskp->vertexListp()->begin();
|
||||
|
|
@ -2040,7 +2034,7 @@ public:
|
|||
TasksByRank tasksByRank;
|
||||
for (V3GraphVertex* vxp = m_mtasksp->verticesBeginp(); vxp;
|
||||
vxp = vxp->verticesNextp()) {
|
||||
LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(vxp);
|
||||
LogicMTask* const mtaskp = static_cast<LogicMTask*>(vxp);
|
||||
if (hasDpiHazard(mtaskp)) tasksByRank[vxp->rank()].insert(mtaskp);
|
||||
}
|
||||
mergeSameRankTasks(&tasksByRank);
|
||||
|
|
@ -2519,7 +2513,7 @@ void V3Partition::hashGraphDebug(const V3Graph* graphp, const char* debugName) {
|
|||
void V3Partition::setupMTaskDeps(V3Graph* mtasksp, const Vx2MTaskMap* vx2mtaskp) {
|
||||
// Look at each mtask
|
||||
for (V3GraphVertex* itp = mtasksp->verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(itp);
|
||||
LogicMTask* const mtaskp = static_cast<LogicMTask*>(itp);
|
||||
const LogicMTask::VxList* vertexListp = mtaskp->vertexListp();
|
||||
|
||||
// For each logic vertex in this mtask, create an mtask-to-mtask
|
||||
|
|
@ -2667,7 +2661,7 @@ void V3Partition::go(V3Graph* mtasksp) {
|
|||
using SortedMTaskSet = std::set<LogicMTask*, LogicMTask::CmpLogicMTask>;
|
||||
SortedMTaskSet sorted;
|
||||
for (V3GraphVertex* itp = mtasksp->verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(itp);
|
||||
LogicMTask* const mtaskp = static_cast<LogicMTask*>(itp);
|
||||
sorted.insert(mtaskp);
|
||||
}
|
||||
for (auto it = sorted.begin(); it != sorted.end(); ++it) {
|
||||
|
|
@ -2683,7 +2677,7 @@ void V3Partition::go(V3Graph* mtasksp) {
|
|||
|
||||
// Set color to indicate an mtaskId on every underlying MTaskMoveVertex.
|
||||
for (V3GraphVertex* itp = mtasksp->verticesBeginp(); itp; itp = itp->verticesNextp()) {
|
||||
const LogicMTask* const mtaskp = dynamic_cast<LogicMTask*>(itp);
|
||||
const LogicMTask* const mtaskp = static_cast<LogicMTask*>(itp);
|
||||
for (LogicMTask::VxList::const_iterator it = mtaskp->vertexListp()->begin();
|
||||
it != mtaskp->vertexListp()->end(); ++it) {
|
||||
MTaskMoveVertex* const mvertexp = *it;
|
||||
|
|
|
|||
|
|
@ -34,298 +34,103 @@
|
|||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
//######################################################################
|
||||
// SortByValueMap
|
||||
// ######################################################################
|
||||
// SortByValueMap
|
||||
|
||||
/// A generic key-value map, except it also supports iterating in
|
||||
/// value-sorted order. Values need not be unique. Uses T_KeyCompare to
|
||||
/// break ties in the sort when values collide.
|
||||
// A generic key-value map, except iteration is in *value* sorted order. Values need not be unique.
|
||||
// Uses T_KeyCompare to break ties in the sort when values collide. Note: Only const iteration is
|
||||
// possible, as updating mapped values via iterators is not safe.
|
||||
|
||||
template <typename T_Key, typename T_Value, class T_KeyCompare = std::less<T_Key>>
|
||||
class SortByValueMap final {
|
||||
// TYPES
|
||||
private:
|
||||
using KeySet = std::set<T_Key, T_KeyCompare>;
|
||||
using Val2Keys = std::map<T_Value, KeySet>;
|
||||
// Current implementation is a std::set of key/value pairs, plus a std_unordered_map from keys
|
||||
// to iterators into the set. This keeps most operations fairly cheap and also has the benefit
|
||||
// of being able to re-use the std::set iterators.
|
||||
|
||||
// TYPES
|
||||
|
||||
using Pair = std::pair<T_Key, T_Value>;
|
||||
|
||||
struct PairCmp final {
|
||||
bool operator()(const Pair& a, const Pair& b) const {
|
||||
// First compare values
|
||||
if (a.second != b.second) return a.second < b.second;
|
||||
// Then compare keys
|
||||
return T_KeyCompare{}(a.first, b.first);
|
||||
}
|
||||
};
|
||||
|
||||
using PairSet = std::set<Pair, PairCmp>;
|
||||
|
||||
public:
|
||||
using const_iterator = typename PairSet::const_iterator;
|
||||
using const_reverse_iterator = typename PairSet::const_reverse_iterator;
|
||||
|
||||
private:
|
||||
// MEMBERS
|
||||
std::unordered_map<T_Key, T_Value> m_keys; // Map each key to its value. Not sorted.
|
||||
Val2Keys m_vals; // Map each value to its keys. Sorted.
|
||||
PairSet m_pairs; // The contents of the map, stored directly as key-value pairs
|
||||
std::unordered_map<T_Key, const_iterator> m_kiMap; // Key to iterator map
|
||||
|
||||
VL_UNCOPYABLE(SortByValueMap);
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
SortByValueMap() = default;
|
||||
|
||||
class const_iterator VL_NOT_FINAL {
|
||||
// TYPES
|
||||
public:
|
||||
using value_type = const_iterator;
|
||||
using reference = const_iterator; // See comment on operator*()
|
||||
using pointer = void;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
// Only const iteration is possible
|
||||
const_iterator begin() const { return m_pairs.begin(); }
|
||||
const_iterator end() const { return m_pairs.end(); }
|
||||
const_iterator cbegin() const { m_pairs.cbegin(); }
|
||||
const_iterator cend() const { return m_pairs.cend(); }
|
||||
const_reverse_iterator rbegin() const { return m_pairs.rbegin(); }
|
||||
const_reverse_iterator rend() const { return m_pairs.rend(); }
|
||||
const_reverse_iterator crbegin() const { return m_pairs.crbegin(); }
|
||||
const_reverse_iterator crend() const { return m_pairs.crend(); }
|
||||
|
||||
protected:
|
||||
friend class SortByValueMap;
|
||||
|
||||
// MEMBERS
|
||||
typename KeySet::iterator m_keyIt;
|
||||
typename Val2Keys::iterator m_valIt;
|
||||
SortByValueMap* const m_sbvmp;
|
||||
bool m_end = true; // At the end()
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit const_iterator(SortByValueMap* sbmvp) // for end()
|
||||
: m_sbvmp{sbmvp} {}
|
||||
const_iterator(typename Val2Keys::iterator valIt, typename KeySet::iterator keyIt,
|
||||
SortByValueMap* sbvmp)
|
||||
: m_keyIt{keyIt}
|
||||
, m_valIt{valIt}
|
||||
, m_sbvmp{sbvmp}
|
||||
, m_end{false} {}
|
||||
|
||||
// METHODS
|
||||
void advanceUntilValid() {
|
||||
++m_keyIt;
|
||||
if (m_keyIt != m_valIt->second.end()) { // Valid iterator, done.
|
||||
return;
|
||||
}
|
||||
// Try the next value?
|
||||
++m_valIt;
|
||||
if (m_valIt == m_sbvmp->m_vals.end()) { // No more values
|
||||
m_end = true;
|
||||
return;
|
||||
}
|
||||
// Should find a value here, as every value bucket is supposed
|
||||
// to have at least one key, even after keys get removed.
|
||||
m_keyIt = m_valIt->second.begin();
|
||||
UASSERT(m_keyIt != m_valIt->second.end(), "Algorithm should have key left");
|
||||
}
|
||||
void reverseUntilValid() {
|
||||
if (m_end) {
|
||||
UASSERT(!m_sbvmp->m_vals.empty(), "Reverse iterator causes underflow");
|
||||
m_valIt = m_sbvmp->m_vals.end();
|
||||
--m_valIt;
|
||||
|
||||
UASSERT(!m_valIt->second.empty(), "Reverse iterator causes underflow");
|
||||
m_keyIt = m_valIt->second.end();
|
||||
--m_keyIt;
|
||||
|
||||
m_end = false;
|
||||
return;
|
||||
}
|
||||
if (m_keyIt != m_valIt->second.begin()) {
|
||||
// Valid iterator, we're done.
|
||||
--m_keyIt;
|
||||
return;
|
||||
}
|
||||
// Try the previous value?
|
||||
if (VL_UNCOVERABLE(m_valIt == m_sbvmp->m_vals.begin())) {
|
||||
// No more values but it's not defined to decrement an
|
||||
// iterator past the beginning.
|
||||
v3fatalSrc("Decremented iterator past beginning");
|
||||
return; // LCOV_EXCL_LINE
|
||||
}
|
||||
--m_valIt;
|
||||
// Should find a value here, as Every value bucket is supposed
|
||||
// to have at least one key, even after keys get removed.
|
||||
UASSERT(!m_valIt->second.empty(), "Value bucket should have key");
|
||||
m_keyIt = m_valIt->second.end();
|
||||
--m_keyIt;
|
||||
UASSERT(m_keyIt != m_valIt->second.end(), "Value bucket should have key");
|
||||
}
|
||||
|
||||
public:
|
||||
const T_Key& key() const { return *m_keyIt; }
|
||||
const T_Value& value() const { return m_valIt->first; }
|
||||
const_iterator& operator++() {
|
||||
advanceUntilValid();
|
||||
return *this;
|
||||
}
|
||||
const_iterator& operator--() {
|
||||
reverseUntilValid();
|
||||
return *this;
|
||||
}
|
||||
bool operator==(const const_iterator& other) const {
|
||||
// It's not legal to compare iterators from different
|
||||
// sequences. So check m_end before comparing m_valIt, and
|
||||
// compare m_valIt's before comparing m_keyIt to ensure nothing
|
||||
// here is undefined.
|
||||
if (m_end || other.m_end) return m_end && other.m_end;
|
||||
return ((m_valIt == other.m_valIt) && (m_keyIt == other.m_keyIt));
|
||||
}
|
||||
bool operator!=(const const_iterator& other) const { return (!this->operator==(other)); }
|
||||
|
||||
// WARNING: Cleverness.
|
||||
//
|
||||
// The "reference" returned by *it must remain valid after 'it'
|
||||
// gets destroyed. The reverse_iterator relies on this for its
|
||||
// operator*(), so it's not just a theoretical requirement, it's a
|
||||
// real requirement.
|
||||
//
|
||||
// To make that work, define the "reference" type to be the
|
||||
// iterator itself. So clients can do (*it).key() and
|
||||
// (*it).value(). This is the clever part.
|
||||
//
|
||||
// That's mostly useful for a reverse iterator, where *rit returns
|
||||
// the forward iterator pointing the to same element, so
|
||||
// (*rit).key() and (*rit).value() work where rit.key() and
|
||||
// rit.value() cannot.
|
||||
//
|
||||
// It would be nice to support it->key() and it->value(), however
|
||||
// uncertain what would be an appropriate 'pointer' type define
|
||||
// that makes this work safely through a reverse iterator. So this
|
||||
// class does not provide an operator->().
|
||||
//
|
||||
// Q) Why not make our value_type be a pair<T_Key, T_Value> like a
|
||||
// normal map, and return a reference to that? This could
|
||||
// return a reference to one of the pairs inside m_keys, that
|
||||
// would satisfy the constraint above.
|
||||
//
|
||||
// A) It would take a lookup to find that pair within m_keys. This
|
||||
// iterator is designed to minimize the number of hashtable and
|
||||
// tree lookups. Increment, decrement, key(), value(), erase()
|
||||
// by iterator, begin(), end() -- none of these require a
|
||||
// container lookup. That's true for reverse_iterators too.
|
||||
reference operator*() const {
|
||||
UASSERT(!m_end, "Dereferencing iterator that is at end()");
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class iterator final : public const_iterator {
|
||||
public:
|
||||
// TYPES
|
||||
using value_type = iterator;
|
||||
using reference = iterator;
|
||||
// pointer, difference_type, and iterator_category inherit from
|
||||
// const_iterator
|
||||
|
||||
// CONSTRUCTORS
|
||||
explicit iterator(SortByValueMap* sbvmp)
|
||||
: const_iterator{sbvmp} {}
|
||||
iterator(typename Val2Keys::iterator valIt, typename KeySet::iterator keyIt,
|
||||
SortByValueMap* sbvmp)
|
||||
: const_iterator{valIt, keyIt, sbvmp} {}
|
||||
|
||||
// METHODS
|
||||
iterator& operator++() {
|
||||
this->advanceUntilValid();
|
||||
return *this;
|
||||
}
|
||||
iterator& operator--() {
|
||||
this->reverseUntilValid();
|
||||
return *this;
|
||||
}
|
||||
reference operator*() const {
|
||||
UASSERT(!this->m_end, "Dereferencing iterator that is at end()");
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
|
||||
// METHODS
|
||||
private:
|
||||
void removeKeyFromOldVal(const T_Key& k, const T_Value& oldVal) {
|
||||
// The value of 'k' is about to change, or, 'k' is about to be
|
||||
// removed from the map.
|
||||
// Clear the m_vals mapping for k.
|
||||
KeySet& keysAtOldVal = m_vals[oldVal];
|
||||
const size_t erased = keysAtOldVal.erase(k);
|
||||
UASSERT(erased == 1, "removeKeyFromOldVal() removal key not found");
|
||||
if (keysAtOldVal.empty()) {
|
||||
// Don't keep empty sets in the value map.
|
||||
m_vals.erase(oldVal);
|
||||
}
|
||||
const_iterator find(const T_Key& key) const {
|
||||
const auto kiIt = m_kiMap.find(key);
|
||||
if (kiIt == m_kiMap.end()) return cend();
|
||||
return kiIt->second;
|
||||
}
|
||||
void removeKeyFromOldVal(iterator it) {
|
||||
it.m_valIt->second.erase(it.m_keyIt);
|
||||
if (it.m_valIt->second.empty()) m_vals.erase(it.m_valIt);
|
||||
}
|
||||
|
||||
public:
|
||||
iterator begin() {
|
||||
const auto valIt = m_vals.begin();
|
||||
if (valIt == m_vals.end()) return end();
|
||||
const auto keyIt = valIt->second.begin();
|
||||
return iterator(valIt, keyIt, this);
|
||||
}
|
||||
const_iterator begin() const {
|
||||
SortByValueMap* const mutp = const_cast<SortByValueMap*>(this);
|
||||
const auto valIt = mutp->m_vals.begin();
|
||||
if (valIt == mutp->m_vals.end()) return end();
|
||||
const auto keyIt = valIt->second.begin();
|
||||
return const_iterator(valIt, keyIt, mutp);
|
||||
}
|
||||
iterator end() { return iterator(this); }
|
||||
const_iterator end() const {
|
||||
// Safe to cast away const; the const_iterator will still enforce
|
||||
// it. Same for the const begin() below.
|
||||
return const_iterator(const_cast<SortByValueMap*>(this));
|
||||
}
|
||||
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||||
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||||
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
|
||||
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
|
||||
|
||||
iterator find(const T_Key& k) {
|
||||
const auto kvit = m_keys.find(k);
|
||||
if (kvit == m_keys.end()) return end();
|
||||
|
||||
const auto valIt = m_vals.find(kvit->second);
|
||||
const auto keyIt = valIt->second.find(k);
|
||||
return iterator(valIt, keyIt, this);
|
||||
}
|
||||
const_iterator find(const T_Key& k) const {
|
||||
SortByValueMap* const mutp = const_cast<SortByValueMap*>(this);
|
||||
const auto kvit = mutp->m_keys.find(k);
|
||||
if (kvit == mutp->m_keys.end()) return end();
|
||||
|
||||
const auto valIt = mutp->m_vals.find(kvit->second);
|
||||
const auto keyIt = valIt->second.find(k);
|
||||
return const_iterator(valIt, keyIt, mutp);
|
||||
}
|
||||
void set(const T_Key& k, const T_Value& v) {
|
||||
const auto kvit = m_keys.find(k);
|
||||
if (kvit != m_keys.end()) {
|
||||
if (kvit->second == v) {
|
||||
return; // LCOV_EXCL_LINE // Same value already present; stop.
|
||||
}
|
||||
// Must remove element from m_vals[oldValue]
|
||||
removeKeyFromOldVal(k, kvit->second);
|
||||
}
|
||||
m_keys[k] = v;
|
||||
m_vals[v].insert(k);
|
||||
}
|
||||
size_t erase(const T_Key& k) {
|
||||
const auto kvit = m_keys.find(k);
|
||||
if (kvit == m_keys.end()) return 0;
|
||||
removeKeyFromOldVal(k, kvit->second);
|
||||
m_keys.erase(kvit);
|
||||
size_t erase(const T_Key& key) {
|
||||
const auto kiIt = m_kiMap.find(key);
|
||||
if (kiIt == m_kiMap.end()) return 0;
|
||||
m_pairs.erase(kiIt->second);
|
||||
m_kiMap.erase(kiIt);
|
||||
return 1;
|
||||
}
|
||||
void erase(const iterator& it) {
|
||||
m_keys.erase(it.key());
|
||||
removeKeyFromOldVal(it);
|
||||
void erase(const_iterator it) {
|
||||
m_kiMap.erase(it->first);
|
||||
m_pairs.erase(it);
|
||||
}
|
||||
void erase(const reverse_iterator& it) {
|
||||
erase(*it); // Dereferencing returns a copy of the forward iterator
|
||||
void erase(const_reverse_iterator rit) {
|
||||
m_kiMap.erase(rit->first);
|
||||
m_pairs.erase(std::next(rit).base());
|
||||
}
|
||||
bool has(const T_Key& k) const { return (m_keys.find(k) != m_keys.end()); }
|
||||
bool empty() const { return m_keys.empty(); }
|
||||
// Look up a value. Returns a reference for efficiency. Note this must
|
||||
// be a const reference, otherwise the client could corrupt the sorted
|
||||
// order of m_byValue by reaching through and changing the value.
|
||||
const T_Value& at(const T_Key& k) const {
|
||||
const auto kvit = m_keys.find(k);
|
||||
UASSERT(kvit != m_keys.end(), "at() lookup key not found");
|
||||
return kvit->second;
|
||||
bool has(const T_Key& key) const { return m_kiMap.count(key); }
|
||||
bool empty() const { return m_pairs.empty(); }
|
||||
// Returns const reference.
|
||||
const T_Value& at(const T_Key& key) const { return m_kiMap.at(key)->second; }
|
||||
// Note this returns const_iterator
|
||||
template <typename... Args> //
|
||||
std::pair<const_iterator, bool> emplace(const T_Key& key, Args&&... args) {
|
||||
const auto kiEmp = m_kiMap.emplace(key, end());
|
||||
if (kiEmp.second) {
|
||||
const auto result = m_pairs.emplace(key, std::forward<Args>(args)...);
|
||||
#if VL_DEBUG
|
||||
UASSERT(result.second, "Should not be in set yet");
|
||||
#endif
|
||||
kiEmp.first->second = result.first;
|
||||
return result;
|
||||
}
|
||||
return {kiEmp.first->second, false};
|
||||
}
|
||||
// Invalidates iterators
|
||||
void update(const_iterator it, T_Value value) {
|
||||
const auto kiIt = m_kiMap.find(it->first);
|
||||
m_pairs.erase(it);
|
||||
kiIt->second = m_pairs.emplace(kiIt->first, value).first;
|
||||
}
|
||||
|
||||
private:
|
||||
VL_UNCOPYABLE(SortByValueMap);
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
|
|
@ -333,7 +138,7 @@ private:
|
|||
/// V3Scoreboard takes a set of Elem*'s, each having some score.
|
||||
/// Scores are assigned by a user-supplied scoring function.
|
||||
///
|
||||
/// At any time, the V3Scoreboard can return the elem with the "best" score
|
||||
/// At any time, the V3Scoreboard can return th515e elem with the "best" score
|
||||
/// among those elements whose scores are known.
|
||||
///
|
||||
/// The best score is the _lowest_ score. This makes sense in contexts
|
||||
|
|
@ -418,9 +223,9 @@ public:
|
|||
// reflected in the result of bestp(). Otherwise, bestp() only
|
||||
// considers elements that aren't pending rescore.
|
||||
const T_Elem* bestp() {
|
||||
const auto result = m_sorted.begin();
|
||||
if (VL_UNLIKELY(result == m_sorted.end())) return nullptr;
|
||||
return (*result).key();
|
||||
const auto it = m_sorted.begin();
|
||||
if (VL_UNLIKELY(it == m_sorted.end())) return nullptr;
|
||||
return it->first;
|
||||
}
|
||||
|
||||
// Tell the scoreboard that this element's score may have changed.
|
||||
|
|
@ -444,20 +249,18 @@ public:
|
|||
bool needsRescore() { return !m_unknown.empty(); }
|
||||
// False if elp's score is known to V3Scoreboard,
|
||||
// else true if elp's score is unknown until the next rescore().
|
||||
bool needsRescore(const T_Elem* elp) { return (m_unknown.find(elp) != m_unknown.end()); }
|
||||
bool needsRescore(const T_Elem* elp) { return m_unknown.count(elp); }
|
||||
// Retrieve the last known score for an element.
|
||||
T_Score cachedScore(const T_Elem* elp) {
|
||||
const auto result = m_sorted.find(elp);
|
||||
UASSERT(result != m_sorted.end(), "V3Scoreboard::cachedScore() failed to find element");
|
||||
return (*result).value();
|
||||
}
|
||||
T_Score cachedScore(const T_Elem* elp) { return m_sorted.at(elp); }
|
||||
// For each element whose score is unknown to V3Scoreboard,
|
||||
// call the client's scoring function to get a new score,
|
||||
// and sort all elements by their current score.
|
||||
void rescore() {
|
||||
for (const T_Elem* elp : m_unknown) {
|
||||
const T_Score sortScore = m_scoreFnp(elp);
|
||||
m_sorted.set(elp, sortScore);
|
||||
VL_ATTR_UNUSED const bool exists = !m_sorted.emplace(elp, m_scoreFnp(elp)).second;
|
||||
#if VL_DEBUG
|
||||
UASSERT(!exists, "Should not be in both m_unknown and m_sorted");
|
||||
#endif
|
||||
}
|
||||
m_unknown.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ private:
|
|||
bool m_varModeReplace; // Replacing varrefs
|
||||
bool m_varAssignHit; // Assign var hit
|
||||
bool m_generate; // Expand single generate For loop
|
||||
int m_unrollLimit; // Unrolling limit
|
||||
string m_beginName; // What name to give begin iterations
|
||||
VDouble0 m_statLoops; // Statistic tracking
|
||||
VDouble0 m_statIters; // Statistic tracking
|
||||
|
|
@ -67,10 +68,6 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
int unrollCount() const {
|
||||
return m_generate ? v3Global.opt.unrollCount() * 16 : v3Global.opt.unrollCount();
|
||||
}
|
||||
|
||||
bool bodySizeOverRecurse(AstNode* nodep, int& bodySize, int bodyLimit) {
|
||||
if (!nodep) return false;
|
||||
bodySize++;
|
||||
|
|
@ -163,7 +160,7 @@ private:
|
|||
|
||||
// Check whether to we actually want to try and unroll.
|
||||
int loops;
|
||||
if (!countLoops(initAssp, condp, incp, unrollCount(), loops)) {
|
||||
if (!countLoops(initAssp, condp, incp, m_unrollLimit, loops)) {
|
||||
return cantUnroll(nodep, "Unable to simulate loop");
|
||||
}
|
||||
|
||||
|
|
@ -336,11 +333,11 @@ private:
|
|||
}
|
||||
|
||||
++m_statIters;
|
||||
if (++times > unrollCount() * 3) {
|
||||
if (++times / 3 > m_unrollLimit) {
|
||||
nodep->v3error(
|
||||
"Loop unrolling took too long;"
|
||||
" probably this is an infinite loop, or set --unroll-count above "
|
||||
<< unrollCount());
|
||||
<< m_unrollLimit);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -485,6 +482,12 @@ public:
|
|||
m_varModeReplace = false;
|
||||
m_varAssignHit = false;
|
||||
m_generate = generate;
|
||||
m_unrollLimit = v3Global.opt.unrollCount();
|
||||
if (generate) {
|
||||
m_unrollLimit = std::numeric_limits<int>::max() / 16 > m_unrollLimit
|
||||
? m_unrollLimit * 16
|
||||
: std::numeric_limits<int>::max();
|
||||
}
|
||||
m_beginName = beginName;
|
||||
}
|
||||
void process(AstNode* nodep, bool generate, const string& beginName) {
|
||||
|
|
|
|||
21
src/astgen
21
src/astgen
|
|
@ -507,15 +507,21 @@ def write_classes(filename):
|
|||
fh.write("\n")
|
||||
|
||||
|
||||
def write_visitor(filename):
|
||||
def write_visitor_decls(filename):
|
||||
with open_file(filename) as fh:
|
||||
for typen in sorted(Classes.keys()):
|
||||
if typen == "Node":
|
||||
fh.write(" virtual void visit(Ast" + typen + "*) = 0;\n")
|
||||
else:
|
||||
if typen != "Node":
|
||||
fh.write("virtual void visit(Ast" + typen + "*);\n")
|
||||
|
||||
|
||||
def write_visitor_defns(filename):
|
||||
with open_file(filename) as fh:
|
||||
for typen in sorted(Classes.keys()):
|
||||
if typen != "Node":
|
||||
base = Classes[typen]
|
||||
fh.write(" virtual void visit(Ast" + typen +
|
||||
"* nodep) { visit((Ast" + base + "*)(nodep)); }\n")
|
||||
fh.write("void VNVisitor::visit(Ast" + typen +
|
||||
"* nodep) { visit(static_cast<Ast" + base +
|
||||
"*>(nodep)); }\n")
|
||||
|
||||
|
||||
def write_impl(filename):
|
||||
|
|
@ -692,7 +698,8 @@ for filename in source_files:
|
|||
if Args.classes:
|
||||
write_report("V3Ast__gen_report.txt")
|
||||
write_classes("V3Ast__gen_classes.h")
|
||||
write_visitor("V3Ast__gen_visitor.h")
|
||||
write_visitor_decls("V3Ast__gen_visitor_decls.h")
|
||||
write_visitor_defns("V3Ast__gen_visitor_defns.h")
|
||||
write_impl("V3Ast__gen_impl.h")
|
||||
write_types("V3Ast__gen_types.h")
|
||||
write_yystype("V3Ast__gen_yystype.h")
|
||||
|
|
|
|||
Loading…
Reference in New Issue