mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-27 05:42:24 +00:00
8350208: CTW: GraphKit::add_safepoint_edges asserts "not enough operands for reexecution"
Co-authored-by: Quan Anh Mai <qamai@openjdk.org> Reviewed-by: mhaessig, vlivanov
This commit is contained in:
parent
95e90299b4
commit
4253db2252
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -37,6 +37,7 @@
|
||||
#include "opto/callGenerator.hpp"
|
||||
#include "opto/castnode.hpp"
|
||||
#include "opto/cfgnode.hpp"
|
||||
#include "opto/graphKit.hpp"
|
||||
#include "opto/mulnode.hpp"
|
||||
#include "opto/parse.hpp"
|
||||
#include "opto/rootnode.hpp"
|
||||
@ -909,8 +910,7 @@ void Parse::catch_call_exceptions(ciExceptionHandlerStream& handlers) {
|
||||
if (handler_bci < 0) { // merge with corresponding rethrow node
|
||||
throw_to_exit(make_exception_state(ex_oop));
|
||||
} else { // Else jump to corresponding handle
|
||||
push_ex_oop(ex_oop); // Clear stack and push just the oop.
|
||||
merge_exception(handler_bci);
|
||||
push_and_merge_exception(handler_bci, ex_oop);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1008,13 +1008,10 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) {
|
||||
int handler_bci = handler->handler_bci();
|
||||
|
||||
if (remaining == 1) {
|
||||
push_ex_oop(ex_node); // Push exception oop for handler
|
||||
if (PrintOpto && WizardMode) {
|
||||
tty->print_cr(" Catching every inline exception bci:%d -> handler_bci:%d", bci(), handler_bci);
|
||||
}
|
||||
// If this is a backwards branch in the bytecodes, add safepoint
|
||||
maybe_add_safepoint(handler_bci);
|
||||
merge_exception(handler_bci); // jump to handler
|
||||
push_and_merge_exception(handler_bci, ex_node); // jump to handler
|
||||
return; // No more handling to be done here!
|
||||
}
|
||||
|
||||
@ -1039,15 +1036,13 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) {
|
||||
const TypeInstPtr* tinst = TypeOopPtr::make_from_klass_unique(klass)->cast_to_ptr_type(TypePtr::NotNull)->is_instptr();
|
||||
assert(klass->has_subklass() || tinst->klass_is_exact(), "lost exactness");
|
||||
Node* ex_oop = _gvn.transform(new CheckCastPPNode(control(), ex_node, tinst));
|
||||
push_ex_oop(ex_oop); // Push exception oop for handler
|
||||
if (PrintOpto && WizardMode) {
|
||||
tty->print(" Catching inline exception bci:%d -> handler_bci:%d -- ", bci(), handler_bci);
|
||||
klass->print_name();
|
||||
tty->cr();
|
||||
}
|
||||
// If this is a backwards branch in the bytecodes, add safepoint
|
||||
maybe_add_safepoint(handler_bci);
|
||||
merge_exception(handler_bci);
|
||||
push_and_merge_exception(handler_bci, ex_oop);
|
||||
}
|
||||
set_control(not_subtype_ctrl);
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -474,8 +474,8 @@ class Parse : public GraphKit {
|
||||
void merge( int target_bci);
|
||||
// Same as plain merge, except that it allocates a new path number.
|
||||
void merge_new_path( int target_bci);
|
||||
// Merge the current mapping into an exception handler.
|
||||
void merge_exception(int target_bci);
|
||||
// Push the exception oop and merge the current mapping into an exception handler.
|
||||
void push_and_merge_exception(int target_bci, Node* ex_oop);
|
||||
// Helper: Merge the current mapping into the given basic block
|
||||
void merge_common(Block* target, int pnum);
|
||||
// Helper functions for merging individual cells.
|
||||
|
||||
@ -1651,9 +1651,14 @@ void Parse::merge_new_path(int target_bci) {
|
||||
}
|
||||
|
||||
//-------------------------merge_exception-------------------------------------
|
||||
// Merge the current mapping into the basic block starting at bci
|
||||
// The ex_oop must be pushed on the stack, unlike throw_to_exit.
|
||||
void Parse::merge_exception(int target_bci) {
|
||||
// Push the given ex_oop onto the stack, then merge the current mapping into
|
||||
// the basic block starting at target_bci.
|
||||
void Parse::push_and_merge_exception(int target_bci, Node* ex_oop) {
|
||||
// Add the safepoint before trimming the stack and pushing the exception oop.
|
||||
// We could add the safepoint after, but then the bci would also need to be
|
||||
// advanced to target_bci first, so the stack state matches.
|
||||
maybe_add_safepoint(target_bci);
|
||||
push_ex_oop(ex_oop); // Push exception oop for handler
|
||||
#ifdef ASSERT
|
||||
if (target_bci <= bci()) {
|
||||
C->set_exception_backedge();
|
||||
|
||||
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package compiler.exceptions;
|
||||
|
||||
import compiler.lib.ir_framework.Run;
|
||||
import compiler.lib.ir_framework.Test;
|
||||
import compiler.lib.ir_framework.TestFramework;
|
||||
|
||||
import java.lang.classfile.Label;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8350208
|
||||
* @summary Safepoints added during the processing of exception handlers should never reexecute
|
||||
* @library /test/lib /test/jdk/java/lang/invoke/common /
|
||||
* @build test.java.lang.invoke.lib.InstructionHelper
|
||||
*
|
||||
* @run main/othervm ${test.main.class}
|
||||
*/
|
||||
public class TestDebugDuringExceptionCatching {
|
||||
|
||||
public static class V {
|
||||
int v;
|
||||
}
|
||||
|
||||
static final int ITERATIONS = 100;
|
||||
static final RuntimeException EXCEPTION = new RuntimeException();
|
||||
|
||||
/**
|
||||
* Construct something that looks like this:
|
||||
* <pre>{@code
|
||||
* int snippet(V v) {
|
||||
* int i = 0;
|
||||
* LoopHead: {
|
||||
* if (i >= 100) {
|
||||
* goto LoopEnd;
|
||||
* }
|
||||
* i++;
|
||||
* try {
|
||||
* v.v = 1;
|
||||
* } catch (Throwable) {
|
||||
* // Not really, the LoopHead is the exception Handler
|
||||
* goto LoopHead;
|
||||
* }
|
||||
* }
|
||||
* LoopEnd:
|
||||
* return i;
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
static final MethodHandle SNIPPET_HANDLE;
|
||||
static final ClassDesc CLASS_DESC = TestDebugDuringExceptionCatching.class.describeConstable().get();
|
||||
static {
|
||||
SNIPPET_HANDLE = InstructionHelper.buildMethodHandle(MethodHandles.lookup(),
|
||||
"snippet",
|
||||
MethodType.methodType(int.class, V.class),
|
||||
CODE -> {
|
||||
Label loopHead = CODE.newLabel();
|
||||
Label loopEnd = CODE.newLabel();
|
||||
Label tryStart = CODE.newLabel();
|
||||
Label tryEnd = CODE.newLabel();
|
||||
CODE.
|
||||
iconst_0().
|
||||
istore(1).
|
||||
// The loop head should have a RuntimeException as the sole element on the stack
|
||||
getstatic(CLASS_DESC, "EXCEPTION", RuntimeException.class.describeConstable().get()).
|
||||
labelBinding(loopHead).
|
||||
pop().
|
||||
iload(1).
|
||||
ldc(ITERATIONS).
|
||||
if_icmpge(loopEnd).
|
||||
iinc(1, 1).
|
||||
aload(0).
|
||||
iconst_1().
|
||||
labelBinding(tryStart).
|
||||
putfield(V.class.describeConstable().get(), "v", int.class.describeConstable().get()).
|
||||
labelBinding(tryEnd).
|
||||
// The stack is empty here
|
||||
labelBinding(loopEnd).
|
||||
iload(1).
|
||||
ireturn();
|
||||
CODE.exceptionCatchAll(tryStart, tryEnd, loopHead);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
private static int testBackwardHandler(V v) throws Throwable {
|
||||
return (int) SNIPPET_HANDLE.invokeExact(v);
|
||||
}
|
||||
|
||||
@Run(test = "testBackwardHandler")
|
||||
public void run() throws Throwable {
|
||||
Asserts.assertEQ(ITERATIONS, testBackwardHandler(null));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2010, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,6 +25,7 @@ package test.java.lang.invoke.lib;
|
||||
|
||||
import java.lang.classfile.ClassBuilder;
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.classfile.CodeBuilder;
|
||||
import java.lang.classfile.TypeKind;
|
||||
|
||||
import java.lang.constant.*;
|
||||
@ -32,6 +33,7 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.lang.invoke.MethodType.fromMethodDescriptorString;
|
||||
|
||||
@ -135,4 +137,25 @@ public class InstructionHelper {
|
||||
String classDescStr = sb.insert(sb.length() - 1, suffix).toString();
|
||||
return ClassDesc.ofDescriptor(classDescStr);
|
||||
}
|
||||
|
||||
public static MethodHandle buildMethodHandle(MethodHandles.Lookup l, String methodName, MethodType methodType, Consumer<? super CodeBuilder> builder) {
|
||||
ClassDesc genClassDesc = classDesc(l.lookupClass(), "$Code_" + COUNT.getAndIncrement());
|
||||
return buildMethodHandle(l, genClassDesc, methodName, methodType, builder);
|
||||
}
|
||||
|
||||
private static MethodHandle buildMethodHandle(MethodHandles.Lookup l, ClassDesc classDesc, String methodName, MethodType methodType, Consumer<? super CodeBuilder> builder) {
|
||||
try {
|
||||
byte[] bytes = ClassFile.of().build(classDesc, classBuilder -> {
|
||||
classBuilder.withMethod(methodName,
|
||||
MethodTypeDesc.ofDescriptor(methodType.toMethodDescriptorString()),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC,
|
||||
methodBuilder -> methodBuilder.withCode(builder));
|
||||
});
|
||||
Class<?> clazz = l.defineClass(bytes);
|
||||
return l.findStatic(clazz, methodName, methodType);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
throw new RuntimeException("Failed to buildMethodHandle: " + methodName + " type " + methodType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user