Speed up Verilator itself by prefetching pointer-chase
This commit is contained in:
parent
8b77379e2c
commit
c595d67311
|
|
@ -680,11 +680,38 @@ void AstNode::operator delete(void* objp, size_t size) {
|
||||||
// Iterators
|
// Iterators
|
||||||
|
|
||||||
void AstNode::iterateChildren(AstNVisitor& v, AstNUser* vup) {
|
void AstNode::iterateChildren(AstNVisitor& v, AstNUser* vup) {
|
||||||
|
// This is a very hot function
|
||||||
if (!this) return;
|
if (!this) return;
|
||||||
m_op1p->iterateAndNext(v, vup);
|
ASTNODE_PREFETCH(m_op1p);
|
||||||
m_op2p->iterateAndNext(v, vup);
|
ASTNODE_PREFETCH(m_op2p);
|
||||||
m_op3p->iterateAndNext(v, vup);
|
ASTNODE_PREFETCH(m_op3p);
|
||||||
m_op4p->iterateAndNext(v, vup);
|
ASTNODE_PREFETCH(m_op4p);
|
||||||
|
// if () not needed since iterateAndNext accepts null this, but faster with it.
|
||||||
|
if (m_op1p) m_op1p->iterateAndNext(v, vup);
|
||||||
|
if (m_op2p) m_op2p->iterateAndNext(v, vup);
|
||||||
|
if (m_op3p) m_op3p->iterateAndNext(v, vup);
|
||||||
|
if (m_op4p) m_op4p->iterateAndNext(v, vup);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AstNode::iterateAndNext(AstNVisitor& v, AstNUser* vup) {
|
||||||
|
// This is a very hot function
|
||||||
|
// IMPORTANT: If you replace a node that's the target of this iterator,
|
||||||
|
// then the NEW node will be iterated on next, it isn't skipped!
|
||||||
|
// if (!this) return; // Part of for()
|
||||||
|
for (AstNode* nodep=this; nodep;) {
|
||||||
|
AstNode* niterp = nodep;
|
||||||
|
ASTNODE_PREFETCH(nodep->m_nextp);
|
||||||
|
niterp->m_iterpp = &niterp;
|
||||||
|
niterp->accept(v, vup);
|
||||||
|
// accept may do a replaceNode and change niterp on us...
|
||||||
|
if (!niterp) return;
|
||||||
|
niterp->m_iterpp = NULL;
|
||||||
|
if (niterp!=nodep) { // Edited it
|
||||||
|
nodep = niterp;
|
||||||
|
} else { // Same node, just loop
|
||||||
|
nodep = niterp->m_nextp;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstNode::iterateListBackwards(AstNVisitor& v, AstNUser* vup) {
|
void AstNode::iterateListBackwards(AstNVisitor& v, AstNUser* vup) {
|
||||||
|
|
@ -707,25 +734,6 @@ void AstNode::iterateChildrenBackwards(AstNVisitor& v, AstNUser* vup) {
|
||||||
this->op4p()->iterateListBackwards(v,vup);
|
this->op4p()->iterateListBackwards(v,vup);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AstNode::iterateAndNext(AstNVisitor& v, AstNUser* vup) {
|
|
||||||
// IMPORTANT: If you replace a node that's the target of this iterator,
|
|
||||||
// then the NEW node will be iterated on next, it isn't skipped!
|
|
||||||
// if (!this) return; // Part of for()
|
|
||||||
for (AstNode* nodep=this; nodep;) {
|
|
||||||
AstNode* niterp = nodep;
|
|
||||||
niterp->m_iterpp = &niterp;
|
|
||||||
niterp->accept(v, vup);
|
|
||||||
// accept may do a replaceNode and change niterp on us...
|
|
||||||
if (!niterp) return;
|
|
||||||
niterp->m_iterpp = NULL;
|
|
||||||
if (niterp!=nodep) { // Edited it
|
|
||||||
nodep = niterp;
|
|
||||||
} else { // Same node, just loop
|
|
||||||
nodep = niterp->m_nextp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AstNode::iterateAndNextIgnoreEdit(AstNVisitor& v, AstNUser* vup) {
|
void AstNode::iterateAndNextIgnoreEdit(AstNVisitor& v, AstNUser* vup) {
|
||||||
// Keep following the current list even if edits change it
|
// Keep following the current list even if edits change it
|
||||||
if (!this) return;
|
if (!this) return;
|
||||||
|
|
@ -796,6 +804,11 @@ void AstNode::checkTreeIter(AstNode* backp) {
|
||||||
if (backp != this->backp()) {
|
if (backp != this->backp()) {
|
||||||
this->v3fatalSrc("Back node inconsistent");
|
this->v3fatalSrc("Back node inconsistent");
|
||||||
}
|
}
|
||||||
|
if (castNodeTermop()) {
|
||||||
|
// Termops have a short-circuited iterateChildren, so check usage
|
||||||
|
if (op1p()||op2p()||op3p()||op4p())
|
||||||
|
this->v3fatalSrc("Terminal operation with non-terminals");
|
||||||
|
}
|
||||||
if (op1p()) op1p()->checkTreeIterList(this);
|
if (op1p()) op1p()->checkTreeIterList(this);
|
||||||
if (op2p()) op2p()->checkTreeIterList(this);
|
if (op2p()) op2p()->checkTreeIterList(this);
|
||||||
if (op3p()) op3p()->checkTreeIterList(this);
|
if (op3p()) op3p()->checkTreeIterList(this);
|
||||||
|
|
|
||||||
11
src/V3Ast.h
11
src/V3Ast.h
|
|
@ -541,16 +541,23 @@ ostream& operator<<(ostream& os, V3Hash rhs);
|
||||||
//######################################################################
|
//######################################################################
|
||||||
// AstNode -- Base type of all Ast types
|
// AstNode -- Base type of all Ast types
|
||||||
|
|
||||||
|
// Prefetch a node.
|
||||||
|
// The if() makes it faster, even though prefetch won't fault on null pointers
|
||||||
|
#define ASTNODE_PREFETCH(nodep) \
|
||||||
|
{ if (nodep) { VL_PREFETCH_RD(&(nodep->m_nextp)); VL_PREFETCH_RD(&(nodep->m_iterpp)); }}
|
||||||
|
|
||||||
class AstNode {
|
class AstNode {
|
||||||
private:
|
// v ASTNODE_PREFETCH depends on below ordering of members
|
||||||
AstNode* m_nextp; // Next peer in the parent's list
|
AstNode* m_nextp; // Next peer in the parent's list
|
||||||
AstNode* m_backp; // Node that points to this one (via next/op1/op2/...)
|
AstNode* m_backp; // Node that points to this one (via next/op1/op2/...)
|
||||||
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
|
|
||||||
AstNode* m_op1p; // Generic pointer 1
|
AstNode* m_op1p; // Generic pointer 1
|
||||||
AstNode* m_op2p; // Generic pointer 2
|
AstNode* m_op2p; // Generic pointer 2
|
||||||
AstNode* m_op3p; // Generic pointer 3
|
AstNode* m_op3p; // Generic pointer 3
|
||||||
AstNode* m_op4p; // Generic pointer 4
|
AstNode* m_op4p; // Generic pointer 4
|
||||||
AstNode** m_iterpp; // Pointer to node iterating on, change it if we replace this node.
|
AstNode** m_iterpp; // Pointer to node iterating on, change it if we replace this node.
|
||||||
|
// ^ ASTNODE_PREFETCH depends on above ordering of members
|
||||||
|
|
||||||
|
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
|
||||||
|
|
||||||
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
|
AstNode* m_clonep; // Pointer to clone of/ source of this module (for *LAST* cloneTree() ONLY)
|
||||||
int m_cloneCnt; // Mark of when userp was set
|
int m_cloneCnt; // Mark of when userp was set
|
||||||
|
|
|
||||||
|
|
@ -421,7 +421,7 @@ void GateVisitor::optimizeSignals(bool allowMultiIn) {
|
||||||
if (!vvertexp->isTop() // Ok if top inputs are driverless
|
if (!vvertexp->isTop() // Ok if top inputs are driverless
|
||||||
&& !vvertexp->varScp()->varp()->initp()
|
&& !vvertexp->varScp()->varp()->initp()
|
||||||
&& !vvertexp->varScp()->varp()->isSigPublic()) {
|
&& !vvertexp->varScp()->varp()->isSigPublic()) {
|
||||||
UINFO(1, "No drivers "<<vvertexp->varScp()<<endl);
|
UINFO(4, "No drivers "<<vvertexp->varScp()<<endl);
|
||||||
if (0) {
|
if (0) {
|
||||||
// If we warned here after constant propagation, what the user considered
|
// If we warned here after constant propagation, what the user considered
|
||||||
// reasonable logic may have disappeared. Issuing a warning would
|
// reasonable logic may have disappeared. Issuing a warning would
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue