Fix constant propagation of post-expand stages (#5963) (#5972).

This commit is contained in:
Wilson Snyder 2025-05-04 21:41:14 -04:00
parent 413183bad8
commit 66e105b444
8 changed files with 190 additions and 15 deletions

View File

@ -17,7 +17,7 @@ Verilator 5.037 devel
* Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon]
* Fix filename backslash escapes in C code (#5947).
* Fix C++ widths in V3Expand (#5953) (#5975). [Geza Lore]
* Fix constant propagation making upper bits Xs (#5955) (#5969).
* Fix constant propagation of post-expand stages (#5955) (#5963) (#5969) (#5972).
* Fix sign extension of signed compared with unsigned case items (#5968).
* Fix always processes ignoring $finish (#5971). [Hennadii Chernyshchyk]
* Fix streaming to/from packed arrays (#5976). [Geza Lore]

View File

@ -427,6 +427,8 @@ public:
string prettyDTypeName(bool full) const override;
const char* broken() const override {
BROKEN_RTN(dtypep() != this);
BROKEN_RTN(v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH
&& widthMin() > width());
return nullptr;
}
void setSignedState(const VSigning& signst) {

View File

@ -927,6 +927,18 @@ class ConstVisitor final : public VNVisitor {
// METHODS
V3Number constNumV(AstNode* nodep) {
// Contract C width to V width (if needed, else just direct copy)
// The upper zeros in the C representation can otherwise cause
// wrong results in some operations, e.g. MulS
const V3Number& numc = VN_AS(nodep, Const)->num();
return !numc.isNumber() ? numc : V3Number{nodep, nodep->widthMinV(), numc};
}
V3Number toNumC(AstNode* nodep, V3Number& numv) {
// Extend V width back to C width for given node
return !numv.isNumber() ? numv : V3Number{nodep, nodep->width(), numv};
}
bool operandConst(AstNode* nodep) { return VN_IS(nodep, Const); }
bool operandAsvConst(const AstNode* nodep) {
// BIASV(CONST, BIASV(CONST,...)) -> BIASV( BIASV_CONSTED(a,b), ...)
@ -1614,31 +1626,32 @@ class ConstVisitor final : public VNVisitor {
VL_DO_DANGLING(replaceNum(nodep, ones), nodep);
}
void replaceConst(AstNodeUniop* nodep) {
V3Number num{nodep, nodep->width()};
nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num());
V3Number numv{nodep, nodep->widthMinV()};
nodep->numberOperate(numv, constNumV(nodep->lhsp()));
const V3Number& num = toNumC(nodep, numv);
UINFO(4, "UNICONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConst(AstNodeBiop* nodep) {
V3Number num{nodep, nodep->width()};
nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(),
VN_AS(nodep->rhsp(), Const)->num());
V3Number numv{nodep, nodep->widthMinV()};
nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()));
const V3Number& num = toNumC(nodep, numv);
UINFO(4, "BICONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConst(AstNodeTriop* nodep) {
V3Number num{nodep, nodep->width()};
nodep->numberOperate(num, VN_AS(nodep->lhsp(), Const)->num(),
VN_AS(nodep->rhsp(), Const)->num(),
VN_AS(nodep->thsp(), Const)->num());
V3Number numv{nodep, nodep->widthMinV()};
nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()),
constNumV(nodep->thsp()));
const V3Number& num = toNumC(nodep, numv);
UINFO(4, "TRICONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
void replaceConst(AstNodeQuadop* nodep) {
V3Number num{nodep, nodep->width()};
nodep->numberOperate(
num, VN_AS(nodep->lhsp(), Const)->num(), VN_AS(nodep->rhsp(), Const)->num(),
VN_AS(nodep->thsp(), Const)->num(), VN_AS(nodep->fhsp(), Const)->num());
V3Number numv{nodep, nodep->widthMinV()};
nodep->numberOperate(numv, constNumV(nodep->lhsp()), constNumV(nodep->rhsp()),
constNumV(nodep->thsp()), constNumV(nodep->fhsp()));
const V3Number& num = toNumC(nodep, numv);
UINFO(4, "QUADCONST -> " << num << endl);
VL_DO_DANGLING(replaceNum(nodep, num), nodep);
}
@ -1719,6 +1732,7 @@ class ConstVisitor final : public VNVisitor {
nodep->rhsp(cp);
rp->lhsp(ap);
rp->rhsp(bp);
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
if (VN_IS(rp->lhsp(), Const) && VN_IS(rp->rhsp(), Const)) replaceConst(rp);
// if (debug()) nodep->dumpTree("- repAsvConst_new: ");
}
@ -1732,6 +1746,7 @@ class ConstVisitor final : public VNVisitor {
nodep->rhsp(lp);
lp->lhsp(lrp);
lp->rhsp(rp);
lp->dtypeFrom(nodep); // Upper widthMin more likely correct
// if (debug()) nodep->dumpTree("- repAsvLUp_new: ");
}
void replaceAsvRUp(AstNodeBiop* nodep) {
@ -1744,6 +1759,7 @@ class ConstVisitor final : public VNVisitor {
nodep->rhsp(rp);
rp->lhsp(lp);
rp->rhsp(rrp);
rp->dtypeFrom(nodep); // Upper widthMin more likely correct
// if (debug()) nodep->dumpTree("- repAsvRUp_new: ");
}
void replaceAndOr(AstNodeBiop* nodep) {
@ -3278,7 +3294,7 @@ class ConstVisitor final : public VNVisitor {
if (argp) {
AstNode* const nextp = argp->nextp();
if (VN_IS(argp, Const)) { // Convert it
const string out = VN_AS(argp, Const)->num().displayed(nodep, fmt);
const string out = constNumV(argp).displayed(nodep, fmt);
UINFO(9, " DispConst: " << fmt << " -> " << out << " for "
<< argp << endl);
// fmt = out w/ replace % with %% as it must be literal.

View File

@ -639,6 +639,7 @@ public:
bool isEqZero() const VL_MT_SAFE;
bool isNeqZero() const;
bool isBitsZero(int msb, int lsb) const;
bool isBroken(int vwidth) const;
bool isEqOne() const;
bool isEqAllOnes(int optwidth = 0) const;
bool isCaseEq(const V3Number& rhs) const; // operator==

View File

@ -0,0 +1,18 @@
#!/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=['--binary', '-fno-expand'])
test.execute()
test.passes()

View File

@ -0,0 +1,42 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0h\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
module t;
// Issue #5972
reg clk;
reg signed [28:28] in1;
reg signed [21:8] reg_10;
// verilator lint_off WIDTHEXPAND
always @(negedge clk) begin
// Issue #5972
reg_10[14:8] <= {1'b1, ~((in1[28:28] & ~(in1[28:28])))};
end
initial begin
clk = 1;
in1 = 1'b0;
reg_10 = '0;
#2;
clk = 0;
#2;
`checkh(reg_10, 3);
in1 = 1'b1;
clk = 1;
#2;
clk = 0;
#2;
`checkh(reg_10, 3);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/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=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,78 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
module t;
wire signed [21:10] out0;
sub sub (
.out0(out0)
);
sub2 sub2 ();
string s;
initial begin
#20;
// Bug with sformat, so can't just number-compare
s = $sformatf("out0=%0d", out0);
`checks(s, "out0=-12");
if (out0 > 0) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module sub (out0);
reg signed [27:20] reg_4;
output wire [21:10] out0;
initial begin
#1;
reg_4 = 0;
end
wire [11:0] w55;
wire [11:0] w23;
// verilator lint_off WIDTHEXPAND
assign w55 = ~reg_4[20];
// verilator lint_on WIDTHEXPAND
assign { w23[3], w23[1:0] } = 3'h0;
assign { w23[11:4], w23[2] } = { w55[11:4], w55[2] };
assign out0 = w23;
endmodule
module sub2;
reg [27:5] in0;
reg [26:11] in1;
wire [24:14] wire_0;
wire [26:5] out1;
wire w085;
wire w082;
wire [10:0] w092;
wire [9:0] w028;
string s;
initial begin
in0 = 6902127;
in1 = 10000;
#10;
s = $sformatf("out0=%0d", out1);
`checks(s, "out0=0");
end
assign w028 = ~ { 9'h000, in0[23] };
assign w092[1] = 1'h0;
assign { w092[10:2], w092[0] } = w028;
assign wire_0 = w092;
assign w082 = | wire_0[18:17];
assign w085 = w082 ? in1[11] : 1'h0;
assign out1 = { 21'h000000, w085 };
endmodule