Support `case` impure expressions (#6563)
This commit is contained in:
parent
3d5d2db64b
commit
96ed725278
|
|
@ -30,7 +30,9 @@
|
|||
|
||||
#include "V3Begin.h"
|
||||
|
||||
#include "V3Stats.h"
|
||||
#include "V3String.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -56,6 +58,10 @@ public:
|
|||
//######################################################################
|
||||
|
||||
class BeginVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
// AstCase::user1 -> bool, if already purified
|
||||
|
||||
V3UniqueNames m_caseTempNames; // For generating unique temporary variable names used by cases
|
||||
// STATE - across all visitors
|
||||
BeginState* const m_statep; // Current global state
|
||||
|
||||
|
|
@ -68,6 +74,7 @@ class BeginVisitor final : public VNVisitor {
|
|||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
int m_ifDepth = 0; // Current if depth
|
||||
bool m_keepBegins = false; // True if begins should not be inlined
|
||||
VDouble0 m_statPurifiedCaseExpr; // Count of purified case expressions
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -146,6 +153,7 @@ class BeginVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
VL_RESTORER(m_caseTempNames);
|
||||
m_modp = nodep;
|
||||
// Rename it (e.g. class under a generate)
|
||||
if (m_unnamedScope != "") {
|
||||
|
|
@ -179,6 +187,7 @@ class BeginVisitor final : public VNVisitor {
|
|||
VL_RESTORER(m_liftedp);
|
||||
VL_RESTORER(m_namedScope);
|
||||
VL_RESTORER(m_unnamedScope);
|
||||
VL_RESTORER(m_caseTempNames);
|
||||
m_displayScope = dot(m_displayScope, nodep->name());
|
||||
m_namedScope = "";
|
||||
m_unnamedScope = "";
|
||||
|
|
@ -303,6 +312,29 @@ class BeginVisitor final : public VNVisitor {
|
|||
// any BEGINs, but V3Coverage adds them all under the module itself.
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCase* nodep) override {
|
||||
// Introduce temporary variable for AstCase if needed - it is done here and not in V3Case
|
||||
// because this phase is before V3Scope and V3Case is not. Doing it before V3Scope ensures
|
||||
// that V3Scope will take care of a scope creation
|
||||
if (!nodep->exprp()->isPure() && !nodep->user1SetOnce()) {
|
||||
++m_statPurifiedCaseExpr;
|
||||
FileLine* const fl = nodep->exprp()->fileline();
|
||||
AstVar* const varp = new AstVar{fl, VVarType::XTEMP, m_caseTempNames.get(nodep),
|
||||
nodep->exprp()->dtypep()};
|
||||
nodep->exprp(new AstExprStmt{fl,
|
||||
new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE},
|
||||
nodep->exprp()->unlinkFrBack()},
|
||||
new AstVarRef{fl, varp, VAccess::READ}});
|
||||
if (m_ftaskp) {
|
||||
varp->funcLocal(true);
|
||||
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
|
||||
} else {
|
||||
m_modp->stmtsp()->addHereThisAsNext(varp);
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
// VISITORS - LINT CHECK
|
||||
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||
VL_RESTORER(m_keepBegins);
|
||||
|
|
@ -329,8 +361,10 @@ class BeginVisitor final : public VNVisitor {
|
|||
public:
|
||||
// CONSTRUCTORS
|
||||
BeginVisitor(AstNetlist* nodep, BeginState* statep)
|
||||
: m_statep{statep} {
|
||||
: m_caseTempNames{"__VCase"}
|
||||
, m_statep{statep} {
|
||||
iterate(nodep);
|
||||
V3Stats::addStatSum("Impure case expressions", m_statPurifiedCaseExpr);
|
||||
}
|
||||
~BeginVisitor() override = default;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ class CaseVisitor final : public VNVisitor {
|
|||
bool m_caseNoOverlapsAllCovered = false; // Proven to be synopsys parallel_case compliant
|
||||
// For each possible value, the case branch we need
|
||||
std::array<AstNode*, 1 << CASE_OVERLAP_WIDTH> m_valueItem;
|
||||
bool m_needToClearCache = false; // Whether cache needs to be cleared
|
||||
|
||||
// METHODS
|
||||
//! Determine whether we should check case items are complete
|
||||
|
|
@ -390,7 +391,14 @@ class CaseVisitor final : public VNVisitor {
|
|||
// CASEx(cexpr,....
|
||||
// -> tree of IF(msb, IF(msb-1, 11, 10)
|
||||
// IF(msb-1, 01, 00))
|
||||
AstNodeExpr* const cexprp = nodep->exprp()->unlinkFrBack();
|
||||
AstNodeExpr* cexprp;
|
||||
AstExprStmt* cexprStmtp = nullptr;
|
||||
if (nodep->exprp()->isPure()) {
|
||||
cexprp = nodep->exprp()->unlinkFrBack();
|
||||
} else {
|
||||
cexprStmtp = VN_AS(nodep->exprp()->unlinkFrBack(), ExprStmt);
|
||||
cexprp = cexprStmtp->resultp()->cloneTreePure(false);
|
||||
}
|
||||
|
||||
if (debug() >= 9) { // LCOV_EXCL_START
|
||||
for (uint32_t i = 0; i < (1UL << m_caseWidth); ++i) {
|
||||
|
|
@ -405,6 +413,14 @@ class CaseVisitor final : public VNVisitor {
|
|||
|
||||
AstNode::user3ClearTree();
|
||||
AstNode* ifrootp = replaceCaseFastRecurse(cexprp, m_caseWidth - 1, 0UL);
|
||||
if (cexprStmtp) {
|
||||
cexprStmtp->resultp()->unlinkFrBack()->deleteTree();
|
||||
AstIf* const ifp = VN_AS(ifrootp, If);
|
||||
cexprStmtp->resultp(ifp->condp()->unlinkFrBack());
|
||||
ifp->condp(cexprStmtp);
|
||||
m_needToClearCache = true;
|
||||
}
|
||||
|
||||
// Case expressions can't be linked twice, so clone them
|
||||
if (ifrootp && !ifrootp->user3()) ifrootp = ifrootp->cloneTree(true);
|
||||
|
||||
|
|
@ -423,7 +439,14 @@ class CaseVisitor final : public VNVisitor {
|
|||
// -> IF((cexpr==icond1),istmts1,
|
||||
// IF((EQ (AND MASK cexpr) (AND MASK icond1)
|
||||
// ,istmts2, istmts3
|
||||
AstNodeExpr* const cexprp = nodep->exprp()->unlinkFrBack();
|
||||
AstNodeExpr* cexprp;
|
||||
AstExprStmt* cexprStmtp = nullptr;
|
||||
if (nodep->exprp()->isPure()) {
|
||||
cexprp = nodep->exprp()->unlinkFrBack();
|
||||
} else {
|
||||
cexprStmtp = VN_AS(nodep->exprp(), ExprStmt)->unlinkFrBack();
|
||||
cexprp = cexprStmtp->resultp()->cloneTreePure(false);
|
||||
}
|
||||
// We'll do this in two stages. First stage, convert the conditions to
|
||||
// the appropriate IF AND terms.
|
||||
UINFOTREE(9, nodep, "", "_comp_IN::");
|
||||
|
|
@ -502,7 +525,7 @@ class CaseVisitor final : public VNVisitor {
|
|||
// should pull out the most common item from here and instead make
|
||||
// it the first IF branch.
|
||||
int depth = 0;
|
||||
AstNode* grouprootp = nullptr;
|
||||
AstIf* grouprootp = nullptr;
|
||||
AstIf* groupnextp = nullptr;
|
||||
AstIf* itemnextp = nullptr;
|
||||
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
||||
|
|
@ -551,6 +574,12 @@ class CaseVisitor final : public VNVisitor {
|
|||
if (grouprootp) {
|
||||
UINFOTREE(9, grouprootp, "", "_new");
|
||||
nodep->replaceWith(grouprootp);
|
||||
if (cexprStmtp) {
|
||||
pushDeletep(cexprStmtp->resultp()->unlinkFrBack());
|
||||
cexprStmtp->resultp(grouprootp->condp()->unlinkFrBack());
|
||||
grouprootp->condp(cexprStmtp);
|
||||
m_needToClearCache = true;
|
||||
}
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
}
|
||||
|
|
@ -611,6 +640,7 @@ public:
|
|||
explicit CaseVisitor(AstNetlist* nodep) {
|
||||
for (auto& itr : m_valueItem) itr = nullptr;
|
||||
iterate(nodep);
|
||||
if (m_needToClearCache) VIsCached::clearCacheTree();
|
||||
}
|
||||
~CaseVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_statCaseFast);
|
||||
|
|
|
|||
|
|
@ -28,11 +28,11 @@
|
|||
#include <string>
|
||||
|
||||
class V3UniqueNames final {
|
||||
const std::string m_prefix; // Prefix to attach to all names
|
||||
std::string m_prefix; // Prefix to attach to all names
|
||||
|
||||
std::map<std::string, unsigned> m_multiplicity; // Suffix number for given key
|
||||
|
||||
const bool m_addSuffix = true; // Ad suffix or not
|
||||
bool m_addSuffix = true; // Ad suffix or not
|
||||
|
||||
public:
|
||||
V3UniqueNames() = default;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--stats'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.file_grep(test.stats, r'Impure case expressions\s+(\d+)', 2)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Cls;
|
||||
int callCount = 0;
|
||||
int callCount2 = 0;
|
||||
int value = 6;
|
||||
bit[5:0] value2 = 6;
|
||||
function int get();
|
||||
callCount += 1;
|
||||
return value;
|
||||
endfunction
|
||||
function bit[5:0] get2();
|
||||
callCount2 += 1;
|
||||
return value2;
|
||||
endfunction
|
||||
function int getPure();
|
||||
return callCount2;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Cls c;
|
||||
initial begin
|
||||
bit called = 0;
|
||||
c = new;
|
||||
case (c.get())
|
||||
4: $stop;
|
||||
5: $stop;
|
||||
6: called = 1;
|
||||
7: $stop;
|
||||
default: $stop;
|
||||
endcase
|
||||
if (!called) $stop;
|
||||
if (c.callCount != 1) $stop;
|
||||
called = 0;
|
||||
case (c.get2())
|
||||
4: $stop;
|
||||
5: $stop;
|
||||
6: called = 1;
|
||||
7: $stop;
|
||||
default: $stop;
|
||||
endcase
|
||||
case (c.getPure())
|
||||
1:;
|
||||
default: $stop;
|
||||
endcase
|
||||
if (!called) $stop;
|
||||
if (c.callCount2 != 1) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 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.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--stats'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.file_grep(test.stats, r'Impure case expressions\s+(\d+)', 2)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Cls;
|
||||
int callCount = 0;
|
||||
int callCount2 = 0;
|
||||
int value = 6;
|
||||
bit[5:0] value2 = 6;
|
||||
function int get();
|
||||
callCount += 1;
|
||||
return value;
|
||||
endfunction
|
||||
function bit[5:0] get2();
|
||||
callCount2 += 1;
|
||||
return value2;
|
||||
endfunction
|
||||
function int getPure();
|
||||
return callCount2;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t;
|
||||
Cls c;
|
||||
initial begin
|
||||
bit called = 0;
|
||||
c = new;
|
||||
case (c.get()) inside
|
||||
[0:5]: $stop;
|
||||
[6:6]: called = 1;
|
||||
[7:100]: $stop;
|
||||
default: $stop;
|
||||
endcase
|
||||
if (!called) $stop;
|
||||
if (c.callCount != 1) $stop;
|
||||
called = 0;
|
||||
case (c.get2()) inside
|
||||
[0:5]: $stop;
|
||||
[6:6]: called = 1;
|
||||
[7:100]: $stop;
|
||||
default: $stop;
|
||||
endcase
|
||||
if (!called) $stop;
|
||||
called = 0;
|
||||
case (c.getPure()) inside
|
||||
[0:1]: called = 1;
|
||||
[2:10]: $stop;
|
||||
default: $stop;
|
||||
endcase
|
||||
if (!called) $stop;
|
||||
if (c.callCount2 != 1) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,19 +1,7 @@
|
|||
%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:12:13: Expression side effect may be mishandled
|
||||
%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:17:31: Expression side effect may be mishandled
|
||||
: ... Suggest use a temporary variable in place of this expression
|
||||
12 | case ($c("1"))
|
||||
| ^~
|
||||
17 | arr[postincrement_i()][postincrement_i()]++;
|
||||
| ^
|
||||
... For warning description see https://verilator.org/warn/SIDEEFFECT?v=latest
|
||||
... Use "/* verilator lint_off SIDEEFFECT */" and lint_on around source to disable this message.
|
||||
%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:13:10: Expression side effect may be mishandled
|
||||
: ... Suggest use a temporary variable in place of this expression
|
||||
13 | 1: $stop;
|
||||
| ^
|
||||
%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:14:10: Expression side effect may be mishandled
|
||||
: ... Suggest use a temporary variable in place of this expression
|
||||
14 | 2: $stop;
|
||||
| ^
|
||||
%Warning-SIDEEFFECT: t/t_lint_sideeffect_bad.v:15:10: Expression side effect may be mishandled
|
||||
: ... Suggest use a temporary variable in place of this expression
|
||||
15 | 3: $stop;
|
||||
| ^
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -1,22 +1,20 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Wilson Snyder.
|
||||
// any use, without warranty, 2022 Krzysztof Boronski.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
int i = 0;
|
||||
|
||||
function int postincrement_i;
|
||||
return i++;
|
||||
endfunction
|
||||
|
||||
module t;
|
||||
|
||||
logic [63:0] array = 64'hfeedf00d12345678;
|
||||
|
||||
initial begin
|
||||
case ($c("1"))
|
||||
1: $stop;
|
||||
2: $stop;
|
||||
3: $stop;
|
||||
default: $stop;
|
||||
endcase
|
||||
|
||||
$display("0x%8x", array[$c(0) +: 32]);
|
||||
end
|
||||
|
||||
initial begin
|
||||
int arr [3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
|
||||
i = 0;
|
||||
arr[postincrement_i()][postincrement_i()]++;
|
||||
$display("Value: %d", i);
|
||||
end
|
||||
endmodule
|
||||
|
|
|
|||
Loading…
Reference in New Issue