Improve Reloop to accept constant index offsets. (#2939)
V3Reloop now can roll up indexed assignments between arrays if there is a constant offset between indices on the left and right hand sides, e.g.: a[0] = b[2]; a[1] = b[3]; ... a[x] = b[x + 2];
This commit is contained in:
parent
45fbd98ff1
commit
267c6f6dce
113
src/V3Reloop.cpp
113
src/V3Reloop.cpp
|
|
@ -18,12 +18,12 @@
|
||||||
// Each CFunc:
|
// Each CFunc:
|
||||||
// Look for a series of assignments that would look better in a loop:
|
// Look for a series of assignments that would look better in a loop:
|
||||||
//
|
//
|
||||||
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #))
|
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #+C))
|
||||||
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1))
|
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1+C))
|
||||||
// ->
|
// ->
|
||||||
// Create __Vilp local variable
|
// Create __Vilp local variable
|
||||||
// FOR(__Vilp = low; __Vilp <= high; ++__Vlip)
|
// FOR(__Vilp = low; __Vilp <= high; ++__Vlip)
|
||||||
// ASSIGN(ARRAYREF(var, __Vilp), ARRAYREF(var, __Vilp))
|
// ASSIGN(ARRAYREF(var, __Vilp), ARRAYREF(var, __Vilp + C))
|
||||||
//
|
//
|
||||||
// Likewise vector assign to the same constant converted to a loop.
|
// Likewise vector assign to the same constant converted to a loop.
|
||||||
//
|
//
|
||||||
|
|
@ -61,6 +61,7 @@ private:
|
||||||
AstNodeSel* m_mgSelRp = nullptr; // Parent select, nullptr = constant
|
AstNodeSel* m_mgSelRp = nullptr; // Parent select, nullptr = constant
|
||||||
AstNodeVarRef* m_mgVarrefLp = nullptr; // Parent varref
|
AstNodeVarRef* m_mgVarrefLp = nullptr; // Parent varref
|
||||||
AstNodeVarRef* m_mgVarrefRp = nullptr; // Parent varref, nullptr = constant
|
AstNodeVarRef* m_mgVarrefRp = nullptr; // Parent varref, nullptr = constant
|
||||||
|
int64_t m_mgOffset = 0; // Index offset
|
||||||
AstConst* m_mgConstRp = nullptr; // Parent RHS constant, nullptr = sel
|
AstConst* m_mgConstRp = nullptr; // Parent RHS constant, nullptr = sel
|
||||||
uint32_t m_mgIndexLo = 0; // Merge range
|
uint32_t m_mgIndexLo = 0; // Merge range
|
||||||
uint32_t m_mgIndexHi = 0; // Merge range
|
uint32_t m_mgIndexHi = 0; // Merge range
|
||||||
|
|
@ -83,38 +84,50 @@ private:
|
||||||
if (!m_mgAssignps.empty()) {
|
if (!m_mgAssignps.empty()) {
|
||||||
uint32_t items = m_mgIndexHi - m_mgIndexLo + 1;
|
uint32_t items = m_mgIndexHi - m_mgIndexLo + 1;
|
||||||
UINFO(9, "End merge iter=" << items << " " << m_mgIndexHi << ":" << m_mgIndexLo << " "
|
UINFO(9, "End merge iter=" << items << " " << m_mgIndexHi << ":" << m_mgIndexLo << " "
|
||||||
<< m_mgAssignps[0] << endl);
|
<< m_mgOffset << " " << m_mgAssignps[0] << endl);
|
||||||
if (items >= RELOOP_MIN_ITERS) {
|
if (items >= RELOOP_MIN_ITERS) {
|
||||||
UINFO(6, "Reloop merging items=" << items << " " << m_mgIndexHi << ":"
|
UINFO(6, "Reloop merging items=" << items << " " << m_mgIndexHi << ":"
|
||||||
<< m_mgIndexLo << " " << m_mgAssignps[0] << endl);
|
<< m_mgIndexLo << " " << m_mgOffset << " "
|
||||||
|
<< m_mgAssignps[0] << endl);
|
||||||
++m_statReloops;
|
++m_statReloops;
|
||||||
m_statReItems += items;
|
m_statReItems += items;
|
||||||
|
|
||||||
// Transform first assign into for loop body
|
// Transform first assign into for loop body
|
||||||
AstNodeAssign* bodyp = m_mgAssignps.front();
|
AstNodeAssign* const bodyp = m_mgAssignps.front();
|
||||||
UASSERT_OBJ(bodyp->lhsp() == m_mgSelLp, bodyp, "Corrupt queue/state");
|
UASSERT_OBJ(bodyp->lhsp() == m_mgSelLp, bodyp, "Corrupt queue/state");
|
||||||
FileLine* fl = bodyp->fileline();
|
FileLine* const fl = bodyp->fileline();
|
||||||
AstVar* itp = findCreateVarTemp(fl, m_mgCfuncp);
|
AstVar* const itp = findCreateVarTemp(fl, m_mgCfuncp);
|
||||||
|
|
||||||
AstNode* initp = new AstAssign(fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
if (m_mgOffset > 0) {
|
||||||
new AstConst(fl, m_mgIndexLo));
|
UASSERT_OBJ(m_mgIndexLo >= m_mgOffset, bodyp,
|
||||||
AstNode* condp = new AstLte(fl, new AstVarRef(fl, itp, VAccess::READ),
|
"Reloop iteration starts at negative index");
|
||||||
new AstConst(fl, m_mgIndexHi));
|
m_mgIndexLo -= m_mgOffset;
|
||||||
AstNode* incp = new AstAssign(
|
m_mgIndexHi -= m_mgOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstNode* const initp = new AstAssign(fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
||||||
|
new AstConst(fl, m_mgIndexLo));
|
||||||
|
AstNode* const condp = new AstLte(fl, new AstVarRef(fl, itp, VAccess::READ),
|
||||||
|
new AstConst(fl, m_mgIndexHi));
|
||||||
|
AstNode* const incp = new AstAssign(
|
||||||
fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
||||||
new AstAdd(fl, new AstConst(fl, 1), new AstVarRef(fl, itp, VAccess::READ)));
|
new AstAdd(fl, new AstConst(fl, 1), new AstVarRef(fl, itp, VAccess::READ)));
|
||||||
AstWhile* whilep = new AstWhile(fl, condp, nullptr, incp);
|
AstWhile* const whilep = new AstWhile(fl, condp, nullptr, incp);
|
||||||
initp->addNext(whilep);
|
initp->addNext(whilep);
|
||||||
bodyp->replaceWith(initp);
|
bodyp->replaceWith(initp);
|
||||||
whilep->addBodysp(bodyp);
|
whilep->addBodysp(bodyp);
|
||||||
|
|
||||||
// Replace constant index with new loop index
|
// Replace constant index with new loop index
|
||||||
AstNode* lbitp = m_mgSelLp->bitp();
|
AstNode* const offsetp
|
||||||
lbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
|
= m_mgOffset == 0 ? nullptr : new AstConst(fl, std::abs(m_mgOffset));
|
||||||
|
AstNode* const lbitp = m_mgSelLp->bitp();
|
||||||
|
AstNode* const lvrefp = new AstVarRef(fl, itp, VAccess::READ);
|
||||||
|
lbitp->replaceWith(m_mgOffset > 0 ? new AstAdd(fl, lvrefp, offsetp) : lvrefp);
|
||||||
VL_DO_DANGLING(lbitp->deleteTree(), lbitp);
|
VL_DO_DANGLING(lbitp->deleteTree(), lbitp);
|
||||||
if (m_mgSelRp) { // else constant and no replace
|
if (m_mgSelRp) { // else constant and no replace
|
||||||
AstNode* rbitp = m_mgSelRp->bitp();
|
AstNode* const rbitp = m_mgSelRp->bitp();
|
||||||
rbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
|
AstNode* const rvrefp = new AstVarRef(fl, itp, VAccess::READ);
|
||||||
|
rbitp->replaceWith(m_mgOffset < 0 ? new AstAdd(fl, rvrefp, offsetp) : rvrefp);
|
||||||
VL_DO_DANGLING(rbitp->deleteTree(), lbitp);
|
VL_DO_DANGLING(rbitp->deleteTree(), lbitp);
|
||||||
}
|
}
|
||||||
if (debug() >= 9) initp->dumpTree(cout, "-new: ");
|
if (debug() >= 9) initp->dumpTree(cout, "-new: ");
|
||||||
|
|
@ -133,6 +146,7 @@ private:
|
||||||
m_mgSelRp = nullptr;
|
m_mgSelRp = nullptr;
|
||||||
m_mgVarrefLp = nullptr;
|
m_mgVarrefLp = nullptr;
|
||||||
m_mgVarrefRp = nullptr;
|
m_mgVarrefRp = nullptr;
|
||||||
|
m_mgOffset = 0;
|
||||||
m_mgConstRp = nullptr;
|
m_mgConstRp = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,6 +157,7 @@ private:
|
||||||
{
|
{
|
||||||
m_cfuncp = nodep;
|
m_cfuncp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
mergeEnd(); // Finish last pending merge, if any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeAssign* nodep) override {
|
virtual void visit(AstNodeAssign* nodep) override {
|
||||||
|
|
@ -155,7 +170,7 @@ private:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Of a constant index
|
// Of a constant index
|
||||||
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
|
AstConst* const lbitp = VN_CAST(lselp->bitp(), Const);
|
||||||
if (!lbitp) {
|
if (!lbitp) {
|
||||||
mergeEnd();
|
mergeEnd();
|
||||||
return;
|
return;
|
||||||
|
|
@ -164,53 +179,58 @@ private:
|
||||||
mergeEnd();
|
mergeEnd();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t index = lbitp->toUInt();
|
const uint32_t lindex = lbitp->toUInt();
|
||||||
// Of variable
|
// Of variable
|
||||||
AstNodeVarRef* lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
AstNodeVarRef* const lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
||||||
if (!lvarrefp) {
|
if (!lvarrefp) {
|
||||||
mergeEnd();
|
mergeEnd();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// RHS is a constant or a select
|
// RHS is a constant or a select
|
||||||
AstConst* rconstp = VN_CAST(nodep->rhsp(), Const);
|
AstConst* const rconstp = VN_CAST(nodep->rhsp(), Const);
|
||||||
AstNodeSel* rselp = VN_CAST(nodep->rhsp(), NodeSel);
|
AstNodeSel* const rselp = VN_CAST(nodep->rhsp(), NodeSel);
|
||||||
AstNodeVarRef* rvarrefp = nullptr;
|
AstNodeVarRef* rvarrefp = nullptr;
|
||||||
|
uint32_t rindex = lindex;
|
||||||
if (rconstp) { // Ok
|
if (rconstp) { // Ok
|
||||||
} else {
|
} else if (rselp) {
|
||||||
if (!rselp) {
|
AstConst* const rbitp = VN_CAST(rselp->bitp(), Const);
|
||||||
mergeEnd();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AstConst* rbitp = VN_CAST(rselp->bitp(), Const);
|
|
||||||
rvarrefp = VN_CAST(rselp->fromp(), NodeVarRef);
|
rvarrefp = VN_CAST(rselp->fromp(), NodeVarRef);
|
||||||
if (!rbitp || rbitp->toUInt() != index || !rvarrefp
|
if (!rbitp || !rvarrefp || lvarrefp->varp() == rvarrefp->varp()) {
|
||||||
|| lvarrefp->varp() == rvarrefp->varp()) {
|
|
||||||
mergeEnd();
|
mergeEnd();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
rindex = rbitp->toUInt();
|
||||||
|
} else {
|
||||||
|
mergeEnd();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_mgSelLp) { // Old merge
|
if (m_mgSelLp) { // Old merge
|
||||||
if (m_mgCfuncp == m_cfuncp && m_mgNextp == nodep && m_mgSelLp->same(lselp)
|
if (m_mgCfuncp == m_cfuncp // In same function
|
||||||
&& m_mgVarrefLp->same(lvarrefp)
|
&& m_mgNextp == nodep // Consecutive node
|
||||||
&& (m_mgConstRp
|
&& m_mgVarrefLp->same(lvarrefp) // Same array on left hand side
|
||||||
? (rconstp && m_mgConstRp->same(rconstp))
|
&& (m_mgConstRp // On the right hand side either ...
|
||||||
: (rselp && m_mgSelRp->same(rselp) && m_mgVarrefRp->same(rvarrefp)))
|
? (rconstp && m_mgConstRp->same(rconstp)) // ... same constant
|
||||||
&& (index == m_mgIndexLo - 1 || index == m_mgIndexHi + 1)) {
|
: (rselp && m_mgVarrefRp->same(rvarrefp))) // ... or same array
|
||||||
|
&& (lindex == m_mgIndexLo - 1 || lindex == m_mgIndexHi + 1) // Left index +/- 1
|
||||||
|
&& (m_mgConstRp || lindex == rindex + m_mgOffset) // Same right index offset
|
||||||
|
) {
|
||||||
// Sequentially next to last assign; continue merge
|
// Sequentially next to last assign; continue merge
|
||||||
if (index == m_mgIndexLo - 1) {
|
if (lindex == m_mgIndexLo - 1) {
|
||||||
m_mgIndexLo = index;
|
m_mgIndexLo = lindex;
|
||||||
} else if (index == m_mgIndexHi + 1) {
|
} else if (lindex == m_mgIndexHi + 1) {
|
||||||
m_mgIndexHi = index;
|
m_mgIndexHi = lindex;
|
||||||
}
|
}
|
||||||
UINFO(9, "Continue merge i=" << index << " " << m_mgIndexHi << ":" << m_mgIndexLo
|
UINFO(9, "Continue merge i=" << lindex << " " << m_mgIndexHi << ":" << m_mgIndexLo
|
||||||
<< " " << nodep << endl);
|
<< " " << nodep << endl);
|
||||||
m_mgAssignps.push_back(nodep);
|
m_mgAssignps.push_back(nodep);
|
||||||
m_mgNextp = nodep->nextp();
|
m_mgNextp = nodep->nextp();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// This assign doesn't merge with previous assign,
|
UINFO(9, "End merge i="
|
||||||
|
<< lindex << " " << m_mgIndexHi << ":" << m_mgIndexLo << " " << nodep
|
||||||
|
<< endl); // This assign doesn't merge with previous assign,
|
||||||
// but should start a new merge
|
// but should start a new merge
|
||||||
mergeEnd();
|
mergeEnd();
|
||||||
}
|
}
|
||||||
|
|
@ -224,10 +244,11 @@ private:
|
||||||
m_mgSelRp = rselp;
|
m_mgSelRp = rselp;
|
||||||
m_mgVarrefLp = lvarrefp;
|
m_mgVarrefLp = lvarrefp;
|
||||||
m_mgVarrefRp = rvarrefp;
|
m_mgVarrefRp = rvarrefp;
|
||||||
|
m_mgOffset = static_cast<int64_t>(lindex) - static_cast<int64_t>(rindex);
|
||||||
m_mgConstRp = rconstp;
|
m_mgConstRp = rconstp;
|
||||||
m_mgIndexLo = index;
|
m_mgIndexLo = lindex;
|
||||||
m_mgIndexHi = index;
|
m_mgIndexHi = lindex;
|
||||||
UINFO(9, "Start merge i=" << index << " " << nodep << endl);
|
UINFO(9, "Start merge i=" << lindex << " o=" << m_mgOffset << nodep << endl);
|
||||||
}
|
}
|
||||||
//--------------------
|
//--------------------
|
||||||
virtual void visit(AstVar*) override {} // Accelerate
|
virtual void visit(AstVar*) override {} // Accelerate
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
shift down 1
|
||||||
|
oarray[63] is 0
|
||||||
|
oarray[62] is 63
|
||||||
|
oarray[61] is 62
|
||||||
|
oarray[32] is 33
|
||||||
|
oarray[ 2] is 3
|
||||||
|
oarray[ 1] is 2
|
||||||
|
oarray[ 0] is 1
|
||||||
|
shift up 2
|
||||||
|
oarray[63] is 61
|
||||||
|
oarray[62] is 60
|
||||||
|
oarray[61] is 59
|
||||||
|
oarray[32] is 30
|
||||||
|
oarray[ 2] is 0
|
||||||
|
oarray[ 1] is 2
|
||||||
|
oarray[ 0] is 1
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003 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
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["-unroll-count 1024",
|
||||||
|
$Self->wno_unopthreads_for_few_cores(),
|
||||||
|
"--stats"],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($Self->{vlt}) {
|
||||||
|
# Note, with vltmt this might be split differently, so only checking vlt
|
||||||
|
file_grep($Self->{stats}, qr/Optimizations, Reloop iterations\s+(\d+)/i,
|
||||||
|
125);
|
||||||
|
file_grep($Self->{stats}, qr/Optimizations, Reloops\s+(\d+)/i,
|
||||||
|
2);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2020 by Geza Lore.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`define show(x) $display("oarray[%2d] is %2d", x, oarray[x])
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/);
|
||||||
|
|
||||||
|
int iarray [63:0];
|
||||||
|
int oarray [63:0];
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
for (int i = 0; i < 64 ; i = i + 1) begin
|
||||||
|
iarray[i] = i;
|
||||||
|
oarray[i] = 0;
|
||||||
|
end
|
||||||
|
|
||||||
|
for (int i = 0; i < 63; i = i + 1) begin
|
||||||
|
oarray[i] = iarray[i + 1];
|
||||||
|
end
|
||||||
|
|
||||||
|
$display("shift down 1");
|
||||||
|
`show(63);
|
||||||
|
`show(62);
|
||||||
|
`show(61);
|
||||||
|
`show(32);
|
||||||
|
`show(2);
|
||||||
|
`show(1);
|
||||||
|
`show(0);
|
||||||
|
|
||||||
|
for (int i = 63; i >= 2 ; i = i - 1) begin
|
||||||
|
oarray[i] = iarray[i - 2];
|
||||||
|
end
|
||||||
|
|
||||||
|
$display("shift up 2");
|
||||||
|
`show(63);
|
||||||
|
`show(62);
|
||||||
|
`show(61);
|
||||||
|
`show(32);
|
||||||
|
`show(2);
|
||||||
|
`show(1);
|
||||||
|
`show(0);
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue