mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-02 12:10:14 +00:00
6614597: Performance variability in jvm2008 xml.validation
Fix incorrect marking of methods as not compilable. Reviewed-by: never
This commit is contained in:
parent
4e8608a124
commit
0c27c5537e
@ -1681,11 +1681,8 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
|
||||
// If no method data exists, go to profile_continue.
|
||||
test_method_data_pointer(profile_continue);
|
||||
|
||||
// We are making a call. Increment the count.
|
||||
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch);
|
||||
|
||||
// Record the receiver type.
|
||||
record_klass_in_profile(receiver, scratch);
|
||||
record_klass_in_profile(receiver, scratch, true);
|
||||
|
||||
// The method data pointer needs to be updated to reflect the new target.
|
||||
update_mdp_by_constant(in_bytes(VirtualCallData::virtual_call_data_size()));
|
||||
@ -1695,9 +1692,13 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
|
||||
|
||||
void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
Register receiver, Register scratch,
|
||||
int start_row, Label& done) {
|
||||
if (TypeProfileWidth == 0)
|
||||
int start_row, Label& done, bool is_virtual_call) {
|
||||
if (TypeProfileWidth == 0) {
|
||||
if (is_virtual_call) {
|
||||
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int last_row = VirtualCallData::row_limit() - 1;
|
||||
assert(start_row <= last_row, "must be work left to do");
|
||||
@ -1714,6 +1715,7 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
// See if the receiver is receiver[n].
|
||||
int recvr_offset = in_bytes(VirtualCallData::receiver_offset(row));
|
||||
test_mdp_data_at(recvr_offset, receiver, next_test, scratch);
|
||||
// delayed()->tst(scratch);
|
||||
|
||||
// The receiver is receiver[n]. Increment count[n].
|
||||
int count_offset = in_bytes(VirtualCallData::receiver_count_offset(row));
|
||||
@ -1723,20 +1725,31 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
bind(next_test);
|
||||
|
||||
if (test_for_null_also) {
|
||||
Label found_null;
|
||||
// Failed the equality check on receiver[n]... Test for null.
|
||||
if (start_row == last_row) {
|
||||
// The only thing left to do is handle the null case.
|
||||
brx(Assembler::notZero, false, Assembler::pt, done);
|
||||
delayed()->nop();
|
||||
if (is_virtual_call) {
|
||||
brx(Assembler::zero, false, Assembler::pn, found_null);
|
||||
delayed()->nop();
|
||||
// Receiver did not match any saved receiver and there is no empty row for it.
|
||||
// Increment total counter to indicate polimorphic case.
|
||||
increment_mdp_data_at(in_bytes(CounterData::count_offset()), scratch);
|
||||
ba(false, done);
|
||||
delayed()->nop();
|
||||
bind(found_null);
|
||||
} else {
|
||||
brx(Assembler::notZero, false, Assembler::pt, done);
|
||||
delayed()->nop();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Since null is rare, make it be the branch-taken case.
|
||||
Label found_null;
|
||||
brx(Assembler::zero, false, Assembler::pn, found_null);
|
||||
delayed()->nop();
|
||||
|
||||
// Put all the "Case 3" tests here.
|
||||
record_klass_in_profile_helper(receiver, scratch, start_row + 1, done);
|
||||
record_klass_in_profile_helper(receiver, scratch, start_row + 1, done, is_virtual_call);
|
||||
|
||||
// Found a null. Keep searching for a matching receiver,
|
||||
// but remember that this is an empty (unused) slot.
|
||||
@ -1753,16 +1766,18 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row));
|
||||
mov(DataLayout::counter_increment, scratch);
|
||||
set_mdp_data_at(count_offset, scratch);
|
||||
ba(false, done);
|
||||
delayed()->nop();
|
||||
if (start_row > 0) {
|
||||
ba(false, done);
|
||||
delayed()->nop();
|
||||
}
|
||||
}
|
||||
|
||||
void InterpreterMacroAssembler::record_klass_in_profile(Register receiver,
|
||||
Register scratch) {
|
||||
Register scratch, bool is_virtual_call) {
|
||||
assert(ProfileInterpreter, "must be profiling");
|
||||
Label done;
|
||||
|
||||
record_klass_in_profile_helper(receiver, scratch, 0, done);
|
||||
record_klass_in_profile_helper(receiver, scratch, 0, done, is_virtual_call);
|
||||
|
||||
bind (done);
|
||||
}
|
||||
@ -1840,7 +1855,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register klass,
|
||||
mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
|
||||
|
||||
// Record the object type.
|
||||
record_klass_in_profile(klass, scratch);
|
||||
record_klass_in_profile(klass, scratch, false);
|
||||
}
|
||||
|
||||
// The method data pointer needs to be updated.
|
||||
|
||||
@ -290,9 +290,9 @@ class InterpreterMacroAssembler: public MacroAssembler {
|
||||
void test_mdp_data_at(int offset, Register value, Label& not_equal_continue,
|
||||
Register scratch);
|
||||
|
||||
void record_klass_in_profile(Register receiver, Register scratch);
|
||||
void record_klass_in_profile(Register receiver, Register scratch, bool is_virtual_call);
|
||||
void record_klass_in_profile_helper(Register receiver, Register scratch,
|
||||
int start_row, Label& done);
|
||||
int start_row, Label& done, bool is_virtual_call);
|
||||
|
||||
void update_mdp_by_offset(int offset_of_disp, Register scratch);
|
||||
void update_mdp_by_offset(Register reg, int offset_of_disp,
|
||||
|
||||
@ -3209,7 +3209,6 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
|
||||
Register mdo = op->mdo()->as_register();
|
||||
__ movoop(mdo, md->constant_encoding());
|
||||
Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset()));
|
||||
__ addl(counter_addr, DataLayout::counter_increment);
|
||||
Bytecodes::Code bc = method->java_code_at_bci(bci);
|
||||
// Perform additional virtual call profiling for invokevirtual and
|
||||
// invokeinterface bytecodes
|
||||
@ -3276,14 +3275,18 @@ void LIR_Assembler::emit_profile_call(LIR_OpProfileCall* op) {
|
||||
__ jcc(Assembler::notEqual, next_test);
|
||||
__ movptr(recv_addr, recv);
|
||||
__ movl(Address(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i))), DataLayout::counter_increment);
|
||||
if (i < (VirtualCallData::row_limit() - 1)) {
|
||||
__ jmp(update_done);
|
||||
}
|
||||
__ jmp(update_done);
|
||||
__ bind(next_test);
|
||||
}
|
||||
// Receiver did not match any saved receiver and there is no empty row for it.
|
||||
// Increment total counter to indicate polimorphic case.
|
||||
__ addl(counter_addr, DataLayout::counter_increment);
|
||||
|
||||
__ bind(update_done);
|
||||
}
|
||||
} else {
|
||||
// Static call
|
||||
__ addl(counter_addr, DataLayout::counter_increment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1239,17 +1239,19 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register
|
||||
// If no method data exists, go to profile_continue.
|
||||
test_method_data_pointer(mdp, profile_continue);
|
||||
|
||||
// We are making a call. Increment the count.
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
|
||||
Label skip_receiver_profile;
|
||||
if (receiver_can_be_null) {
|
||||
Label not_null;
|
||||
testptr(receiver, receiver);
|
||||
jcc(Assembler::zero, skip_receiver_profile);
|
||||
jccb(Assembler::notZero, not_null);
|
||||
// We are making a call. Increment the count for null receiver.
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
jmp(skip_receiver_profile);
|
||||
bind(not_null);
|
||||
}
|
||||
|
||||
// Record the receiver type.
|
||||
record_klass_in_profile(receiver, mdp, reg2);
|
||||
record_klass_in_profile(receiver, mdp, reg2, true);
|
||||
bind(skip_receiver_profile);
|
||||
|
||||
// The method data pointer needs to be updated to reflect the new target.
|
||||
@ -1263,10 +1265,14 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver, Register
|
||||
|
||||
void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
Register receiver, Register mdp,
|
||||
Register reg2,
|
||||
int start_row, Label& done) {
|
||||
if (TypeProfileWidth == 0)
|
||||
Register reg2, int start_row,
|
||||
Label& done, bool is_virtual_call) {
|
||||
if (TypeProfileWidth == 0) {
|
||||
if (is_virtual_call) {
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int last_row = VirtualCallData::row_limit() - 1;
|
||||
assert(start_row <= last_row, "must be work left to do");
|
||||
@ -1294,19 +1300,28 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
bind(next_test);
|
||||
|
||||
if (row == start_row) {
|
||||
Label found_null;
|
||||
// Failed the equality check on receiver[n]... Test for null.
|
||||
testptr(reg2, reg2);
|
||||
if (start_row == last_row) {
|
||||
// The only thing left to do is handle the null case.
|
||||
jcc(Assembler::notZero, done);
|
||||
if (is_virtual_call) {
|
||||
jccb(Assembler::zero, found_null);
|
||||
// Receiver did not match any saved receiver and there is no empty row for it.
|
||||
// Increment total counter to indicate polimorphic case.
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
jmp(done);
|
||||
bind(found_null);
|
||||
} else {
|
||||
jcc(Assembler::notZero, done);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Since null is rare, make it be the branch-taken case.
|
||||
Label found_null;
|
||||
jcc(Assembler::zero, found_null);
|
||||
|
||||
// Put all the "Case 3" tests here.
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done);
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done, is_virtual_call);
|
||||
|
||||
// Found a null. Keep searching for a matching receiver,
|
||||
// but remember that this is an empty (unused) slot.
|
||||
@ -1323,16 +1338,18 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row));
|
||||
movptr(reg2, (int32_t)DataLayout::counter_increment);
|
||||
set_mdp_data_at(mdp, count_offset, reg2);
|
||||
jmp(done);
|
||||
if (start_row > 0) {
|
||||
jmp(done);
|
||||
}
|
||||
}
|
||||
|
||||
void InterpreterMacroAssembler::record_klass_in_profile(Register receiver,
|
||||
Register mdp,
|
||||
Register reg2) {
|
||||
Register mdp, Register reg2,
|
||||
bool is_virtual_call) {
|
||||
assert(ProfileInterpreter, "must be profiling");
|
||||
Label done;
|
||||
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, 0, done);
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call);
|
||||
|
||||
bind (done);
|
||||
}
|
||||
@ -1425,7 +1442,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass,
|
||||
mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
|
||||
|
||||
// Record the object type.
|
||||
record_klass_in_profile(klass, mdp, reg2);
|
||||
record_klass_in_profile(klass, mdp, reg2, false);
|
||||
assert(reg2 == rdi, "we know how to fix this blown reg");
|
||||
restore_locals(); // Restore EDI
|
||||
}
|
||||
|
||||
@ -213,10 +213,10 @@ class InterpreterMacroAssembler: public MacroAssembler {
|
||||
Label& not_equal_continue);
|
||||
|
||||
void record_klass_in_profile(Register receiver, Register mdp,
|
||||
Register reg2);
|
||||
Register reg2, bool is_virtual_call);
|
||||
void record_klass_in_profile_helper(Register receiver, Register mdp,
|
||||
Register reg2,
|
||||
int start_row, Label& done);
|
||||
Register reg2, int start_row,
|
||||
Label& done, bool is_virtual_call);
|
||||
|
||||
void update_mdp_by_offset(Register mdp_in, int offset_of_offset);
|
||||
void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp);
|
||||
|
||||
@ -1262,17 +1262,19 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
|
||||
// If no method data exists, go to profile_continue.
|
||||
test_method_data_pointer(mdp, profile_continue);
|
||||
|
||||
// We are making a call. Increment the count.
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
|
||||
Label skip_receiver_profile;
|
||||
if (receiver_can_be_null) {
|
||||
Label not_null;
|
||||
testptr(receiver, receiver);
|
||||
jcc(Assembler::zero, skip_receiver_profile);
|
||||
jccb(Assembler::notZero, not_null);
|
||||
// We are making a call. Increment the count for null receiver.
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
jmp(skip_receiver_profile);
|
||||
bind(not_null);
|
||||
}
|
||||
|
||||
// Record the receiver type.
|
||||
record_klass_in_profile(receiver, mdp, reg2);
|
||||
record_klass_in_profile(receiver, mdp, reg2, true);
|
||||
bind(skip_receiver_profile);
|
||||
|
||||
// The method data pointer needs to be updated to reflect the new target.
|
||||
@ -1296,10 +1298,14 @@ void InterpreterMacroAssembler::profile_virtual_call(Register receiver,
|
||||
// See below for example code.
|
||||
void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
Register receiver, Register mdp,
|
||||
Register reg2,
|
||||
int start_row, Label& done) {
|
||||
if (TypeProfileWidth == 0)
|
||||
Register reg2, int start_row,
|
||||
Label& done, bool is_virtual_call) {
|
||||
if (TypeProfileWidth == 0) {
|
||||
if (is_virtual_call) {
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int last_row = VirtualCallData::row_limit() - 1;
|
||||
assert(start_row <= last_row, "must be work left to do");
|
||||
@ -1327,19 +1333,28 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
bind(next_test);
|
||||
|
||||
if (test_for_null_also) {
|
||||
Label found_null;
|
||||
// Failed the equality check on receiver[n]... Test for null.
|
||||
testptr(reg2, reg2);
|
||||
if (start_row == last_row) {
|
||||
// The only thing left to do is handle the null case.
|
||||
jcc(Assembler::notZero, done);
|
||||
if (is_virtual_call) {
|
||||
jccb(Assembler::zero, found_null);
|
||||
// Receiver did not match any saved receiver and there is no empty row for it.
|
||||
// Increment total counter to indicate polimorphic case.
|
||||
increment_mdp_data_at(mdp, in_bytes(CounterData::count_offset()));
|
||||
jmp(done);
|
||||
bind(found_null);
|
||||
} else {
|
||||
jcc(Assembler::notZero, done);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Since null is rare, make it be the branch-taken case.
|
||||
Label found_null;
|
||||
jcc(Assembler::zero, found_null);
|
||||
|
||||
// Put all the "Case 3" tests here.
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done);
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, start_row + 1, done, is_virtual_call);
|
||||
|
||||
// Found a null. Keep searching for a matching receiver,
|
||||
// but remember that this is an empty (unused) slot.
|
||||
@ -1356,7 +1371,9 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
int count_offset = in_bytes(VirtualCallData::receiver_count_offset(start_row));
|
||||
movl(reg2, DataLayout::counter_increment);
|
||||
set_mdp_data_at(mdp, count_offset, reg2);
|
||||
jmp(done);
|
||||
if (start_row > 0) {
|
||||
jmp(done);
|
||||
}
|
||||
}
|
||||
|
||||
// Example state machine code for three profile rows:
|
||||
@ -1368,7 +1385,7 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
// if (row[1].rec != NULL) {
|
||||
// // degenerate decision tree, rooted at row[2]
|
||||
// if (row[2].rec == rec) { row[2].incr(); goto done; }
|
||||
// if (row[2].rec != NULL) { goto done; } // overflow
|
||||
// if (row[2].rec != NULL) { count.incr(); goto done; } // overflow
|
||||
// row[2].init(rec); goto done;
|
||||
// } else {
|
||||
// // remember row[1] is empty
|
||||
@ -1381,14 +1398,15 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper(
|
||||
// if (row[2].rec == rec) { row[2].incr(); goto done; }
|
||||
// row[0].init(rec); goto done;
|
||||
// }
|
||||
// done:
|
||||
|
||||
void InterpreterMacroAssembler::record_klass_in_profile(Register receiver,
|
||||
Register mdp,
|
||||
Register reg2) {
|
||||
Register mdp, Register reg2,
|
||||
bool is_virtual_call) {
|
||||
assert(ProfileInterpreter, "must be profiling");
|
||||
Label done;
|
||||
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, 0, done);
|
||||
record_klass_in_profile_helper(receiver, mdp, reg2, 0, done, is_virtual_call);
|
||||
|
||||
bind (done);
|
||||
}
|
||||
@ -1484,7 +1502,7 @@ void InterpreterMacroAssembler::profile_typecheck(Register mdp, Register klass,
|
||||
mdp_delta = in_bytes(VirtualCallData::virtual_call_data_size());
|
||||
|
||||
// Record the object type.
|
||||
record_klass_in_profile(klass, mdp, reg2);
|
||||
record_klass_in_profile(klass, mdp, reg2, false);
|
||||
}
|
||||
update_mdp_by_constant(mdp, mdp_delta);
|
||||
|
||||
|
||||
@ -222,10 +222,10 @@ class InterpreterMacroAssembler: public MacroAssembler {
|
||||
Label& not_equal_continue);
|
||||
|
||||
void record_klass_in_profile(Register receiver, Register mdp,
|
||||
Register reg2);
|
||||
Register reg2, bool is_virtual_call);
|
||||
void record_klass_in_profile_helper(Register receiver, Register mdp,
|
||||
Register reg2,
|
||||
int start_row, Label& done);
|
||||
Register reg2, int start_row,
|
||||
Label& done, bool is_virtual_call);
|
||||
|
||||
void update_mdp_by_offset(Register mdp_in, int offset_of_offset);
|
||||
void update_mdp_by_offset(Register mdp_in, Register reg, int offset_of_disp);
|
||||
|
||||
@ -436,15 +436,20 @@ ciCallProfile ciMethod::call_profile_at_bci(int bci) {
|
||||
// we will set result._method also.
|
||||
}
|
||||
// Determine call site's morphism.
|
||||
// The call site count could be == (receivers_count_total + 1)
|
||||
// not only in the case of a polymorphic call but also in the case
|
||||
// when a method data snapshot is taken after the site count was updated
|
||||
// but before receivers counters were updated.
|
||||
if (morphism == result._limit) {
|
||||
// There were no array klasses and morphism <= MorphismLimit.
|
||||
if (morphism < ciCallProfile::MorphismLimit ||
|
||||
morphism == ciCallProfile::MorphismLimit &&
|
||||
(receivers_count_total+1) >= count) {
|
||||
// The call site count is 0 with known morphism (onlt 1 or 2 receivers)
|
||||
// or < 0 in the case of a type check failured for checkcast, aastore, instanceof.
|
||||
// The call site count is > 0 in the case of a polymorphic virtual call.
|
||||
if (morphism > 0 && morphism == result._limit) {
|
||||
// The morphism <= MorphismLimit.
|
||||
if ((morphism < ciCallProfile::MorphismLimit) ||
|
||||
(morphism == ciCallProfile::MorphismLimit && count == 0)) {
|
||||
#ifdef ASSERT
|
||||
if (count > 0) {
|
||||
tty->print_cr("bci: %d", bci);
|
||||
this->print_codes();
|
||||
assert(false, "this call site should not be polymorphic");
|
||||
}
|
||||
#endif
|
||||
result._morphism = morphism;
|
||||
}
|
||||
}
|
||||
@ -452,10 +457,8 @@ ciCallProfile ciMethod::call_profile_at_bci(int bci) {
|
||||
// zero or less, presume that this is a typecheck profile and
|
||||
// do nothing. Otherwise, increase count to be the sum of all
|
||||
// receiver's counts.
|
||||
if (count > 0) {
|
||||
if (count < receivers_count_total) {
|
||||
count = receivers_count_total;
|
||||
}
|
||||
if (count >= 0) {
|
||||
count += receivers_count_total;
|
||||
}
|
||||
}
|
||||
result._count = count;
|
||||
|
||||
@ -843,13 +843,15 @@ static bool count_find_witness_calls() {
|
||||
if (occasional_print || final_stats) {
|
||||
// Every now and then dump a little info about dependency searching.
|
||||
if (xtty != NULL) {
|
||||
xtty->elem("deps_find_witness calls='%d' steps='%d' recursions='%d' singles='%d'",
|
||||
ttyLocker ttyl;
|
||||
xtty->elem("deps_find_witness calls='%d' steps='%d' recursions='%d' singles='%d'",
|
||||
deps_find_witness_calls,
|
||||
deps_find_witness_steps,
|
||||
deps_find_witness_recursions,
|
||||
deps_find_witness_singles);
|
||||
}
|
||||
if (final_stats || (TraceDependencies && WizardMode)) {
|
||||
ttyLocker ttyl;
|
||||
tty->print_cr("Dependency check (find_witness) "
|
||||
"calls=%d, steps=%d (avg=%.1f), recursions=%d, singles=%d",
|
||||
deps_find_witness_calls,
|
||||
|
||||
@ -1117,7 +1117,6 @@ void nmethod::make_unloaded(BoolObjectClosure* is_alive, oop cause) {
|
||||
if (_method->code() == this) {
|
||||
_method->clear_code(); // Break a cycle
|
||||
}
|
||||
inc_decompile_count(); // Last chance to make a mark on the MDO
|
||||
_method = NULL; // Clear the method of this dead nmethod
|
||||
}
|
||||
// Make the class unloaded - i.e., change state and notify sweeper
|
||||
@ -1177,15 +1176,17 @@ void nmethod::log_state_change() const {
|
||||
bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
||||
assert(state == zombie || state == not_entrant, "must be zombie or not_entrant");
|
||||
|
||||
// If the method is already zombie there is nothing to do
|
||||
if (is_zombie()) {
|
||||
return false;
|
||||
}
|
||||
bool was_alive = false;
|
||||
|
||||
// Make sure the nmethod is not flushed in case of a safepoint in code below.
|
||||
nmethodLocker nml(this);
|
||||
|
||||
{
|
||||
// If the method is already zombie there is nothing to do
|
||||
if (is_zombie()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// invalidate osr nmethod before acquiring the patching lock since
|
||||
// they both acquire leaf locks and we don't want a deadlock.
|
||||
// This logic is equivalent to the logic below for patching the
|
||||
@ -1223,6 +1224,8 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
||||
assert(state == not_entrant, "other cases may need to be handled differently");
|
||||
}
|
||||
|
||||
was_alive = is_in_use(); // Read state under lock
|
||||
|
||||
// Change state
|
||||
flags.state = state;
|
||||
|
||||
@ -1249,8 +1252,11 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
|
||||
mark_as_seen_on_stack();
|
||||
}
|
||||
|
||||
// It's a true state change, so mark the method as decompiled.
|
||||
inc_decompile_count();
|
||||
if (was_alive) {
|
||||
// It's a true state change, so mark the method as decompiled.
|
||||
// Do it only for transition from alive.
|
||||
inc_decompile_count();
|
||||
}
|
||||
|
||||
// zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event
|
||||
// and it hasn't already been reported for this nmethod then report it now.
|
||||
|
||||
@ -1391,6 +1391,9 @@ public:
|
||||
}
|
||||
void inc_decompile_count() {
|
||||
_nof_decompiles += 1;
|
||||
if (decompile_count() > (uint)PerMethodRecompilationCutoff) {
|
||||
method()->set_not_compilable();
|
||||
}
|
||||
}
|
||||
|
||||
// Support for code generation
|
||||
|
||||
@ -575,12 +575,6 @@ bool methodOopDesc::is_not_compilable(int comp_level) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
methodDataOop mdo = method_data();
|
||||
if (mdo != NULL
|
||||
&& (uint)mdo->decompile_count() > (uint)PerMethodRecompilationCutoff) {
|
||||
// Since (uint)-1 is large, -1 really means 'no cutoff'.
|
||||
return true;
|
||||
}
|
||||
#ifdef COMPILER2
|
||||
if (is_tier1_compile(comp_level)) {
|
||||
if (is_not_tier1_compilable()) {
|
||||
@ -594,6 +588,15 @@ bool methodOopDesc::is_not_compilable(int comp_level) const {
|
||||
|
||||
// call this when compiler finds that this method is not compilable
|
||||
void methodOopDesc::set_not_compilable(int comp_level) {
|
||||
if (PrintCompilation) {
|
||||
ttyLocker ttyl;
|
||||
tty->print("made not compilable ");
|
||||
this->print_short_name(tty);
|
||||
int size = this->code_size();
|
||||
if (size > 0)
|
||||
tty->print(" (%d bytes)", size);
|
||||
tty->cr();
|
||||
}
|
||||
if ((TraceDeoptimization || LogCompilation) && (xtty != NULL)) {
|
||||
ttyLocker ttyl;
|
||||
xtty->begin_elem("make_not_compilable thread='%d'", (int) os::current_thread_id());
|
||||
|
||||
@ -182,26 +182,16 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||
}
|
||||
}
|
||||
CallGenerator* miss_cg;
|
||||
Deoptimization::DeoptReason reason = (profile.morphism() == 2) ?
|
||||
Deoptimization::Reason_bimorphic :
|
||||
Deoptimization::Reason_class_check;
|
||||
if (( profile.morphism() == 1 ||
|
||||
(profile.morphism() == 2 && next_hit_cg != NULL) ) &&
|
||||
|
||||
!too_many_traps(Deoptimization::Reason_class_check)
|
||||
|
||||
// Check only total number of traps per method to allow
|
||||
// the transition from monomorphic to bimorphic case between
|
||||
// compilations without falling into virtual call.
|
||||
// A monomorphic case may have the class_check trap flag is set
|
||||
// due to the time gap between the uncommon trap processing
|
||||
// when flags are set in MDO and the call site bytecode execution
|
||||
// in Interpreter when MDO counters are updated.
|
||||
// There was also class_check trap in monomorphic case due to
|
||||
// the bug 6225440.
|
||||
|
||||
!too_many_traps(jvms->method(), jvms->bci(), reason)
|
||||
) {
|
||||
// Generate uncommon trap for class check failure path
|
||||
// in case of monomorphic or bimorphic virtual call site.
|
||||
miss_cg = CallGenerator::for_uncommon_trap(call_method,
|
||||
Deoptimization::Reason_class_check,
|
||||
miss_cg = CallGenerator::for_uncommon_trap(call_method, reason,
|
||||
Deoptimization::Action_maybe_recompile);
|
||||
} else {
|
||||
// Generate virtual call for class check failure path
|
||||
|
||||
@ -414,8 +414,6 @@ void Parse::profile_not_taken_branch(bool force_update) {
|
||||
void Parse::profile_call(Node* receiver) {
|
||||
if (!method_data_update()) return;
|
||||
|
||||
profile_generic_call();
|
||||
|
||||
switch (bc()) {
|
||||
case Bytecodes::_invokevirtual:
|
||||
case Bytecodes::_invokeinterface:
|
||||
@ -424,6 +422,7 @@ void Parse::profile_call(Node* receiver) {
|
||||
case Bytecodes::_invokestatic:
|
||||
case Bytecodes::_invokedynamic:
|
||||
case Bytecodes::_invokespecial:
|
||||
profile_generic_call();
|
||||
break;
|
||||
default: fatal("unexpected call bytecode");
|
||||
}
|
||||
@ -444,13 +443,16 @@ void Parse::profile_generic_call() {
|
||||
void Parse::profile_receiver_type(Node* receiver) {
|
||||
assert(method_data_update(), "must be generating profile code");
|
||||
|
||||
// Skip if we aren't tracking receivers
|
||||
if (TypeProfileWidth < 1) return;
|
||||
|
||||
ciMethodData* md = method()->method_data();
|
||||
assert(md != NULL, "expected valid ciMethodData");
|
||||
ciProfileData* data = md->bci_to_data(bci());
|
||||
assert(data->is_ReceiverTypeData(), "need ReceiverTypeData here");
|
||||
|
||||
// Skip if we aren't tracking receivers
|
||||
if (TypeProfileWidth < 1) {
|
||||
increment_md_counter_at(md, data, CounterData::count_offset());
|
||||
return;
|
||||
}
|
||||
ciReceiverTypeData* rdata = (ciReceiverTypeData*)data->as_ReceiverTypeData();
|
||||
|
||||
Node* method_data = method_data_addressing(md, rdata, in_ByteSize(0));
|
||||
|
||||
@ -706,6 +706,11 @@ JRT_LEAF(void, OptoRuntime::profile_receiver_type_C(DataLayout* data, oopDesc* r
|
||||
// vc->set_receiver_count(empty_row, DataLayout::counter_increment);
|
||||
int count_off = ReceiverTypeData::receiver_count_cell_index(empty_row);
|
||||
*(mdp + count_off) = DataLayout::counter_increment;
|
||||
} else {
|
||||
// Receiver did not match any saved receiver and there is no empty row for it.
|
||||
// Increment total counter to indicate polimorphic case.
|
||||
intptr_t* count_p = (intptr_t*)(((byte*)(data)) + in_bytes(CounterData::count_offset()));
|
||||
*count_p += DataLayout::counter_increment;
|
||||
}
|
||||
JRT_END
|
||||
|
||||
|
||||
@ -1338,13 +1338,14 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
||||
// Whether the interpreter is producing MDO data or not, we also need
|
||||
// to use the MDO to detect hot deoptimization points and control
|
||||
// aggressive optimization.
|
||||
bool inc_recompile_count = false;
|
||||
ProfileData* pdata = NULL;
|
||||
if (ProfileTraps && update_trap_state && trap_mdo.not_null()) {
|
||||
assert(trap_mdo() == get_method_data(thread, trap_method, false), "sanity");
|
||||
uint this_trap_count = 0;
|
||||
bool maybe_prior_trap = false;
|
||||
bool maybe_prior_recompile = false;
|
||||
ProfileData* pdata
|
||||
= query_update_method_data(trap_mdo, trap_bci, reason,
|
||||
pdata = query_update_method_data(trap_mdo, trap_bci, reason,
|
||||
//outputs:
|
||||
this_trap_count,
|
||||
maybe_prior_trap,
|
||||
@ -1380,18 +1381,7 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
||||
// Detect repeated recompilation at the same BCI, and enforce a limit.
|
||||
if (make_not_entrant && maybe_prior_recompile) {
|
||||
// More than one recompile at this point.
|
||||
trap_mdo->inc_overflow_recompile_count();
|
||||
if (maybe_prior_trap
|
||||
&& ((uint)trap_mdo->overflow_recompile_count()
|
||||
> (uint)PerBytecodeRecompilationCutoff)) {
|
||||
// Give up on the method containing the bad BCI.
|
||||
if (trap_method() == nm->method()) {
|
||||
make_not_compilable = true;
|
||||
} else {
|
||||
trap_method->set_not_compilable();
|
||||
// But give grace to the enclosing nm->method().
|
||||
}
|
||||
}
|
||||
inc_recompile_count = maybe_prior_trap;
|
||||
}
|
||||
} else {
|
||||
// For reasons which are not recorded per-bytecode, we simply
|
||||
@ -1418,7 +1408,17 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
||||
reset_counters = true;
|
||||
}
|
||||
|
||||
if (make_not_entrant && pdata != NULL) {
|
||||
}
|
||||
|
||||
// Take requested actions on the method:
|
||||
|
||||
// Recompile
|
||||
if (make_not_entrant) {
|
||||
if (!nm->make_not_entrant()) {
|
||||
return; // the call did not change nmethod's state
|
||||
}
|
||||
|
||||
if (pdata != NULL) {
|
||||
// Record the recompilation event, if any.
|
||||
int tstate0 = pdata->trap_state();
|
||||
int tstate1 = trap_state_set_recompiled(tstate0, true);
|
||||
@ -1427,7 +1427,19 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
||||
}
|
||||
}
|
||||
|
||||
// Take requested actions on the method:
|
||||
if (inc_recompile_count) {
|
||||
trap_mdo->inc_overflow_recompile_count();
|
||||
if ((uint)trap_mdo->overflow_recompile_count() >
|
||||
(uint)PerBytecodeRecompilationCutoff) {
|
||||
// Give up on the method containing the bad BCI.
|
||||
if (trap_method() == nm->method()) {
|
||||
make_not_compilable = true;
|
||||
} else {
|
||||
trap_method->set_not_compilable();
|
||||
// But give grace to the enclosing nm->method().
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset invocation counters
|
||||
if (reset_counters) {
|
||||
@ -1437,13 +1449,8 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* thread, jint tra
|
||||
reset_invocation_counter(trap_scope);
|
||||
}
|
||||
|
||||
// Recompile
|
||||
if (make_not_entrant) {
|
||||
nm->make_not_entrant();
|
||||
}
|
||||
|
||||
// Give up compiling
|
||||
if (make_not_compilable) {
|
||||
if (make_not_compilable && !nm->method()->is_not_compilable()) {
|
||||
assert(make_not_entrant, "consistent");
|
||||
nm->method()->set_not_compilable();
|
||||
}
|
||||
@ -1516,9 +1523,11 @@ Deoptimization::query_update_method_data(methodDataHandle trap_mdo,
|
||||
if (tstate1 != tstate0)
|
||||
pdata->set_trap_state(tstate1);
|
||||
} else {
|
||||
if (LogCompilation && xtty != NULL)
|
||||
if (LogCompilation && xtty != NULL) {
|
||||
ttyLocker ttyl;
|
||||
// Missing MDP? Leave a small complaint in the log.
|
||||
xtty->elem("missing_mdp bci='%d'", trap_bci);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1672,6 +1681,7 @@ const char* Deoptimization::_trap_reason_name[Reason_LIMIT] = {
|
||||
"class_check",
|
||||
"array_check",
|
||||
"intrinsic",
|
||||
"bimorphic",
|
||||
"unloaded",
|
||||
"uninitialized",
|
||||
"unreached",
|
||||
|
||||
@ -33,12 +33,15 @@ class Deoptimization : AllStatic {
|
||||
enum DeoptReason {
|
||||
Reason_many = -1, // indicates presence of several reasons
|
||||
Reason_none = 0, // indicates absence of a relevant deopt.
|
||||
// Next 7 reasons are recorded per bytecode in DataLayout::trap_bits
|
||||
Reason_null_check, // saw unexpected null or zero divisor (@bci)
|
||||
Reason_null_assert, // saw unexpected non-null or non-zero (@bci)
|
||||
Reason_range_check, // saw unexpected array index (@bci)
|
||||
Reason_class_check, // saw unexpected object class (@bci)
|
||||
Reason_array_check, // saw unexpected array class (aastore @bci)
|
||||
Reason_intrinsic, // saw unexpected operand to intrinsic (@bci)
|
||||
Reason_bimorphic, // saw unexpected object class in bimorphic inlining (@bci)
|
||||
|
||||
Reason_unloaded, // unloaded class or constant pool entry
|
||||
Reason_uninitialized, // bad class state (uninitialized)
|
||||
Reason_unreached, // code is not reached, compiler
|
||||
@ -49,7 +52,7 @@ class Deoptimization : AllStatic {
|
||||
Reason_predicate, // compiler generated predicate failed
|
||||
Reason_LIMIT,
|
||||
// Note: Keep this enum in sync. with _trap_reason_name.
|
||||
Reason_RECORDED_LIMIT = Reason_unloaded // some are not recorded per bc
|
||||
Reason_RECORDED_LIMIT = Reason_bimorphic // some are not recorded per bc
|
||||
// Note: Reason_RECORDED_LIMIT should be < 8 to fit into 3 bits of
|
||||
// DataLayout::trap_bits. This dependency is enforced indirectly
|
||||
// via asserts, to avoid excessive direct header-to-header dependencies.
|
||||
@ -279,7 +282,7 @@ class Deoptimization : AllStatic {
|
||||
int trap_state);
|
||||
|
||||
static bool reason_is_recorded_per_bytecode(DeoptReason reason) {
|
||||
return reason > Reason_none && reason < Reason_RECORDED_LIMIT;
|
||||
return reason > Reason_none && reason <= Reason_RECORDED_LIMIT;
|
||||
}
|
||||
|
||||
static DeoptReason reason_recorded_per_bytecode_if_any(DeoptReason reason) {
|
||||
|
||||
@ -2864,7 +2864,7 @@ class CommandLineFlags {
|
||||
product(intx, PerMethodRecompilationCutoff, 400, \
|
||||
"After recompiling N times, stay in the interpreter (-1=>'Inf')") \
|
||||
\
|
||||
product(intx, PerBytecodeRecompilationCutoff, 100, \
|
||||
product(intx, PerBytecodeRecompilationCutoff, 200, \
|
||||
"Per-BCI limit on repeated recompilation (-1=>'Inf')") \
|
||||
\
|
||||
product(intx, PerMethodTrapLimit, 100, \
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user