mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-27 13:40:43 +00:00
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
This commit is contained in:
parent
0749000e14
commit
a716933950
@ -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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user