From a716933950c6c9bc8ca79ff77c312a9c2707d73c Mon Sep 17 00:00:00 2001 From: Philip Reames Date: Wed, 19 Apr 2017 23:16:13 +0000 Subject: [PATCH] Refresh the statepoint docs a bit The documentation had gotten a bit stale. The revised one are by no means perfect, but I tried to remove the obvious incorrect or misleading statements. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@300782 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/Statepoints.rst | 149 ++++++++++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 58 deletions(-) diff --git a/docs/Statepoints.rst b/docs/Statepoints.rst index 7f2b2054481..28546c78a02 100644 --- a/docs/Statepoints.rst +++ b/docs/Statepoints.rst @@ -9,15 +9,22 @@ Garbage Collection Safepoints in LLVM Status ======= -This document describes a set of experimental extensions to LLVM. Use -with caution. Because the intrinsics have experimental status, -compatibility across LLVM releases is not guaranteed. +This document describes a set of extensions to LLVM to support garbage +collection. By now, these mechanisms are well proven with commercial java +implementation with a fully relocating collector having shipped using them. +There are a couple places where bugs might still linger; these are called out +below. -LLVM currently supports an alternate mechanism for conservative -garbage collection support using the ``gcroot`` intrinsic. The mechanism -described here shares little in common with the alternate ``gcroot`` -implementation and it is hoped that this mechanism will eventually -replace the gc_root mechanism. +They are still listed as "experimental" to indicate that no forward or backward +compatibility guarantees are offered across versions. If your use case is such +that you need some form of forward compatibility guarantee, please raise the +issue on the llvm-dev mailing list. + +LLVM still supports an alternate mechanism for conservative garbage collection +support using the ``gcroot`` intrinsic. The ``gcroot`` mechanism is mostly of +historical interest at this point with one exception - it's implementation of +shadow stacks has been used successfully by a number of language frontends and +is still supported. Overview ======== @@ -86,9 +93,36 @@ the collector must be able to: This document describes the mechanism by which an LLVM based compiler can provide this information to a language runtime/collector, and -ensure that all pointers can be read and updated if desired. The -heart of the approach is to construct (or rewrite) the IR in a manner -where the possible updates performed by the garbage collector are +ensure that all pointers can be read and updated if desired. + +At a high level, LLVM has been extended to support compiling to an abstract +machine which extends the actual target with a non-integral pointer type +suitable for representing a garbage collected reference to an object. In +particular, such non-integral pointer type have no defined mapping to an +integer representation. This semantic quirk allows the runtime to pick a +integer mapping for each point in the program allowing relocations of objects +without visible effects. + +Warning: Non-Integral Pointer Types are a newly added concept in LLVM IR. +It's possible that we've missed disabling some of the optimizations which +assume an integral value for pointers. If you find such a case, please +file a bug or share a patch. + +Warning: There is one currently known semantic hole in the definition of +non-integral pointers which has not been addressed upstream. To work around +this, you need to disable speculation of loads unless the memory type +(non-integral pointer vs anything else) is known to unchanged. That is, it is +not safe to speculate a load if doing causes a non-integral pointer value to +be loaded as any other type or vice versa. In practice, this restriction is +well isolated to isSafeToSpeculate in ValueTracking.cpp. + +This high level abstract machine model is used for most of the LLVM optimizer. +Before starting code generation, we switch representations to an explicit form. +In theory, a frontend could directly generate this low level explicit form, but +doing so is likely to inhibit optimization. + +The heart of the explicit approach is to construct (or rewrite) the IR in a +manner where the possible updates performed by the garbage collector are explicitly visible in the IR. Doing so requires that we: #. create a new SSA value for each potentially relocated pointer, and @@ -654,11 +688,15 @@ Utility Passes for Safepoint Insertion RewriteStatepointsForGC ^^^^^^^^^^^^^^^^^^^^^^^^ -The pass RewriteStatepointsForGC transforms a functions IR by replacing a -``gc.statepoint`` (with an optional ``gc.result``) with a full relocation -sequence, including all required ``gc.relocates``. To function, the pass -requires that the GC strategy specified for the function be able to reliably -distinguish between GC references and non-GC references in IR it is given. +The pass RewriteStatepointsForGC transforms a function's IR to lower from the +abstract machine model described above to the explicit statepoint model of +relocations. To do this, it replaces all calls or invokes of functions which +might contain a safepoint poll with a ``gc.statepoint`` and associated full +relocation sequence, including all required ``gc.relocates``. + +Note that by default, this pass only runs for the "statepoint-example" or +"core-clr" gc strategies. You will need to add your custom strategy to this +whitelist or use one of the predefined ones. As an example, given this code: @@ -666,7 +704,7 @@ As an example, given this code: define i8 addrspace(1)* @test1(i8 addrspace(1)* %obj) gc "statepoint-example" { - call token (i64, i32, void ()*, i32, i32, ...)* @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0) + call void @foo() ret i8 addrspace(1)* %obj } @@ -683,7 +721,8 @@ The pass would produce this IR: In the above examples, the addrspace(1) marker on the pointers is the mechanism that the ``statepoint-example`` GC strategy uses to distinguish references from -non references. Address space 1 is not globally reserved for this purpose. +non references. The pass assumes that all addrspace(1) pointers are non-integral +pointer types. Address space 1 is not globally reserved for this purpose. This pass can be used an utility function by a language frontend that doesn't want to manually reason about liveness, base pointers, or relocation when @@ -701,23 +740,34 @@ can be relaxed to producing interior derived pointers provided the target collector can find the associated allocation from an arbitrary interior derived pointer. -In practice, RewriteStatepointsForGC can be run much later in the pass +By default RewriteStatepointsForGC passes in ``0xABCDEF00`` as the statepoint +ID and ``0`` as the number of patchable bytes to the newly constructed +``gc.statepoint``. These values can be configured on a per-callsite +basis using the attributes ``"statepoint-id"`` and +``"statepoint-num-patch-bytes"``. If a call site is marked with a +``"statepoint-id"`` function attribute and its value is a positive +integer (represented as a string), then that value is used as the ID +of the newly constructed ``gc.statepoint``. If a call site is marked +with a ``"statepoint-num-patch-bytes"`` function attribute and its +value is a positive integer, then that value is used as the 'num patch +bytes' parameter of the newly constructed ``gc.statepoint``. The +``"statepoint-id"`` and ``"statepoint-num-patch-bytes"`` attributes +are not propagated to the ``gc.statepoint`` call or invoke if they +could be successfully parsed. + +In practice, RewriteStatepointsForGC should be run much later in the pass pipeline, after most optimization is already done. This helps to improve the quality of the generated code when compiled with garbage collection support. -In the long run, this is the intended usage model. At this time, a few details -have yet to be worked out about the semantic model required to guarantee this -is always correct. As such, please use with caution and report bugs. .. _PlaceSafepoints: PlaceSafepoints ^^^^^^^^^^^^^^^^ -The pass PlaceSafepoints transforms a function's IR by replacing any call or -invoke instructions with appropriate ``gc.statepoint`` and ``gc.result`` pairs, -and inserting safepoint polls sufficient to ensure running code checks for a -safepoint request on a timely manner. This pass is expected to be run before -RewriteStatepointsForGC and thus does not produce full relocation sequences. +The pass PlaceSafepoints inserts safepoint polls sufficient to ensure running +code checks for a safepoint request on a timely manner. This pass is expected +to be run before RewriteStatepointsForGC and thus does not produce full +relocation sequences. As an example, given input IR of the following: @@ -740,13 +790,16 @@ This pass would produce the following IR: .. code-block:: text define void @test() gc "statepoint-example" { - %safepoint_token = call token (i64, i32, void ()*, i32, i32, ...)* @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @do_safepoint, i32 0, i32 0, i32 0, i32 0) - %safepoint_token1 = call token (i64, i32, void ()*, i32, i32, ...)* @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @foo, i32 0, i32 0, i32 0, i32 0) + call void @do_safepoint() + call void @foo() ret void } -In this case, we've added an (unconditional) entry safepoint poll and converted the call into a ``gc.statepoint``. Note that despite appearances, the entry poll is not necessarily redundant. We'd have to know that ``foo`` and ``test`` were not mutually recursive for the poll to be redundant. In practice, you'd probably want to your poll definition to contain a conditional branch of some form. - +In this case, we've added an (unconditional) entry safepoint poll. Note that +despite appearances, the entry poll is not necessarily redundant. We'd have to +know that ``foo`` and ``test`` were not mutually recursive for the poll to be +redundant. In practice, you'd probably want to your poll definition to contain +a conditional branch of some form. At the moment, PlaceSafepoints can insert safepoint polls at method entry and loop backedges locations. Extending this to work with return polls would be @@ -763,26 +816,13 @@ of this function is inserted at each poll site desired. While calls or invokes inside this method are transformed to a ``gc.statepoints``, recursive poll insertion is not performed. -By default PlaceSafepoints passes in ``0xABCDEF00`` as the statepoint -ID and ``0`` as the number of patchable bytes to the newly constructed -``gc.statepoint``. These values can be configured on a per-callsite -basis using the attributes ``"statepoint-id"`` and -``"statepoint-num-patch-bytes"``. If a call site is marked with a -``"statepoint-id"`` function attribute and its value is a positive -integer (represented as a string), then that value is used as the ID -of the newly constructed ``gc.statepoint``. If a call site is marked -with a ``"statepoint-num-patch-bytes"`` function attribute and its -value is a positive integer, then that value is used as the 'num patch -bytes' parameter of the newly constructed ``gc.statepoint``. The -``"statepoint-id"`` and ``"statepoint-num-patch-bytes"`` attributes -are not propagated to the ``gc.statepoint`` call or invoke if they -could be successfully parsed. - -If you are scheduling the RewriteStatepointsForGC pass late in the pass order, -you should probably schedule this pass immediately before it. The exception -would be if you need to preserve abstract frame information (e.g. for -deoptimization or introspection) at safepoints. In that case, ask on the -llvm-dev mailing list for suggestions. +This pass is useful for any language frontend which only has to support +garbage collection semantics at safepoints. If you need other abstract +frame information at safepoints (e.g. for deoptimization or introspection), +you can insert safepoint polls in the frontend. If you have the later case, +please ask on llvm-dev for suggestions. There's been a good amount of work +done on making such a scheme work well in practice which is not yet documented +here. Supported Architectures @@ -794,13 +834,6 @@ Today, only X86_64 is supported. Problem Areas and Active Work ============================= -#. As the existing users of the late rewriting model have matured, we've found - cases where the optimizer breaks the assumption that an SSA value of - gc-pointer type actually contains a gc-pointer and vice-versa. We need to - clarify our expectations and propose at least one small IR change. (Today, - the gc-pointer distinction is managed via address spaces. This turns out - not to be quite strong enough.) - #. Support for languages which allow unmanaged pointers to garbage collected objects (i.e. pass a pointer to an object to a C routine) via pinning.