forked from verilator/verilator
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split up assignments to wides with Concat on the RHS
Add a new pass to split up (recursively): foo = {l, r}; into the following, with the right indices, iff the concatenation straddles a wide word boundary. foo[_:_] = r; foo[_:_] = l; This eliminates more wide temporaries. Another 23% speedup on VeeR EH2 high_perf. Also brings the predicted stack size from 8M to 40k.
- Loading branch information
Showing
10 changed files
with
248 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
// -*- mode: C++; c-file-style: "cc-mode" -*- | ||
//************************************************************************* | ||
// DESCRIPTION: Verilator: Generic optimizations on a per function basis | ||
// | ||
// Code available from: https://verilator.org | ||
// | ||
//************************************************************************* | ||
// | ||
// Copyright 2003-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 | ||
// | ||
//************************************************************************* | ||
// | ||
// - Split assignments to wide locations with Concat on the RHS | ||
// at word boundaries: | ||
// foo = {l, r}; | ||
// becomes (recursively): | ||
// foo[_:_] = r; | ||
// foo[_:_] = l; | ||
// | ||
//************************************************************************* | ||
|
||
#include "V3PchAstMT.h" | ||
|
||
#include "V3Funcopt.h" | ||
|
||
#include "V3Global.h" | ||
#include "V3Stats.h" | ||
#include "V3ThreadPool.h" | ||
|
||
VL_DEFINE_DEBUG_FUNCTIONS; | ||
|
||
class FuncoptVisitor final : public VNVisitor { | ||
// NODE STATE | ||
// AstNodeAssign::user() -> bool. Already checked, safe to split. Omit expensive check. | ||
|
||
// STATE | ||
AstCFunc* const m_funcp; // The function being processes | ||
|
||
VDouble0 m_concatSplits; // Number of splits in assignments with Concat on RHS | ||
|
||
static bool rhsReadsLhs(AstNodeAssign* nodep) { | ||
// It is expected that the number of vars written on the LHS is very small (should be 1). | ||
std::vector<AstVar*> writtenVarps; | ||
nodep->lhsp()->foreach([&](const AstVarRef* refp) { | ||
if (refp->access().isWriteOrRW()) writtenVarps.emplace_back(refp->varp()); | ||
}); | ||
if (writtenVarps.size() == 1) { | ||
// Common case | ||
return nodep->rhsp()->exists([&](const AstVarRef* refp) { // | ||
return refp->varp() == writtenVarps[0]; | ||
}); | ||
} else { | ||
// Generic case - TODO: should this ever be hit at this point? | ||
return nodep->rhsp()->exists([&](const AstVarRef* refp) { | ||
for (AstVar* const writtenVarp : writtenVarps) { | ||
if (refp->varp() == writtenVarp) return true; | ||
} | ||
return false; | ||
}); | ||
} | ||
} | ||
|
||
// METHODS | ||
// Split wide assignments with a wide concatenation on the RHS. | ||
// Returns true if 'nodep' was deleted | ||
bool splitConcat(AstNodeAssign* nodep) { | ||
// Only care about concatenations on the right | ||
AstConcat* const rhsp = VN_CAST(nodep->rhsp(), Concat); | ||
if (!rhsp) return false; | ||
// Will need the LHS | ||
AstNodeExpr* lhsp = nodep->lhsp(); | ||
UASSERT_OBJ(lhsp->width() == rhsp->width(), nodep, "Inconsistent assignment"); | ||
// Only consider pure assignments. Nodes inserted below are safe. | ||
if (!nodep->user1() && (!lhsp->isPure() || !rhsp->isPure())) return false; | ||
// Check for a Sel on the LHS if present, and skip over it | ||
uint32_t lsb = 0; | ||
if (AstSel* const selp = VN_CAST(lhsp, Sel)) { | ||
if (AstConst* const lsbp = VN_CAST(selp->lsbp(), Const)) { | ||
lhsp = selp->fromp(); | ||
lsb = lsbp->toUInt(); | ||
} else { | ||
// Don't optimize if it's a variable select | ||
return false; | ||
} | ||
} | ||
// No need to split assignments targeting storage smaller than a machine register | ||
if (lhsp->width() <= VL_QUADSIZE) return false; | ||
|
||
// If it's a concat straddling a word boundary, try to split it. | ||
// The next visit on the new nodes will split it recursively. | ||
// Otherwise, keep the original assignment. | ||
const int lsbWord = lsb / VL_EDATASIZE; | ||
const int msbWord = (lsb + rhsp->width() - 1) / VL_EDATASIZE; | ||
if (lsbWord == msbWord) return false; | ||
|
||
// If the RHS reads the LHS, we can't actually do this. Nodes inserted below are safe. | ||
if (!nodep->user1() && rhsReadsLhs(nodep)) return false; | ||
|
||
// Ok, actually split it now | ||
++m_concatSplits; | ||
// The 2 parts and their offsets | ||
AstNodeExpr* const rrp = rhsp->rhsp()->unlinkFrBack(); | ||
AstNodeExpr* const rlp = rhsp->lhsp()->unlinkFrBack(); | ||
const int rLsb = lsb; | ||
const int lLsb = lsb + rrp->width(); | ||
// Insert the 2 assignment right after the original. They will be visited next. | ||
AstAssign* const arp = new AstAssign{ | ||
nodep->fileline(), | ||
new AstSel{lhsp->fileline(), lhsp->cloneTreePure(false), rLsb, rrp->width()}, rrp}; | ||
AstAssign* const alp = new AstAssign{ | ||
nodep->fileline(), | ||
new AstSel{lhsp->fileline(), lhsp->unlinkFrBack(), lLsb, rlp->width()}, rlp}; | ||
nodep->addNextHere(arp); | ||
arp->addNextHere(alp); | ||
// Safe to split these. | ||
arp->user1(true); | ||
alp->user1(true); | ||
// Nuke what is left | ||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); | ||
return true; | ||
} | ||
|
||
// VISIT | ||
void visit(AstNodeAssign* nodep) override { | ||
// TODO: Only thing remaining inside functions should be AstAssign (that is, an actual | ||
// assignment statemant), but we stil use AstAssignW, AstAssignDly, and all, fix. | ||
if (v3Global.opt.fFuncSplitCat()) { | ||
if (splitConcat(nodep)) return; // Must return here, in case more code is added below | ||
} | ||
} | ||
|
||
void visit(AstNodeExpr*) override {} // No need to descend further (Ignore AstExprStmt...) | ||
|
||
void visit(AstNode* nodep) override { iterateChildren(nodep); } | ||
|
||
// CONSTRUCTORS | ||
explicit FuncoptVisitor(AstCFunc* funcp) | ||
: m_funcp{funcp} { | ||
iterateChildren(funcp); | ||
} | ||
~FuncoptVisitor() override { | ||
V3Stats::addStatSum("Optimizations, Funcopt concat splits", m_concatSplits); | ||
} | ||
|
||
public: | ||
static void apply(AstCFunc* funcp) { FuncoptVisitor{funcp}; } | ||
}; | ||
|
||
//###################################################################### | ||
|
||
void V3Funcopt::funcoptAll(AstNetlist* nodep) { | ||
UINFO(2, __FUNCTION__ << ": " << endl); | ||
{ | ||
const VNUser1InUse user1InUse; | ||
V3ThreadScope threadScope; | ||
for (AstNodeModule *modp = nodep->modulesp(), *nextModp; modp; modp = nextModp) { | ||
nextModp = VN_AS(modp->nextp(), NodeModule); | ||
for (AstNode *nodep = modp->stmtsp(), *nextNodep; nodep; nodep = nextNodep) { | ||
nextNodep = nodep->nextp(); | ||
if (AstCFunc* const cfuncp = VN_CAST(nodep, CFunc)) { | ||
threadScope.enqueue([cfuncp]() { FuncoptVisitor::apply(cfuncp); }); | ||
} | ||
} | ||
} | ||
} | ||
V3Global::dumpCheckGlobalTree("funcopt", 0, dumpTreeEitherLevel() >= 3); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// -*- mode: C++; c-file-style: "cc-mode" -*- | ||
//************************************************************************* | ||
// DESCRIPTION: Verilator: Generic optimizations on a per function basis | ||
// | ||
// Code available from: https://verilator.org | ||
// | ||
//************************************************************************* | ||
// | ||
// Copyright 2003-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 | ||
// | ||
//************************************************************************* | ||
|
||
#ifndef VERILATOR_V3FUNCOPT_H_ | ||
#define VERILATOR_V3FUNCOPT_H_ | ||
|
||
#include "config_build.h" | ||
#include "verilatedos.h" | ||
|
||
class AstNetlist; | ||
|
||
//============================================================================ | ||
|
||
class V3Funcopt final { | ||
public: | ||
static void funcoptAll(AstNetlist* nodep); | ||
}; | ||
|
||
#endif // Guard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
#!/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('vlt') | ||
|
||
test.top_filename = "t/t_dfg_balance_cats.v" | ||
|
||
test.compile(verilator_flags2=["--stats", "-fno-func-split-cat"]) | ||
|
||
test.file_grep(test.stats, | ||
r' Optimizations, DFG pre inline BalanceTrees, concat trees balanced\s+(\d+)', 0) | ||
test.file_grep(test.stats, | ||
r' Optimizations, DFG post inline BalanceTrees, concat trees balanced\s+(\d+)', 1) | ||
test.file_grep(test.stats, r'Optimizations, DFG pre inline Dfg2Ast, result equations\s+(\d+)', 1) | ||
test.file_grep(test.stats, r'Optimizations, DFG post inline Dfg2Ast, result equations\s+(\d+)', 1) | ||
test.file_grep_not(test.stats, r'Optimizations, Funcopt concat splits') | ||
|
||
test.passes() |