mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-08-27 15:29:51 +00:00
docs: Add HowToSetUpLLVMStyleRTTI.rst.
This document describes how to set up LLVM-style RTTI for a class hierarchy. Surprisingly, this was not previously documented. Also, link it into ProgrammersManual.html. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@165293 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
63554988a9
commit
36be1aeac7
281
docs/HowToSetUpLLVMStyleRTTI.rst
Normal file
281
docs/HowToSetUpLLVMStyleRTTI.rst
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
.. _how-to-set-up-llvm-style-rtti:
|
||||||
|
|
||||||
|
======================================================
|
||||||
|
How to set up LLVM-style RTTI for your class hierarchy
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
.. sectionauthor:: Sean Silva <silvas@purdue.edu>
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
|
||||||
|
Background
|
||||||
|
==========
|
||||||
|
|
||||||
|
LLVM avoids using C++'s built in RTTI. Instead, it pervasively uses its
|
||||||
|
own hand-rolled form of RTTI which is much more efficient and flexible,
|
||||||
|
although it requires a bit more work from you as a class author.
|
||||||
|
|
||||||
|
A description of how to use LLVM-style RTTI from a client's perspective is
|
||||||
|
given in the `Programmer's Manual <ProgrammersManual.html#isa>`_. This
|
||||||
|
document, in contrast, discusses the steps you need to take as a class
|
||||||
|
hierarchy author to make LLVM-style RTTI available to your clients.
|
||||||
|
|
||||||
|
Before diving in, make sure that you are familiar with the Object Oriented
|
||||||
|
Programming concept of "`is-a`_".
|
||||||
|
|
||||||
|
.. _is-a: http://en.wikipedia.org/wiki/Is-a
|
||||||
|
|
||||||
|
Basic Setup
|
||||||
|
===========
|
||||||
|
|
||||||
|
This section describes how to set up the most basic form of LLVM-style RTTI
|
||||||
|
(which is sufficient for 99.9% of the cases). We will set up LLVM-style
|
||||||
|
RTTI for this class hierarchy:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
class Shape {
|
||||||
|
public:
|
||||||
|
Shape() {};
|
||||||
|
virtual double computeArea() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Square : public Shape {
|
||||||
|
double SideLength;
|
||||||
|
public:
|
||||||
|
Square(double S) : SideLength(S) {}
|
||||||
|
double computeArea() /* override */;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Circle : public Shape {
|
||||||
|
double Radius;
|
||||||
|
public:
|
||||||
|
Circle(double R) : Radius(R) {}
|
||||||
|
double computeArea() /* override */;
|
||||||
|
};
|
||||||
|
|
||||||
|
The most basic working setup for LLVM-style RTTI requires the following
|
||||||
|
steps:
|
||||||
|
|
||||||
|
#. In the header where you declare ``Shape``, you will want to ``#include
|
||||||
|
"llvm/Support/Casting.h"``, which declares LLVM's RTTI templates. That
|
||||||
|
way your clients don't even have to think about it.
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
#include "llvm/Support/Casting.h"
|
||||||
|
|
||||||
|
|
||||||
|
#. In the base class, introduce an enum which discriminates all of the
|
||||||
|
different classes in the hierarchy, and stash the enum value somewhere in
|
||||||
|
the base class.
|
||||||
|
|
||||||
|
Here is the code after introducing this change:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
class Shape {
|
||||||
|
public:
|
||||||
|
+ /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
|
||||||
|
+ enum ShapeKind {
|
||||||
|
+ SquareKind,
|
||||||
|
+ CircleKind
|
||||||
|
+ };
|
||||||
|
+private:
|
||||||
|
+ const ShapeKind Kind;
|
||||||
|
+public:
|
||||||
|
+ ShapeKind getKind() const { return Kind; }
|
||||||
|
+
|
||||||
|
Shape() {};
|
||||||
|
virtual double computeArea() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
You will usually want to keep the ``Kind`` member encapsulated and
|
||||||
|
private, but let the enum ``ShapeKind`` be public along with providing a
|
||||||
|
``getKind()`` method. This is convenient for clients so that they can do
|
||||||
|
a ``switch`` over the enum.
|
||||||
|
|
||||||
|
A common naming convention is that these enums are "kind"s, to avoid
|
||||||
|
ambiguity with the words "type" or "class" which have overloaded meanings
|
||||||
|
in many contexts within LLVM. Sometimes there will be a natural name for
|
||||||
|
it, like "opcode". Don't bikeshed over this; when in doubt use ``Kind``.
|
||||||
|
|
||||||
|
You might wonder why the ``Kind`` enum doesn't have an entry for
|
||||||
|
``Shape``. The reason for this is that since ``Shape`` is abstract
|
||||||
|
(``computeArea() = 0;``), you will never actually have non-derived
|
||||||
|
instances of exactly that class (only subclasses). See `Concrete Bases
|
||||||
|
and Deeper Hierarchies`_ for information on how to deal with
|
||||||
|
non-abstract bases. It's worth mentioning here that unlike
|
||||||
|
``dynamic_cast<>``, LLVM-style RTTI can be used (and is often used) for
|
||||||
|
classes that don't have v-tables.
|
||||||
|
|
||||||
|
#. Next, you need to make sure that the ``Kind`` gets initialized to the
|
||||||
|
value corresponding to the dynamic type of the class. Typically, you will
|
||||||
|
want to have it be an argument to the constructor of the base class, and
|
||||||
|
then pass in the respective ``XXXKind`` from subclass constructors.
|
||||||
|
|
||||||
|
Here is the code after that change:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
class Shape {
|
||||||
|
public:
|
||||||
|
/// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
|
||||||
|
enum ShapeKind {
|
||||||
|
SquareKind,
|
||||||
|
CircleKind
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
const ShapeKind Kind;
|
||||||
|
public:
|
||||||
|
ShapeKind getKind() const { return Kind; }
|
||||||
|
|
||||||
|
- Shape() {};
|
||||||
|
+ Shape(ShapeKind K) : Kind(K) {};
|
||||||
|
virtual double computeArea() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Square : public Shape {
|
||||||
|
double SideLength;
|
||||||
|
public:
|
||||||
|
- Square(double S) : SideLength(S) {}
|
||||||
|
+ Square(double S) : Shape(SquareKind), SideLength(S) {}
|
||||||
|
double computeArea() /* override */;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Circle : public Shape {
|
||||||
|
double Radius;
|
||||||
|
public:
|
||||||
|
- Circle(double R) : Radius(R) {}
|
||||||
|
+ Circle(double R) : Shape(CircleKind), Radius(R) {}
|
||||||
|
double computeArea() /* override */;
|
||||||
|
};
|
||||||
|
|
||||||
|
#. Finally, you need to inform LLVM's RTTI templates how to dynamically
|
||||||
|
determine the type of a class (i.e. whether the ``isa<>``/``dyn_cast<>``
|
||||||
|
should succeed). The default "99.9% of use cases" way to accomplish this
|
||||||
|
is through a small static member function ``classof``. In order to have
|
||||||
|
proper context for an explanation, we will display this code first, and
|
||||||
|
then below describe each part:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
class Shape {
|
||||||
|
public:
|
||||||
|
/// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
|
||||||
|
enum ShapeKind {
|
||||||
|
SquareKind,
|
||||||
|
CircleKind
|
||||||
|
};
|
||||||
|
private:
|
||||||
|
const ShapeKind Kind;
|
||||||
|
public:
|
||||||
|
ShapeKind getKind() const { return Kind; }
|
||||||
|
|
||||||
|
Shape(ShapeKind K) : Kind(K) {};
|
||||||
|
virtual double computeArea() = 0;
|
||||||
|
+
|
||||||
|
+ static bool classof(const Shape *) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Square : public Shape {
|
||||||
|
double SideLength;
|
||||||
|
public:
|
||||||
|
Square(double S) : Shape(SquareKind), SideLength(S) {}
|
||||||
|
double computeArea() /* override */;
|
||||||
|
+
|
||||||
|
+ static bool classof(const Square *) { return true; }
|
||||||
|
+ static bool classof(const Shape *S) {
|
||||||
|
+ return S->getKind() == SquareKind;
|
||||||
|
+ }
|
||||||
|
};
|
||||||
|
|
||||||
|
class Circle : public Shape {
|
||||||
|
double Radius;
|
||||||
|
public:
|
||||||
|
Circle(double R) : Shape(CircleKind), Radius(R) {}
|
||||||
|
double computeArea() /* override */;
|
||||||
|
+
|
||||||
|
+ static bool classof(const Circle *) { return true; }
|
||||||
|
+ static bool classof(const Shape *S) {
|
||||||
|
+ return S->getKind() == CircleKind;
|
||||||
|
+ }
|
||||||
|
};
|
||||||
|
|
||||||
|
Basically, the job of ``classof`` is to return ``true`` if its argument
|
||||||
|
is of the enclosing class's type. As you can see, there are two general
|
||||||
|
overloads of ``classof`` in use here.
|
||||||
|
|
||||||
|
#. The first, which just returns ``true``, means that if we know that the
|
||||||
|
argument of the cast is of the enclosing type *at compile time*, then
|
||||||
|
we don't need to bother to check anything since we already know that
|
||||||
|
the type is convertible. This is an optimization for the case that we
|
||||||
|
statically know the conversion is OK.
|
||||||
|
|
||||||
|
#. The other overload takes a pointer to an object of the base of the
|
||||||
|
class hierarchy: this is the "general case" of the cast. We need to
|
||||||
|
check the ``Kind`` to dynamically decide if the argument is of (or
|
||||||
|
derived from) the enclosing type.
|
||||||
|
|
||||||
|
To be more precise, let ``classof`` be inside a class ``C``. Then the
|
||||||
|
contract for ``classof`` is "return ``true`` if the argument is-a
|
||||||
|
``C``". As long as your implementation fulfills this contract, you can
|
||||||
|
tweak and optimize it as much as you want.
|
||||||
|
|
||||||
|
Although for this small example setting up LLVM-style RTTI seems like a lot
|
||||||
|
of "boilerplate", if your classes are doing anything interesting then this
|
||||||
|
will end up being a tiny fraction of the code.
|
||||||
|
|
||||||
|
Concrete Bases and Deeper Hierarchies
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
For concrete bases (i.e. non-abstract interior nodes of the inheritance
|
||||||
|
tree), the ``Kind`` check inside ``classof`` needs to be a bit more
|
||||||
|
complicated. Say that ``SpecialSquare`` and ``OtherSpecialSquare`` derive
|
||||||
|
from ``Square``, and so ``ShapeKind`` becomes:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
enum ShapeKind {
|
||||||
|
SquareKind,
|
||||||
|
+ SpecialSquareKind,
|
||||||
|
+ OtherSpecialSquareKind,
|
||||||
|
CircleKind
|
||||||
|
}
|
||||||
|
|
||||||
|
Then in ``Square``, we would need to modify the ``classof`` like so:
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
static bool classof(const Square *) { return true; }
|
||||||
|
- static bool classof(const Shape *S) {
|
||||||
|
- return S->getKind() == SquareKind;
|
||||||
|
- }
|
||||||
|
+ static bool classof(const Shape *S) {
|
||||||
|
+ return S->getKind() >= SquareKind &&
|
||||||
|
+ S->getKind() <= OtherSpecialSquareKind;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
The reason that we need to test a range like this instead of just equality
|
||||||
|
is that both ``SpecialSquare`` and ``OtherSpecialSquare`` "is-a"
|
||||||
|
``Square``, and so ``classof`` needs to return ``true`` for them.
|
||||||
|
|
||||||
|
This approach can be made to scale to arbitrarily deep hierarchies. The
|
||||||
|
trick is that you arrange the enum values so that they correspond to a
|
||||||
|
preorder traversal of the class hierarchy tree. With that arrangement, all
|
||||||
|
subclass tests can be done with two comparisons as shown above. If you just
|
||||||
|
list the class hierarchy like a list of bullet points, you'll get the
|
||||||
|
ordering right::
|
||||||
|
|
||||||
|
| Shape
|
||||||
|
| Square
|
||||||
|
| SpecialSquare
|
||||||
|
| OtherSpecialSquare
|
||||||
|
| Circle
|
||||||
|
|
||||||
|
.. TODO::
|
||||||
|
|
||||||
|
Touch on some of the more advanced features, like ``isa_impl`` and
|
||||||
|
``simplify_type``. However, those two need reference documentation in
|
||||||
|
the form of doxygen comments as well. We need the doxygen so that we can
|
||||||
|
say "for full details, see http://llvm.org/doxygen/..."
|
@ -432,10 +432,10 @@ if (<a href="#AllocationInst">AllocationInst</a> *AI = dyn_cast<<a href="#All
|
|||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<p>These five templates can be used with any classes, whether they have a
|
<p>These five templates can be used with any classes, whether they have a
|
||||||
v-table or not. To add support for these templates, you simply need to add
|
v-table or not. If you want to add support for these templates, see the
|
||||||
<tt>classof</tt> static methods to the class you are interested casting
|
document <a href="HowToSetUpLLVMStyleRTTI.html">How to set up LLVM-style
|
||||||
to. Describing this is currently outside the scope of this document, but there
|
RTTI for your class hierarchy </a>.
|
||||||
are lots of examples in the LLVM source base.</p>
|
</p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ Programming Documentation
|
|||||||
CodingStandards
|
CodingStandards
|
||||||
CommandLine
|
CommandLine
|
||||||
Atomics
|
Atomics
|
||||||
|
HowToSetUpLLVMStyleRTTI
|
||||||
|
|
||||||
* `LLVM Language Reference Manual <LangRef.html>`_
|
* `LLVM Language Reference Manual <LangRef.html>`_
|
||||||
|
|
||||||
@ -33,6 +34,11 @@ Programming Documentation
|
|||||||
Details the LLVM coding standards and provides useful information on writing
|
Details the LLVM coding standards and provides useful information on writing
|
||||||
efficient C++ code.
|
efficient C++ code.
|
||||||
|
|
||||||
|
* :doc:`HowToSetUpLLVMStyleRTTI`
|
||||||
|
|
||||||
|
How to make ``isa<>``, ``dyn_cast<>``, etc. available for clients of your
|
||||||
|
class hierarchy.
|
||||||
|
|
||||||
* `Extending LLVM <ExtendingLLVM.html>`_
|
* `Extending LLVM <ExtendingLLVM.html>`_
|
||||||
|
|
||||||
Look here to see how to add instructions and intrinsics to LLVM.
|
Look here to see how to add instructions and intrinsics to LLVM.
|
||||||
|
Loading…
Reference in New Issue
Block a user