Fix internal error on gate optimization of assign, bug1475.

This commit is contained in:
Wilson Snyder 2019-08-03 21:49:39 -04:00
parent 9e58ede480
commit a95f58749f
7 changed files with 167 additions and 4 deletions

View File

@ -22,6 +22,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
**** Fix enum values not being sized based on parent, bug1442. [Dan Petrisko]
**** Fix internal error on gate optimization of assign, bug1475. [Oyvind Harboe]
* Verilator 4.016 2019-06-16

View File

@ -61,6 +61,9 @@ typedef std::set<int> MTaskIdSet; // Set of mtaskIds for Var sorting
#define VN_CAST(nodep,nodetypename) (AstNode::privateCast ## nodetypename(nodep))
#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast ## nodetypename(nodep) )
// (V)erilator (N)ode deleted: Reference to deleted child (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
//######################################################################
class AstType {

View File

@ -835,6 +835,8 @@ void GateVisitor::warnSignals() {
//######################################################################
// Push constant into expressions and reevaluate
class GateDedupeVarVisitor;
class GateElimVisitor : public GateBaseVisitor {
private:
// NODE STATE
@ -842,6 +844,11 @@ private:
AstVarScope* m_elimVarScp; // Variable being eliminated
AstNode* m_replaceTreep; // What to replace the variable with
bool m_didReplace; // Did we do any replacements
GateDedupeVarVisitor* m_varVisp; // Callback to keep hash up to date
// METHODS
void hashReplace(AstNode* oldp, AstNode* newp);
// VISITORS
virtual void visit(AstNodeVarRef* nodep) {
if (nodep->varScopep() == m_elimVarScp) {
@ -865,6 +872,7 @@ private:
if (VN_IS(substp, VarRef)) substp->fileline(nodep->fileline());
// Make the substp an rvalue like nodep. This facilitate the hashing in dedupe.
if (AstNodeVarRef* varrefp = VN_CAST(substp, NodeVarRef)) varrefp->lvalue(false);
hashReplace(nodep, substp);
nodep->replaceWith(substp);
nodep->deleteTree(); VL_DANGLING(nodep);
}
@ -875,10 +883,12 @@ private:
public:
// CONSTUCTORS
virtual ~GateElimVisitor() {}
GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep) {
GateElimVisitor(AstNode* nodep, AstVarScope* varscp, AstNode* replaceTreep,
GateDedupeVarVisitor* varVisp) {
m_didReplace = false;
m_elimVarScp = varscp;
m_replaceTreep = replaceTreep;
m_varVisp = varVisp;
iterate(nodep);
}
bool didReplace() const { return m_didReplace; }
@ -886,7 +896,7 @@ public:
void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode* consumerp) {
if (debug()>=5) consumerp->dumpTree(cout, "\telimUsePre: ");
GateElimVisitor elimVisitor (consumerp, varscp, substp);
GateElimVisitor elimVisitor (consumerp, varscp, substp, NULL);
if (elimVisitor.didReplace()) {
if (debug()>=9) consumerp->dumpTree(cout, "\telimUseCns: ");
//Caution: Can't let V3Const change our handle to consumerp, such as by
@ -903,16 +913,27 @@ void GateVisitor::optimizeElimVar(AstVarScope* varscp, AstNode* substp, AstNode*
// Auxiliary hash class for GateDedupeVarVisitor
class GateDedupeHash : public V3HashedUserSame {
public:
// TYPES
typedef std::set<AstNode*> NodeSet;
private:
// NODE STATE
// Ast*::user2p -> parent AstNodeAssign* for this rhsp
// Ast*::user3p -> AstActive* of assign, for isSame() in test for duplicate
// Set to NULL if this assign's tree was later replaced
// Ast*::user5p -> AstNode* of assign if condition, for isSame() in test for duplicate
// Set to NULL if this assign's tree was later replaced
// AstUser1InUse m_inuser1; (Allocated for use in GateVisitor)
// AstUser2InUse m_inuser2; (Allocated for use in GateVisitor)
AstUser3InUse m_inuser3;
// AstUser4InUse m_inuser4; (Allocated for use in V3Hashed)
AstUser5InUse m_inuser5;
V3Hashed m_hashed; // Hash, contains rhs of assigns
NodeSet m_nodeDeleteds; // Any node in this hash was deleted
VL_DEBUG_FUNC; // Declare debug()
void hash(AstNode* nodep) {
// !NULL && the object is hashable
@ -930,13 +951,46 @@ private:
return node1p == node2p || sameHash(node1p, node2p);
}
public:
GateDedupeHash() { }
~GateDedupeHash() {
if (v3Global.opt.debugCheck()) check();
}
// About to replace a node; any node we're tracking refers to oldp, change it to newp.
// This might be a variable on the lhs of the duplicate tree,
// or could be a rhs variable in a tree we're not replacing (or not yet anyways)
void hashReplace(AstNode* oldp, AstNode* newp) {
UINFO(9,"replacing "<<(void*)oldp<<" with "<<(void*)newp<<endl);
// We could update the user3p and user5p but the resulting node
// still has incorrect hash. We really need to remove all hash on
// the whole hash entry tree involving the replaced node and
// rehash. That's complicated and this is rare, so just remove it
// from consideration.
m_nodeDeleteds.insert(oldp);
}
bool isReplaced(AstNode* nodep) {
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
UASSERT_OBJ(!VN_IS(nodep, NodeAssign), nodep, "Dedup attempt on non-assign");
AstNode* extra1p = nodep->user3p();
AstNode* extra2p = nodep->user5p();
return ((extra1p && m_nodeDeleteds.find(extra1p) != m_nodeDeleteds.end())
|| (extra2p && m_nodeDeleteds.find(extra2p) != m_nodeDeleteds.end()));
}
// Callback from V3Hashed::findDuplicate
bool isSame(AstNode* node1p, AstNode* node2p) {
// Assignment may have been hashReplaced, if so consider non-match (effectively removed)
if (isReplaced(node1p) || isReplaced(node2p)) {
//UINFO(9, "isSame hit on replaced "<<(void*)node1p<<" "<<(void*)node2p<<endl);
return false;
}
return same(node1p->user3p(), node2p->user3p())
&& same(node1p->user5p(), node2p->user5p())
&& node1p->user2p()->type() == node2p->user2p()->type();
}
AstNodeAssign* hashAndFindDupe(AstNodeAssign* assignp, AstNode* extra1p, AstNode* extra2p) {
// Legal for extra1p/2p to be NULL, we'll compare with other assigns with extras also NULL
AstNode *rhsp = assignp->rhsp();
rhsp->user2p(assignp);
rhsp->user3p(extra1p);
@ -954,8 +1008,26 @@ public:
m_hashed.erase(inserted);
return VN_CAST(m_hashed.iteratorNodep(dupit)->user2p(), NodeAssign);
}
// Retain new inserted information
return NULL;
}
void check() {
m_hashed.check();
for (V3Hashed::HashMmap::iterator it = m_hashed.begin(); it != m_hashed.end(); ++it) {
AstNode* nodep = it->second;
AstNode* activep = nodep->user3p();
AstNode* condVarp = nodep->user5p();
if (!isReplaced(nodep)) {
// This class won't break if activep isn't an active, or
// ifVar isn't a var, but this is checking the caller's construction.
UASSERT_OBJ(!activep || (!VN_DELETED(activep) && VN_IS(activep, Active)),
nodep, "V3Hashed check failed, lost active pointer");
UASSERT_OBJ(!condVarp || !VN_DELETED(condVarp),
nodep, "V3Hashed check failed, lost if pointer");
}
}
}
};
//######################################################################
@ -1032,6 +1104,7 @@ public:
m_always = false;
m_dedupable = true;
}
~GateDedupeVarVisitor() { }
// PUBLIC METHODS
AstNodeVarRef* findDupe(AstNode* nodep, AstVarScope* consumerVarScopep, AstActive* activep) {
m_assignp = NULL;
@ -1052,8 +1125,20 @@ public:
}
return NULL;
}
void hashReplace(AstNode* oldp, AstNode* newp) {
m_ghash.hashReplace(oldp, newp);
}
};
//######################################################################
void GateElimVisitor::hashReplace(AstNode* oldp, AstNode* newp) {
UINFO(9,"hashReplace "<<(void*)oldp<<" -> "<<(void*)newp<<endl);
if (m_varVisp) {
m_varVisp->hashReplace(oldp, newp);
}
}
//######################################################################
// Recurse through the graph, looking for duplicate expressions on the rhs of an assign
@ -1093,7 +1178,7 @@ private:
GateLogicVertex* consumeVertexp
= dynamic_cast<GateLogicVertex*>(outedgep->top());
AstNode* consumerp = consumeVertexp->nodep();
GateElimVisitor elimVisitor(consumerp, vvertexp->varScp(), dupVarRefp);
GateElimVisitor elimVisitor(consumerp, vvertexp->varScp(), dupVarRefp, &m_varVisitor);
outedgep = outedgep->relinkFromp(dupVvertexp);
}
@ -1299,6 +1384,7 @@ public:
//----------------------------------------------------------------------
void GateVisitor::mergeAssigns() {
UINFO(6, "mergeAssigns\n");
GateMergeAssignsGraphVisitor merger(&m_graph);
for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp=itp->verticesNextp()) {
if (GateVarVertex* vvertexp = dynamic_cast<GateVarVertex*>(itp)) {

View File

@ -145,6 +145,13 @@ void V3Hashed::erase(iterator it) {
nodep->user4p(NULL); // So we don't allow removeNode again
}
void V3Hashed::check() {
for (HashMmap::iterator it = begin(); it != end(); ++it) {
AstNode* nodep = it->second;
UASSERT_OBJ(nodep->user4p(), nodep, "V3Hashed check failed, non-hashed node");
}
}
void V3Hashed::dumpFilePrefixed(const string& nameComment, bool tree) {
if (v3Global.opt.dumpTree()) {
dumpFile(v3Global.debugFilename(nameComment)+".hash", tree);

View File

@ -53,8 +53,8 @@ class V3Hashed : public VHashedBase {
AstUser4InUse m_inuser4;
// TYPES
typedef std::multimap<V3Hash,AstNode*> HashMmap;
public:
typedef std::multimap<V3Hash,AstNode*> HashMmap;
typedef HashMmap::iterator iterator;
private:
// MEMBERS
@ -72,6 +72,7 @@ public:
// METHODS
void clear() { m_hashMmap.clear(); AstNode::user4ClearTree(); }
void check(); // Check assertions on structure
iterator hashAndInsert(AstNode* nodep); // Hash the node, and insert into map. Return iterator to inserted
void hash(AstNode* nodep); // Only hash the node
bool sameNodes(AstNode* node1p, AstNode* node2p); // After hashing, and tell if identical

16
test_regress/t/t_gate_delref.pl Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2004 by Wilson Snyder. This program is free software; you can
# redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
scenarios(simulator => 1);
compile(
);
ok(1);
1;

View File

@ -0,0 +1,48 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2019 by Wilson Snyder.
// bug1475
module t (/*AUTOARG*/
// Outputs
ID_45, IDa_f4c,
// Inputs
clk, ID_d9f, IDa_657, ID_477
);
input clk;
output reg ID_45;
input ID_d9f;
input IDa_657;
output reg IDa_f4c;
reg ID_13;
input ID_477;
reg ID_489;
reg ID_8d1;
reg IDa_183;
reg IDa_91c;
reg IDa_a96;
reg IDa_d6b;
reg IDa_eb9;
wire ID_fc8 = ID_d9f & ID_13; //<<
wire ID_254 = ID_d9f & ID_13;
wire ID_f40 = ID_fc8 ? ID_8d1 : 0;
wire ID_f4c = ID_fc8 ? 0 : ID_477;
wire ID_442 = IDa_91c;
wire ID_825 = ID_489;
always @(posedge clk) begin
ID_13 <= ID_f40;
ID_8d1 <= IDa_eb9;
ID_489 <= ID_442;
ID_45 <= ID_825;
IDa_d6b <= IDa_a96;
IDa_f4c <= ID_f4c;
if (ID_254) begin
IDa_91c <= IDa_d6b;
IDa_183 <= IDa_657;
IDa_a96 <= IDa_657;
IDa_eb9 <= IDa_183;
end
end
endmodule