Created
April 30, 2018 11:05
-
-
Save davezarzycki/e1181101841a62b612415e14afb8f072 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //===- llvm/Support/VTable.h - LLVM-style vtables ---------------*- C++ -*-===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file provides LLVM_VIRTUAL() and related macros for creating virtual | |
| // methods that demultiplex via LLVM-style RTTI. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_SUPPORT_VTABLE_H | |
| #define LLVM_SUPPORT_VTABLE_H | |
| #include <functional> | |
| namespace llvm { | |
| //===----------------------------------------------------------------------===// | |
| // LLVM-style VTable Support macros | |
| //===----------------------------------------------------------------------===// | |
| // Virtual method dispatch via LLVM runtime type information. This approach | |
| // requires a little bit more work than native C++ 'virtual' methods, but the | |
| // memory savings can be well worth it. On non-PIC x86 systems, the same | |
| // number of instructions are generated. On PIC x86 systems, one additional | |
| // instruction is generated to compute (or load) the pointer to the vtable | |
| // (LEA or MOV respectively), and the compiler is smart enough to hoist the | |
| // LEA/MOV outside of loops and cache the result. | |
| // | |
| // For subclasses, virtual methods are declared like so: | |
| // | |
| // LLVM_VIRTUAL_THUNK(BaseTy, makeSound) | |
| // void LLVM_VIRTUAL(makeSound)(int howLoud) { /* normal body */ } | |
| // | |
| // For base classes, one must do a little more work. The simplest case is an | |
| // abstract virtual method in a type called 'Base': | |
| // | |
| // void LLVM_ABSTRACT_VIRTUAL(BaseTy, makeSound) | |
| // | |
| // And then later in the header file, the vtable definition: | |
| // | |
| // LLVM_ABSTRACT_VIRTUAL_BEGIN(BaseTy, makeSound) | |
| // #define BASE_NODE(Ty, ...) LLVM_ABSTRACT_VIRTUAL_SLOT(Ty, makeSound) | |
| // #include <BaseTy.inc> | |
| // LLVM_ABSTRACT_VIRTUAL_END(getKind()) | |
| // | |
| // | |
| // Example: | |
| // | |
| // class Cat { | |
| // ... | |
| // | |
| // void LLVM_ABSTRACT_VIRTUAL(Base, makeSound) | |
| // | |
| // LLVM_BASE_VIRTUAL(Base, getOffspringCount) | |
| // size_t LLVM_VIRTUAL(getOffspringCount)() const { | |
| // return 0; | |
| // } | |
| // }; | |
| // | |
| // | |
| // class Lion : public Cat { | |
| // ... | |
| // | |
| // LLVM_VIRTUAL_THUNK(Base, makeSound) | |
| // void LLVM_VIRTUAL(makeSound)() { | |
| // ... | |
| // } | |
| // | |
| // LLVM_VIRTUAL_THUNK(Base, getOffspringCount) | |
| // int LLVM_VIRTUAL(getOffspringCount)() const { | |
| // ... | |
| // } | |
| // }; | |
| #define LLVM_VIRTUAL_THUNK(Ty, BaseTy, Method) \ | |
| template <typename... Args> \ | |
| static auto _virtual_##Method(BaseTy *b, Args... args) -> auto { \ | |
| return static_cast<Ty*>(b) \ | |
| ->Method##_virtualbody_(std::forward<Args>(args)...); \ | |
| } | |
| #define LLVM_VIRTUAL(Method) \ | |
| __attribute__((always_inline)) Method##_virtualbody_ | |
| #define LLVM_ABSTRACT_VIRTUAL(BaseTy, Method) \ | |
| __attribute__((noreturn)) \ | |
| LLVM_VIRTUAL(Method)(...) { \ | |
| llvm_unreachable("Unhandled LLVM-virtual method"); \ | |
| } \ | |
| template <typename... Args> \ | |
| auto Method(Args... args) -> auto; \ | |
| LLVM_VIRTUAL_THUNK(BaseTy, BaseTy, Method) | |
| #define LLVM_BASE_VIRTUAL(BaseTy, Method) \ | |
| template <typename... Args> \ | |
| auto Method(Args... args) -> auto; \ | |
| LLVM_VIRTUAL_THUNK(BaseTy, BaseTy, Method) | |
| #define LLVM_ABSTRACT_VIRTUAL_BEGIN(BaseTy, Method) \ | |
| template <typename... Args> \ | |
| auto BaseTy::Method(Args... args) -> auto { \ | |
| static const decltype(&BaseTy::_virtual_##Method<Args...>) vtable[] = { | |
| #define LLVM_ABSTRACT_VIRTUAL_SLOT(Ty, Method) \ | |
| &Ty::_virtual_##Method<Args...>, | |
| #define LLVM_ABSTRACT_VIRTUAL_END(GetKind) \ | |
| }; \ | |
| return vtable[GetKind](this, std::forward<Args>(args)...); \ | |
| } | |
| } // end namespace llvm | |
| #endif // LLVM_SUPPORT_VTABLE_H |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey @rjmccall, @DougGregor, @jckarter, and @jrose-apple –
I toyed with the above "LLVM style" vtable dispatch a while back. Do any of you think this idea is worth pursuing further at some point? The performance/code-gen should be essentially the same as normal virtual method dispatch, and saving eight bytes per [virtual using] AST node feels compelling.
What do you think?