mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 06:14:49 +02:00
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:
parent
f2ea22a547
commit
ae00753bf7
29 changed files with 465 additions and 241 deletions
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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)); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -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()) ||
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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; }
|
||||||
|
@ -60,8 +61,9 @@ class ScopeDesc : public ResourceObj {
|
||||||
ScopeDesc(const nmethod* code, int decode_offset);
|
ScopeDesc(const nmethod* code, int decode_offset);
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
|
@ -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
|
||||||
address bcp,
|
static address deopt_continue_after_entry(methodOop method,
|
||||||
int callee_parameters,
|
address bcp,
|
||||||
bool is_top_frame,
|
int callee_parameters,
|
||||||
bool& use_next_mdp);
|
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:
|
// share implementation of size_activation and layout_activation:
|
||||||
static int size_activation(methodOop method,
|
static int size_activation(methodOop method,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
// 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) {
|
||||||
// This is used for deopt during registration of finalizers
|
// This is used for deopt during registration of finalizers
|
||||||
// during Object.<init>. We simply need to resume execution at
|
// during Object.<init>. We simply need to resume execution at
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
address bcp,
|
static address deopt_continue_after_entry(methodOop method,
|
||||||
int callee_parameters,
|
address bcp,
|
||||||
bool is_top_frame,
|
int callee_parameters,
|
||||||
bool& use_next_mdp);
|
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"
|
#include "incls/_templateInterpreter_pd.hpp.incl"
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
@ -222,16 +231,18 @@ public:
|
||||||
bool is_mon(uint i) const { return i >= _monoff && i < _scloff; }
|
bool is_mon(uint i) const { return i >= _monoff && i < _scloff; }
|
||||||
bool is_scl(uint i) const { return i >= _scloff && i < _endoff; }
|
bool is_scl(uint i) const { return i >= _scloff && i < _endoff; }
|
||||||
|
|
||||||
uint sp() const { return _sp; }
|
uint sp() const { return _sp; }
|
||||||
int bci() const { return _bci; }
|
int bci() const { return _bci; }
|
||||||
bool has_method() const { return _method != NULL; }
|
bool should_reexecute() const { return _reexecute==Reexecute_True; }
|
||||||
ciMethod* method() const { assert(has_method(), ""); return _method; }
|
bool is_reexecute_undefined() const { return _reexecute==Reexecute_Undefined; }
|
||||||
JVMState* caller() const { return _caller; }
|
bool has_method() const { return _method != NULL; }
|
||||||
SafePointNode* map() const { return _map; }
|
ciMethod* method() const { assert(has_method(), ""); return _method; }
|
||||||
uint depth() const { return _depth; }
|
JVMState* caller() const { return _caller; }
|
||||||
uint debug_start() const; // returns locoff of root caller
|
SafePointNode* map() const { return _map; }
|
||||||
uint debug_end() const; // returns endoff of self
|
uint depth() const { return _depth; }
|
||||||
uint debug_size() const {
|
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();
|
return loc_size() + sp() + mon_size() + scl_size();
|
||||||
}
|
}
|
||||||
uint debug_depth() const; // returns sum of debug_size values at all depths
|
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_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
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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();
|
||||||
|
};
|
||||||
|
|
|
@ -3222,24 +3222,32 @@ bool LibraryCallKit::inline_array_copyOf(bool is_copyOfRange) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stopped()) {
|
if (!stopped()) {
|
||||||
// How many elements will we copy from the original?
|
Node *newcopy;
|
||||||
// The answer is MinI(orig_length - start, length).
|
//set the original stack and the reexecute bit for the interpreter to reexecute
|
||||||
Node* orig_tail = _gvn.transform( new(C, 3) SubINode(orig_length, start) );
|
//the bytecode that invokes Arrays.copyOf if deoptimization happens
|
||||||
Node* moved = generate_min_max(vmIntrinsics::_min, orig_tail, length);
|
{ PreserveReexecuteState preexecs(this);
|
||||||
|
_sp += nargs;
|
||||||
|
jvms()->set_should_reexecute(true);
|
||||||
|
|
||||||
const bool raw_mem_only = true;
|
// How many elements will we copy from the original?
|
||||||
Node* newcopy = new_array(klass_node, length, nargs, raw_mem_only);
|
// 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);
|
||||||
|
|
||||||
// Generate a direct call to the right arraycopy function(s).
|
const bool raw_mem_only = true;
|
||||||
// We know the copy is disjoint but we might not know if the
|
newcopy = new_array(klass_node, length, 0, raw_mem_only);
|
||||||
// oop stores need checking.
|
|
||||||
// Extreme case: Arrays.copyOf((Integer[])x, 10, String[].class).
|
// Generate a direct call to the right arraycopy function(s).
|
||||||
// This will fail a store-check if x contains any non-nulls.
|
// We know the copy is disjoint but we might not know if the
|
||||||
bool disjoint_bases = true;
|
// oop stores need checking.
|
||||||
bool length_never_negative = true;
|
// Extreme case: Arrays.copyOf((Integer[])x, 10, String[].class).
|
||||||
generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT,
|
// This will fail a store-check if x contains any non-nulls.
|
||||||
original, start, newcopy, intcon(0), moved,
|
bool disjoint_bases = true;
|
||||||
disjoint_bases, length_never_negative);
|
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);
|
push(newcopy);
|
||||||
}
|
}
|
||||||
|
@ -4024,109 +4032,116 @@ 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;
|
||||||
|
|
||||||
Node* array_ctl = generate_array_guard(obj_klass, (RegionNode*)NULL);
|
//set the original stack and the reexecute bit for the interpreter to reexecute
|
||||||
if (array_ctl != NULL) {
|
//the bytecode that invokes Object.clone if deoptimization happens
|
||||||
// It's an array.
|
{ PreserveReexecuteState preexecs(this);
|
||||||
PreserveJVMState pjvms(this);
|
_sp += nargs;
|
||||||
set_control(array_ctl);
|
jvms()->set_should_reexecute(true);
|
||||||
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()) {
|
Node* array_ctl = generate_array_guard(obj_klass, (RegionNode*)NULL);
|
||||||
// If it is an oop array, it requires very special treatment,
|
if (array_ctl != NULL) {
|
||||||
// because card marking is required on each card of the array.
|
// It's an array.
|
||||||
Node* is_obja = generate_objArray_guard(obj_klass, (RegionNode*)NULL);
|
PreserveJVMState pjvms(this);
|
||||||
if (is_obja != NULL) {
|
set_control(array_ctl);
|
||||||
PreserveJVMState pjvms2(this);
|
Node* obj_length = load_array_length(obj);
|
||||||
set_control(is_obja);
|
Node* obj_size = NULL;
|
||||||
// Generate a direct call to the right arraycopy function(s).
|
Node* alloc_obj = new_array(obj_klass, obj_length, 0,
|
||||||
bool disjoint_bases = true;
|
raw_mem_only, &obj_size);
|
||||||
bool length_never_negative = true;
|
|
||||||
generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT,
|
if (!use_ReduceInitialCardMarks()) {
|
||||||
obj, intcon(0), alloc_obj, intcon(0),
|
// If it is an oop array, it requires very special treatment,
|
||||||
obj_length,
|
// because card marking is required on each card of the array.
|
||||||
disjoint_bases, length_never_negative);
|
Node* is_obja = generate_objArray_guard(obj_klass, (RegionNode*)NULL);
|
||||||
result_reg->init_req(_objArray_path, control());
|
if (is_obja != NULL) {
|
||||||
result_val->init_req(_objArray_path, alloc_obj);
|
PreserveJVMState pjvms2(this);
|
||||||
result_i_o ->set_req(_objArray_path, i_o());
|
set_control(is_obja);
|
||||||
result_mem ->set_req(_objArray_path, reset_memory());
|
// 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()) {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
if (!stopped()) {
|
if (!stopped()) {
|
||||||
copy_to_clone(obj, alloc_obj, obj_size, true, false);
|
// 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);
|
||||||
|
|
||||||
// Present the results of the copy.
|
copy_to_clone(obj, alloc_obj, obj_size, false, !use_ReduceInitialCardMarks());
|
||||||
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 only go to the instance fast case code if we pass a number of guards.
|
// Present the results of the slow call.
|
||||||
// The paths which do not pass are accumulated in the slow_region.
|
result_reg->init_req(_instance_path, control());
|
||||||
RegionNode* slow_region = new (C, 1) RegionNode(1);
|
result_val->init_req(_instance_path, alloc_obj);
|
||||||
record_for_igvn(slow_region);
|
result_i_o ->set_req(_instance_path, i_o());
|
||||||
if (!stopped()) {
|
result_mem ->set_req(_instance_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The object must be cloneable and must not have a finalizer.
|
// Generate code for the slow case. We make a call to clone().
|
||||||
// Both of these conditions may be checked in a single test.
|
set_control(_gvn.transform(slow_region));
|
||||||
// We could optimize the cloneable test further, but we don't care.
|
if (!stopped()) {
|
||||||
generate_access_flags_guard(obj_klass,
|
PreserveJVMState pjvms(this);
|
||||||
// Test both conditions:
|
CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_clone, is_virtual);
|
||||||
JVM_ACC_IS_CLONEABLE | JVM_ACC_HAS_FINALIZER,
|
Node* slow_result = set_results_for_java_call(slow_call);
|
||||||
// Must be cloneable but not finalizer:
|
// this->control() comes from set_results_for_java_call
|
||||||
JVM_ACC_IS_CLONEABLE,
|
result_reg->init_req(_slow_path, control());
|
||||||
slow_region);
|
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());
|
||||||
if (!stopped()) {
|
}
|
||||||
// It's an instance, and it passed the slow-path tests.
|
} //original reexecute and sp are set back here
|
||||||
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());
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the combined state.
|
// Return the combined state.
|
||||||
set_control( _gvn.transform(result_reg) );
|
set_control( _gvn.transform(result_reg) );
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ class vframeArrayElement : public _ValueObj {
|
||||||
private:
|
private:
|
||||||
|
|
||||||
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; }
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -25,11 +25,12 @@
|
||||||
class compiledVFrame: public javaVFrame {
|
class compiledVFrame: public javaVFrame {
|
||||||
public:
|
public:
|
||||||
// JVM state
|
// JVM state
|
||||||
methodOop method() const;
|
methodOop method() const;
|
||||||
int bci() const;
|
int bci() const;
|
||||||
StackValueCollection* locals() const;
|
bool should_reexecute() const;
|
||||||
StackValueCollection* expressions() const;
|
StackValueCollection* locals() const;
|
||||||
GrowableArray<MonitorInfo*>* monitors() const;
|
StackValueCollection* expressions() const;
|
||||||
|
GrowableArray<MonitorInfo*>* monitors() const;
|
||||||
|
|
||||||
void set_locals(StackValueCollection* values) const;
|
void set_locals(StackValueCollection* values) const;
|
||||||
|
|
||||||
|
|
62
hotspot/test/compiler/6833129/Test.java
Normal file
62
hotspot/test/compiler/6833129/Test.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue