Internals: Further performance improvement of AstNode type tests, #2138. No functional change intended.

Replace the virtual type() method on AstNode with a non-virtual, inlined
accessor to a const member variable m_type.  This means that in order to be
able to use this for type testing, it needs to be initialized based on the
final type of the node. This is achieved by passing the relevant AstType
value back through the constructor call chain. Most of the boilerplate
involved is auto generated by first feeding V3AstNodes.h through astgen to
get V3AstNodes__gen.h, which is then included in V3Ast.h. No client code
needs to be aware and there is no functional change intended.

Eliminating the virtual function call to fetch the node type identifier
results in measured compilation speed improvement of 5-10% as it
eliminates up to 20% of all mispredicted branches from the execution.
This commit is contained in:
Geza Lore 2020-01-25 15:29:44 -05:00 committed by Wilson Snyder
parent eafed88a6e
commit ef5250f0ca
5 changed files with 680 additions and 594 deletions

View File

@ -9,6 +9,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
** Add -match to lint_off to waive warnings. [Philipp Wagner]
*** Verilation speed improvements, #2133, #2138. [Geza Lore]
*** Support libgoogle-perftools-dev's libtcmalloc if available. #2137. [Geza Lore]
*** Support $readmem/$writemem with assoc arrarys. Closes #2100. [agrobman]

View File

@ -1159,6 +1159,8 @@ class AstNode {
AstNode* m_headtailp; // When at begin/end of list, the opposite end of the list
const AstType m_type; // Node sub-type identifier
FileLine* m_fileline; // Where it was declared
vluint64_t m_editCount; // When it was last edited
static vluint64_t s_editCntGbl; // Global edit counter
@ -1213,8 +1215,10 @@ public:
protected:
// CONSTRUCTORS
AstNode() { init(); }
explicit AstNode(FileLine* fileline) {init(); m_fileline = fileline; }
AstNode(AstType t)
: m_type(t) { init(); }
AstNode(AstType t, FileLine* fl)
: m_type(t) { init(); m_fileline = fl; }
virtual AstNode* clone() = 0; // Generally, cloneTree is what you want instead
virtual void cloneRelink() {}
void cloneRelinkTree();
@ -1245,7 +1249,7 @@ protected:
public:
// ACCESSORS
virtual AstType type() const = 0;
inline AstType type() const { return m_type; }
const char* typeName() const { return type().ascii(); } // See also prettyTypeName
AstNode* nextp() const { return m_nextp; }
AstNode* backp() const { return m_backp; }
@ -1570,8 +1574,8 @@ inline void AstNRelinker::relink(AstNode* newp) { newp->AstNode::relink(this); }
class AstNodeMath : public AstNode {
// Math -- anything that's part of an expression tree
public:
explicit AstNodeMath(FileLine* fl)
: AstNode(fl) {}
AstNodeMath(AstType t, FileLine* fl)
: AstNode(t, fl) {}
ASTNODE_BASE_FUNCS(NodeMath)
// METHODS
virtual bool hasDType() const { return true; }
@ -1587,8 +1591,8 @@ public:
class AstNodeTermop : public AstNodeMath {
// Terminal operator -- a operator with no "inputs"
public:
explicit AstNodeTermop(FileLine* fl)
: AstNodeMath(fl) {}
AstNodeTermop(AstType t, FileLine* fl)
: AstNodeMath(t, fl) {}
ASTNODE_BASE_FUNCS(NodeTermop)
// Know no children, and hot function, so skip iterator for speed
// See checkTreeIter also that asserts no children
@ -1599,8 +1603,8 @@ public:
class AstNodeUniop : public AstNodeMath {
// Unary math
public:
AstNodeUniop(FileLine* fl, AstNode* lhsp)
: AstNodeMath(fl) {
AstNodeUniop(AstType t, FileLine* fl, AstNode* lhsp)
: AstNodeMath(t, fl) {
dtypeFrom(lhsp);
setOp1p(lhsp); }
ASTNODE_BASE_FUNCS(NodeUniop)
@ -1621,8 +1625,8 @@ public:
class AstNodeBiop : public AstNodeMath {
// Binary math
public:
AstNodeBiop(FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeMath(fl) {
AstNodeBiop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeMath(t, fl) {
setOp1p(lhs); setOp2p(rhs); }
ASTNODE_BASE_FUNCS(NodeBiop)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) = 0; // Clone single node, just get same type back.
@ -1648,8 +1652,8 @@ public:
class AstNodeTriop : public AstNodeMath {
// Trinary math
public:
AstNodeTriop(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
: AstNodeMath(fl) {
AstNodeTriop(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
: AstNodeMath(t, fl) {
setOp1p(lhs); setOp2p(rhs); setOp3p(ths); }
ASTNODE_BASE_FUNCS(NodeTriop)
AstNode* lhsp() const { return op1p(); }
@ -1676,22 +1680,22 @@ public:
class AstNodeBiCom : public AstNodeBiop {
// Binary math with commutative properties
public:
AstNodeBiCom(FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiop(fl, lhs, rhs) {}
AstNodeBiCom(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiop(t, fl, lhs, rhs) {}
ASTNODE_BASE_FUNCS(NodeBiCom)
};
class AstNodeBiComAsv : public AstNodeBiCom {
// Binary math with commutative & associative properties
public:
AstNodeBiComAsv(FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiCom(fl, lhs, rhs) {}
AstNodeBiComAsv(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs)
: AstNodeBiCom(t, fl, lhs, rhs) {}
ASTNODE_BASE_FUNCS(NodeBiComAsv)
};
class AstNodeCond : public AstNodeTriop {
public:
AstNodeCond(FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p)
: AstNodeTriop(fl, condp, expr1p, expr2p) {
AstNodeCond(AstType t, FileLine* fl, AstNode* condp, AstNode* expr1p, AstNode* expr2p)
: AstNodeTriop(t, fl, condp, expr1p, expr2p) {
if (expr1p) dtypeFrom(expr1p);
else if (expr2p) dtypeFrom(expr2p);
}
@ -1717,8 +1721,8 @@ public:
class AstNodePreSel : public AstNode {
// Something that becomes an AstSel
public:
AstNodePreSel(FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
: AstNode(fl) {
AstNodePreSel(AstType t, FileLine* fl, AstNode* lhs, AstNode* rhs, AstNode* ths)
: AstNode(t, fl) {
setOp1p(lhs); setOp2p(rhs); setNOp3p(ths); }
ASTNODE_BASE_FUNCS(NodePreSel)
AstNode* lhsp() const { return op1p(); }
@ -1739,8 +1743,8 @@ class AstNodeStmt : public AstNode {
// Statement -- anything that's directly under a function
bool m_statement; // Really a statement (e.g. not a function with return)
public:
explicit AstNodeStmt(FileLine* fl, bool statement = true)
: AstNode(fl)
AstNodeStmt(AstType t, FileLine* fl, bool statement = true)
: AstNode(t, fl)
, m_statement(statement) {}
ASTNODE_BASE_FUNCS(NodeStmt)
// METHODS
@ -1752,8 +1756,8 @@ public:
class AstNodeAssign : public AstNodeStmt {
public:
AstNodeAssign(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: AstNodeStmt(fl) {
AstNodeAssign(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: AstNodeStmt(t, fl) {
setOp1p(rhsp); setOp2p(lhsp);
dtypeFrom(lhsp);
}
@ -1775,9 +1779,9 @@ public:
class AstNodeFor : public AstNodeStmt {
public:
AstNodeFor(FileLine* fileline, AstNode* initsp, AstNode* condp,
AstNodeFor(AstType t, FileLine* fl, AstNode* initsp, AstNode* condp,
AstNode* incsp, AstNode* bodysp)
: AstNodeStmt(fileline) {
: AstNodeStmt(t, fl) {
addNOp1p(initsp); setOp2p(condp); addNOp3p(incsp); addNOp4p(bodysp);
}
ASTNODE_BASE_FUNCS(NodeFor)
@ -1795,8 +1799,8 @@ class AstNodeIf : public AstNodeStmt {
private:
VBranchPred m_branchPred; // Branch prediction as taken/untaken?
public:
AstNodeIf(FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
: AstNodeStmt(fl) {
AstNodeIf(AstType t, FileLine* fl, AstNode* condp, AstNode* ifsp, AstNode* elsesp)
: AstNodeStmt(t, fl) {
setOp1p(condp); addNOp2p(ifsp); addNOp3p(elsesp);
}
ASTNODE_BASE_FUNCS(NodeIf)
@ -1817,8 +1821,8 @@ public:
class AstNodeCase : public AstNodeStmt {
public:
AstNodeCase(FileLine* fl, AstNode* exprp, AstNode* casesp)
: AstNodeStmt(fl) {
AstNodeCase(AstType t, FileLine* fl, AstNode* exprp, AstNode* casesp)
: AstNodeStmt(t, fl) {
setOp1p(exprp); addNOp2p(casesp);
}
ASTNODE_BASE_FUNCS(NodeCase)
@ -1833,7 +1837,8 @@ public:
class AstNodeSenItem : public AstNode {
// An AstSenItem or AstSenGate
public:
explicit AstNodeSenItem(FileLine* fl) : AstNode(fl) {}
AstNodeSenItem(AstType t, FileLine* fl)
: AstNode(t, fl) {}
ASTNODE_BASE_FUNCS(NodeSenItem)
virtual bool isClocked() const = 0;
virtual bool isCombo() const = 0;
@ -1854,14 +1859,14 @@ private:
bool m_hierThis; // Hiername points to "this" function
void init();
public:
AstNodeVarRef(FileLine* fl, const string& name, bool lvalue)
: AstNodeMath(fl), m_lvalue(lvalue), m_varp(NULL), m_varScopep(NULL),
m_packagep(NULL), m_name(name), m_hierThis(false) {
AstNodeVarRef(AstType t, FileLine* fl, const string& name, bool lvalue)
: AstNodeMath(t, fl), m_lvalue(lvalue), m_varp(NULL), m_varScopep(NULL)
, m_packagep(NULL), m_name(name), m_hierThis(false) {
init();
}
AstNodeVarRef(FileLine* fl, const string& name, AstVar* varp, bool lvalue)
: AstNodeMath(fl), m_lvalue(lvalue), m_varp(varp), m_varScopep(NULL),
m_packagep(NULL), m_name(name), m_hierThis(false) {
AstNodeVarRef(AstType t, FileLine* fl, const string& name, AstVar* varp, bool lvalue)
: AstNodeMath(t, fl), m_lvalue(lvalue), m_varp(varp), m_varScopep(NULL)
, m_packagep(NULL), m_name(name), m_hierThis(false) {
// May have varp==NULL
init();
}
@ -1896,8 +1901,8 @@ private:
string m_text;
public:
// Node that simply puts text into the output stream
AstNodeText(FileLine* fileline, const string& textp)
: AstNode(fileline) {
AstNodeText(AstType t, FileLine* fl, const string& textp)
: AstNode(t, fl) {
m_text = textp; // Copy it
}
ASTNODE_BASE_FUNCS(NodeText)
@ -1922,7 +1927,8 @@ private:
static int s_uniqueNum; // Unique number assigned to each dtype during creation for IEEE matching
public:
// CONSTRUCTORS
explicit AstNodeDType(FileLine* fl) : AstNode(fl) {
AstNodeDType(AstType t, FileLine* fl)
: AstNode(t, fl) {
m_width = 0; m_widthMin = 0; m_generic = false;
}
ASTNODE_BASE_FUNCS(NodeDType)
@ -1982,8 +1988,8 @@ private:
bool m_isFourstate;
MemberNameMap m_members;
public:
AstNodeUOrStructDType(FileLine* fl, AstNumeric numericUnpack)
: AstNodeDType(fl) {
AstNodeUOrStructDType(AstType t, FileLine* fl, AstNumeric numericUnpack)
: AstNodeDType(t, fl) {
// AstNumeric::NOSIGN overloaded to indicate not packed
m_packed = (numericUnpack != AstNumeric::NOSIGN);
m_isFourstate = false; // V3Width computes
@ -2033,7 +2039,8 @@ private:
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
AstNode* rangenp() const { return op2p(); } // op2 = Array(s) of variable
public:
explicit AstNodeArrayDType(FileLine* fl) : AstNodeDType(fl) {
AstNodeArrayDType(AstType t, FileLine* fl)
: AstNodeDType(t, fl) {
m_refDTypep = NULL;
}
ASTNODE_BASE_FUNCS(NodeArrayDType)
@ -2084,8 +2091,8 @@ public:
class AstNodeSel : public AstNodeBiop {
// Single bit range extraction, perhaps with non-constant selection or array selection
public:
AstNodeSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
: AstNodeBiop(fl, fromp, bitp) {}
AstNodeSel(AstType t, FileLine* fl, AstNode* fromp, AstNode* bitp)
: AstNodeBiop(t, fl, fromp, bitp) {}
ASTNODE_BASE_FUNCS(NodeSel)
AstNode* fromp() const { return op1p(); } // op1 = Extracting what (NULL=TBD during parsing)
void fromp(AstNode* nodep) { setOp1p(nodep); }
@ -2098,7 +2105,8 @@ public:
class AstNodeStream : public AstNodeBiop {
// Verilog {rhs{lhs}} - Note rhsp() is the slice size, not the lhsp()
public:
AstNodeStream(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
AstNodeStream(AstType t, FileLine* fl, AstNode* lhsp, AstNode* rhsp)
: AstNodeBiop(t, fl, lhsp, rhsp) {
if (lhsp->dtypep()) {
dtypeSetLogicSized(lhsp->dtypep()->width(), AstNumeric::UNSIGNED);
}
@ -2124,8 +2132,8 @@ private:
bool m_dpiTask:1; // DPI import task (vs. void function)
bool m_pure:1; // DPI import pure
public:
AstNodeFTask(FileLine* fileline, const string& name, AstNode* stmtsp)
: AstNode(fileline)
AstNodeFTask(AstType t, FileLine* fl, const string& name, AstNode* stmtsp)
: AstNode(t, fl)
, m_name(name)
, m_dpiOpenParent(0), m_taskPublic(false)
, m_attrIsolateAssign(false), m_prototype(false)
@ -2187,13 +2195,13 @@ private:
string m_inlinedDots; // Dotted hierarchy flattened out
AstPackage* m_packagep; // Package hierarchy
public:
AstNodeFTaskRef(FileLine* fl, bool statement, AstNode* namep, AstNode* pinsp)
: AstNodeStmt(fl, statement)
AstNodeFTaskRef(AstType t, FileLine* fl, bool statement, AstNode* namep, AstNode* pinsp)
: AstNodeStmt(t, fl, statement)
, m_taskp(NULL), m_packagep(NULL) {
setOp1p(namep); addNOp2p(pinsp);
}
AstNodeFTaskRef(FileLine* fl, bool statement, const string& name, AstNode* pinsp)
: AstNodeStmt(fl, statement)
AstNodeFTaskRef(AstType t, FileLine* fl, bool statement, const string& name, AstNode* pinsp)
: AstNodeStmt(t, fl, statement)
, m_taskp(NULL), m_name(name), m_packagep(NULL) {
addNOp2p(pinsp);
}
@ -2244,8 +2252,8 @@ private:
int m_varNum; // Incrementing variable number
int m_typeNum; // Incrementing implicit type number
public:
AstNodeModule(FileLine* fl, const string& name)
: AstNode(fl)
AstNodeModule(AstType t, FileLine* fl, const string& name)
: AstNode(t, fl)
, m_name(name), m_origName(name)
, m_modPublic(false), m_modTrace(false), m_inLibrary(false), m_dead(false)
, m_internal(false), m_recursive(false), m_recursiveClone(false)
@ -2289,13 +2297,14 @@ public:
class AstNodeRange : public AstNode {
// A range, sized or unsized
public:
explicit AstNodeRange(FileLine* fl) : AstNode(fl) { }
AstNodeRange(AstType t, FileLine* fl)
: AstNode(t, fl) {}
ASTNODE_BASE_FUNCS(NodeRange)
};
//######################################################################
#include "V3AstNodes.h"
#include "V3AstNodes__gen.h"
//######################################################################
// Inline AstNVisitor METHODS

View File

@ -168,7 +168,7 @@ AstNodeBiop* AstEqWild::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
}
AstExecGraph::AstExecGraph(FileLine* fileline)
: AstNode(fileline) {
: AstNode(AstType::atExecGraph, fileline) {
m_depGraphp = new V3Graph;
}
AstExecGraph::~AstExecGraph() {

File diff suppressed because it is too large Load Diff

View File

@ -31,7 +31,7 @@ if (! GetOptions(
read_types("$Opt_I[0]/V3Ast.h");
read_types("$Opt_I[0]/V3AstNodes.h");
foreach my $type (sort (keys %Classes)) {
# Chekc all leafs are not AstNode* and non-leave are AstNode*
# Check all leaves are not AstNode* and non-leaves are AstNode*
my @children = children_of($type);
if ($type =~ /^Node/) {
@children || die "Error: Final AstNode subclasses must not be named AstNode*: Ast$type"
@ -50,6 +50,7 @@ if ($opt_classes) {
write_visitor("V3Ast__gen_visitor.h");
write_impl("V3Ast__gen_impl.h");
write_types("V3Ast__gen_types.h");
write_header("V3AstNodes__gen.h");
}
foreach my $cpt (@Opt_Cpt) {
Cpt::process(in_filename=>"$Opt_I[0]/${cpt}.cpp", out_filename=>"${cpt}__gen.cpp");
@ -346,7 +347,7 @@ sub write_types {
printf $fh " };\n";
printf $fh " const char* ascii() const {\n";
printf $fh " const char* const names[_ENUM_END + 1] = {\n";
printf $fh " static const char* const names[_ENUM_END + 1] = {\n";
write_type_enum($fh, "Node", 0, {}, "concrete-ascii", 3);
printf $fh " \"_ENUM_END\"\n";
printf $fh " };\n";
@ -355,6 +356,37 @@ sub write_types {
$fh->close();
}
sub write_header {
my $fh = open_file(@_);
my $type = "None";
my $base = "None";
my $ifile = "$Opt_I[0]/V3AstNodes.h";
my $ifh = IO::File->new($ifile) or die "%Error: $! $ifile,";
while (defined (my $line = $ifh->getline())) {
# Drop expanded macro definitions - but keep empty line so compiler
# message locations are accurate
$line =~ s/^\s*#(define|undef)\s+ASTGEN_.*$//;
# Track current node type and base class
if ($line =~ /^\s*class\s*Ast(\S+)\s*:\s*(public)?\s*(AstNode\S*)/) {
$type = $1;
$base = $3;
}
# Substitute macros
$line =~ s/\bASTGEN_SUPER\s*\(/$base(AstType::at$type, /;
# Emit the line
print $fh $line;
}
$ifh->close();
$fh->close();
}
#######################################################################
package Cpt;