# HG changeset patch # User kvn # Date 1265707873 28800 # Node ID 8a76fd3d098da304a3be34c6a96f9a6e30e1cf1d # Parent fedc27b54caa4f0e7ad2344e059b1ccea40b8f04 6910618: C2: Error: assert(d->is_oop(),"JVM_ArrayCopy: dst not an oop") Summary: Mark in PcDesc call sites which return oop and save the result oop across objects reallocation during deoptimization. Reviewed-by: never diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/c1/c1_IR.hpp --- a/hotspot/src/share/vm/c1/c1_IR.hpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/c1/c1_IR.hpp Tue Feb 09 01:31:13 2010 -0800 @@ -253,7 +253,8 @@ // reexecute allowed only for the topmost frame bool reexecute = topmost ? should_reexecute() : false; bool is_method_handle_invoke = false; - recorder->describe_scope(pc_offset, scope()->method(), bci(), reexecute, is_method_handle_invoke, locvals, expvals, monvals); + bool return_oop = false; // This flag will be ignored since it used only for C2 with escape analysis. + recorder->describe_scope(pc_offset, scope()->method(), bci(), reexecute, is_method_handle_invoke, return_oop, locvals, expvals, monvals); } }; diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/code/debugInfoRec.cpp --- a/hotspot/src/share/vm/code/debugInfoRec.cpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/code/debugInfoRec.cpp Tue Feb 09 01:31:13 2010 -0800 @@ -282,6 +282,7 @@ int bci, bool reexecute, bool is_method_handle_invoke, + bool return_oop, DebugToken* locals, DebugToken* expressions, DebugToken* monitors) { @@ -296,6 +297,7 @@ // Record flags into pcDesc. last_pd->set_should_reexecute(reexecute); last_pd->set_is_method_handle_invoke(is_method_handle_invoke); + last_pd->set_return_oop(return_oop); // serialize sender stream offest stream()->write_int(sender_stream_offset); diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/code/debugInfoRec.hpp --- a/hotspot/src/share/vm/code/debugInfoRec.hpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/code/debugInfoRec.hpp Tue Feb 09 01:31:13 2010 -0800 @@ -89,6 +89,7 @@ int bci, bool reexecute, bool is_method_handle_invoke = false, + bool return_oop = false, DebugToken* locals = NULL, DebugToken* expressions = NULL, DebugToken* monitors = NULL); diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/code/nmethod.cpp --- a/hotspot/src/share/vm/code/nmethod.cpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/code/nmethod.cpp Tue Feb 09 01:31:13 2010 -0800 @@ -988,7 +988,8 @@ PcDesc* pd = pc_desc_at(pc); guarantee(pd != NULL, "scope must be present"); return new ScopeDesc(this, pd->scope_decode_offset(), - pd->obj_decode_offset(), pd->should_reexecute()); + pd->obj_decode_offset(), pd->should_reexecute(), + pd->return_oop()); } @@ -2159,7 +2160,8 @@ PcDesc* pd = pc_desc_at(ic->end_of_call()); assert(pd != NULL, "PcDesc must exist"); for (ScopeDesc* sd = new ScopeDesc(this, pd->scope_decode_offset(), - pd->obj_decode_offset(), pd->should_reexecute()); + pd->obj_decode_offset(), pd->should_reexecute(), + pd->return_oop()); !sd->is_top(); sd = sd->sender()) { sd->verify(); } @@ -2424,7 +2426,8 @@ PcDesc* p = pc_desc_near(begin+1); if (p != NULL && p->real_pc(this) <= end) { return new ScopeDesc(this, p->scope_decode_offset(), - p->obj_decode_offset(), p->should_reexecute()); + p->obj_decode_offset(), p->should_reexecute(), + p->return_oop()); } return NULL; } diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/code/pcDesc.hpp --- a/hotspot/src/share/vm/code/pcDesc.hpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/code/pcDesc.hpp Tue Feb 09 01:31:13 2010 -0800 @@ -39,6 +39,7 @@ struct { unsigned int reexecute: 1; unsigned int is_method_handle_invoke: 1; + unsigned int return_oop: 1; } bits; bool operator ==(const PcDescFlags& other) { return word == other.word; } } _flags; @@ -76,6 +77,9 @@ bool is_method_handle_invoke() const { return _flags.bits.is_method_handle_invoke; } void set_is_method_handle_invoke(bool z) { _flags.bits.is_method_handle_invoke = z; } + bool return_oop() const { return _flags.bits.return_oop; } + void set_return_oop(bool z) { _flags.bits.return_oop = z; } + // Returns the real pc address real_pc(const nmethod* code) const; diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/code/scopeDesc.cpp --- a/hotspot/src/share/vm/code/scopeDesc.cpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/code/scopeDesc.cpp Tue Feb 09 01:31:13 2010 -0800 @@ -26,19 +26,21 @@ # include "incls/_scopeDesc.cpp.incl" -ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset, bool reexecute) { +ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset, bool reexecute, bool return_oop) { _code = code; _decode_offset = decode_offset; _objects = decode_object_values(obj_decode_offset); _reexecute = reexecute; + _return_oop = return_oop; decode_body(); } -ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, bool reexecute) { +ScopeDesc::ScopeDesc(const nmethod* code, int decode_offset, bool reexecute, bool return_oop) { _code = code; _decode_offset = decode_offset; _objects = decode_object_values(DebugInformationRecorder::serialized_null); _reexecute = reexecute; + _return_oop = return_oop; decode_body(); } @@ -48,6 +50,7 @@ _decode_offset = parent->_sender_decode_offset; _objects = parent->_objects; _reexecute = false; //reexecute only applies to the first scope + _return_oop = false; decode_body(); } diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/code/scopeDesc.hpp --- a/hotspot/src/share/vm/code/scopeDesc.hpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/code/scopeDesc.hpp Tue Feb 09 01:31:13 2010 -0800 @@ -52,17 +52,18 @@ class ScopeDesc : public ResourceObj { public: // Constructor - ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset, bool reexecute); + ScopeDesc(const nmethod* code, int decode_offset, int obj_decode_offset, bool reexecute, bool return_oop); // Calls above, giving default value of "serialized_null" to the // "obj_decode_offset" argument. (We don't use a default argument to // avoid a .hpp-.hpp dependency.) - ScopeDesc(const nmethod* code, int decode_offset, bool reexecute); + ScopeDesc(const nmethod* code, int decode_offset, bool reexecute, bool return_oop); // JVM state methodHandle method() const { return _method; } int bci() const { return _bci; } bool should_reexecute() const { return _reexecute; } + bool return_oop() const { return _return_oop; } GrowableArray* locals(); GrowableArray* expressions(); @@ -88,6 +89,7 @@ methodHandle _method; int _bci; bool _reexecute; + bool _return_oop; // Decoding offsets int _decode_offset; diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/includeDB_core --- a/hotspot/src/share/vm/includeDB_core Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/includeDB_core Tue Feb 09 01:31:13 2010 -0800 @@ -1483,6 +1483,7 @@ deoptimization.cpp vframe.hpp deoptimization.cpp vframeArray.hpp deoptimization.cpp vframe_hp.hpp +deoptimization.cpp vmreg_.inline.hpp deoptimization.cpp xmlstream.hpp deoptimization.hpp allocation.hpp diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/opto/output.cpp --- a/hotspot/src/share/vm/opto/output.cpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/opto/output.cpp Tue Feb 09 01:31:13 2010 -0800 @@ -795,6 +795,7 @@ int safepoint_pc_offset = current_offset; bool is_method_handle_invoke = false; + bool return_oop = false; // Add the safepoint in the DebugInfoRecorder if( !mach->is_MachCall() ) { @@ -807,6 +808,11 @@ if (mcall->is_MachCallJava()) is_method_handle_invoke = mcall->as_MachCallJava()->_method_handle_invoke; + // Check if a call returns an object. + if (mcall->return_value_is_used() && + mcall->tf()->range()->field_at(TypeFunc::Parms)->isa_ptr()) { + return_oop = true; + } safepoint_pc_offset += mcall->ret_addr_offset(); debug_info()->add_safepoint(safepoint_pc_offset, mcall->_oop_map); } @@ -919,7 +925,7 @@ assert(jvms->bci() >= InvocationEntryBci && jvms->bci() <= 0x10000, "must be a valid or entry BCI"); assert(!jvms->should_reexecute() || depth == max_depth, "reexecute allowed only for the youngest"); // Now we can describe the scope. - debug_info()->describe_scope(safepoint_pc_offset, scope_method, jvms->bci(), jvms->should_reexecute(), is_method_handle_invoke, locvals, expvals, monvals); + debug_info()->describe_scope(safepoint_pc_offset, scope_method, jvms->bci(), jvms->should_reexecute(), is_method_handle_invoke, return_oop, locvals, expvals, monvals); } // End jvms loop // Mark the end of the scope set. diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp --- a/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/prims/jvmtiCodeBlobEvents.cpp Tue Feb 09 01:31:13 2010 -0800 @@ -402,7 +402,7 @@ address scopes_data = nm->scopes_data_begin(); for( pcd = nm->scopes_pcs_begin(); pcd < nm->scopes_pcs_end(); ++pcd ) { - ScopeDesc sc0(nm, pcd->scope_decode_offset(), pcd->should_reexecute()); + ScopeDesc sc0(nm, pcd->scope_decode_offset(), pcd->should_reexecute(), pcd->return_oop()); ScopeDesc *sd = &sc0; while( !sd->is_top() ) { sd = sd->sender(); } int bci = sd->bci(); diff -r fedc27b54caa -r 8a76fd3d098d hotspot/src/share/vm/runtime/deoptimization.cpp --- a/hotspot/src/share/vm/runtime/deoptimization.cpp Mon Feb 08 12:20:09 2010 -0800 +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp Tue Feb 09 01:31:13 2010 -0800 @@ -145,6 +145,27 @@ if (EliminateAllocations) { assert (chunk->at(0)->scope() != NULL,"expect only compiled java frames"); GrowableArray* objects = chunk->at(0)->scope()->objects(); + + // The flag return_oop() indicates call sites which return oop + // in compiled code. Such sites include java method calls, + // runtime calls (for example, used to allocate new objects/arrays + // on slow code path) and any other calls generated in compiled code. + // It is not guaranteed that we can get such information here only + // by analyzing bytecode in deoptimized frames. This is why this flag + // is set during method compilation (see Compile::Process_OopMap_Node()). + bool save_oop_result = chunk->at(0)->scope()->return_oop(); + Handle return_value; + if (save_oop_result) { + // Reallocation may trigger GC. If deoptimization happened on return from + // call which returns oop we need to save it since it is not in oopmap. + oop result = deoptee.saved_oop_result(&map); + assert(result == NULL || result->is_oop(), "must be oop"); + return_value = Handle(thread, result); + assert(Universe::heap()->is_in_or_null(result), "must be heap pointer"); + if (TraceDeoptimization) { + tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, result, thread); + } + } bool reallocated = false; if (objects != NULL) { JRT_BLOCK @@ -158,8 +179,12 @@ ttyLocker ttyl; tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); print_objects(objects); + } +#endif } -#endif + if (save_oop_result) { + // Restore result. + deoptee.set_saved_oop_result(&map, return_value()); } } if (EliminateLocks) { diff -r fedc27b54caa -r 8a76fd3d098d hotspot/test/compiler/6910618/Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/6910618/Test.java Tue Feb 09 01:31:13 2010 -0800 @@ -0,0 +1,74 @@ +/* + * Copyright 2010 Sun Microsystems, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/** + * @test + * @bug 6910605 + * @summary C2: NullPointerException/ClassCaseException is thrown when C2 with DeoptimizeALot is used + * + * @run main/othervm -Xmx64m -XX:+IgnoreUnrecognizedVMOptions -XX:+DeoptimizeALot -XX:+DoEscapeAnalysis -Xbatch -XX:InlineSmallCode=2000 Test + * + */ + +/* + * Added InlineSmallCode=2000 to guaranty inlining of StringBuilder::append() to allow scalar replace StringBuilder object. + * + * original test: gc/gctests/StringGC + */ + +public class Test { + private final String toAdd = "0123456789abcdef"; + private int maxLength; + private static final int numberOfThreads = 8; + + private class StringAdder extends Thread { + private String s; + + public void test() { + s = s + toAdd; + } + public void run() { + do { + test(); + } while (s.length() < maxLength); + } + } + + public void test() throws InterruptedException { + maxLength = toAdd.length() * 15000/ numberOfThreads; + StringAdder[] sa = new StringAdder[numberOfThreads]; + for (int i = 0; i < numberOfThreads; i++) { + sa[i] = new StringAdder(); + sa[i].start(); + } + for (int i = 0; i < numberOfThreads; i++) { + sa[i].join(); + } + } + + public static void main(String[] args) throws InterruptedException { + Test t = new Test(); + t.test(); + } +}