2012-04-12 21:08:20 -04:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 11:35:28 +00:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: Lifelicate variable assignment elimination
|
|
|
|
|
//
|
2019-11-07 22:33:59 -05:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2023-01-01 10:18:39 -05:00
|
|
|
// Copyright 2003-2023 by Wilson Snyder. This program is free software; you
|
2020-03-21 11:24:24 -04:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 17:07:57 -04:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 11:24:24 -04:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 11:35:28 +00:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// LIFE TRANSFORMATIONS:
|
2019-05-19 16:13:13 -04:00
|
|
|
// Build control-flow graph with assignments and var usages
|
|
|
|
|
// All modules:
|
|
|
|
|
// ASSIGN(x,...), ASSIGN(x,...) => delete first one
|
|
|
|
|
// We also track across if statements:
|
|
|
|
|
// ASSIGN(X,...) IF( ..., ASSIGN(X,...), ASSIGN(X,...)) => deletes first
|
|
|
|
|
// We don't do the opposite yet though (remove assigns in if followed by outside if)
|
2008-06-09 21:25:10 -04:00
|
|
|
//
|
2006-08-26 11:35:28 +00:00
|
|
|
//*************************************************************************
|
2019-10-04 20:17:11 -04:00
|
|
|
|
2023-09-25 04:12:23 +02:00
|
|
|
#define VL_MT_DISABLED_CODE_UNIT 1
|
|
|
|
|
|
2006-12-18 19:20:45 +00:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
#include "V3Life.h"
|
2022-08-05 10:56:57 +01:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
#include "V3Ast.h"
|
2006-09-26 15:05:35 +00:00
|
|
|
#include "V3Const.h"
|
2022-08-05 10:56:57 +01:00
|
|
|
#include "V3Global.h"
|
|
|
|
|
#include "V3Stats.h"
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2018-10-14 13:43:24 -04:00
|
|
|
#include <map>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2022-09-18 20:53:42 +01:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
//######################################################################
|
2006-09-26 15:05:35 +00:00
|
|
|
// Structure for global state
|
|
|
|
|
|
2020-11-18 21:32:16 -05:00
|
|
|
class LifeState final {
|
2008-11-21 15:50:33 -05:00
|
|
|
// NODE STATE
|
|
|
|
|
// See below
|
2022-01-02 13:56:40 -05:00
|
|
|
const VNUser1InUse m_inuser1;
|
2008-11-21 15:50:33 -05:00
|
|
|
|
2006-09-26 15:05:35 +00:00
|
|
|
// STATE
|
|
|
|
|
public:
|
2019-10-05 07:54:14 -04:00
|
|
|
VDouble0 m_statAssnDel; // Statistic tracking
|
|
|
|
|
VDouble0 m_statAssnCon; // Statistic tracking
|
2018-02-01 21:24:41 -05:00
|
|
|
std::vector<AstNode*> m_unlinkps;
|
2006-09-26 15:05:35 +00:00
|
|
|
|
2009-10-14 08:26:30 -04:00
|
|
|
// CONSTRUCTORS
|
2020-11-16 19:56:16 -05:00
|
|
|
LifeState() = default;
|
2006-09-26 15:05:35 +00:00
|
|
|
~LifeState() {
|
2019-05-19 16:13:13 -04:00
|
|
|
V3Stats::addStatSum("Optimizations, Lifetime assign deletions", m_statAssnDel);
|
|
|
|
|
V3Stats::addStatSum("Optimizations, Lifetime constant prop", m_statAssnCon);
|
2020-08-16 11:43:49 -04:00
|
|
|
for (AstNode* ip : m_unlinkps) {
|
|
|
|
|
ip->unlinkFrBack();
|
|
|
|
|
ip->deleteTree();
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
2009-10-14 08:26:30 -04:00
|
|
|
// METHODS
|
|
|
|
|
void pushUnlinkDeletep(AstNode* nodep) { m_unlinkps.push_back(nodep); }
|
2006-09-26 15:05:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Structure for each variable encountered
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2020-11-18 21:32:16 -05:00
|
|
|
class LifeVarEntry final {
|
2020-11-10 21:40:14 -05:00
|
|
|
// Last assignment to this varscope, nullptr if no longer relevant
|
|
|
|
|
AstNodeAssign* m_assignp = nullptr;
|
|
|
|
|
AstConst* m_constp = nullptr; // Known constant value
|
2020-04-15 07:58:34 -04:00
|
|
|
// First access was a set (and thus block above may have a set that can be deleted
|
|
|
|
|
bool m_setBeforeUse;
|
|
|
|
|
// Was ever assigned (and thus above block may not preserve constant propagation)
|
2020-11-10 21:40:14 -05:00
|
|
|
bool m_everSet = false;
|
2020-04-15 07:58:34 -04:00
|
|
|
|
2006-08-26 11:35:28 +00:00
|
|
|
public:
|
2006-09-26 15:05:35 +00:00
|
|
|
class SIMPLEASSIGN {};
|
|
|
|
|
class COMPLEXASSIGN {};
|
|
|
|
|
class CONSUMED {};
|
|
|
|
|
|
2020-11-10 21:40:14 -05:00
|
|
|
LifeVarEntry(SIMPLEASSIGN, AstNodeAssign* assp)
|
|
|
|
|
: m_setBeforeUse{true} {
|
2020-04-15 07:58:34 -04:00
|
|
|
simpleAssign(assp);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2020-11-10 21:40:14 -05:00
|
|
|
explicit LifeVarEntry(COMPLEXASSIGN)
|
|
|
|
|
: m_setBeforeUse{false} {
|
2020-04-15 07:58:34 -04:00
|
|
|
complexAssign();
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
2020-11-10 21:40:14 -05:00
|
|
|
explicit LifeVarEntry(CONSUMED)
|
|
|
|
|
: m_setBeforeUse{false} {
|
2020-04-15 07:58:34 -04:00
|
|
|
consumed();
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
2020-11-16 19:56:16 -05:00
|
|
|
~LifeVarEntry() = default;
|
2022-09-16 13:17:38 +01:00
|
|
|
void simpleAssign(AstNodeAssign* assp) { // New simple A=.... assignment
|
2019-05-19 16:13:13 -04:00
|
|
|
m_assignp = assp;
|
2020-08-15 10:12:55 -04:00
|
|
|
m_constp = nullptr;
|
2019-05-19 16:13:13 -04:00
|
|
|
m_everSet = true;
|
2021-10-22 13:56:48 +01:00
|
|
|
if (VN_IS(assp->rhsp(), Const)) m_constp = VN_AS(assp->rhsp(), Const);
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
2022-09-16 13:17:38 +01:00
|
|
|
void complexAssign() { // A[x]=... or some complicated assignment
|
2020-08-15 10:12:55 -04:00
|
|
|
m_assignp = nullptr;
|
|
|
|
|
m_constp = nullptr;
|
2019-05-19 16:13:13 -04:00
|
|
|
m_everSet = true;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 13:17:38 +01:00
|
|
|
void consumed() { // Rvalue read of A
|
2020-08-15 10:12:55 -04:00
|
|
|
m_assignp = nullptr;
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
AstNodeAssign* assignp() const { return m_assignp; }
|
|
|
|
|
AstConst* constNodep() const { return m_constp; }
|
|
|
|
|
bool setBeforeUse() const { return m_setBeforeUse; }
|
|
|
|
|
bool everSet() const { return m_everSet; }
|
2006-08-26 11:35:28 +00:00
|
|
|
};
|
|
|
|
|
|
2006-09-26 15:05:35 +00:00
|
|
|
//######################################################################
|
|
|
|
|
// Structure for all variables under a given meta-basic block
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2020-11-18 21:32:16 -05:00
|
|
|
class LifeBlock final {
|
2006-09-26 15:05:35 +00:00
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared each AstIf:
|
2019-05-19 16:13:13 -04:00
|
|
|
// AstVarScope::user1() -> int. Used in combining to detect duplicates
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
// LIFE MAP
|
|
|
|
|
// For each basic block, we'll make a new map of what variables that if/else is changing
|
2021-03-12 18:10:45 -05:00
|
|
|
using LifeMap = std::unordered_map<AstVarScope*, LifeVarEntry>;
|
2020-04-15 07:58:34 -04:00
|
|
|
LifeMap m_map; // Current active lifetime map for current scope
|
2022-01-01 11:46:49 -05:00
|
|
|
LifeBlock* const m_aboveLifep; // Upper life, or nullptr
|
|
|
|
|
LifeState* const m_statep; // Current global state
|
2022-09-24 20:44:00 +01:00
|
|
|
bool m_replacedVref = false; // Replaced a variable reference since last clearing
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2006-09-26 15:05:35 +00:00
|
|
|
public:
|
2022-01-01 11:46:49 -05:00
|
|
|
LifeBlock(LifeBlock* aboveLifep, LifeState* statep)
|
|
|
|
|
: m_aboveLifep{aboveLifep} // Null if top
|
|
|
|
|
, m_statep{statep} {}
|
2020-11-16 19:56:16 -05:00
|
|
|
~LifeBlock() = default;
|
2006-08-26 11:35:28 +00:00
|
|
|
// METHODS
|
2006-09-26 15:05:35 +00:00
|
|
|
void checkRemoveAssign(const LifeMap::iterator& it) {
|
2021-11-13 13:50:44 -05:00
|
|
|
const AstVar* const varp = it->first->varp();
|
|
|
|
|
LifeVarEntry* const entp = &(it->second);
|
2022-10-20 12:31:00 +02:00
|
|
|
if (!varp->isSigPublic() && !varp->isUsedVirtIface()) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Rather than track what sigs AstUCFunc/AstUCStmt may change,
|
|
|
|
|
// we just don't optimize any public sigs
|
|
|
|
|
// Check the var entry, and remove if appropriate
|
2021-11-13 13:50:44 -05:00
|
|
|
if (AstNode* const oldassp = entp->assignp()) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(7, " PREV: " << oldassp << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
// Redundant assignment, in same level block
|
|
|
|
|
// Don't delete it now as it will confuse iteration since it maybe WAY
|
|
|
|
|
// above our current iteration point.
|
2022-11-27 08:31:22 -05:00
|
|
|
if (debug() > 4) oldassp->dumpTree("- REMOVE/SAMEBLK: ");
|
2019-05-19 16:13:13 -04:00
|
|
|
entp->complexAssign();
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(m_statep->pushUnlinkDeletep(oldassp), oldassp);
|
2019-05-19 16:13:13 -04:00
|
|
|
++m_statep->m_statAssnDel;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
void simpleAssign(AstVarScope* nodep, AstNodeAssign* assp) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Do we have a old assignment we can nuke?
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " ASSIGNof: " << nodep << endl);
|
|
|
|
|
UINFO(7, " new: " << assp << endl);
|
2020-08-16 11:43:49 -04:00
|
|
|
const auto it = m_map.find(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
checkRemoveAssign(it);
|
|
|
|
|
it->second.simpleAssign(assp);
|
|
|
|
|
} else {
|
2022-09-15 19:58:01 -04:00
|
|
|
m_map.emplace(nodep, LifeVarEntry{LifeVarEntry::SIMPLEASSIGN{}, assp});
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2020-04-15 07:58:34 -04:00
|
|
|
// lifeDump();
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
void complexAssign(AstVarScope* nodep) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " clearof: " << nodep << endl);
|
2020-08-16 11:43:49 -04:00
|
|
|
const auto it = m_map.find(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
it->second.complexAssign();
|
|
|
|
|
} else {
|
2022-09-15 19:58:01 -04:00
|
|
|
m_map.emplace(nodep, LifeVarEntry{LifeVarEntry::COMPLEXASSIGN{}});
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-24 20:44:00 +01:00
|
|
|
void clearReplaced() { m_replacedVref = false; }
|
|
|
|
|
bool replaced() const { return m_replacedVref; }
|
2018-08-25 09:52:45 -04:00
|
|
|
void varUsageReplace(AstVarScope* nodep, AstVarRef* varrefp) {
|
2022-03-30 20:17:59 -04:00
|
|
|
// Variable rvalue. If it references a constant, we can replace it
|
2020-08-16 11:43:49 -04:00
|
|
|
const auto it = m_map.find(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (it != m_map.end()) {
|
2021-11-13 13:50:44 -05:00
|
|
|
if (AstConst* const constp = it->second.constNodep()) {
|
2022-10-20 12:31:00 +02:00
|
|
|
if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->isUsedVirtIface()) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Aha, variable is constant; substitute in.
|
|
|
|
|
// We'll later constant propagate
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " replaceconst: " << varrefp << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
varrefp->replaceWith(constp->cloneTree(false));
|
2022-09-24 20:44:00 +01:00
|
|
|
m_replacedVref = true;
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(varrefp->deleteTree(), varrefp);
|
2019-05-19 16:13:13 -04:00
|
|
|
++m_statep->m_statAssnCon;
|
|
|
|
|
return; // **DONE, no longer a var reference**
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " usage: " << nodep << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
it->second.consumed();
|
|
|
|
|
} else {
|
2022-09-15 19:58:01 -04:00
|
|
|
m_map.emplace(nodep, LifeVarEntry{LifeVarEntry::CONSUMED{}});
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
void complexAssignFind(AstVarScope* nodep) {
|
2020-08-16 11:43:49 -04:00
|
|
|
const auto it = m_map.find(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (it != m_map.end()) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " casfind: " << it->first << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
it->second.complexAssign();
|
|
|
|
|
} else {
|
2022-09-15 19:58:01 -04:00
|
|
|
m_map.emplace(nodep, LifeVarEntry{LifeVarEntry::COMPLEXASSIGN{}});
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
|
|
|
|
void consumedFind(AstVarScope* nodep) {
|
2020-08-16 11:43:49 -04:00
|
|
|
const auto it = m_map.find(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
if (it != m_map.end()) {
|
|
|
|
|
it->second.consumed();
|
|
|
|
|
} else {
|
2022-09-15 19:58:01 -04:00
|
|
|
m_map.emplace(nodep, LifeVarEntry{LifeVarEntry::CONSUMED{}});
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
|
|
|
|
void lifeToAbove() {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Any varrefs under a if/else branch affect statements outside and after the if/else
|
|
|
|
|
if (!m_aboveLifep) v3fatalSrc("Pushing life when already at the top level");
|
2022-06-15 18:49:32 -04:00
|
|
|
for (auto& itr : m_map) {
|
|
|
|
|
AstVarScope* const nodep = itr.first;
|
2019-05-19 16:13:13 -04:00
|
|
|
m_aboveLifep->complexAssignFind(nodep);
|
2022-06-15 18:49:32 -04:00
|
|
|
if (itr.second.everSet()) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Record there may be an assignment, so we don't constant propagate across the if.
|
|
|
|
|
complexAssignFind(nodep);
|
|
|
|
|
} else {
|
|
|
|
|
// Record consumption, so we don't eliminate earlier assignments
|
|
|
|
|
consumedFind(nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2018-08-25 09:52:45 -04:00
|
|
|
void dualBranch(LifeBlock* life1p, LifeBlock* life2p) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Find any common sets on both branches of IF and propagate upwards
|
2020-04-15 07:58:34 -04:00
|
|
|
// life1p->lifeDump();
|
|
|
|
|
// life2p->lifeDump();
|
2019-05-19 16:13:13 -04:00
|
|
|
AstNode::user1ClearTree(); // user1p() used on entire tree
|
2022-06-15 18:49:32 -04:00
|
|
|
for (auto& itr : life1p->m_map) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// When the if branch sets a var before it's used, mark that variable
|
2022-06-15 18:49:32 -04:00
|
|
|
if (itr.second.setBeforeUse()) itr.first->user1(1);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2022-06-15 18:49:32 -04:00
|
|
|
for (auto& itr : life2p->m_map) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// When the else branch sets a var before it's used
|
2022-06-15 18:49:32 -04:00
|
|
|
AstVarScope* const nodep = itr.first;
|
|
|
|
|
if (itr.second.setBeforeUse() && nodep->user1()) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Both branches set the var, we can remove the assignment before the IF.
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, "DUALBRANCH " << nodep << endl);
|
2020-08-16 11:43:49 -04:00
|
|
|
const auto itab = m_map.find(nodep);
|
2020-04-15 07:58:34 -04:00
|
|
|
if (itab != m_map.end()) checkRemoveAssign(itab);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 07:58:34 -04:00
|
|
|
// this->lifeDump();
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-06-16 00:11:03 +02:00
|
|
|
void clear() { m_map.clear(); }
|
2006-09-26 15:05:35 +00:00
|
|
|
// DEBUG
|
|
|
|
|
void lifeDump() {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(5, " LifeMap:" << endl);
|
2022-06-15 18:49:32 -04:00
|
|
|
for (const auto& itr : m_map) {
|
|
|
|
|
UINFO(5, " Ent: " << (itr.second.setBeforeUse() ? "[F] " : " ") << itr.first
|
2020-04-15 07:58:34 -04:00
|
|
|
<< endl);
|
2022-06-15 18:49:32 -04:00
|
|
|
if (itr.second.assignp()) { //
|
|
|
|
|
UINFO(5, " Ass: " << itr.second.assignp() << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Life state, as a visitor of each AstNode
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2022-01-02 13:56:40 -05:00
|
|
|
class LifeVisitor final : public VNVisitor {
|
2006-09-26 15:05:35 +00:00
|
|
|
private:
|
|
|
|
|
// STATE
|
2022-01-01 11:46:49 -05:00
|
|
|
LifeState* const m_statep; // Current state
|
2020-08-15 13:11:27 -04:00
|
|
|
bool m_sideEffect = false; // Side effects discovered in assign RHS
|
|
|
|
|
bool m_noopt = false; // Disable optimization of variables in this block
|
|
|
|
|
bool m_tracingCall = false; // Iterating into a CCall to a CFunc
|
2006-09-26 15:05:35 +00:00
|
|
|
|
|
|
|
|
// LIFE MAP
|
|
|
|
|
// For each basic block, we'll make a new map of what variables that if/else is changing
|
2021-03-12 18:10:45 -05:00
|
|
|
using LifeMap = std::unordered_map<AstVarScope*, LifeVarEntry>;
|
2011-08-04 21:58:45 -04:00
|
|
|
// cppcheck-suppress memleak // cppcheck bug - it is deleted
|
2020-04-15 07:58:34 -04:00
|
|
|
LifeBlock* m_lifep; // Current active lifetime map for current scope
|
2006-09-26 15:05:35 +00:00
|
|
|
|
|
|
|
|
// METHODS
|
2022-06-16 00:11:03 +02:00
|
|
|
void setNoopt() {
|
|
|
|
|
m_noopt = true;
|
|
|
|
|
m_lifep->clear();
|
|
|
|
|
}
|
2009-01-21 16:56:50 -05:00
|
|
|
|
2006-09-26 15:05:35 +00:00
|
|
|
// VISITORS
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstVarRef* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Consumption/generation of a variable,
|
|
|
|
|
// it's used so can't elim assignment before this use.
|
2020-08-15 10:12:55 -04:00
|
|
|
UASSERT_OBJ(nodep->varScopep(), nodep, "nullptr");
|
2019-05-19 16:13:13 -04:00
|
|
|
//
|
2021-11-13 13:50:44 -05:00
|
|
|
AstVarScope* const vscp = nodep->varScopep();
|
2019-07-06 12:57:50 -04:00
|
|
|
UASSERT_OBJ(vscp, nodep, "Scope not assigned");
|
2020-11-07 10:37:55 -05:00
|
|
|
if (nodep->access().isWriteOrRW()) {
|
2019-07-05 20:09:56 -04:00
|
|
|
m_sideEffect = true; // $sscanf etc may have RHS vars that are lvalues
|
2019-05-19 16:13:13 -04:00
|
|
|
m_lifep->complexAssign(vscp);
|
|
|
|
|
} else {
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(m_lifep->varUsageReplace(vscp, nodep), nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNodeAssign* nodep) override {
|
2022-08-22 14:26:32 +02:00
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
// V3Life doesn't understand time sense - don't optimize
|
|
|
|
|
setNoopt();
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-19 16:13:13 -04:00
|
|
|
// Collect any used variables first, as lhs may also be on rhs
|
|
|
|
|
// Similar code in V3Dead
|
|
|
|
|
m_sideEffect = false;
|
2022-09-24 20:44:00 +01:00
|
|
|
m_lifep->clearReplaced();
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->rhsp());
|
2022-09-24 20:44:00 +01:00
|
|
|
if (m_lifep->replaced()) {
|
2019-05-19 16:13:13 -04:00
|
|
|
// We changed something, try to constant propagate, but don't delete the
|
|
|
|
|
// assignment as we still need nodep to remain.
|
|
|
|
|
V3Const::constifyEdit(nodep->rhsp()); // rhsp may change
|
|
|
|
|
}
|
|
|
|
|
// Has to be direct assignment without any EXTRACTing.
|
2018-02-01 21:32:58 -05:00
|
|
|
if (VN_IS(nodep->lhsp(), VarRef) && !m_sideEffect && !m_noopt) {
|
2021-11-13 13:50:44 -05:00
|
|
|
AstVarScope* const vscp = VN_AS(nodep->lhsp(), VarRef)->varScopep();
|
2019-07-06 12:57:50 -04:00
|
|
|
UASSERT_OBJ(vscp, nodep, "Scope lost on variable");
|
2019-05-19 16:13:13 -04:00
|
|
|
m_lifep->simpleAssign(vscp, nodep);
|
|
|
|
|
} else {
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->lhsp());
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstAssignDly* nodep) override {
|
2022-08-22 14:26:32 +02:00
|
|
|
// V3Life doesn't understand time sense
|
|
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
// Don't optimize
|
|
|
|
|
setNoopt();
|
|
|
|
|
}
|
|
|
|
|
// Don't treat as normal assign
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
2008-06-09 21:25:10 -04:00
|
|
|
//---- Track control flow changes
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNodeIf* nodep) override {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " IF " << nodep << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
// Condition is part of PREVIOUS block
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->condp());
|
2021-11-13 13:50:44 -05:00
|
|
|
LifeBlock* const prevLifep = m_lifep;
|
2022-11-20 13:11:01 -05:00
|
|
|
LifeBlock* const ifLifep = new LifeBlock{prevLifep, m_statep};
|
|
|
|
|
LifeBlock* const elseLifep = new LifeBlock{prevLifep, m_statep};
|
2019-05-19 16:13:13 -04:00
|
|
|
{
|
|
|
|
|
m_lifep = ifLifep;
|
2022-09-15 19:43:56 +01:00
|
|
|
iterateAndNextNull(nodep->thensp());
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
m_lifep = elseLifep;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->elsesp());
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2019-10-24 21:48:45 -04:00
|
|
|
m_lifep = prevLifep;
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " join " << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
// Find sets on both flows
|
2018-08-25 09:52:45 -04:00
|
|
|
m_lifep->dualBranch(ifLifep, elseLifep);
|
2019-05-19 16:13:13 -04:00
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
ifLifep->lifeToAbove();
|
|
|
|
|
elseLifep->lifeToAbove();
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(delete ifLifep, ifLifep);
|
|
|
|
|
VL_DO_DANGLING(delete elseLifep, elseLifep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstWhile* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
// While's are a problem, as we don't allow loops in the graph. We
|
|
|
|
|
// may go around the cond/body multiple times. Thus a
|
|
|
|
|
// lifelication just in the body is ok, but we can't delete an
|
|
|
|
|
// assignment in the body that's used in the cond. (And otherwise
|
|
|
|
|
// would because it only appears used after-the-fact. So, we model
|
|
|
|
|
// it as a IF statement, and just don't allow elimination of
|
|
|
|
|
// variables across the body.
|
2021-11-13 13:50:44 -05:00
|
|
|
LifeBlock* const prevLifep = m_lifep;
|
2022-11-20 13:11:01 -05:00
|
|
|
LifeBlock* const condLifep = new LifeBlock{prevLifep, m_statep};
|
|
|
|
|
LifeBlock* const bodyLifep = new LifeBlock{prevLifep, m_statep};
|
2019-05-19 16:13:13 -04:00
|
|
|
{
|
|
|
|
|
m_lifep = condLifep;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->precondsp());
|
|
|
|
|
iterateAndNextNull(nodep->condp());
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
m_lifep = bodyLifep;
|
2022-09-15 19:43:56 +01:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->incsp());
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2019-10-24 22:19:46 -04:00
|
|
|
m_lifep = prevLifep;
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " joinfor" << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
condLifep->lifeToAbove();
|
|
|
|
|
bodyLifep->lifeToAbove();
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(delete condLifep, condLifep);
|
|
|
|
|
VL_DO_DANGLING(delete bodyLifep, bodyLifep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstJumpBlock* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
// As with While's we can't predict if a JumpGo will kill us or not
|
|
|
|
|
// It's worse though as an IF(..., JUMPGO) may change the control flow.
|
|
|
|
|
// Just don't optimize blocks with labels; they're rare - so far.
|
2021-11-13 13:50:44 -05:00
|
|
|
LifeBlock* const prevLifep = m_lifep;
|
2022-11-20 13:11:01 -05:00
|
|
|
LifeBlock* const bodyLifep = new LifeBlock{prevLifep, m_statep};
|
2019-05-19 16:13:13 -04:00
|
|
|
{
|
2022-06-15 18:49:32 -04:00
|
|
|
VL_RESTORER(m_noopt);
|
2019-05-19 16:13:13 -04:00
|
|
|
m_lifep = bodyLifep;
|
2022-06-16 00:11:03 +02:00
|
|
|
setNoopt();
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
2019-05-19 16:13:13 -04:00
|
|
|
m_lifep = prevLifep;
|
|
|
|
|
}
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " joinjump" << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
// For the next assignments, clear any variables that were read or written in the block
|
|
|
|
|
bodyLifep->lifeToAbove();
|
2020-01-16 20:17:11 -05:00
|
|
|
VL_DO_DANGLING(delete bodyLifep, bodyLifep);
|
2010-02-14 10:01:21 -05:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNodeCCall* nodep) override {
|
2020-04-15 07:58:34 -04:00
|
|
|
// UINFO(4, " CCALL " << nodep << endl);
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2019-05-19 16:13:13 -04:00
|
|
|
// Enter the function and trace it
|
2020-04-15 07:58:34 -04:00
|
|
|
// else is non-inline or public function we optimize separately
|
|
|
|
|
if (!nodep->funcp()->entryPoint()) {
|
2017-11-28 18:38:19 -05:00
|
|
|
m_tracingCall = true;
|
2018-05-10 20:55:37 -04:00
|
|
|
iterate(nodep->funcp());
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2020-04-15 07:58:34 -04:00
|
|
|
// UINFO(4, " CFUNC " << nodep << endl);
|
2017-11-28 18:38:19 -05:00
|
|
|
if (!m_tracingCall && !nodep->entryPoint()) return;
|
|
|
|
|
m_tracingCall = false;
|
2023-09-08 08:51:19 +02:00
|
|
|
if (nodep->dpiImportPrototype() && !nodep->dpiPure()) {
|
2019-05-19 16:13:13 -04:00
|
|
|
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
|
|
|
|
}
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2009-12-03 06:55:29 -05:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstUCFunc* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2006-09-26 17:05:08 +00:00
|
|
|
}
|
2022-10-12 10:19:21 +01:00
|
|
|
void visit(AstCExpr* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
m_sideEffect = true; // If appears on assign RHS, don't ever delete the assignment
|
2018-05-10 20:55:37 -04:00
|
|
|
iterateChildren(nodep);
|
2009-12-03 06:55:29 -05:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstVar*) override {} // Don't want varrefs under it
|
2022-09-16 16:15:10 +01:00
|
|
|
void visit(AstNode* nodep) override {
|
2022-08-22 14:26:32 +02:00
|
|
|
if (nodep->isTimingControl()) {
|
|
|
|
|
// V3Life doesn't understand time sense - don't optimize
|
|
|
|
|
setNoopt();
|
|
|
|
|
}
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2022-01-01 11:46:49 -05:00
|
|
|
LifeVisitor(AstNode* nodep, LifeState* statep)
|
|
|
|
|
: m_statep{statep} {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(4, " LifeVisitor on " << nodep << endl);
|
2019-05-19 16:13:13 -04:00
|
|
|
{
|
2022-11-20 13:11:01 -05:00
|
|
|
m_lifep = new LifeBlock{nullptr, m_statep};
|
2018-05-10 20:55:37 -04:00
|
|
|
iterate(nodep);
|
2020-08-15 10:12:55 -04:00
|
|
|
if (m_lifep) VL_DO_CLEAR(delete m_lifep, m_lifep = nullptr);
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
~LifeVisitor() override {
|
2020-08-15 10:12:55 -04:00
|
|
|
if (m_lifep) VL_DO_CLEAR(delete m_lifep, m_lifep = nullptr);
|
2015-10-03 22:33:06 -04:00
|
|
|
}
|
2020-01-08 19:33:47 -05:00
|
|
|
VL_UNCOPYABLE(LifeVisitor);
|
2006-09-26 15:05:35 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
|
2022-01-02 13:56:40 -05:00
|
|
|
class LifeTopVisitor final : public VNVisitor {
|
2006-09-26 15:05:35 +00:00
|
|
|
// Visit all top nodes searching for functions that are entry points we want to start
|
|
|
|
|
// finding code within.
|
|
|
|
|
private:
|
|
|
|
|
// STATE
|
2021-11-13 13:50:44 -05:00
|
|
|
LifeState* const m_statep; // Current state
|
2006-09-26 15:05:35 +00:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstCFunc* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
if (nodep->entryPoint()) {
|
|
|
|
|
// Usage model 1: Simulate all C code, doing lifetime analysis
|
2021-11-26 10:52:36 -05:00
|
|
|
LifeVisitor{nodep, m_statep};
|
2019-05-19 16:13:13 -04:00
|
|
|
}
|
2006-09-26 15:05:35 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNodeProcedure* nodep) override {
|
2019-05-19 16:13:13 -04:00
|
|
|
// Usage model 2: Cleanup basic blocks
|
2021-11-26 10:52:36 -05:00
|
|
|
LifeVisitor{nodep, m_statep};
|
2006-09-26 17:05:08 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstVar*) override {} // Accelerate
|
|
|
|
|
void visit(AstNodeStmt*) override {} // Accelerate
|
2022-10-12 10:19:21 +01:00
|
|
|
void visit(AstNodeExpr*) override {} // Accelerate
|
2022-09-16 11:22:11 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-04-04 08:31:14 -04:00
|
|
|
|
2006-09-26 15:05:35 +00:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2020-08-15 13:11:27 -04:00
|
|
|
LifeTopVisitor(AstNetlist* nodep, LifeState* statep)
|
2020-08-16 09:55:36 -04:00
|
|
|
: m_statep{statep} {
|
2018-05-10 20:55:37 -04:00
|
|
|
iterate(nodep);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|
2022-09-16 11:22:11 +01:00
|
|
|
~LifeTopVisitor() override = default;
|
2006-08-26 11:35:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Life class functions
|
|
|
|
|
|
|
|
|
|
void V3Life::lifeAll(AstNetlist* nodep) {
|
2020-04-15 07:58:34 -04:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2018-03-10 12:57:50 -05:00
|
|
|
{
|
|
|
|
|
LifeState state;
|
2021-11-26 10:52:36 -05:00
|
|
|
LifeTopVisitor{nodep, &state};
|
2018-03-10 12:57:50 -05:00
|
|
|
} // Destruct before checking
|
2023-05-03 18:04:10 -04:00
|
|
|
V3Global::dumpCheckGlobalTree("life", 0, dumpTreeLevel() >= 3);
|
2006-08-26 11:35:28 +00:00
|
|
|
}
|