diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 5f9be643d..e893c5133 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -119,13 +119,25 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { m_constp = constp; m_msb = constp->widthMin() - 1; } + // updateBitRange(), limitBitRangeToLsb(), and polarity() must be called during ascending + // back to the root. + void updateBitRange(int newLsb, int newMsb) { + if ((m_lsb <= m_msb && newLsb > newMsb) || (m_lsb > m_msb && m_lsb < newLsb)) { + // When the new bit range is out of m_refp, clear polarity because nodes below is + // shifted out to zero. + // This kind of clear may happen several times. e.g. (!(1'b1 >> 1)) >> 1 + polarity(true); + } + m_lsb = newLsb; + m_msb = newMsb; + } void updateBitRange(const AstCCast* castp) { - m_msb = std::min(m_msb, m_lsb + castp->width() - 1); + updateBitRange(m_lsb, std::min(m_msb, m_lsb + castp->width() - 1)); } void updateBitRange(const AstShiftR* shiftp) { - m_lsb += VN_AS(shiftp->rhsp(), Const)->toUInt(); + updateBitRange(m_lsb + VN_AS(shiftp->rhsp(), Const)->toUInt(), m_msb); } - void limitBitRangeToLsb() { m_msb = std::min(m_msb, m_lsb); } + void limitBitRangeToLsb() { updateBitRange(m_lsb, std::min(m_msb, m_lsb)); } int wordIdx() const { return m_wordIdx; } void wordIdx(int i) { m_wordIdx = i; } bool polarity() const { return m_polarity; } @@ -467,6 +479,7 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { // Don't restore m_polarity for Xor as it counts parity of the entire tree if (!isXorTree()) m_polarity = !m_polarity; if (m_leafp && castp) m_leafp->updateBitRange(castp); + if (m_leafp) m_leafp->polarity(!m_leafp->polarity()); } void visit(AstWordSel* nodep) override { CONST_BITOP_RETURN_IF(!m_leafp, nodep); @@ -479,7 +492,6 @@ class ConstBitOpTreeVisitor final : public VNVisitorConst { void visit(AstVarRef* nodep) override { CONST_BITOP_RETURN_IF(!m_leafp, nodep); m_leafp->setLeaf(nodep); - m_leafp->polarity(m_polarity); } void visit(AstConst* nodep) override { CONST_BITOP_RETURN_IF(!m_leafp, nodep); diff --git a/test_regress/t/t_opt_const.v b/test_regress/t/t_opt_const.v index 970774fe3..be0e361c1 100644 --- a/test_regress/t/t_opt_const.v +++ b/test_regress/t/t_opt_const.v @@ -154,6 +154,7 @@ module Test(/*AUTOARG*/ bug4864 i_bug4864(.clk(clk), .in(d), .out(bug4864_out)); bug5186 i_bug5186(.clk(clk), .in(d), .out(bug5186_out)); bug5993 i_bug5993(.clk(clk), .in(d[10])); + bug6016 i_bug6016(.clk(clk), .in(d[10])); endmodule @@ -593,4 +594,32 @@ module bug5993(input wire clk, input wire in); in4 <= in ? 8'b00111__0__10 : 8'b00111__1__10; checkd(wire_2, 1'b0); end + endmodule + +// See issue #6016 +// When traversing a tree, a signal may be shifted out. +// Then the polarity has to be cleared, but was not. +// "(!in[18]) > 1" should be 0, but was not. +module bug6016(input wire clk, input wire in); + reg in0; + reg signed [7:0] in4; + wire [1:0] wire_0; + wire out20; + + // verilator lint_off WIDTH + assign wire_0 = in4[0:0] ? ({{7{in4[3:1]}}, 12'd201} & 2'h2) : (!(in0) >> 9'b1111); + // verilator lint_on WIDTH + assign out20 = wire_0[0:0]; + + logic in_s1 = 1'b0; + always @(posedge clk) begin + in_s1 <= in; + if (in) begin + in4 <= 8'b1111_1110; + in0 <= 1'b0; + end + if (in_s1) begin + if (out20 != 1'b0) $stop; + end + end endmodule