From f2814925a2f60fd8820d273bcd299097c1417672 Mon Sep 17 00:00:00 2001 From: Philip Reames Date: Tue, 24 Feb 2015 19:44:46 +0000 Subject: [PATCH] Starting to cleanup the garbage collection documentation In this change: - Put the getting started section first - Create a dedicated section to document the built in collector strategies - Move discuss of ShadowStack into new section - Add placeholders for erlang, ocaml, and statepoint-example collectors There will be many more changes following. I plan on full integrating the documentation for gc.statepoint and gc.root. I want to make it much clearer on how to get started and what users should expect in terms of effort. llvm-svn: 230359 --- docs/GarbageCollection.rst | 365 +++++++++++++++++++++---------------- 1 file changed, 206 insertions(+), 159 deletions(-) diff --git a/docs/GarbageCollection.rst b/docs/GarbageCollection.rst index 40f5b1287b0..8d52a61f717 100644 --- a/docs/GarbageCollection.rst +++ b/docs/GarbageCollection.rst @@ -1,5 +1,5 @@ ===================================== -Accurate Garbage Collection with LLVM +Garbage Collection with LLVM ===================================== .. contents:: @@ -8,6 +8,153 @@ Accurate Garbage Collection with LLVM Introduction ============ +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 +=============== + +Using a GC with LLVM implies many things, for example: + +* 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: + +.. 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. + +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. + +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); + } + } + + + + + +What is Garbage Collection? +=========================== + Garbage collection is a widely used technique that frees the programmer from having to know the lifetimes of heap objects, making software easier to produce and maintain. Many programming languages rely on garbage collection for @@ -116,164 +263,6 @@ 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. -Getting started -=============== - -Using a GC with LLVM implies many things, for example: - -* 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: - -.. code-block:: c++ - - F.setGC("shadow-stack"); - -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. - -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. - -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); - } - } - -About the shadow stack ----------------------- - -Unlike many GC algorithms which rely on a cooperative code generator to compile -stack maps, this algorithm carefully maintains a linked list of stack roots -[:ref:`Henderson2002 `]. This so-called "shadow stack" mirrors the -machine stack. Maintaining this data structure is slower than using a stack map -compiled into the executable as constant data, but has a significant portability -advantage because it requires no special support from the target code generator, -and does not require tricky platform-specific code to crawl the machine stack. - -The tradeoff for this simplicity and portability is: - -* High overhead per function call. - -* Not thread-safe. - -Still, it's an easy way to get started. After your compiler and runtime are up -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. - .. _gc_intrinsics: IR features @@ -447,6 +436,64 @@ greater performance impact since pointer reads are more frequent than writes. .. _plugin: +Built In Collectors +==================== + +LLVM includes built in support for several varieties of garbage collectors. + +The Shadow Stack GC +---------------------- + +To use this collector strategy, mark your functions with: + +.. code-block:: c++ + + F.setGC("shadow-stack"); + +Unlike many GC algorithms which rely on a cooperative code generator to compile +stack maps, this algorithm carefully maintains a linked list of stack roots +[:ref:`Henderson2002 `]. This so-called "shadow stack" mirrors the +machine stack. Maintaining this data structure is slower than using a stack map +compiled into the executable as constant data, but has a significant portability +advantage because it requires no special support from the target code generator, +and does not require tricky platform-specific code to crawl the machine stack. + +The tradeoff for this simplicity and portability is: + +* High overhead per function call. + +* Not thread-safe. + +Still, it's an easy way to get started. After your compiler and runtime are up +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 'Erlang' and 'Ocaml' GCs +----------------------------- + +LLVM ships with two example collectors which leverage the ''gcroot'' +mechanisms. To our knowledge, these are not actually used by any language +runtime, but they do provide a reasonable starting point for someone interested +in writing an ''gcroot' compatible GC plugin. In particular, these are the +only in tree examples of how to produce a custom binary stack map format using +a ''gcroot'' strategy. + +As there names imply, the binary format produced is intended to model that +used by the Erlang and OCaml compilers respectively. + + +The Statepoint Example GC +------------------------- + +.. code-block:: c++ + + F.setGC("statepoint-example"); + +This GC provides an example of how one might use the infrastructure provided +by ''gc.statepoint''. + + Implementing a collector plugin ===============================