Fix slice memory overflow on large output arrays (#6636) (#6638)

This commit is contained in:
Jens Yuechao Liu 2025-11-05 11:48:22 +01:00 committed by GitHub
parent fe1a9e9ea7
commit e2f5854088
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 125 additions and 2 deletions

View File

@ -105,6 +105,7 @@ Jamey Hicks
Jamie Iles Jamie Iles
Jan Van Winkel Jan Van Winkel
Jean Berniolles Jean Berniolles
Jens Yuechao Liu
Jeremy Bennett Jeremy Bennett
Jesse Taube Jesse Taube
Jevin Sweval Jevin Sweval

View File

@ -719,6 +719,11 @@ Summary:
automatically. Variables explicitly annotated with automatically. Variables explicitly annotated with
:option:`/*verilator&32;split_var*/` are still split. :option:`/*verilator&32;split_var*/` are still split.
.. option:: --fslice-element-limit
Rarely needed. Set the maximum array size (number of elements)
for slice optimization to avoid excessive memory usage.
.. option:: -future0 <option> .. option:: -future0 <option>
Rarely needed. Suppress an unknown Verilator option for an option that Rarely needed. Suppress an unknown Verilator option for an option that

View File

@ -1458,13 +1458,16 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
DECL_OPTION("-freloop", FOnOff, &m_fReloop); DECL_OPTION("-freloop", FOnOff, &m_fReloop);
DECL_OPTION("-freorder", FOnOff, &m_fReorder); DECL_OPTION("-freorder", FOnOff, &m_fReorder);
DECL_OPTION("-fslice", FOnOff, &m_fSlice); DECL_OPTION("-fslice", FOnOff, &m_fSlice);
DECL_OPTION("-fslice-element-limit", CbVal, [this, fl](const char* valp) {
m_fSliceElementLimit = std::atoi(valp);
if (m_fSliceElementLimit < 0) fl->v3fatal("--fslice-element-limit must be >= 0: " << valp);
});
DECL_OPTION("-fsplit", FOnOff, &m_fSplit); DECL_OPTION("-fsplit", FOnOff, &m_fSplit);
DECL_OPTION("-fsubst", FOnOff, &m_fSubst); DECL_OPTION("-fsubst", FOnOff, &m_fSubst);
DECL_OPTION("-fsubst-const", FOnOff, &m_fSubstConst); DECL_OPTION("-fsubst-const", FOnOff, &m_fSubstConst);
DECL_OPTION("-ftable", FOnOff, &m_fTable); DECL_OPTION("-ftable", FOnOff, &m_fTable);
DECL_OPTION("-ftaskify-all-forked", FOnOff, &m_fTaskifyAll).undocumented(); // Debug DECL_OPTION("-ftaskify-all-forked", FOnOff, &m_fTaskifyAll).undocumented(); // Debug
DECL_OPTION("-fvar-split", FOnOff, &m_fVarSplit); DECL_OPTION("-fvar-split", FOnOff, &m_fVarSplit);
DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); }); DECL_OPTION("-G", CbPartialMatch, [this](const char* optp) { addParameter(optp, false); });
DECL_OPTION("-gate-stmts", Set, &m_gateStmts); DECL_OPTION("-gate-stmts", Set, &m_gateStmts);
DECL_OPTION("-gdb", CbCall, []() {}); // Processed only in bin/verilator shell DECL_OPTION("-gdb", CbCall, []() {}); // Processed only in bin/verilator shell

View File

@ -413,6 +413,7 @@ private:
bool m_fReloop; // main switch: -fno-reloop: reform loops bool m_fReloop; // main switch: -fno-reloop: reform loops
bool m_fReorder; // main switch: -fno-reorder: reorder assignments in blocks bool m_fReorder; // main switch: -fno-reorder: reorder assignments in blocks
bool m_fSlice = true; // main switch: -fno-slice: array assignment slicing bool m_fSlice = true; // main switch: -fno-slice: array assignment slicing
int m_fSliceElementLimit = 256; // main switch: --fslice-element-limit
bool m_fSplit; // main switch: -fno-split: always assignment splitting bool m_fSplit; // main switch: -fno-split: always assignment splitting
bool m_fSubst; // main switch: -fno-subst: substitute expression temp values bool m_fSubst; // main switch: -fno-subst: substitute expression temp values
bool m_fSubstConst; // main switch: -fno-subst-const: final constant substitution bool m_fSubstConst; // main switch: -fno-subst-const: final constant substitution
@ -726,6 +727,7 @@ public:
bool fReloop() const { return m_fReloop; } bool fReloop() const { return m_fReloop; }
bool fReorder() const { return m_fReorder; } bool fReorder() const { return m_fReorder; }
bool fSlice() const { return m_fSlice; } bool fSlice() const { return m_fSlice; }
int fSliceElementLimit() const { return m_fSliceElementLimit; }
bool fSplit() const { return m_fSplit; } bool fSplit() const { return m_fSplit; }
bool fSubst() const { return m_fSubst; } bool fSubst() const { return m_fSubst; }
bool fSubstConst() const { return m_fSubstConst; } bool fSubstConst() const { return m_fSubstConst; }

View File

@ -58,6 +58,7 @@ class SliceVisitor final : public VNVisitor {
// STATE - across all visitors // STATE - across all visitors
VDouble0 m_statAssigns; // Statistic tracking VDouble0 m_statAssigns; // Statistic tracking
VDouble0 m_statSliceElementSkips; // Statistic tracking
// STATE - for current visit position (use VL_RESTORER) // STATE - for current visit position (use VL_RESTORER)
AstNode* m_assignp = nullptr; // Assignment we are under AstNode* m_assignp = nullptr; // Assignment we are under
@ -248,6 +249,14 @@ class SliceVisitor final : public VNVisitor {
return false; return false;
} }
// Skip optimization if array is too large
const int elements = arrayp->rangep()->elementsConst();
const int elementLimit = v3Global.opt.fSliceElementLimit();
if (elements > elementLimit && elementLimit > 0) {
++m_statSliceElementSkips;
return false;
}
UINFO(4, "Slice optimizing " << nodep); UINFO(4, "Slice optimizing " << nodep);
++m_statAssigns; ++m_statAssigns;
@ -256,7 +265,6 @@ class SliceVisitor final : public VNVisitor {
// Assign of an ascending range slice to a descending range one must reverse // Assign of an ascending range slice to a descending range one must reverse
// the elements // the elements
AstNodeAssign* newlistp = nullptr; AstNodeAssign* newlistp = nullptr;
const int elements = arrayp->rangep()->elementsConst();
for (int elemIdx = 0; elemIdx < elements; ++elemIdx) { for (int elemIdx = 0; elemIdx < elements; ++elemIdx) {
// Original node is replaced, so it is safe to copy it one time even if it is impure. // Original node is replaced, so it is safe to copy it one time even if it is impure.
AstNodeAssign* const newp AstNodeAssign* const newp
@ -383,6 +391,7 @@ public:
explicit SliceVisitor(AstNetlist* nodep) { iterate(nodep); } explicit SliceVisitor(AstNetlist* nodep) { iterate(nodep); }
~SliceVisitor() override { ~SliceVisitor() override {
V3Stats::addStat("Optimizations, Slice array assignments", m_statAssigns); V3Stats::addStat("Optimizations, Slice array assignments", m_statAssigns);
V3Stats::addStat("Optimizations, Slice array skips due to size limit", m_statSliceElementSkips);
} }
}; };

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.top_filename = "t/t_opt_slice_element_limit.v"
test.compile(verilator_flags2=['--stats', '--fslice-element-limit', '10'])
test.file_grep(test.stats, r'Optimizations, Slice array skips due to size limit\s+(\d+)', 4)
test.passes()

View File

@ -0,0 +1,20 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (
input logic [7:0] i1 [8],
input logic [7:0] i2 [16],
input logic [7:0] i3 [512],
output logic [7:0] o1 [8],
output logic [7:0] o2 [16],
output logic [7:0] o3 [256]
);
initial begin
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.top_filename = "t/t_opt_slice_element_limit.v"
test.compile(verilator_flags2=['--stats', '--fslice-element-limit', '0'])
test.file_grep(test.stats, r'Optimizations, Slice array skips due to size limit\s+(\d+)', 0)
test.passes()

View File

@ -0,0 +1,2 @@
%Error: --fslice-element-limit must be >= 0: -100
... See the manual at https://verilator.org/verilator_doc.html?v=5.043 for more assistance.

View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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('linter')
test.top_filename = "t/t_opt_slice_element_limit.v"
test.golden_filename = "t/t_opt_slice_element_limit_bad.out"
test.lint(fails=True,
verilator_flags2=['--stats', '--fslice-element-limit', '-100'],
except_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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.top_filename = "t/t_opt_slice_element_limit.v"
test.compile(verilator_flags2=['--stats'])
test.file_grep(test.stats, r'Optimizations, Slice array skips due to size limit\s+(\d+)', 1)
test.passes()