From c5d04631d15837609f0880724342f722fae81977 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Thu, 23 Jan 2020 00:07:48 +0000 Subject: [PATCH] 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. --- src/V3Ast.h | 28 +++++++------ src/astgen | 111 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 94 insertions(+), 45 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 4c078a870..0b476cfdc 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -57,12 +57,12 @@ typedef std::set 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(nodep)) // (V)erilator (N)ode cast: Cast to given type if can; effectively // dynamic_cast(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(nodep)) +#define VN_CAST_CONST(nodep,nodetypename) (AstNode::privateConstCast(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 + static bool privateIs(const AstNode* nodep); + + // These for use by VN_CAST macro only + template + static T* privateCast(AstNode* nodep); + + // These for use by VN_CAST_CONST macro only + template + 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(this); } -// inline bool AstNode::privateIsaAlways(const AstNode* nodep) { return nodep && nodep->type() == AstType::atAlways; } - //###################################################################### // Inline AstNVisitor METHODS diff --git a/src/astgen b/src/astgen index d5f6af222..f99c7949f 100644 --- a/src/astgen +++ b/src/astgen @@ -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(const AstNode* nodep) { "; + if ($type eq "Node") { + print $fh "return nodep != NULL; "; + } else { + print $fh "return nodep && "; + if ($type =~ /^Node/) { + print $fh "(static_cast(nodep->type()) >= static_cast(AstType::first",$type,")) && "; + print $fh "(static_cast(nodep->type()) <= static_cast(AstType::last",$type,")); "; + } else { + print $fh "(static_cast(nodep->type()) == static_cast(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(AstNode* nodep) { "; + if ($type eq "Node") { + print $fh "return nodep; "; + } else { + print $fh "return AstNode::privateIs(nodep) ? "; + print $fh "reinterpret_cast(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(const AstNode* nodep) { "; + if ($type eq "Node") { + print $fh "return nodep; "; + } else { + print $fh "return AstNode::privateIs(nodep) ? "; + print $fh "reinterpret_cast(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(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(nodep); }\n"; - } - foreach my $type (sort (keys %Classes)) { - print $fh "inline const Ast",$type,"* AstNode::privateConstCast",$type,"(const AstNode* nodep) { return dynamic_cast(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";