mirror of
https://github.com/verilator/verilator.git
synced 2026-10-02 16:13:34 +02:00
Internals: Make V3LiftExpr non optional (#8369)
This commit is contained in:
@@ -813,6 +813,10 @@ Summary:
|
||||
|
||||
.. option:: -fno-lift-expr
|
||||
|
||||
Deprecated and has no effect (ignored).
|
||||
|
||||
In versions before 5.054: Disable lifting of expressions out of statements.
|
||||
|
||||
.. option:: -fno-localize
|
||||
|
||||
.. option:: -fno-merge-cond
|
||||
|
||||
+25
-4
@@ -49,9 +49,8 @@
|
||||
// __VleLogAnd_0 = __VleCall_0;
|
||||
// }
|
||||
// z = __VleLogAnd_1;
|
||||
// Similar patterns are used for AstLogOr and AstCond to preserve the
|
||||
// short-circuiting semantics and side effects. All AstLogIf should have
|
||||
// been converted to AstLogOr earlier by V3Const.
|
||||
// Similar patterns are used for AstLogOr, AstLogIf and AstCond to preserve
|
||||
// the short-circuiting semantics and side effects.
|
||||
//
|
||||
// Care must be taken for impure LValues as well. While, all LValue expressions
|
||||
// permitted by IEEE-1800 are themselves pure (except possibly for the non-lvalue
|
||||
@@ -108,6 +107,7 @@ class LiftExprVisitor final : public VNVisitor {
|
||||
VDouble0 m_statLiftedConds;
|
||||
VDouble0 m_statLiftedLogAnds;
|
||||
VDouble0 m_statLiftedLogOrs;
|
||||
VDouble0 m_statLiftedLogIfs;
|
||||
VDouble0 m_statLiftedExprStmts;
|
||||
VDouble0 m_statTemporariesCreated;
|
||||
VDouble0 m_statTemporariesReused;
|
||||
@@ -432,7 +432,27 @@ class LiftExprVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstLogIf* nodep) override {
|
||||
if (!m_lift) return;
|
||||
nodep->v3fatalSrc("AstLogIf should have been folded by V3Const");
|
||||
|
||||
// Lift from LHS
|
||||
iterate(nodep->lhsp());
|
||||
|
||||
// Lift from RHS, if nothing lifted, then nothing to do
|
||||
AstNode* const rhsStmtps = lift(nodep->rhsp());
|
||||
if (!rhsStmtps) return;
|
||||
|
||||
// Otherwise convert to an AstIf with a temporary variable
|
||||
++m_statLiftedLogIfs;
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstVar* varp = getExistingVar(nodep->rhsp());
|
||||
if (!varp) varp = newVar("LogIf", nodep);
|
||||
addStmtps(new AstAssign{flp, new AstVarRef{flp, varp, VAccess::WRITE},
|
||||
new AstLogNot{flp, nodep->lhsp()->unlinkFrBack()}});
|
||||
AstIf* const ifp = new AstIf{flp, new AstVarRef{flp, varp, VAccess::READ}};
|
||||
addStmtps(ifp);
|
||||
ifp->addElsesp(rhsStmtps);
|
||||
ifp->addElsesp(assignIfDifferent(flp, varp, nodep->rhsp()));
|
||||
nodep->replaceWith(new AstVarRef{flp, varp, VAccess::READ});
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
void visit(AstExprStmt* nodep) override {
|
||||
if (!m_lift) return;
|
||||
@@ -476,6 +496,7 @@ public:
|
||||
V3Stats::addStat("LiftExpr, lifted Cond", m_statLiftedConds);
|
||||
V3Stats::addStat("LiftExpr, lifted LogAnd", m_statLiftedLogAnds);
|
||||
V3Stats::addStat("LiftExpr, lifted LogOr", m_statLiftedLogOrs);
|
||||
V3Stats::addStat("LiftExpr, lifted LogIf", m_statLiftedLogIfs);
|
||||
V3Stats::addStat("LiftExpr, lifted ExprStmt", m_statLiftedExprStmts);
|
||||
V3Stats::addStat("LiftExpr, temporaries created", m_statTemporariesCreated);
|
||||
V3Stats::addStat("LiftExpr, temporaries reused", m_statTemporariesReused);
|
||||
|
||||
+3
-1
@@ -1518,7 +1518,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
||||
DECL_OPTION("-finline-funcs-eager", FOnOff, &m_fInlineFuncsEager);
|
||||
DECL_OPTION("-flife", FOnOff, &m_fLife);
|
||||
DECL_OPTION("-flife-post", FOnOff, &m_fLifePost);
|
||||
DECL_OPTION("-flift-expr", FOnOff, &m_fLiftExpr);
|
||||
DECL_OPTION("-flift-expr", CbFOnOff, [fl](bool) {
|
||||
fl->v3warn(DEPRECATED, "Option '-fno-lift-expr' is deprecated and has no effect");
|
||||
});
|
||||
DECL_OPTION("-flocalize", FOnOff, &m_fLocalize);
|
||||
DECL_OPTION("-fmerge-cond", FOnOff, &m_fMergeCond);
|
||||
DECL_OPTION("-fmerge-cond-motion", FOnOff, &m_fMergeCondMotion);
|
||||
|
||||
@@ -424,7 +424,6 @@ private:
|
||||
bool m_fInlineFuncsEager = true; // main switch: -fno-inline-funcs-eager: don't inline eagerly
|
||||
bool m_fLife; // main switch: -fno-life: variable lifetime
|
||||
bool m_fLifePost; // main switch: -fno-life-post: delayed assignment elimination
|
||||
bool m_fLiftExpr = true; // main switch: -fno-lift-expr: lift expressions out of statements
|
||||
bool m_fLocalize; // main switch: -fno-localize: convert temps to local variables
|
||||
bool m_fMergeCond; // main switch: -fno-merge-cond: merge conditionals
|
||||
bool m_fMergeCondMotion = true; // main switch: -fno-merge-cond-motion: perform code motion
|
||||
@@ -765,7 +764,6 @@ public:
|
||||
bool fInlineFuncsEager() const { return m_fInlineFuncsEager; }
|
||||
bool fLife() const { return m_fLife; }
|
||||
bool fLifePost() const { return m_fLifePost; }
|
||||
bool fLiftExpr() const { return m_fLiftExpr; }
|
||||
bool fLocalize() const { return m_fLocalize; }
|
||||
bool fMergeCond() const { return m_fMergeCond; }
|
||||
bool fMergeCondMotion() const { return m_fMergeCondMotion; }
|
||||
|
||||
+1
-1
@@ -307,7 +307,7 @@ static void process() {
|
||||
|
||||
if (!v3Global.opt.serializeOnly()) {
|
||||
// Lift expressions out of statements.
|
||||
if (v3Global.opt.fLiftExpr()) V3LiftExpr::liftExprAll(v3Global.rootp());
|
||||
V3LiftExpr::liftExprAll(v3Global.rootp());
|
||||
|
||||
// Move assignments from X into MODULE temps.
|
||||
// (Before flattening, so each new X variable is shared between all scopes of that
|
||||
|
||||
@@ -17,13 +17,8 @@ test.scenarios('vlt_all')
|
||||
# it reliably. --no-threads-coarsen keeps the sample and the writer in separate MTasks.
|
||||
test.enable_tsan()
|
||||
|
||||
# -fno-lift-expr is what makes this test bite. With expression lifting on, V3LiftExpr rewrites
|
||||
# 'arr[0] = new(sigs[0])' into '__VlemCall_0 = new(sigs[0]); arr[0] = __VlemCall_0', so the
|
||||
# construction always assigns to a plain variable and the array element never reaches
|
||||
# V3SchedCovergroup. With it off the raw form survives, and the binding is only seen at all
|
||||
# because constructions are also collected where they are not a simple assignment.
|
||||
coverage_covergroup_common.run(test,
|
||||
verilator_flags2=(['--stats', '-fno-lift-expr'] +
|
||||
verilator_flags2=(['--stats'] +
|
||||
(['--no-threads-coarsen'] if test.vltmt else [])),
|
||||
threads=(2 if test.vltmt else 1))
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
// DESCRIPTION: Verilator: Test covergroup 'ref' bindings where no handle is a plain variable
|
||||
// Companion to t_covergroup_ref_bind, which covers the resolvable shapes. Here nothing is a
|
||||
// plain variable: the covergroup is constructed into an array element, sampled through an array
|
||||
// element, and the 'ref' actual is an array element too. So neither the construction nor the
|
||||
// sample names one covergroup object, and both must fall back to the union over the covergroup
|
||||
// type -- which must still order every sample against the non-blocking writer of what any
|
||||
// instance of that type reads. cg_b repeats that with a struct member as the handle instead of
|
||||
// an array element. Runs under --vltmt, where an unordered sample is a data race,
|
||||
// and with -fno-lift-expr, which leaves the construction assigning directly to the array
|
||||
// element instead of to a lifted temporary.
|
||||
// element, and the 'ref' actual is an array element too. So no sample names one covergroup
|
||||
// object, and each must fall back to the union over the covergroup type -- which must still
|
||||
// order every sample against the non-blocking writer of what any instance of that type reads.
|
||||
// cg_b repeats that with a struct member as the handle instead of an array element. Runs
|
||||
// under --vltmt, where an unordered sample is a data race.
|
||||
// This file ONLY is placed into the Public Domain, for any use, without warranty.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
@@ -94,7 +94,6 @@ test.compile(verilator_flags2=[
|
||||
"-Mdir", test.obj_dir + "/obj_opt",
|
||||
"--prefix", "Vopt",
|
||||
"-fno-const-before-dfg", # Otherwise V3Const makes testing painful
|
||||
"-fno-lift-expr", # Assumes V3Const run prior to V3LiftExpr
|
||||
"-fdfg-synthesize-all",
|
||||
"--dump-dfg", # To fill code coverage
|
||||
"-CFLAGS \"-I .. -I ../obj_ref\"",
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
%Warning-DEPRECATED: Option '-fno-dfg-post-inline' is deprecated and has no effect
|
||||
%Warning-DEPRECATED: Option '-fno-dfg-scoped' is deprecated, use '-fno-dfg' instead.
|
||||
%Warning-DEPRECATED: Option '-fno-dfg-break-cycles' is deprecated and has no effect
|
||||
%Warning-DEPRECATED: Option '-fno-lift-expr' is deprecated and has no effect
|
||||
%Warning-DEPRECATED: Option '--assert-unroll-limit' is deprecated and has no effect.
|
||||
%Error: Exiting due to
|
||||
|
||||
@@ -12,7 +12,7 @@ import vltest_bootstrap
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(verilator_flags2=[
|
||||
"--trace-fst-thread --trace-threads 2 --order-clock-delay --clk foo --no-clk bar -fno-dfg-pre-inline -fno-dfg-post-inline -fno-dfg-scoped -fno-dfg-break-cycles --assert-unroll-limit 1024",
|
||||
"--trace-fst-thread --trace-threads 2 --order-clock-delay --clk foo --no-clk bar -fno-dfg-pre-inline -fno-dfg-post-inline -fno-dfg-scoped -fno-dfg-break-cycles -fno-lift-expr --assert-unroll-limit 1024",
|
||||
],
|
||||
fails=True,
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "Vt_opt_lift_expr__Dpi.h"
|
||||
#include "svdpi.h"
|
||||
|
||||
svLogic impure_0() { return sv_0; }
|
||||
|
||||
svLogic impure_1() { return sv_1; }
|
||||
@@ -11,8 +11,20 @@ import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.compile()
|
||||
# -fno-const-before-dfg keeps V3Const from folding AstLogIf into AstLogOr,
|
||||
# which is the only way V3LiftExpr ever encounters an AstLogIf. Option
|
||||
# -fno-const-before-dfg itself is needed for testing Dfg, so needs to work.
|
||||
test.compile(verilator_flags2=['--binary', '--stats', '-fno-const-before-dfg', test.pli_filename])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.file_grep(test.stats, r'LiftExpr, lifted LogAnd\s+(\d+)', 16)
|
||||
test.file_grep(test.stats, r'LiftExpr, lifted LogOr\s+(\d+)', 16)
|
||||
test.file_grep(test.stats, r'LiftExpr, lifted LogIf\s+(\d+)', 16)
|
||||
test.file_grep(test.stats, r'LiftExpr, lifted Cond\s+(\d+)', 32)
|
||||
test.file_grep(test.stats, r'LiftExpr, lifted calls\s+(\d+)', 178)
|
||||
test.file_grep(test.stats, r'LiftExpr, lifted impure expressions\s+(\d+)', 2)
|
||||
test.file_grep(test.stats, r'LiftExpr, temporaries created\s+(\d+)', 196)
|
||||
test.file_grep(test.stats, r'LiftExpr, temporaries reused\s+(\d+)', 16)
|
||||
|
||||
test.passes()
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
|
||||
module t;
|
||||
|
||||
import "DPI-C" function logic impure_0();
|
||||
import "DPI-C" function logic impure_1();
|
||||
|
||||
function automatic int one();
|
||||
return 1;
|
||||
endfunction
|
||||
@@ -30,6 +33,73 @@ module t;
|
||||
initial begin
|
||||
`checkh(C::i, 2);
|
||||
`checkh(C::j, 3);
|
||||
// Expr
|
||||
`checkh(|$urandom_range(2,1), 1'b1);
|
||||
// LogAnd
|
||||
`checkh(impure_0() && 1'b0, 1'b0);
|
||||
`checkh(impure_0() && 1'b1, 1'b0);
|
||||
`checkh(impure_1() && 1'b0, 1'b0);
|
||||
`checkh(impure_1() && 1'b1, 1'b1);
|
||||
`checkh(1'b0 && impure_0(), 1'b0);
|
||||
`checkh(1'b0 && impure_1(), 1'b0);
|
||||
`checkh(1'b1 && impure_0(), 1'b0);
|
||||
`checkh(1'b1 && impure_1(), 1'b1);
|
||||
`checkh(impure_0() && impure_0(), 1'b0);
|
||||
`checkh(impure_0() && impure_1(), 1'b0);
|
||||
`checkh(impure_1() && impure_0(), 1'b0);
|
||||
`checkh(impure_1() && impure_1(), 1'b1);
|
||||
// LogOr
|
||||
`checkh(impure_0() || 1'b0, 1'b0);
|
||||
`checkh(impure_0() || 1'b1, 1'b1);
|
||||
`checkh(impure_1() || 1'b0, 1'b1);
|
||||
`checkh(impure_1() || 1'b1, 1'b1);
|
||||
`checkh(1'b0 || impure_0(), 1'b0);
|
||||
`checkh(1'b0 || impure_1(), 1'b1);
|
||||
`checkh(1'b1 || impure_0(), 1'b1);
|
||||
`checkh(1'b1 || impure_1(), 1'b1);
|
||||
`checkh(impure_0() || impure_0(), 1'b0);
|
||||
`checkh(impure_0() || impure_1(), 1'b1);
|
||||
`checkh(impure_1() || impure_0(), 1'b1);
|
||||
`checkh(impure_1() || impure_1(), 1'b1);
|
||||
// LogIf
|
||||
`checkh(impure_0() -> 1'b0, 1'b1);
|
||||
`checkh(impure_0() -> 1'b1, 1'b1);
|
||||
`checkh(impure_1() -> 1'b0, 1'b0);
|
||||
`checkh(impure_1() -> 1'b1, 1'b1);
|
||||
`checkh(1'b0 -> impure_0(), 1'b1);
|
||||
`checkh(1'b0 -> impure_1(), 1'b1);
|
||||
`checkh(1'b1 -> impure_0(), 1'b0);
|
||||
`checkh(1'b1 -> impure_1(), 1'b1);
|
||||
`checkh(impure_0() -> impure_0(), 1'b1);
|
||||
`checkh(impure_0() -> impure_1(), 1'b1);
|
||||
`checkh(impure_1() -> impure_0(), 1'b0);
|
||||
`checkh(impure_1() -> impure_1(), 1'b1);
|
||||
// Cond
|
||||
`checkh(impure_0() ? 1'b0 : 1'b0, 1'b0);
|
||||
`checkh(impure_0() ? 1'b0 : 1'b1, 1'b1);
|
||||
`checkh(impure_0() ? 1'b1 : 1'b0, 1'b0);
|
||||
`checkh(impure_0() ? 1'b1 : 1'b1, 1'b1);
|
||||
`checkh(impure_1() ? 1'b0 : 1'b0, 1'b0);
|
||||
`checkh(impure_1() ? 1'b0 : 1'b1, 1'b0);
|
||||
`checkh(impure_1() ? 1'b1 : 1'b0, 1'b1);
|
||||
`checkh(impure_1() ? 1'b1 : 1'b1, 1'b1);
|
||||
`checkh(impure_0() ? impure_0() : 1'b0, 1'b0);
|
||||
`checkh(impure_0() ? impure_0() : 1'b1, 1'b1);
|
||||
`checkh(impure_0() ? impure_1() : 1'b0, 1'b0);
|
||||
`checkh(impure_0() ? impure_1() : 1'b1, 1'b1);
|
||||
`checkh(impure_1() ? impure_0() : 1'b0, 1'b0);
|
||||
`checkh(impure_1() ? impure_0() : 1'b1, 1'b0);
|
||||
`checkh(impure_1() ? impure_1() : 1'b0, 1'b1);
|
||||
`checkh(impure_1() ? impure_1() : 1'b1, 1'b1);
|
||||
`checkh(impure_0() ? 1'b0 : impure_0(), 1'b0);
|
||||
`checkh(impure_0() ? 1'b0 : impure_1(), 1'b1);
|
||||
`checkh(impure_0() ? 1'b1 : impure_0(), 1'b0);
|
||||
`checkh(impure_0() ? 1'b1 : impure_1(), 1'b1);
|
||||
`checkh(impure_1() ? 1'b0 : impure_0(), 1'b0);
|
||||
`checkh(impure_1() ? 1'b0 : impure_1(), 1'b0);
|
||||
`checkh(impure_1() ? 1'b1 : impure_0(), 1'b1);
|
||||
`checkh(impure_1() ? 1'b1 : impure_1(), 1'b1);
|
||||
// End test
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user