C++ (following the C++ 11 standard) is the implementation language for the compiler. This file contains notes on how we use C++ and conventions we use for things that C++ does not handle well.
C++ does not support multiple virtual dispatch, but we use it heavily in
the design of the Visitor classes for the compiler. To support this use
(reasonably) cleanly, we use a preprocessor ir-generator
to read the
IR classes from .def
files and generate all the necessary boilerplate
for multiple dispatch in the IR and Visitor classes.
C++ does not handle inner classes natively, so to implement an inner class,
we use a nested class with a reference member to the containing class
called self
.
IR nodes and their handling code (e.g. Visitors) make extensive use of RTTI to perform type-checking and corresponding downcasting from Node*
down to a particular implementation. C++ native RTTI as implemented in dynamic_cast
and typeid
built-ins is inherently slow and has lots of overhead. To circumvent this IR node classes use dedicated light-weight RTTI designed for semi-open class hierarchies. The implementation itself requires some boilerplate that is automatically generated by ir-generator
for all IR classes from .def
. The corresponding RTTI functionality could be accessed via ICastable
interface from lib/castable.h
. There are also freestandig helper functions and type traits in lib/rtti_utils.h
, these helpers are mainly useful with generic algorithms and ranges. The lower-level RTTI implementation details are in lib/rtti.h
. Overall, use of dynamic_cast
and typeid
for IR nodes is discouraged.
Other class hierarchies besides IR::Node
descendants could also benefit from lightweight RTTI functionality. In order to use it one needs to annotate the intended class hierarchy as follows:
- Derive from
IR::Castable
(or lower-levelRTTI::Base
) - Use
DECLARE_TYPEINFO(Class, Bases..)
macro call inside class to generate necessary boilerplate. Here typeid forClass
will automatically be generated at compile time from the type name. Should necessary, explicit typeid could be specified byDECLARE_TYPEINFO(Class, TypeId, Bases...)
. DECLARE_TYPEINFO
with no bases could be used to indicate the base class for the given hierarchy (e.g.DECLARE_TYPEINFO(Class)
) and separate different class hierarchies from each other.
One example of custom hierarchy using this RTTI implementation could be seen in frontends/p4/typeChecking/typeConstraints.h
.
Important notes:
- In order for RTTI to work all class hierarchy should be annotated using
DECLARE_TYPEINFO
macro. Static assert would fire if something is missed. - One need to correctly specify all direct bases for the given
Class
including virtual ones. Otherwise upcasts and downcasts will not work via missed bases. - The typeid values are unique only for the given class hierarchy, they are not intended to be compared across different class hierarchies (although this is always something questionable).
DECLARE_TYPEINFO
should likely be the last in the class as it expands to a series or private and public fields. Alternatively, a visibility must be explicitly specified afterDECLARE_TYPEINFO
.