6910618: C2: Error: assert(d->is_oop(),"JVM_ArrayCopy: dst not an oop")
Mark in PcDesc call sites which return oop and save the result oop across objects reallocation during deoptimization. Reviewed-by: never
This commit is contained in:
parent
3f7a94c3f2
commit
22409a5704
@ -253,7 +253,8 @@ class IRScopeDebugInfo: public CompilationResourceObj {
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -282,6 +282,7 @@ void DebugInformationRecorder::describe_scope(int pc_offset,
|
||||
int bci,
|
||||
bool reexecute,
|
||||
bool is_method_handle_invoke,
|
||||
bool return_oop,
|
||||
DebugToken* locals,
|
||||
DebugToken* expressions,
|
||||
DebugToken* monitors) {
|
||||
@ -296,6 +297,7 @@ void DebugInformationRecorder::describe_scope(int pc_offset,
|
||||
// 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);
|
||||
|
@ -89,6 +89,7 @@ class DebugInformationRecorder: public ResourceObj {
|
||||
int bci,
|
||||
bool reexecute,
|
||||
bool is_method_handle_invoke = false,
|
||||
bool return_oop = false,
|
||||
DebugToken* locals = NULL,
|
||||
DebugToken* expressions = NULL,
|
||||
DebugToken* monitors = NULL);
|
||||
|
@ -988,7 +988,8 @@ ScopeDesc* nmethod::scope_desc_at(address pc) {
|
||||
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 @@ void nmethod::verify_interrupt_point(address call_site) {
|
||||
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 @@ ScopeDesc* nmethod::scope_desc_in(address begin, address end) {
|
||||
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;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ class PcDesc VALUE_OBJ_CLASS_SPEC {
|
||||
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 @@ class PcDesc VALUE_OBJ_CLASS_SPEC {
|
||||
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;
|
||||
|
||||
|
@ -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 @@ ScopeDesc::ScopeDesc(const ScopeDesc* parent) {
|
||||
_decode_offset = parent->_sender_decode_offset;
|
||||
_objects = parent->_objects;
|
||||
_reexecute = false; //reexecute only applies to the first scope
|
||||
_return_oop = false;
|
||||
decode_body();
|
||||
}
|
||||
|
||||
|
@ -52,17 +52,18 @@ class SimpleScopeDesc : public StackObj {
|
||||
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<ScopeValue*>* locals();
|
||||
GrowableArray<ScopeValue*>* expressions();
|
||||
@ -88,6 +89,7 @@ class ScopeDesc : public ResourceObj {
|
||||
methodHandle _method;
|
||||
int _bci;
|
||||
bool _reexecute;
|
||||
bool _return_oop;
|
||||
|
||||
// Decoding offsets
|
||||
int _decode_offset;
|
||||
|
@ -1483,6 +1483,7 @@ deoptimization.cpp thread.hpp
|
||||
deoptimization.cpp vframe.hpp
|
||||
deoptimization.cpp vframeArray.hpp
|
||||
deoptimization.cpp vframe_hp.hpp
|
||||
deoptimization.cpp vmreg_<arch>.inline.hpp
|
||||
deoptimization.cpp xmlstream.hpp
|
||||
|
||||
deoptimization.hpp allocation.hpp
|
||||
|
@ -795,6 +795,7 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
|
||||
|
||||
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 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
|
||||
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 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
|
||||
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.
|
||||
|
@ -402,7 +402,7 @@ void JvmtiCodeBlobEvents::build_jvmti_addr_location_map(nmethod *nm,
|
||||
|
||||
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();
|
||||
|
@ -145,6 +145,27 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||
if (EliminateAllocations) {
|
||||
assert (chunk->at(0)->scope() != NULL,"expect only compiled java frames");
|
||||
GrowableArray<ScopeValue*>* 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,9 +179,13 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||
ttyLocker ttyl;
|
||||
tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread);
|
||||
print_objects(objects);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (save_oop_result) {
|
||||
// Restore result.
|
||||
deoptee.set_saved_oop_result(&map, return_value());
|
||||
}
|
||||
}
|
||||
if (EliminateLocks) {
|
||||
#ifndef PRODUCT
|
||||
|
74
hotspot/test/compiler/6910618/Test.java
Normal file
74
hotspot/test/compiler/6910618/Test.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user