6833129: specjvm98 fails with NullPointerException in the compiler with -XX:DeoptimizeALot

Developed a reexecute logic for the interpreter to reexecute the bytecode when deopt happens

Reviewed-by: kvn, never, jrose, twisti
This commit is contained in:
Changpeng Fang 2009-07-31 17:12:33 -07:00
parent f2ea22a547
commit ae00753bf7
29 changed files with 465 additions and 241 deletions

View file

@ -81,8 +81,4 @@ public class DebugInfoReadStream extends CompressedReadStream {
Assert.that(false, "should not reach here"); Assert.that(false, "should not reach here");
return null; return null;
} }
public int readBCI() {
return readInt() + InvocationEntryBCI;
}
} }

View file

@ -82,6 +82,7 @@ public class PCDesc extends VMObject {
tty.print(" "); tty.print(" ");
sd.getMethod().printValueOn(tty); sd.getMethod().printValueOn(tty);
tty.print(" @" + sd.getBCI()); tty.print(" @" + sd.getBCI());
tty.print(" reexecute=" + sd.getReexecute());
tty.println(); tty.println();
} }
} }

View file

@ -41,6 +41,7 @@ public class ScopeDesc {
private NMethod code; private NMethod code;
private Method method; private Method method;
private int bci; private int bci;
private boolean reexecute;
/** Decoding offsets */ /** Decoding offsets */
private int decodeOffset; private int decodeOffset;
private int senderDecodeOffset; private int senderDecodeOffset;
@ -61,7 +62,7 @@ public class ScopeDesc {
senderDecodeOffset = stream.readInt(); senderDecodeOffset = stream.readInt();
method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle()); method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle());
bci = stream.readBCI(); setBCIAndReexecute(stream.readInt());
// Decode offsets for body and sender // Decode offsets for body and sender
localsDecodeOffset = stream.readInt(); localsDecodeOffset = stream.readInt();
expressionsDecodeOffset = stream.readInt(); expressionsDecodeOffset = stream.readInt();
@ -78,7 +79,7 @@ public class ScopeDesc {
senderDecodeOffset = stream.readInt(); senderDecodeOffset = stream.readInt();
method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle()); method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle());
bci = stream.readBCI(); setBCIAndReexecute(stream.readInt());
// Decode offsets for body and sender // Decode offsets for body and sender
localsDecodeOffset = stream.readInt(); localsDecodeOffset = stream.readInt();
expressionsDecodeOffset = stream.readInt(); expressionsDecodeOffset = stream.readInt();
@ -88,6 +89,7 @@ public class ScopeDesc {
public NMethod getNMethod() { return code; } public NMethod getNMethod() { return code; }
public Method getMethod() { return method; } public Method getMethod() { return method; }
public int getBCI() { return bci; } public int getBCI() { return bci; }
public boolean getReexecute() {return reexecute;}
/** Returns a List<ScopeValue> */ /** Returns a List<ScopeValue> */
public List getLocals() { public List getLocals() {
@ -150,6 +152,7 @@ public class ScopeDesc {
tty.print("ScopeDesc for "); tty.print("ScopeDesc for ");
method.printValueOn(tty); method.printValueOn(tty);
tty.println(" @bci " + bci); tty.println(" @bci " + bci);
tty.println(" reexecute: " + reexecute);
} }
// FIXME: add more accessors // FIXME: add more accessors
@ -157,6 +160,11 @@ public class ScopeDesc {
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Internals only below this point // Internals only below this point
// //
private void setBCIAndReexecute(int combination) {
int InvocationEntryBci = VM.getVM().getInvocationEntryBCI();
bci = (combination >> 1) + InvocationEntryBci;
reexecute = (combination & 1)==1 ? true : false;
}
private DebugInfoReadStream streamAt(int decodeOffset) { private DebugInfoReadStream streamAt(int decodeOffset) {
return new DebugInfoReadStream(code, decodeOffset, objects); return new DebugInfoReadStream(code, decodeOffset, objects);

View file

@ -208,6 +208,15 @@ int IRScope::top_scope_bci() const {
return scope->caller_bci(); return scope->caller_bci();
} }
bool IRScopeDebugInfo::should_reexecute() {
ciMethod* cur_method = scope()->method();
int cur_bci = bci();
if (cur_method != NULL && cur_bci != SynchronizationEntryBCI) {
Bytecodes::Code code = cur_method->java_code_at_bci(cur_bci);
return Interpreter::bytecode_should_reexecute(code);
} else
return false;
}
// Implementation of CodeEmitInfo // Implementation of CodeEmitInfo
@ -253,7 +262,7 @@ CodeEmitInfo::CodeEmitInfo(CodeEmitInfo* info, bool lock_stack_only)
void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset) { void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset) {
// record the safepoint before recording the debug info for enclosing scopes // record the safepoint before recording the debug info for enclosing scopes
recorder->add_safepoint(pc_offset, _oop_map->deep_copy()); recorder->add_safepoint(pc_offset, _oop_map->deep_copy());
_scope_debug_info->record_debug_info(recorder, pc_offset); _scope_debug_info->record_debug_info(recorder, pc_offset, true/*topmost*/);
recorder->end_safepoint(pc_offset); recorder->end_safepoint(pc_offset);
} }

View file

@ -239,15 +239,20 @@ class IRScopeDebugInfo: public CompilationResourceObj {
GrowableArray<MonitorValue*>* monitors() { return _monitors; } GrowableArray<MonitorValue*>* monitors() { return _monitors; }
IRScopeDebugInfo* caller() { return _caller; } IRScopeDebugInfo* caller() { return _caller; }
void record_debug_info(DebugInformationRecorder* recorder, int pc_offset) { //Whether we should reexecute this bytecode for deopt
bool should_reexecute();
void record_debug_info(DebugInformationRecorder* recorder, int pc_offset, bool topmost) {
if (caller() != NULL) { if (caller() != NULL) {
// Order is significant: Must record caller first. // Order is significant: Must record caller first.
caller()->record_debug_info(recorder, pc_offset); caller()->record_debug_info(recorder, pc_offset, false/*topmost*/);
} }
DebugToken* locvals = recorder->create_scope_values(locals()); DebugToken* locvals = recorder->create_scope_values(locals());
DebugToken* expvals = recorder->create_scope_values(expressions()); DebugToken* expvals = recorder->create_scope_values(expressions());
DebugToken* monvals = recorder->create_monitor_values(monitors()); DebugToken* monvals = recorder->create_monitor_values(monitors());
recorder->describe_scope(pc_offset, scope()->method(), bci(), locvals, expvals, monvals); // reexecute allowed only for the topmost frame
bool reexecute = topmost ? should_reexecute() : false;
recorder->describe_scope(pc_offset, scope()->method(), bci(), reexecute, locvals, expvals, monvals);
} }
}; };

View file

@ -379,7 +379,8 @@ void LIR_Assembler::record_non_safepoint_debug_info() {
ValueStack* s = nth_oldest(vstack, n, s_bci); ValueStack* s = nth_oldest(vstack, n, s_bci);
if (s == NULL) break; if (s == NULL) break;
IRScope* scope = s->scope(); IRScope* scope = s->scope();
debug_info->describe_scope(pc_offset, scope->method(), s_bci); //Always pass false for reexecute since these ScopeDescs are never used for deopt
debug_info->describe_scope(pc_offset, scope->method(), s_bci, false/*reexecute*/);
} }
debug_info->end_non_safepoint(pc_offset); debug_info->end_non_safepoint(pc_offset);

View file

@ -1229,10 +1229,13 @@ void java_lang_Throwable::fill_in_stack_trace(Handle throwable, TRAPS) {
// Compiled java method case. // Compiled java method case.
if (decode_offset != 0) { if (decode_offset != 0) {
bool dummy_reexecute = false;
DebugInfoReadStream stream(nm, decode_offset); DebugInfoReadStream stream(nm, decode_offset);
decode_offset = stream.read_int(); decode_offset = stream.read_int();
method = (methodOop)nm->oop_at(stream.read_int()); method = (methodOop)nm->oop_at(stream.read_int());
bci = stream.read_bci(); //fill_in_stack_trace does not need the reexecute information which is designed
//for the deopt to reexecute
bci = stream.read_bci_and_reexecute(dummy_reexecute);
} else { } else {
if (fr.is_first_frame()) break; if (fr.is_first_frame()) break;
address pc = fr.pc(); address pc = fr.pc();

View file

@ -255,7 +255,8 @@ class DebugInfoReadStream : public CompressedReadStream {
ScopeValue* read_object_value(); ScopeValue* read_object_value();
ScopeValue* get_cached_object(); ScopeValue* get_cached_object();
// BCI encoding is mostly unsigned, but -1 is a distinguished value // BCI encoding is mostly unsigned, but -1 is a distinguished value
int read_bci() { return read_int() + InvocationEntryBci; } // Decoding based on encoding: bci = InvocationEntryBci + read_int()/2; reexecute = read_int()%2 == 1 ? true : false;
int read_bci_and_reexecute(bool& reexecute) { int i = read_int(); reexecute = (i & 1) ? true : false; return (i >> 1) + InvocationEntryBci; }
}; };
// DebugInfoWriteStream specializes CompressedWriteStream for // DebugInfoWriteStream specializes CompressedWriteStream for
@ -268,5 +269,6 @@ class DebugInfoWriteStream : public CompressedWriteStream {
public: public:
DebugInfoWriteStream(DebugInformationRecorder* recorder, int initial_size); DebugInfoWriteStream(DebugInformationRecorder* recorder, int initial_size);
void write_handle(jobject h); void write_handle(jobject h);
void write_bci(int bci) { write_int(bci - InvocationEntryBci); } //Encoding bci and reexecute into one word as (bci - InvocationEntryBci)*2 + reexecute
void write_bci_and_reexecute(int bci, bool reexecute) { write_int(((bci - InvocationEntryBci) << 1) + (reexecute ? 1 : 0)); }
}; };

View file

@ -280,6 +280,7 @@ int DebugInformationRecorder::find_sharable_decode_offset(int stream_offset) {
void DebugInformationRecorder::describe_scope(int pc_offset, void DebugInformationRecorder::describe_scope(int pc_offset,
ciMethod* method, ciMethod* method,
int bci, int bci,
bool reexecute,
DebugToken* locals, DebugToken* locals,
DebugToken* expressions, DebugToken* expressions,
DebugToken* monitors) { DebugToken* monitors) {
@ -297,7 +298,7 @@ void DebugInformationRecorder::describe_scope(int pc_offset,
// serialize scope // serialize scope
jobject method_enc = (method == NULL)? NULL: method->encoding(); jobject method_enc = (method == NULL)? NULL: method->encoding();
stream()->write_int(oop_recorder()->find_index(method_enc)); stream()->write_int(oop_recorder()->find_index(method_enc));
stream()->write_bci(bci); stream()->write_bci_and_reexecute(bci, reexecute);
assert(method == NULL || assert(method == NULL ||
(method->is_native() && bci == 0) || (method->is_native() && bci == 0) ||
(!method->is_native() && 0 <= bci && bci < method->code_size()) || (!method->is_native() && 0 <= bci && bci < method->code_size()) ||

View file

@ -87,6 +87,7 @@ class DebugInformationRecorder: public ResourceObj {
void describe_scope(int pc_offset, void describe_scope(int pc_offset,
ciMethod* method, ciMethod* method,
int bci, int bci,
bool reexecute,
DebugToken* locals = NULL, DebugToken* locals = NULL,
DebugToken* expressions = NULL, DebugToken* expressions = NULL,
DebugToken* monitors = NULL); DebugToken* monitors = NULL);

View file

@ -46,6 +46,7 @@ ScopeDesc::ScopeDesc(const ScopeDesc* parent) {
_decode_offset = parent->_sender_decode_offset; _decode_offset = parent->_sender_decode_offset;
_objects = parent->_objects; _objects = parent->_objects;
decode_body(); decode_body();
assert(_reexecute == false, "reexecute not allowed");
} }
@ -56,6 +57,7 @@ void ScopeDesc::decode_body() {
_sender_decode_offset = DebugInformationRecorder::serialized_null; _sender_decode_offset = DebugInformationRecorder::serialized_null;
_method = methodHandle(_code->method()); _method = methodHandle(_code->method());
_bci = InvocationEntryBci; _bci = InvocationEntryBci;
_reexecute = false;
_locals_decode_offset = DebugInformationRecorder::serialized_null; _locals_decode_offset = DebugInformationRecorder::serialized_null;
_expressions_decode_offset = DebugInformationRecorder::serialized_null; _expressions_decode_offset = DebugInformationRecorder::serialized_null;
_monitors_decode_offset = DebugInformationRecorder::serialized_null; _monitors_decode_offset = DebugInformationRecorder::serialized_null;
@ -65,7 +67,8 @@ void ScopeDesc::decode_body() {
_sender_decode_offset = stream->read_int(); _sender_decode_offset = stream->read_int();
_method = methodHandle((methodOop) stream->read_oop()); _method = methodHandle((methodOop) stream->read_oop());
_bci = stream->read_bci(); _bci = stream->read_bci_and_reexecute(_reexecute);
// decode offsets for body and sender // decode offsets for body and sender
_locals_decode_offset = stream->read_int(); _locals_decode_offset = stream->read_int();
_expressions_decode_offset = stream->read_int(); _expressions_decode_offset = stream->read_int();
@ -170,6 +173,7 @@ void ScopeDesc::print_on(outputStream* st, PcDesc* pd) const {
st->print("ScopeDesc[%d]@" PTR_FORMAT " ", _decode_offset, _code->instructions_begin()); st->print("ScopeDesc[%d]@" PTR_FORMAT " ", _decode_offset, _code->instructions_begin());
st->print_cr(" offset: %d", _decode_offset); st->print_cr(" offset: %d", _decode_offset);
st->print_cr(" bci: %d", bci()); st->print_cr(" bci: %d", bci());
st->print_cr(" reexecute: %s", should_reexecute() ? "true" : "false");
st->print_cr(" locals: %d", _locals_decode_offset); st->print_cr(" locals: %d", _locals_decode_offset);
st->print_cr(" stack: %d", _expressions_decode_offset); st->print_cr(" stack: %d", _expressions_decode_offset);
st->print_cr(" monitor: %d", _monitors_decode_offset); st->print_cr(" monitor: %d", _monitors_decode_offset);

View file

@ -39,7 +39,8 @@ class SimpleScopeDesc : public StackObj {
DebugInfoReadStream buffer(code, pc_desc->scope_decode_offset()); DebugInfoReadStream buffer(code, pc_desc->scope_decode_offset());
int ignore_sender = buffer.read_int(); int ignore_sender = buffer.read_int();
_method = methodOop(buffer.read_oop()); _method = methodOop(buffer.read_oop());
_bci = buffer.read_bci(); bool dummy_reexecute; //only methodOop and bci are needed!
_bci = buffer.read_bci_and_reexecute(dummy_reexecute);
} }
methodOop method() { return _method; } methodOop method() { return _method; }
@ -62,6 +63,7 @@ class ScopeDesc : public ResourceObj {
// JVM state // JVM state
methodHandle method() const { return _method; } methodHandle method() const { return _method; }
int bci() const { return _bci; } int bci() const { return _bci; }
bool should_reexecute() const { return _reexecute; }
GrowableArray<ScopeValue*>* locals(); GrowableArray<ScopeValue*>* locals();
GrowableArray<ScopeValue*>* expressions(); GrowableArray<ScopeValue*>* expressions();
@ -86,6 +88,7 @@ class ScopeDesc : public ResourceObj {
// JVM state // JVM state
methodHandle _method; methodHandle _method;
int _bci; int _bci;
bool _reexecute;
// Decoding offsets // Decoding offsets
int _decode_offset; int _decode_offset;

View file

@ -122,11 +122,15 @@ class AbstractInterpreter: AllStatic {
static int size_top_interpreter_activation(methodOop method); static int size_top_interpreter_activation(methodOop method);
// Deoptimization support // Deoptimization support
static address continuation_for(methodOop method, // Compute the entry address for continuation after
static address deopt_continue_after_entry(methodOop method,
address bcp, address bcp,
int callee_parameters, int callee_parameters,
bool is_top_frame, bool is_top_frame);
bool& use_next_mdp); // Compute the entry address for reexecution
static address deopt_reexecute_entry(methodOop method, address bcp);
// Deoptimization should reexecute this bytecode
static bool bytecode_should_reexecute(Bytecodes::Code code);
// share implementation of size_activation and layout_activation: // share implementation of size_activation and layout_activation:
static int size_activation(methodOop method, static int size_activation(methodOop method,

View file

@ -284,76 +284,19 @@ static BasicType constant_pool_type(methodOop method, int index) {
//------------------------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------------------------
// Deoptimization support // Deoptimization support
// If deoptimization happens, this method returns the point where to continue in // If deoptimization happens, this function returns the point of next bytecode to continue execution
// interpreter. For calls (invokexxxx, newxxxx) the continuation is at next address AbstractInterpreter::deopt_continue_after_entry(methodOop method, address bcp, int callee_parameters, bool is_top_frame) {
// bci and the top of stack is in eax/edx/FPU tos.
// For putfield/getfield, put/getstatic, the continuation is at the same
// bci and the TOS is on stack.
// Note: deopt_entry(type, 0) means reexecute bytecode
// deopt_entry(type, length) means continue at next bytecode
address AbstractInterpreter::continuation_for(methodOop method, address bcp, int callee_parameters, bool is_top_frame, bool& use_next_mdp) {
assert(method->contains(bcp), "just checkin'"); assert(method->contains(bcp), "just checkin'");
Bytecodes::Code code = Bytecodes::java_code_at(bcp); Bytecodes::Code code = Bytecodes::java_code_at(bcp);
assert(!Interpreter::bytecode_should_reexecute(code), "should not reexecute");
int bci = method->bci_from(bcp); int bci = method->bci_from(bcp);
int length = -1; // initial value for debugging int length = -1; // initial value for debugging
// compute continuation length // compute continuation length
length = Bytecodes::length_at(bcp); length = Bytecodes::length_at(bcp);
// compute result type // compute result type
BasicType type = T_ILLEGAL; BasicType type = T_ILLEGAL;
// when continuing after a compiler safepoint, re-execute the bytecode
// (an invoke is continued after the safepoint)
use_next_mdp = true;
switch (code) { switch (code) {
case Bytecodes::_lookupswitch:
case Bytecodes::_tableswitch:
case Bytecodes::_fast_binaryswitch:
case Bytecodes::_fast_linearswitch:
// recompute condtional expression folded into _if<cond>
case Bytecodes::_lcmp :
case Bytecodes::_fcmpl :
case Bytecodes::_fcmpg :
case Bytecodes::_dcmpl :
case Bytecodes::_dcmpg :
case Bytecodes::_ifnull :
case Bytecodes::_ifnonnull :
case Bytecodes::_goto :
case Bytecodes::_goto_w :
case Bytecodes::_ifeq :
case Bytecodes::_ifne :
case Bytecodes::_iflt :
case Bytecodes::_ifge :
case Bytecodes::_ifgt :
case Bytecodes::_ifle :
case Bytecodes::_if_icmpeq :
case Bytecodes::_if_icmpne :
case Bytecodes::_if_icmplt :
case Bytecodes::_if_icmpge :
case Bytecodes::_if_icmpgt :
case Bytecodes::_if_icmple :
case Bytecodes::_if_acmpeq :
case Bytecodes::_if_acmpne :
// special cases
case Bytecodes::_getfield :
case Bytecodes::_putfield :
case Bytecodes::_getstatic :
case Bytecodes::_putstatic :
case Bytecodes::_aastore :
// reexecute the operation and TOS value is on stack
assert(is_top_frame, "must be top frame");
use_next_mdp = false;
return Interpreter::deopt_entry(vtos, 0);
break;
#ifdef COMPILER1
case Bytecodes::_athrow :
assert(is_top_frame, "must be top frame");
use_next_mdp = false;
return Interpreter::rethrow_exception_entry();
break;
#endif /* COMPILER1 */
case Bytecodes::_invokevirtual : case Bytecodes::_invokevirtual :
case Bytecodes::_invokespecial : case Bytecodes::_invokespecial :
case Bytecodes::_invokestatic : case Bytecodes::_invokestatic :
@ -392,6 +335,70 @@ address AbstractInterpreter::continuation_for(methodOop method, address bcp, int
: Interpreter::return_entry(as_TosState(type), length); : Interpreter::return_entry(as_TosState(type), length);
} }
// If deoptimization happens, this function returns the point where the interpreter reexecutes
// the bytecode.
// Note: Bytecodes::_athrow is a special case in that it does not return
// Interpreter::deopt_entry(vtos, 0) like others
address AbstractInterpreter::deopt_reexecute_entry(methodOop method, address bcp) {
assert(method->contains(bcp), "just checkin'");
Bytecodes::Code code = Bytecodes::java_code_at(bcp);
#ifdef COMPILER1
if(code == Bytecodes::_athrow ) {
return Interpreter::rethrow_exception_entry();
}
#endif /* COMPILER1 */
return Interpreter::deopt_entry(vtos, 0);
}
// If deoptimization happens, the interpreter should reexecute these bytecodes.
// This function mainly helps the compilers to set up the reexecute bit.
bool AbstractInterpreter::bytecode_should_reexecute(Bytecodes::Code code) {
switch (code) {
case Bytecodes::_lookupswitch:
case Bytecodes::_tableswitch:
case Bytecodes::_fast_binaryswitch:
case Bytecodes::_fast_linearswitch:
// recompute condtional expression folded into _if<cond>
case Bytecodes::_lcmp :
case Bytecodes::_fcmpl :
case Bytecodes::_fcmpg :
case Bytecodes::_dcmpl :
case Bytecodes::_dcmpg :
case Bytecodes::_ifnull :
case Bytecodes::_ifnonnull :
case Bytecodes::_goto :
case Bytecodes::_goto_w :
case Bytecodes::_ifeq :
case Bytecodes::_ifne :
case Bytecodes::_iflt :
case Bytecodes::_ifge :
case Bytecodes::_ifgt :
case Bytecodes::_ifle :
case Bytecodes::_if_icmpeq :
case Bytecodes::_if_icmpne :
case Bytecodes::_if_icmplt :
case Bytecodes::_if_icmpge :
case Bytecodes::_if_icmpgt :
case Bytecodes::_if_icmple :
case Bytecodes::_if_acmpeq :
case Bytecodes::_if_acmpne :
// special cases
case Bytecodes::_getfield :
case Bytecodes::_putfield :
case Bytecodes::_getstatic :
case Bytecodes::_putstatic :
case Bytecodes::_aastore :
#ifdef COMPILER1
//special case of reexecution
case Bytecodes::_athrow :
#endif
return true;
default:
return false;
}
}
void AbstractInterpreterGenerator::bang_stack_shadow_pages(bool native_call) { void AbstractInterpreterGenerator::bang_stack_shadow_pages(bool native_call) {
// Quick & dirty stack overflow checking: bang the stack & handle trap. // Quick & dirty stack overflow checking: bang the stack & handle trap.
// Note that we do the banging after the frame is setup, since the exception // Note that we do the banging after the frame is setup, since the exception

View file

@ -605,16 +605,19 @@ void TemplateInterpreter::ignore_safepoints() {
} }
} }
// If deoptimization happens, this method returns the point where to continue in //------------------------------------------------------------------------------------------------------------------------
// interpreter. For calls (invokexxxx, newxxxx) the continuation is at next // Deoptimization support
// bci and the top of stack is in eax/edx/FPU tos.
// For putfield/getfield, put/getstatic, the continuation is at the same
// bci and the TOS is on stack.
// Note: deopt_entry(type, 0) means reexecute bytecode // If deoptimization happens, this function returns the point of next bytecode to continue execution
// deopt_entry(type, length) means continue at next bytecode address TemplateInterpreter::deopt_continue_after_entry(methodOop method, address bcp, int callee_parameters, bool is_top_frame) {
return AbstractInterpreter::deopt_continue_after_entry(method, bcp, callee_parameters, is_top_frame);
}
address TemplateInterpreter::continuation_for(methodOop method, address bcp, int callee_parameters, bool is_top_frame, bool& use_next_mdp) { // If deoptimization happens, this function returns the point where the interpreter reexecutes
// the bytecode.
// Note: Bytecodes::_athrow (C1 only) and Bytecodes::_return are the special cases
// that do not return "Interpreter::deopt_entry(vtos, 0)"
address TemplateInterpreter::deopt_reexecute_entry(methodOop method, address bcp) {
assert(method->contains(bcp), "just checkin'"); assert(method->contains(bcp), "just checkin'");
Bytecodes::Code code = Bytecodes::java_code_at(bcp); Bytecodes::Code code = Bytecodes::java_code_at(bcp);
if (code == Bytecodes::_return) { if (code == Bytecodes::_return) {
@ -623,10 +626,20 @@ address TemplateInterpreter::continuation_for(methodOop method, address bcp, int
// the standard return vtos bytecode to pop the frame normally. // the standard return vtos bytecode to pop the frame normally.
// reexecuting the real bytecode would cause double registration // reexecuting the real bytecode would cause double registration
// of the finalizable object. // of the finalizable object.
assert(is_top_frame, "must be on top");
return _normal_table.entry(Bytecodes::_return).entry(vtos); return _normal_table.entry(Bytecodes::_return).entry(vtos);
} else { } else {
return AbstractInterpreter::continuation_for(method, bcp, callee_parameters, is_top_frame, use_next_mdp); return AbstractInterpreter::deopt_reexecute_entry(method, bcp);
}
}
// If deoptimization happens, the interpreter should reexecute this bytecode.
// This function mainly helps the compilers to set up the reexecute bit.
bool TemplateInterpreter::bytecode_should_reexecute(Bytecodes::Code code) {
if (code == Bytecodes::_return) {
//Yes, we consider Bytecodes::_return as a special case of reexecution
return true;
} else {
return AbstractInterpreter::bytecode_should_reexecute(code);
} }
} }

View file

@ -171,11 +171,15 @@ class TemplateInterpreter: public AbstractInterpreter {
static void ignore_safepoints(); // ignores safepoints static void ignore_safepoints(); // ignores safepoints
// Deoptimization support // Deoptimization support
static address continuation_for(methodOop method, // Compute the entry address for continuation after
static address deopt_continue_after_entry(methodOop method,
address bcp, address bcp,
int callee_parameters, int callee_parameters,
bool is_top_frame, bool is_top_frame);
bool& use_next_mdp); // Deoptimization should reexecute this bytecode
static bool bytecode_should_reexecute(Bytecodes::Code code);
// Compute the address for reexecution
static address deopt_reexecute_entry(methodOop method, address bcp);
#include "incls/_templateInterpreter_pd.hpp.incl" #include "incls/_templateInterpreter_pd.hpp.incl"

View file

@ -37,6 +37,7 @@ InlineTree::InlineTree( Compile* c, const InlineTree *caller_tree, ciMethod* cal
// Keep a private copy of the caller_jvms: // Keep a private copy of the caller_jvms:
_caller_jvms = new (C) JVMState(caller_jvms->method(), caller_tree->caller_jvms()); _caller_jvms = new (C) JVMState(caller_jvms->method(), caller_tree->caller_jvms());
_caller_jvms->set_bci(caller_jvms->bci()); _caller_jvms->set_bci(caller_jvms->bci());
assert(!caller_jvms->should_reexecute(), "there should be no reexecute bytecode with inlining");
} }
assert(_caller_jvms->same_calls_as(caller_jvms), "consistent JVMS"); assert(_caller_jvms->same_calls_as(caller_jvms), "consistent JVMS");
assert((caller_tree == NULL ? 0 : caller_tree->inline_depth() + 1) == inline_depth(), "correct (redundant) depth parameter"); assert((caller_tree == NULL ? 0 : caller_tree->inline_depth() + 1) == inline_depth(), "correct (redundant) depth parameter");

View file

@ -223,6 +223,7 @@ uint TailJumpNode::match_edge(uint idx) const {
JVMState::JVMState(ciMethod* method, JVMState* caller) { JVMState::JVMState(ciMethod* method, JVMState* caller) {
assert(method != NULL, "must be valid call site"); assert(method != NULL, "must be valid call site");
_method = method; _method = method;
_reexecute = Reexecute_Undefined;
debug_only(_bci = -99); // random garbage value debug_only(_bci = -99); // random garbage value
debug_only(_map = (SafePointNode*)-1); debug_only(_map = (SafePointNode*)-1);
_caller = caller; _caller = caller;
@ -237,6 +238,7 @@ JVMState::JVMState(ciMethod* method, JVMState* caller) {
JVMState::JVMState(int stack_size) { JVMState::JVMState(int stack_size) {
_method = NULL; _method = NULL;
_bci = InvocationEntryBci; _bci = InvocationEntryBci;
_reexecute = Reexecute_Undefined;
debug_only(_map = (SafePointNode*)-1); debug_only(_map = (SafePointNode*)-1);
_caller = NULL; _caller = NULL;
_depth = 1; _depth = 1;
@ -269,6 +271,7 @@ bool JVMState::same_calls_as(const JVMState* that) const {
if (p->_method != q->_method) return false; if (p->_method != q->_method) return false;
if (p->_method == NULL) return true; // bci is irrelevant if (p->_method == NULL) return true; // bci is irrelevant
if (p->_bci != q->_bci) return false; if (p->_bci != q->_bci) return false;
if (p->_reexecute != q->_reexecute) return false;
p = p->caller(); p = p->caller();
q = q->caller(); q = q->caller();
if (p == q) return true; if (p == q) return true;
@ -490,6 +493,7 @@ void JVMState::dump_spec(outputStream *st) const {
if (!printed) if (!printed)
_method->print_short_name(st); _method->print_short_name(st);
st->print(" @ bci:%d",_bci); st->print(" @ bci:%d",_bci);
st->print(" reexecute:%s", _reexecute==Reexecute_True?"true":"false");
} else { } else {
st->print(" runtime stub"); st->print(" runtime stub");
} }
@ -509,8 +513,8 @@ void JVMState::dump_on(outputStream* st) const {
} }
_map->dump(2); _map->dump(2);
} }
st->print("JVMS depth=%d loc=%d stk=%d mon=%d scalar=%d end=%d mondepth=%d sp=%d bci=%d method=", st->print("JVMS depth=%d loc=%d stk=%d mon=%d scalar=%d end=%d mondepth=%d sp=%d bci=%d reexecute=%s method=",
depth(), locoff(), stkoff(), monoff(), scloff(), endoff(), monitor_depth(), sp(), bci()); depth(), locoff(), stkoff(), monoff(), scloff(), endoff(), monitor_depth(), sp(), bci(), should_reexecute()?"true":"false");
if (_method == NULL) { if (_method == NULL) {
st->print_cr("(none)"); st->print_cr("(none)");
} else { } else {
@ -537,6 +541,7 @@ void dump_jvms(JVMState* jvms) {
JVMState* JVMState::clone_shallow(Compile* C) const { JVMState* JVMState::clone_shallow(Compile* C) const {
JVMState* n = has_method() ? new (C) JVMState(_method, _caller) : new (C) JVMState(0); JVMState* n = has_method() ? new (C) JVMState(_method, _caller) : new (C) JVMState(0);
n->set_bci(_bci); n->set_bci(_bci);
n->_reexecute = _reexecute;
n->set_locoff(_locoff); n->set_locoff(_locoff);
n->set_stkoff(_stkoff); n->set_stkoff(_stkoff);
n->set_monoff(_monoff); n->set_monoff(_monoff);

View file

@ -178,6 +178,13 @@ public:
// This provides a way to map the optimized program back into the interpreter, // This provides a way to map the optimized program back into the interpreter,
// or to let the GC mark the stack. // or to let the GC mark the stack.
class JVMState : public ResourceObj { class JVMState : public ResourceObj {
public:
typedef enum {
Reexecute_Undefined = -1, // not defined -- will be translated into false later
Reexecute_False = 0, // false -- do not reexecute
Reexecute_True = 1 // true -- reexecute the bytecode
} ReexecuteState; //Reexecute State
private: private:
JVMState* _caller; // List pointer for forming scope chains JVMState* _caller; // List pointer for forming scope chains
uint _depth; // One mroe than caller depth, or one. uint _depth; // One mroe than caller depth, or one.
@ -188,10 +195,12 @@ private:
uint _endoff; // Offset to end of input edge mapping uint _endoff; // Offset to end of input edge mapping
uint _sp; // Jave Expression Stack Pointer for this state uint _sp; // Jave Expression Stack Pointer for this state
int _bci; // Byte Code Index of this JVM point int _bci; // Byte Code Index of this JVM point
ReexecuteState _reexecute; // Whether this bytecode need to be re-executed
ciMethod* _method; // Method Pointer ciMethod* _method; // Method Pointer
SafePointNode* _map; // Map node associated with this scope SafePointNode* _map; // Map node associated with this scope
public: public:
friend class Compile; friend class Compile;
friend class PreserveReexecuteState;
// Because JVMState objects live over the entire lifetime of the // Because JVMState objects live over the entire lifetime of the
// Compile object, they are allocated into the comp_arena, which // Compile object, they are allocated into the comp_arena, which
@ -224,6 +233,8 @@ public:
uint sp() const { return _sp; } uint sp() const { return _sp; }
int bci() const { return _bci; } int bci() const { return _bci; }
bool should_reexecute() const { return _reexecute==Reexecute_True; }
bool is_reexecute_undefined() const { return _reexecute==Reexecute_Undefined; }
bool has_method() const { return _method != NULL; } bool has_method() const { return _method != NULL; }
ciMethod* method() const { assert(has_method(), ""); return _method; } ciMethod* method() const { assert(has_method(), ""); return _method; }
JVMState* caller() const { return _caller; } JVMState* caller() const { return _caller; }
@ -267,7 +278,9 @@ public:
} }
void set_map(SafePointNode *map) { _map = map; } void set_map(SafePointNode *map) { _map = map; }
void set_sp(uint sp) { _sp = sp; } void set_sp(uint sp) { _sp = sp; }
void set_bci(int bci) { _bci = bci; } // _reexecute is initialized to "undefined" for a new bci
void set_bci(int bci) {if(_bci != bci)_reexecute=Reexecute_Undefined; _bci = bci; }
void set_should_reexecute(bool reexec) {_reexecute = reexec ? Reexecute_True : Reexecute_False;}
// Miscellaneous utility functions // Miscellaneous utility functions
JVMState* clone_deep(Compile* C) const; // recursively clones caller chain JVMState* clone_deep(Compile* C) const; // recursively clones caller chain

View file

@ -620,6 +620,16 @@ BuildCutout::~BuildCutout() {
assert(kit->stopped(), "cutout code must stop, throw, return, etc."); assert(kit->stopped(), "cutout code must stop, throw, return, etc.");
} }
//---------------------------PreserveReexecuteState----------------------------
PreserveReexecuteState::PreserveReexecuteState(GraphKit* kit) {
_kit = kit;
_sp = kit->sp();
_reexecute = kit->jvms()->_reexecute;
}
PreserveReexecuteState::~PreserveReexecuteState() {
_kit->jvms()->_reexecute = _reexecute;
_kit->set_sp(_sp);
}
//------------------------------clone_map-------------------------------------- //------------------------------clone_map--------------------------------------
// Implementation of PreserveJVMState // Implementation of PreserveJVMState
@ -738,6 +748,18 @@ bool GraphKit::dead_locals_are_killed() {
#endif //ASSERT #endif //ASSERT
// Helper function for enforcing certain bytecodes to reexecute if
// deoptimization happens
static bool should_reexecute_implied_by_bytecode(JVMState *jvms) {
ciMethod* cur_method = jvms->method();
int cur_bci = jvms->bci();
if (cur_method != NULL && cur_bci != InvocationEntryBci) {
Bytecodes::Code code = cur_method->java_code_at_bci(cur_bci);
return Interpreter::bytecode_should_reexecute(code);
} else
return false;
}
// Helper function for adding JVMState and debug information to node // Helper function for adding JVMState and debug information to node
void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) {
// Add the safepoint edges to the call (or other safepoint). // Add the safepoint edges to the call (or other safepoint).
@ -781,6 +803,13 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) {
JVMState* out_jvms = youngest_jvms->clone_deep(C); JVMState* out_jvms = youngest_jvms->clone_deep(C);
call->set_jvms(out_jvms); // Start jvms list for call node call->set_jvms(out_jvms); // Start jvms list for call node
// For a known set of bytecodes, the interpreter should reexecute them if
// deoptimization happens. We set the reexecute state for them here
if (out_jvms->is_reexecute_undefined() && //don't change if already specified
should_reexecute_implied_by_bytecode(out_jvms)) {
out_jvms->set_should_reexecute(true); //NOTE: youngest_jvms not changed
}
// Presize the call: // Presize the call:
debug_only(uint non_debug_edges = call->req()); debug_only(uint non_debug_edges = call->req());
call->add_req_batch(top(), youngest_jvms->debug_depth()); call->add_req_batch(top(), youngest_jvms->debug_depth());

View file

@ -763,3 +763,16 @@ class BuildCutout: public PreserveJVMState {
BuildCutout(GraphKit* kit, Node* p, float prob, float cnt = COUNT_UNKNOWN); BuildCutout(GraphKit* kit, Node* p, float prob, float cnt = COUNT_UNKNOWN);
~BuildCutout(); ~BuildCutout();
}; };
// Helper class to preserve the original _reexecute bit and _sp and restore
// them back
class PreserveReexecuteState: public StackObj {
protected:
GraphKit* _kit;
uint _sp;
JVMState::ReexecuteState _reexecute;
public:
PreserveReexecuteState(GraphKit* kit);
~PreserveReexecuteState();
};

View file

@ -3222,13 +3222,20 @@ bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) {
} }
if (!stopped()) { if (!stopped()) {
Node *newcopy;
//set the original stack and the reexecute bit for the interpreter to reexecute
//the bytecode that invokes Arrays.copyOf if deoptimization happens
{ PreserveReexecuteState preexecs(this);
_sp += nargs;
jvms()->set_should_reexecute(true);
// How many elements will we copy from the original? // How many elements will we copy from the original?
// The answer is MinI(orig_length - start, length). // The answer is MinI(orig_length - start, length).
Node* orig_tail = _gvn.transform( new(C, 3) SubINode(orig_length, start) ); Node* orig_tail = _gvn.transform( new(C, 3) SubINode(orig_length, start) );
Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length); Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length);
const bool raw_mem_only = true; const bool raw_mem_only = true;
Node* newcopy = new_array(klass_node, length, nargs, raw_mem_only); newcopy = new_array(klass_node, length, 0, raw_mem_only);
// Generate a direct call to the right arraycopy function(s). // Generate a direct call to the right arraycopy function(s).
// We know the copy is disjoint but we might not know if the // We know the copy is disjoint but we might not know if the
@ -3240,6 +3247,7 @@ bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) {
generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT,
original, start, newcopy, intcon(0), moved, original, start, newcopy, intcon(0), moved,
disjoint_bases, length_never_negative); disjoint_bases, length_never_negative);
} //original reexecute and sp are set back here
push(newcopy); push(newcopy);
} }
@ -4024,6 +4032,12 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) {
int raw_adr_idx = Compile::AliasIdxRaw; int raw_adr_idx = Compile::AliasIdxRaw;
const bool raw_mem_only = true; const bool raw_mem_only = true;
//set the original stack and the reexecute bit for the interpreter to reexecute
//the bytecode that invokes Object.clone if deoptimization happens
{ PreserveReexecuteState preexecs(this);
_sp += nargs;
jvms()->set_should_reexecute(true);
Node* array_ctl = generate_array_guard(obj_klass, (RegionNode*)NULL); Node* array_ctl = generate_array_guard(obj_klass, (RegionNode*)NULL);
if (array_ctl != NULL) { if (array_ctl != NULL) {
// It's an array. // It's an array.
@ -4031,7 +4045,7 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) {
set_control(array_ctl); set_control(array_ctl);
Node* obj_length = load_array_length(obj); Node* obj_length = load_array_length(obj);
Node* obj_size = NULL; Node* obj_size = NULL;
Node* alloc_obj = new_array(obj_klass, obj_length, nargs, Node* alloc_obj = new_array(obj_klass, obj_length, 0,
raw_mem_only, &obj_size); raw_mem_only, &obj_size);
if (!use_ReduceInitialCardMarks()) { if (!use_ReduceInitialCardMarks()) {
@ -4127,6 +4141,7 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) {
result_i_o ->set_req(_slow_path, i_o()); result_i_o ->set_req(_slow_path, i_o());
result_mem ->set_req(_slow_path, reset_memory()); result_mem ->set_req(_slow_path, reset_memory());
} }
} //original reexecute and sp are set back here
// Return the combined state. // Return the combined state.
set_control( _gvn.transform(result_reg) ); set_control( _gvn.transform(result_reg) );

View file

@ -911,8 +911,9 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) {
ciMethod* scope_method = method ? method : _method; ciMethod* scope_method = method ? method : _method;
// Describe the scope here // Describe the scope here
assert(jvms->bci() >= InvocationEntryBci && jvms->bci() <= 0x10000, "must be a valid or entry BCI"); 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. // Now we can describe the scope.
debug_info()->describe_scope(safepoint_pc_offset,scope_method,jvms->bci(),locvals,expvals,monvals); debug_info()->describe_scope(safepoint_pc_offset,scope_method,jvms->bci(),jvms->should_reexecute(),locvals,expvals,monvals);
} // End jvms loop } // End jvms loop
// Mark the end of the scope set. // Mark the end of the scope set.
@ -994,7 +995,8 @@ void NonSafepointEmitter::emit_non_safepoint() {
for (int depth = 1; depth <= max_depth; depth++) { for (int depth = 1; depth <= max_depth; depth++) {
JVMState* jvms = youngest_jvms->of_depth(depth); JVMState* jvms = youngest_jvms->of_depth(depth);
ciMethod* method = jvms->has_method() ? jvms->method() : NULL; ciMethod* method = jvms->has_method() ? jvms->method() : NULL;
debug_info->describe_scope(pc_offset, method, jvms->bci()); assert(!jvms->should_reexecute() || depth==max_depth, "reexecute allowed only for the youngest");
debug_info->describe_scope(pc_offset, method, jvms->bci(), jvms->should_reexecute());
} }
// Mark the end of the scope set. // Mark the end of the scope set.

View file

@ -402,7 +402,12 @@ inline void vframeStreamCommon::fill_from_compiled_frame(int decode_offset) {
DebugInfoReadStream buffer(nm(), decode_offset); DebugInfoReadStream buffer(nm(), decode_offset);
_sender_decode_offset = buffer.read_int(); _sender_decode_offset = buffer.read_int();
_method = methodOop(buffer.read_oop()); _method = methodOop(buffer.read_oop());
_bci = buffer.read_bci(); // Deoptimization needs reexecute bit to determine whether to reexecute the bytecode
// only at the time when it "unpack_frames", and the reexecute bit info could always
// be obtained from the scopeDesc in the compiledVFrame. As a result, we don't keep
// the reexecute bit here.
bool dummy_reexecute;
_bci = buffer.read_bci_and_reexecute(dummy_reexecute);
assert(_method->is_method(), "checking type of decoded method"); assert(_method->is_method(), "checking type of decoded method");
} }

View file

@ -44,6 +44,7 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) {
_method = vf->method(); _method = vf->method();
_bci = vf->raw_bci(); _bci = vf->raw_bci();
_reexecute = vf->should_reexecute();
int index; int index;
@ -148,16 +149,20 @@ void vframeArrayElement::unpack_on_stack(int callee_parameters,
// C++ interpreter doesn't need a pc since it will figure out what to do when it // C++ interpreter doesn't need a pc since it will figure out what to do when it
// begins execution // begins execution
address pc; address pc;
bool use_next_mdp; // true if we should use the mdp associated with the next bci bool use_next_mdp = false; // true if we should use the mdp associated with the next bci
// rather than the one associated with bcp // rather than the one associated with bcp
if (raw_bci() == SynchronizationEntryBCI) { if (raw_bci() == SynchronizationEntryBCI) {
// We are deoptimizing while hanging in prologue code for synchronized method // We are deoptimizing while hanging in prologue code for synchronized method
bcp = method()->bcp_from(0); // first byte code bcp = method()->bcp_from(0); // first byte code
pc = Interpreter::deopt_entry(vtos, 0); // step = 0 since we don't skip current bytecode pc = Interpreter::deopt_entry(vtos, 0); // step = 0 since we don't skip current bytecode
use_next_mdp = false; } else if (should_reexecute()) { //reexecute this bytecode
assert(is_top_frame, "reexecute allowed only for the top frame");
bcp = method()->bcp_from(bci());
pc = Interpreter::deopt_reexecute_entry(method(), bcp);
} else { } else {
bcp = method()->bcp_from(bci()); bcp = method()->bcp_from(bci());
pc = Interpreter::continuation_for(method(), bcp, callee_parameters, is_top_frame, use_next_mdp); pc = Interpreter::deopt_continue_after_entry(method(), bcp, callee_parameters, is_top_frame);
use_next_mdp = true;
} }
assert(Bytecodes::is_defined(*bcp), "must be a valid bytecode"); assert(Bytecodes::is_defined(*bcp), "must be a valid bytecode");

View file

@ -42,6 +42,7 @@ class vframeArrayElement : public _ValueObj {
frame _frame; // the interpreter frame we will unpack into frame _frame; // the interpreter frame we will unpack into
int _bci; // raw bci for this vframe int _bci; // raw bci for this vframe
bool _reexecute; // whether sould we reexecute this bytecode
methodOop _method; // the method for this vframe methodOop _method; // the method for this vframe
MonitorChunk* _monitors; // active monitors for this vframe MonitorChunk* _monitors; // active monitors for this vframe
StackValueCollection* _locals; StackValueCollection* _locals;
@ -54,6 +55,7 @@ class vframeArrayElement : public _ValueObj {
int bci(void) const; int bci(void) const;
int raw_bci(void) const { return _bci; } int raw_bci(void) const { return _bci; }
bool should_reexecute(void) const { return _reexecute; }
methodOop method(void) const { return _method; } methodOop method(void) const { return _method; }

View file

@ -276,6 +276,15 @@ int compiledVFrame::raw_bci() const {
return scope()->bci(); return scope()->bci();
} }
bool compiledVFrame::should_reexecute() const {
if (scope() == NULL) {
// native nmethods have no scope the method/bci is implied
nmethod* nm = code();
assert(nm->is_native_method(), "must be native");
return false;
}
return scope()->should_reexecute();
}
vframe* compiledVFrame::sender() const { vframe* compiledVFrame::sender() const {
const frame f = fr(); const frame f = fr();

View file

@ -27,6 +27,7 @@ class compiledVFrame: public javaVFrame {
// JVM state // JVM state
methodOop method() const; methodOop method() const;
int bci() const; int bci() const;
bool should_reexecute() const;
StackValueCollection* locals() const; StackValueCollection* locals() const;
StackValueCollection* expressions() const; StackValueCollection* expressions() const;
GrowableArray<MonitorInfo*>* monitors() const; GrowableArray<MonitorInfo*>* monitors() const;

View file

@ -0,0 +1,62 @@
/*
* Copyright 2009 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 6833129
* @summary Object.clone() and Arrays.copyOf ignore coping with -XX:+DeoptimizeALot
* @run main/othervm -Xbatch -XX:+DeoptimizeALot Test
*/
public class Test{
public static void init(int src[]) {
for (int i =0; i<src.length; i++) {
src[i] = i;
}
}
public static void clone_and_verify(int src[]) {
for (int i = 0; i < src.length; i++) {
int [] src_clone = src.clone();
if (src[i] != src_clone[i]) {
System.out.println("Error: allocated but not copied: ");
for( int j =0; j < src_clone.length; j++)
System.out.print(" " + src_clone[j]);
System.out.println();
System.exit(97);
}
}
}
public static void test() {
int[] src = new int[34];
init(src);
clone_and_verify(src);
}
public static void main(String[] args) {
for (int i=0; i< 20000; i++) {
test();
}
}
}