Skip to content

Commit

Permalink
Improve Reloop to accept constant index offsets. (verilator#2939)
Browse files Browse the repository at this point in the history
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];
  • Loading branch information
gezalore committed May 10, 2021
1 parent 45fbd98 commit 267c6f6
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 47 deletions.
115 changes: 68 additions & 47 deletions src/V3Reloop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
//
Expand Down Expand Up @@ -61,6 +61,7 @@ class ReloopVisitor final : public AstNVisitor {
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
Expand All @@ -83,38 +84,50 @@ class ReloopVisitor final : public AstNVisitor {
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);

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(
FileLine* const fl = bodyp->fileline();
AstVar* const itp = findCreateVarTemp(fl, m_mgCfuncp);

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: ");
Expand All @@ -133,6 +146,7 @@ class ReloopVisitor final : public AstNVisitor {
m_mgSelRp = nullptr;
m_mgVarrefLp = nullptr;
m_mgVarrefRp = nullptr;
m_mgOffset = 0;
m_mgConstRp = nullptr;
}
}
Expand All @@ -143,6 +157,7 @@ class ReloopVisitor final : public AstNVisitor {
{
m_cfuncp = nodep;
iterateChildren(nodep);
mergeEnd(); // Finish last pending merge, if any
}
}
virtual void visit(AstNodeAssign* nodep) override {
Expand All @@ -155,7 +170,7 @@ class ReloopVisitor final : public AstNVisitor {
return;
}
// Of a constant index
AstConst* lbitp = VN_CAST(lselp->bitp(), Const);
AstConst* const lbitp = VN_CAST(lselp->bitp(), Const);
if (!lbitp) {
mergeEnd();
return;
Expand All @@ -164,53 +179,58 @@ class ReloopVisitor final : public AstNVisitor {
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();
}
Expand All @@ -224,10 +244,11 @@ class ReloopVisitor final : public AstNVisitor {
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
Expand Down
17 changes: 17 additions & 0 deletions test_regress/t/t_reloop_offset.out
Original file line number Diff line number Diff line change
@@ -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 *-*
33 changes: 33 additions & 0 deletions test_regress/t/t_reloop_offset.pl
Original file line number Diff line number Diff line change
@@ -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;
50 changes: 50 additions & 0 deletions test_regress/t/t_reloop_offset.v
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 267c6f6

Please sign in to comment.