You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
```rust
fn decode(compressed_oop: u32) -> u64 {
BASE + ((compressed_oop as u64) << SHIFT)
}
```
OpenJDK has a few optimizations to reduce the add and shift operations
in JIT-compiled code, this PR supports them all:
1. For heap <= 3G, if we set the heap range as
`0x4000_0000..0x1_0000_0000`, it is possible to totally remove the add
and the shift. The compressed and uncompressed forms are identical.
* Set `BASE = 0` and `SHIFT = 0` for this case.
2. For heap <= 31G, if we set the heap range as
`0x4000_0000..0x8_0000_0000`, it is possible to remove the add.
* Set `BASE = 0` and `SHIFT = 3` for this case.
3. For heap > 31G, the add and shift operation is still necessary.
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` or `SHIFT` 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 than `HEAP_START`,
otherwise `HEAP_START` will be encoded as `0` and be treated as a null
pointer.
Same as openjdk, we set `BASE` to `HEAP_START - 4096` to solve this
issue.
## Type specialization
Since we only support one edge type per binding, providing two
`OpenJDKEdge` in one `MMTK` 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 two `MMTK` singletons: `MMTK<OpenJDK<COMPRESSED = true>>` and
`MMTK<OpenJDK<COMPRESSED = false>>`. `MMTK<OpenJDK<COMPRESSED = true>>`
will have the `OpenJDKEdge<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 the `OpenJDKEdge::{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*` pointers
When `UseCompressedOops` is enabled, by default it also enables
`UseCompressedClassPointers`. This will make the `Klass*` pointer in the
object header compressed to a `u32` 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](http://squirrel.anu.edu.au/plotty-public/wenyuz/v8/p/mm26Ra)
[Immix](http://squirrel.anu.edu.au/plotty-public/wenyuz/v8/p/wEDPv4)
---------
Co-authored-by: Yi Lin <[email protected]>
0 commit comments