Optimize Dfg with relaxed live variable analysis
Relax the live variable analysis performed by Dfg to bail on fewer cases. This analysis was already conservative (meaning it might think variables are live when they are not), which is good enough for Dfg use. This change in particular enables synthesizing more complex logic involving arrays, e.g. those introduce by V3Table creating lookup tables.
This commit is contained in:
parent
de0236be2f
commit
c76dc5bf24
|
|
@ -987,6 +987,7 @@ public:
|
||||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||||
void cloneRelink() override { V3ERROR_NA; } // Not cloneable
|
void cloneRelink() override { V3ERROR_NA; } // Not cloneable
|
||||||
AstModule* modp() const { return m_modp; }
|
AstModule* modp() const { return m_modp; }
|
||||||
|
AstScope* scopep() const { return m_scopep; }
|
||||||
|
|
||||||
// Find a table (unpacked array) within the constant pool which is initialized with the
|
// Find a table (unpacked array) within the constant pool which is initialized with the
|
||||||
// given value, or create one if one does not already exists. The returned VarScope *might*
|
// given value, or create one if one does not already exists. The returned VarScope *might*
|
||||||
|
|
|
||||||
|
|
@ -51,28 +51,18 @@ class CfgLiveVariables final : VNVisitorConst {
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
// This is to match DFG, but can be extended independently - eventually should handle all
|
static bool isPacked(const AstVarScope* vscp) {
|
||||||
static bool isSupportedPackedDType(const AstNodeDType* dtypep) {
|
AstNodeDType* const dtypep = vscp->dtypep()->skipRefp();
|
||||||
dtypep = dtypep->skipRefp();
|
|
||||||
if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) {
|
if (const AstBasicDType* const typep = VN_CAST(dtypep, BasicDType)) {
|
||||||
return typep->keyword().isIntNumeric();
|
return typep->keyword().isIntNumeric();
|
||||||
}
|
}
|
||||||
if (const AstPackArrayDType* const typep = VN_CAST(dtypep, PackArrayDType)) {
|
if (VN_IS(dtypep, PackArrayDType)) return true;
|
||||||
return isSupportedPackedDType(typep->subDTypep());
|
|
||||||
}
|
|
||||||
if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
if (const AstNodeUOrStructDType* const typep = VN_CAST(dtypep, NodeUOrStructDType)) {
|
||||||
return typep->packed();
|
return typep->packed();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check and return if variable is incompatible
|
|
||||||
bool incompatible(AstVarScope* vscp) {
|
|
||||||
if (!isSupportedPackedDType(vscp->dtypep())) return true;
|
|
||||||
if (vscp->varp()->ignoreSchedWrite()) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateGen(AstNode* nodep) {
|
void updateGen(AstNode* nodep) {
|
||||||
UASSERT_OBJ(!m_abort, nodep, "Doing useless work");
|
UASSERT_OBJ(!m_abort, nodep, "Doing useless work");
|
||||||
m_abort = nodep->exists([&](const AstVarRef* refp) {
|
m_abort = nodep->exists([&](const AstVarRef* refp) {
|
||||||
|
|
@ -80,8 +70,6 @@ class CfgLiveVariables final : VNVisitorConst {
|
||||||
if (refp->access().isWriteOnly()) return false;
|
if (refp->access().isWriteOnly()) return false;
|
||||||
// Grab referenced variable
|
// Grab referenced variable
|
||||||
AstVarScope* const vscp = refp->varScopep();
|
AstVarScope* const vscp = refp->varScopep();
|
||||||
// Bail if not a compatible type
|
|
||||||
if (incompatible(vscp)) return true;
|
|
||||||
// Add to gen set, if not killed - assume whole variable read
|
// Add to gen set, if not killed - assume whole variable read
|
||||||
if (m_currp->m_kill.count(vscp)) return false;
|
if (m_currp->m_kill.count(vscp)) return false;
|
||||||
m_currp->m_gen.emplace(vscp);
|
m_currp->m_gen.emplace(vscp);
|
||||||
|
|
@ -96,11 +84,13 @@ class CfgLiveVariables final : VNVisitorConst {
|
||||||
if (refp->access().isReadOnly()) return false;
|
if (refp->access().isReadOnly()) return false;
|
||||||
// Grab referenced variable
|
// Grab referenced variable
|
||||||
AstVarScope* const vscp = refp->varScopep();
|
AstVarScope* const vscp = refp->varScopep();
|
||||||
// Bail if not a compatible type
|
// Safety case: bail on variables with unusual semantics
|
||||||
if (incompatible(vscp)) return true;
|
if (vscp->varp()->ignoreSchedWrite()) return true;
|
||||||
// If whole written, add to kill set
|
// If not a packed variable, assume it's not a whole update, don't add to kill set
|
||||||
if (refp->nextp()) return false;
|
if (!isPacked(vscp)) return false;
|
||||||
if (VN_IS(refp->abovep(), Sel)) return false;
|
// If packed, and updated partially, don't add to kill set
|
||||||
|
if (refp->backp()->nextp() != refp && VN_IS(refp->abovep(), Sel)) return false;
|
||||||
|
// Whole variable is writen, add to kill set
|
||||||
m_currp->m_kill.emplace(vscp);
|
m_currp->m_kill.emplace(vscp);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
@ -198,7 +188,7 @@ public:
|
||||||
CfgLiveVariables analysis{cfg};
|
CfgLiveVariables analysis{cfg};
|
||||||
// If failed, return nullptr
|
// If failed, return nullptr
|
||||||
if (analysis.m_abort) return nullptr;
|
if (analysis.m_abort) return nullptr;
|
||||||
// Gather variables live in to the entry blockstd::unique_ptr<std::vector<AstVarScope*>>
|
// Gather variables live in to the entry block
|
||||||
const std::unordered_set<AstVarScope*>& lin = analysis.m_blockState[cfg.enter()].m_liveIn;
|
const std::unordered_set<AstVarScope*>& lin = analysis.m_blockState[cfg.enter()].m_liveIn;
|
||||||
std::vector<AstVarScope*>* const resultp
|
std::vector<AstVarScope*>* const resultp
|
||||||
= new std::vector<AstVarScope*>{lin.begin(), lin.end()};
|
= new std::vector<AstVarScope*>{lin.begin(), lin.end()};
|
||||||
|
|
|
||||||
|
|
@ -906,17 +906,20 @@ AstScope* DfgVertex::scopep(ScopeCache& cache, bool tryResultVar) VL_MT_DISABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
|
||||||
|
AstScope* const constPoolp = v3Global.rootp()->constPoolp()->scopep();
|
||||||
|
|
||||||
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
|
// Note: the recursive invocation can cause a re-hash but that will not invalidate references
|
||||||
AstScope*& resultr = cache[this];
|
AstScope*& resultr = cache[this];
|
||||||
if (!resultr) {
|
if (!resultr) {
|
||||||
// Mark to prevent infinite recursion on circular graphs - should never be called on such
|
// Mark to prevent infinite recursion on circular graphs - should never be called on such
|
||||||
resultr = reinterpret_cast<AstScope*>(1);
|
resultr = reinterpret_cast<AstScope*>(1);
|
||||||
// Find scope based on sources, falling back on the root scope
|
// Find scope based on sources, falling back on the root scope,
|
||||||
AstScope* const rootp = v3Global.rootp()->topScopep()->scopep();
|
// also make sure it's not the constant pool scope, which is special.
|
||||||
AstScope* foundp = nullptr;
|
AstScope* foundp = nullptr;
|
||||||
foreachSource([&](DfgVertex& src) {
|
foreachSource([&](DfgVertex& src) {
|
||||||
AstScope* const scp = src.scopep(cache, true);
|
AstScope* const scp = src.scopep(cache, true);
|
||||||
if (scp != rootp) {
|
if (scp != rootp && scp != constPoolp) {
|
||||||
foundp = scp;
|
foundp = scp;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ import vltest_bootstrap
|
||||||
|
|
||||||
test.scenarios('simulator')
|
test.scenarios('simulator')
|
||||||
|
|
||||||
test.compile(verilator_flags2=["--stats"])
|
# This tests combining CFuncs, but Dfg would inline the submodule, disabling
|
||||||
|
test.compile(verilator_flags2=["--stats", "-fno-dfg"])
|
||||||
|
|
||||||
test.execute()
|
test.execute()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -577,4 +577,13 @@ module t (
|
||||||
end
|
end
|
||||||
`signal(VIA_CRESET, via_creset);
|
`signal(VIA_CRESET, via_creset);
|
||||||
|
|
||||||
|
logic [1:0] LUT [2] = '{0: 2'b11, 1: 2'b10};
|
||||||
|
logic [1:0] array_read;
|
||||||
|
always_comb begin
|
||||||
|
array_read = 2'd0;
|
||||||
|
array_read += LUT[rand_a[0]];
|
||||||
|
array_read += LUT[rand_a[1]];
|
||||||
|
end
|
||||||
|
`signal(ARRAY_READ, array_read);
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
||||||
|
|
||||||
test.scenarios('vlt')
|
test.scenarios('vlt')
|
||||||
|
|
||||||
test.lint(verilator_flags2=['--stats', '--expand-limit 5'])
|
test.lint(verilator_flags2=['--stats', '--expand-limit 5', '-fno-dfg'])
|
||||||
|
|
||||||
test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 0)
|
test.file_grep(test.stats, r'Optimizations, Gate excluded wide expressions\s+(\d+)', 0)
|
||||||
test.file_grep(test.stats, r'Optimizations, Gate sigs deleted\s+(\d+)', 1)
|
test.file_grep(test.stats, r'Optimizations, Gate sigs deleted\s+(\d+)', 1)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue