Add the experimental support for so called "blackholes", the methods that are specially treated as having one special effect, and one effect only: keeping the arguments alive. This is important for the reliable and precise nano-benchmarking. Blackholes are marked by explicit HotSpot JVM flag
The ability to avoid dead-code elimination is critical for benchmarking, see the linked RFE. JMH achieves this by providing a Java-based
Blackhole.consume method. It unfortunately comes with several major performance drawbacks: call costs dominate nanobenchmarks, the work spent in
Blackhole.consume dominates nanobenchmarks too, argument preparation for call makes different argument types behave differently. Compiler support for Blackholes is very useful for nano-benchmarks, as demonstrated by the experiments.
Current C1/C2 JIT compilers implementation is able to receive the "blackhole" command from the user, and inline the method with removing its body, but keeping the incoming arguments alive. The CSR-related question is how to communicate that command to JVM.
Current implementation reuses the
CompilerOracle facilities, available via
-XX:CompileCommand HotSpot JVM flag. This eliminates the need to specify the public Java API, and also limits the accessibility/exposure of the new feature. JMH is able to use this feature by explicitly enabling it with
-XX:CompileCommand=blackhole,org.openjdk.jmh.infra.Blackhole::consume. JMH already uses a lot of
CompileCommands to ask for unusual things from the Hotspot JVMs, like inline/dontinlne/exclude, etc.
The upside of exposing the functionality as a command-line option is it has much lower level of commitment associated with it. If there's enough motivation/justification to graduate it into public Java API, it can be done later when there's enough confidence that it's the right fit there. Not overexposing the blackhole support is the key to collect the data how it works or does not work, which has the important implications to any future API discussion. See the "Alternatives" for more discussion.
This CSR comes from two points:
CompileCommand is the
product flag. Therefore, the change introduces a new behavior to the externally available
product flag. Arguably, the spirit of the flag is
experimental, but it's "letter" is
product, due to historical reasons. The implementation requires
-XX:+UnlockExperimentalVMOptions to use
-XX:CompileCommand=blackhole,<method> can potentially change the semantics of the target method call by not doing the actual call. This is mitigated by only allowing already-resolved empty static methods to be blackholed. This way, even though the method body is technically not called, there is no effect anyway. The body of target method is empty, so no effects for non-existent user code can be observed either way. The method is static, which means its invocation would not involve the null-check for receiver. The method is resolved, which means its invocation would not trigger class loading and initialization.
Even with these limitations, the application code like JMH can use it, see CODETOOLS-7902813.
See a wider discussion here: https://mail.openjdk.java.net/pipermail/jdk-dev/2021-March/005239.html
Alternative 1. Intrinsify JMH methods directly. Unfortunately, this goes against the formal requirement that JVM only intrinsifies the "trusted" code from within the JDK, and thus intrinsifying 3rd-party code is unlikely to be accepted by VM teams. That said, this is the strategy that Graal uses today: https://github.com/oracle/graal/blob/744654c108178d531c99fc802f451b55b72a48c9/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java#L1492.
Alternative 2. Introduce
Unsafe.blackhole methods, and intrinsify them. Unfortunately, benchmark harnesses like JMH are the 3rd party code as far as JDK is concerned, which means using them requires linking to either
sun.misc.Unsafe (which would extend the surface of "public" Unsafe, that we are reluctant to do), or
jdk.internal.vm.Unsafe (which would require breaking in with elevated module system privileges).
Alternative 3. Introduce
blackhole methods in
Whitebox API. Unfortunately, the experience with Whitebox API in jcstress (which uses it to gain access for deoptimization) tells that linking against frequently changing
Whitebox is very tedious -- the interface there is too fluid. Additionally, this would require intrinsifying the
Whitebox methods themselves, which again raises the question about intrinsifying the methods formally outside of JDK (while there are native methods defined in JVM in ./src/hotspot/share/prims/whitebox.cpp, Whitebox Java entries are in ./test/lib/sun/hotspot/WhiteBox.java).
Alternative 4. Introduce public methods in the platform APIs. As stated in the Solution, this does not look appealing at this phase in compiler support work. Not overexposing the blackhole support allows wider testing with JMH, while retaining the right to retract it at any time if problems show up. Rushing to introduce the public API at this point would risk introducing the problematic API before understanding all the problems with its implementation.
There is no formal specification for
-XX:CompileCommand option, the change adds new sub-behavior in product macro-flag.