2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Combine common code into functions
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2020-03-21 16:24:24 +01:00
|
|
|
// Copyright 2003-2020 by Wilson Snyder. This program is free software; you
|
|
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Combine's Transformations:
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2019-05-19 22:13:13 +02:00
|
|
|
// For every function that we spit out
|
|
|
|
|
// Examine code to find largest common blocks
|
|
|
|
|
// Hash each node depth first
|
|
|
|
|
// Hash includes varp name and operator type, and constants
|
|
|
|
|
// Form lookup table based on hash of each statement w/ nodep and next nodep
|
|
|
|
|
// GO through table
|
|
|
|
|
// Lookup in hash, while next of each statement match, grow that common block
|
|
|
|
|
// Foreach common block
|
|
|
|
|
// If common block large enough (> 20 statements) & used 2x or more
|
|
|
|
|
// Make new function
|
|
|
|
|
// Move common block to function
|
|
|
|
|
// Replace each common block ref with funccall
|
2008-06-10 03:25:10 +02:00
|
|
|
//
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Combine.h"
|
|
|
|
|
#include "V3Hashed.h"
|
|
|
|
|
#include "V3Stats.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <cstdarg>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
#define COMBINE_MIN_STATEMENTS 50 // Min # of statements to be worth making a function
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
|
|
|
|
class CombBaseVisitor : public AstNVisitor {
|
|
|
|
|
protected:
|
|
|
|
|
// STATE
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
|
|
|
|
virtual ~CombBaseVisitor() {}
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//***** optimization levels
|
2011-08-05 03:15:24 +02:00
|
|
|
static bool emptyFunctionDeletion() { return true; }
|
|
|
|
|
static bool duplicateFunctionCombine() { return true; }
|
2009-12-05 16:38:49 +01:00
|
|
|
// Note this is disabled, it still needed work
|
2020-01-25 02:10:44 +01:00
|
|
|
// Also repair it for DPI functions; when make __common need to ensure proper
|
2009-12-05 16:38:49 +01:00
|
|
|
// flags get inherited from the old to new AstCFunc, and that AstText doesn't
|
2019-09-09 13:50:21 +02:00
|
|
|
// get split between functions causing the text to have a dangling reference.
|
2018-10-15 00:39:33 +02:00
|
|
|
bool statementCombine() { return false; } // duplicateFunctionCombine();
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Combine replacement function
|
|
|
|
|
|
|
|
|
|
class CombCallVisitor : CombBaseVisitor {
|
|
|
|
|
// Find all CCALLS of each CFUNC, so that we can later rename them
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2020-04-15 13:58:34 +02:00
|
|
|
typedef std::multimap<AstCFunc*, AstCCall*> CallMmap;
|
|
|
|
|
CallMmap m_callMmap; // Associative array of {function}{call}
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
|
|
|
|
public:
|
2018-08-25 15:52:45 +02:00
|
|
|
void replaceFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (oldfuncp == newfuncp) return;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (newfuncp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " Replace " << oldfuncp << " -WITH-> " << newfuncp << endl);
|
|
|
|
|
} else {
|
|
|
|
|
UINFO(4, " Remove " << oldfuncp << endl);
|
|
|
|
|
}
|
|
|
|
|
std::pair<CallMmap::iterator, CallMmap::iterator> eqrange
|
2019-05-19 22:13:13 +02:00
|
|
|
= m_callMmap.equal_range(oldfuncp);
|
|
|
|
|
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
|
|
|
|
CallMmap::iterator eqit = nextit++;
|
|
|
|
|
AstCCall* callp = eqit->second;
|
|
|
|
|
if (!callp->user3()) { // !already done
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " Called " << callp << endl);
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(callp->funcp() == oldfuncp, callp,
|
|
|
|
|
"Call list broken, points to call w/different func");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (newfuncp) {
|
|
|
|
|
AstCCall* newp = new AstCCall(callp, newfuncp);
|
|
|
|
|
// Special new AstCCall form above transfers children of callp to newfuncp
|
|
|
|
|
callp->replaceWith(newp);
|
|
|
|
|
addCall(newp); // Fix the table
|
|
|
|
|
} else { // Just deleting empty function
|
|
|
|
|
callp->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
callp->user3(true); // Dead now
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(callp), callp);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_callMmap.erase(eqit); // Fix the table
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
// METHODS
|
2020-04-15 13:58:34 +02:00
|
|
|
void addCall(AstCCall* nodep) { m_callMmap.insert(make_pair(nodep->funcp(), nodep)); }
|
2006-08-26 13:35:28 +02:00
|
|
|
void deleteCall(AstCCall* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
std::pair<CallMmap::iterator, CallMmap::iterator> eqrange
|
2019-05-19 22:13:13 +02:00
|
|
|
= m_callMmap.equal_range(nodep->funcp());
|
|
|
|
|
for (CallMmap::iterator nextit = eqrange.first; nextit != eqrange.second;) {
|
|
|
|
|
CallMmap::iterator eqit = nextit++;
|
|
|
|
|
AstCCall* callp = eqit->second;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (callp == nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_callMmap.erase(eqit);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nodep->v3fatalSrc("deleteCall node not found in table");
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
|
|
|
|
// VISITORS
|
2020-04-15 13:58:34 +02:00
|
|
|
virtual void visit(AstCCall* nodep) VL_OVERRIDE { addCall(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
// Speed things up
|
2020-04-04 14:31:14 +02:00
|
|
|
virtual void visit(AstNodeAssign*) VL_OVERRIDE {}
|
|
|
|
|
virtual void visit(AstNodeMath*) VL_OVERRIDE {}
|
|
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-04-05 14:22:51 +02:00
|
|
|
CombCallVisitor() {}
|
2006-08-26 13:35:28 +02:00
|
|
|
virtual ~CombCallVisitor() {}
|
2020-04-04 14:31:14 +02:00
|
|
|
void main(AstNetlist* nodep) { iterate(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Combine marking function
|
|
|
|
|
|
|
|
|
|
class CombMarkVisitor : CombBaseVisitor {
|
|
|
|
|
// Mark all nodes under specified one.
|
|
|
|
|
private:
|
|
|
|
|
// OUTPUT:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNode::user3() -> bool. True to indicate duplicated
|
2006-08-26 13:35:28 +02:00
|
|
|
// VISITORS
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
nodep->user3(true);
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-04-15 13:58:34 +02:00
|
|
|
explicit CombMarkVisitor(AstNode* nodep) { iterate(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
virtual ~CombMarkVisitor() {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Combine state, as a visitor of each AstNode
|
|
|
|
|
|
|
|
|
|
class CombineVisitor : CombBaseVisitor {
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Entire netlist:
|
2019-05-19 22:13:13 +02:00
|
|
|
// AstNodeStmt::user() -> bool. True if iterated already
|
|
|
|
|
// AstCFunc::user3p() -> AstCFunc*, If set, replace ccalls to this func with new func
|
|
|
|
|
// AstNodeStmt::user3() -> AstNode*. True if to ignore this cell
|
2020-04-15 13:58:34 +02:00
|
|
|
// AstNodeStmt::user4() -> V3Hashed::V3Hash. Hash value of this node (hash of 0 is
|
|
|
|
|
// illegal)
|
|
|
|
|
AstUser1InUse m_inuser1;
|
|
|
|
|
AstUser3InUse m_inuser3;
|
|
|
|
|
// AstUser4InUse part of V3Hashed
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2020-04-15 13:58:34 +02:00
|
|
|
typedef enum { STATE_IDLE, STATE_HASH, STATE_DUP } CombineState;
|
|
|
|
|
VDouble0 m_statCombs; // Statistic tracking
|
|
|
|
|
CombineState m_state; // Major state
|
|
|
|
|
AstNodeModule* m_modp; // Current module
|
|
|
|
|
AstCFunc* m_funcp; // Current function
|
|
|
|
|
V3Hash m_lowerHash; // Hash of the statement we're building
|
|
|
|
|
CombCallVisitor m_call; // Tracking of function call users
|
|
|
|
|
int m_modNFuncs; // Number of functions made
|
|
|
|
|
AstNode* m_walkLast1p; // Final node that is the same in duplicate list
|
|
|
|
|
AstNode* m_walkLast2p; // Final node that is the same in duplicate list
|
|
|
|
|
V3Hashed m_hashed; // Hash for every node in module
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void hashStatement(AstNode* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Compute hash on entire tree of this statement
|
|
|
|
|
m_hashed.hashAndInsert(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9, " stmthash " << hex << nodep->user4() << " " << nodep << endl);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void hashFunctions(AstCFunc* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Compute hash of all statement trees in the function
|
|
|
|
|
CombineState oldState = m_state;
|
|
|
|
|
{
|
|
|
|
|
m_state = STATE_HASH;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
m_state = oldState;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void walkEmptyFuncs() {
|
2019-05-19 22:13:13 +02:00
|
|
|
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
|
|
|
|
AstNode* node1p = it->second;
|
2018-02-02 03:32:58 +01:00
|
|
|
AstCFunc* oldfuncp = VN_CAST(node1p, CFunc);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (oldfuncp && oldfuncp->emptyBody() && !oldfuncp->dontCombine()) {
|
|
|
|
|
UINFO(5, " EmptyFunc " << std::hex << V3Hash(oldfuncp->user4p()) << " "
|
|
|
|
|
<< oldfuncp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Mark user3p on entire old tree, so we don't process it more
|
|
|
|
|
CombMarkVisitor visitor(oldfuncp);
|
|
|
|
|
m_call.replaceFunc(oldfuncp, NULL);
|
|
|
|
|
oldfuncp->unlinkFrBack();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void walkDupFuncs() {
|
2019-05-19 22:13:13 +02:00
|
|
|
for (V3Hashed::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
|
|
|
|
|
V3Hash hashval = it->first;
|
|
|
|
|
AstNode* node1p = it->second;
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_IS(node1p, CFunc)) continue;
|
2019-07-06 18:57:50 +02:00
|
|
|
UASSERT_OBJ(!hashval.isIllegal(), node1p, "Illegal (unhashed) nodes");
|
2019-05-19 22:13:13 +02:00
|
|
|
for (V3Hashed::iterator eqit = it; eqit != m_hashed.end(); ++eqit) {
|
|
|
|
|
AstNode* node2p = eqit->second;
|
|
|
|
|
if (!(eqit->first == hashval)) break;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (node1p == node2p) continue; // Identical iterator
|
2019-05-19 22:13:13 +02:00
|
|
|
if (node1p->user3p() || node2p->user3p()) continue; // Already merged
|
|
|
|
|
if (node1p->sameTree(node2p)) { // walk of tree has same comparison
|
|
|
|
|
// Replace AstCCall's that point here
|
2018-02-02 03:32:58 +01:00
|
|
|
replaceFuncWFunc(VN_CAST(node2p, CFunc), VN_CAST(node1p, CFunc));
|
2019-05-19 22:13:13 +02:00
|
|
|
// Replacement may promote a slow routine to fast path
|
2018-02-02 03:32:58 +01:00
|
|
|
if (!VN_CAST(node2p, CFunc)->slow()) VN_CAST(node1p, CFunc)->slow(false);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceFuncWFunc(AstCFunc* oldfuncp, AstCFunc* newfuncp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " DupFunc " << std::hex << V3Hash(newfuncp->user4p()) << " " << newfuncp
|
|
|
|
|
<< endl);
|
|
|
|
|
UINFO(5, " and " << std::hex << V3Hash(oldfuncp->user4p()) << " " << oldfuncp
|
|
|
|
|
<< endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
// Mark user3p on entire old tree, so we don't process it more
|
|
|
|
|
++m_statCombs;
|
|
|
|
|
CombMarkVisitor visitor(oldfuncp);
|
|
|
|
|
m_call.replaceFunc(oldfuncp, newfuncp);
|
|
|
|
|
oldfuncp->unlinkFrBack();
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void replaceOnlyCallFunc(AstCCall* nodep) {
|
2018-02-02 03:32:58 +01:00
|
|
|
if (AstCFunc* oldfuncp = VN_CAST(nodep->backp(), CFunc)) {
|
2020-04-15 13:58:34 +02:00
|
|
|
// oldfuncp->dumpTree(cout, "MAYDEL: ");
|
|
|
|
|
if (nodep->nextp() == NULL && oldfuncp->initsp() == NULL && oldfuncp->stmtsp() == nodep
|
|
|
|
|
&& oldfuncp->finalsp() == NULL) {
|
|
|
|
|
UINFO(9, " Function only has call " << oldfuncp << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_call.deleteCall(nodep);
|
|
|
|
|
CombMarkVisitor visitor(oldfuncp);
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceFuncWFunc(oldfuncp, nodep->funcp()), nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void walkDupCodeStart(AstNode* node1p) {
|
2018-08-25 15:52:45 +02:00
|
|
|
V3Hash hashval(node1p->user4p());
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(4," STMT " << hashval << " " << node1p << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
int bestDepth = 0; // Best substitution found in the search
|
|
|
|
|
AstNode* bestNode2p = NULL;
|
|
|
|
|
AstNode* bestLast1p = NULL;
|
|
|
|
|
AstNode* bestLast2p = NULL;
|
|
|
|
|
//
|
2020-04-15 13:58:34 +02:00
|
|
|
std::pair<V3Hashed::iterator, V3Hashed::iterator> eqrange
|
2019-05-19 22:13:13 +02:00
|
|
|
= m_hashed.mmap().equal_range(hashval);
|
|
|
|
|
for (V3Hashed::iterator eqit = eqrange.first; eqit != eqrange.second; ++eqit) {
|
|
|
|
|
AstNode* node2p = eqit->second;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (node1p == node2p) continue;
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
// We need to mark iteration to prevent matching code inside
|
|
|
|
|
// code (abab matching in ababab)
|
|
|
|
|
AstNode::user1ClearTree(); // user1p() used on entire tree
|
|
|
|
|
m_walkLast1p = NULL;
|
|
|
|
|
m_walkLast2p = NULL;
|
|
|
|
|
int depth = walkDupCodeNext(node1p, node2p, 1);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (depth > COMBINE_MIN_STATEMENTS && depth > bestDepth) {
|
2019-05-19 22:13:13 +02:00
|
|
|
bestDepth = depth;
|
|
|
|
|
bestNode2p = node2p;
|
|
|
|
|
bestLast1p = m_walkLast1p;
|
|
|
|
|
bestLast2p = m_walkLast2p;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (bestDepth) {
|
|
|
|
|
// Found a replacement
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(5, " Duplicate of depth " << bestDepth << endl);
|
|
|
|
|
UINFO(5, " DupFunc "
|
|
|
|
|
<< " " << node1p << endl);
|
|
|
|
|
UINFO(5, " and "
|
|
|
|
|
<< " " << bestNode2p << endl);
|
|
|
|
|
UINFO(5, " Through "
|
|
|
|
|
<< " " << bestLast1p << endl);
|
|
|
|
|
UINFO(5, " and "
|
|
|
|
|
<< " " << bestLast2p << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
//
|
|
|
|
|
walkReplace(node1p, bestNode2p, bestLast1p, bestLast2p);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int walkDupCodeNext(AstNode* node1p, AstNode* node2p, int level) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Find number of common statements between the two node1p_nextp's...
|
|
|
|
|
if (node1p->user1p() || node2p->user1p()) return 0; // Already iterated
|
|
|
|
|
if (node1p->user3p() || node2p->user3p()) return 0; // Already merged
|
|
|
|
|
if (!m_hashed.sameNodes(node1p, node2p)) return 0; // walk of tree has same comparison
|
|
|
|
|
V3Hash hashval(node1p->user4p());
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9, " wdup1 "<<level<<" "<<V3Hash(node1p->user4p())<<" "<<node1p<<endl);
|
|
|
|
|
// UINFO(9, " wdup2 "<<level<<" "<<V3Hash(node2p->user4p())<<" "<<node2p<<endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_walkLast1p = node1p;
|
|
|
|
|
m_walkLast2p = node2p;
|
|
|
|
|
node1p->user1(true);
|
|
|
|
|
node2p->user1(true);
|
|
|
|
|
if (node1p->nextp() && node2p->nextp()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return hashval.depth() + walkDupCodeNext(node1p->nextp(), node2p->nextp(), level + 1);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return hashval.depth();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
void walkReplace(AstNode* node1p, AstNode* node2p, AstNode* last1p,
|
|
|
|
|
AstNode* last2p) { // Final node in linked list, maybe null if all statements
|
|
|
|
|
// to be grabbed
|
2019-05-19 22:13:13 +02:00
|
|
|
// Make new function
|
|
|
|
|
string oldname = m_funcp->name();
|
|
|
|
|
string::size_type pos;
|
2020-04-15 13:58:34 +02:00
|
|
|
if ((pos = oldname.find("_common")) != string::npos) oldname.erase(pos);
|
|
|
|
|
if ((pos = oldname.find("__")) != string::npos) oldname.erase(pos);
|
2019-05-19 22:13:13 +02:00
|
|
|
AstCFunc* newfuncp = new AstCFunc(node1p->fileline(),
|
2020-04-15 13:58:34 +02:00
|
|
|
oldname + "_common" + cvtToStr(++m_modNFuncs), NULL);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp->addStmtp(newfuncp);
|
|
|
|
|
// Create calls
|
|
|
|
|
AstCCall* call1p = new AstCCall(node1p->fileline(), newfuncp);
|
|
|
|
|
AstCCall* call2p = new AstCCall(node2p->fileline(), newfuncp);
|
|
|
|
|
// Grab statement bodies
|
|
|
|
|
AstNRelinker relink1Handle;
|
|
|
|
|
AstNRelinker relink2Handle;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode *nextp, *walkp = node1p; 1; walkp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = walkp->nextp();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (walkp == node1p) {
|
|
|
|
|
walkp->unlinkFrBack(&relink1Handle);
|
|
|
|
|
} else {
|
|
|
|
|
walkp->unlinkFrBack();
|
|
|
|
|
node1p->addNext(walkp);
|
|
|
|
|
}
|
|
|
|
|
if (walkp == last1p) break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
for (AstNode *nextp, *walkp = node2p; 1; walkp = nextp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
nextp = walkp->nextp();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (walkp == node2p) {
|
|
|
|
|
walkp->unlinkFrBack(&relink2Handle);
|
|
|
|
|
} else {
|
|
|
|
|
walkp->unlinkFrBack();
|
|
|
|
|
node2p->addNext(walkp);
|
|
|
|
|
}
|
|
|
|
|
if (walkp == last2p) break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
// Move node1 statements to new function
|
|
|
|
|
newfuncp->addStmtsp(node1p);
|
2020-04-15 13:58:34 +02:00
|
|
|
// newfuncp->dumpTree(cout, " newfunctree: ");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Mark node2 statements as dead
|
|
|
|
|
CombMarkVisitor visitor(node2p);
|
|
|
|
|
pushDeletep(node2p); // Delete later
|
|
|
|
|
// Link in new function
|
|
|
|
|
relink1Handle.relink(call1p);
|
|
|
|
|
relink2Handle.relink(call2p);
|
|
|
|
|
// Hash the new function
|
|
|
|
|
hashFunctions(newfuncp);
|
|
|
|
|
m_call.addCall(call1p);
|
|
|
|
|
m_call.addCall(call2p);
|
|
|
|
|
// If either new statement makes a func with only a single call, replace
|
|
|
|
|
// the above callers to call it directly
|
2020-01-17 02:17:11 +01:00
|
|
|
VL_DO_DANGLING(replaceOnlyCallFunc(call1p), call1p);
|
|
|
|
|
VL_DO_DANGLING(replaceOnlyCallFunc(call2p), call2p);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNetlist* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Track all callers of each function
|
|
|
|
|
m_call.main(nodep);
|
|
|
|
|
//
|
2020-04-15 13:58:34 +02:00
|
|
|
// In V3Hashed AstNode::user4ClearTree(); // user4p() used on entire tree
|
2019-05-19 22:13:13 +02:00
|
|
|
// Iterate modules backwards, in bottom-up order.
|
|
|
|
|
// Required so that a module instantiating another can benefit from collapsing.
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildrenBackwards(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNodeModule* nodep) VL_OVERRIDE {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " MOD " << nodep << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_modp = nodep;
|
|
|
|
|
m_modNFuncs = 0;
|
2018-06-15 00:59:24 +02:00
|
|
|
m_walkLast2p = NULL;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_hashed.clear();
|
|
|
|
|
// Compute hash of all statement trees in the function
|
|
|
|
|
m_state = STATE_HASH;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_state = STATE_IDLE;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (debug() >= 9) m_hashed.dumpFilePrefixed("combine");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Walk the hashes removing empty functions
|
2020-04-15 13:58:34 +02:00
|
|
|
if (emptyFunctionDeletion()) walkEmptyFuncs();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Walk the hashes looking for duplicate functions
|
2020-04-15 13:58:34 +02:00
|
|
|
if (duplicateFunctionCombine()) walkDupFuncs();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Walk the statements looking for large replicated code sections
|
|
|
|
|
if (statementCombine()) {
|
|
|
|
|
m_state = STATE_DUP;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_state = STATE_IDLE;
|
|
|
|
|
}
|
|
|
|
|
m_modp = NULL;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstCFunc* nodep) VL_OVERRIDE {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_funcp = nodep;
|
|
|
|
|
if (!nodep->dontCombine()) {
|
|
|
|
|
if (m_state == STATE_HASH) {
|
|
|
|
|
hashStatement(nodep); // Hash the entire function - it might be identical
|
|
|
|
|
} else if (m_state == STATE_DUP) {
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_funcp = NULL;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstNodeStmt* nodep) VL_OVERRIDE {
|
2019-01-06 23:38:27 +01:00
|
|
|
if (!nodep->isStatement()) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_state == STATE_HASH && m_funcp) {
|
|
|
|
|
hashStatement(nodep);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (m_state == STATE_DUP && m_funcp) {
|
2019-05-19 22:13:13 +02:00
|
|
|
walkDupCodeStart(nodep);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
2020-01-21 23:35:56 +01:00
|
|
|
virtual void visit(AstVar*) VL_OVERRIDE {}
|
|
|
|
|
virtual void visit(AstTraceDecl*) VL_OVERRIDE {}
|
|
|
|
|
virtual void visit(AstTraceInc*) VL_OVERRIDE {}
|
2020-04-04 14:31:14 +02:00
|
|
|
virtual void visit(AstNode* nodep) VL_OVERRIDE { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2015-10-04 04:33:06 +02:00
|
|
|
explicit CombineVisitor(AstNetlist* nodep) {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_state = STATE_IDLE;
|
2018-06-15 01:04:52 +02:00
|
|
|
m_modp = NULL;
|
|
|
|
|
m_funcp = NULL;
|
|
|
|
|
m_modNFuncs = 0;
|
|
|
|
|
m_walkLast1p = NULL;
|
|
|
|
|
m_walkLast2p = NULL;
|
2018-05-11 02:55:37 +02:00
|
|
|
iterate(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
virtual ~CombineVisitor() { //
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Stats::addStat("Optimizations, Combined CFuncs", m_statCombs);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Combine class functions
|
|
|
|
|
|
|
|
|
|
void V3Combine::combineAll(AstNetlist* nodep) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
|
|
|
|
{ CombineVisitor visitor(nodep); } // Destruct before checking
|
2017-09-18 04:52:57 +02:00
|
|
|
V3Global::dumpCheckGlobalTree("combine", 0, v3Global.opt.dumpTreeLevel(__FILE__) >= 3);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|