From d32f99ee65679601d6e160e7975fc1e367bfa6f4 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Wed, 2 Feb 2022 07:34:22 +0000 Subject: [PATCH] 8279219: [REDO] C2 crash when allocating array of size too large Reviewed-by: thartmann, neliasso --- src/hotspot/share/opto/callnode.cpp | 49 +----------- src/hotspot/share/opto/callnode.hpp | 10 +-- src/hotspot/share/opto/cfgnode.cpp | 11 +++ src/hotspot/share/opto/compile.cpp | 29 ++++--- src/hotspot/share/opto/graphKit.cpp | 16 +++- src/hotspot/share/opto/macro.cpp | 14 +++- src/hotspot/share/opto/macro.hpp | 4 +- src/hotspot/share/opto/split_if.cpp | 13 +-- .../TestAllocArrayAfterAllocNoUse.java | 52 ++++++++++++ .../allocation/TestCCPAllocateArray.java | 53 +++++++++++++ .../TestFailedAllocationBadGraph.java | 79 +++++++++++++++++++ 11 files changed, 254 insertions(+), 76 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/allocation/TestAllocArrayAfterAllocNoUse.java create mode 100644 test/hotspot/jtreg/compiler/allocation/TestCCPAllocateArray.java create mode 100644 test/hotspot/jtreg/compiler/allocation/TestFailedAllocationBadGraph.java diff --git a/src/hotspot/share/opto/callnode.cpp b/src/hotspot/share/opto/callnode.cpp index 7345f49cbf1..b4cf07cfbbb 100644 --- a/src/hotspot/share/opto/callnode.cpp +++ b/src/hotspot/share/opto/callnode.cpp @@ -1656,6 +1656,7 @@ AllocateNode::AllocateNode(Compile* C, const TypeFunc *atype, init_req( KlassNode , klass_node); init_req( InitialTest , initial_test); init_req( ALength , topnode); + init_req( ValidLengthTest , topnode); C->add_macro_node(this); } @@ -1682,54 +1683,6 @@ Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, N return mark_node; } -//============================================================================= -Node* AllocateArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) { - if (remove_dead_region(phase, can_reshape)) return this; - // Don't bother trying to transform a dead node - if (in(0) && in(0)->is_top()) return NULL; - - const Type* type = phase->type(Ideal_length()); - if (type->isa_int() && type->is_int()->_hi < 0) { - if (can_reshape) { - PhaseIterGVN *igvn = phase->is_IterGVN(); - // Unreachable fall through path (negative array length), - // the allocation can only throw so disconnect it. - Node* proj = proj_out_or_null(TypeFunc::Control); - Node* catchproj = NULL; - if (proj != NULL) { - for (DUIterator_Fast imax, i = proj->fast_outs(imax); i < imax; i++) { - Node *cn = proj->fast_out(i); - if (cn->is_Catch()) { - catchproj = cn->as_Multi()->proj_out_or_null(CatchProjNode::fall_through_index); - break; - } - } - } - if (catchproj != NULL && catchproj->outcnt() > 0 && - (catchproj->outcnt() > 1 || - catchproj->unique_out()->Opcode() != Op_Halt)) { - assert(catchproj->is_CatchProj(), "must be a CatchProjNode"); - Node* nproj = catchproj->clone(); - igvn->register_new_node_with_optimizer(nproj); - - Node *frame = new ParmNode( phase->C->start(), TypeFunc::FramePtr ); - frame = phase->transform(frame); - // Halt & Catch Fire - Node* halt = new HaltNode(nproj, frame, "unexpected negative array length"); - phase->C->root()->add_req(halt); - phase->transform(halt); - - igvn->replace_node(catchproj, phase->C->top()); - return this; - } - } else { - // Can't correct it during regular GVN so register for IGVN - phase->C->record_for_igvn(this); - } - } - return NULL; -} - // Retrieve the length from the AllocateArrayNode. Narrow the type with a // CastII, if appropriate. If we are not allowed to create new nodes, and // a CastII is appropriate, return NULL. diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index 7c8baefbca9..1017a76a25e 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -913,6 +913,7 @@ public: KlassNode, // type (maybe dynamic) of the obj. InitialTest, // slow-path test (may be constant) ALength, // array length (or TOP if none) + ValidLengthTest, ParmLimit }; @@ -922,6 +923,7 @@ public: fields[KlassNode] = TypeInstPtr::NOTNULL; fields[InitialTest] = TypeInt::BOOL; fields[ALength] = t; // length (can be a bad length) + fields[ValidLengthTest] = TypeInt::BOOL; const TypeTuple *domain = TypeTuple::make(ParmLimit, fields); @@ -1016,18 +1018,16 @@ public: // class AllocateArrayNode : public AllocateNode { public: - AllocateArrayNode(Compile* C, const TypeFunc *atype, Node *ctrl, Node *mem, Node *abio, - Node* size, Node* klass_node, Node* initial_test, - Node* count_val - ) + AllocateArrayNode(Compile* C, const TypeFunc* atype, Node* ctrl, Node* mem, Node* abio, Node* size, Node* klass_node, + Node* initial_test, Node* count_val, Node* valid_length_test) : AllocateNode(C, atype, ctrl, mem, abio, size, klass_node, initial_test) { init_class_id(Class_AllocateArray); set_req(AllocateNode::ALength, count_val); + set_req(AllocateNode::ValidLengthTest, valid_length_test); } virtual int Opcode() const; - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); // Dig the length operand out of a array allocation site. Node* Ideal_length() { diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index e583dd9deec..14357ec8e46 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2687,6 +2687,17 @@ const Type* CatchNode::Value(PhaseGVN* phase) const { // Rethrows always throw exceptions, never return if (call->entry_point() == OptoRuntime::rethrow_stub()) { f[CatchProjNode::fall_through_index] = Type::TOP; + } else if (call->is_AllocateArray()) { + Node* klass_node = call->in(AllocateNode::KlassNode); + Node* length = call->in(AllocateNode::ALength); + const Type* length_type = phase->type(length); + const Type* klass_type = phase->type(klass_node); + Node* valid_length_test = call->in(AllocateNode::ValidLengthTest); + const Type* valid_length_test_t = phase->type(valid_length_test); + if (length_type == Type::TOP || klass_type == Type::TOP || valid_length_test_t == Type::TOP || + valid_length_test_t->is_int()->is_con(0)) { + f[CatchProjNode::fall_through_index] = Type::TOP; + } } else if( call->req() > TypeFunc::Parms ) { const Type *arg0 = phase->type( call->in(TypeFunc::Parms) ); // Check for null receiver to virtual or interface calls diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 9adda4961e5..1296a3ff8b8 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -3812,7 +3812,7 @@ bool Compile::final_graph_reshaping() { // 'fall-thru' path, so expected kids is 1 less. if (n->is_PCTable() && n->in(0) && n->in(0)->in(0)) { if (n->in(0)->in(0)->is_Call()) { - CallNode *call = n->in(0)->in(0)->as_Call(); + CallNode* call = n->in(0)->in(0)->as_Call(); if (call->entry_point() == OptoRuntime::rethrow_stub()) { required_outcnt--; // Rethrow always has 1 less kid } else if (call->req() > TypeFunc::Parms && @@ -3821,22 +3821,25 @@ bool Compile::final_graph_reshaping() { // detected that the virtual call will always result in a null // pointer exception. The fall-through projection of this CatchNode // will not be populated. - Node *arg0 = call->in(TypeFunc::Parms); + Node* arg0 = call->in(TypeFunc::Parms); if (arg0->is_Type() && arg0->as_Type()->type()->higher_equal(TypePtr::NULL_PTR)) { required_outcnt--; } - } else if (call->entry_point() == OptoRuntime::new_array_Java() && - call->req() > TypeFunc::Parms+1 && - call->is_CallStaticJava()) { - // Check for negative array length. In such case, the optimizer has + } else if (call->entry_point() == OptoRuntime::new_array_Java() || + call->entry_point() == OptoRuntime::new_array_nozero_Java()) { + // Check for illegal array length. In such case, the optimizer has // detected that the allocation attempt will always result in an // exception. There is no fall-through projection of this CatchNode . - Node *arg1 = call->in(TypeFunc::Parms+1); - if (arg1->is_Type() && - arg1->as_Type()->type()->join(TypeInt::POS)->empty()) { + assert(call->is_CallStaticJava(), "static call expected"); + assert(call->req() == call->jvms()->endoff() + 1, "missing extra input"); + Node* valid_length_test = call->in(call->req()-1); + call->del_req(call->req()-1); + if (valid_length_test->find_int_con(1) == 0) { required_outcnt--; } + assert(n->outcnt() == required_outcnt, "malformed control flow"); + continue; } } } @@ -3845,6 +3848,14 @@ bool Compile::final_graph_reshaping() { record_method_not_compilable("malformed control flow"); return true; // Not all targets reachable! } + } else if (n->is_PCTable() && n->in(0) && n->in(0)->in(0) && n->in(0)->in(0)->is_Call()) { + CallNode* call = n->in(0)->in(0)->as_Call(); + if (call->entry_point() == OptoRuntime::new_array_Java() || + call->entry_point() == OptoRuntime::new_array_nozero_Java()) { + assert(call->is_CallStaticJava(), "static call expected"); + assert(call->req() == call->jvms()->endoff() + 1, "missing extra input"); + call->del_req(call->req()-1); // valid length test useless now + } } // Check that I actually visited all kids. Unreached kids // must be infinite loops. diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index da96ebd9169..ecee3147787 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2728,7 +2728,9 @@ void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool sep // Make a catch node with just two handlers: fall-through and catch-all Node* i_o = _gvn.transform( new ProjNode(call, TypeFunc::I_O, separate_io_proj) ); Node* catc = _gvn.transform( new CatchNode(control(), i_o, 2) ); - Node* norm = _gvn.transform( new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci) ); + Node* norm = new CatchProjNode(catc, CatchProjNode::fall_through_index, CatchProjNode::no_handler_bci); + _gvn.set_type_bottom(norm); + C->record_for_igvn(norm); Node* excp = _gvn.transform( new CatchProjNode(catc, CatchProjNode::catch_all_index, CatchProjNode::no_handler_bci) ); { PreserveJVMState pjvms(this); @@ -3969,20 +3971,28 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable) initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn); } + const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type(); + Node* valid_length_test = _gvn.intcon(1); + if (ary_type->klass()->is_array_klass()) { + BasicType bt = ary_type->klass()->as_array_klass()->element_type()->basic_type(); + jint max = TypeAryPtr::max_array_length(bt); + Node* valid_length_cmp = _gvn.transform(new CmpUNode(length, intcon(max))); + valid_length_test = _gvn.transform(new BoolNode(valid_length_cmp, BoolTest::le)); + } + // Create the AllocateArrayNode and its result projections AllocateArrayNode* alloc = new AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT), control(), mem, i_o(), size, klass_node, initial_slow_test, - length); + length, valid_length_test); // Cast to correct type. Note that the klass_node may be constant or not, // and in the latter case the actual array type will be inexact also. // (This happens via a non-constant argument to inline_native_newArray.) // In any case, the value of klass_node provides the desired array type. const TypeInt* length_type = _gvn.find_int_type(length); - const TypeOopPtr* ary_type = _gvn.type(klass_node)->is_klassptr()->as_instance_type(); if (ary_type->isa_aryptr() && length_type != NULL) { // Try to get a better type than POS for the size ary_type = ary_type->is_aryptr()->cast_to_size(length_type); diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index a4bdcecb12a..5f45ffc09c3 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1208,7 +1208,8 @@ void PhaseMacroExpand::expand_allocate_common( AllocateNode* alloc, // allocation node to be expanded Node* length, // array length for an array allocation const TypeFunc* slow_call_type, // Type of slow call - address slow_call_address // Address of slow call + address slow_call_address, // Address of slow call + Node* valid_length_test // whether length is valid or not ) { Node* ctrl = alloc->in(TypeFunc::Control); @@ -1393,6 +1394,12 @@ void PhaseMacroExpand::expand_allocate_common( // Copy debug information and adjust JVMState information, then replace // allocate node with the call call->copy_call_debug_info(&_igvn, alloc); + // For array allocations, copy the valid length check to the call node so Compile::final_graph_reshaping() can verify + // that the call has the expected number of CatchProj nodes (in case the allocation always fails and the fallthrough + // path dies). + if (valid_length_test != NULL) { + call->add_req(valid_length_test); + } if (expand_fast_path) { call->set_cnt(PROB_UNLIKELY_MAG(4)); // Same effect as RC_UNCOMMON. } else { @@ -1875,11 +1882,12 @@ Node* PhaseMacroExpand::prefetch_allocation(Node* i_o, Node*& needgc_false, void PhaseMacroExpand::expand_allocate(AllocateNode *alloc) { expand_allocate_common(alloc, NULL, OptoRuntime::new_instance_Type(), - OptoRuntime::new_instance_Java()); + OptoRuntime::new_instance_Java(), NULL); } void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) { Node* length = alloc->in(AllocateNode::ALength); + Node* valid_length_test = alloc->in(AllocateNode::ValidLengthTest); InitializeNode* init = alloc->initialization(); Node* klass_node = alloc->in(AllocateNode::KlassNode); ciKlass* k = _igvn.type(klass_node)->is_klassptr()->klass(); @@ -1894,7 +1902,7 @@ void PhaseMacroExpand::expand_allocate_array(AllocateArrayNode *alloc) { } expand_allocate_common(alloc, length, OptoRuntime::new_array_Type(), - slow_call_address); + slow_call_address, valid_length_test); } //-------------------mark_eliminated_box---------------------------------- diff --git a/src/hotspot/share/opto/macro.hpp b/src/hotspot/share/opto/macro.hpp index b65b97fec87..c028120a0fc 100644 --- a/src/hotspot/share/opto/macro.hpp +++ b/src/hotspot/share/opto/macro.hpp @@ -92,8 +92,8 @@ private: void expand_allocate_common(AllocateNode* alloc, Node* length, const TypeFunc* slow_call_type, - address slow_call_address); - void yank_initalize_node(InitializeNode* node); + address slow_call_address, + Node* valid_length_test); void yank_alloc_node(AllocateNode* alloc); Node *value_from_mem(Node *mem, Node *ctl, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc); Node *value_from_mem_phi(Node *mem, BasicType ft, const Type *ftype, const TypeOopPtr *adr_t, AllocateNode *alloc, Node_Stack *value_phis, int level); diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp index 060d3464514..05babc625c6 100644 --- a/src/hotspot/share/opto/split_if.cpp +++ b/src/hotspot/share/opto/split_if.cpp @@ -128,8 +128,8 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { } } else { // We might see an Opaque1 from a loop limit check here - assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1, "unexpected node type"); - Node *use_c = use->is_If() ? use->in(0) : get_ctrl(use); + assert(use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque1 || use->is_AllocateArray(), "unexpected node type"); + Node *use_c = (use->is_If() || use->is_AllocateArray()) ? use->in(0) : get_ctrl(use); if (use_c == blk1 || use_c == blk2) { assert(use->is_CMove(), "unexpected node type"); continue; @@ -166,14 +166,15 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) { --j; } else { // We might see an Opaque1 from a loop limit check here - assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1, "unexpected node type"); - assert(u->in(1) == bol, ""); + assert(u->is_If() || u->is_CMove() || u->Opcode() == Op_Opaque1 || u->is_AllocateArray(), "unexpected node type"); + assert(u->is_AllocateArray() || u->in(1) == bol, ""); + assert(!u->is_AllocateArray() || u->in(AllocateNode::ValidLengthTest) == bol, "wrong input to AllocateArray"); // Get control block of either the CMove or the If input - Node *u_ctrl = u->is_If() ? u->in(0) : get_ctrl(u); + Node *u_ctrl = (u->is_If() || u->is_AllocateArray()) ? u->in(0) : get_ctrl(u); assert((u_ctrl != blk1 && u_ctrl != blk2) || u->is_CMove(), "won't converge"); Node *x = bol->clone(); register_new_node(x, u_ctrl); - _igvn.replace_input_of(u, 1, x); + _igvn.replace_input_of(u, u->is_AllocateArray() ? AllocateNode::ValidLengthTest : 1, x); --j; } } diff --git a/test/hotspot/jtreg/compiler/allocation/TestAllocArrayAfterAllocNoUse.java b/test/hotspot/jtreg/compiler/allocation/TestAllocArrayAfterAllocNoUse.java new file mode 100644 index 00000000000..e2bc9bfdd16 --- /dev/null +++ b/test/hotspot/jtreg/compiler/allocation/TestAllocArrayAfterAllocNoUse.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. 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 8279125 + * @summary fatal error: no reachable node should have no use + * @requires vm.flavor == "server" + * + * @run main/othervm -XX:-BackgroundCompilation -XX:-DoEscapeAnalysis TestAllocArrayAfterAllocNoUse + * + */ + +public class TestAllocArrayAfterAllocNoUse { + private static Object field; + + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + test(); + } + } + + private static void test() { + try { + final TestAllocArrayAfterAllocNoUse o = new TestAllocArrayAfterAllocNoUse(); + } catch (Exception e) { + final int[] array = new int[100]; + field = array; + } + + } +} diff --git a/test/hotspot/jtreg/compiler/allocation/TestCCPAllocateArray.java b/test/hotspot/jtreg/compiler/allocation/TestCCPAllocateArray.java new file mode 100644 index 00000000000..9e312a79530 --- /dev/null +++ b/test/hotspot/jtreg/compiler/allocation/TestCCPAllocateArray.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. 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 8279062 + * @summary C2: assert(t->meet(t0) == t) failed: Not monotonic after JDK-8278413 + * + * @run main/othervm -XX:-BackgroundCompilation TestCCPAllocateArray + * + */ + +public class TestCCPAllocateArray { + public static void main(String[] args) { + for (int i = 0; i < 20_000; i++) { + try { + test(); + } catch (OutOfMemoryError e) { + } + length(42); + } + } + + private static int[] test() { + int i = 2; + for (; i < 4; i *= 2); + return new int[length(i)]; + } + + private static int length(int i) { + return i == 4 ? Integer.MAX_VALUE : 0; + } +} diff --git a/test/hotspot/jtreg/compiler/allocation/TestFailedAllocationBadGraph.java b/test/hotspot/jtreg/compiler/allocation/TestFailedAllocationBadGraph.java new file mode 100644 index 00000000000..bd720a9b4f1 --- /dev/null +++ b/test/hotspot/jtreg/compiler/allocation/TestFailedAllocationBadGraph.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022, Red Hat, Inc. 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 8279219 + * @summary C2 crash when allocating array of size too large + * @library /test/lib / + * @build sun.hotspot.WhiteBox + * @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -ea -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation TestFailedAllocationBadGraph + */ + +import sun.hotspot.WhiteBox; +import java.lang.reflect.Method; +import compiler.whitebox.CompilerWhiteBoxTest; + +public class TestFailedAllocationBadGraph { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + private static long[] array; + private static int field; + private static volatile int barrier; + + public static void main(String[] args) throws Exception { + run("test1"); + run("test2"); + } + + private static void run(String method) throws Exception { + Method m = TestFailedAllocationBadGraph.class.getDeclaredMethod(method); + WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); + if (!WHITE_BOX.isMethodCompiled(m) || WHITE_BOX.getMethodCompilationLevel(m) != CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) { + throw new RuntimeException("should still be compiled"); + } + } + + private static int test1() { + int length = Integer.MAX_VALUE; + try { + array = new long[length]; + } catch (OutOfMemoryError outOfMemoryError) { + barrier = 0x42; + length = field; + } + return length; + } + + private static int test2() { + int length = -1; + try { + array = new long[length]; + } catch (OutOfMemoryError outOfMemoryError) { + barrier = 0x42; + length = field; + } + return length; + } +}