Hi all, 

This PR fixes [JDK-8370479](https://bugs.openjdk.org/browse/JDK-8370479), as we 
currently don't emit barriers correctly. Tested tiers 1-3.

## Context

If we consider a value class/record that contains a single field, which is a 
reference to an identity object:

public static value record Element(Identity underlying) {}
public static class Identity {}

We can create a flattened array via the JDK-internal `ValueClass` API:

Object[] array = ValueClass.newNullableAtomicArray(Element.class, 16);

This will indeed be flattened when running with compressed oops. T the 
reference to `underlying` will be four bytes, and the null-marker an additional 
byte. Hence, we are below the 64-bit limit. Copying this array via 
`Arrays.copyOf` will trigger Valhalla-specific copying.  

When running with G1, there are various crashes and verification errors. This 
should not impact ZGC, as the pointers are too large to be flattened in a 
nullable array.

## New Barrier Emission

We do not emit a post-write barrier when copying to an uninitialized memory 
destination. The tables below summarize what barriers, if any, are emitted both 
in the old and new versions of the copy implementation. Note that Serial, 
Parallel and G1 have the notion of post-write barriers to track 
intergenerational references. G1 is the only GC requiring pre-write barriers. 

**Old G1 barrier emission during flat array copying:**
|  | oopless | contains oops |
| --- | --- | --- |
| uninitialized | | |
| initialized | | pre, post |

**New G1 barrier emission during flat array copying:**
| | oopless | contains oops |
| --- | --- | --- |
| uninitialized | | post |
| initialized | | pre, post |

As mentioned, when copying to uninitialized memory, a cross-generational could 
be "lost" due to the lack of a post-write barrier. We should *not* use a 
pre-write barrier when copying to uninitialized memory when running with G1. 
Doing so means we may get garbage in our SATB buffers.

## New Test Cases

I introduce a test scenario where we grow a flat array similar to how one would 
grow an `ArrayList`. This should generate plenty of garbage, and triggers this 
crash even without the whitebox GC. I test the three GCs that use 
`ModRefBarrierSet`: Serial, Parallel and G1. These are tweaked to be less 
concurrent/parallel to aid with reproducability in case of crashes.

## Fixed Oop Printing

When G1 verification fails, it tries to print diagnostic information. This will 
eventually end up printing oops. We handle the case of `String` oops specially, 
and for that we need to check the klass. However, in this failed verification 
state, we can't guarantee that the class isn't garbage (either through a race 
or literal garbage). While debugging this issue, I ran into a scenario where 
the klass does not pass assertion. Consequently, we crash before the helpful 
diagnostic error messages finish printing. I've introduced a 
`klass_without_asserts` version of the string check, intended to be used only 
for diagnostics, which will perform the `String` check even if the VM is 
metaphorically on fire after a failed GC. That way, G1 is able to finish 
printing what it wants to print.

-------------

Commit messages:
 - Rework the G1 barrier model with flat oops.
 - Improve flat array copying test.
 - Clean up test.
 - Add test.
 - Update Access API to not check uninitialized memory.
 - Fix oop error printing bug.

Changes: https://git.openjdk.org/valhalla/pull/1713/files
  Webrev: https://webrevs.openjdk.org/?repo=valhalla&pr=1713&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8370479
  Stats: 131 lines in 5 files changed: 128 ins; 0 del; 3 mod
  Patch: https://git.openjdk.org/valhalla/pull/1713.diff
  Fetch: git fetch https://git.openjdk.org/valhalla.git pull/1713/head:pull/1713

PR: https://git.openjdk.org/valhalla/pull/1713

Reply via email to