RFR: 8350208: CTW: GraphKit::add_safepoint_edges asserts "not enough operands for reexecution"

Quan Anh Mai qamai at openjdk.org
Wed Dec 3 10:24:50 UTC 2025


On Wed, 3 Dec 2025 03:36:13 GMT, Dean Long <dlong at openjdk.org> wrote:

>> Hi,
>> 
>> This PR fixes the issue of the compiler crashing with "not enough operands for reexecution". The issue here is that during `Parse::catch_inline_exceptions`, the old stack is gone, and we cannot reexecute the current bytecode anymore. However, there are some places where we try to insert safepoints into the graph, such as if the handler is a backward jump, or if one of the exceptions in the handlers is not loaded. Since the `_reexecute` state of the current jvms is "undefined", it is inferred automatically that it should reexecute for some bytecodes such as `putfield`. The solution then is to explicitly set `_reexecute` to false.
>> 
>> I can manage to write a unit test for the case of a backward handler, for the other cases, since the exceptions that can be thrown for a bytecode that is inferred to reexecute are `NullPointerException`, `ArrayIndexOutOfBoundsException`, and `ArrayStoreException`. I find it hard to construct such a test in which one of them is not loaded.
>> 
>> Please kindly review, thanks a lot.
>
> src/hotspot/share/opto/doCall.cpp line 958:
> 
>> 956:   ex_node = use_exception_state(ex_map);
>> 957:   // The stack from before the throwing bytecode is gone, cannot reexecute here
>> 958:   jvms()->set_should_reexecute(false);
> 
> I agree there are situations where we need to set the reexecute flag explicitly and not base it on the bytecode.  I recently fixed JDK-8370766 and filed JDK-8372846 as a followup for similar issues.  I need to try out your test to understand this better.  Does it cause a backwards-branch safepoint?  I suspect that it may not be safe to set rexeecute to false here.  If reexecute is false and -XX:+VerifyStack is set, deoptimization may fail if the operands are not on the stack.

Yes, it is a backwards-branch safepoint.

Tbh, after looking deeper, I don't really understand what is happening here. I modified the test a little bit so the final compiled code does not elide the safepoint in the loop, and ran with `-XX:+VerifyStack -XX:+DeoptimizeALot -XX:+SafepointALot`, but the test still passed after 100 repeats. I think that the state is correct, but I don't see how the compiled code notifies the deoptimizater and the interpreter that it is in an exception state, and the interpreter needs to find an exception handler instead of continuing with the next bytecode. My guess is that the compiled code should store the exception into `Thread::_pending_exception`, or the deoptimizer needs to do so, and the interpreter needs to check that when being handed the control. But I have not yet found that.

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

PR Review Comment: https://git.openjdk.org/jdk/pull/28597#discussion_r2584505420


More information about the hotspot-compiler-dev mailing list