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:
|
||||
// Look for a series of assignments that would look better in a loop:
|
||||
//
|
||||
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #))
|
||||
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1))
|
||||
// ASSIGN(ARRAYREF(var, #), ARRAYREF(var, #+C))
|
||||
// ASSIGN(ARRAYREF(var, #+1), ARRAYREF(var, #+1+C))
|
||||
// ->
|
||||
// Create __Vilp local variable
|
||||
// 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.
|
||||
//
|
||||
|
|
@ -61,6 +61,7 @@ private:
|
|||
AstNodeSel* m_mgSelRp = nullptr; // Parent select, nullptr = constant
|
||||
AstNodeVarRef* m_mgVarrefLp = nullptr; // Parent varref
|
||||
AstNodeVarRef* m_mgVarrefRp = nullptr; // Parent varref, nullptr = constant
|
||||
int64_t m_mgOffset = 0; // Index offset
|
||||
AstConst* m_mgConstRp = nullptr; // Parent RHS constant, nullptr = sel
|
||||
uint32_t m_mgIndexLo = 0; // Merge range
|
||||
uint32_t m_mgIndexHi = 0; // Merge range
|
||||
|
|
@ -83,38 +84,50 @@ private:
|
|||
if (!m_mgAssignps.empty()) {
|
||||
uint32_t items = m_mgIndexHi - m_mgIndexLo + 1;
|
||||
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) {
|
||||
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_statReItems += items;
|
||||
|
||||
// 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");
|
||||
FileLine* fl = bodyp->fileline();
|
||||
AstVar* itp = findCreateVarTemp(fl, m_mgCfuncp);
|
||||
FileLine* const fl = bodyp->fileline();
|
||||
AstVar* const itp = findCreateVarTemp(fl, m_mgCfuncp);
|
||||
|
||||
AstNode* initp = new AstAssign(fl, new AstVarRef(fl, itp, VAccess::WRITE),
|
||||
new AstConst(fl, m_mgIndexLo));
|
||||
AstNode* condp = new AstLte(fl, new AstVarRef(fl, itp, VAccess::READ),
|
||||
new AstConst(fl, m_mgIndexHi));
|
||||
AstNode* incp = new AstAssign(
|
||||
if (m_mgOffset > 0) {
|
||||
UASSERT_OBJ(m_mgIndexLo >= m_mgOffset, bodyp,
|
||||
"Reloop iteration starts at negative index");
|
||||
m_mgIndexLo -= m_mgOffset;
|
||||
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),
|
||||
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);
|
||||
bodyp->replaceWith(initp);
|
||||
whilep->addBodysp(bodyp);
|
||||
|
||||
// Replace constant index with new loop index
|
||||
AstNode* lbitp = m_mgSelLp->bitp();
|
||||
lbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
|
||||
AstNode* const offsetp
|
||||
= 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);
|
||||
if (m_mgSelRp) { // else constant and no replace
|
||||
AstNode* rbitp = m_mgSelRp->bitp();
|
||||
rbitp->replaceWith(new AstVarRef(fl, itp, VAccess::READ));
|
||||
AstNode* const rbitp = m_mgSelRp->bitp();
|
||||
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);
|
||||
}
|
||||
if (debug() >= 9) initp->dumpTree(cout, "-new: ");
|
||||
|
|
@ -133,6 +146,7 @@ private:
|
|||
m_mgSelRp = nullptr;
|
||||
m_mgVarrefLp = nullptr;
|
||||
m_mgVarrefRp = nullptr;
|
||||
m_mgOffset = 0;
|
||||
m_mgConstRp = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
@ -143,6 +157,7 @@ private:
|
|||
{
|
||||
m_cfuncp = nodep;
|
||||
iterateChildren(nodep);
|
||||
mergeEnd(); // Finish last pending merge, if any
|
||||
}
|
||||
}
|
||||
virtual void visit(AstNodeAssign* nodep) override {
|
||||
|
|
@ -155,7 +170,7 @@ private:
|
|||
return;
|
||||
}
|
||||
// Of a constant index
|
||||
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
|
||||
AstConst* const lbitp = VN_CAST(lselp->bitp(), Const);
|
||||
if (!lbitp) {
|
||||
mergeEnd();
|
||||
return;
|
||||
|
|
@ -164,53 +179,58 @@ private:
|
|||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
uint32_t index = lbitp->toUInt();
|
||||
const uint32_t lindex = lbitp->toUInt();
|
||||
// Of variable
|
||||
AstNodeVarRef* lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
||||
AstNodeVarRef* const lvarrefp = VN_CAST(lselp->fromp(), NodeVarRef);
|
||||
if (!lvarrefp) {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
// RHS is a constant or a select
|
||||
AstConst* rconstp = VN_CAST(nodep->rhsp(), Const);
|
||||
AstNodeSel* rselp = VN_CAST(nodep->rhsp(), NodeSel);
|
||||
AstConst* const rconstp = VN_CAST(nodep->rhsp(), Const);
|
||||
AstNodeSel* const rselp = VN_CAST(nodep->rhsp(), NodeSel);
|
||||
AstNodeVarRef* rvarrefp = nullptr;
|
||||
uint32_t rindex = lindex;
|
||||
if (rconstp) { // Ok
|
||||
} else {
|
||||
if (!rselp) {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
AstConst* rbitp = VN_CAST(rselp->bitp(), Const);
|
||||
} else if (rselp) {
|
||||
AstConst* const rbitp = VN_CAST(rselp->bitp(), Const);
|
||||
rvarrefp = VN_CAST(rselp->fromp(), NodeVarRef);
|
||||
if (!rbitp || rbitp->toUInt() != index || !rvarrefp
|
||||
|| lvarrefp->varp() == rvarrefp->varp()) {
|
||||
if (!rbitp || !rvarrefp || lvarrefp->varp() == rvarrefp->varp()) {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
rindex = rbitp->toUInt();
|
||||
} else {
|
||||
mergeEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_mgSelLp) { // Old merge
|
||||
if (m_mgCfuncp == m_cfuncp && m_mgNextp == nodep && m_mgSelLp->same(lselp)
|
||||
&& m_mgVarrefLp->same(lvarrefp)
|
||||
&& (m_mgConstRp
|
||||
? (rconstp && m_mgConstRp->same(rconstp))
|
||||
: (rselp && m_mgSelRp->same(rselp) && m_mgVarrefRp->same(rvarrefp)))
|
||||
&& (index == m_mgIndexLo - 1 || index == m_mgIndexHi + 1)) {
|
||||
if (m_mgCfuncp == m_cfuncp // In same function
|
||||
&& m_mgNextp == nodep // Consecutive node
|
||||
&& m_mgVarrefLp->same(lvarrefp) // Same array on left hand side
|
||||
&& (m_mgConstRp // On the right hand side either ...
|
||||
? (rconstp && m_mgConstRp->same(rconstp)) // ... same constant
|
||||
: (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
|
||||
if (index == m_mgIndexLo - 1) {
|
||||
m_mgIndexLo = index;
|
||||
} else if (index == m_mgIndexHi + 1) {
|
||||
m_mgIndexHi = index;
|
||||
if (lindex == m_mgIndexLo - 1) {
|
||||
m_mgIndexLo = lindex;
|
||||
} else if (lindex == m_mgIndexHi + 1) {
|
||||
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);
|
||||
m_mgAssignps.push_back(nodep);
|
||||
m_mgNextp = nodep->nextp();
|
||||
return;
|
||||
} 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
|
||||
mergeEnd();
|
||||
}
|
||||
|
|
@ -224,10 +244,11 @@ private:
|
|||
m_mgSelRp = rselp;
|
||||
m_mgVarrefLp = lvarrefp;
|
||||
m_mgVarrefRp = rvarrefp;
|
||||
m_mgOffset = static_cast<int64_t>(lindex) - static_cast<int64_t>(rindex);
|
||||
m_mgConstRp = rconstp;
|
||||
m_mgIndexLo = index;
|
||||
m_mgIndexHi = index;
|
||||
UINFO(9, "Start merge i=" << index << " " << nodep << endl);
|
||||
m_mgIndexLo = lindex;
|
||||
m_mgIndexHi = lindex;
|
||||
UINFO(9, "Start merge i=" << lindex << " o=" << m_mgOffset << nodep << endl);
|
||||
}
|
||||
//--------------------
|
||||
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