-
Notifications
You must be signed in to change notification settings - Fork 33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compressed Oops Support #235
Conversation
script to check it.
The PR is ready for review. Sometimes the CI job |
These are the runs that failed. Error messages are: The hosted runner: GitHub Actions 3 lost communication with the server. Anything in your workflow that terminates the runner process, starves it for CPU/Memory, or blocks its network access can cause this error. The runner has received a shutdown signal. This can happen when the runner service is stopped, or a manually started runner is canceled. If this only happened in a certain period of time, it simply could be a Github issue. |
The runner has received a shutdown signal. This can happen when the runner service is stopped, or a manually started runner is canceled. There are many discussions about this error for Github runners. It usually means the resources consumed in the process exceeded the limits. It could be related with the code change in the PR (if it consumes more memory or more CPU). It could also be changes in the Github runner specs. The simplest thing to try is to create another PR without any code change, and see if we see this error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
I also left a comment, but it may be too pedantic.
This PR adds compressed oops support for mmtk-openjdk, and enables it by default.
Implementation strategy and workarounds
Heap layout and compression policy
This PR uses the
Edge
type to abstract over the compressed edge and uncompressed edge. Object field loads and stores for uncompressed edges work as before. Loads and stores for compressed edges will involve an additional compression and decompression step.In general, this is the function to decode a 32-bit compressed pointer to its uncompressed form:
OpenJDK has a few optimizations to reduce the add and shift operations in JIT-compiled code, this PR supports them all:
0x4000_0000..0x1_0000_0000
, it is possible to totally remove the add and the shift. The compressed and uncompressed forms are identical.BASE = 0
andSHIFT = 0
for this case.0x4000_0000..0x8_0000_0000
, it is possible to remove the add.BASE = 0
andSHIFT = 3
for this case.For cases (1) and (2), the jit compiled code will contain less or even no encoding/decoding instructions, and hence improve the mutator performance. However, in Rust code, we still do the add and shift unconditionally, even when
BASE
orSHIFT
is set to zero.NULL pointer checking
Generally,
BASE
can be any address as long as the memory is not reserved by others. However,BASE
must be smaller thanHEAP_START
, otherwiseHEAP_START
will be encoded as0
and be treated as a null pointer.Same as openjdk, we set
BASE
toHEAP_START - 4096
to solve this issue.Type specialization
Since we only support one edge type per binding, providing two
OpenJDKEdge
in oneMMTK
instance is not possible.This PR solves the issue by specializing almost all the types in the binding, with a
const COMPRESSED: bool
generic type argument. It provides twoMMTK
singletons:MMTK<OpenJDK<COMPRESSED = true>>
andMMTK<OpenJDK<COMPRESSED = false>>
.MMTK<OpenJDK<COMPRESSED = true>>
will have theOpenJDKEdge<COMPRESSED = true>
edge that does the extra pointer compression/decompression.The two MMTK singletons are wrapped in two lazy_static global variables. The binding will only initialize one of them depending on the OpenJDK command-line arguments. Initializing the wrong one that does not match the
UseCompressedOops
flag will trigger an assertion failure.Pointer tagging
When compressed oops is enabled, all the fields are guaranteed to be compressed oops. However, stack or other global root pointers may be still uncompressed. The GC needs to handle both compressed and uncompressed edges and be able to distinguish between them.
To support this, this PR treats all the root
OpenJDKEdge<COMPRESSED = true>
s as tagged pointers. If the 63-th bit is set, this indicates that this edge points to a 64-nit uncompressed oop, instead of a compressed oop. And theOpenJDKEdge::{load, store}
methods will skip the encoding/decoding step.For object field edges, the encoding is performed unconditionally without the pointer tag check.
When compressed oops is disabled, there is no pointer tag check as well.
Embedded pointers
Some (or probably all) pointers embedded in code objects are also compressed. On x64, it is always compressed to a
u32
integer that sits in an unaligned memory location. This means we need to (1) treat them as compressed oops just like other roots. (2) still performs the unaligned stores and loads.However, for other architectures, the compressed embedded pointers may not be encoded as a
u32
anymore.Compressed
Klass*
pointersWhen
UseCompressedOops
is enabled, by default it also enablesUseCompressedClassPointers
. This will make theKlass*
pointer in the object header compressed to au32
as well. This PR supports class pointer compression as well.However, class pointer compression is only supported and tested when the compressed oops is enabled. The two flags must be enabled or disabled together. Enabling only one of them is not tested, not supported, and will trigger a runtime assertion failure.
Performance results
SemiSpace
Immix