Internals: More performance efficient AstNode casting. Closes #2133.

dynamic_cast can have large run-time cost, so here we implement type
tests for AstNode instances by checking the unique type() property, which
in turn is a constant generated by astgen. For leaf types in the AstNode
type hierarchy, this is a simple equality check. To handle intermediate
types, we generate the type ids of leaf types in a pre-order traversal of
the type hierarchy. This yields contiguous ranges of ids for sub-type
trees, which means we can check membership of a non-leaf type via 2
comparisons against a low and high id. This single patch makes Verilator
itself 6-13% faster (depending on which optimizations are enabled) on a
large design of over 250k lines of Verilog.
This commit is contained in:
Geza Lore 2020-01-23 00:07:48 +00:00 committed by Wilson Snyder
parent 957c1d606b
commit c5d04631d1
2 changed files with 94 additions and 45 deletions

View File

@ -57,12 +57,12 @@ typedef std::set<int> MTaskIdSet; // Set of mtaskIds for Var sorting
} while (0)
// (V)erilator (N)ode is: True if AstNode is of a a given AstType
#define VN_IS(nodep,nodetypename) (AstNode::privateIs ## nodetypename(nodep))
#define VN_IS(nodep,nodetypename) (AstNode::privateIs<Ast ## nodetypename>(nodep))
// (V)erilator (N)ode cast: Cast to given type if can; effectively
// dynamic_cast<nodetypename>(nodep)
#define VN_CAST(nodep,nodetypename) (AstNode::privateCast ## nodetypename(nodep))
#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast ## nodetypename(nodep) )
#define VN_CAST(nodep,nodetypename) (AstNode::privateCast<Ast ## nodetypename>(nodep))
#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast<Ast ## nodetypename>(nodep))
// (V)erilator (N)ode deleted: Reference to deleted child (for assertions only)
#define VN_DELETED(nodep) VL_UNLIKELY((vluint64_t)(nodep) == 0x1)
@ -1535,11 +1535,22 @@ private:
// CONVERSION
public:
#include "V3Ast__gen_interface.h" // From ./astgen
// Things like:
// AstAlways* castAlways();
// These for use by VN_IS macro only
template<class T>
static bool privateIs(const AstNode* nodep);
// These for use by VN_CAST macro only
template<class T>
static T* privateCast(AstNode* nodep);
// These for use by VN_CAST_CONST macro only
template<class T>
static const T* privateConstCast(const AstNode* nodep);
};
// Specialisations of privateIs/privateCast
#include "V3Ast__gen_impl.h" // From ./astgen
inline std::ostream& operator<<(std::ostream& os, const AstNode* rhs) {
if (!rhs) os<<"NULL"; else rhs->dump(os); return os;
}
@ -2286,11 +2297,6 @@ public:
#include "V3AstNodes.h"
#include "V3Ast__gen_impl.h" // From ./astgen
// Things like:
// inline AstAlways* AstNode::castAlways() { return dynamic_cast<AstAlways*>(this); }
// inline bool AstNode::privateIsaAlways(const AstNode* nodep) { return nodep && nodep->type() == AstType::atAlways; }
//######################################################################
// Inline AstNVisitor METHODS

View File

@ -48,7 +48,6 @@ if ($opt_classes) {
write_report("V3Ast__gen_report.txt");
write_classes("V3Ast__gen_classes.h");
write_visitor("V3Ast__gen_visitor.h");
write_intf("V3Ast__gen_interface.h");
write_impl("V3Ast__gen_impl.h");
write_types("V3Ast__gen_types.h");
}
@ -244,68 +243,112 @@ sub write_visitor {
$fh->close();
}
sub write_intf {
sub write_impl {
my $fh = open_file(@_);
print $fh "\n";
print $fh " // These for use by VN_IS macro only\n";
print $fh " // These for use by VN_IS only\n";
foreach my $type (sort (keys %Classes)) {
print $fh " static bool privateIs",$type,"(const AstNode* nodep);\n";
print $fh "template<> inline bool AstNode::privateIs<Ast",$type,">(const AstNode* nodep) { ";
if ($type eq "Node") {
print $fh "return nodep != NULL; ";
} else {
print $fh "return nodep && ";
if ($type =~ /^Node/) {
print $fh "(static_cast<int>(nodep->type()) >= static_cast<int>(AstType::first",$type,")) && ";
print $fh "(static_cast<int>(nodep->type()) <= static_cast<int>(AstType::last",$type,")); ";
} else {
print $fh "(static_cast<int>(nodep->type()) == static_cast<int>(AstType::at",$type,")); ";
}
}
print $fh "}\n"
}
print $fh "\n";
print $fh " // These for use by VN_CAST macro only\n";
foreach my $type (sort (keys %Classes)) {
print $fh " static Ast",$type,"* privateCast",$type,"(AstNode* nodep);\n";
print $fh "template<> inline Ast",$type,"* AstNode::privateCast<Ast",$type,">(AstNode* nodep) { ";
if ($type eq "Node") {
print $fh "return nodep; ";
} else {
print $fh "return AstNode::privateIs<Ast",$type,">(nodep) ? ";
print $fh "reinterpret_cast<Ast",$type,"*>(nodep) : NULL; ";
}
print $fh "}\n";
}
print $fh " // These for use by VN_CAST_CONST macro only\n";
foreach my $type (sort (keys %Classes)) {
print $fh " static const Ast",$type,"* privateConstCast",$type,"(const AstNode* nodep);\n";
print $fh "template<> inline const Ast",$type,"* AstNode::privateConstCast<Ast",$type,">(const AstNode* nodep) { ";
if ($type eq "Node") {
print $fh "return nodep; ";
} else {
print $fh "return AstNode::privateIs<Ast",$type,">(nodep) ? ";
print $fh "reinterpret_cast<const Ast",$type,"*>(nodep) : NULL; ";
}
print $fh "}\n";
}
$fh->close();
}
sub write_impl {
my $fh = open_file(@_);
sub write_type_enum {
my $fh = shift;
my $type = shift;
my $idx = shift;
my $processed = shift;
my $kind = shift;
my $indent = shift;
print $fh "\n";
print $fh " // These for use by VN_IS macro only\n";
foreach my $type (sort (keys %Classes)) {
if (children_of($type)) {
print $fh "inline bool AstNode::privateIs",$type,"(const AstNode* nodep) { return (bool)(dynamic_cast<const Ast",$type,"*>(nodep)); }\n";
} else {
print $fh "inline bool AstNode::privateIs",$type,"(const AstNode* nodep) { return nodep && nodep->type() == AstType::at",$type,"; }\n";
# Skip this if it has already been processed
return $idx if (exists $processed->{$type});
# Mark processed
$processed->{$type} = 1;
# The last used index
my $last;
if ($type !~ /^Node/) {
$last = $idx;
if ($kind eq "concrete-enum") {
print $fh " "x($indent*4), "at",$type," = ",$idx,",\n";
} elsif ($kind eq "concrete-ascii") {
print $fh " "x($indent*4), "\"", uc $type, "\",\n";
}
$idx += 1;
} elsif ($kind eq "abstract-enum") {
print $fh " "x($indent*4), "first",$type," = ",$idx,",\n";
}
foreach my $type (sort (keys %Classes)) {
print $fh "inline Ast",$type,"* AstNode::privateCast",$type,"(AstNode* nodep) { return dynamic_cast<Ast",$type,"*>(nodep); }\n";
}
foreach my $type (sort (keys %Classes)) {
print $fh "inline const Ast",$type,"* AstNode::privateConstCast",$type,"(const AstNode* nodep) { return dynamic_cast<const Ast",$type,"*>(nodep); }\n";
foreach my $child (sort keys %{$::Children{$type}}) {
($idx, $last) = write_type_enum($fh, $child, $idx, $processed, $kind, $indent);
}
$fh->close();
if ($type =~ /^Node/ && ($kind eq "abstract-enum")) {
print $fh " "x($indent*4), "last",$type," = ",$last,",\n";
}
return $idx, $last;
}
sub write_types {
my $fh = open_file(@_);
printf $fh " enum en {\n";
# Add "at" prefix to avoid conflicting with FOPEN and other macros in include files
foreach my $type (sort (keys %Classes)) {
next if $type =~ /^Node/;
print $fh "\tat",$type,",\n";
}
printf $fh "\t_ENUM_END\n";
(my $final, undef) = write_type_enum($fh, "Node", 0, {}, "concrete-enum", 2);
printf $fh " _ENUM_END = $final\n";
printf $fh " };\n";
printf $fh " enum bounds {\n";
write_type_enum($fh, "Node", 0, {}, "abstract-enum", 2);
printf $fh " _BOUNDS_END\n";
printf $fh " };\n";
printf $fh " const char* ascii() const {\n";
printf $fh " const char* const names[] = {\n";
foreach my $type (sort (keys %Classes)) {
next if $type =~ /^Node/;
print $fh "\t\"", uc $type, "\",\n";
}
printf $fh "\t\"_ENUM_END\"\n";
printf $fh " 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";
printf $fh " return names[m_e];\n";
printf $fh " };\n";