[15] RFR(S): 8244719: CTW: C2 compilation fails with "assert(!VerifyHashTableKeys || _hash_lock == 0) failed: remove node from hash table before modifying it"
Christian Hagedorn
christian.hagedorn at oracle.com
Fri Jun 5 15:58:59 UTC 2020
Hi
Please review the following patch:
https://bugs.openjdk.java.net/browse/JDK-8244719
http://cr.openjdk.java.net/~chagedorn/8244719/webrev.00/
The assertion failure at [5] can be traced back to a wrong assumption
made in Parse::Block::init_graph(). It explicitly states in a comment
there that we never call next_path_num() along exception paths [1]. But
it turns out that this is only true for bytecode generated by Javac
which does not seem to produce bytecode where an exception handler is
reached by an explicit jump or "fall through" bytecode. An exception
handler is only reached with an athrow.
However, it is possible to break that assumption with some custom
bytecode. The jasm testcase generates such a valid bytecode sequence
where an exception handler is reached by jumps from another exception
handler:
69: astore_1
70: aload_1
71: aload_0
72: getfield #5 // Field loopCounter:I
75: bipush 10
77: if_icmpge 93 // Explicit jump to exception handler, non-Javac
90: goto 93 // Explicit jump to exception handler, non-Javac
93: astore_1
94: return
Exception table:
from to target type
0 66 69 Class java/lang/RuntimeException
0 66 93 Class java/lang/Throwable
This means that the first time Parse::merge_exception() is called for
the exception handler block at bci 93, pnum is set to 3 since there are
2 predecessors (2 jumps to it). In the very first call to
merge_common(), is_merged() is still false and we record a state. All
following calls to merge_common() for this exception block will take the
else case [2]. Once we are processing the blocks for the exception
handler at bci 69, we call merge() (and therefore next_path_num()) in
do_one_block() [3] twice with target_bci = 93 (2 jumps to bci 93). The
last time with pnum = 1 for bci 90: goto and we transform the phi with
gvn and set the hash_lock for it to 1 at [4].
Now comes a second bytecode modification trick where we first hit a trap
while parsing a block in do_all_blocks(). Therefore, all successor
blocks on that path are not merged and skipped in the first iteration of
the loop in do_all_blocks() (at this point these blocks seem to be
dead). But later we can have a jump back to such a seemingly dead block
again. Those are then processed in the second iteration of the loop in
do_all_blocks(). If one of these blocks now additionally throw an
exception, we can hit this assertion failure. An example could look as
follows:
Example:
// First iteration in do_all_blocks()
Parse B1;
Parse B2; // Hit trap. Stop parsing on that path, skip on B3 and B4
which immediately follow B2 and have no other predecessors
Skip B3; // Was not merged. Assumed to be dead at this point
Skip B4; // Was not merged. Assumed to be dead at this point
Parse B5; // Discover jump to B3 -> merge B3. Will be processed but only
in the next iteration since rpo of B2 is smaller than the one of B5
Parse E1; // Parse exception handler 1 at bci 69
Parse E2; // Parse exception handler 2 at bci 93, apply gvn for phi
// Next iteration in do_all_blocks()
Parse B3; // Is now merged and ready to be parsed. Has exception to E2:
call merge_exception() -> merge_common() with E2 as target and pnum > 1.
We hit the assertion at [5] since we already applied a transformation
for a phi in the last iteration and therefore have a non-zero hash_lock.
As a solution to this problem, I suggest to fix the wrong assumption by
changing Parse::Block::init_graph() to also count predecessors for
exception blocks. This ensures that [4] is really the last merge for a phi.
I did some additional performance testing with standard benchmarks and
did not find any regressions.
Thank you!
Best regards,
Christian
[1]
http://hg.openjdk.java.net/jdk/jdk/file/71ec718a0bd0/src/hotspot/share/opto/parse1.cpp#l1314
[2]
http://hg.openjdk.java.net/jdk/jdk/file/71ec718a0bd0/src/hotspot/share/opto/parse1.cpp#l1678
[3]
http://hg.openjdk.java.net/jdk/jdk/file/71ec718a0bd0/src/hotspot/share/opto/parse1.cpp#l1508
[4]
http://hg.openjdk.java.net/jdk/jdk/file/71ec718a0bd0/src/hotspot/share/opto/parse1.cpp#l1773
[5]
http://hg.openjdk.java.net/jdk/jdk/file/71ec718a0bd0/src/hotspot/share/opto/parse1.cpp#l1764
More information about the hotspot-compiler-dev
mailing list