From 5c8576ad9ce7b4937e90b86faf470137724385e0 Mon Sep 17 00:00:00 2001 From: cfang Date: Fri, 31 Jul 2009 17:12:33 -0700 Subject: [PATCH] 6833129: specjvm98 fails with NullPointerException in the compiler with -XX:DeoptimizeALot Summary: developed a reexecute logic for the interpreter to reexecute the bytecode when deopt happens Reviewed-by: kvn, never, jrose, twisti --- .../jvm/hotspot/code/DebugInfoReadStream.java | 4 - .../classes/sun/jvm/hotspot/code/PCDesc.java | 1 + .../sun/jvm/hotspot/code/ScopeDesc.java | 12 +- src/share/vm/c1/c1_IR.cpp | 11 +- src/share/vm/c1/c1_IR.hpp | 11 +- src/share/vm/c1/c1_LIRAssembler.cpp | 3 +- src/share/vm/classfile/javaClasses.cpp | 5 +- src/share/vm/code/debugInfo.hpp | 6 +- src/share/vm/code/debugInfoRec.cpp | 3 +- src/share/vm/code/debugInfoRec.hpp | 1 + src/share/vm/code/scopeDesc.cpp | 6 +- src/share/vm/code/scopeDesc.hpp | 9 +- .../vm/interpreter/abstractInterpreter.hpp | 14 +- src/share/vm/interpreter/interpreter.cpp | 123 ++++----- .../vm/interpreter/templateInterpreter.cpp | 45 ++-- .../vm/interpreter/templateInterpreter.hpp | 14 +- src/share/vm/opto/bytecodeInfo.cpp | 1 + src/share/vm/opto/callnode.cpp | 9 +- src/share/vm/opto/callnode.hpp | 35 ++- src/share/vm/opto/graphKit.cpp | 29 +++ src/share/vm/opto/graphKit.hpp | 13 + src/share/vm/opto/library_call.cpp | 237 ++++++++++-------- src/share/vm/opto/output.cpp | 6 +- src/share/vm/runtime/vframe.hpp | 7 +- src/share/vm/runtime/vframeArray.cpp | 13 +- src/share/vm/runtime/vframeArray.hpp | 4 +- src/share/vm/runtime/vframe_hp.cpp | 9 + src/share/vm/runtime/vframe_hp.hpp | 11 +- test/compiler/6833129/Test.java | 62 +++++ 29 files changed, 464 insertions(+), 240 deletions(-) create mode 100644 test/compiler/6833129/Test.java diff --git a/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java b/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java index fa60c4983..dd0d781c4 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java +++ b/agent/src/share/classes/sun/jvm/hotspot/code/DebugInfoReadStream.java @@ -81,8 +81,4 @@ public class DebugInfoReadStream extends CompressedReadStream { Assert.that(false, "should not reach here"); return null; } - - public int readBCI() { - return readInt() + InvocationEntryBCI; - } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java b/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java index fe0388bb6..6883b6791 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java +++ b/agent/src/share/classes/sun/jvm/hotspot/code/PCDesc.java @@ -82,6 +82,7 @@ public class PCDesc extends VMObject { tty.print(" "); sd.getMethod().printValueOn(tty); tty.print(" @" + sd.getBCI()); + tty.print(" reexecute=" + sd.getReexecute()); tty.println(); } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java b/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java index e75365ae2..2c8c2557c 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java +++ b/agent/src/share/classes/sun/jvm/hotspot/code/ScopeDesc.java @@ -41,6 +41,7 @@ public class ScopeDesc { private NMethod code; private Method method; private int bci; + private boolean reexecute; /** Decoding offsets */ private int decodeOffset; private int senderDecodeOffset; @@ -61,7 +62,7 @@ public class ScopeDesc { senderDecodeOffset = stream.readInt(); method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle()); - bci = stream.readBCI(); + setBCIAndReexecute(stream.readInt()); // Decode offsets for body and sender localsDecodeOffset = stream.readInt(); expressionsDecodeOffset = stream.readInt(); @@ -78,7 +79,7 @@ public class ScopeDesc { senderDecodeOffset = stream.readInt(); method = (Method) VM.getVM().getObjectHeap().newOop(stream.readOopHandle()); - bci = stream.readBCI(); + setBCIAndReexecute(stream.readInt()); // Decode offsets for body and sender localsDecodeOffset = stream.readInt(); expressionsDecodeOffset = stream.readInt(); @@ -88,6 +89,7 @@ public class ScopeDesc { public NMethod getNMethod() { return code; } public Method getMethod() { return method; } public int getBCI() { return bci; } + public boolean getReexecute() {return reexecute;} /** Returns a List<ScopeValue> */ public List getLocals() { @@ -150,6 +152,7 @@ public class ScopeDesc { tty.print("ScopeDesc for "); method.printValueOn(tty); tty.println(" @bci " + bci); + tty.println(" reexecute: " + reexecute); } // FIXME: add more accessors @@ -157,6 +160,11 @@ public class ScopeDesc { //-------------------------------------------------------------------------------- // 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) { return new DebugInfoReadStream(code, decodeOffset, objects); diff --git a/src/share/vm/c1/c1_IR.cpp b/src/share/vm/c1/c1_IR.cpp index f1a268de4..47e1c0d41 100644 --- a/src/share/vm/c1/c1_IR.cpp +++ b/src/share/vm/c1/c1_IR.cpp @@ -208,6 +208,15 @@ int IRScope::top_scope_bci() const { 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 @@ -253,7 +262,7 @@ CodeEmitInfo::CodeEmitInfo(CodeEmitInfo* info, bool lock_stack_only) void CodeEmitInfo::record_debug_info(DebugInformationRecorder* recorder, int pc_offset) { // record the safepoint before recording the debug info for enclosing scopes 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); } diff --git a/src/share/vm/c1/c1_IR.hpp b/src/share/vm/c1/c1_IR.hpp index ecd5b4ba5..a3bcc2a47 100644 --- a/src/share/vm/c1/c1_IR.hpp +++ b/src/share/vm/c1/c1_IR.hpp @@ -239,15 +239,20 @@ class IRScopeDebugInfo: public CompilationResourceObj { GrowableArray* monitors() { return _monitors; } 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) { // 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* expvals = recorder->create_scope_values(expressions()); 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); } }; diff --git a/src/share/vm/c1/c1_LIRAssembler.cpp b/src/share/vm/c1/c1_LIRAssembler.cpp index 5a46b03e5..d12a23ca2 100644 --- a/src/share/vm/c1/c1_LIRAssembler.cpp +++ b/src/share/vm/c1/c1_LIRAssembler.cpp @@ -379,7 +379,8 @@ void LIR_Assembler::record_non_safepoint_debug_info() { ValueStack* s = nth_oldest(vstack, n, s_bci); if (s == NULL) break; 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); diff --git a/src/share/vm/classfile/javaClasses.cpp b/src/share/vm/classfile/javaClasses.cpp index 248a10336..a11f6738a 100644 --- a/src/share/vm/classfile/javaClasses.cpp +++ b/src/share/vm/classfile/javaClasses.cpp @@ -1229,10 +1229,13 @@ void java_lang_Throwable::fill_in_stack_trace(Handle throwable, TRAPS) { // Compiled java method case. if (decode_offset != 0) { + bool dummy_reexecute = false; DebugInfoReadStream stream(nm, decode_offset); decode_offset = 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 { if (fr.is_first_frame()) break; address pc = fr.pc(); diff --git a/src/share/vm/code/debugInfo.hpp b/src/share/vm/code/debugInfo.hpp index 13b3775b7..78dcc0cff 100644 --- a/src/share/vm/code/debugInfo.hpp +++ b/src/share/vm/code/debugInfo.hpp @@ -255,7 +255,8 @@ class DebugInfoReadStream : public CompressedReadStream { ScopeValue* read_object_value(); ScopeValue* get_cached_object(); // 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 @@ -268,5 +269,6 @@ class DebugInfoWriteStream : public CompressedWriteStream { public: DebugInfoWriteStream(DebugInformationRecorder* recorder, int initial_size); 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)); } }; diff --git a/src/share/vm/code/debugInfoRec.cpp b/src/share/vm/code/debugInfoRec.cpp index 04b58f4c7..d7a90c0b2 100644 --- a/src/share/vm/code/debugInfoRec.cpp +++ b/src/share/vm/code/debugInfoRec.cpp @@ -280,6 +280,7 @@ int DebugInformationRecorder::find_sharable_decode_offset(int stream_offset) { void DebugInformationRecorder::describe_scope(int pc_offset, ciMethod* method, int bci, + bool reexecute, DebugToken* locals, DebugToken* expressions, DebugToken* monitors) { @@ -297,7 +298,7 @@ void DebugInformationRecorder::describe_scope(int pc_offset, // serialize scope jobject method_enc = (method == NULL)? NULL: method->encoding(); stream()->write_int(oop_recorder()->find_index(method_enc)); - stream()->write_bci(bci); + stream()->write_bci_and_reexecute(bci, reexecute); assert(method == NULL || (method->is_native() && bci == 0) || (!method->is_native() && 0 <= bci && bci < method->code_size()) || diff --git a/src/share/vm/code/debugInfoRec.hpp b/src/share/vm/code/debugInfoRec.hpp index 9e775c964..4ce5f1e45 100644 --- a/src/share/vm/code/debugInfoRec.hpp +++ b/src/share/vm/code/debugInfoRec.hpp @@ -87,6 +87,7 @@ class DebugInformationRecorder: public ResourceObj { void describe_scope(int pc_offset, ciMethod* method, int bci, + bool reexecute, DebugToken* locals = NULL, DebugToken* expressions = NULL, DebugToken* monitors = NULL); diff --git a/src/share/vm/code/scopeDesc.cpp b/src/share/vm/code/scopeDesc.cpp index 7aed17f76..b5f4bc652 100644 --- a/src/share/vm/code/scopeDesc.cpp +++ b/src/share/vm/code/scopeDesc.cpp @@ -46,6 +46,7 @@ ScopeDesc::ScopeDesc(const ScopeDesc* parent) { _decode_offset = parent->_sender_decode_offset; _objects = parent->_objects; decode_body(); + assert(_reexecute == false, "reexecute not allowed"); } @@ -56,6 +57,7 @@ void ScopeDesc::decode_body() { _sender_decode_offset = DebugInformationRecorder::serialized_null; _method = methodHandle(_code->method()); _bci = InvocationEntryBci; + _reexecute = false; _locals_decode_offset = DebugInformationRecorder::serialized_null; _expressions_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(); _method = methodHandle((methodOop) stream->read_oop()); - _bci = stream->read_bci(); + _bci = stream->read_bci_and_reexecute(_reexecute); + // decode offsets for body and sender _locals_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_cr(" offset: %d", _decode_offset); 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(" stack: %d", _expressions_decode_offset); st->print_cr(" monitor: %d", _monitors_decode_offset); diff --git a/src/share/vm/code/scopeDesc.hpp b/src/share/vm/code/scopeDesc.hpp index 35c99176c..bd042dc37 100644 --- a/src/share/vm/code/scopeDesc.hpp +++ b/src/share/vm/code/scopeDesc.hpp @@ -39,7 +39,8 @@ class SimpleScopeDesc : public StackObj { DebugInfoReadStream buffer(code, pc_desc->scope_decode_offset()); int ignore_sender = buffer.read_int(); _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; } @@ -60,8 +61,9 @@ class ScopeDesc : public ResourceObj { ScopeDesc(const nmethod* code, int decode_offset); // JVM state - methodHandle method() const { return _method; } - int bci() const { return _bci; } + methodHandle method() const { return _method; } + int bci() const { return _bci; } + bool should_reexecute() const { return _reexecute; } GrowableArray* locals(); GrowableArray* expressions(); @@ -86,6 +88,7 @@ class ScopeDesc : public ResourceObj { // JVM state methodHandle _method; int _bci; + bool _reexecute; // Decoding offsets int _decode_offset; diff --git a/src/share/vm/interpreter/abstractInterpreter.hpp b/src/share/vm/interpreter/abstractInterpreter.hpp index 260288b09..78323ee2a 100644 --- a/src/share/vm/interpreter/abstractInterpreter.hpp +++ b/src/share/vm/interpreter/abstractInterpreter.hpp @@ -122,11 +122,15 @@ class AbstractInterpreter: AllStatic { static int size_top_interpreter_activation(methodOop method); // Deoptimization support - static address continuation_for(methodOop method, - address bcp, - int callee_parameters, - bool is_top_frame, - bool& use_next_mdp); + // Compute the entry address for continuation after + static address deopt_continue_after_entry(methodOop method, + address bcp, + int callee_parameters, + bool is_top_frame); + // 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: static int size_activation(methodOop method, diff --git a/src/share/vm/interpreter/interpreter.cpp b/src/share/vm/interpreter/interpreter.cpp index 16715cbc3..b4f1007bc 100644 --- a/src/share/vm/interpreter/interpreter.cpp +++ b/src/share/vm/interpreter/interpreter.cpp @@ -284,27 +284,75 @@ static BasicType constant_pool_type(methodOop method, int index) { //------------------------------------------------------------------------------------------------------------------------ // Deoptimization support -// If deoptimization happens, this method returns the point where to continue in -// interpreter. For calls (invokexxxx, newxxxx) the continuation is at next -// 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) { +// If deoptimization happens, this function returns the point of next bytecode to continue execution +address AbstractInterpreter::deopt_continue_after_entry(methodOop method, address bcp, int callee_parameters, bool is_top_frame) { assert(method->contains(bcp), "just checkin'"); Bytecodes::Code code = Bytecodes::java_code_at(bcp); + assert(!Interpreter::bytecode_should_reexecute(code), "should not reexecute"); int bci = method->bci_from(bcp); int length = -1; // initial value for debugging // compute continuation length length = Bytecodes::length_at(bcp); // compute result type 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) { + case Bytecodes::_invokevirtual : + case Bytecodes::_invokespecial : + case Bytecodes::_invokestatic : + case Bytecodes::_invokeinterface: { + Thread *thread = Thread::current(); + ResourceMark rm(thread); + methodHandle mh(thread, method); + type = Bytecode_invoke_at(mh, bci)->result_type(thread); + // since the cache entry might not be initialized: + // (NOT needed for the old calling convension) + if (!is_top_frame) { + int index = Bytes::get_native_u2(bcp+1); + method->constants()->cache()->entry_at(index)->set_parameter_size(callee_parameters); + } + break; + } + + case Bytecodes::_ldc : + type = constant_pool_type( method, *(bcp+1) ); + break; + + case Bytecodes::_ldc_w : // fall through + case Bytecodes::_ldc2_w: + type = constant_pool_type( method, Bytes::get_Java_u2(bcp+1) ); + break; + + default: + type = Bytecodes::result_type(code); + break; + } + + // return entry point for computed continuation state & bytecode length + return + is_top_frame + ? Interpreter::deopt_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: @@ -340,56 +388,15 @@ address AbstractInterpreter::continuation_for(methodOop method, address bcp, int 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 + //special case of reexecution 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::_invokespecial : - case Bytecodes::_invokestatic : - case Bytecodes::_invokeinterface: { - Thread *thread = Thread::current(); - ResourceMark rm(thread); - methodHandle mh(thread, method); - type = Bytecode_invoke_at(mh, bci)->result_type(thread); - // since the cache entry might not be initialized: - // (NOT needed for the old calling convension) - if (!is_top_frame) { - int index = Bytes::get_native_u2(bcp+1); - method->constants()->cache()->entry_at(index)->set_parameter_size(callee_parameters); - } - break; - } - - case Bytecodes::_ldc : - type = constant_pool_type( method, *(bcp+1) ); - break; - - case Bytecodes::_ldc_w : // fall through - case Bytecodes::_ldc2_w: - type = constant_pool_type( method, Bytes::get_Java_u2(bcp+1) ); - break; +#endif + return true; default: - type = Bytecodes::result_type(code); - break; + return false; } - - // return entry point for computed continuation state & bytecode length - return - is_top_frame - ? Interpreter::deopt_entry (as_TosState(type), length) - : Interpreter::return_entry(as_TosState(type), length); } void AbstractInterpreterGenerator::bang_stack_shadow_pages(bool native_call) { diff --git a/src/share/vm/interpreter/templateInterpreter.cpp b/src/share/vm/interpreter/templateInterpreter.cpp index 437561732..9f12f44e3 100644 --- a/src/share/vm/interpreter/templateInterpreter.cpp +++ b/src/share/vm/interpreter/templateInterpreter.cpp @@ -605,28 +605,41 @@ 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 -// 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. +//------------------------------------------------------------------------------------------------------------------------ +// Deoptimization support -// Note: deopt_entry(type, 0) means reexecute bytecode -// deopt_entry(type, length) means continue at next bytecode +// If deoptimization happens, this function returns the point of next bytecode to continue execution +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'"); Bytecodes::Code code = Bytecodes::java_code_at(bcp); if (code == Bytecodes::_return) { - // This is used for deopt during registration of finalizers - // during Object.. We simply need to resume execution at - // the standard return vtos bytecode to pop the frame normally. - // reexecuting the real bytecode would cause double registration - // of the finalizable object. - assert(is_top_frame, "must be on top"); - return _normal_table.entry(Bytecodes::_return).entry(vtos); + // This is used for deopt during registration of finalizers + // during Object.. We simply need to resume execution at + // the standard return vtos bytecode to pop the frame normally. + // reexecuting the real bytecode would cause double registration + // of the finalizable object. + return _normal_table.entry(Bytecodes::_return).entry(vtos); + } else { + 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::continuation_for(method, bcp, callee_parameters, is_top_frame, use_next_mdp); + return AbstractInterpreter::bytecode_should_reexecute(code); } } diff --git a/src/share/vm/interpreter/templateInterpreter.hpp b/src/share/vm/interpreter/templateInterpreter.hpp index 2c78e31fc..7de665b88 100644 --- a/src/share/vm/interpreter/templateInterpreter.hpp +++ b/src/share/vm/interpreter/templateInterpreter.hpp @@ -171,11 +171,15 @@ class TemplateInterpreter: public AbstractInterpreter { static void ignore_safepoints(); // ignores safepoints // Deoptimization support - static address continuation_for(methodOop method, - address bcp, - int callee_parameters, - bool is_top_frame, - bool& use_next_mdp); + // Compute the entry address for continuation after + static address deopt_continue_after_entry(methodOop method, + address bcp, + int callee_parameters, + bool is_top_frame); + // 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" diff --git a/src/share/vm/opto/bytecodeInfo.cpp b/src/share/vm/opto/bytecodeInfo.cpp index 20e1faff5..18b8b4d01 100644 --- a/src/share/vm/opto/bytecodeInfo.cpp +++ b/src/share/vm/opto/bytecodeInfo.cpp @@ -37,6 +37,7 @@ InlineTree::InlineTree( Compile* c, const InlineTree *caller_tree, ciMethod* cal // Keep a private copy of the caller_jvms: _caller_jvms = new (C) JVMState(caller_jvms->method(), caller_tree->caller_jvms()); _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_tree == NULL ? 0 : caller_tree->inline_depth() + 1) == inline_depth(), "correct (redundant) depth parameter"); diff --git a/src/share/vm/opto/callnode.cpp b/src/share/vm/opto/callnode.cpp index 94a261d46..2f0483afb 100644 --- a/src/share/vm/opto/callnode.cpp +++ b/src/share/vm/opto/callnode.cpp @@ -223,6 +223,7 @@ uint TailJumpNode::match_edge(uint idx) const { JVMState::JVMState(ciMethod* method, JVMState* caller) { assert(method != NULL, "must be valid call site"); _method = method; + _reexecute = Reexecute_Undefined; debug_only(_bci = -99); // random garbage value debug_only(_map = (SafePointNode*)-1); _caller = caller; @@ -237,6 +238,7 @@ JVMState::JVMState(ciMethod* method, JVMState* caller) { JVMState::JVMState(int stack_size) { _method = NULL; _bci = InvocationEntryBci; + _reexecute = Reexecute_Undefined; debug_only(_map = (SafePointNode*)-1); _caller = NULL; _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 == NULL) return true; // bci is irrelevant if (p->_bci != q->_bci) return false; + if (p->_reexecute != q->_reexecute) return false; p = p->caller(); q = q->caller(); if (p == q) return true; @@ -490,6 +493,7 @@ void JVMState::dump_spec(outputStream *st) const { if (!printed) _method->print_short_name(st); st->print(" @ bci:%d",_bci); + st->print(" reexecute:%s", _reexecute==Reexecute_True?"true":"false"); } else { st->print(" runtime stub"); } @@ -509,8 +513,8 @@ void JVMState::dump_on(outputStream* st) const { } _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=", - depth(), locoff(), stkoff(), monoff(), scloff(), endoff(), monitor_depth(), sp(), bci()); + 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(), should_reexecute()?"true":"false"); if (_method == NULL) { st->print_cr("(none)"); } else { @@ -537,6 +541,7 @@ void dump_jvms(JVMState* jvms) { JVMState* JVMState::clone_shallow(Compile* C) const { JVMState* n = has_method() ? new (C) JVMState(_method, _caller) : new (C) JVMState(0); n->set_bci(_bci); + n->_reexecute = _reexecute; n->set_locoff(_locoff); n->set_stkoff(_stkoff); n->set_monoff(_monoff); diff --git a/src/share/vm/opto/callnode.hpp b/src/share/vm/opto/callnode.hpp index e82f7606b..559120aff 100644 --- a/src/share/vm/opto/callnode.hpp +++ b/src/share/vm/opto/callnode.hpp @@ -178,6 +178,13 @@ public: // This provides a way to map the optimized program back into the interpreter, // or to let the GC mark the stack. 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: JVMState* _caller; // List pointer for forming scope chains uint _depth; // One mroe than caller depth, or one. @@ -188,10 +195,12 @@ private: uint _endoff; // Offset to end of input edge mapping uint _sp; // Jave Expression Stack Pointer for this state int _bci; // Byte Code Index of this JVM point + ReexecuteState _reexecute; // Whether this bytecode need to be re-executed ciMethod* _method; // Method Pointer SafePointNode* _map; // Map node associated with this scope public: friend class Compile; + friend class PreserveReexecuteState; // Because JVMState objects live over the entire lifetime of the // Compile object, they are allocated into the comp_arena, which @@ -222,16 +231,18 @@ public: bool is_mon(uint i) const { return i >= _monoff && i < _scloff; } bool is_scl(uint i) const { return i >= _scloff && i < _endoff; } - uint sp() const { return _sp; } - int bci() const { return _bci; } - bool has_method() const { return _method != NULL; } - ciMethod* method() const { assert(has_method(), ""); return _method; } - JVMState* caller() const { return _caller; } - SafePointNode* map() const { return _map; } - uint depth() const { return _depth; } - uint debug_start() const; // returns locoff of root caller - uint debug_end() const; // returns endoff of self - uint debug_size() const { + uint sp() const { return _sp; } + 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; } + ciMethod* method() const { assert(has_method(), ""); return _method; } + JVMState* caller() const { return _caller; } + SafePointNode* map() const { return _map; } + uint depth() const { return _depth; } + uint debug_start() const; // returns locoff of root caller + uint debug_end() const; // returns endoff of self + uint debug_size() const { return loc_size() + sp() + mon_size() + scl_size(); } uint debug_depth() const; // returns sum of debug_size values at all depths @@ -267,7 +278,9 @@ public: } void set_map(SafePointNode *map) { _map = map; } 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 JVMState* clone_deep(Compile* C) const; // recursively clones caller chain diff --git a/src/share/vm/opto/graphKit.cpp b/src/share/vm/opto/graphKit.cpp index 90ffdfca8..955b736ee 100644 --- a/src/share/vm/opto/graphKit.cpp +++ b/src/share/vm/opto/graphKit.cpp @@ -620,6 +620,16 @@ BuildCutout::~BuildCutout() { 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-------------------------------------- // Implementation of PreserveJVMState @@ -738,6 +748,18 @@ bool GraphKit::dead_locals_are_killed() { #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 void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) { // 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); 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: debug_only(uint non_debug_edges = call->req()); call->add_req_batch(top(), youngest_jvms->debug_depth()); diff --git a/src/share/vm/opto/graphKit.hpp b/src/share/vm/opto/graphKit.hpp index cd4fceff9..ae1b5b062 100644 --- a/src/share/vm/opto/graphKit.hpp +++ b/src/share/vm/opto/graphKit.hpp @@ -763,3 +763,16 @@ class BuildCutout: public PreserveJVMState { BuildCutout(GraphKit* kit, Node* p, float prob, float cnt = COUNT_UNKNOWN); ~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(); +}; diff --git a/src/share/vm/opto/library_call.cpp b/src/share/vm/opto/library_call.cpp index 159eaddf1..6b14911da 100644 --- a/src/share/vm/opto/library_call.cpp +++ b/src/share/vm/opto/library_call.cpp @@ -3222,24 +3222,32 @@ bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) { } if (!stopped()) { - // How many elements will we copy from the original? - // The answer is MinI(orig_length - start, length). - Node* orig_tail = _gvn.transform( new(C, 3) SubINode(orig_length, start) ); - Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length); - - const bool raw_mem_only = true; - Node* newcopy = new_array(klass_node, length, nargs, raw_mem_only); - - // Generate a direct call to the right arraycopy function(s). - // We know the copy is disjoint but we might not know if the - // oop stores need checking. - // Extreme case: Arrays.copyOf((Integer[])x, 10, String[].class). - // This will fail a store-check if x contains any non-nulls. - bool disjoint_bases = true; - bool length_never_negative = true; - generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, - original, start, newcopy, intcon(0), moved, - disjoint_bases, length_never_negative); + 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? + // The answer is MinI(orig_length - start, length). + Node* orig_tail = _gvn.transform( new(C, 3) SubINode(orig_length, start) ); + Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length); + + const bool raw_mem_only = true; + newcopy = new_array(klass_node, length, 0, raw_mem_only); + + // Generate a direct call to the right arraycopy function(s). + // We know the copy is disjoint but we might not know if the + // oop stores need checking. + // Extreme case: Arrays.copyOf((Integer[])x, 10, String[].class). + // This will fail a store-check if x contains any non-nulls. + bool disjoint_bases = true; + bool length_never_negative = true; + generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, + original, start, newcopy, intcon(0), moved, + disjoint_bases, length_never_negative); + } //original reexecute and sp are set back here push(newcopy); } @@ -4024,109 +4032,116 @@ bool LibraryCallKit::inline_native_clone(bool is_virtual) { int raw_adr_idx = Compile::AliasIdxRaw; const bool raw_mem_only = true; - Node* array_ctl = generate_array_guard(obj_klass, (RegionNode*)NULL); - if (array_ctl != NULL) { - // It's an array. - PreserveJVMState pjvms(this); - set_control(array_ctl); - Node* obj_length = load_array_length(obj); - Node* obj_size = NULL; - Node* alloc_obj = new_array(obj_klass, obj_length, nargs, - raw_mem_only, &obj_size); - - if (!use_ReduceInitialCardMarks()) { - // If it is an oop array, it requires very special treatment, - // because card marking is required on each card of the array. - Node* is_obja = generate_objArray_guard(obj_klass, (RegionNode*)NULL); - if (is_obja != NULL) { - PreserveJVMState pjvms2(this); - set_control(is_obja); - // Generate a direct call to the right arraycopy function(s). - bool disjoint_bases = true; - bool length_never_negative = true; - generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, - obj, intcon(0), alloc_obj, intcon(0), - obj_length, - disjoint_bases, length_never_negative); - result_reg->init_req(_objArray_path, control()); - result_val->init_req(_objArray_path, alloc_obj); - result_i_o ->set_req(_objArray_path, i_o()); - result_mem ->set_req(_objArray_path, reset_memory()); + //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); + if (array_ctl != NULL) { + // It's an array. + PreserveJVMState pjvms(this); + set_control(array_ctl); + Node* obj_length = load_array_length(obj); + Node* obj_size = NULL; + Node* alloc_obj = new_array(obj_klass, obj_length, 0, + raw_mem_only, &obj_size); + + if (!use_ReduceInitialCardMarks()) { + // If it is an oop array, it requires very special treatment, + // because card marking is required on each card of the array. + Node* is_obja = generate_objArray_guard(obj_klass, (RegionNode*)NULL); + if (is_obja != NULL) { + PreserveJVMState pjvms2(this); + set_control(is_obja); + // Generate a direct call to the right arraycopy function(s). + bool disjoint_bases = true; + bool length_never_negative = true; + generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT, + obj, intcon(0), alloc_obj, intcon(0), + obj_length, + disjoint_bases, length_never_negative); + result_reg->init_req(_objArray_path, control()); + result_val->init_req(_objArray_path, alloc_obj); + result_i_o ->set_req(_objArray_path, i_o()); + result_mem ->set_req(_objArray_path, reset_memory()); + } + } + // We can dispense with card marks if we know the allocation + // comes out of eden (TLAB)... In fact, ReduceInitialCardMarks + // causes the non-eden paths to simulate a fresh allocation, + // insofar that no further card marks are required to initialize + // the object. + + // Otherwise, there are no card marks to worry about. + + if (!stopped()) { + copy_to_clone(obj, alloc_obj, obj_size, true, false); + + // Present the results of the copy. + result_reg->init_req(_array_path, control()); + result_val->init_req(_array_path, alloc_obj); + result_i_o ->set_req(_array_path, i_o()); + result_mem ->set_req(_array_path, reset_memory()); } } - // We can dispense with card marks if we know the allocation - // comes out of eden (TLAB)... In fact, ReduceInitialCardMarks - // causes the non-eden paths to simulate a fresh allocation, - // insofar that no further card marks are required to initialize - // the object. - - // Otherwise, there are no card marks to worry about. + // We only go to the instance fast case code if we pass a number of guards. + // The paths which do not pass are accumulated in the slow_region. + RegionNode* slow_region = new (C, 1) RegionNode(1); + record_for_igvn(slow_region); if (!stopped()) { - copy_to_clone(obj, alloc_obj, obj_size, true, false); - - // Present the results of the copy. - result_reg->init_req(_array_path, control()); - result_val->init_req(_array_path, alloc_obj); - result_i_o ->set_req(_array_path, i_o()); - result_mem ->set_req(_array_path, reset_memory()); - } - } + // It's an instance (we did array above). Make the slow-path tests. + // If this is a virtual call, we generate a funny guard. We grab + // the vtable entry corresponding to clone() from the target object. + // If the target method which we are calling happens to be the + // Object clone() method, we pass the guard. We do not need this + // guard for non-virtual calls; the caller is known to be the native + // Object clone(). + if (is_virtual) { + generate_virtual_guard(obj_klass, slow_region); + } - // We only go to the instance fast case code if we pass a number of guards. - // The paths which do not pass are accumulated in the slow_region. - RegionNode* slow_region = new (C, 1) RegionNode(1); - record_for_igvn(slow_region); - if (!stopped()) { - // It's an instance (we did array above). Make the slow-path tests. - // If this is a virtual call, we generate a funny guard. We grab - // the vtable entry corresponding to clone() from the target object. - // If the target method which we are calling happens to be the - // Object clone() method, we pass the guard. We do not need this - // guard for non-virtual calls; the caller is known to be the native - // Object clone(). - if (is_virtual) { - generate_virtual_guard(obj_klass, slow_region); + // The object must be cloneable and must not have a finalizer. + // Both of these conditions may be checked in a single test. + // We could optimize the cloneable test further, but we don't care. + generate_access_flags_guard(obj_klass, + // Test both conditions: + JVM_ACC_IS_CLONEABLE | JVM_ACC_HAS_FINALIZER, + // Must be cloneable but not finalizer: + JVM_ACC_IS_CLONEABLE, + slow_region); } - // The object must be cloneable and must not have a finalizer. - // Both of these conditions may be checked in a single test. - // We could optimize the cloneable test further, but we don't care. - generate_access_flags_guard(obj_klass, - // Test both conditions: - JVM_ACC_IS_CLONEABLE | JVM_ACC_HAS_FINALIZER, - // Must be cloneable but not finalizer: - JVM_ACC_IS_CLONEABLE, - slow_region); - } - - if (!stopped()) { - // It's an instance, and it passed the slow-path tests. - PreserveJVMState pjvms(this); - Node* obj_size = NULL; - Node* alloc_obj = new_instance(obj_klass, NULL, raw_mem_only, &obj_size); + if (!stopped()) { + // It's an instance, and it passed the slow-path tests. + PreserveJVMState pjvms(this); + Node* obj_size = NULL; + Node* alloc_obj = new_instance(obj_klass, NULL, raw_mem_only, &obj_size); - copy_to_clone(obj, alloc_obj, obj_size, false, !use_ReduceInitialCardMarks()); + copy_to_clone(obj, alloc_obj, obj_size, false, !use_ReduceInitialCardMarks()); - // Present the results of the slow call. - result_reg->init_req(_instance_path, control()); - result_val->init_req(_instance_path, alloc_obj); - result_i_o ->set_req(_instance_path, i_o()); - result_mem ->set_req(_instance_path, reset_memory()); - } + // Present the results of the slow call. + result_reg->init_req(_instance_path, control()); + result_val->init_req(_instance_path, alloc_obj); + result_i_o ->set_req(_instance_path, i_o()); + result_mem ->set_req(_instance_path, reset_memory()); + } - // Generate code for the slow case. We make a call to clone(). - set_control(_gvn.transform(slow_region)); - if (!stopped()) { - PreserveJVMState pjvms(this); - CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_clone, is_virtual); - Node* slow_result = set_results_for_java_call(slow_call); - // this->control() comes from set_results_for_java_call - result_reg->init_req(_slow_path, control()); - result_val->init_req(_slow_path, slow_result); - result_i_o ->set_req(_slow_path, i_o()); - result_mem ->set_req(_slow_path, reset_memory()); - } + // Generate code for the slow case. We make a call to clone(). + set_control(_gvn.transform(slow_region)); + if (!stopped()) { + PreserveJVMState pjvms(this); + CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_clone, is_virtual); + Node* slow_result = set_results_for_java_call(slow_call); + // this->control() comes from set_results_for_java_call + result_reg->init_req(_slow_path, control()); + result_val->init_req(_slow_path, slow_result); + result_i_o ->set_req(_slow_path, i_o()); + result_mem ->set_req(_slow_path, reset_memory()); + } + } //original reexecute and sp are set back here // Return the combined state. set_control( _gvn.transform(result_reg) ); diff --git a/src/share/vm/opto/output.cpp b/src/share/vm/opto/output.cpp index 1bc5361c7..713a75b98 100644 --- a/src/share/vm/opto/output.cpp +++ b/src/share/vm/opto/output.cpp @@ -911,8 +911,9 @@ void Compile::Process_OopMap_Node(MachNode *mach, int current_offset) { ciMethod* scope_method = method ? method : _method; // Describe the scope here 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(),locvals,expvals,monvals); + debug_info()->describe_scope(safepoint_pc_offset,scope_method,jvms->bci(),jvms->should_reexecute(),locvals,expvals,monvals); } // End jvms loop // Mark the end of the scope set. @@ -994,7 +995,8 @@ void NonSafepointEmitter::emit_non_safepoint() { for (int depth = 1; depth <= max_depth; depth++) { JVMState* jvms = youngest_jvms->of_depth(depth); 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. diff --git a/src/share/vm/runtime/vframe.hpp b/src/share/vm/runtime/vframe.hpp index 2924aeae9..28991b6cc 100644 --- a/src/share/vm/runtime/vframe.hpp +++ b/src/share/vm/runtime/vframe.hpp @@ -402,7 +402,12 @@ inline void vframeStreamCommon::fill_from_compiled_frame(int decode_offset) { DebugInfoReadStream buffer(nm(), decode_offset); _sender_decode_offset = buffer.read_int(); _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"); } diff --git a/src/share/vm/runtime/vframeArray.cpp b/src/share/vm/runtime/vframeArray.cpp index 60a2a23b7..75604f9b5 100644 --- a/src/share/vm/runtime/vframeArray.cpp +++ b/src/share/vm/runtime/vframeArray.cpp @@ -44,6 +44,7 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { _method = vf->method(); _bci = vf->raw_bci(); + _reexecute = vf->should_reexecute(); 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 // begins execution address pc; - bool use_next_mdp; // true if we should use the mdp associated with the next bci - // rather than the one associated with bcp + bool use_next_mdp = false; // true if we should use the mdp associated with the next bci + // rather than the one associated with bcp if (raw_bci() == SynchronizationEntryBCI) { // We are deoptimizing while hanging in prologue code for synchronized method bcp = method()->bcp_from(0); // first byte code 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 { 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"); diff --git a/src/share/vm/runtime/vframeArray.hpp b/src/share/vm/runtime/vframeArray.hpp index 767b98907..869fcdebe 100644 --- a/src/share/vm/runtime/vframeArray.hpp +++ b/src/share/vm/runtime/vframeArray.hpp @@ -41,7 +41,8 @@ class vframeArrayElement : public _ValueObj { private: 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 MonitorChunk* _monitors; // active monitors for this vframe StackValueCollection* _locals; @@ -54,6 +55,7 @@ class vframeArrayElement : public _ValueObj { int bci(void) const; int raw_bci(void) const { return _bci; } + bool should_reexecute(void) const { return _reexecute; } methodOop method(void) const { return _method; } diff --git a/src/share/vm/runtime/vframe_hp.cpp b/src/share/vm/runtime/vframe_hp.cpp index 1fc0f2b7d..0ddd0bfe9 100644 --- a/src/share/vm/runtime/vframe_hp.cpp +++ b/src/share/vm/runtime/vframe_hp.cpp @@ -276,6 +276,15 @@ int compiledVFrame::raw_bci() const { 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 { const frame f = fr(); diff --git a/src/share/vm/runtime/vframe_hp.hpp b/src/share/vm/runtime/vframe_hp.hpp index a0dd0be2f..3e1a01f25 100644 --- a/src/share/vm/runtime/vframe_hp.hpp +++ b/src/share/vm/runtime/vframe_hp.hpp @@ -25,11 +25,12 @@ class compiledVFrame: public javaVFrame { public: // JVM state - methodOop method() const; - int bci() const; - StackValueCollection* locals() const; - StackValueCollection* expressions() const; - GrowableArray* monitors() const; + methodOop method() const; + int bci() const; + bool should_reexecute() const; + StackValueCollection* locals() const; + StackValueCollection* expressions() const; + GrowableArray* monitors() const; void set_locals(StackValueCollection* values) const; diff --git a/test/compiler/6833129/Test.java b/test/compiler/6833129/Test.java new file mode 100644 index 000000000..45e9472e5 --- /dev/null +++ b/test/compiler/6833129/Test.java @@ -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