From 7a1721e7c3ea7562077f78add8ecca97b80abc9b Mon Sep 17 00:00:00 2001 From: Philip Reames Date: Tue, 24 Feb 2015 23:12:27 +0000 Subject: [PATCH] Improve the getting started instructions in the GC docs This is still gcroot vs gc.statepoint agnostic. I'm just trying to clarify the general documentation at this point. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@230393 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/GarbageCollection.rst | 275 ++++++++++++++++++++----------------- 1 file changed, 147 insertions(+), 128 deletions(-) diff --git a/docs/GarbageCollection.rst b/docs/GarbageCollection.rst index 8d52a61f717..cd9cedaf0be 100644 --- a/docs/GarbageCollection.rst +++ b/docs/GarbageCollection.rst @@ -12,145 +12,63 @@ This document covers how to integrate LLVM into a compiler for a language which supports garbage collection. **Note that LLVM itself does not provide a garbage collector.** You must provide your own. -Getting started -=============== +Quick Start +============ -Using a GC with LLVM implies many things, for example: +First, you should pick a collector strategy. LLVM includes a number of built +in ones, but you can also implement a loadable plugin with a custom definition. +Note that the collector strategy is a description of how LLVM should generate +code such that it interacts with your collector and runtime, not a description +of the collector itself. -* Write a runtime library or find an existing one which implements a GC heap. - - #. Implement a memory allocator. - - #. Design a binary interface for the stack map, used to identify references - within a stack frame on the machine stack.\* - - #. Implement a stack crawler to discover functions on the call stack.\* - - #. Implement a registry for global roots. - - #. Design a binary interface for type maps, used to identify references - within heap objects. - - #. Implement a collection routine bringing together all of the above. - -* Emit compatible code from your compiler. - - * Initialization in the main function. - - * Use the ``gc "..."`` attribute to enable GC code generation (or - ``F.setGC("...")``). - - * Use ``@llvm.gcroot`` to mark stack roots. - - * Use ``@llvm.gcread`` and/or ``@llvm.gcwrite`` to manipulate GC references, - if necessary. - - * Allocate memory using the GC allocation routine provided by the runtime - library. - - * Generate type maps according to your runtime's binary interface. - -* Write a compiler plugin to interface LLVM with the runtime library.\* - - * Lower ``@llvm.gcread`` and ``@llvm.gcwrite`` to appropriate code - sequences.\* - - * Compile LLVM's stack map to the binary form expected by the runtime. - -* Load the plugin into the compiler. Use ``llc -load`` or link the plugin - statically with your language's compiler.\* - -* Link program executables with the runtime. - -To help with several of these tasks (those indicated with a \*), LLVM includes a -highly portable, built-in ShadowStack code generator. It is compiled into -``llc`` and works even with the interpreter and C backends. - -In your compiler ----------------- - -To turn the shadow stack on for your functions, first call: +Next, mark your generated functions as using your chosen collector strategy. +From c++, you can call: .. code-block:: c++ F.setGC(); -for each function your compiler emits. Since the shadow stack is built into -LLVM, you do not need to load a plugin. -Your compiler must also use ``@llvm.gcroot`` as documented. Don't forget to -create a root for each intermediate value that is generated when evaluating an -expression. In ``h(f(), g())``, the result of ``f()`` could easily be collected -if evaluating ``g()`` triggers a collection. +This will produce IR like the following fragment: -There's no need to use ``@llvm.gcread`` and ``@llvm.gcwrite`` over plain -``load`` and ``store`` for now. You will need them when switching to a more -advanced GC. +.. code-block:: llvm -In your runtime ---------------- - -The shadow stack doesn't imply a memory allocation algorithm. A semispace -collector or building atop ``malloc`` are great places to start, and can be -implemented with very little code. - -When it comes time to collect, however, your runtime needs to traverse the stack -roots, and for this it needs to integrate with the shadow stack. Luckily, doing -so is very simple. (This code is heavily commented to help you understand the -data structure, but there are only 20 lines of meaningful code.) - -.. code-block:: c++ - - /// @brief The map for a single function's stack frame. One of these is - /// compiled as constant data into the executable for each function. - /// - /// Storage of metadata values is elided if the %metadata parameter to - /// @llvm.gcroot is null. - struct FrameMap { - int32_t NumRoots; //< Number of roots in stack frame. - int32_t NumMeta; //< Number of metadata entries. May be < NumRoots. - const void *Meta[0]; //< Metadata for each root. - }; - - /// @brief A link in the dynamic shadow stack. One of these is embedded in - /// the stack frame of each function on the call stack. - struct StackEntry { - StackEntry *Next; //< Link to next stack entry (the caller's). - const FrameMap *Map; //< Pointer to constant FrameMap. - void *Roots[0]; //< Stack roots (in-place array). - }; - - /// @brief The head of the singly-linked list of StackEntries. Functions push - /// and pop onto this in their prologue and epilogue. - /// - /// Since there is only a global list, this technique is not threadsafe. - StackEntry *llvm_gc_root_chain; - - /// @brief Calls Visitor(root, meta) for each GC root on the stack. - /// root and meta are exactly the values passed to - /// @llvm.gcroot. - /// - /// Visitor could be a function to recursively mark live objects. Or it - /// might copy them to another heap or generation. - /// - /// @param Visitor A function to invoke for every GC root on the stack. - void visitGCRoots(void (*Visitor)(void **Root, const void *Meta)) { - for (StackEntry *R = llvm_gc_root_chain; R; R = R->Next) { - unsigned i = 0; - - // For roots [0, NumMeta), the metadata pointer is in the FrameMap. - for (unsigned e = R->Map->NumMeta; i != e; ++i) - Visitor(&R->Roots[i], R->Map->Meta[i]); - - // For roots [NumMeta, NumRoots), the metadata pointer is null. - for (unsigned e = R->Map->NumRoots; i != e; ++i) - Visitor(&R->Roots[i], NULL); - } - } + define void @foo() gc "" { ... } +When generating LLVM IR for your functions, you will need to: +* Use ``@llvm.gcread`` and/or ``@llvm.gcwrite`` in place of standard load and + store instructions. These intrinsics are used to represent load and store + barriers. If you collector does not require such barriers, you can skip + this step. +* Use the memory allocation routines provided by your garbage collector's + runtime library. + +* If your collector requires them, generate type maps according to your + runtime's binary interface. LLVM is not involved in the process. In + particular, the LLVM type system is not suitable for conveying such + information though the compiler. + +* Insert any coordination code required for interacting with your collector. + Many collectors require running application code to periodically check a + flag and conditionally call a runtime function. This is often referred to + as a safepoint poll. + +You will need to identify roots (i.e. references to heap objects your collector +needs to know about) in your generated IR, so that LLVM can encode them into +your final stack maps. Depending on the collector strategy chosen, this is +accomplished by using either the ''@llvm.gcroot'' intrinsics or an +''gc.statepoint'' relocation sequence. + +Don't forget to create a root for each intermediate value that is generated when +evaluating an expression. In ``h(f(), g())``, the result of ``f()`` could +easily be collected if evaluating ``g()`` triggers a collection. + +Finally, you need to link your runtime library with the generated program +executable (for a static compiler) or ensure the appropriate symbols are +available for the runtime linker (for a JIT compiler). What is Garbage Collection? =========================== @@ -263,10 +181,34 @@ lot of work for the developer of a novel language. However, it's easy to get started quickly and scale up to a more sophisticated implementation as your compiler matures. +Runtime Requirements +==================== + +LLVM does not provide a garbage collector. You should be able to leverage any existing collector library that includes the following elements: + +#. A memory allocator which exposes an allocation function your compiled + code can call. + +#. A binary format for the stack map. A stack map describes the location + of references at a safepoint and is used by precise collectors to identify + references within a stack frame on the machine stack. Note that collectors + which conservatively scan the stack don't require such a structure. + +#. A stack crawler to discover functions on the call stack, and enumerate the + references listed in the stack map for each call site. + +#. A mechanism for identifying references in global locations (e.g. global + variables). + +#. If you collector requires them, an LLVM IR implementation of your collectors + load and store barriers. Note that since many collectors don't require + barriers at all, LLVM defaults to lowering such barriers to normal loads + and stores unless you arrange otherwise. + .. _gc_intrinsics: -IR features -=========== +LLVM IR Features +================ This section describes the garbage collection facilities provided by the :doc:`LLVM intermediate representation `. The exact behavior of these @@ -469,6 +411,66 @@ and running, writing a :ref:`plugin ` will allow you to take advantage of :ref:`more advanced GC features ` of LLVM in order to improve performance. + +The shadow stack doesn't imply a memory allocation algorithm. A semispace +collector or building atop ``malloc`` are great places to start, and can be +implemented with very little code. + +When it comes time to collect, however, your runtime needs to traverse the stack +roots, and for this it needs to integrate with the shadow stack. Luckily, doing +so is very simple. (This code is heavily commented to help you understand the +data structure, but there are only 20 lines of meaningful code.) + +.. code-block:: c++ + + /// @brief The map for a single function's stack frame. One of these is + /// compiled as constant data into the executable for each function. + /// + /// Storage of metadata values is elided if the %metadata parameter to + /// @llvm.gcroot is null. + struct FrameMap { + int32_t NumRoots; //< Number of roots in stack frame. + int32_t NumMeta; //< Number of metadata entries. May be < NumRoots. + const void *Meta[0]; //< Metadata for each root. + }; + + /// @brief A link in the dynamic shadow stack. One of these is embedded in + /// the stack frame of each function on the call stack. + struct StackEntry { + StackEntry *Next; //< Link to next stack entry (the caller's). + const FrameMap *Map; //< Pointer to constant FrameMap. + void *Roots[0]; //< Stack roots (in-place array). + }; + + /// @brief The head of the singly-linked list of StackEntries. Functions push + /// and pop onto this in their prologue and epilogue. + /// + /// Since there is only a global list, this technique is not threadsafe. + StackEntry *llvm_gc_root_chain; + + /// @brief Calls Visitor(root, meta) for each GC root on the stack. + /// root and meta are exactly the values passed to + /// @llvm.gcroot. + /// + /// Visitor could be a function to recursively mark live objects. Or it + /// might copy them to another heap or generation. + /// + /// @param Visitor A function to invoke for every GC root on the stack. + void visitGCRoots(void (*Visitor)(void **Root, const void *Meta)) { + for (StackEntry *R = llvm_gc_root_chain; R; R = R->Next) { + unsigned i = 0; + + // For roots [0, NumMeta), the metadata pointer is in the FrameMap. + for (unsigned e = R->Map->NumMeta; i != e; ++i) + Visitor(&R->Roots[i], R->Map->Meta[i]); + + // For roots [NumMeta, NumRoots), the metadata pointer is null. + for (unsigned e = R->Map->NumRoots; i != e; ++i) + Visitor(&R->Roots[i], NULL); + } + } + + The 'Erlang' and 'Ocaml' GCs ----------------------------- @@ -494,8 +496,25 @@ This GC provides an example of how one might use the infrastructure provided by ''gc.statepoint''. +Custom GC Strategies +==================== + +If none of the built in GC strategy descriptions met your needs above, you will +need to define a custom GCStrategy and possibly, a custom LLVM pass to perform +lowering. Your best example of where to start defining a custom GCStrategy +would be to look at one of the built in strategies. + +You may be able to structure this additional code as a loadable plugin library. +Loadable plugins are sufficient if all you need is to enable a different +combination of built in functionality, but if you need to provide a custom +lowering pass, you will need to build a patched version of LLVM. If you think +you need a patched build, please ask for advice on llvm-dev. There may be an +easy way we can extend the support to make it work for your use case without +requiring a custom build. + + Implementing a collector plugin -=============================== +------------------------------- User code specifies which GC code generation to use with the ``gc`` function attribute or, equivalently, with the ``setGC`` method of ``Function``.