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
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// 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
|
|
|
//
|
2021-05-17 14:26:24 +02:00
|
|
|
// Combine identical CFuncs by retaining only a single copy
|
|
|
|
|
// Also drop empty CFuncs
|
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"
|
2021-05-21 02:41:46 +02:00
|
|
|
#include "V3DupFinder.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
#include "V3Stats.h"
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
|
2018-10-14 19:43:24 +02:00
|
|
|
#include <algorithm>
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class CombBaseVisitor VL_NOT_FINAL : public VNVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
protected:
|
|
|
|
|
// STATE
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
// METHODS
|
2020-11-17 01:56:16 +01:00
|
|
|
virtual ~CombBaseVisitor() override = default;
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Combine replacement function
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class CombCallVisitor final : CombBaseVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
// Find all CCALLS of each CFUNC, so that we can later rename them
|
|
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
2021-05-17 14:26:24 +02:00
|
|
|
std::multimap<AstCFunc*, AstCCall*> 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);
|
|
|
|
|
}
|
2020-05-07 15:25:57 +02:00
|
|
|
// Note: m_callMmap modified in loop, so not using equal_range.
|
2021-03-12 23:26:53 +01:00
|
|
|
for (auto it = m_callMmap.find(oldfuncp); it != m_callMmap.end();
|
2020-05-07 15:25:57 +02:00
|
|
|
it = m_callMmap.find(oldfuncp)) {
|
2021-05-17 14:26:24 +02:00
|
|
|
AstCCall* const oldp = it->second;
|
|
|
|
|
UINFO(4, " Called " << oldp << endl);
|
|
|
|
|
UASSERT_OBJ(oldp->funcp() == oldfuncp, oldp,
|
|
|
|
|
"Call list broken, points to call w/different func");
|
|
|
|
|
if (newfuncp) {
|
|
|
|
|
// Replace call to oldfuncp with call to newfuncp
|
|
|
|
|
AstNode* const argsp
|
|
|
|
|
= oldp->argsp() ? oldp->argsp()->unlinkFrBackWithNext() : nullptr;
|
|
|
|
|
AstCCall* const newp = new AstCCall(oldp->fileline(), newfuncp, argsp);
|
2021-06-13 15:33:11 +02:00
|
|
|
newp->selfPointer(oldp->selfPointer());
|
2021-05-17 14:26:24 +02:00
|
|
|
newp->argTypes(oldp->argTypes());
|
|
|
|
|
addCall(newp); // Fix the table, in case the newfuncp itself gets replaced
|
|
|
|
|
oldp->replaceWith(newp);
|
|
|
|
|
} else {
|
|
|
|
|
// Just deleting empty function
|
|
|
|
|
oldp->unlinkFrBack();
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2021-05-17 14:26:24 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(oldp), oldp);
|
|
|
|
|
m_callMmap.erase(it); // Fix the table, This call has been replaced
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
// METHODS
|
2021-06-13 15:33:11 +02:00
|
|
|
void addCall(AstCCall* nodep) { m_callMmap.emplace(nodep->funcp(), nodep); }
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
|
|
|
|
// VISITORS
|
2021-06-13 15:33:11 +02:00
|
|
|
virtual void visit(AstCCall* nodep) override {
|
|
|
|
|
if (nodep->funcp()->dontCombine()) return;
|
|
|
|
|
addCall(nodep);
|
|
|
|
|
}
|
|
|
|
|
// LCOV_EXCL_START
|
|
|
|
|
virtual void visit(AstAddrOfCFunc* nodep) override {
|
|
|
|
|
// We cannot yet handle references via AstAddrOfCFunc, but currently those are
|
|
|
|
|
// only used in tracing functions, which are not combined. Blow up in case this changes.
|
|
|
|
|
if (nodep->funcp()->dontCombine()) return;
|
|
|
|
|
nodep->v3fatalSrc(
|
|
|
|
|
"Don't know how to combine functions that are referenced via AstAddrOfCFunc");
|
|
|
|
|
}
|
|
|
|
|
// LCOV_EXCL_END
|
2006-08-26 13:35:28 +02:00
|
|
|
// Speed things up
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeAssign*) override {}
|
|
|
|
|
virtual void visit(AstNodeMath*) override {}
|
|
|
|
|
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 14:31:14 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-11-17 01:56:16 +01:00
|
|
|
CombCallVisitor() = default;
|
|
|
|
|
virtual ~CombCallVisitor() override = default;
|
2020-04-04 14:31:14 +02:00
|
|
|
void main(AstNetlist* nodep) { iterate(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Combine state, as a visitor of each AstNode
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class CombineVisitor final : CombBaseVisitor {
|
2006-08-26 13:35:28 +02:00
|
|
|
private:
|
|
|
|
|
// NODE STATE
|
|
|
|
|
// Entire netlist:
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser3InUse m_user3InUse; // Marks replaced AstCFuncs
|
|
|
|
|
// VNUser4InUse part of V3Hasher in V3DupFinder
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// STATE
|
2021-05-17 14:26:24 +02:00
|
|
|
VDouble0 m_cfuncsCombined; // Statistic tracking
|
2020-04-15 13:58:34 +02:00
|
|
|
CombCallVisitor m_call; // Tracking of function call users
|
2021-05-21 02:41:46 +02:00
|
|
|
V3DupFinder m_dupFinder; // Duplicate finder for CFuncs in module
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
void walkEmptyFuncs() {
|
2021-05-21 02:41:46 +02:00
|
|
|
for (const auto& itr : m_dupFinder) {
|
2021-10-22 14:56:48 +02:00
|
|
|
AstCFunc* const oldfuncp = VN_AS(itr.second, CFunc);
|
2021-05-17 14:26:24 +02:00
|
|
|
UASSERT_OBJ(oldfuncp, itr.second, "Not a CFunc in hash");
|
|
|
|
|
if (!oldfuncp->emptyBody()) continue;
|
|
|
|
|
UASSERT_OBJ(!oldfuncp->dontCombine(), oldfuncp,
|
|
|
|
|
"dontCombine function should not be in hash");
|
|
|
|
|
|
|
|
|
|
// Remove calls to empty function
|
|
|
|
|
UASSERT_OBJ(!oldfuncp->user3(), oldfuncp, "Should not be processed yet");
|
2021-05-21 15:34:27 +02:00
|
|
|
UINFO(5, " Drop empty CFunc " << itr.first << " " << oldfuncp << endl);
|
2021-05-17 14:26:24 +02:00
|
|
|
oldfuncp->user3SetOnce(); // Mark replaced
|
|
|
|
|
m_call.replaceFunc(oldfuncp, nullptr);
|
|
|
|
|
oldfuncp->unlinkFrBack();
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2021-05-17 14:26:24 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void walkDupFuncs() {
|
2020-12-14 03:53:55 +01:00
|
|
|
// Do non-slow first as then favors naming functions based on fast name
|
2021-05-17 14:26:24 +02:00
|
|
|
for (const bool slow : {false, true}) {
|
2021-05-21 02:41:46 +02:00
|
|
|
for (auto newIt = m_dupFinder.begin(); newIt != m_dupFinder.end(); ++newIt) {
|
2021-10-22 14:56:48 +02:00
|
|
|
AstCFunc* const newfuncp = VN_AS(newIt->second, CFunc);
|
2021-05-17 14:26:24 +02:00
|
|
|
UASSERT_OBJ(newfuncp, newIt->second, "Not a CFunc in hash");
|
|
|
|
|
if (newfuncp->user3()) continue; // Already replaced
|
|
|
|
|
if (newfuncp->slow() != slow) continue;
|
|
|
|
|
auto oldIt = newIt;
|
|
|
|
|
++oldIt; // Skip over current position
|
2021-05-21 02:41:46 +02:00
|
|
|
for (; oldIt != m_dupFinder.end(); ++oldIt) {
|
2021-10-22 14:56:48 +02:00
|
|
|
AstCFunc* const oldfuncp = VN_AS(oldIt->second, CFunc);
|
2021-05-17 14:26:24 +02:00
|
|
|
UASSERT_OBJ(oldfuncp, oldIt->second, "Not a CFunc in hash");
|
|
|
|
|
UASSERT_OBJ(newfuncp != oldfuncp, newfuncp,
|
|
|
|
|
"Same function hashed multiple times");
|
|
|
|
|
if (newIt->first != oldIt->first) break; // Iterate over same hashes only
|
|
|
|
|
if (oldfuncp->user3()) continue; // Already replaced
|
|
|
|
|
if (!newfuncp->sameTree(oldfuncp)) continue; // Different functions
|
|
|
|
|
|
|
|
|
|
// Replace calls to oldfuncp with calls to newfuncp
|
2021-05-21 15:34:27 +02:00
|
|
|
UINFO(5, " Replace CFunc " << newIt->first << " " << newfuncp << endl);
|
|
|
|
|
UINFO(5, " with " << oldIt->first << " " << oldfuncp << endl);
|
2021-05-17 14:26:24 +02:00
|
|
|
++m_cfuncsCombined;
|
|
|
|
|
oldfuncp->user3SetOnce(); // Mark replaced
|
|
|
|
|
m_call.replaceFunc(oldfuncp, newfuncp);
|
|
|
|
|
oldfuncp->unlinkFrBack();
|
|
|
|
|
// Replacement may promote a slow routine to fast path
|
|
|
|
|
if (!oldfuncp->slow()) newfuncp->slow(false);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(oldfuncp), oldfuncp);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNetlist* nodep) override {
|
2021-05-17 14:26:24 +02:00
|
|
|
m_call.main(nodep); // Track all call sites of each function
|
|
|
|
|
iterateChildren(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNodeModule* nodep) override {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(4, " MOD " << nodep << endl);
|
2021-05-21 02:41:46 +02:00
|
|
|
m_dupFinder.clear();
|
2021-05-17 14:26:24 +02:00
|
|
|
// Compute hash of all CFuncs in the module
|
2018-05-11 02:55:37 +02:00
|
|
|
iterateChildren(nodep);
|
2021-05-21 02:41:46 +02:00
|
|
|
if (debug() >= 9) m_dupFinder.dumpFilePrefixed("combine");
|
2019-05-19 22:13:13 +02:00
|
|
|
// Walk the hashes removing empty functions
|
2020-06-05 03:55:00 +02:00
|
|
|
walkEmptyFuncs();
|
2019-05-19 22:13:13 +02:00
|
|
|
// Walk the hashes looking for duplicate functions
|
2020-06-05 03:55:00 +02:00
|
|
|
walkDupFuncs();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstCFunc* nodep) override {
|
2021-05-17 14:26:24 +02:00
|
|
|
if (nodep->dontCombine()) return;
|
|
|
|
|
// Hash the entire function
|
2021-05-21 02:41:46 +02:00
|
|
|
m_dupFinder.insert(nodep);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
// Default: Just iterate
|
2021-05-17 14:26:24 +02:00
|
|
|
virtual void visit(AstVar*) override {} // Accelerate
|
|
|
|
|
virtual void visit(AstNodeStmt* nodep) override {} // Accelerate
|
2020-08-15 16:03:34 +02:00
|
|
|
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
public:
|
2019-09-12 13:22:22 +02:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit CombineVisitor(AstNetlist* nodep) { iterate(nodep); }
|
2021-05-17 14:26:24 +02:00
|
|
|
virtual ~CombineVisitor() override {
|
|
|
|
|
V3Stats::addStat("Optimizations, Combined CFuncs", m_cfuncsCombined);
|
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);
|
2021-11-26 16:52:36 +01:00
|
|
|
{ CombineVisitor{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
|
|
|
}
|