8031752: Failed speculative optimizations should be reattempted when root of compilation is different
Summary: support for speculative traps that keep track of the root of the compilation in which a trap occurs.
Reviewed-by: kvn, twisti
--- a/hotspot/src/share/vm/ci/ciClassList.hpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/ci/ciClassList.hpp Tue Feb 25 18:16:24 2014 +0100
@@ -103,6 +103,7 @@
friend class ciMethodType; \
friend class ciReceiverTypeData; \
friend class ciTypeEntries; \
+friend class ciSpeculativeTrapData; \
friend class ciSymbol; \
friend class ciArray; \
friend class ciObjArray; \
--- a/hotspot/src/share/vm/ci/ciMethodData.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/ci/ciMethodData.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -78,6 +78,35 @@
_parameters = NULL;
}
+void ciMethodData::load_extra_data() {
+ MethodData* mdo = get_MethodData();
+
+ // speculative trap entries also hold a pointer to a Method so need to be translated
+ DataLayout* dp_src = mdo->extra_data_base();
+ DataLayout* end_src = mdo->extra_data_limit();
+ DataLayout* dp_dst = extra_data_base();
+ for (;; dp_src = MethodData::next_extra(dp_src), dp_dst = MethodData::next_extra(dp_dst)) {
+ assert(dp_src < end_src, "moved past end of extra data");
+ assert(dp_src->tag() == dp_dst->tag(), err_msg("should be same tags %d != %d", dp_src->tag(), dp_dst->tag()));
+ switch(dp_src->tag()) {
+ case DataLayout::speculative_trap_data_tag: {
+ ciSpeculativeTrapData* data_dst = new ciSpeculativeTrapData(dp_dst);
+ SpeculativeTrapData* data_src = new SpeculativeTrapData(dp_src);
+ data_dst->translate_from(data_src);
+ break;
+ }
+ case DataLayout::bit_data_tag:
+ break;
+ case DataLayout::no_tag:
+ case DataLayout::arg_info_data_tag:
+ // An empty slot or ArgInfoData entry marks the end of the trap data
+ return;
+ default:
+ fatal(err_msg("bad tag = %d", dp_src->tag()));
+ }
+ }
+}
+
void ciMethodData::load_data() {
MethodData* mdo = get_MethodData();
if (mdo == NULL) {
@@ -116,6 +145,8 @@
parameters->translate_from(mdo->parameters_type_data());
}
+ load_extra_data();
+
// Note: Extra data are all BitData, and do not need translation.
_current_mileage = MethodData::mileage_of(mdo->method());
_invocation_counter = mdo->invocation_count();
@@ -156,6 +187,12 @@
set_type(translate_klass(k));
}
+void ciSpeculativeTrapData::translate_from(const ProfileData* data) {
+ Method* m = data->as_SpeculativeTrapData()->method();
+ ciMethod* ci_m = CURRENT_ENV->get_method(m);
+ set_method(ci_m);
+}
+
// Get the data at an arbitrary (sort of) data index.
ciProfileData* ciMethodData::data_at(int data_index) {
if (out_of_bounds(data_index)) {
@@ -203,32 +240,64 @@
return next;
}
-// Translate a bci to its corresponding data, or NULL.
-ciProfileData* ciMethodData::bci_to_data(int bci) {
- ciProfileData* data = data_before(bci);
- for ( ; is_valid(data); data = next_data(data)) {
- if (data->bci() == bci) {
- set_hint_di(dp_to_di(data->dp()));
- return data;
- } else if (data->bci() > bci) {
- break;
- }
- }
+ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots) {
// bci_to_extra_data(bci) ...
DataLayout* dp = data_layout_at(data_size());
DataLayout* end = data_layout_at(data_size() + extra_data_size());
- for (; dp < end; dp = MethodData::next_extra(dp)) {
- if (dp->tag() == DataLayout::no_tag) {
+ two_free_slots = false;
+ for (;dp < end; dp = MethodData::next_extra(dp)) {
+ switch(dp->tag()) {
+ case DataLayout::no_tag:
_saw_free_extra_data = true; // observed an empty slot (common case)
+ two_free_slots = (MethodData::next_extra(dp)->tag() == DataLayout::no_tag);
return NULL;
+ case DataLayout::arg_info_data_tag:
+ return NULL; // ArgInfoData is at the end of extra data section.
+ case DataLayout::bit_data_tag:
+ if (m == NULL && dp->bci() == bci) {
+ return new ciBitData(dp);
+ }
+ break;
+ case DataLayout::speculative_trap_data_tag: {
+ ciSpeculativeTrapData* data = new ciSpeculativeTrapData(dp);
+ // data->method() might be null if the MDO is snapshotted
+ // concurrently with a trap
+ if (m != NULL && data->method() == m && dp->bci() == bci) {
+ return data;
+ }
+ break;
+ }
+ default:
+ fatal(err_msg("bad tag = %d", dp->tag()));
}
- if (dp->tag() == DataLayout::arg_info_data_tag) {
- break; // ArgInfoData is at the end of extra data section.
+ }
+ return NULL;
+}
+
+// Translate a bci to its corresponding data, or NULL.
+ciProfileData* ciMethodData::bci_to_data(int bci, ciMethod* m) {
+ // If m is not NULL we look for a SpeculativeTrapData entry
+ if (m == NULL) {
+ ciProfileData* data = data_before(bci);
+ for ( ; is_valid(data); data = next_data(data)) {
+ if (data->bci() == bci) {
+ set_hint_di(dp_to_di(data->dp()));
+ return data;
+ } else if (data->bci() > bci) {
+ break;
+ }
}
- if (dp->bci() == bci) {
- assert(dp->tag() == DataLayout::bit_data_tag, "sane");
- return new ciBitData(dp);
- }
+ }
+ bool two_free_slots = false;
+ ciProfileData* result = bci_to_extra_data(bci, m, two_free_slots);
+ if (result != NULL) {
+ return result;
+ }
+ if (m != NULL && !two_free_slots) {
+ // We were looking for a SpeculativeTrapData entry we didn't
+ // find. Room is not available for more SpeculativeTrapData
+ // entries, look in the non SpeculativeTrapData entries.
+ return bci_to_data(bci, NULL);
}
return NULL;
}
@@ -525,18 +594,25 @@
st->print_cr("--- Extra data:");
DataLayout* dp = data_layout_at(data_size());
DataLayout* end = data_layout_at(data_size() + extra_data_size());
- for (; dp < end; dp = MethodData::next_extra(dp)) {
- if (dp->tag() == DataLayout::no_tag) continue;
- if (dp->tag() == DataLayout::bit_data_tag) {
+ for (;; dp = MethodData::next_extra(dp)) {
+ assert(dp < end, "moved past end of extra data");
+ switch (dp->tag()) {
+ case DataLayout::no_tag:
+ continue;
+ case DataLayout::bit_data_tag:
data = new BitData(dp);
- } else {
- assert(dp->tag() == DataLayout::arg_info_data_tag, "must be BitData or ArgInfo");
+ break;
+ case DataLayout::arg_info_data_tag:
data = new ciArgInfoData(dp);
dp = end; // ArgInfoData is at the end of extra data section.
+ break;
+ default:
+ fatal(err_msg("unexpected tag %d", dp->tag()));
}
st->print("%d", dp_to_di(data->dp()));
st->fill_to(6);
data->print_data_on(st);
+ if (dp >= end) return;
}
}
@@ -569,8 +645,8 @@
st->cr();
}
-void ciCallTypeData::print_data_on(outputStream* st) const {
- print_shared(st, "ciCallTypeData");
+void ciCallTypeData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "ciCallTypeData", extra);
if (has_arguments()) {
tab(st, true);
st->print("argument types");
@@ -599,18 +675,18 @@
}
}
-void ciReceiverTypeData::print_data_on(outputStream* st) const {
- print_shared(st, "ciReceiverTypeData");
+void ciReceiverTypeData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "ciReceiverTypeData", extra);
print_receiver_data_on(st);
}
-void ciVirtualCallData::print_data_on(outputStream* st) const {
- print_shared(st, "ciVirtualCallData");
+void ciVirtualCallData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "ciVirtualCallData", extra);
rtd_super()->print_receiver_data_on(st);
}
-void ciVirtualCallTypeData::print_data_on(outputStream* st) const {
- print_shared(st, "ciVirtualCallTypeData");
+void ciVirtualCallTypeData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "ciVirtualCallTypeData", extra);
rtd_super()->print_receiver_data_on(st);
if (has_arguments()) {
tab(st, true);
@@ -624,8 +700,15 @@
}
}
-void ciParametersTypeData::print_data_on(outputStream* st) const {
- st->print_cr("Parametertypes");
+void ciParametersTypeData::print_data_on(outputStream* st, const char* extra) const {
+ st->print_cr("ciParametersTypeData");
parameters()->print_data_on(st);
}
+
+void ciSpeculativeTrapData::print_data_on(outputStream* st, const char* extra) const {
+ st->print_cr("ciSpeculativeTrapData");
+ tab(st);
+ method()->print_short_name(st);
+ st->cr();
+}
#endif
--- a/hotspot/src/share/vm/ci/ciMethodData.hpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/ci/ciMethodData.hpp Tue Feb 25 18:16:24 2014 +0100
@@ -31,6 +31,7 @@
#include "ci/ciUtilities.hpp"
#include "oops/methodData.hpp"
#include "oops/oop.inline.hpp"
+#include "runtime/deoptimization.hpp"
class ciBitData;
class ciCounterData;
@@ -44,6 +45,7 @@
class ciCallTypeData;
class ciVirtualCallTypeData;
class ciParametersTypeData;
+class ciSpeculativeTrapData;;
typedef ProfileData ciProfileData;
@@ -173,7 +175,7 @@
}
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra) const;
#endif
};
@@ -200,7 +202,7 @@
}
void translate_receiver_data_from(const ProfileData* data);
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra) const;
void print_receiver_data_on(outputStream* st) const;
#endif
};
@@ -225,7 +227,7 @@
rtd_super()->translate_receiver_data_from(data);
}
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra) const;
#endif
};
@@ -287,7 +289,7 @@
}
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra) const;
#endif
};
@@ -336,7 +338,26 @@
}
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra) const;
+#endif
+};
+
+class ciSpeculativeTrapData : public SpeculativeTrapData {
+public:
+ ciSpeculativeTrapData(DataLayout* layout) : SpeculativeTrapData(layout) {}
+
+ virtual void translate_from(const ProfileData* data);
+
+ ciMethod* method() const {
+ return (ciMethod*)intptr_at(method_offset);
+ }
+
+ void set_method(ciMethod* m) {
+ set_intptr_at(method_offset, (intptr_t)m);
+ }
+
+#ifndef PRODUCT
+ void print_data_on(outputStream* st, const char* extra) const;
#endif
};
@@ -436,6 +457,16 @@
ciArgInfoData *arg_info() const;
+ address data_base() const {
+ return (address) _data;
+ }
+ DataLayout* limit_data_position() const {
+ return (DataLayout*)((address)data_base() + _data_size);
+ }
+
+ void load_extra_data();
+ ciProfileData* bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots);
+
public:
bool is_method_data() const { return true; }
@@ -475,9 +506,11 @@
ciProfileData* next_data(ciProfileData* current);
bool is_valid(ciProfileData* current) { return current != NULL; }
- // Get the data at an arbitrary bci, or NULL if there is none.
- ciProfileData* bci_to_data(int bci);
- ciProfileData* bci_to_extra_data(int bci, bool create_if_missing);
+ DataLayout* extra_data_base() const { return limit_data_position(); }
+
+ // Get the data at an arbitrary bci, or NULL if there is none. If m
+ // is not NULL look for a SpeculativeTrapData if any first.
+ ciProfileData* bci_to_data(int bci, ciMethod* m = NULL);
uint overflow_trap_count() const {
return _orig.overflow_trap_count();
@@ -496,12 +529,13 @@
// Helpful query functions that decode trap_state.
int has_trap_at(ciProfileData* data, int reason);
- int has_trap_at(int bci, int reason) {
- return has_trap_at(bci_to_data(bci), reason);
+ int has_trap_at(int bci, ciMethod* m, int reason) {
+ assert((m != NULL) == Deoptimization::reason_is_speculate(reason), "inconsistent method/reason");
+ return has_trap_at(bci_to_data(bci, m), reason);
}
int trap_recompiled_at(ciProfileData* data);
- int trap_recompiled_at(int bci) {
- return trap_recompiled_at(bci_to_data(bci));
+ int trap_recompiled_at(int bci, ciMethod* m) {
+ return trap_recompiled_at(bci_to_data(bci, m));
}
void clear_escape_info();
--- a/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/interpreter/bytecodeTracer.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -596,7 +596,7 @@
if (data != NULL) {
st->print(" %d", mdo->dp_to_di(data->dp()));
st->fill_to(6);
- data->print_data_on(st);
+ data->print_data_on(st, mdo);
}
}
}
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -2192,15 +2192,7 @@
for (int m = 0; m < methods()->length(); m++) {
MethodData* mdo = methods()->at(m)->method_data();
if (mdo != NULL) {
- for (ProfileData* data = mdo->first_data();
- mdo->is_valid(data);
- data = mdo->next_data(data)) {
- data->clean_weak_klass_links(is_alive);
- }
- ParametersTypeData* parameters = mdo->parameters_type_data();
- if (parameters != NULL) {
- parameters->clean_weak_klass_links(is_alive);
- }
+ mdo->clean_method_data(is_alive);
}
}
}
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Tue Feb 25 18:16:24 2014 +0100
@@ -306,7 +306,7 @@
// three cases:
// NULL: no implementor.
// A Klass* that's not itself: one implementor.
- // Itsef: more than one implementors.
+ // Itself: more than one implementors.
// embedded host klass follows here
// The embedded host klass only exists in an anonymous class for
// dynamic language support (JSR 292 enabled). The host class grants
--- a/hotspot/src/share/vm/oops/methodData.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/oops/methodData.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -80,8 +80,42 @@
_data = NULL;
}
+char* ProfileData::print_data_on_helper(const MethodData* md) const {
+ DataLayout* dp = md->extra_data_base();
+ DataLayout* end = md->extra_data_limit();
+ stringStream ss;
+ for (;; dp = MethodData::next_extra(dp)) {
+ assert(dp < end, "moved past end of extra data");
+ switch(dp->tag()) {
+ case DataLayout::speculative_trap_data_tag:
+ if (dp->bci() == bci()) {
+ SpeculativeTrapData* data = new SpeculativeTrapData(dp);
+ int trap = data->trap_state();
+ char buf[100];
+ ss.print("trap/");
+ data->method()->print_short_name(&ss);
+ ss.print("(%s) ", Deoptimization::format_trap_state(buf, sizeof(buf), trap));
+ }
+ break;
+ case DataLayout::bit_data_tag:
+ break;
+ case DataLayout::no_tag:
+ case DataLayout::arg_info_data_tag:
+ return ss.as_string();
+ break;
+ default:
+ fatal(err_msg("unexpected tag %d", dp->tag()));
+ }
+ }
+ return NULL;
+}
+
+void ProfileData::print_data_on(outputStream* st, const MethodData* md) const {
+ print_data_on(st, print_data_on_helper(md));
+}
+
#ifndef PRODUCT
-void ProfileData::print_shared(outputStream* st, const char* name) const {
+void ProfileData::print_shared(outputStream* st, const char* name, const char* extra) const {
st->print("bci: %d", bci());
st->fill_to(tab_width_one);
st->print("%s", name);
@@ -91,9 +125,13 @@
char buf[100];
st->print("trap(%s) ", Deoptimization::format_trap_state(buf, sizeof(buf), trap));
}
+ if (extra != NULL) {
+ st->print(extra);
+ }
int flags = data()->flags();
- if (flags != 0)
+ if (flags != 0) {
st->print("flags(%d) ", flags);
+ }
}
void ProfileData::tab(outputStream* st, bool first) const {
@@ -109,8 +147,8 @@
#ifndef PRODUCT
-void BitData::print_data_on(outputStream* st) const {
- print_shared(st, "BitData");
+void BitData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "BitData", extra);
}
#endif // !PRODUCT
@@ -120,8 +158,8 @@
// A CounterData corresponds to a simple counter.
#ifndef PRODUCT
-void CounterData::print_data_on(outputStream* st) const {
- print_shared(st, "CounterData");
+void CounterData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "CounterData", extra);
st->print_cr("count(%u)", count());
}
#endif // !PRODUCT
@@ -150,8 +188,8 @@
}
#ifndef PRODUCT
-void JumpData::print_data_on(outputStream* st) const {
- print_shared(st, "JumpData");
+void JumpData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "JumpData", extra);
st->print_cr("taken(%u) displacement(%d)", taken(), displacement());
}
#endif // !PRODUCT
@@ -332,8 +370,8 @@
st->cr();
}
-void CallTypeData::print_data_on(outputStream* st) const {
- CounterData::print_data_on(st);
+void CallTypeData::print_data_on(outputStream* st, const char* extra) const {
+ CounterData::print_data_on(st, extra);
if (has_arguments()) {
tab(st, true);
st->print("argument types");
@@ -346,8 +384,8 @@
}
}
-void VirtualCallTypeData::print_data_on(outputStream* st) const {
- VirtualCallData::print_data_on(st);
+void VirtualCallTypeData::print_data_on(outputStream* st, const char* extra) const {
+ VirtualCallData::print_data_on(st, extra);
if (has_arguments()) {
tab(st, true);
st->print("argument types");
@@ -400,12 +438,12 @@
}
}
}
-void ReceiverTypeData::print_data_on(outputStream* st) const {
- print_shared(st, "ReceiverTypeData");
+void ReceiverTypeData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "ReceiverTypeData", extra);
print_receiver_data_on(st);
}
-void VirtualCallData::print_data_on(outputStream* st) const {
- print_shared(st, "VirtualCallData");
+void VirtualCallData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "VirtualCallData", extra);
print_receiver_data_on(st);
}
#endif // !PRODUCT
@@ -461,8 +499,8 @@
#endif // CC_INTERP
#ifndef PRODUCT
-void RetData::print_data_on(outputStream* st) const {
- print_shared(st, "RetData");
+void RetData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "RetData", extra);
uint row;
int entries = 0;
for (row = 0; row < row_limit(); row++) {
@@ -496,8 +534,8 @@
}
#ifndef PRODUCT
-void BranchData::print_data_on(outputStream* st) const {
- print_shared(st, "BranchData");
+void BranchData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "BranchData", extra);
st->print_cr("taken(%u) displacement(%d)",
taken(), displacement());
tab(st);
@@ -570,8 +608,8 @@
}
#ifndef PRODUCT
-void MultiBranchData::print_data_on(outputStream* st) const {
- print_shared(st, "MultiBranchData");
+void MultiBranchData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "MultiBranchData", extra);
st->print_cr("default_count(%u) displacement(%d)",
default_count(), default_displacement());
int cases = number_of_cases();
@@ -584,8 +622,8 @@
#endif
#ifndef PRODUCT
-void ArgInfoData::print_data_on(outputStream* st) const {
- print_shared(st, "ArgInfoData");
+void ArgInfoData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "ArgInfoData", extra);
int nargs = number_of_args();
for (int i = 0; i < nargs; i++) {
st->print(" 0x%x", arg_modified(i));
@@ -616,10 +654,17 @@
}
#ifndef PRODUCT
-void ParametersTypeData::print_data_on(outputStream* st) const {
- st->print("parameter types");
+void ParametersTypeData::print_data_on(outputStream* st, const char* extra) const {
+ st->print("parameter types", extra);
_parameters.print_data_on(st);
}
+
+void SpeculativeTrapData::print_data_on(outputStream* st, const char* extra) const {
+ print_shared(st, "SpeculativeTrapData", extra);
+ tab(st);
+ method()->print_short_name(st);
+ st->cr();
+}
#endif
// ==================================================================
@@ -745,7 +790,27 @@
return DataLayout::compute_size_in_bytes(cell_count);
}
-int MethodData::compute_extra_data_count(int data_size, int empty_bc_count) {
+bool MethodData::is_speculative_trap_bytecode(Bytecodes::Code code) {
+ // Bytecodes for which we may use speculation
+ switch (code) {
+ case Bytecodes::_checkcast:
+ case Bytecodes::_instanceof:
+ case Bytecodes::_aastore:
+ case Bytecodes::_invokevirtual:
+ case Bytecodes::_invokeinterface:
+ case Bytecodes::_if_acmpeq:
+ case Bytecodes::_if_acmpne:
+ case Bytecodes::_invokestatic:
+#ifdef COMPILER2
+ return UseTypeSpeculation;
+#endif
+ default:
+ return false;
+ }
+ return false;
+}
+
+int MethodData::compute_extra_data_count(int data_size, int empty_bc_count, bool needs_speculative_traps) {
if (ProfileTraps) {
// Assume that up to 3% of BCIs with no MDP will need to allocate one.
int extra_data_count = (uint)(empty_bc_count * 3) / 128 + 1;
@@ -756,7 +821,18 @@
extra_data_count = one_percent_of_data;
if (extra_data_count > empty_bc_count)
extra_data_count = empty_bc_count; // no need for more
- return extra_data_count;
+
+ // Make sure we have a minimum number of extra data slots to
+ // allocate SpeculativeTrapData entries. We would want to have one
+ // entry per compilation that inlines this method and for which
+ // some type speculation assumption fails. So the room we need for
+ // the SpeculativeTrapData entries doesn't directly depend on the
+ // size of the method. Because it's hard to estimate, we reserve
+ // space for an arbitrary number of entries.
+ int spec_data_count = (needs_speculative_traps ? SpecTrapLimitExtraEntries : 0) *
+ (SpeculativeTrapData::static_cell_count() + DataLayout::header_size_in_cells());
+
+ return MAX2(extra_data_count, spec_data_count);
} else {
return 0;
}
@@ -769,15 +845,17 @@
BytecodeStream stream(method);
Bytecodes::Code c;
int empty_bc_count = 0; // number of bytecodes lacking data
+ bool needs_speculative_traps = false;
while ((c = stream.next()) >= 0) {
int size_in_bytes = compute_data_size(&stream);
data_size += size_in_bytes;
if (size_in_bytes == 0) empty_bc_count += 1;
+ needs_speculative_traps = needs_speculative_traps || is_speculative_trap_bytecode(c);
}
int object_size = in_bytes(data_offset()) + data_size;
// Add some extra DataLayout cells (at least one) to track stray traps.
- int extra_data_count = compute_extra_data_count(data_size, empty_bc_count);
+ int extra_data_count = compute_extra_data_count(data_size, empty_bc_count, needs_speculative_traps);
object_size += extra_data_count * DataLayout::compute_size_in_bytes(0);
// Add a cell to record information about modified arguments.
@@ -1009,18 +1087,23 @@
_data[0] = 0; // apparently not set below.
BytecodeStream stream(method);
Bytecodes::Code c;
+ bool needs_speculative_traps = false;
while ((c = stream.next()) >= 0) {
int size_in_bytes = initialize_data(&stream, data_size);
data_size += size_in_bytes;
if (size_in_bytes == 0) empty_bc_count += 1;
+ needs_speculative_traps = needs_speculative_traps || is_speculative_trap_bytecode(c);
}
_data_size = data_size;
int object_size = in_bytes(data_offset()) + data_size;
// Add some extra DataLayout cells (at least one) to track stray traps.
- int extra_data_count = compute_extra_data_count(data_size, empty_bc_count);
+ int extra_data_count = compute_extra_data_count(data_size, empty_bc_count, needs_speculative_traps);
int extra_size = extra_data_count * DataLayout::compute_size_in_bytes(0);
+ // Let's zero the space for the extra data
+ Copy::zero_to_bytes(((address)_data) + data_size, extra_size);
+
// Add a cell to record information about modified arguments.
// Set up _args_modified array after traps cells so that
// the code for traps cells works.
@@ -1032,17 +1115,17 @@
int arg_data_size = DataLayout::compute_size_in_bytes(arg_size+1);
object_size += extra_size + arg_data_size;
- int args_cell = ParametersTypeData::compute_cell_count(method());
+ int parms_cell = ParametersTypeData::compute_cell_count(method());
// If we are profiling parameters, we reserver an area near the end
// of the MDO after the slots for bytecodes (because there's no bci
// for method entry so they don't fit with the framework for the
// profiling of bytecodes). We store the offset within the MDO of
// this area (or -1 if no parameter is profiled)
- if (args_cell > 0) {
- object_size += DataLayout::compute_size_in_bytes(args_cell);
+ if (parms_cell > 0) {
+ object_size += DataLayout::compute_size_in_bytes(parms_cell);
_parameters_type_data_di = data_size + extra_size + arg_data_size;
DataLayout *dp = data_layout_at(data_size + extra_size + arg_data_size);
- dp->initialize(DataLayout::parameters_type_data_tag, 0, args_cell);
+ dp->initialize(DataLayout::parameters_type_data_tag, 0, parms_cell);
} else {
_parameters_type_data_di = -1;
}
@@ -1133,39 +1216,113 @@
break;
}
}
- return bci_to_extra_data(bci, false);
+ return bci_to_extra_data(bci, NULL, false);
}
-// Translate a bci to its corresponding extra data, or NULL.
-ProfileData* MethodData::bci_to_extra_data(int bci, bool create_if_missing) {
- DataLayout* dp = extra_data_base();
- DataLayout* end = extra_data_limit();
- DataLayout* avail = NULL;
- for (; dp < end; dp = next_extra(dp)) {
+DataLayout* MethodData::next_extra(DataLayout* dp) {
+ int nb_cells = 0;
+ switch(dp->tag()) {
+ case DataLayout::bit_data_tag:
+ case DataLayout::no_tag:
+ nb_cells = BitData::static_cell_count();
+ break;
+ case DataLayout::speculative_trap_data_tag:
+ nb_cells = SpeculativeTrapData::static_cell_count();
+ break;
+ default:
+ fatal(err_msg("unexpected tag %d", dp->tag()));
+ }
+ return (DataLayout*)((address)dp + DataLayout::compute_size_in_bytes(nb_cells));
+}
+
+ProfileData* MethodData::bci_to_extra_data_helper(int bci, Method* m, DataLayout*& dp) {
+ DataLayout* end = extra_data_limit();
+
+ for (;; dp = next_extra(dp)) {
+ assert(dp < end, "moved past end of extra data");
// No need for "OrderAccess::load_acquire" ops,
// since the data structure is monotonic.
- if (dp->tag() == DataLayout::no_tag) break;
- if (dp->tag() == DataLayout::arg_info_data_tag) {
- dp = end; // ArgInfoData is at the end of extra data section.
+ switch(dp->tag()) {
+ case DataLayout::no_tag:
+ return NULL;
+ case DataLayout::arg_info_data_tag:
+ dp = end;
+ return NULL; // ArgInfoData is at the end of extra data section.
+ case DataLayout::bit_data_tag:
+ if (m == NULL && dp->bci() == bci) {
+ return new BitData(dp);
+ }
break;
- }
- if (dp->bci() == bci) {
- assert(dp->tag() == DataLayout::bit_data_tag, "sane");
- return new BitData(dp);
+ case DataLayout::speculative_trap_data_tag:
+ if (m != NULL) {
+ SpeculativeTrapData* data = new SpeculativeTrapData(dp);
+ // data->method() may be null in case of a concurrent
+ // allocation. Assume it's for the same method and use that
+ // entry in that case.
+ if (dp->bci() == bci) {
+ if (data->method() == NULL) {
+ return NULL;
+ } else if (data->method() == m) {
+ return data;
+ }
+ }
+ }
+ break;
+ default:
+ fatal(err_msg("unexpected tag %d", dp->tag()));
}
}
- if (create_if_missing && dp < end) {
- // Allocate this one. There is no mutual exclusion,
- // so two threads could allocate different BCIs to the
- // same data layout. This means these extra data
- // records, like most other MDO contents, must not be
- // trusted too much.
- DataLayout temp;
- temp.initialize(DataLayout::bit_data_tag, bci, 0);
- dp->release_set_header(temp.header());
- assert(dp->tag() == DataLayout::bit_data_tag, "sane");
- //NO: assert(dp->bci() == bci, "no concurrent allocation");
- return new BitData(dp);
+ return NULL;
+}
+
+
+// Translate a bci to its corresponding extra data, or NULL.
+ProfileData* MethodData::bci_to_extra_data(int bci, Method* m, bool create_if_missing) {
+ // This code assumes an entry for a SpeculativeTrapData is 2 cells
+ assert(2*DataLayout::compute_size_in_bytes(BitData::static_cell_count()) ==
+ DataLayout::compute_size_in_bytes(SpeculativeTrapData::static_cell_count()),
+ "code needs to be adjusted");
+
+ DataLayout* dp = extra_data_base();
+ DataLayout* end = extra_data_limit();
+
+ // Allocation in the extra data space has to be atomic because not
+ // all entries have the same size and non atomic concurrent
+ // allocation would result in a corrupted extra data space.
+ while (true) {
+ ProfileData* result = bci_to_extra_data_helper(bci, m, dp);
+ if (result != NULL) {
+ return result;
+ }
+
+ if (create_if_missing && dp < end) {
+ assert(dp->tag() == DataLayout::no_tag || (dp->tag() == DataLayout::speculative_trap_data_tag && m != NULL), "should be free");
+ assert(next_extra(dp)->tag() == DataLayout::no_tag || next_extra(dp)->tag() == DataLayout::arg_info_data_tag, "should be free or arg info");
+ u1 tag = m == NULL ? DataLayout::bit_data_tag : DataLayout::speculative_trap_data_tag;
+ // SpeculativeTrapData is 2 slots. Make sure we have room.
+ if (m != NULL && next_extra(dp)->tag() != DataLayout::no_tag) {
+ return NULL;
+ }
+ DataLayout temp;
+ temp.initialize(tag, bci, 0);
+ // May have been set concurrently
+ if (dp->header() != temp.header() && !dp->atomic_set_header(temp.header())) {
+ // Allocation failure because of concurrent allocation. Try
+ // again.
+ continue;
+ }
+ assert(dp->tag() == tag, "sane");
+ assert(dp->bci() == bci, "no concurrent allocation");
+ if (tag == DataLayout::bit_data_tag) {
+ return new BitData(dp);
+ } else {
+ // If being allocated concurrently, one trap may be lost
+ SpeculativeTrapData* data = new SpeculativeTrapData(dp);
+ data->set_method(m);
+ return data;
+ }
+ }
+ return NULL;
}
return NULL;
}
@@ -1210,25 +1367,35 @@
for ( ; is_valid(data); data = next_data(data)) {
st->print("%d", dp_to_di(data->dp()));
st->fill_to(6);
- data->print_data_on(st);
+ data->print_data_on(st, this);
}
st->print_cr("--- Extra data:");
DataLayout* dp = extra_data_base();
DataLayout* end = extra_data_limit();
- for (; dp < end; dp = next_extra(dp)) {
+ for (;; dp = next_extra(dp)) {
+ assert(dp < end, "moved past end of extra data");
// No need for "OrderAccess::load_acquire" ops,
// since the data structure is monotonic.
- if (dp->tag() == DataLayout::no_tag) continue;
- if (dp->tag() == DataLayout::bit_data_tag) {
+ switch(dp->tag()) {
+ case DataLayout::no_tag:
+ continue;
+ case DataLayout::bit_data_tag:
data = new BitData(dp);
- } else {
- assert(dp->tag() == DataLayout::arg_info_data_tag, "must be BitData or ArgInfo");
+ break;
+ case DataLayout::speculative_trap_data_tag:
+ data = new SpeculativeTrapData(dp);
+ break;
+ case DataLayout::arg_info_data_tag:
data = new ArgInfoData(dp);
dp = end; // ArgInfoData is at the end of extra data section.
+ break;
+ default:
+ fatal(err_msg("unexpected tag %d", dp->tag()));
}
st->print("%d", dp_to_di(data->dp()));
st->fill_to(6);
data->print_data_on(st);
+ if (dp >= end) return;
}
}
#endif
@@ -1351,3 +1518,110 @@
assert(profile_parameters_jsr292_only(), "inconsistent");
return m->is_compiled_lambda_form();
}
+
+void MethodData::clean_extra_data_helper(DataLayout* dp, int shift, bool reset) {
+ if (shift == 0) {
+ return;
+ }
+ if (!reset) {
+ // Move all cells of trap entry at dp left by "shift" cells
+ intptr_t* start = (intptr_t*)dp;
+ intptr_t* end = (intptr_t*)next_extra(dp);
+ for (intptr_t* ptr = start; ptr < end; ptr++) {
+ *(ptr-shift) = *ptr;
+ }
+ } else {
+ // Reset "shift" cells stopping at dp
+ intptr_t* start = ((intptr_t*)dp) - shift;
+ intptr_t* end = (intptr_t*)dp;
+ for (intptr_t* ptr = start; ptr < end; ptr++) {
+ *ptr = 0;
+ }
+ }
+}
+
+// Remove SpeculativeTrapData entries that reference an unloaded
+// method
+void MethodData::clean_extra_data(BoolObjectClosure* is_alive) {
+ DataLayout* dp = extra_data_base();
+ DataLayout* end = extra_data_limit();
+
+ int shift = 0;
+ for (; dp < end; dp = next_extra(dp)) {
+ switch(dp->tag()) {
+ case DataLayout::speculative_trap_data_tag: {
+ SpeculativeTrapData* data = new SpeculativeTrapData(dp);
+ Method* m = data->method();
+ assert(m != NULL, "should have a method");
+ if (!m->method_holder()->is_loader_alive(is_alive)) {
+ // "shift" accumulates the number of cells for dead
+ // SpeculativeTrapData entries that have been seen so
+ // far. Following entries must be shifted left by that many
+ // cells to remove the dead SpeculativeTrapData entries.
+ shift += (int)((intptr_t*)next_extra(dp) - (intptr_t*)dp);
+ } else {
+ // Shift this entry left if it follows dead
+ // SpeculativeTrapData entries
+ clean_extra_data_helper(dp, shift);
+ }
+ break;
+ }
+ case DataLayout::bit_data_tag:
+ // Shift this entry left if it follows dead SpeculativeTrapData
+ // entries
+ clean_extra_data_helper(dp, shift);
+ continue;
+ case DataLayout::no_tag:
+ case DataLayout::arg_info_data_tag:
+ // We are at end of the live trap entries. The previous "shift"
+ // cells contain entries that are either dead or were shifted
+ // left. They need to be reset to no_tag
+ clean_extra_data_helper(dp, shift, true);
+ return;
+ default:
+ fatal(err_msg("unexpected tag %d", dp->tag()));
+ }
+ }
+}
+
+// Verify there's no unloaded method referenced by a
+// SpeculativeTrapData entry
+void MethodData::verify_extra_data_clean(BoolObjectClosure* is_alive) {
+#ifdef ASSERT
+ DataLayout* dp = extra_data_base();
+ DataLayout* end = extra_data_limit();
+
+ for (; dp < end; dp = next_extra(dp)) {
+ switch(dp->tag()) {
+ case DataLayout::speculative_trap_data_tag: {
+ SpeculativeTrapData* data = new SpeculativeTrapData(dp);
+ Method* m = data->method();
+ assert(m != NULL && m->method_holder()->is_loader_alive(is_alive), "Method should exist");
+ break;
+ }
+ case DataLayout::bit_data_tag:
+ continue;
+ case DataLayout::no_tag:
+ case DataLayout::arg_info_data_tag:
+ return;
+ default:
+ fatal(err_msg("unexpected tag %d", dp->tag()));
+ }
+ }
+#endif
+}
+
+void MethodData::clean_method_data(BoolObjectClosure* is_alive) {
+ for (ProfileData* data = first_data();
+ is_valid(data);
+ data = next_data(data)) {
+ data->clean_weak_klass_links(is_alive);
+ }
+ ParametersTypeData* parameters = parameters_type_data();
+ if (parameters != NULL) {
+ parameters->clean_weak_klass_links(is_alive);
+ }
+
+ clean_extra_data(is_alive);
+ verify_extra_data_clean(is_alive);
+}
--- a/hotspot/src/share/vm/oops/methodData.hpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/oops/methodData.hpp Tue Feb 25 18:16:24 2014 +0100
@@ -120,7 +120,8 @@
arg_info_data_tag,
call_type_data_tag,
virtual_call_type_data_tag,
- parameters_type_data_tag
+ parameters_type_data_tag,
+ speculative_trap_data_tag
};
enum {
@@ -189,8 +190,11 @@
void set_header(intptr_t value) {
_header._bits = value;
}
- void release_set_header(intptr_t value) {
- OrderAccess::release_store_ptr(&_header._bits, value);
+ bool atomic_set_header(intptr_t value) {
+ if (Atomic::cmpxchg_ptr(value, (volatile intptr_t*)&_header._bits, 0) == 0) {
+ return true;
+ }
+ return false;
}
intptr_t header() {
return _header._bits;
@@ -271,6 +275,7 @@
class MultiBranchData;
class ArgInfoData;
class ParametersTypeData;
+class SpeculativeTrapData;
// ProfileData
//
@@ -291,6 +296,8 @@
// This is a pointer to a section of profiling data.
DataLayout* _data;
+ char* print_data_on_helper(const MethodData* md) const;
+
protected:
DataLayout* data() { return _data; }
const DataLayout* data() const { return _data; }
@@ -440,6 +447,7 @@
virtual bool is_CallTypeData() const { return false; }
virtual bool is_VirtualCallTypeData()const { return false; }
virtual bool is_ParametersTypeData() const { return false; }
+ virtual bool is_SpeculativeTrapData()const { return false; }
BitData* as_BitData() const {
@@ -494,6 +502,10 @@
assert(is_ParametersTypeData(), "wrong type");
return is_ParametersTypeData() ? (ParametersTypeData*)this : NULL;
}
+ SpeculativeTrapData* as_SpeculativeTrapData() const {
+ assert(is_SpeculativeTrapData(), "wrong type");
+ return is_SpeculativeTrapData() ? (SpeculativeTrapData*)this : NULL;
+ }
// Subclass specific initialization
@@ -509,12 +521,14 @@
// translation here, and the required translators are in the ci subclasses.
virtual void translate_from(const ProfileData* data) {}
- virtual void print_data_on(outputStream* st) const {
+ virtual void print_data_on(outputStream* st, const char* extra = NULL) const {
ShouldNotReachHere();
}
+ void print_data_on(outputStream* st, const MethodData* md) const;
+
#ifndef PRODUCT
- void print_shared(outputStream* st, const char* name) const;
+ void print_shared(outputStream* st, const char* name, const char* extra) const;
void tab(outputStream* st, bool first = false) const;
#endif
};
@@ -576,7 +590,7 @@
#endif // CC_INTERP
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -639,7 +653,7 @@
#endif // CC_INTERP
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -726,7 +740,7 @@
void post_initialize(BytecodeStream* stream, MethodData* mdo);
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1137,7 +1151,7 @@
}
#ifndef PRODUCT
- virtual void print_data_on(outputStream* st) const;
+ virtual void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1282,7 +1296,7 @@
#ifndef PRODUCT
void print_receiver_data_on(outputStream* st) const;
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1325,7 +1339,7 @@
#endif // CC_INTERP
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1451,7 +1465,7 @@
}
#ifndef PRODUCT
- virtual void print_data_on(outputStream* st) const;
+ virtual void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1554,7 +1568,7 @@
void post_initialize(BytecodeStream* stream, MethodData* mdo);
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1632,7 +1646,7 @@
void post_initialize(BytecodeStream* stream, MethodData* mdo);
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1825,7 +1839,7 @@
void post_initialize(BytecodeStream* stream, MethodData* mdo);
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1852,7 +1866,7 @@
}
#ifndef PRODUCT
- void print_data_on(outputStream* st) const;
+ void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
};
@@ -1913,7 +1927,7 @@
}
#ifndef PRODUCT
- virtual void print_data_on(outputStream* st) const;
+ virtual void print_data_on(outputStream* st, const char* extra = NULL) const;
#endif
static ByteSize stack_slot_offset(int i) {
@@ -1925,6 +1939,54 @@
}
};
+// SpeculativeTrapData
+//
+// A SpeculativeTrapData is used to record traps due to type
+// speculation. It records the root of the compilation: that type
+// speculation is wrong in the context of one compilation (for
+// method1) doesn't mean it's wrong in the context of another one (for
+// method2). Type speculation could have more/different data in the
+// context of the compilation of method2 and it's worthwhile to try an
+// optimization that failed for compilation of method1 in the context
+// of compilation of method2.
+// Space for SpeculativeTrapData entries is allocated from the extra
+// data space in the MDO. If we run out of space, the trap data for
+// the ProfileData at that bci is updated.
+class SpeculativeTrapData : public ProfileData {
+protected:
+ enum {
+ method_offset,
+ speculative_trap_cell_count
+ };
+public:
+ SpeculativeTrapData(DataLayout* layout) : ProfileData(layout) {
+ assert(layout->tag() == DataLayout::speculative_trap_data_tag, "wrong type");
+ }
+
+ virtual bool is_SpeculativeTrapData() const { return true; }
+
+ static int static_cell_count() {
+ return speculative_trap_cell_count;
+ }
+
+ virtual int cell_count() const {
+ return static_cell_count();
+ }
+
+ // Direct accessor
+ Method* method() const {
+ return (Method*)intptr_at(method_offset);
+ }
+
+ void set_method(Method* m) {
+ set_intptr_at(method_offset, (intptr_t)m);
+ }
+
+#ifndef PRODUCT
+ virtual void print_data_on(outputStream* st, const char* extra = NULL) const;
+#endif
+};
+
// MethodData*
//
// A MethodData* holds information which has been collected about
@@ -1994,7 +2056,7 @@
// Whole-method sticky bits and flags
enum {
- _trap_hist_limit = 17, // decoupled from Deoptimization::Reason_LIMIT
+ _trap_hist_limit = 18, // decoupled from Deoptimization::Reason_LIMIT
_trap_hist_mask = max_jubyte,
_extra_data_count = 4 // extra DataLayout headers, for trap history
}; // Public flag values
@@ -2049,6 +2111,7 @@
// Helper for size computation
static int compute_data_size(BytecodeStream* stream);
static int bytecode_cell_count(Bytecodes::Code code);
+ static bool is_speculative_trap_bytecode(Bytecodes::Code code);
enum { no_profile_data = -1, variable_cell_count = -2 };
// Helper for initialization
@@ -2092,8 +2155,9 @@
// What is the index of the first data entry?
int first_di() const { return 0; }
+ ProfileData* bci_to_extra_data_helper(int bci, Method* m, DataLayout*& dp);
// Find or create an extra ProfileData:
- ProfileData* bci_to_extra_data(int bci, bool create_if_missing);
+ ProfileData* bci_to_extra_data(int bci, Method* m, bool create_if_missing);
// return the argument info cell
ArgInfoData *arg_info();
@@ -2116,6 +2180,10 @@
static bool profile_parameters_jsr292_only();
static bool profile_all_parameters();
+ void clean_extra_data(BoolObjectClosure* is_alive);
+ void clean_extra_data_helper(DataLayout* dp, int shift, bool reset = false);
+ void verify_extra_data_clean(BoolObjectClosure* is_alive);
+
public:
static int header_size() {
return sizeof(MethodData)/wordSize;
@@ -2124,7 +2192,7 @@
// Compute the size of a MethodData* before it is created.
static int compute_allocation_size_in_bytes(methodHandle method);
static int compute_allocation_size_in_words(methodHandle method);
- static int compute_extra_data_count(int data_size, int empty_bc_count);
+ static int compute_extra_data_count(int data_size, int empty_bc_count, bool needs_speculative_traps);
// Determine if a given bytecode can have profile information.
static bool bytecode_has_profile(Bytecodes::Code code) {
@@ -2265,9 +2333,26 @@
ProfileData* bci_to_data(int bci);
// Same, but try to create an extra_data record if one is needed:
- ProfileData* allocate_bci_to_data(int bci) {
- ProfileData* data = bci_to_data(bci);
- return (data != NULL) ? data : bci_to_extra_data(bci, true);
+ ProfileData* allocate_bci_to_data(int bci, Method* m) {
+ ProfileData* data = NULL;
+ // If m not NULL, try to allocate a SpeculativeTrapData entry
+ if (m == NULL) {
+ data = bci_to_data(bci);
+ }
+ if (data != NULL) {
+ return data;
+ }
+ data = bci_to_extra_data(bci, m, true);
+ if (data != NULL) {
+ return data;
+ }
+ // If SpeculativeTrapData allocation fails try to allocate a
+ // regular entry
+ data = bci_to_data(bci);
+ if (data != NULL) {
+ return data;
+ }
+ return bci_to_extra_data(bci, NULL, true);
}
// Add a handful of extra data records, for trap tracking.
@@ -2275,7 +2360,7 @@
DataLayout* extra_data_limit() const { return (DataLayout*)((address)this + size_in_bytes()); }
int extra_data_size() const { return (address)extra_data_limit()
- (address)extra_data_base(); }
- static DataLayout* next_extra(DataLayout* dp) { return (DataLayout*)((address)dp + in_bytes(DataLayout::cell_offset(0))); }
+ static DataLayout* next_extra(DataLayout* dp);
// Return (uint)-1 for overflow.
uint trap_count(int reason) const {
@@ -2375,6 +2460,8 @@
static bool profile_return();
static bool profile_parameters();
static bool profile_return_jsr292_only();
+
+ void clean_method_data(BoolObjectClosure* is_alive);
};
#endif // SHARE_VM_OOPS_METHODDATAOOP_HPP
--- a/hotspot/src/share/vm/opto/compile.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/opto/compile.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -3249,7 +3249,8 @@
// because of a transient condition during start-up in the interpreter.
return false;
}
- if (md->has_trap_at(bci, reason) != 0) {
+ ciMethod* m = Deoptimization::reason_is_speculate(reason) ? this->method() : NULL;
+ if (md->has_trap_at(bci, m, reason) != 0) {
// Assume PerBytecodeTrapLimit==0, for a more conservative heuristic.
// Also, if there are multiple reasons, or if there is no per-BCI record,
// assume the worst.
@@ -3267,7 +3268,7 @@
// Less-accurate variant which does not require a method and bci.
bool Compile::too_many_traps(Deoptimization::DeoptReason reason,
ciMethodData* logmd) {
- if (trap_count(reason) >= (uint)PerMethodTrapLimit) {
+ if (trap_count(reason) >= Deoptimization::per_method_trap_limit(reason)) {
// Too many traps globally.
// Note that we use cumulative trap_count, not just md->trap_count.
if (log()) {
@@ -3302,10 +3303,11 @@
uint m_cutoff = (uint) PerMethodRecompilationCutoff / 2 + 1; // not zero
Deoptimization::DeoptReason per_bc_reason
= Deoptimization::reason_recorded_per_bytecode_if_any(reason);
+ ciMethod* m = Deoptimization::reason_is_speculate(reason) ? this->method() : NULL;
if ((per_bc_reason == Deoptimization::Reason_none
- || md->has_trap_at(bci, reason) != 0)
+ || md->has_trap_at(bci, m, reason) != 0)
// The trap frequency measure we care about is the recompile count:
- && md->trap_recompiled_at(bci)
+ && md->trap_recompiled_at(bci, m)
&& md->overflow_recompile_count() >= bc_cutoff) {
// Do not emit a trap here if it has already caused recompilations.
// Also, if there are multiple reasons, or if there is no per-BCI record,
--- a/hotspot/src/share/vm/opto/doCall.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/opto/doCall.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -250,7 +250,7 @@
CallGenerator* miss_cg;
Deoptimization::DeoptReason reason = morphism == 2 ?
Deoptimization::Reason_bimorphic :
- Deoptimization::Reason_class_check;
+ (speculative_receiver_type == NULL ? Deoptimization::Reason_class_check : Deoptimization::Reason_speculate_class_check);
if ((morphism == 1 || (morphism == 2 && next_hit_cg != NULL)) &&
!too_many_traps(jvms->method(), jvms->bci(), reason)
) {
--- a/hotspot/src/share/vm/opto/graphKit.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/opto/graphKit.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -612,9 +612,10 @@
// Usual case: Bail to interpreter.
// Reserve the right to recompile if we haven't seen anything yet.
+ assert(!Deoptimization::reason_is_speculate(reason), "unsupported");
Deoptimization::DeoptAction action = Deoptimization::Action_maybe_recompile;
if (treat_throw_as_hot
- && (method()->method_data()->trap_recompiled_at(bci())
+ && (method()->method_data()->trap_recompiled_at(bci(), NULL)
|| C->too_many_traps(reason))) {
// We cannot afford to take more traps here. Suffer in the interpreter.
if (C->log() != NULL)
@@ -2145,7 +2146,7 @@
*
* @param n receiver node
*
- * @return node with improved type
+ * @return node with improved type
*/
Node* GraphKit::record_profiled_receiver_for_speculation(Node* n) {
if (!UseTypeSpeculation) {
@@ -2739,12 +2740,14 @@
// Subsequent type checks will always fold up.
Node* GraphKit::maybe_cast_profiled_receiver(Node* not_null_obj,
ciKlass* require_klass,
- ciKlass* spec_klass,
+ ciKlass* spec_klass,
bool safe_for_replace) {
if (!UseTypeProfile || !TypeProfileCasts) return NULL;
+ Deoptimization::DeoptReason reason = spec_klass == NULL ? Deoptimization::Reason_class_check : Deoptimization::Reason_speculate_class_check;
+
// Make sure we haven't already deoptimized from this tactic.
- if (too_many_traps(Deoptimization::Reason_class_check))
+ if (too_many_traps(reason))
return NULL;
// (No, this isn't a call, but it's enough like a virtual call
@@ -2766,7 +2769,7 @@
&exact_obj);
{ PreserveJVMState pjvms(this);
set_control(slow_ctl);
- uncommon_trap(Deoptimization::Reason_class_check,
+ uncommon_trap(reason,
Deoptimization::Action_maybe_recompile);
}
if (safe_for_replace) {
@@ -2793,8 +2796,10 @@
bool not_null) {
// type == NULL if profiling tells us this object is always null
if (type != NULL) {
- if (!too_many_traps(Deoptimization::Reason_null_check) &&
- !too_many_traps(Deoptimization::Reason_class_check)) {
+ Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check;
+ Deoptimization::DeoptReason null_reason = Deoptimization::Reason_null_check;
+ if (!too_many_traps(null_reason) &&
+ !too_many_traps(class_reason)) {
Node* not_null_obj = NULL;
// not_null is true if we know the object is not null and
// there's no need for a null check
@@ -2813,7 +2818,7 @@
{
PreserveJVMState pjvms(this);
set_control(slow_ctl);
- uncommon_trap(Deoptimization::Reason_class_check,
+ uncommon_trap(class_reason,
Deoptimization::Action_maybe_recompile);
}
replace_in_map(not_null_obj, exact_obj);
@@ -2882,7 +2887,7 @@
}
if (known_statically && UseTypeSpeculation) {
- // If we know the type check always succeed then we don't use the
+ // If we know the type check always succeeds then we don't use the
// profiling data at this bytecode. Don't lose it, feed it to the
// type system as a speculative type.
not_null_obj = record_profiled_receiver_for_speculation(not_null_obj);
--- a/hotspot/src/share/vm/opto/graphKit.hpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/opto/graphKit.hpp Tue Feb 25 18:16:24 2014 +0100
@@ -406,7 +406,7 @@
// Use the type profile to narrow an object type.
Node* maybe_cast_profiled_receiver(Node* not_null_obj,
ciKlass* require_klass,
- ciKlass* spec,
+ ciKlass* spec,
bool safe_for_replace);
// Cast obj to type and emit guard unless we had too many traps here already
--- a/hotspot/src/share/vm/runtime/deoptimization.cpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/runtime/deoptimization.cpp Tue Feb 25 18:16:24 2014 +0100
@@ -1489,6 +1489,7 @@
bool maybe_prior_trap = false;
bool maybe_prior_recompile = false;
pdata = query_update_method_data(trap_mdo, trap_bci, reason,
+ nm->method(),
//outputs:
this_trap_count,
maybe_prior_trap,
@@ -1534,7 +1535,7 @@
}
// Go back to the compiler if there are too many traps in this method.
- if (this_trap_count >= (uint)PerMethodTrapLimit) {
+ if (this_trap_count >= per_method_trap_limit(reason)) {
// If there are too many traps in this method, force a recompile.
// This will allow the compiler to see the limit overflow, and
// take corrective action, if possible.
@@ -1622,6 +1623,7 @@
Deoptimization::query_update_method_data(MethodData* trap_mdo,
int trap_bci,
Deoptimization::DeoptReason reason,
+ Method* compiled_method,
//outputs:
uint& ret_this_trap_count,
bool& ret_maybe_prior_trap,
@@ -1645,9 +1647,16 @@
// Find the profile data for this BCI. If there isn't one,
// try to allocate one from the MDO's set of spares.
// This will let us detect a repeated trap at this point.
- pdata = trap_mdo->allocate_bci_to_data(trap_bci);
+ pdata = trap_mdo->allocate_bci_to_data(trap_bci, reason_is_speculate(reason) ? compiled_method : NULL);
if (pdata != NULL) {
+ if (reason_is_speculate(reason) && !pdata->is_SpeculativeTrapData()) {
+ if (LogCompilation && xtty != NULL) {
+ ttyLocker ttyl;
+ // no more room for speculative traps in this MDO
+ xtty->elem("speculative_traps_oom");
+ }
+ }
// Query the trap state of this profile datum.
int tstate0 = pdata->trap_state();
if (!trap_state_has_reason(tstate0, per_bc_reason))
@@ -1685,8 +1694,10 @@
uint ignore_this_trap_count;
bool ignore_maybe_prior_trap;
bool ignore_maybe_prior_recompile;
+ assert(!reason_is_speculate(reason), "reason speculate only used by compiler");
query_update_method_data(trap_mdo, trap_bci,
(DeoptReason)reason,
+ NULL,
ignore_this_trap_count,
ignore_maybe_prior_trap,
ignore_maybe_prior_recompile);
@@ -1814,7 +1825,8 @@
"div0_check",
"age",
"predicate",
- "loop_limit_check"
+ "loop_limit_check",
+ "speculate_class_check"
};
const char* Deoptimization::_trap_action_name[Action_LIMIT] = {
// Note: Keep this in sync. with enum DeoptAction.
--- a/hotspot/src/share/vm/runtime/deoptimization.hpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/runtime/deoptimization.hpp Tue Feb 25 18:16:24 2014 +0100
@@ -59,6 +59,7 @@
Reason_age, // nmethod too old; tier threshold reached
Reason_predicate, // compiler generated predicate failed
Reason_loop_limit_check, // compiler generated loop limits check failed
+ Reason_speculate_class_check, // saw unexpected object class from type speculation
Reason_LIMIT,
// Note: Keep this enum in sync. with _trap_reason_name.
Reason_RECORDED_LIMIT = Reason_bimorphic // some are not recorded per bc
@@ -311,10 +312,23 @@
return reason;
else if (reason == Reason_div0_check) // null check due to divide-by-zero?
return Reason_null_check; // recorded per BCI as a null check
+ else if (reason == Reason_speculate_class_check)
+ return Reason_class_check;
else
return Reason_none;
}
+ static bool reason_is_speculate(int reason) {
+ if (reason == Reason_speculate_class_check) {
+ return true;
+ }
+ return false;
+ }
+
+ static uint per_method_trap_limit(int reason) {
+ return reason_is_speculate(reason) ? (uint)PerMethodSpecTrapLimit : (uint)PerMethodTrapLimit;
+ }
+
static const char* trap_reason_name(int reason);
static const char* trap_action_name(int action);
// Format like reason='foo' action='bar' index='123'.
@@ -337,6 +351,7 @@
static ProfileData* query_update_method_data(MethodData* trap_mdo,
int trap_bci,
DeoptReason reason,
+ Method* compiled_method,
//outputs:
uint& ret_this_trap_count,
bool& ret_maybe_prior_trap,
--- a/hotspot/src/share/vm/runtime/globals.hpp Tue Feb 25 14:09:02 2014 +0100
+++ b/hotspot/src/share/vm/runtime/globals.hpp Tue Feb 25 18:16:24 2014 +0100
@@ -3078,9 +3078,15 @@
product(intx, PerMethodTrapLimit, 100, \
"Limit on traps (of one kind) in a method (includes inlines)") \
\
+ experimental(intx, PerMethodSpecTrapLimit, 5000, \
+ "Limit on speculative traps (of one kind) in a method (includes inlines)") \
+ \
product(intx, PerBytecodeTrapLimit, 4, \
"Limit on traps (of one kind) at a particular BCI") \
\
+ experimental(intx, SpecTrapLimitExtraEntries, 3, \
+ "Extra method data trap entries for speculation") \
+ \
develop(intx, InlineFrequencyRatio, 20, \
"Ratio of call site execution to caller method invocation") \
\
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/uncommontrap/TestSpecTrapClassUnloading.java Tue Feb 25 18:16:24 2014 +0100
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8031752
+ * @summary speculative traps need to be cleaned up at GC
+ * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:TypeProfileLevel=222 -XX:CompileCommand=exclude,java.lang.reflect.Method::invoke -XX:CompileCommand=exclude,sun.reflect.DelegatingMethodAccessorImpl::invoke -Xmx1M TestSpecTrapClassUnloading
+ *
+ */
+
+import java.lang.reflect.Method;
+
+public class TestSpecTrapClassUnloading {
+ static class B {
+ final public boolean m(Object o) {
+ if (o.getClass() == B.class) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ static class MemoryChunk {
+ MemoryChunk other;
+ long[] array;
+ MemoryChunk(MemoryChunk other) {
+ other = other;
+ array = new long[1024 * 1024 * 1024];
+ }
+ }
+
+ static void m1(B b, Object o) {
+ b.m(o);
+ }
+
+ static void m2(B b, Object o) {
+ b.m(o);
+ }
+
+ public static void main(String[] args) throws Exception {
+ Method m = B.class.getMethod("m", Object.class);
+ Object o = new Object();
+ B b = new B();
+
+ // add speculative trap in B.m() for m1
+ for (int i = 0; i < 20000; i++) {
+ m1(b, b);
+ }
+ m1(b, o);
+
+ // add speculative trap in B.m() for code generated by reflection
+ for (int i = 0; i < 20000; i++) {
+ m.invoke(b, b);
+ }
+ m.invoke(b, o);
+
+ m = null;
+
+ // add speculative trap in B.m() for m2
+ for (int i = 0; i < 20000; i++) {
+ m2(b, b);
+ }
+ m2(b, o);
+
+ // Exhaust memory which causes the code generated by
+ // reflection to be unloaded but B.m() is not.
+ MemoryChunk root = null;
+ try {
+ while (true) {
+ root = new MemoryChunk(root);
+ }
+ } catch(OutOfMemoryError e) {
+ root = null;
+ }
+ }
+}