Fix DFG misoptimizing bound checks (#7755)

Signed-off-by: Jakub Michalski <jmichalski@antmicro.com>
This commit is contained in:
Jakub Michalski 2026-07-02 12:33:38 +02:00 committed by GitHub
parent 964474837f
commit 853ee5df17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 66 additions and 11 deletions

View File

@ -620,6 +620,12 @@ DfgVertex::DfgVertex(DfgGraph& dfg, VDfgType type, FileLine* flp, const DfgDataT
dfg.addVertex(*this);
}
bool DfgVertex::unsafe() const {
if (is<DfgMux>()) return true;
if (is<DfgArraySel>()) return !as<DfgArraySel>()->bitp()->is<DfgConst>();
return false;
}
void DfgVertex::typeCheck(const DfgGraph& dfg) const {
#define CHECK(cond, msg) \

View File

@ -195,6 +195,8 @@ public:
UASSERT_OBJ(m_dtype.isPacked(), this, "Non packed vertex has no 'width'");
return m_dtype.size();
}
// Has terminating side-effect
bool unsafe() const;
// Type check vertex (for debugging)
void typeCheck(const DfgGraph& dfg) const;

View File

@ -2269,7 +2269,7 @@ class V3DfgPeephole final : public DfgVisitor {
}
void visit(DfgLogAnd* const vtxp) override {
if (binary(vtxp)) return;
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
DfgVertex* const lhsp = vtxp->lhsp();
DfgVertex* const rhsp = vtxp->rhsp();
@ -2287,11 +2287,11 @@ class V3DfgPeephole final : public DfgVisitor {
}
void visit(DfgLogIf* const vtxp) override {
if (binary(vtxp)) return;
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
}
void visit(DfgLogOr* const vtxp) override {
if (binary(vtxp)) return;
if (binary(vtxp) || vtxp->rhsp()->unsafe()) return;
DfgVertex* const lhsp = vtxp->lhsp();
DfgVertex* const rhsp = vtxp->rhsp();
@ -2890,13 +2890,13 @@ class V3DfgPeephole final : public DfgVisitor {
}
if (vtxp->dtype() == m_bitDType) {
if (isSame(condp, thenp)) { // a ? a : b becomes a | b
if (isSame(condp, thenp) && !elsep->unsafe()) { // a ? a : b becomes a | b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_COND) {
replace(make<DfgOr>(vtxp, condp, elsep));
return;
}
}
if (isSame(condp, elsep)) { // a ? b : a becomes a & b
if (isSame(condp, elsep) && !thenp->unsafe()) { // a ? b : a becomes a & b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_COND) {
replace(make<DfgAnd>(vtxp, condp, thenp));
return;
@ -2905,28 +2905,28 @@ class V3DfgPeephole final : public DfgVisitor {
}
if (vtxp->width() <= VL_QUADSIZE) {
if (isZero(thenp)) { // a ? 0 : b becomes ~a & b
if (isZero(thenp) && !elsep->unsafe()) { // a ? 0 : b becomes ~a & b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ZERO) {
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
replace(make<DfgAnd>(vtxp, maskp, elsep));
return;
}
}
if (isOnes(thenp)) { // a ? 1 : b becomes a | b
if (isOnes(thenp) && !elsep->unsafe()) { // a ? 1 : b becomes a | b
APPLYING(REPLACE_COND_WITH_THEN_BRANCH_ONES) {
DfgVertex* const maskp = replicate(vtxp, condp);
replace(make<DfgOr>(vtxp, maskp, elsep));
return;
}
}
if (isZero(elsep)) { // a ? b : 0 becomes a & b
if (isZero(elsep) && !thenp->unsafe()) { // a ? b : 0 becomes a & b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ZERO) {
DfgVertex* const maskp = replicate(vtxp, condp);
replace(make<DfgAnd>(vtxp, maskp, thenp));
return;
}
}
if (isOnes(elsep)) { // a ? b : 1 becomes ~a | b
if (isOnes(elsep) && !thenp->unsafe()) { // a ? b : 1 becomes ~a | b
APPLYING(REPLACE_COND_WITH_ELSE_BRANCH_ONES) {
DfgVertex* const maskp = replicate(vtxp, make<DfgNot>(condp, condp));
replace(make<DfgOr>(vtxp, maskp, thenp));
@ -2935,7 +2935,8 @@ class V3DfgPeephole final : public DfgVisitor {
}
if (DfgOr* const tOrp = thenp->cast<DfgOr>()) {
if (isSame(tOrp->lhsp(), elsep)) { // a ? b | c : b becomes b | (a & c)
if (isSame(tOrp->lhsp(), elsep)
&& !tOrp->rhsp()->unsafe()) { // a ? b | c : b becomes b | (a & c)
APPLYING(REPLACE_COND_THEN_OR_LHS) {
DfgVertex* const maskp = replicate(vtxp, condp);
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->rhsp());
@ -2943,7 +2944,8 @@ class V3DfgPeephole final : public DfgVisitor {
return;
}
}
if (isSame(tOrp->rhsp(), elsep)) { // a ? b | c : c becomes c | (a & b)
if (isSame(tOrp->rhsp(), elsep)
&& !tOrp->lhsp()->unsafe()) { // a ? b | c : c becomes c | (a & b)
APPLYING(REPLACE_COND_THEN_OR_RHS) {
DfgVertex* const maskp = replicate(vtxp, condp);
DfgAnd* const andp = make<DfgAnd>(vtxp, maskp, tOrp->lhsp());

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["-runtime-debug"])
test.execute()
test.passes()

View File

@ -0,0 +1,27 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t (
input clk
);
wire mem_wire;
bit [15:0] idx = 65535;
bit mem_reg[0:34000];
assign mem_wire = mem_reg[idx];
always @(posedge clk) begin
if (idx < 65533) begin
$display("oob_val %d", mem_wire);
$write("*-* All Finished *-*\n");
$finish;
end
else begin
idx <= idx - 1;
mem_reg[idx] <= 0;
end
end
endmodule