diff --git a/src/hotspot/share/ci/ciMethod.cpp b/src/hotspot/share/ci/ciMethod.cpp index 8c8739b19df..04336dd1ceb 100644 --- a/src/hotspot/share/ci/ciMethod.cpp +++ b/src/hotspot/share/ci/ciMethod.cpp @@ -86,6 +86,7 @@ ciMethod::ciMethod(const methodHandle& h_m, ciInstanceKlass* holder) : _max_locals = h_m->max_locals(); _code_size = h_m->code_size(); _handler_count = h_m->exception_table_length(); + _catch_handler_count= 0; _size_of_parameters = h_m->size_of_parameters(); _uses_monitors = h_m->has_monitor_bytecodes(); _balanced_monitors = !_uses_monitors || h_m->guaranteed_monitor_matching(); @@ -258,6 +259,9 @@ void ciMethod::load_code() { /* limit */ exc_table.end_pc(i), /* goto pc */ exc_table.handler_pc(i), /* cp index */ exc_table.catch_type_index(i)); + if (exc_table.catch_type_index(i) > 0) { + _catch_handler_count++; + } } } diff --git a/src/hotspot/share/ci/ciMethod.hpp b/src/hotspot/share/ci/ciMethod.hpp index 259cd998293..e019aada9c5 100644 --- a/src/hotspot/share/ci/ciMethod.hpp +++ b/src/hotspot/share/ci/ciMethod.hpp @@ -81,6 +81,7 @@ class ciMethod : public ciMetadata { int _max_locals; vmIntrinsicID _intrinsic_id; int _handler_count; + int _catch_handler_count; int _interpreter_invocation_count; int _interpreter_throwout_count; int _inline_instructions_size; @@ -193,6 +194,7 @@ class ciMethod : public ciMetadata { int interpreter_invocation_count() const { check_is_loaded(); return _interpreter_invocation_count; } int interpreter_throwout_count() const { check_is_loaded(); return _interpreter_throwout_count; } int size_of_parameters() const { check_is_loaded(); return _size_of_parameters; } + bool has_catch_exception_handlers() const { check_is_loaded(); return _catch_handler_count > 0; } // Code size for inlining decisions. int code_size_for_inlining(); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 6f9913ad889..336d56b8bc0 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -1033,6 +1033,7 @@ void Compile::Init(bool aliasing) { _fixed_slots = 0; set_has_split_ifs(false); set_has_loops(false); // first approximation + set_has_catch_exception_handlers(false); set_has_stringbuilder(false); set_has_boxed_value(false); _trap_can_recompile = false; // no traps emitted yet diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 03a8a232a75..5701379d470 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -328,6 +328,7 @@ class Compile : public Phase { bool _has_loops; // True if the method _may_ have some loops bool _has_split_ifs; // True if the method _may_ have some split-if bool _has_unsafe_access; // True if the method _may_ produce faults in unsafe loads or stores. + bool _has_catch_exception_handlers; // True if the method has catch exception handlers bool _has_stringbuilder; // True StringBuffers or StringBuilders are allocated bool _has_boxed_value; // True if a boxed object is allocated bool _has_reserved_stack_access; // True if the method or an inlined method is annotated with ReservedStackAccess @@ -637,6 +638,8 @@ class Compile : public Phase { void set_has_split_ifs(bool z) { _has_split_ifs = z; } bool has_unsafe_access() const { return _has_unsafe_access; } void set_has_unsafe_access(bool z) { _has_unsafe_access = z; } + bool has_catch_exception_handlers() const { return _has_catch_exception_handlers; } + void set_has_catch_exception_handlers(bool z) { _has_catch_exception_handlers = z; } bool has_stringbuilder() const { return _has_stringbuilder; } void set_has_stringbuilder(bool z) { _has_stringbuilder = z; } bool has_boxed_value() const { return _has_boxed_value; } diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 514155c6af8..b545a745370 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -77,6 +77,12 @@ ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn, int invocation } bool ConnectionGraph::has_candidates(Compile *C) { + // Skip EA if there are catch exception handlers in code. + // We can't handle correctly re-allocation and re-locking + // during deoptimization in such case. + if (C->has_catch_exception_handlers()) { + return false; + } // EA brings benefits only when the code has allocations and/or locks which // are represented by ideal Macro nodes. int cnt = C->macro_count(); diff --git a/src/hotspot/share/opto/parse1.cpp b/src/hotspot/share/opto/parse1.cpp index a40e92965b3..3808cd0e659 100644 --- a/src/hotspot/share/opto/parse1.cpp +++ b/src/hotspot/share/opto/parse1.cpp @@ -493,6 +493,10 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses) log->elem("observe that='has_exception_handlers'"); } + if (method()->has_catch_exception_handlers()) { + if (UseNewCode) C->set_has_catch_exception_handlers(true); + } + assert(InlineTree::check_can_parse(method()) == nullptr, "Can not parse this method, cutout earlier"); assert(method()->has_balanced_monitors(), "Can not parse unbalanced monitors, cutout earlier"); diff --git a/test/hotspot/jtreg/compiler/escapeAnalysis/TestReallocAtDeopt.java b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReallocAtDeopt.java new file mode 100644 index 00000000000..53c48aea8e4 --- /dev/null +++ b/test/hotspot/jtreg/compiler/escapeAnalysis/TestReallocAtDeopt.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2024, 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. + */ + +/* + * @test + * @bug 8325003 + * @summary During deoptimization 'catch(OOM ex)' can be incorrectly skipped when we failed reallocate scalarized object. + * @requires vm.compMode != "Xint" + * @run main/othervm -Xmx128M TestReallocAtDeopt + */ + +import java.util.ArrayList; + +public class TestReallocAtDeopt { + + static final int CHUNK = 1000; + static ArrayList arr = null; + static int cnt = 0; + + public static void main(String[] args) throws Error { + try { + test0(); + test(); + } catch (Error ex) { + arr = null; // Free memory + System.out.println("Test failed to catch exception: " + ex.getClass().getCanonicalName()); + throw(ex); // rethrow + } + } + + static void test0() { + arr = new ArrayList<>(); + try { + while (true) { + synchronized (TestReallocAtDeopt.class) { // nested lock eliminated + synchronized (new TestReallocAtDeopt()) { // lock eliminated - not escaped allocation + synchronized (TestReallocAtDeopt.class) { // nested lock eliminated + arr.add(new byte[CHUNK]); + } + } + } + } + } catch (OutOfMemoryError oom) { + arr = null; // Free memory + System.out.println("OOM caught in test0"); + } + } + + static void test() { + arr = new ArrayList<>(); + try { + while (true) { + test1(); + } + } catch (OutOfMemoryError oom) { + arr = null; // Free memory + System.out.println("OOM caught in test1"); + } + arr = new ArrayList<>(); + try { + while (true) { + test2(); + } + } catch (OutOfMemoryError oom) { + arr = null; // Free memory + System.out.println("OOM caught in test2"); + } + arr = new ArrayList<>(); + try { + while (true) { + test3(); + } + } catch (OutOfMemoryError oom) { + arr = null; // Free memory + System.out.println("OOM caught in test3"); + } + } + + // Nested locks in one method + static void test1() { // Nested lock in one method + synchronized (TestReallocAtDeopt.class) { + synchronized (new TestReallocAtDeopt()) { // lock eliminated - not escaped allocation + synchronized (TestReallocAtDeopt.class) { // nested lock eliminated + synchronized (new TestReallocAtDeopt()) { // lock eliminated - not escaped allocation + synchronized (TestReallocAtDeopt.class) { // nested lock eliminated + arr.add(new byte[CHUNK]); + } + } + } + } + } + } + + static void test2() { + synchronized (TestReallocAtDeopt.class) { + test1(); + } + } + + // Nested locks in inlined method + static void foo() { + synchronized (new TestReallocAtDeopt()) { // lock eliminated - not escaped allocation + synchronized (TestReallocAtDeopt.class) { // nested lock eliminated when inlined + arr.add(new byte[CHUNK]); + } + } + } + + static void test3() { + synchronized (TestReallocAtDeopt.class) { + synchronized (new TestReallocAtDeopt()) { // lock eliminated - not escaped allocation + synchronized (TestReallocAtDeopt.class) { // nested lock eliminated + foo(); // Inline + } + } + } + } +}