Klass* Dependencies::find_unique_concrete_subtype(Klass* ctxk) {
ClassHierarchyWalker wf(ctxk); // Ignore ctxk when walking.
wf.record_witnesses(1); // Record one other witness when walking.
Klass* wit = wf.find_witness_subtype(ctxk);
if (wit != NULL) return NULL; // Too many witnesses.
Klass* conck = wf.participant(0);
if (conck == NULL) {
#ifndef PRODUCT
if (VerifyDependencies) {
FlagSetting fs(TraceDependencies, false);
if (!Dependencies::is_concrete_klass(ctxk)) {
guarantee(NULL ==
(void *)check_abstract_with_no_concrete_subtype(ctxk),
"verify dep.");
} else {
guarantee(NULL ==
(void *)check_concrete_with_no_concrete_subtype(ctxk),
"verify dep.");
}
}
#endif //PRODUCT
return ctxk; // Return ctxk as a flag for "no subtypes".
} else {
#ifndef PRODUCT
if (VerifyDependencies) {
FlagSetting fs(TraceDependencies, false);
if (!Dependencies::is_concrete_klass(ctxk)) {
guarantee(NULL == (void *)
check_abstract_with_unique_concrete_subtype(ctxk, conck),
"verify dep.");
}
}
#endif //PRODUCT
return conck;
}
}
Klass* Dependencies::check_abstract_with_exclusive_concrete_subtypes(
Klass* ctxk,
Klass* k1,
Klass* k2,
KlassDepChange* changes) {
ClassHierarchyWalker wf;
wf.add_participant(k1);
wf.add_participant(k2);
return wf.find_witness_subtype(ctxk, changes);
}
int Dependencies::find_exclusive_concrete_subtypes(Klass* ctxk,
int klen,
Klass* karray[]) {
ClassHierarchyWalker wf;
wf.record_witnesses(klen);
Klass* wit = wf.find_witness_subtype(ctxk);
if (wit != NULL) return -1; // Too many witnesses.
int num = wf.num_participants();
assert(num <= klen, "oob");
for (int i = 0; i < num; i++)
karray[i] = wf.participant(i);
#ifndef PRODUCT
if (VerifyDependencies) {
FlagSetting fs(TraceDependencies, false);
switch (Dependencies::is_concrete_klass(ctxk)? -1: num) {
case -1: // ctxk was itself concrete
guarantee(num == 1 && karray[0] == ctxk, "verify dep.");
break;
case 0:
guarantee(NULL == (void *)check_abstract_with_no_concrete_subtype(ctxk),
"verify dep.");
break;
case 1:
guarantee(NULL == (void *)
check_abstract_with_unique_concrete_subtype(ctxk, karray[0]),
"verify dep.");
break;
case 2:
guarantee(NULL == (void *)
check_abstract_with_exclusive_concrete_subtypes(ctxk,
karray[0],
karray[1]),
"verify dep.");
break;
default:
ShouldNotReachHere(); // klen > 2 yet supported
}
}
#endif //PRODUCT
return num;
}
bool Dependencies::is_concrete_root_method(Method* uniqm, Klass* ctxk) {
if (uniqm == NULL) {
return false; // match Dependencies::is_concrete_method() behavior
}
if (ctxk->is_interface()) {
Klass* implementor = InstanceKlass::cast(ctxk)->implementor();
assert(implementor != ctxk, "single implementor only"); // should have been invalidated earlier
ctxk = implementor;
}
InstanceKlass* holder = uniqm->method_holder();
assert(!holder->is_interface(), "no default methods allowed");
assert(ctxk->is_subclass_of(holder) || holder->is_subclass_of(ctxk), "not related");
return ctxk->is_subclass_of(holder);
}
Klass* Dependencies::check_unique_concrete_method(Klass* ctxk,
Method* uniqm,
KlassDepChange* changes) {
ClassHierarchyWalker wf(uniqm->method_holder(), uniqm);
Klass* witness = wf.find_witness_definer(ctxk, changes);
if (witness != NULL) {
return witness;
}
if (!Dependencies::is_concrete_root_method(uniqm, ctxk) || changes != NULL) {
Klass* conck = find_witness_AME(ctxk, uniqm, changes);
if (conck != NULL) {
return conck;
}
}
return NULL;
}
Klass* Dependencies::find_witness_AME(Klass* ctxk, Method* m, KlassDepChange* changes) {
if (m != NULL) {
if (changes != NULL) {
ClassHierarchyWalker wf(m);
Klass* new_type = changes->new_type();
if (wf.witnessed_reabstraction_in_supers(new_type)) {
return new_type;
}
} else {
ClassHierarchyWalker wf(m->method_holder());
Klass* conck = wf.find_witness_subtype(ctxk);
if (conck != NULL) {
Method* cm = InstanceKlass::cast(conck)->find_instance_method(m->name(), m->signature(), Klass::skip_private);
if (!Dependencies::is_concrete_method(cm, conck)) {
return conck;
}
}
}
}
return NULL;
}
static bool overrides(Method* sub_m, Method* base_m) {
assert(base_m != NULL, "base method should be non null");
if (sub_m == NULL) {
return false;
}
if (base_m->is_public() || base_m->is_protected() ||
base_m->method_holder()->is_same_class_package(sub_m->method_holder())) {
return true;
}
return false;
}
Method* Dependencies::find_unique_concrete_method(Klass* ctxk, Method* m) {
ClassHierarchyWalker wf(m);
assert(wf.check_method_context(ctxk, m), "proper context");
wf.record_witnesses(1);
Klass* wit = wf.find_witness_definer(ctxk);
if (wit != NULL) return NULL; // Too many witnesses.
Method* fm = wf.found_method(0); // Will be NULL if num_parts == 0.
if (Dependencies::is_concrete_method(m, ctxk)) {
if (fm == NULL) {
fm = m;
} else if (fm != m) {
return NULL;
}
} else if (Dependencies::find_witness_AME(ctxk, fm) != NULL) {
return NULL;
} else if (!overrides(fm, m)) {
return NULL;
}
assert(Dependencies::is_concrete_root_method(fm, ctxk) == Dependencies::is_concrete_method(m, ctxk), "mismatch");
#ifndef PRODUCT
if (VerifyDependencies && fm != NULL) {
guarantee(NULL == (void *)check_unique_concrete_method(ctxk, fm),
"verify dep.");
}
#endif //PRODUCT
return fm;
}
Klass* Dependencies::check_exclusive_concrete_methods(Klass* ctxk,
Method* m1,
Method* m2,
KlassDepChange* changes) {
ClassHierarchyWalker wf(m1);
wf.add_participant(m1->method_holder());
wf.add_participant(m2->method_holder());
return wf.find_witness_definer(ctxk, changes);
}
Klass* Dependencies::check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepChange* changes) {
Klass* search_at = ctxk;
if (changes != NULL)
search_at = changes->new_type(); // just look at the new bit
return find_finalizable_subclass(search_at);
}
Klass* Dependencies::check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes) {
assert(call_site ->is_a(SystemDictionary::CallSite_klass()), "sanity");
assert(method_handle->is_a(SystemDictionary::MethodHandle_klass()), "sanity");
if (changes == NULL) {
if (java_lang_invoke_CallSite::target(call_site) != method_handle)
return call_site->klass(); // assertion failed
} else {
if (call_site == changes->call_site() && java_lang_invoke_CallSite::target(call_site) != changes->method_handle()) {
assert(method_handle != changes->method_handle(), "must be");
return call_site->klass(); // assertion failed
}
}
return NULL; // assertion still valid
}
void Dependencies::DepStream::trace_and_log_witness(Klass* witness) {
if (witness != NULL) {
if (TraceDependencies) {
print_dependency(witness, /*verbose=*/ true);
}
log_dependency(witness);
}
}
Klass* Dependencies::DepStream::check_klass_dependency(KlassDepChange* changes) {
assert_locked_or_safepoint(Compile_lock);
Dependencies::check_valid_dependency_type(type());
Klass* witness = NULL;
switch (type()) {
case evol_method:
witness = check_evol_method(method_argument(0));
break;
case leaf_type:
witness = check_leaf_type(context_type());
break;
case abstract_with_unique_concrete_subtype:
witness = check_abstract_with_unique_concrete_subtype(context_type(), type_argument(1), changes);
break;
case abstract_with_no_concrete_subtype:
witness = check_abstract_with_no_concrete_subtype(context_type(), changes);
break;
case concrete_with_no_concrete_subtype:
witness = check_concrete_with_no_concrete_subtype(context_type(), changes);
break;
case unique_concrete_method:
witness = check_unique_concrete_method(context_type(), method_argument(1), changes);
break;
case abstract_with_exclusive_concrete_subtypes_2:
witness = check_abstract_with_exclusive_concrete_subtypes(context_type(), type_argument(1), type_argument(2), changes);
break;
case exclusive_concrete_methods_2:
witness = check_exclusive_concrete_methods(context_type(), method_argument(1), method_argument(2), changes);
break;
case no_finalizable_subclasses:
witness = check_has_no_finalizable_subclasses(context_type(), changes);
break;
default:
witness = NULL;
break;
}
trace_and_log_witness(witness);
return witness;
}
Klass* Dependencies::DepStream::check_call_site_dependency(CallSiteDepChange* changes) {
assert_locked_or_safepoint(Compile_lock);
Dependencies::check_valid_dependency_type(type());
Klass* witness = NULL;
switch (type()) {
case call_site_target_value:
witness = check_call_site_target_value(argument_oop(0), argument_oop(1), changes);
break;
default:
witness = NULL;
break;
}
trace_and_log_witness(witness);
return witness;
}
Klass* Dependencies::DepStream::spot_check_dependency_at(DepChange& changes) {
if (changes.is_klass_change() && changes.as_klass_change()->involves_context(context_type()))
return check_klass_dependency(changes.as_klass_change());
if (changes.is_call_site_change())
return check_call_site_dependency(changes.as_call_site_change());
return NULL;
}
void DepChange::print() {
int nsup = 0, nint = 0;
for (ContextStream str(*this); str.next(); ) {
Klass* k = str.klass();
switch (str.change_type()) {
case Change_new_type:
tty->print_cr(" dependee = %s", InstanceKlass::cast(k)->external_name());
break;
case Change_new_sub:
if (!WizardMode) {
++nsup;
} else {
tty->print_cr(" context super = %s", InstanceKlass::cast(k)->external_name());
}
break;
case Change_new_impl:
if (!WizardMode) {
++nint;
} else {
tty->print_cr(" context interface = %s", InstanceKlass::cast(k)->external_name());
}
break;
}
}
if (nsup + nint != 0) {
tty->print_cr(" context supers = %d, interfaces = %d", nsup, nint);
}
}
void DepChange::ContextStream::start() {
Klass* new_type = _changes.is_klass_change() ? _changes.as_klass_change()->new_type() : (Klass*) NULL;
_change_type = (new_type == NULL ? NO_CHANGE : Start_Klass);
_klass = new_type;
_ti_base = NULL;
_ti_index = 0;
_ti_limit = 0;
}
bool DepChange::ContextStream::next() {
switch (_change_type) {
case Start_Klass: // initial state; _klass is the new type
_ti_base = InstanceKlass::cast(_klass)->transitive_interfaces();
_ti_index = 0;
_change_type = Change_new_type;
return true;
case Change_new_type:
_change_type = Change_new_sub;
case Change_new_sub:
{
_klass = InstanceKlass::cast(_klass)->super();
if (_klass != NULL) {
return true;
}
}
_ti_limit = (_ti_base == NULL) ? 0 : _ti_base->length();
_change_type = Change_new_impl;
case Change_new_impl:
if (_ti_index < _ti_limit) {
_klass = _ti_base->at(_ti_index++);
return true;
}
_change_type = NO_CHANGE; // iterator is exhausted
case NO_CHANGE:
break;
default:
ShouldNotReachHere();
}
return false;
}
void KlassDepChange::initialize() {
assert_lock_strong(Compile_lock);
for (ContextStream str(*this); str.next(); ) {
Klass* d = str.klass();
assert(!InstanceKlass::cast(d)->is_marked_dependent(), "checking");
InstanceKlass::cast(d)->set_is_marked_dependent(true);
}
}
KlassDepChange::~KlassDepChange() {
for (ContextStream str(*this); str.next(); ) {
Klass* d = str.klass();
InstanceKlass::cast(d)->set_is_marked_dependent(false);
}
}
bool KlassDepChange::involves_context(Klass* k) {
if (k == NULL || !k->oop_is_instance()) {
return false;
}
InstanceKlass* ik = InstanceKlass::cast(k);
bool is_contained = ik->is_marked_dependent();
assert(is_contained == new_type()->is_subtype_of(k),
"correct marking of potential context types");
return is_contained;
}
#ifndef PRODUCT
void Dependencies::print_statistics() {
if (deps_find_witness_print != 0) {
deps_find_witness_print = -1;
count_find_witness_calls();
}
}
#endif
C:\hotspot-69087d08d473\src\share\vm/code/dependencies.hpp
#ifndef SHARE_VM_CODE_DEPENDENCIES_HPP
#define SHARE_VM_CODE_DEPENDENCIES_HPP
#include "ci/ciCallSite.hpp"
#include "ci/ciKlass.hpp"
#include "ci/ciMethodHandle.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/compressedStream.hpp"
#include "code/nmethod.hpp"
#include "utilities/growableArray.hpp"
class ciEnv;
class nmethod;
class OopRecorder;
class xmlStream;
class CompileLog;
class DepChange;
class KlassDepChange;
class CallSiteDepChange;
class No_Safepoint_Verifier;
class Dependencies: public ResourceObj {
public:
enum DepType {
end_marker = 0,
evol_method,
FIRST_TYPE = evol_method,
leaf_type,
abstract_with_unique_concrete_subtype,
abstract_with_no_concrete_subtype,
concrete_with_no_concrete_subtype,
unique_concrete_method, // one unique concrete method under CX
abstract_with_exclusive_concrete_subtypes_2,
exclusive_concrete_methods_2,
no_finalizable_subclasses,
call_site_target_value,
TYPE_LIMIT
};
enum {
LG2_TYPE_LIMIT = 4, // assert(TYPE_LIMIT <= (1<<LG2_TYPE_LIMIT))
all_types = ((1 << TYPE_LIMIT) - 1) & ((-1) << FIRST_TYPE),
non_klass_types = (1 << call_site_target_value),
klass_types = all_types & ~non_klass_types,
non_ctxk_types = (1 << evol_method),
implicit_ctxk_types = (1 << call_site_target_value),
explicit_ctxk_types = all_types & ~(non_ctxk_types | implicit_ctxk_types),
max_arg_count = 3, // current maximum number of arguments (incl. ctxk)
default_context_type_bit = (1<<LG2_TYPE_LIMIT)
};
static const char* dep_name(DepType dept);
static int dep_args(DepType dept);
static bool is_klass_type( DepType dept) { return dept_in_mask(dept, klass_types ); }
static bool has_explicit_context_arg(DepType dept) { return dept_in_mask(dept, explicit_ctxk_types); }
static bool has_implicit_context_arg(DepType dept) { return dept_in_mask(dept, implicit_ctxk_types); }
static int dep_context_arg(DepType dept) { return has_explicit_context_arg(dept) ? 0 : -1; }
static int dep_implicit_context_arg(DepType dept) { return has_implicit_context_arg(dept) ? 0 : -1; }
static void check_valid_dependency_type(DepType dept);
private:
GrowableArray<int>* _dep_seen; // (seen[h->ident] & (1<<dept))
GrowableArray<ciBaseObject*>* _deps[TYPE_LIMIT];
static const char* _dep_name[TYPE_LIMIT];
static int _dep_args[TYPE_LIMIT];
static bool dept_in_mask(DepType dept, int mask) {
return (int)dept >= 0 && dept < TYPE_LIMIT && ((1<<dept) & mask) != 0;
}
bool note_dep_seen(int dept, ciBaseObject* x) {
assert(dept < BitsPerInt, "oob");
int x_id = x->ident();
assert(_dep_seen != NULL, "deps must be writable");
int seen = _dep_seen->at_grow(x_id, 0);
_dep_seen->at_put(x_id, seen | (1<<dept));
return (seen & (1<<dept)) != 0;
}
bool maybe_merge_ctxk(GrowableArray<ciBaseObject*>* deps,
int ctxk_i, ciKlass* ctxk);
void sort_all_deps();
size_t estimate_size_in_bytes();
void initialize(ciEnv* env);
OopRecorder* _oop_recorder;
CompileLog* _log;
address _content_bytes; // everything but the oop references, encoded
size_t _size_in_bytes;
public:
Dependencies(ciEnv* env) {
initialize(env);
}
private:
static void check_ctxk(ciKlass* ctxk) {
assert(ctxk->is_instance_klass(), "java types only");
}
static void check_ctxk_concrete(ciKlass* ctxk) {
assert(is_concrete_klass(ctxk->as_instance_klass()), "must be concrete");
}
static void check_ctxk_abstract(ciKlass* ctxk) {
check_ctxk(ctxk);
assert(!is_concrete_klass(ctxk->as_instance_klass()), "must be abstract");
}
void assert_common_1(DepType dept, ciBaseObject* x);
void assert_common_2(DepType dept, ciBaseObject* x0, ciBaseObject* x1);
void assert_common_3(DepType dept, ciKlass* ctxk, ciBaseObject* x1, ciBaseObject* x2);
public:
void assert_evol_method(ciMethod* m);
void assert_leaf_type(ciKlass* ctxk);
void assert_abstract_with_unique_concrete_subtype(ciKlass* ctxk, ciKlass* conck);
void assert_abstract_with_no_concrete_subtype(ciKlass* ctxk);
void assert_concrete_with_no_concrete_subtype(ciKlass* ctxk);
void assert_unique_concrete_method(ciKlass* ctxk, ciMethod* uniqm);
void assert_abstract_with_exclusive_concrete_subtypes(ciKlass* ctxk, ciKlass* k1, ciKlass* k2);
void assert_exclusive_concrete_methods(ciKlass* ctxk, ciMethod* m1, ciMethod* m2);
void assert_has_no_finalizable_subclasses(ciKlass* ctxk);
void assert_call_site_target_value(ciCallSite* call_site, ciMethodHandle* method_handle);
static bool is_concrete_klass(Klass* k); // k is instantiable
static bool is_concrete_method(Method* m, Klass* k); // m is invocable
static Klass* find_finalizable_subclass(Klass* k);
static bool is_concrete_root_method(Method* uniqm, Klass* ctxk);
static Klass* find_witness_AME(Klass* ctxk, Method* m, KlassDepChange* changes = NULL);
static bool is_concrete_klass(ciInstanceKlass* k); // k appears instantiable
static bool has_finalizable_subclass(ciInstanceKlass* k);
static Klass* check_evol_method(Method* m);
static Klass* check_leaf_type(Klass* ctxk);
static Klass* check_abstract_with_unique_concrete_subtype(Klass* ctxk, Klass* conck,
KlassDepChange* changes = NULL);
static Klass* check_abstract_with_no_concrete_subtype(Klass* ctxk,
KlassDepChange* changes = NULL);
static Klass* check_concrete_with_no_concrete_subtype(Klass* ctxk,
KlassDepChange* changes = NULL);
static Klass* check_unique_concrete_method(Klass* ctxk, Method* uniqm,
KlassDepChange* changes = NULL);
static Klass* check_abstract_with_exclusive_concrete_subtypes(Klass* ctxk, Klass* k1, Klass* k2,
KlassDepChange* changes = NULL);
static Klass* check_exclusive_concrete_methods(Klass* ctxk, Method* m1, Method* m2,
KlassDepChange* changes = NULL);
static Klass* check_has_no_finalizable_subclasses(Klass* ctxk, KlassDepChange* changes = NULL);
static Klass* check_call_site_target_value(oop call_site, oop method_handle, CallSiteDepChange* changes = NULL);
static Klass* find_unique_concrete_subtype(Klass* ctxk);
static Method* find_unique_concrete_method(Klass* ctxk, Method* m);
static int find_exclusive_concrete_subtypes(Klass* ctxk, int klen, Klass* k[]);
void encode_content_bytes();
address content_bytes() {
assert(_content_bytes != NULL, "encode it first");
return _content_bytes;
}
size_t size_in_bytes() {
assert(_content_bytes != NULL, "encode it first");
return _size_in_bytes;
}
OopRecorder* oop_recorder() { return _oop_recorder; }
CompileLog* log() { return _log; }
void copy_to(nmethod* nm);
void log_all_dependencies();
void log_dependency(DepType dept, GrowableArray<ciBaseObject*>* args) {
ResourceMark rm;
int argslen = args->length();
write_dependency_to(log(), dept, args);
guarantee(argslen == args->length(),
"args array cannot grow inside nested ResoureMark scope");
}
void log_dependency(DepType dept,
ciBaseObject* x0,
ciBaseObject* x1 = NULL,
ciBaseObject* x2 = NULL) {
if (log() == NULL) {
return;
}
ResourceMark rm;
GrowableArray<ciBaseObject*>* ciargs =
new GrowableArray<ciBaseObject*>(dep_args(dept));
assert (x0 != NULL, "no log x0");
ciargs->push(x0);
if (x1 != NULL) {
ciargs->push(x1);
}
if (x2 != NULL) {
ciargs->push(x2);
}
assert(ciargs->length() == dep_args(dept), "");
log_dependency(dept, ciargs);
}
class DepArgument : public ResourceObj {
private:
bool _is_oop;
bool _valid;
void* _value;
public:
DepArgument() : _is_oop(false), _value(NULL), _valid(false) {}
DepArgument(oop v): _is_oop(true), _value(v), _valid(true) {}
DepArgument(Metadata* v): _is_oop(false), _value(v), _valid(true) {}
bool is_null() const { return _value == NULL; }
bool is_oop() const { return _is_oop; }
bool is_metadata() const { return !_is_oop; }
bool is_klass() const { return is_metadata() && metadata_value()->is_klass(); }
bool is_method() const { return is_metadata() && metadata_value()->is_method(); }
oop oop_value() const { assert(_is_oop && _valid, "must be"); return (oop) _value; }
Metadata* metadata_value() const { assert(!_is_oop && _valid, "must be"); return (Metadata*) _value; }
};
static void print_dependency(DepType dept,
GrowableArray<DepArgument>* args,
Klass* witness = NULL);
private:
static ciKlass* ctxk_encoded_as_null(DepType dept, ciBaseObject* x);
static Klass* ctxk_encoded_as_null(DepType dept, Metadata* x);
static void write_dependency_to(CompileLog* log,
DepType dept,
GrowableArray<ciBaseObject*>* args,
Klass* witness = NULL);
static void write_dependency_to(CompileLog* log,
DepType dept,
GrowableArray<DepArgument>* args,
Klass* witness = NULL);
static void write_dependency_to(xmlStream* xtty,
DepType dept,
GrowableArray<DepArgument>* args,
Klass* witness = NULL);
public:
class DepStream {
private:
nmethod* _code; // null if in a compiler thread
Dependencies* _deps; // null if not in a compiler thread
CompressedReadStream _bytes;
#ifdef ASSERT
size_t _byte_limit;
#endif
DepType _type;
int _xi[max_arg_count+1];
void initial_asserts(size_t byte_limit) NOT_DEBUG({});
inline Metadata* recorded_metadata_at(int i);
inline oop recorded_oop_at(int i);
Klass* check_klass_dependency(KlassDepChange* changes);
Klass* check_call_site_dependency(CallSiteDepChange* changes);
void trace_and_log_witness(Klass* witness);
public:
DepStream(Dependencies* deps)
: _deps(deps),
_code(NULL),
_bytes(deps->content_bytes())
{
initial_asserts(deps->size_in_bytes());
}
DepStream(nmethod* code)
: _deps(NULL),
_code(code),
_bytes(code->dependencies_begin())
{
initial_asserts(code->dependencies_size());
}
bool next();
DepType type() { return _type; }
int argument_count() { return dep_args(type()); }
int argument_index(int i) { assert(0 <= i && i < argument_count(), "oob");
return _xi[i]; }
Metadata* argument(int i); // => recorded_oop_at(argument_index(i))
oop argument_oop(int i); // => recorded_oop_at(argument_index(i))
Klass* context_type();
bool is_klass_type() { return Dependencies::is_klass_type(type()); }
Method* method_argument(int i) {
Metadata* x = argument(i);
assert(x->is_method(), "type");
return (Method*) x;
}
Klass* type_argument(int i) {
Metadata* x = argument(i);
assert(x->is_klass(), "type");
return (Klass*) x;
}
Klass* check_dependency() {
Klass* result = check_klass_dependency(NULL);
if (result != NULL) return result;
return check_call_site_dependency(NULL);
}
Klass* spot_check_dependency_at(DepChange& changes);
void log_dependency(Klass* witness = NULL);
void print_dependency(Klass* witness = NULL, bool verbose = false);
};
friend class Dependencies::DepStream;
static void print_statistics() PRODUCT_RETURN;
};
class DepChange : public StackObj {
public:
virtual bool is_klass_change() const { return false; }
virtual bool is_call_site_change() const { return false; }
KlassDepChange* as_klass_change() {
assert(is_klass_change(), "bad cast");
return (KlassDepChange*) this;
}
CallSiteDepChange* as_call_site_change() {
assert(is_call_site_change(), "bad cast");
return (CallSiteDepChange*) this;
}
void print();
public:
enum ChangeType {
NO_CHANGE = 0, // an uninvolved klass
Change_new_type, // a newly loaded type
Change_new_sub, // a super with a new subtype
Change_new_impl, // an interface with a new implementation
CHANGE_LIMIT,
Start_Klass = CHANGE_LIMIT // internal indicator for ContextStream
};
class ContextStream : public StackObj {
private:
DepChange& _changes;
friend class DepChange;
ChangeType _change_type;
Klass* _klass;
Array<Klass*>* _ti_base; // i.e., transitive_interfaces
int _ti_index;
int _ti_limit;
void start();
public:
ContextStream(DepChange& changes)
: _changes(changes)
{ start(); }
ContextStream(DepChange& changes, No_Safepoint_Verifier& nsv)
: _changes(changes)
{ start(); }
bool next();
ChangeType change_type() { return _change_type; }
Klass* klass() { return _klass; }
};
friend class DepChange::ContextStream;
};
class KlassDepChange : public DepChange {
private:
KlassHandle _new_type;
void initialize();
public:
KlassDepChange(KlassHandle new_type)
: _new_type(new_type)
{
initialize();
}
~KlassDepChange();
virtual bool is_klass_change() const { return true; }
Klass* new_type() { return _new_type(); }
bool involves_context(Klass* k);
};
class CallSiteDepChange : public DepChange {
private:
Handle _call_site;
Handle _method_handle;
public:
CallSiteDepChange(Handle call_site, Handle method_handle)
: _call_site(call_site),
_method_handle(method_handle)
{
assert(_call_site() ->is_a(SystemDictionary::CallSite_klass()), "must be");
assert(_method_handle()->is_a(SystemDictionary::MethodHandle_klass()), "must be");
}
virtual bool is_call_site_change() const { return true; }
oop call_site() const { return _call_site(); }
oop method_handle() const { return _method_handle(); }
};
#endif // SHARE_VM_CODE_DEPENDENCIES_HPP
C:\hotspot-69087d08d473\src\share\vm/code/exceptionHandlerTable.cpp
#include "precompiled.hpp"
#include "code/exceptionHandlerTable.hpp"
#include "code/nmethod.hpp"
#include "memory/allocation.inline.hpp"
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
void ExceptionHandlerTable::add_entry(HandlerTableEntry entry) {
_nesting.check();
if (_length >= _size) {
guarantee(_size > 0, "no space allocated => cannot grow the table since it is part of nmethod");
int new_size = _size * 2;
_table = REALLOC_RESOURCE_ARRAY(HandlerTableEntry, _table, _size, new_size);
_size = new_size;
}
assert(_length < _size, "sanity check");
_table[_length++] = entry;
}
HandlerTableEntry* ExceptionHandlerTable::subtable_for(int catch_pco) const {
int i = 0;
while (i < _length) {
HandlerTableEntry* t = _table + i;
if (t->pco() == catch_pco) {
return t;
} else {
i += t->len() + 1; // +1 for header
}
}
return NULL;
}
ExceptionHandlerTable::ExceptionHandlerTable(int initial_size) {
guarantee(initial_size > 0, "initial size must be > 0");
_table = NEW_RESOURCE_ARRAY(HandlerTableEntry, initial_size);
_length = 0;
_size = initial_size;
}
ExceptionHandlerTable::ExceptionHandlerTable(const nmethod* nm) {
_table = (HandlerTableEntry*)nm->handler_table_begin();
_length = nm->handler_table_size() / sizeof(HandlerTableEntry);
_size = 0; // no space allocated by ExeptionHandlerTable!
}
void ExceptionHandlerTable::add_subtable(
int catch_pco,
GrowableArray<intptr_t>* handler_bcis,
GrowableArray<intptr_t>* scope_depths_from_top_scope,
GrowableArray<intptr_t>* handler_pcos
) {
assert(subtable_for(catch_pco) == NULL, "catch handlers for this catch_pco added twice");
assert(handler_bcis->length() == handler_pcos->length(), "bci & pc table have different length");
assert(scope_depths_from_top_scope == NULL || handler_bcis->length() == scope_depths_from_top_scope->length(), "bci & scope_depths table have different length");
if (handler_bcis->length() > 0) {
add_entry(HandlerTableEntry(handler_bcis->length(), catch_pco, 0));
for (int i = 0; i < handler_bcis->length(); i++) {
intptr_t scope_depth = 0;
if (scope_depths_from_top_scope != NULL) {
scope_depth = scope_depths_from_top_scope->at(i);
}
add_entry(HandlerTableEntry(handler_bcis->at(i), handler_pcos->at(i), scope_depth));
assert(entry_for(catch_pco, handler_bcis->at(i), scope_depth)->pco() == handler_pcos->at(i), "entry not added correctly (1)");
assert(entry_for(catch_pco, handler_bcis->at(i), scope_depth)->scope_depth() == scope_depth, "entry not added correctly (2)");
}
}
}
void ExceptionHandlerTable::copy_to(nmethod* nm) {
assert(size_in_bytes() == nm->handler_table_size(), "size of space allocated in nmethod incorrect");
memmove(nm->handler_table_begin(), _table, size_in_bytes());
}
HandlerTableEntry* ExceptionHandlerTable::entry_for(int catch_pco, int handler_bci, int scope_depth) const {
HandlerTableEntry* t = subtable_for(catch_pco);
if (t != NULL) {
int l = t->len();
while (l-- > 0) {
t++;
if (t->bci() == handler_bci && t->scope_depth() == scope_depth) return t;
}
}
return NULL;
}
void ExceptionHandlerTable::print_subtable(HandlerTableEntry* t) const {
int l = t->len();
tty->print_cr("catch_pco = %d (%d entries)", t->pco(), l);
while (l-- > 0) {
t++;
tty->print_cr(" bci %d at scope depth %d -> pco %d", t->bci(), t->scope_depth(), t->pco());
}
}
void ExceptionHandlerTable::print() const {
tty->print_cr("ExceptionHandlerTable (size = %d bytes)", size_in_bytes());
int i = 0;
while (i < _length) {
HandlerTableEntry* t = _table + i;
print_subtable(t);
i += t->len() + 1; // +1 for header
}
}
void ExceptionHandlerTable::print_subtable_for(int catch_pco) const {
HandlerTableEntry* subtable = subtable_for(catch_pco);
if( subtable != NULL ) { print_subtable( subtable ); }
}
void ImplicitExceptionTable::set_size( uint size ) {
_size = size;
_data = NEW_RESOURCE_ARRAY(implicit_null_entry, (size*2));
_len = 0;
}
void ImplicitExceptionTable::append( uint exec_off, uint cont_off ) {
assert( (sizeof(implicit_null_entry) >= 4) || (exec_off < 65535), "" );
assert( (sizeof(implicit_null_entry) >= 4) || (cont_off < 65535), "" );
uint l = len();
if (l == _size) {
uint old_size_in_elements = _size*2;
if (_size == 0) _size = 4;
_size *= 2;
uint new_size_in_elements = _size*2;
_data = REALLOC_RESOURCE_ARRAY(uint, _data, old_size_in_elements, new_size_in_elements);
}
_len = l+1;
};
uint ImplicitExceptionTable::at( uint exec_off ) const {
uint l = len();
for( uint i=0; i<l; i++ )
if( *adr(i) == exec_off )
return *(adr(i)+1);
return 0; // Failed to find any execption offset
}
void ImplicitExceptionTable::print(address base) const {
tty->print("{");
for( uint i=0; i<len(); i++ )
tty->print("< " INTPTR_FORMAT ", " INTPTR_FORMAT " > ",base + *adr(i), base + *(adr(i)+1));
tty->print_cr("}");
}
ImplicitExceptionTable::ImplicitExceptionTable(const nmethod* nm) {
if (nm->nul_chk_table_size() == 0) {
_len = 0;
_data = NULL;
} else {
_data = (implicit_null_entry*)nm->nul_chk_table_begin();
_len = _data[0];
_data++;
}
_size = len();
assert(size_in_bytes() <= nm->nul_chk_table_size(), "size of space allocated in nmethod incorrect");
}
void ImplicitExceptionTable::copy_to( nmethod* nm ) {
assert(size_in_bytes() <= nm->nul_chk_table_size(), "size of space allocated in nmethod incorrect");
if (len() != 0) {
implicit_null_entry* nmdata = (implicit_null_entry*)nm->nul_chk_table_begin();
nmdata[0] = _len;
nmdata++;
memmove( nmdata, _data, 2 * len() * sizeof(implicit_null_entry));
} else {
assert(size_in_bytes() == 0, "bad size");
assert(nm->nul_chk_table_size() == 0, "bad size");
}
}
void ImplicitExceptionTable::verify(nmethod *nm) const {
for (uint i = 0; i < len(); i++) {
if ((*adr(i) > (unsigned int)nm->insts_size()) ||
(*(adr(i)+1) > (unsigned int)nm->insts_size()))
fatal(err_msg("Invalid offset in ImplicitExceptionTable at " PTR_FORMAT, _data));
}
}
C:\hotspot-69087d08d473\src\share\vm/code/exceptionHandlerTable.hpp
#ifndef SHARE_VM_CODE_EXCEPTIONHANDLERTABLE_HPP
#define SHARE_VM_CODE_EXCEPTIONHANDLERTABLE_HPP
#include "memory/allocation.hpp"
#include "oops/method.hpp"
class HandlerTableEntry {
private:
int _bci;
int _pco;
int _scope_depth;
public:
HandlerTableEntry(int bci, int pco, int scope_depth) {
assert( 0 <= pco, "pco must be positive");
assert( 0 <= scope_depth, "scope_depth must be positive");
_bci = bci;
_pco = pco;
_scope_depth = scope_depth;
}
int len() const { return _bci; } // for entry at subtable begin
int bci() const { return _bci; }
int pco() const { return _pco; }
int scope_depth() const { return _scope_depth; }
};
class nmethod;
class ExceptionHandlerTable VALUE_OBJ_CLASS_SPEC {
private:
HandlerTableEntry* _table; // the table
int _length; // the current length of the table
int _size; // the number of allocated entries
ReallocMark _nesting; // assertion check for reallocations
void add_entry(HandlerTableEntry entry);
HandlerTableEntry* subtable_for(int catch_pco) const;
public:
ExceptionHandlerTable(int initial_size = 8);
ExceptionHandlerTable(const nmethod* nm);
void add_subtable(
int catch_pco, // the pc offset for the CatchNode
GrowableArray<intptr_t>* handler_bcis, // the exception handler entry point bcis
GrowableArray<intptr_t>* scope_depths_from_top_scope,
GrowableArray<intptr_t>* handler_pcos // pc offsets for the compiled handlers
);
int size_in_bytes() const { return round_to(_length * sizeof(HandlerTableEntry), oopSize); }
void copy_to(nmethod* nm);
HandlerTableEntry* entry_for(int catch_pco, int handler_bci, int scope_depth) const;
void print_subtable(HandlerTableEntry* t) const;
void print() const;
void print_subtable_for(int catch_pco) const;
};
typedef uint implicit_null_entry;
class ImplicitExceptionTable VALUE_OBJ_CLASS_SPEC {
uint _size;
uint _len;
implicit_null_entry *_data;
implicit_null_entry *adr( uint idx ) const { return &_data[2*idx]; }
ReallocMark _nesting; // assertion check for reallocations
public:
ImplicitExceptionTable( ) : _data(0), _size(0), _len(0) { }
ImplicitExceptionTable( const nmethod *nm );
void set_size( uint size );
void append( uint exec_off, uint cont_off );
uint at( uint exec_off ) const;
uint len() const { return _len; }
int size_in_bytes() const { return len() == 0 ? 0 : ((2 * len() + 1) * sizeof(implicit_null_entry)); }
void copy_to(nmethod* nm);
void print(address base) const;
void verify(nmethod *nm) const;
};
#endif // SHARE_VM_CODE_EXCEPTIONHANDLERTABLE_HPP
C:\hotspot-69087d08d473\src\share\vm/code/icBuffer.cpp
#include "precompiled.hpp"
#include "code/codeCache.hpp"
#include "code/compiledIC.hpp"
#include "code/icBuffer.hpp"
#include "code/nmethod.hpp"
#include "code/scopeDesc.hpp"
#include "gc_interface/collectedHeap.inline.hpp"
#include "interpreter/interpreter.hpp"
#include "interpreter/linkResolver.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.inline.hpp"
#include "oops/method.hpp"
#include "oops/oop.inline.hpp"
#include "oops/oop.inline2.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/stubRoutines.hpp"
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
DEF_STUB_INTERFACE(ICStub);
StubQueue* InlineCacheBuffer::_buffer = NULL;
ICStub* InlineCacheBuffer::_next_stub = NULL;
CompiledICHolder* InlineCacheBuffer::_pending_released = NULL;
int InlineCacheBuffer::_pending_count = 0;
void ICStub::finalize() {
if (!is_empty()) {
ResourceMark rm;
CompiledIC *ic = CompiledIC_at(CodeCache::find_nmethod(ic_site()), ic_site());
assert(CodeCache::find_nmethod(ic->instruction_address()) != NULL, "inline cache in non-nmethod?");
assert(this == ICStub_from_destination_address(ic->stub_address()), "wrong owner of ic buffer");
ic->set_ic_destination_and_value(destination(), cached_value());
}
}
address ICStub::destination() const {
return InlineCacheBuffer::ic_buffer_entry_point(code_begin());
}
void* ICStub::cached_value() const {
return InlineCacheBuffer::ic_buffer_cached_value(code_begin());
}
void ICStub::set_stub(CompiledIC *ic, void* cached_val, address dest_addr) {
_ic_site = ic->instruction_address();
InlineCacheBuffer::assemble_ic_buffer_code(code_begin(), cached_val, dest_addr);
assert(destination() == dest_addr, "can recover destination");
assert(cached_value() == cached_val, "can recover destination");
}
void ICStub::clear() {
if (CompiledIC::is_icholder_entry(destination())) {
InlineCacheBuffer::queue_for_release((CompiledICHolder*)cached_value());
}
_ic_site = NULL;
}
#ifndef PRODUCT
void ICStub::verify() {
}
void ICStub::print() {
tty->print_cr("ICStub: site: " INTPTR_FORMAT, _ic_site);
}
#endif
void InlineCacheBuffer::init_next_stub() {
ICStub* ic_stub = (ICStub*)buffer()->request_committed (ic_stub_code_size());
assert (ic_stub != NULL, "no room for a single stub");
set_next_stub(ic_stub);
}
void InlineCacheBuffer::initialize() {
if (_buffer != NULL) return; // already initialized
_buffer = new StubQueue(new ICStubInterface, 10*K, InlineCacheBuffer_lock, "InlineCacheBuffer");
assert (_buffer != NULL, "cannot allocate InlineCacheBuffer");
init_next_stub();
}
ICStub* InlineCacheBuffer::new_ic_stub() {
while (true) {
ICStub* ic_stub = (ICStub*)buffer()->request_committed(ic_stub_code_size());
if (ic_stub != NULL) {
return ic_stub;
}
EXCEPTION_MARK;
VM_ForceSafepoint vfs;
VMThread::execute(&vfs);
if (HAS_PENDING_EXCEPTION) {
oop exception = PENDING_EXCEPTION;
CLEAR_PENDING_EXCEPTION;
Thread::send_async_exception(JavaThread::current()->threadObj(), exception);
}
}
ShouldNotReachHere();
return NULL;
}
void InlineCacheBuffer::update_inline_caches() {
if (buffer()->number_of_stubs() > 1) {
if (TraceICBuffer) {
tty->print_cr("[updating inline caches with %d stubs]", buffer()->number_of_stubs());
}
buffer()->remove_all();
init_next_stub();
}
release_pending_icholders();
}
bool InlineCacheBuffer::contains(address instruction_address) {
return buffer()->contains(instruction_address);
}
bool InlineCacheBuffer::is_empty() {
return buffer()->number_of_stubs() == 1; // always has sentinel
}
void InlineCacheBuffer_init() {
InlineCacheBuffer::initialize();
}
void InlineCacheBuffer::create_transition_stub(CompiledIC *ic, void* cached_value, address entry) {
assert(!SafepointSynchronize::is_at_safepoint(), "should not be called during a safepoint");
assert (CompiledIC_lock->is_locked(), "");
if (TraceICBuffer) {
tty->print_cr(" create transition stub for " INTPTR_FORMAT " destination " INTPTR_FORMAT " cached value " INTPTR_FORMAT,
ic->instruction_address(), entry, cached_value);
}
if (ic->is_in_transition_state()) {
ICStub* old_stub = ICStub_from_destination_address(ic->stub_address());
old_stub->clear();
}
ICStub* ic_stub = get_next_stub();
ic_stub->set_stub(ic, cached_value, entry);
ic->set_ic_destination(ic_stub);
set_next_stub(new_ic_stub()); // can cause safepoint synchronization
}
address InlineCacheBuffer::ic_destination_for(CompiledIC *ic) {
ICStub* stub = ICStub_from_destination_address(ic->stub_address());
return stub->destination();
}
void* InlineCacheBuffer::cached_value_for(CompiledIC *ic) {
ICStub* stub = ICStub_from_destination_address(ic->stub_address());
return stub->cached_value();
}
void InlineCacheBuffer::release_pending_icholders() {
assert(SafepointSynchronize::is_at_safepoint(), "should only be called during a safepoint");
CompiledICHolder* holder = _pending_released;
_pending_released = NULL;
while (holder != NULL) {
CompiledICHolder* next = holder->next();
delete holder;
holder = next;
_pending_count--;
}
assert(_pending_count == 0, "wrong count");
}
void InlineCacheBuffer::queue_for_release(CompiledICHolder* icholder) {
MutexLockerEx mex(InlineCacheBuffer_lock);
icholder->set_next(_pending_released);
_pending_released = icholder;
_pending_count++;
if (TraceICBuffer) {
tty->print_cr("enqueueing icholder " INTPTR_FORMAT " to be freed", icholder);
}
}
C:\hotspot-69087d08d473\src\share\vm/code/icBuffer.hpp
#ifndef SHARE_VM_CODE_ICBUFFER_HPP
#define SHARE_VM_CODE_ICBUFFER_HPP
#include "asm/codeBuffer.hpp"
#include "code/stubs.hpp"
#include "interpreter/bytecodes.hpp"
#include "memory/allocation.hpp"
class ICStub: public Stub {
private:
int _size; // total size of the stub incl. code
address _ic_site; // points at call instruction of owning ic-buffer
protected:
friend class ICStubInterface;
void initialize(int size,
CodeStrings strings) { _size = size; _ic_site = NULL; }
void finalize(); // called when a method is removed
int size() const { return _size; }
static int code_size_to_size(int code_size) { return round_to(sizeof(ICStub), CodeEntryAlignment) + code_size; }
public:
void set_stub(CompiledIC *ic, void* cached_value, address dest_addr);
address code_begin() const { return (address)this + round_to(sizeof(ICStub), CodeEntryAlignment); }
address code_end() const { return (address)this + size(); }
address ic_site() const { return _ic_site; }
void clear();
bool is_empty() const { return _ic_site == NULL; }
address destination() const; // destination of jump instruction
void* cached_value() const; // cached_value for stub
void verify() PRODUCT_RETURN;
void print() PRODUCT_RETURN;
friend ICStub* ICStub_from_destination_address(address destination_address);
};
inline ICStub* ICStub_from_destination_address(address destination_address) {
ICStub* stub = (ICStub*) (destination_address - round_to(sizeof(ICStub), CodeEntryAlignment));
#ifdef ASSERT
stub->verify();
#endif
return stub;
}
class InlineCacheBuffer: public AllStatic {
private:
friend class ICStub;
static int ic_stub_code_size();
static StubQueue* _buffer;
static ICStub* _next_stub;
static CompiledICHolder* _pending_released;
static int _pending_count;
static StubQueue* buffer() { return _buffer; }
static void set_next_stub(ICStub* next_stub) { _next_stub = next_stub; }
static ICStub* get_next_stub() { return _next_stub; }
static void init_next_stub();
static ICStub* new_ic_stub();
static void assemble_ic_buffer_code(address code_begin, void* cached_value, address entry_point);
static address ic_buffer_entry_point (address code_begin);
static void* ic_buffer_cached_value (address code_begin);
public:
static void initialize();
static bool contains(address instruction_address);
static void update_inline_caches();
static bool is_empty();
static void release_pending_icholders();
static void queue_for_release(CompiledICHolder* icholder);
static int pending_icholder_count() { return _pending_count; }
static void create_transition_stub(CompiledIC *ic, void* cached_value, address entry);
static address ic_destination_for(CompiledIC *ic);
static void* cached_value_for(CompiledIC *ic);
};
#endif // SHARE_VM_CODE_ICBUFFER_HPP
C:\hotspot-69087d08d473\src\share\vm/code/jvmticmlr.h
#ifndef _JVMTI_CMLR_H_
#define _JVMTI_CMLR_H_
enum {
JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001,
JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000,
JVMTI_CMLR_MAJOR_VERSION = 0x00000001,
JVMTI_CMLR_MINOR_VERSION = 0x00000000
};
typedef enum {
JVMTI_CMLR_DUMMY = 1,
JVMTI_CMLR_INLINE_INFO = 2
} jvmtiCMLRKind;
typedef struct _jvmtiCompiledMethodLoadRecordHeader {
jvmtiCMLRKind kind; /* id for the kind of info passed in the record */
jint majorinfoversion; /* major and minor info version values. Init'ed */
jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */
struct _jvmtiCompiledMethodLoadRecordHeader* next;
} jvmtiCompiledMethodLoadRecordHeader;
typedef struct _PCStackInfo {
void* pc; /* the pc address for this compiled method */
jint numstackframes; /* number of methods on the stack */
jmethodID* methods; /* array of numstackframes method ids */
jint* bcis; /* array of numstackframes bytecode indices */
} PCStackInfo;
typedef struct _jvmtiCompiledMethodLoadInlineRecord {
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
jint numpcs; /* number of pc descriptors in this nmethod */
PCStackInfo* pcinfo; /* array of numpcs pc descriptors */
} jvmtiCompiledMethodLoadInlineRecord;
typedef struct _jvmtiCompiledMethodLoadDummyRecord {
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
char message[50];
} jvmtiCompiledMethodLoadDummyRecord;
#endif
C:\hotspot-69087d08d473\src\share\vm/code/location.cpp
#include "precompiled.hpp"
#include "code/debugInfo.hpp"
#include "code/location.hpp"
void Location::print_on(outputStream* st) const {
if(type() == invalid) {
switch (where()) {
case on_stack: st->print("empty"); break;
case in_register: st->print("invalid"); break;
}
return;
}
switch (where()) {
case on_stack: st->print("stack[%d]", stack_offset()); break;
case in_register: st->print("reg %s [%d]", reg()->name(), register_number()); break;
default: st->print("Wrong location where %d", where());
}
switch (type()) {
case normal: break;
case oop: st->print(",oop"); break;
case narrowoop: st->print(",narrowoop"); break;
case int_in_long: st->print(",int"); break;
case lng: st->print(",long"); break;
case float_in_dbl: st->print(",float"); break;
case dbl: st->print(",double"); break;
case addr: st->print(",address"); break;
default: st->print("Wrong location type %d", type());
}
}
Location::Location(DebugInfoReadStream* stream) {
_value = (juint) stream->read_int();
}
void Location::write_on(DebugInfoWriteStream* stream) {
stream->write_int(_value);
}
bool Location::legal_offset_in_bytes(int offset_in_bytes) {
if ((offset_in_bytes % BytesPerInt) != 0) return false;
return (juint)(offset_in_bytes / BytesPerInt) < (OFFSET_MASK >> OFFSET_SHIFT);
}
C:\hotspot-69087d08d473\src\share\vm/code/location.hpp
#ifndef SHARE_VM_CODE_LOCATION_HPP
#define SHARE_VM_CODE_LOCATION_HPP
#include "asm/assembler.hpp"
#include "code/vmreg.hpp"
#include "memory/allocation.hpp"
class Location VALUE_OBJ_CLASS_SPEC {
friend class VMStructs;
public:
enum Where {
on_stack,
in_register
};
enum Type {
invalid, // Invalid location
normal, // Ints, floats, double halves
oop, // Oop (please GC me!)
int_in_long, // Integer held in long register
lng, // Long held in one register
float_in_dbl, // Float held in double register
dbl, // Double held in one register
addr, // JSR return address
narrowoop // Narrow Oop (please GC me!)
};
private:
enum {
TYPE_MASK = (juint) 0x0F,
TYPE_SHIFT = 0,
WHERE_MASK = (juint) 0x10,
WHERE_SHIFT = 4,
OFFSET_MASK = (juint) 0xFFFFFFE0,
OFFSET_SHIFT = 5
};
juint _value;
Location(Where where_, Type type_, unsigned offset_) {
set(where_, type_, offset_);
assert( where () == where_ , "" );
assert( type () == type_ , "" );
assert( offset() == offset_, "" );
}
inline void set(Where where_, Type type_, unsigned offset_) {
_value = (juint) ((where_ << WHERE_SHIFT) |
(type_ << TYPE_SHIFT) |
((offset_ << OFFSET_SHIFT) & OFFSET_MASK));
}
public:
static Location new_stk_loc( Type t, int offset ) { return Location(on_stack,t,offset>>LogBytesPerInt); }
static Location new_reg_loc( Type t, VMReg reg ) { return Location(in_register, t, reg->value()); }
Location() { set(on_stack,invalid,0); }
Where where() const { return (Where) ((_value & WHERE_MASK) >> WHERE_SHIFT);}
Type type() const { return (Type) ((_value & TYPE_MASK) >> TYPE_SHIFT); }
unsigned offset() const { return (unsigned) ((_value & OFFSET_MASK) >> OFFSET_SHIFT); }
bool is_register() const { return where() == in_register; }
bool is_stack() const { return where() == on_stack; }
int stack_offset() const { assert(where() == on_stack, "wrong Where"); return offset()<<LogBytesPerInt; }
int register_number() const { assert(where() == in_register, "wrong Where"); return offset() ; }
VMReg reg() const { assert(where() == in_register, "wrong Where"); return VMRegImpl::as_VMReg(offset()) ; }
void print_on(outputStream* st) const;
Location(DebugInfoReadStream* stream);
void write_on(DebugInfoWriteStream* stream);
static bool legal_offset_in_bytes(int offset_in_bytes);
};
#endif // SHARE_VM_CODE_LOCATION_HPP
C:\hotspot-69087d08d473\src\share\vm/code/nmethod.cpp
#include "precompiled.hpp"
#include "code/codeCache.hpp"
#include "code/compiledIC.hpp"
#include "code/dependencies.hpp"
#include "code/nmethod.hpp"
#include "code/scopeDesc.hpp"
#include "compiler/abstractCompiler.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
#include "compiler/compilerOracle.hpp"
#include "compiler/disassembler.hpp"
#include "interpreter/bytecode.hpp"
#include "oops/methodData.hpp"
#include "prims/jvmtiRedefineClassesTrace.hpp"
#include "prims/jvmtiImpl.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/sweeper.hpp"
#include "utilities/dtrace.hpp"
#include "utilities/events.hpp"
#include "utilities/xmlstream.hpp"
#ifdef SHARK
#include "shark/sharkCompiler.hpp"
#endif
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
unsigned char nmethod::_global_unloading_clock = 0;
#ifdef DTRACE_ENABLED
#ifndef USDT2
HS_DTRACE_PROBE_DECL8(hotspot, compiled__method__load,
const char*, int, const char*, int, const char*, int, void*, size_t);
HS_DTRACE_PROBE_DECL6(hotspot, compiled__method__unload,
char*, int, char*, int, char*, int);
#define DTRACE_METHOD_UNLOAD_PROBE(method) \
{ \
Method* m = (method); \
if (m != NULL) { \
Symbol* klass_name = m->klass_name(); \
Symbol* name = m->name(); \
Symbol* signature = m->signature(); \
HS_DTRACE_PROBE6(hotspot, compiled__method__unload, \
klass_name->bytes(), klass_name->utf8_length(), \
name->bytes(), name->utf8_length(), \
signature->bytes(), signature->utf8_length()); \
} \
}
#else /* USDT2 */
#define DTRACE_METHOD_UNLOAD_PROBE(method) \
{ \
Method* m = (method); \
if (m != NULL) { \
Symbol* klass_name = m->klass_name(); \
Symbol* name = m->name(); \
Symbol* signature = m->signature(); \
HOTSPOT_COMPILED_METHOD_UNLOAD( \
(char *) klass_name->bytes(), klass_name->utf8_length(), \
(char *) name->bytes(), name->utf8_length(), \
(char *) signature->bytes(), signature->utf8_length()); \
} \
}
#endif /* USDT2 */
#else // ndef DTRACE_ENABLED
#define DTRACE_METHOD_UNLOAD_PROBE(method)
#endif
bool nmethod::is_compiled_by_c1() const {
if (compiler() == NULL) {
return false;
}
return compiler()->is_c1();
}
bool nmethod::is_compiled_by_c2() const {
if (compiler() == NULL) {
return false;
}
return compiler()->is_c2();
}
bool nmethod::is_compiled_by_shark() const {
if (compiler() == NULL) {
return false;
}
return compiler()->is_shark();
}
#ifndef PRODUCT
static
struct nmethod_stats_struct {
int nmethod_count;
int total_size;
int relocation_size;
int consts_size;
int insts_size;
int stub_size;
int scopes_data_size;
int scopes_pcs_size;
int dependencies_size;
int handler_table_size;
int nul_chk_table_size;
int oops_size;
void note_nmethod(nmethod* nm) {
nmethod_count += 1;
total_size += nm->size();
relocation_size += nm->relocation_size();
consts_size += nm->consts_size();
insts_size += nm->insts_size();
stub_size += nm->stub_size();
oops_size += nm->oops_size();
scopes_data_size += nm->scopes_data_size();
scopes_pcs_size += nm->scopes_pcs_size();
dependencies_size += nm->dependencies_size();
handler_table_size += nm->handler_table_size();
nul_chk_table_size += nm->nul_chk_table_size();
}
void print_nmethod_stats() {
if (nmethod_count == 0) return;
tty->print_cr("Statistics for %d bytecoded nmethods:", nmethod_count);
if (total_size != 0) tty->print_cr(" total in heap = %d", total_size);
if (relocation_size != 0) tty->print_cr(" relocation = %d", relocation_size);
if (consts_size != 0) tty->print_cr(" constants = %d", consts_size);
if (insts_size != 0) tty->print_cr(" main code = %d", insts_size);
if (stub_size != 0) tty->print_cr(" stub code = %d", stub_size);
if (oops_size != 0) tty->print_cr(" oops = %d", oops_size);
if (scopes_data_size != 0) tty->print_cr(" scopes data = %d", scopes_data_size);
if (scopes_pcs_size != 0) tty->print_cr(" scopes pcs = %d", scopes_pcs_size);
if (dependencies_size != 0) tty->print_cr(" dependencies = %d", dependencies_size);
if (handler_table_size != 0) tty->print_cr(" handler table = %d", handler_table_size);
if (nul_chk_table_size != 0) tty->print_cr(" nul chk table = %d", nul_chk_table_size);
}
int native_nmethod_count;
int native_total_size;
int native_relocation_size;
int native_insts_size;
int native_oops_size;
void note_native_nmethod(nmethod* nm) {
native_nmethod_count += 1;
native_total_size += nm->size();
native_relocation_size += nm->relocation_size();
native_insts_size += nm->insts_size();
native_oops_size += nm->oops_size();
}
void print_native_nmethod_stats() {
if (native_nmethod_count == 0) return;
tty->print_cr("Statistics for %d native nmethods:", native_nmethod_count);
if (native_total_size != 0) tty->print_cr(" N. total size = %d", native_total_size);
if (native_relocation_size != 0) tty->print_cr(" N. relocation = %d", native_relocation_size);
if (native_insts_size != 0) tty->print_cr(" N. main code = %d", native_insts_size);
if (native_oops_size != 0) tty->print_cr(" N. oops = %d", native_oops_size);
}
int pc_desc_resets; // number of resets (= number of caches)
int pc_desc_queries; // queries to nmethod::find_pc_desc
int pc_desc_approx; // number of those which have approximate true
int pc_desc_repeats; // number of _pc_descs[0] hits
int pc_desc_hits; // number of LRU cache hits
int pc_desc_tests; // total number of PcDesc examinations
int pc_desc_searches; // total number of quasi-binary search steps
int pc_desc_adds; // number of LUR cache insertions
void print_pc_stats() {
tty->print_cr("PcDesc Statistics: %d queries, %.2f comparisons per query",
pc_desc_queries,
(double)(pc_desc_tests + pc_desc_searches)
/ pc_desc_queries);
tty->print_cr(" caches=%d queries=%d/%d, hits=%d+%d, tests=%d+%d, adds=%d",
pc_desc_resets,
pc_desc_queries, pc_desc_approx,
pc_desc_repeats, pc_desc_hits,
pc_desc_tests, pc_desc_searches, pc_desc_adds);
}
} nmethod_stats;
#endif //PRODUCT
ExceptionCache::ExceptionCache(Handle exception, address pc, address handler) {
assert(pc != NULL, "Must be non null");
assert(exception.not_null(), "Must be non null");
assert(handler != NULL, "Must be non null");
_count = 0;
_exception_type = exception->klass();
_next = NULL;
add_address_and_handler(pc,handler);
}
address ExceptionCache::match(Handle exception, address pc) {
assert(pc != NULL,"Must be non null");
assert(exception.not_null(),"Must be non null");
if (exception->klass() == exception_type()) {
return (test_address(pc));
}
return NULL;
}
bool ExceptionCache::match_exception_with_space(Handle exception) {
assert(exception.not_null(),"Must be non null");
if (exception->klass() == exception_type() && count() < cache_size) {
return true;
}
return false;
}
address ExceptionCache::test_address(address addr) {
int limit = count();
for (int i = 0; i < limit; i++) {
if (pc_at(i) == addr) {
return handler_at(i);
}
}
return NULL;
}
bool ExceptionCache::add_address_and_handler(address addr, address handler) {
if (test_address(addr) == handler) return true;
int index = count();
if (index < cache_size) {
set_pc_at(index, addr);
set_handler_at(index, handler);
increment_count();
return true;
}
return false;
}
ExceptionCache* nmethod::exception_cache_entry_for_exception(Handle exception) {
ExceptionCache* ec = exception_cache();
while (ec != NULL) {
if (ec->match_exception_with_space(exception)) {
return ec;
}
ec = ec->next();
}
return NULL;
}
static inline bool match_desc(PcDesc* pc, int pc_offset, bool approximate) {
NOT_PRODUCT(++nmethod_stats.pc_desc_tests);
if (!approximate)
return pc->pc_offset() == pc_offset;
else
return (pc-1)->pc_offset() < pc_offset && pc_offset <= pc->pc_offset();
}
void PcDescCache::reset_to(PcDesc* initial_pc_desc) {
if (initial_pc_desc == NULL) {
_pc_descs[0] = NULL; // native method; no PcDescs at all
return;
}
NOT_PRODUCT(++nmethod_stats.pc_desc_resets);
assert(initial_pc_desc->pc_offset() < 0, "must be sentinel");
for (int i = 0; i < cache_size; i++)
_pc_descs[i] = initial_pc_desc;
}
PcDesc* PcDescCache::find_pc_desc(int pc_offset, bool approximate) {
NOT_PRODUCT(++nmethod_stats.pc_desc_queries);
NOT_PRODUCT(if (approximate) ++nmethod_stats.pc_desc_approx);
PcDesc* res;
res = _pc_descs[0];
if (res == NULL) return NULL; // native method; no PcDescs at all
if (match_desc(res, pc_offset, approximate)) {
NOT_PRODUCT(++nmethod_stats.pc_desc_repeats);
return res;
}
for (int i = 1; i < cache_size; ++i) {
res = _pc_descs[i];
if (res->pc_offset() < 0) break; // optimization: skip empty cache
if (match_desc(res, pc_offset, approximate)) {
NOT_PRODUCT(++nmethod_stats.pc_desc_hits);
return res;
}
}
return NULL;
}
void PcDescCache::add_pc_desc(PcDesc* pc_desc) {
NOT_PRODUCT(++nmethod_stats.pc_desc_adds);
for (int i = 0; i < cache_size; i++) {
PcDesc* next = _pc_descs[i];
_pc_descs[i] = pc_desc;
pc_desc = next;
}
}
static int adjust_pcs_size(int pcs_size) {
int nsize = round_to(pcs_size, oopSize);
if ((nsize % sizeof(PcDesc)) != 0) {
nsize = pcs_size + sizeof(PcDesc);
}
assert((nsize % oopSize) == 0, "correct alignment");
return nsize;
}
void nmethod::add_exception_cache_entry(ExceptionCache* new_entry) {
assert(ExceptionCache_lock->owned_by_self(),"Must hold the ExceptionCache_lock");
assert(new_entry != NULL,"Must be non null");
assert(new_entry->next() == NULL, "Must be null");
ExceptionCache *ec = exception_cache();
if (ec != NULL) {
new_entry->set_next(ec);
}
release_set_exception_cache(new_entry);
}
void nmethod::clean_exception_cache(BoolObjectClosure* is_alive) {
ExceptionCache* prev = NULL;
ExceptionCache* curr = exception_cache();
while (curr != NULL) {
ExceptionCache* next = curr->next();
Klass* ex_klass = curr->exception_type();
if (ex_klass != NULL && !ex_klass->is_loader_alive(is_alive)) {
if (prev == NULL) {
set_exception_cache(next);
} else {
prev->set_next(next);
}
delete curr;
} else {
prev = curr;
}
curr = next;
}
}
address nmethod::handler_for_exception_and_pc(Handle exception, address pc) {
ExceptionCache* ec = exception_cache();
while (ec != NULL) {
address ret_val;
if ((ret_val = ec->match(exception,pc)) != NULL) {
return ret_val;
}
ec = ec->next();
}
return NULL;
}
void nmethod::add_handler_for_exception_and_pc(Handle exception, address pc, address handler) {
MutexLocker ml(ExceptionCache_lock);
ExceptionCache* target_entry = exception_cache_entry_for_exception(exception);
if (target_entry == NULL || !target_entry->add_address_and_handler(pc,handler)) {
target_entry = new ExceptionCache(exception,pc,handler);
add_exception_cache_entry(target_entry);
}
}
int nmethod::total_size() const {
return
consts_size() +
insts_size() +
stub_size() +
scopes_data_size() +
scopes_pcs_size() +
handler_table_size() +
nul_chk_table_size();
}
const char* nmethod::compile_kind() const {
if (is_osr_method()) return "osr";
if (method() != NULL && is_native_method()) return "c2n";
return NULL;
}
void nmethod::init_defaults() {
_state = in_use;
_unloading_clock = 0;
_marked_for_reclamation = 0;
_has_flushed_dependencies = 0;
_has_unsafe_access = 0;
_has_method_handle_invokes = 0;
_lazy_critical_native = 0;
_has_wide_vectors = 0;
_marked_for_deoptimization = 0;
_lock_count = 0;
_stack_traversal_mark = 0;
_unload_reported = false; // jvmti state
#ifdef ASSERT
_oops_are_stale = false;
#endif
_oops_do_mark_link = NULL;
_jmethod_id = NULL;
_osr_link = NULL;
if (UseG1GC) {
_unloading_next = NULL;
} else {
_scavenge_root_link = NULL;
}
_scavenge_root_state = 0;
_compiler = NULL;
#if INCLUDE_RTM_OPT
_rtm_state = NoRTM;
#endif
#ifdef HAVE_DTRACE_H
_trap_offset = 0;
#endif // def HAVE_DTRACE_H
}
nmethod* nmethod::new_native_nmethod(methodHandle method,
int compile_id,
CodeBuffer *code_buffer,
int vep_offset,
int frame_complete,
int frame_size,
ByteSize basic_lock_owner_sp_offset,
ByteSize basic_lock_sp_offset,
OopMapSet* oop_maps) {
code_buffer->finalize_oop_references(method);
nmethod* nm = NULL;
{
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
int native_nmethod_size = allocation_size(code_buffer, sizeof(nmethod));
CodeOffsets offsets;
offsets.set_value(CodeOffsets::Verified_Entry, vep_offset);
offsets.set_value(CodeOffsets::Frame_Complete, frame_complete);
nm = new (native_nmethod_size) nmethod(method(), native_nmethod_size,
compile_id, &offsets,
code_buffer, frame_size,
basic_lock_owner_sp_offset,
basic_lock_sp_offset, oop_maps);
NOT_PRODUCT(if (nm != NULL) nmethod_stats.note_native_nmethod(nm));
if (PrintAssembly && nm != NULL) {
Disassembler::decode(nm);
}
}
debug_only(if (nm) nm->verify();) // might block
if (nm != NULL) {
nm->log_new_nmethod();
}
return nm;
}
#ifdef HAVE_DTRACE_H
nmethod* nmethod::new_dtrace_nmethod(methodHandle method,
CodeBuffer *code_buffer,
int vep_offset,
int trap_offset,
int frame_complete,
int frame_size) {
code_buffer->finalize_oop_references(method);
nmethod* nm = NULL;
{
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
int nmethod_size = allocation_size(code_buffer, sizeof(nmethod));
CodeOffsets offsets;
offsets.set_value(CodeOffsets::Verified_Entry, vep_offset);
offsets.set_value(CodeOffsets::Dtrace_trap, trap_offset);
offsets.set_value(CodeOffsets::Frame_Complete, frame_complete);
nm = new (nmethod_size) nmethod(method(), nmethod_size,
&offsets, code_buffer, frame_size);
NOT_PRODUCT(if (nm != NULL) nmethod_stats.note_nmethod(nm));
if (PrintAssembly && nm != NULL) {
Disassembler::decode(nm);
}
}
debug_only(if (nm) nm->verify();) // might block
if (nm != NULL) {
nm->log_new_nmethod();
}
return nm;
}
#endif // def HAVE_DTRACE_H
nmethod* nmethod::new_nmethod(methodHandle method,
int compile_id,
int entry_bci,
CodeOffsets* offsets,
int orig_pc_offset,
DebugInformationRecorder* debug_info,
Dependencies* dependencies,
CodeBuffer* code_buffer, int frame_size,
OopMapSet* oop_maps,
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* nul_chk_table,
AbstractCompiler* compiler,
int comp_level
)
{
assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR");
code_buffer->finalize_oop_references(method);
nmethod* nm = NULL;
{ MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
int nmethod_size =
allocation_size(code_buffer, sizeof(nmethod))
+ adjust_pcs_size(debug_info->pcs_size())
+ round_to(dependencies->size_in_bytes() , oopSize)
+ round_to(handler_table->size_in_bytes(), oopSize)
+ round_to(nul_chk_table->size_in_bytes(), oopSize)
+ round_to(debug_info->data_size() , oopSize);
nm = new (nmethod_size)
nmethod(method(), nmethod_size, compile_id, entry_bci, offsets,
orig_pc_offset, debug_info, dependencies, code_buffer, frame_size,
oop_maps,
handler_table,
nul_chk_table,
compiler,
comp_level);
if (nm != NULL) {
for (Dependencies::DepStream deps(nm); deps.next(); ) {
Klass* klass = deps.context_type();
if (klass == NULL) {
continue; // ignore things like evol_method
}
InstanceKlass::cast(klass)->add_dependent_nmethod(nm);
}
NOT_PRODUCT(nmethod_stats.note_nmethod(nm));
if (PrintAssembly || CompilerOracle::has_option_string(method, "PrintAssembly")) {
Disassembler::decode(nm);
}
}
}
if (nm != NULL) {
DEBUG_ONLY(nm->verify();)
nm->log_new_nmethod();
}
return nm;
}
nmethod::nmethod(
Method* method,
int nmethod_size,
int compile_id,
CodeOffsets* offsets,
CodeBuffer* code_buffer,
int frame_size,
ByteSize basic_lock_owner_sp_offset,
ByteSize basic_lock_sp_offset,
OopMapSet* oop_maps )
: CodeBlob("native nmethod", code_buffer, sizeof(nmethod),
nmethod_size, offsets->value(CodeOffsets::Frame_Complete), frame_size, oop_maps),
_native_receiver_sp_offset(basic_lock_owner_sp_offset),
_native_basic_lock_sp_offset(basic_lock_sp_offset)
{
{
debug_only(No_Safepoint_Verifier nsv;)
assert_locked_or_safepoint(CodeCache_lock);
init_defaults();
_method = method;
_entry_bci = InvocationEntryBci;
_exception_offset = 0;
_deoptimize_offset = 0;
_deoptimize_mh_offset = 0;
_orig_pc_offset = 0;
_consts_offset = data_offset();
_stub_offset = data_offset();
_oops_offset = data_offset();
_metadata_offset = _oops_offset + round_to(code_buffer->total_oop_size(), oopSize);
_scopes_data_offset = _metadata_offset + round_to(code_buffer->total_metadata_size(), wordSize);
_scopes_pcs_offset = _scopes_data_offset;
_dependencies_offset = _scopes_pcs_offset;
_handler_table_offset = _dependencies_offset;
_nul_chk_table_offset = _handler_table_offset;
_nmethod_end_offset = _nul_chk_table_offset;
_compile_id = compile_id;
_comp_level = CompLevel_none;
_entry_point = code_begin() + offsets->value(CodeOffsets::Entry);
_verified_entry_point = code_begin() + offsets->value(CodeOffsets::Verified_Entry);
_osr_entry_point = NULL;
_exception_cache = NULL;
_pc_desc_cache.reset_to(NULL);
_hotness_counter = NMethodSweeper::hotness_counter_reset_val();
code_buffer->copy_values_to(this);
if (ScavengeRootsInCode) {
if (detect_scavenge_root_oops()) {
CodeCache::add_scavenge_root_nmethod(this);
}
Universe::heap()->register_nmethod(this);
}
debug_only(verify_scavenge_root_oops());
CodeCache::commit(this);
}
if (PrintNativeNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) {
ttyLocker ttyl; // keep the following output all in one block
if (xtty != NULL) {
xtty->begin_head("print_native_nmethod");
xtty->method(_method);
xtty->stamp();
xtty->end_head(" address='" INTPTR_FORMAT "'", (intptr_t) this);
}
print();
if (PrintNativeNMethods) {
print_code();
if (oop_maps != NULL) {
oop_maps->print();
}
}
if (PrintRelocations) {
print_relocations();
}
if (xtty != NULL) {
xtty->tail("print_native_nmethod");
}
}
}
#ifdef HAVE_DTRACE_H
nmethod::nmethod(
Method* method,
int nmethod_size,
CodeOffsets* offsets,
CodeBuffer* code_buffer,
int frame_size)
: CodeBlob("dtrace nmethod", code_buffer, sizeof(nmethod),
nmethod_size, offsets->value(CodeOffsets::Frame_Complete), frame_size, NULL),
_native_receiver_sp_offset(in_ByteSize(-1)),
_native_basic_lock_sp_offset(in_ByteSize(-1))
{
{
debug_only(No_Safepoint_Verifier nsv;)
assert_locked_or_safepoint(CodeCache_lock);
init_defaults();
_method = method;
_entry_bci = InvocationEntryBci;
_exception_offset = 0;
_deoptimize_offset = 0;
_deoptimize_mh_offset = 0;
_unwind_handler_offset = -1;
_trap_offset = offsets->value(CodeOffsets::Dtrace_trap);
_orig_pc_offset = 0;
_consts_offset = data_offset();
_stub_offset = data_offset();
_oops_offset = data_offset();
_metadata_offset = _oops_offset + round_to(code_buffer->total_oop_size(), oopSize);
_scopes_data_offset = _metadata_offset + round_to(code_buffer->total_metadata_size(), wordSize);
_scopes_pcs_offset = _scopes_data_offset;
_dependencies_offset = _scopes_pcs_offset;
_handler_table_offset = _dependencies_offset;
_nul_chk_table_offset = _handler_table_offset;
_nmethod_end_offset = _nul_chk_table_offset;
_compile_id = 0; // default
_comp_level = CompLevel_none;
_entry_point = code_begin() + offsets->value(CodeOffsets::Entry);
_verified_entry_point = code_begin() + offsets->value(CodeOffsets::Verified_Entry);
_osr_entry_point = NULL;
_exception_cache = NULL;
_pc_desc_cache.reset_to(NULL);
_hotness_counter = NMethodSweeper::hotness_counter_reset_val();
code_buffer->copy_values_to(this);
if (ScavengeRootsInCode) {
if (detect_scavenge_root_oops()) {
CodeCache::add_scavenge_root_nmethod(this);
}
Universe::heap()->register_nmethod(this);
}
DEBUG_ONLY(verify_scavenge_root_oops();)
CodeCache::commit(this);
}
if (PrintNMethods || PrintDebugInfo || PrintRelocations || PrintDependencies) {
ttyLocker ttyl; // keep the following output all in one block
if (xtty != NULL) {
xtty->begin_head("print_dtrace_nmethod");
xtty->method(_method);
xtty->stamp();
xtty->end_head(" address='" INTPTR_FORMAT "'", (intptr_t) this);
}
print();
if (PrintNMethods) {
print_code();
}
if (PrintRelocations) {
print_relocations();
}
if (xtty != NULL) {
xtty->tail("print_dtrace_nmethod");
}
}
}
#endif // def HAVE_DTRACE_H
void* nmethod::operator new(size_t size, int nmethod_size) throw() {
return CodeCache::allocate(nmethod_size);
}
nmethod::nmethod(
Method* method,
int nmethod_size,
int compile_id,
int entry_bci,
CodeOffsets* offsets,
int orig_pc_offset,
DebugInformationRecorder* debug_info,
Dependencies* dependencies,
CodeBuffer *code_buffer,
int frame_size,
OopMapSet* oop_maps,
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* nul_chk_table,
AbstractCompiler* compiler,
int comp_level
)
: CodeBlob("nmethod", code_buffer, sizeof(nmethod),
nmethod_size, offsets->value(CodeOffsets::Frame_Complete), frame_size, oop_maps),
_native_receiver_sp_offset(in_ByteSize(-1)),
_native_basic_lock_sp_offset(in_ByteSize(-1))
{
assert(debug_info->oop_recorder() == code_buffer->oop_recorder(), "shared OR");
{
debug_only(No_Safepoint_Verifier nsv;)
assert_locked_or_safepoint(CodeCache_lock);
init_defaults();
_method = method;
_entry_bci = entry_bci;
_compile_id = compile_id;
_comp_level = comp_level;
_compiler = compiler;
_orig_pc_offset = orig_pc_offset;
_hotness_counter = NMethodSweeper::hotness_counter_reset_val();
_consts_offset = content_offset() + code_buffer->total_offset_of(code_buffer->consts());
_stub_offset = content_offset() + code_buffer->total_offset_of(code_buffer->stubs());
assert(offsets->value(CodeOffsets::Exceptions) != -1, "must be set");
assert(offsets->value(CodeOffsets::Deopt ) != -1, "must be set");
_exception_offset = _stub_offset + offsets->value(CodeOffsets::Exceptions);
_deoptimize_offset = _stub_offset + offsets->value(CodeOffsets::Deopt);
if (offsets->value(CodeOffsets::DeoptMH) != -1) {
_deoptimize_mh_offset = _stub_offset + offsets->value(CodeOffsets::DeoptMH);
} else {
_deoptimize_mh_offset = -1;
}
if (offsets->value(CodeOffsets::UnwindHandler) != -1) {
_unwind_handler_offset = code_offset() + offsets->value(CodeOffsets::UnwindHandler);
} else {
_unwind_handler_offset = -1;
}
_oops_offset = data_offset();
_metadata_offset = _oops_offset + round_to(code_buffer->total_oop_size(), oopSize);
_scopes_data_offset = _metadata_offset + round_to(code_buffer->total_metadata_size(), wordSize);
_scopes_pcs_offset = _scopes_data_offset + round_to(debug_info->data_size (), oopSize);
_dependencies_offset = _scopes_pcs_offset + adjust_pcs_size(debug_info->pcs_size());
_handler_table_offset = _dependencies_offset + round_to(dependencies->size_in_bytes (), oopSize);
_nul_chk_table_offset = _handler_table_offset + round_to(handler_table->size_in_bytes(), oopSize);
_nmethod_end_offset = _nul_chk_table_offset + round_to(nul_chk_table->size_in_bytes(), oopSize);
_entry_point = code_begin() + offsets->value(CodeOffsets::Entry);
_verified_entry_point = code_begin() + offsets->value(CodeOffsets::Verified_Entry);
_osr_entry_point = code_begin() + offsets->value(CodeOffsets::OSR_Entry);
_exception_cache = NULL;
_pc_desc_cache.reset_to(scopes_pcs_begin());
code_buffer->copy_values_to(this);
debug_info->copy_to(this);
dependencies->copy_to(this);
if (ScavengeRootsInCode) {
if (detect_scavenge_root_oops()) {
CodeCache::add_scavenge_root_nmethod(this);
}
Universe::heap()->register_nmethod(this);
}
debug_only(verify_scavenge_root_oops());
CodeCache::commit(this);
handler_table->copy_to(this);
nul_chk_table->copy_to(this);
assert(compiler->is_c2() ||
_method->is_static() == (entry_point() == _verified_entry_point),
" entry points must be same for static methods and vice versa");
}
bool printnmethods = PrintNMethods
|| CompilerOracle::should_print(_method)
|| CompilerOracle::has_option_string(_method, "PrintNMethods");
if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) {
print_nmethod(printnmethods);
}
}
void nmethod::log_identity(xmlStream* log) const {
log->print(" compile_id='%d'", compile_id());
const char* nm_kind = compile_kind();
if (nm_kind != NULL) log->print(" compile_kind='%s'", nm_kind);
if (compiler() != NULL) {
log->print(" compiler='%s'", compiler()->name());
}
if (TieredCompilation) {
log->print(" level='%d'", comp_level());
}
}
#define LOG_OFFSET(log, name) \
if ((intptr_t)name##_end() - (intptr_t)name##_begin()) \
log->print(" " XSTR(name) "_offset='%d'" , \
(intptr_t)name##_begin() - (intptr_t)this)
void nmethod::log_new_nmethod() const {
if (LogCompilation && xtty != NULL) {
ttyLocker ttyl;
HandleMark hm;
xtty->begin_elem("nmethod");
log_identity(xtty);
xtty->print(" entry='" INTPTR_FORMAT "' size='%d'", code_begin(), size());
xtty->print(" address='" INTPTR_FORMAT "'", (intptr_t) this);
LOG_OFFSET(xtty, relocation);
LOG_OFFSET(xtty, consts);
LOG_OFFSET(xtty, insts);
LOG_OFFSET(xtty, stub);
LOG_OFFSET(xtty, scopes_data);
LOG_OFFSET(xtty, scopes_pcs);
LOG_OFFSET(xtty, dependencies);
LOG_OFFSET(xtty, handler_table);
LOG_OFFSET(xtty, nul_chk_table);
LOG_OFFSET(xtty, oops);
xtty->method(method());
xtty->stamp();
xtty->end_elem();
}
}
#undef LOG_OFFSET
void nmethod::print_on(outputStream* st, const char* msg) const {
if (st != NULL) {
ttyLocker ttyl;
if (WizardMode) {
CompileTask::print_compilation(st, this, msg, /*short_form:*/ true);
st->print_cr(" (" INTPTR_FORMAT ")", this);
} else {
CompileTask::print_compilation(st, this, msg, /*short_form:*/ false);
}
}
}
void nmethod::print_nmethod(bool printmethod) {
ttyLocker ttyl; // keep the following output all in one block
if (xtty != NULL) {
xtty->begin_head("print_nmethod");
xtty->stamp();
xtty->end_head();
}
print();
if (printmethod) {
print_code();
print_pcs();
if (oop_maps()) {
oop_maps()->print();
}
}
if (PrintDebugInfo) {
print_scopes();
}
if (PrintRelocations) {
print_relocations();
}
if (PrintDependencies) {
print_dependencies();
}
if (PrintExceptionHandlers) {
print_handler_table();
print_nul_chk_table();
}
if (xtty != NULL) {
xtty->tail("print_nmethod");
}
}
inline void nmethod::initialize_immediate_oop(oop* dest, jobject handle) {
if (handle == NULL ||
handle == (jobject) Universe::non_oop_word()) {
(*dest) = (oop) handle;
} else {
(*dest) = JNIHandles::resolve_non_null(handle);
}
}
void nmethod::copy_values(GrowableArray<jobject>* array) {
int length = array->length();
assert((address)(oops_begin() + length) <= (address)oops_end(), "oops big enough");
oop* dest = oops_begin();
for (int index = 0 ; index < length; index++) {
initialize_immediate_oop(&dest[index], array->at(index));
}
fix_oop_relocations(NULL, NULL, /*initialize_immediates=*/ true);
}
void nmethod::copy_values(GrowableArray<Metadata*>* array) {
int length = array->length();
assert((address)(metadata_begin() + length) <= (address)metadata_end(), "big enough");
Metadata** dest = metadata_begin();
for (int index = 0 ; index < length; index++) {
dest[index] = array->at(index);
}
}
bool nmethod::is_at_poll_return(address pc) {
RelocIterator iter(this, pc, pc+1);
while (iter.next()) {
if (iter.type() == relocInfo::poll_return_type)
return true;
}
return false;
}
bool nmethod::is_at_poll_or_poll_return(address pc) {
RelocIterator iter(this, pc, pc+1);
while (iter.next()) {
relocInfo::relocType t = iter.type();
if (t == relocInfo::poll_return_type || t == relocInfo::poll_type)
return true;
}
return false;
}
void nmethod::fix_oop_relocations(address begin, address end, bool initialize_immediates) {
RelocIterator iter(this, begin, end);
while (iter.next()) {
if (iter.type() == relocInfo::oop_type) {
oop_Relocation* reloc = iter.oop_reloc();
if (initialize_immediates && reloc->oop_is_immediate()) {
oop* dest = reloc->oop_addr();
initialize_immediate_oop(dest, (jobject) *dest);
}
reloc->fix_oop_relocation();
} else if (iter.type() == relocInfo::metadata_type) {
metadata_Relocation* reloc = iter.metadata_reloc();
reloc->fix_metadata_relocation();
}
}
}
void nmethod::verify_oop_relocations() {
RelocIterator iter(this, NULL, NULL);
while (iter.next()) {
if (iter.type() == relocInfo::oop_type) {
oop_Relocation* reloc = iter.oop_reloc();
if (!reloc->oop_is_immediate()) {
reloc->verify_oop_relocation();
}
}
}
}
ScopeDesc* nmethod::scope_desc_at(address pc) {
PcDesc* pd = pc_desc_at(pc);
guarantee(pd != NULL, "scope must be present");
return new ScopeDesc(this, pd->scope_decode_offset(),
pd->obj_decode_offset(), pd->should_reexecute(),
pd->return_oop());
}
void nmethod::clear_inline_caches() {
assert(SafepointSynchronize::is_at_safepoint(), "cleaning of IC's only allowed at safepoint");
if (is_zombie()) {
return;
}
RelocIterator iter(this);
while (iter.next()) {
iter.reloc()->clear_inline_cache();
}
}
void nmethod::clear_ic_stubs() {
assert_locked_or_safepoint(CompiledIC_lock);
ResourceMark rm;
RelocIterator iter(this);
while(iter.next()) {
if (iter.type() == relocInfo::virtual_call_type) {
CompiledIC* ic = CompiledIC_at(&iter);
ic->clear_ic_stub();
}
}
}
void nmethod::cleanup_inline_caches() {
assert_locked_or_safepoint(CompiledIC_lock);
address low_boundary = verified_entry_point();
if (!is_in_use()) {
low_boundary += NativeJump::instruction_size;
}
ResourceMark rm;
RelocIterator iter(this, low_boundary);
while(iter.next()) {
switch(iter.type()) {
case relocInfo::virtual_call_type:
case relocInfo::opt_virtual_call_type: {
CompiledIC *ic = CompiledIC_at(&iter);
CodeBlob *cb = CodeCache::find_blob_unsafe(ic->ic_destination());
if( cb != NULL && cb->is_nmethod() ) {
nmethod* nm = (nmethod*)cb;
if (!nm->is_in_use() || (nm->method()->code() != nm)) ic->set_to_clean(is_alive());
}
break;
}
case relocInfo::static_call_type: {
CompiledStaticCall *csc = compiledStaticCall_at(iter.reloc());
CodeBlob *cb = CodeCache::find_blob_unsafe(csc->destination());
if( cb != NULL && cb->is_nmethod() ) {
nmethod* nm = (nmethod*)cb;
if (!nm->is_in_use() || (nm->method()->code() != nm)) csc->set_to_clean();
}
break;
}
}
}
}
void nmethod::verify_clean_inline_caches() {
assert_locked_or_safepoint(CompiledIC_lock);
address low_boundary = verified_entry_point();
if (!is_in_use()) {
low_boundary += NativeJump::instruction_size;
}
ResourceMark rm;
RelocIterator iter(this, low_boundary);
while(iter.next()) {
switch(iter.type()) {
case relocInfo::virtual_call_type:
case relocInfo::opt_virtual_call_type: {
CompiledIC *ic = CompiledIC_at(&iter);
CodeBlob *cb = CodeCache::find_blob_unsafe(ic->ic_destination());
if( cb != NULL && cb->is_nmethod() ) {
nmethod* nm = (nmethod*)cb;
if (!nm->is_in_use() || (nm->method()->code() != nm)) {
assert(ic->is_clean(), "IC should be clean");
}
}
break;
}
case relocInfo::static_call_type: {
CompiledStaticCall *csc = compiledStaticCall_at(iter.reloc());
CodeBlob *cb = CodeCache::find_blob_unsafe(csc->destination());
if( cb != NULL && cb->is_nmethod() ) {
nmethod* nm = (nmethod*)cb;
if (!nm->is_in_use() || (nm->method()->code() != nm)) {
assert(csc->is_clean(), "IC should be clean");
}
}
break;
}
}
}
}
int nmethod::verify_icholder_relocations() {
int count = 0;
RelocIterator iter(this);
while(iter.next()) {
if (iter.type() == relocInfo::virtual_call_type) {
if (CompiledIC::is_icholder_call_site(iter.virtual_call_reloc())) {
CompiledIC *ic = CompiledIC_at(&iter);
if (TraceCompiledIC) {
tty->print("noticed icholder " INTPTR_FORMAT " ", p2i(ic->cached_icholder()));
ic->print();
}
assert(ic->cached_icholder() != NULL, "must be non-NULL");
count++;
}
}
}
return count;
}
void nmethod::mark_as_seen_on_stack() {
assert(is_alive(), "Must be an alive method");
set_stack_traversal_mark(NMethodSweeper::traversal_count());
}
bool nmethod::can_convert_to_zombie() {
assert(is_not_entrant(), "must be a non-entrant method");
return stack_traversal_mark()+1 < NMethodSweeper::traversal_count() &&
!is_locked_by_vm();
}
void nmethod::inc_decompile_count() {
if (!is_compiled_by_c2()) return;
Method* m = method();
if (m == NULL) return;
MethodData* mdo = m->method_data();
if (mdo == NULL) return;
mdo->inc_decompile_count();
}
void nmethod::increase_unloading_clock() {
_global_unloading_clock++;
if (_global_unloading_clock == 0) {
_global_unloading_clock = 1;
}
}
void nmethod::set_unloading_clock(unsigned char unloading_clock) {
OrderAccess::release_store((volatile jubyte*)&_unloading_clock, unloading_clock);
}
unsigned char nmethod::unloading_clock() {
return (unsigned char)OrderAccess::load_acquire((volatile jubyte*)&_unloading_clock);
}
void nmethod::make_unloaded(BoolObjectClosure* is_alive, oop cause) {
post_compiled_method_unload();
assert(Universe::heap()->is_gc_active(), "should only be called during gc");
assert(is_alive != NULL, "Should be non-NULL");
flush_dependencies(is_alive);
if (TraceClassUnloading && WizardMode) {
tty->print_cr("[Class unloading: Making nmethod " INTPTR_FORMAT
" unloadable], Method*(" INTPTR_FORMAT
"), cause(" INTPTR_FORMAT ")",
this, (address)_method, (address)cause);
if (!Universe::heap()->is_gc_active())
cause->klass()->print();
}
if (is_osr_method()) {
invalidate_osr_method();
}
if (_method != NULL) {
if (_method->code() == this) {
_method->clear_code(); // Break a cycle
}
_method = NULL; // Clear the method of this dead nmethod
}
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
if (is_in_use()) {
CodeCache::set_needs_cache_clean(true);
}
Universe::heap()->unregister_nmethod(this);
_state = unloaded;
log_state_change();
assert(_method == NULL, "Tautology");
set_osr_link(NULL);
NMethodSweeper::report_state_change(this);
}
void nmethod::invalidate_osr_method() {
assert(_entry_bci != InvocationEntryBci, "wrong kind of nmethod");
if (method() != NULL)
method()->method_holder()->remove_osr_nmethod(this);
_entry_bci = InvalidOSREntryBci;
}
void nmethod::log_state_change() const {
if (LogCompilation) {
if (xtty != NULL) {
ttyLocker ttyl; // keep the following output all in one block
if (_state == unloaded) {
xtty->begin_elem("make_unloaded thread='" UINTX_FORMAT "'",
os::current_thread_id());
} else {
xtty->begin_elem("make_not_entrant thread='" UINTX_FORMAT "'%s",
os::current_thread_id(),
(_state == zombie ? " zombie='1'" : ""));
}
log_identity(xtty);
xtty->stamp();
xtty->end_elem();
}
}
if (PrintCompilation && _state != unloaded) {
print_on(tty, _state == zombie ? "made zombie" : "made not entrant");
}
}
bool nmethod::make_not_entrant_or_zombie(unsigned int state) {
assert(state == zombie || state == not_entrant, "must be zombie or not_entrant");
assert(!is_zombie(), "should not already be a zombie");
nmethodLocker nml(this);
methodHandle the_method(method());
No_Safepoint_Verifier nsv;
bool nmethod_needs_unregister = false;
{
if (is_osr_method()) {
invalidate_osr_method();
}
MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag);
if (_state == state) {
return false;
}
if (!is_osr_method() && !is_not_entrant()) {
NativeJump::patch_verified_entry(entry_point(), verified_entry_point(),
SharedRuntime::get_handle_wrong_method_stub());
}
if (is_in_use()) {
inc_decompile_count();
}
if ((state == zombie) && !is_unloaded()) {
nmethod_needs_unregister = true;
}
if (state == not_entrant) {
mark_as_seen_on_stack();
OrderAccess::storestore();
}
_state = state;
log_state_change();
if (method() != NULL && (method()->code() == this ||
method()->from_compiled_entry() == verified_entry_point())) {
HandleMark hm;
method()->clear_code(false /* already owns Patching_lock */);
}
} // leave critical region under Patching_lock
if (state == zombie) {
{
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
if (nmethod_needs_unregister) {
Universe::heap()->unregister_nmethod(this);
}
flush_dependencies(NULL);
}
post_compiled_method_unload();
#ifdef ASSERT
_oops_are_stale = true;
#endif
set_method(NULL);
} else {
assert(state == not_entrant, "other cases may need to be handled differently");
}
if (TraceCreateZombies) {
tty->print_cr("nmethod <" INTPTR_FORMAT "> code made %s", this, (state == not_entrant) ? "not entrant" : "zombie");
}
NMethodSweeper::report_state_change(this);
return true;
}
void nmethod::flush() {
assert(is_zombie() || (is_osr_method() && is_unloaded()), "must be a zombie method");
assert(is_marked_for_reclamation() || (is_osr_method() && is_unloaded()), "must be marked for reclamation");
assert (!is_locked_by_vm(), "locked methods shouldn't be flushed");
assert_locked_or_safepoint(CodeCache_lock);
Events::log(JavaThread::current(), "flushing nmethod " INTPTR_FORMAT, this);
if (PrintMethodFlushing) {
tty->print_cr("*flushing nmethod %3d/" INTPTR_FORMAT ". Live blobs:" UINT32_FORMAT "/Free CodeCache:" SIZE_FORMAT "Kb",
_compile_id, this, CodeCache::nof_blobs(), CodeCache::unallocated_capacity()/1024);
}
ExceptionCache* ec = exception_cache();
set_exception_cache(NULL);
while(ec != NULL) {
ExceptionCache* next = ec->next();
delete ec;
ec = next;
}
if (on_scavenge_root_list()) {
CodeCache::drop_scavenge_root_nmethod(this);
}
#ifdef SHARK
((SharkCompiler *) compiler())->free_compiled_method(insts_begin());
#endif // SHARK
((CodeBlob*)(this))->flush();
CodeCache::free(this);
}
void nmethod::flush_dependencies(BoolObjectClosure* is_alive) {
assert_locked_or_safepoint(CodeCache_lock);
assert(Universe::heap()->is_gc_active() == (is_alive != NULL),
"is_alive is non-NULL if and only if we are called during GC");
if (!has_flushed_dependencies()) {
set_has_flushed_dependencies();
for (Dependencies::DepStream deps(this); deps.next(); ) {
Klass* klass = deps.context_type();
if (klass == NULL) continue; // ignore things like evol_method
if (is_alive == NULL || klass->is_loader_alive(is_alive)) {
bool delete_immediately = is_alive == NULL;
InstanceKlass::cast(klass)->remove_dependent_nmethod(this, delete_immediately);
}
}
}
}
bool nmethod::can_unload(BoolObjectClosure* is_alive, oop* root, bool unloading_occurred) {
assert(root != NULL, "just checking");
oop obj = *root;
if (obj == NULL || is_alive->do_object_b(obj)) {
return false;
}
assert(unloading_occurred || ScavengeRootsInCode, "Inconsistency in unloading");
make_unloaded(is_alive, obj);
return true;
}
void nmethod::post_compiled_method_load_event() {
Method* moop = method();
#ifndef USDT2
HS_DTRACE_PROBE8(hotspot, compiled__method__load,
moop->klass_name()->bytes(),
moop->klass_name()->utf8_length(),
moop->name()->bytes(),
moop->name()->utf8_length(),
moop->signature()->bytes(),
moop->signature()->utf8_length(),
insts_begin(), insts_size());
#else /* USDT2 */
HOTSPOT_COMPILED_METHOD_LOAD(
(char *) moop->klass_name()->bytes(),
moop->klass_name()->utf8_length(),
(char *) moop->name()->bytes(),
moop->name()->utf8_length(),
(char *) moop->signature()->bytes(),
moop->signature()->utf8_length(),
insts_begin(), insts_size());
#endif /* USDT2 */
if (JvmtiExport::should_post_compiled_method_load() ||
JvmtiExport::should_post_compiled_method_unload()) {
get_and_cache_jmethod_id();
}
if (JvmtiExport::should_post_compiled_method_load()) {
MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
JvmtiDeferredEventQueue::enqueue(
JvmtiDeferredEvent::compiled_method_load_event(this));
}
}
jmethodID nmethod::get_and_cache_jmethod_id() {
if (_jmethod_id == NULL) {
_jmethod_id = method()->jmethod_id();
}
return _jmethod_id;
}
void nmethod::post_compiled_method_unload() {
if (unload_reported()) {
return;
}
assert(_method != NULL && !is_unloaded(), "just checking");
DTRACE_METHOD_UNLOAD_PROBE(method());
if (_jmethod_id != NULL && JvmtiExport::should_post_compiled_method_unload()) {
assert(!unload_reported(), "already unloaded");
JvmtiDeferredEvent event =
JvmtiDeferredEvent::compiled_method_unload_event(this,
_jmethod_id, insts_begin());
if (SafepointSynchronize::is_at_safepoint()) {
JvmtiDeferredEventQueue::add_pending_event(event);
} else {
MutexLockerEx ml(Service_lock, Mutex::_no_safepoint_check_flag);
JvmtiDeferredEventQueue::enqueue(event);
}
}
set_unload_reported();
}
void static clean_ic_if_metadata_is_dead(CompiledIC *ic, BoolObjectClosure *is_alive, bool mark_on_stack) {
if (ic->is_icholder_call()) {
CompiledICHolder* cichk_oop = ic->cached_icholder();
if (mark_on_stack) {
Metadata::mark_on_stack(cichk_oop->holder_metadata());
Metadata::mark_on_stack(cichk_oop->holder_klass());
}
if (cichk_oop->is_loader_alive(is_alive)) {
return;
}
} else {
Metadata* ic_oop = ic->cached_metadata();
if (ic_oop != NULL) {
if (mark_on_stack) {
Metadata::mark_on_stack(ic_oop);
}
if (ic_oop->is_klass()) {
if (((Klass*)ic_oop)->is_loader_alive(is_alive)) {
return;
}
} else if (ic_oop->is_method()) {
if (((Method*)ic_oop)->method_holder()->is_loader_alive(is_alive)) {
return;
}
} else {
ShouldNotReachHere();
}
}
}
ic->set_to_clean();
}
void nmethod::do_unloading(BoolObjectClosure* is_alive, bool unloading_occurred) {
assert(!is_zombie() && !is_unloaded(),
"should not call follow on zombie or unloaded nmethod");
address low_boundary = verified_entry_point();
if (is_not_entrant()) {
low_boundary += NativeJump::instruction_size;
}
bool a_class_was_redefined = JvmtiExport::has_redefined_a_class();
if (a_class_was_redefined) {
unloading_occurred = true;
}
clean_exception_cache(is_alive);
if (unloading_occurred) {
RelocIterator iter(this, low_boundary);
while(iter.next()) {
if (iter.type() == relocInfo::virtual_call_type) {
CompiledIC *ic = CompiledIC_at(&iter);
clean_ic_if_metadata_is_dead(ic, is_alive, false);
}
}
}
{
RelocIterator iter(this, low_boundary);
while (iter.next()) {
if (iter.type() == relocInfo::oop_type) {
oop_Relocation* r = iter.oop_reloc();
assert(1 == (r->oop_is_immediate()) +
(r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()),
"oop must be found in exactly one place");
if (r->oop_is_immediate() && r->oop_value() != NULL) {
if (can_unload(is_alive, r->oop_addr(), unloading_occurred)) {
return;
}
}
}
}
}
for (oop* p = oops_begin(); p < oops_end(); p++) {
if (*p == Universe::non_oop_word()) continue; // skip non-oops
if (can_unload(is_alive, p, unloading_occurred)) {
return;
}
}
verify_metadata_loaders(low_boundary, is_alive);
}
template <class CompiledICorStaticCall>
static bool clean_if_nmethod_is_unloaded(CompiledICorStaticCall *ic, address addr, BoolObjectClosure *is_alive, nmethod* from) {
CodeBlob *cb = CodeCache::find_blob_unsafe(addr);
if (cb != NULL && cb->is_nmethod()) {
nmethod* nm = (nmethod*)cb;
if (nm->unloading_clock() != nmethod::global_unloading_clock()) {
return true;
}
if (!nm->is_in_use() || (nm->method()->code() != nm)) {
ic->set_to_clean();
assert(ic->is_clean(), err_msg("nmethod " PTR_FORMAT "not clean %s", from, from->method()->name_and_sig_as_C_string()));
}
}
return false;
}
static bool clean_if_nmethod_is_unloaded(CompiledIC *ic, BoolObjectClosure *is_alive, nmethod* from) {
return clean_if_nmethod_is_unloaded(ic, ic->ic_destination(), is_alive, from);
}
static bool clean_if_nmethod_is_unloaded(CompiledStaticCall *csc, BoolObjectClosure *is_alive, nmethod* from) {
return clean_if_nmethod_is_unloaded(csc, csc->destination(), is_alive, from);
}
bool nmethod::unload_if_dead_at(RelocIterator* iter_at_oop, BoolObjectClosure *is_alive, bool unloading_occurred) {
assert(iter_at_oop->type() == relocInfo::oop_type, "Wrong relocation type");
oop_Relocation* r = iter_at_oop->oop_reloc();
assert(1 == (r->oop_is_immediate()) +
(r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()),
"oop must be found in exactly one place");
if (r->oop_is_immediate() && r->oop_value() != NULL) {
if (can_unload(is_alive, r->oop_addr(), unloading_occurred)) {
return true;;
}
}
return false;
}
void nmethod::mark_metadata_on_stack_at(RelocIterator* iter_at_metadata) {
assert(iter_at_metadata->type() == relocInfo::metadata_type, "Wrong relocation type");
metadata_Relocation* r = iter_at_metadata->metadata_reloc();
assert(1 == (r->metadata_is_immediate()) +
(r->metadata_addr() >= metadata_begin() && r->metadata_addr() < metadata_end()),
"metadata must be found in exactly one place");
if (r->metadata_is_immediate() && r->metadata_value() != NULL) {
Metadata* md = r->metadata_value();
if (md != _method) Metadata::mark_on_stack(md);
}
}
void nmethod::mark_metadata_on_stack_non_relocs() {
for (Metadata** p = metadata_begin(); p < metadata_end(); p++) {
if (*p == Universe::non_oop_word() || *p == NULL) continue; // skip non-oops
Metadata* md = *p;
Metadata::mark_on_stack(md);
}
if (_method != NULL) Metadata::mark_on_stack(_method);
}
bool nmethod::do_unloading_parallel(BoolObjectClosure* is_alive, bool unloading_occurred) {
ResourceMark rm;
assert(!is_zombie() && !is_unloaded(),
"should not call follow on zombie or unloaded nmethod");
address low_boundary = verified_entry_point();
if (is_not_entrant()) {
low_boundary += NativeJump::instruction_size;
}
bool a_class_was_redefined = JvmtiExport::has_redefined_a_class();
if (a_class_was_redefined) {
unloading_occurred = true;
}
bool mark_metadata_on_stack = a_class_was_redefined;
clean_exception_cache(is_alive);
bool is_unloaded = false;
bool postponed = false;
RelocIterator iter(this, low_boundary);
while(iter.next()) {
switch (iter.type()) {
case relocInfo::virtual_call_type:
if (unloading_occurred) {
clean_ic_if_metadata_is_dead(CompiledIC_at(&iter), is_alive, mark_metadata_on_stack);
}
postponed |= clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), is_alive, this);
break;
case relocInfo::opt_virtual_call_type:
postponed |= clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), is_alive, this);
break;
case relocInfo::static_call_type:
postponed |= clean_if_nmethod_is_unloaded(compiledStaticCall_at(iter.reloc()), is_alive, this);
break;
case relocInfo::oop_type:
if (!is_unloaded) {
is_unloaded = unload_if_dead_at(&iter, is_alive, unloading_occurred);
}
break;
case relocInfo::metadata_type:
if (mark_metadata_on_stack) {
mark_metadata_on_stack_at(&iter);
}
}
}
if (mark_metadata_on_stack) {
mark_metadata_on_stack_non_relocs();
}
if (is_unloaded) {
return postponed;
}
for (oop* p = oops_begin(); p < oops_end(); p++) {
if (*p == Universe::non_oop_word()) continue; // skip non-oops
if (can_unload(is_alive, p, unloading_occurred)) {
is_unloaded = true;
break;
}
}
if (is_unloaded) {
return postponed;
}
verify_metadata_loaders(low_boundary, is_alive);
return postponed;
}
void nmethod::do_unloading_parallel_postponed(BoolObjectClosure* is_alive, bool unloading_occurred) {
ResourceMark rm;
assert(!is_zombie(),
"should not call follow on zombie nmethod");
address low_boundary = verified_entry_point();
if (is_not_entrant()) {
low_boundary += NativeJump::instruction_size;
}
RelocIterator iter(this, low_boundary);
while(iter.next()) {
switch (iter.type()) {
case relocInfo::virtual_call_type:
clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), is_alive, this);
break;
case relocInfo::opt_virtual_call_type:
clean_if_nmethod_is_unloaded(CompiledIC_at(&iter), is_alive, this);
break;
case relocInfo::static_call_type:
clean_if_nmethod_is_unloaded(compiledStaticCall_at(iter.reloc()), is_alive, this);
break;
}
}
}
#ifdef ASSERT
class CheckClass : AllStatic {
static BoolObjectClosure* _is_alive;
static void check_class(Metadata* md) {
Klass* klass = NULL;
if (md->is_klass()) {
klass = ((Klass*)md);
} else if (md->is_method()) {
klass = ((Method*)md)->method_holder();
} else if (md->is_methodData()) {
klass = ((MethodData*)md)->method()->method_holder();
} else {
md->print();
ShouldNotReachHere();
}
assert(klass->is_loader_alive(_is_alive), "must be alive");
}
public:
static void do_check_class(BoolObjectClosure* is_alive, nmethod* nm) {
assert(SafepointSynchronize::is_at_safepoint(), "this is only ok at safepoint");
_is_alive = is_alive;
nm->metadata_do(check_class);
}
};
BoolObjectClosure* CheckClass::_is_alive = NULL;
#endif // ASSERT
void nmethod::verify_metadata_loaders(address low_boundary, BoolObjectClosure* is_alive) {
#ifdef ASSERT
RelocIterator iter(this, low_boundary);
while (iter.next()) {
address static_call_addr = NULL;
if (iter.type() == relocInfo::opt_virtual_call_type) {
CompiledIC* cic = CompiledIC_at(&iter);
if (!cic->is_call_to_interpreted()) {
static_call_addr = iter.addr();
}
} else if (iter.type() == relocInfo::static_call_type) {
CompiledStaticCall* csc = compiledStaticCall_at(iter.reloc());
if (!csc->is_call_to_interpreted()) {
static_call_addr = iter.addr();
}
}
if (static_call_addr != NULL) {
RelocIterator sciter(this, low_boundary);
while (sciter.next()) {
if (sciter.type() == relocInfo::static_stub_type &&
sciter.static_stub_reloc()->static_call() == static_call_addr) {
sciter.static_stub_reloc()->clear_inline_cache();
}
}
}
}
CheckClass::do_check_class(is_alive, this);
#endif
}
void nmethod::metadata_do(void f(Metadata*)) {
address low_boundary = verified_entry_point();
if (is_not_entrant()) {
low_boundary += NativeJump::instruction_size;
}
{
RelocIterator iter(this, low_boundary);
while (iter.next()) {
if (iter.type() == relocInfo::metadata_type ) {
metadata_Relocation* r = iter.metadata_reloc();
assert(1 == (r->metadata_is_immediate()) +
(r->metadata_addr() >= metadata_begin() && r->metadata_addr() < metadata_end()),
"metadata must be found in exactly one place");
if (r->metadata_is_immediate() && r->metadata_value() != NULL) {
Metadata* md = r->metadata_value();
if (md != _method) f(md);
}
} else if (iter.type() == relocInfo::virtual_call_type) {
ResourceMark rm;
CompiledIC *ic = CompiledIC_at(&iter);
if (ic->is_icholder_call()) {
CompiledICHolder* cichk = ic->cached_icholder();
f(cichk->holder_metadata());
f(cichk->holder_klass());
} else {
Metadata* ic_oop = ic->cached_metadata();
if (ic_oop != NULL) {
f(ic_oop);
}
}
}
}
}
for (Metadata** p = metadata_begin(); p < metadata_end(); p++) {
if (*p == Universe::non_oop_word() || *p == NULL) continue; // skip non-oops
Metadata* md = *p;
f(md);
}
if (_method != NULL) f(_method);
}
void nmethod::oops_do(OopClosure* f, bool allow_zombie) {
assert(allow_zombie || !is_zombie(), "should not call follow on zombie nmethod");
assert(!is_unloaded(), "should not call follow on unloaded nmethod");
address low_boundary = verified_entry_point();
if (is_not_entrant()) {
low_boundary += NativeJump::instruction_size;
}
RelocIterator iter(this, low_boundary);
while (iter.next()) {
if (iter.type() == relocInfo::oop_type ) {
oop_Relocation* r = iter.oop_reloc();
assert(1 == (r->oop_is_immediate()) +
(r->oop_addr() >= oops_begin() && r->oop_addr() < oops_end()),
"oop must be found in exactly one place");
if (r->oop_is_immediate() && r->oop_value() != NULL) {
f->do_oop(r->oop_addr());
}
}
}
for (oop* p = oops_begin(); p < oops_end(); p++) {
if (*p == Universe::non_oop_word()) continue; // skip non-oops
f->do_oop(p);
}
}
#define NMETHOD_SENTINEL ((nmethod*)badAddress)
nmethod* volatile nmethod::_oops_do_mark_nmethods;
bool nmethod::test_set_oops_do_mark() {
assert(nmethod::oops_do_marking_is_active(), "oops_do_marking_prologue must be called");
nmethod* observed_mark_link = _oops_do_mark_link;
if (observed_mark_link == NULL) {
observed_mark_link = (nmethod*)
Atomic::cmpxchg_ptr(NMETHOD_SENTINEL, &_oops_do_mark_link, NULL);
if (observed_mark_link == NULL) {
nmethod* observed_mark_nmethods = _oops_do_mark_nmethods;
for (;;) {
nmethod* required_mark_nmethods = observed_mark_nmethods;
_oops_do_mark_link = required_mark_nmethods;
observed_mark_nmethods = (nmethod*)
Atomic::cmpxchg_ptr(this, &_oops_do_mark_nmethods, required_mark_nmethods);
if (observed_mark_nmethods == required_mark_nmethods)
break;
}
NOT_PRODUCT(if (TraceScavenge) print_on(tty, "oops_do, mark"));
return false;
}
}
return true;
}
void nmethod::oops_do_marking_prologue() {
NOT_PRODUCT(if (TraceScavenge) tty->print_cr("[oops_do_marking_prologue"));
assert(_oops_do_mark_nmethods == NULL, "must not call oops_do_marking_prologue twice in a row");
void* observed = Atomic::cmpxchg_ptr(NMETHOD_SENTINEL, &_oops_do_mark_nmethods, NULL);
guarantee(observed == NULL, "no races in this sequential code");
}
void nmethod::oops_do_marking_epilogue() {
assert(_oops_do_mark_nmethods != NULL, "must not call oops_do_marking_epilogue twice in a row");
nmethod* cur = _oops_do_mark_nmethods;
while (cur != NMETHOD_SENTINEL) {
assert(cur != NULL, "not NULL-terminated");
nmethod* next = cur->_oops_do_mark_link;
cur->_oops_do_mark_link = NULL;
DEBUG_ONLY(cur->verify_oop_relocations());
NOT_PRODUCT(if (TraceScavenge) cur->print_on(tty, "oops_do, unmark"));
cur = next;
}
void* required = _oops_do_mark_nmethods;
void* observed = Atomic::cmpxchg_ptr(NULL, &_oops_do_mark_nmethods, required);
guarantee(observed == required, "no races in this sequential code");
NOT_PRODUCT(if (TraceScavenge) tty->print_cr("oops_do_marking_epilogue]"));
}
class DetectScavengeRoot: public OopClosure {
bool _detected_scavenge_root;
public:
DetectScavengeRoot() : _detected_scavenge_root(false)
{ NOT_PRODUCT(_print_nm = NULL); }
bool detected_scavenge_root() { return _detected_scavenge_root; }
virtual void do_oop(oop* p) {
if ((*p) != NULL && (*p)->is_scavengable()) {
NOT_PRODUCT(maybe_print(p));
_detected_scavenge_root = true;
}
}
virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); }
#ifndef PRODUCT
nmethod* _print_nm;
void maybe_print(oop* p) {
if (_print_nm == NULL) return;
if (!_detected_scavenge_root) _print_nm->print_on(tty, "new scavenge root");
tty->print_cr("" PTR_FORMAT "[offset=%d] detected scavengable oop " PTR_FORMAT " (found at " PTR_FORMAT ")",
_print_nm, (int)((intptr_t)p - (intptr_t)_print_nm),
(void *)(*p), (intptr_t)p);
(*p)->print();
}
#endif //PRODUCT
};
bool nmethod::detect_scavenge_root_oops() {
DetectScavengeRoot detect_scavenge_root;
NOT_PRODUCT(if (TraceScavenge) detect_scavenge_root._print_nm = this);
oops_do(&detect_scavenge_root);
return detect_scavenge_root.detected_scavenge_root();
}
void nmethod::preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) {
#ifndef SHARK
if (!method()->is_native()) {
SimpleScopeDesc ssd(this, fr.pc());
Bytecode_invoke call(ssd.method(), ssd.bci());
bool has_receiver = call.has_receiver();
bool has_appendix = call.has_appendix();
Symbol* signature = call.signature();
fr.oops_compiled_arguments_do(signature, has_receiver, has_appendix, reg_map, f);
}
#endif // !SHARK
}
oop nmethod::embeddedOop_at(u_char* p) {
RelocIterator iter(this, p, p + 1);
while (iter.next())
if (iter.type() == relocInfo::oop_type) {
return iter.oop_reloc()->oop_value();
}
return NULL;
}
inline bool includes(void* p, void* from, void* to) {
return from <= p && p < to;
}
void nmethod::copy_scopes_pcs(PcDesc* pcs, int count) {
assert(count >= 2, "must be sentinel values, at least");
#ifdef ASSERT
int prev_offset = pcs[0].pc_offset();
assert(prev_offset == PcDesc::lower_offset_limit,
"must start with a sentinel");
for (int i = 1; i < count; i++) {
int this_offset = pcs[i].pc_offset();
assert(this_offset > prev_offset, "offsets must be sorted");
prev_offset = this_offset;
}
assert(prev_offset == PcDesc::upper_offset_limit,
"must end with a sentinel");
#endif //ASSERT
for (int i = 0; i < count; i++) {
if (pcs[i].is_method_handle_invoke()) {
set_has_method_handle_invokes(true);
break;
}
}
assert(has_method_handle_invokes() == (_deoptimize_mh_offset != -1), "must have deopt mh handler");
int size = count * sizeof(PcDesc);
assert(scopes_pcs_size() >= size, "oob");
memcpy(scopes_pcs_begin(), pcs, size);
PcDesc* last_pc = &scopes_pcs_begin()[count-1];
assert(last_pc->pc_offset() == PcDesc::upper_offset_limit, "sanity");
last_pc->set_pc_offset(content_size() + 1);
for (; last_pc + 1 < scopes_pcs_end(); last_pc += 1) {
last_pc[1] = last_pc[0];
}
assert(last_pc + 1 == scopes_pcs_end(), "must match exactly");
}
void nmethod::copy_scopes_data(u_char* buffer, int size) {
assert(scopes_data_size() >= size, "oob");
memcpy(scopes_data_begin(), buffer, size);
}
#ifdef ASSERT
static PcDesc* linear_search(nmethod* nm, int pc_offset, bool approximate) {
PcDesc* lower = nm->scopes_pcs_begin();
PcDesc* upper = nm->scopes_pcs_end();
lower += 1; // exclude initial sentinel
PcDesc* res = NULL;
for (PcDesc* p = lower; p < upper; p++) {
NOT_PRODUCT(--nmethod_stats.pc_desc_tests); // don't count this call to match_desc
if (match_desc(p, pc_offset, approximate)) {
if (res == NULL)
res = p;
else
res = (PcDesc*) badAddress;
}
}
return res;
}
#endif
PcDesc* nmethod::find_pc_desc_internal(address pc, bool approximate) {
address base_address = code_begin();
if ((pc < base_address) ||
(pc - base_address) >= (ptrdiff_t) PcDesc::upper_offset_limit) {
return NULL; // PC is wildly out of range
}
int pc_offset = (int) (pc - base_address);
PcDesc* res = _pc_desc_cache.find_pc_desc(pc_offset, approximate);
if (res != NULL) {
assert(res == linear_search(this, pc_offset, approximate), "cache ok");
return res;
}
PcDesc* lower = scopes_pcs_begin();
PcDesc* upper = scopes_pcs_end();
upper -= 1; // exclude final sentinel
if (lower >= upper) return NULL; // native method; no PcDescs at all
#define assert_LU_OK \
assert(lower->pc_offset() < pc_offset, "sanity"); \
assert(upper->pc_offset() >= pc_offset, "sanity")
assert_LU_OK;
PcDesc* mid = _pc_desc_cache.last_pc_desc();
NOT_PRODUCT(++nmethod_stats.pc_desc_searches);
if (mid->pc_offset() < pc_offset) {
lower = mid;
} else {
upper = mid;
}
const int LOG2_RADIX = 4 /*smaller steps in debug mode:*/ debug_only(-1);
const int RADIX = (1 << LOG2_RADIX);
for (int step = (1 << (LOG2_RADIX*3)); step > 1; step >>= LOG2_RADIX) {
while ((mid = lower + step) < upper) {
assert_LU_OK;
NOT_PRODUCT(++nmethod_stats.pc_desc_searches);
if (mid->pc_offset() < pc_offset) {
lower = mid;
} else {
upper = mid;
break;
}
}
assert_LU_OK;
}
while (true) {
assert_LU_OK;
mid = lower + 1;
NOT_PRODUCT(++nmethod_stats.pc_desc_searches);
if (mid->pc_offset() < pc_offset) {
lower = mid;
} else {
upper = mid;
break;
}
}
#undef assert_LU_OK
if (match_desc(upper, pc_offset, approximate)) {
assert(upper == linear_search(this, pc_offset, approximate), "search ok");
_pc_desc_cache.add_pc_desc(upper);
return upper;
} else {
assert(NULL == linear_search(this, pc_offset, approximate), "search ok");
return NULL;
}
}
bool nmethod::check_all_dependencies() {
bool found_check = false;
for (Dependencies::DepStream deps(this); deps.next(); ) {
if (deps.check_dependency() != NULL) {
found_check = true;
NOT_DEBUG(break);
}
}
return found_check; // tell caller if we found anything
}
bool nmethod::check_dependency_on(DepChange& changes) {
bool found_check = false; // set true if we are upset
for (Dependencies::DepStream deps(this); deps.next(); ) {
if (deps.spot_check_dependency_at(changes) != NULL) {
found_check = true;
NOT_DEBUG(break);
}
}
return found_check;
}
bool nmethod::is_evol_dependent_on(Klass* dependee) {
InstanceKlass *dependee_ik = InstanceKlass::cast(dependee);
Array<Method*>* dependee_methods = dependee_ik->methods();
for (Dependencies::DepStream deps(this); deps.next(); ) {
if (deps.type() == Dependencies::evol_method) {
Method* method = deps.method_argument(0);
for (int j = 0; j < dependee_methods->length(); j++) {
if (dependee_methods->at(j) == method) {
RC_TRACE(0x01000000,
("Found evol dependency of nmethod %s.%s(%s) compile_id=%d on method %s.%s(%s)",
_method->method_holder()->external_name(),
_method->name()->as_C_string(),
_method->signature()->as_C_string(), compile_id(),
method->method_holder()->external_name(),
method->name()->as_C_string(),
method->signature()->as_C_string()));
if (TraceDependencies || LogCompilation)
deps.log_dependency(dependee);
return true;
}
}
}
}
return false;
}
bool nmethod::is_dependent_on_method(Method* dependee) {
for (Dependencies::DepStream deps(this); deps.next(); ) {
if (deps.type() != Dependencies::evol_method)
continue;
Method* method = deps.method_argument(0);
if (method == dependee) return true;
}
return false;
}
bool nmethod::is_patchable_at(address instr_addr) {
assert(insts_contains(instr_addr), "wrong nmethod used");
if (is_zombie()) {
return false;
}
return true;
}
address nmethod::continuation_for_implicit_exception(address pc) {
int exception_offset = pc - code_begin();
int cont_offset = ImplicitExceptionTable(this).at( exception_offset );
#ifdef ASSERT
if (cont_offset == 0) {
Thread* thread = ThreadLocalStorage::get_thread_slow();
ResetNoHandleMark rnm; // Might be called from LEAF/QUICK ENTRY
HandleMark hm(thread);
ResourceMark rm(thread);
CodeBlob* cb = CodeCache::find_blob(pc);
assert(cb != NULL && cb == this, "");
ttyLocker ttyl;
tty->print_cr("implicit exception happened at " INTPTR_FORMAT, pc);
print();
method()->print_codes();
print_code();
print_pcs();
}
#endif
if (cont_offset == 0) {
return NULL;
}
return code_begin() + cont_offset;
}
void nmethod_init() {
assert(sizeof(nmethod) % oopSize == 0, "nmethod size must be multiple of a word");
}
nmethodLocker::nmethodLocker(address pc) {
CodeBlob* cb = CodeCache::find_blob(pc);
guarantee(cb != NULL && cb->is_nmethod(), "bad pc for a nmethod found");
_nm = (nmethod*)cb;
lock_nmethod(_nm);
}
void nmethodLocker::lock_nmethod(nmethod* nm, bool zombie_ok) {
if (nm == NULL) return;
Atomic::inc(&nm->_lock_count);
guarantee(zombie_ok || !nm->is_zombie(), "cannot lock a zombie method");
}
void nmethodLocker::unlock_nmethod(nmethod* nm) {
if (nm == NULL) return;
Atomic::dec(&nm->_lock_count);
guarantee(nm->_lock_count >= 0, "unmatched nmethod lock/unlock");
}
address nmethod::get_deopt_original_pc(const frame* fr) {
if (fr->cb() == NULL) return NULL;
nmethod* nm = fr->cb()->as_nmethod_or_null();
if (nm != NULL && nm->is_deopt_pc(fr->pc()))
return nm->get_original_pc(fr);
return NULL;
}
bool nmethod::is_method_handle_return(address return_pc) {
if (!has_method_handle_invokes()) return false;
PcDesc* pd = pc_desc_at(return_pc);
if (pd == NULL)
return false;
return pd->is_method_handle_invoke();
}
class VerifyOopsClosure: public OopClosure {
nmethod* _nm;
bool _ok;
public:
VerifyOopsClosure(nmethod* nm) : _nm(nm), _ok(true) { }
bool ok() { return _ok; }
virtual void do_oop(oop* p) {
if ((*p) == NULL || (*p)->is_oop()) return;
if (_ok) {
_nm->print_nmethod(true);
_ok = false;
}
tty->print_cr("*** non-oop " PTR_FORMAT " found at " PTR_FORMAT " (offset %d)",
(void *)(*p), (intptr_t)p, (int)((intptr_t)p - (intptr_t)_nm));
}
virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); }
};
void nmethod::verify() {
if (is_zombie() || is_not_entrant() || is_unloaded())
return;
NativeJump::check_verified_entry_alignment(entry_point(), verified_entry_point());
ResourceMark rm;
if (!CodeCache::contains(this)) {
fatal(err_msg("nmethod at " INTPTR_FORMAT " not in zone", this));
}
if(is_native_method() )
return;
nmethod* nm = CodeCache::find_nmethod(verified_entry_point());
if (nm != this) {
fatal(err_msg("findNMethod did not find this nmethod (" INTPTR_FORMAT ")",
this));
}
for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
if (! p->verify(this)) {
tty->print_cr("\t\tin nmethod at " INTPTR_FORMAT " (pcs)", this);
}
}
VerifyOopsClosure voc(this);
oops_do(&voc);
assert(voc.ok(), "embedded oops must be OK");
verify_scavenge_root_oops();
verify_scopes();
}
void nmethod::verify_interrupt_point(address call_site) {
bool is_installed = (method()->code() == this) // nmethod is in state 'in_use' and installed
|| !this->is_in_use(); // nmethod is installed, but not in 'in_use' state
if (is_installed) {
Thread *cur = Thread::current();
if (CompiledIC_lock->owner() == cur ||
((cur->is_VM_thread() || cur->is_ConcurrentGC_thread()) &&
SafepointSynchronize::is_at_safepoint())) {
CompiledIC_at(this, call_site);
CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
} else {
MutexLocker ml_verify (CompiledIC_lock);
CompiledIC_at(this, call_site);
}
}
PcDesc* pd = pc_desc_at(nativeCall_at(call_site)->return_address());
assert(pd != NULL, "PcDesc must exist");
for (ScopeDesc* sd = new ScopeDesc(this, pd->scope_decode_offset(),
pd->obj_decode_offset(), pd->should_reexecute(),
pd->return_oop());
!sd->is_top(); sd = sd->sender()) {
sd->verify();
}
}
void nmethod::verify_scopes() {
if( !method() ) return; // Runtime stubs have no scope
if (method()->is_native()) return; // Ignore stub methods.
RelocIterator iter((nmethod*)this);
while (iter.next()) {
address stub = NULL;
switch (iter.type()) {
case relocInfo::virtual_call_type:
verify_interrupt_point(iter.addr());
break;
case relocInfo::opt_virtual_call_type:
stub = iter.opt_virtual_call_reloc()->static_stub();
verify_interrupt_point(iter.addr());
break;
case relocInfo::static_call_type:
stub = iter.static_call_reloc()->static_stub();
break;
case relocInfo::runtime_call_type:
address destination = iter.reloc()->value();
break;
}
assert(stub == NULL || stub_contains(stub), "static call stub outside stub section");
}
}
#ifndef PRODUCT
class DebugScavengeRoot: public OopClosure {
nmethod* _nm;
bool _ok;
public:
DebugScavengeRoot(nmethod* nm) : _nm(nm), _ok(true) { }
bool ok() { return _ok; }
virtual void do_oop(oop* p) {
if ((*p) == NULL || !(*p)->is_scavengable()) return;
if (_ok) {
_nm->print_nmethod(true);
_ok = false;
}
tty->print_cr("*** scavengable oop " PTR_FORMAT " found at " PTR_FORMAT " (offset %d)",
(void *)(*p), (intptr_t)p, (int)((intptr_t)p - (intptr_t)_nm));
(*p)->print();
}
virtual void do_oop(narrowOop* p) { ShouldNotReachHere(); }
};
void nmethod::verify_scavenge_root_oops() {
if (UseG1GC) {
return;
}
if (!on_scavenge_root_list()) {
DebugScavengeRoot debug_scavenge_root(this);
oops_do(&debug_scavenge_root);
if (!debug_scavenge_root.ok())
fatal("found an unadvertised bad scavengable oop in the code cache");
}
assert(scavenge_root_not_marked(), "");
}
#endif // PRODUCT
void nmethod::print() const {
ResourceMark rm;
ttyLocker ttyl; // keep the following output all in one block
tty->print("Compiled method ");
if (is_compiled_by_c1()) {
tty->print("(c1) ");
} else if (is_compiled_by_c2()) {
tty->print("(c2) ");
} else if (is_compiled_by_shark()) {
tty->print("(shark) ");
} else {
tty->print("(nm) ");
}
print_on(tty, NULL);
if (WizardMode) {
tty->print("((nmethod*) " INTPTR_FORMAT ") ", this);
tty->print(" for method " INTPTR_FORMAT , (address)method());
tty->print(" { ");
if (is_in_use()) tty->print("in_use ");
if (is_not_entrant()) tty->print("not_entrant ");
if (is_zombie()) tty->print("zombie ");
if (is_unloaded()) tty->print("unloaded ");
if (on_scavenge_root_list()) tty->print("scavenge_root ");
tty->print_cr("}:");
}
if (size () > 0) tty->print_cr(" total in heap [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
(address)this,
(address)this + size(),
size());
if (relocation_size () > 0) tty->print_cr(" relocation [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
relocation_begin(),
relocation_end(),
relocation_size());
if (consts_size () > 0) tty->print_cr(" constants [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
consts_begin(),
consts_end(),
consts_size());
if (insts_size () > 0) tty->print_cr(" main code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
insts_begin(),
insts_end(),
insts_size());
if (stub_size () > 0) tty->print_cr(" stub code [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
stub_begin(),
stub_end(),
stub_size());
if (oops_size () > 0) tty->print_cr(" oops [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
oops_begin(),
oops_end(),
oops_size());
if (metadata_size () > 0) tty->print_cr(" metadata [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
metadata_begin(),
metadata_end(),
metadata_size());
if (scopes_data_size () > 0) tty->print_cr(" scopes data [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
scopes_data_begin(),
scopes_data_end(),
scopes_data_size());
if (scopes_pcs_size () > 0) tty->print_cr(" scopes pcs [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
scopes_pcs_begin(),
scopes_pcs_end(),
scopes_pcs_size());
if (dependencies_size () > 0) tty->print_cr(" dependencies [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
dependencies_begin(),
dependencies_end(),
dependencies_size());
if (handler_table_size() > 0) tty->print_cr(" handler table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
handler_table_begin(),
handler_table_end(),
handler_table_size());
if (nul_chk_table_size() > 0) tty->print_cr(" nul chk table [" INTPTR_FORMAT "," INTPTR_FORMAT "] = %d",
nul_chk_table_begin(),
nul_chk_table_end(),
nul_chk_table_size());
}
#ifndef PRODUCT
void nmethod::print_scopes() {
ResourceMark rm;
for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
if (p->scope_decode_offset() == DebugInformationRecorder::serialized_null)
continue;
ScopeDesc* sd = scope_desc_at(p->real_pc(this));
sd->print_on(tty, p);
}
}
void nmethod::print_dependencies() {
ResourceMark rm;
ttyLocker ttyl; // keep the following output all in one block
tty->print_cr("Dependencies:");
for (Dependencies::DepStream deps(this); deps.next(); ) {
deps.print_dependency();
Klass* ctxk = deps.context_type();
if (ctxk != NULL) {
if (ctxk->oop_is_instance() && ((InstanceKlass*)ctxk)->is_dependent_nmethod(this)) {
tty->print_cr(" [nmethod<=klass]%s", ctxk->external_name());
}
}
deps.log_dependency(); // put it into the xml log also
}
}
void nmethod::print_relocations() {
ResourceMark m; // in case methods get printed via the debugger
tty->print_cr("relocations:");
RelocIterator iter(this);
iter.print();
if (UseRelocIndex) {
jint* index_end = (jint*)relocation_end() - 1;
jint index_size = *index_end;
jint* index_start = (jint*)( (address)index_end - index_size );
tty->print_cr(" index @" INTPTR_FORMAT ": index_size=%d", index_start, index_size);
if (index_size > 0) {
jint* ip;
for (ip = index_start; ip+2 <= index_end; ip += 2)
tty->print_cr(" (%d %d) addr=" INTPTR_FORMAT " @" INTPTR_FORMAT,
ip[0],
ip[1],
header_end()+ip[0],
relocation_begin()-1+ip[1]);
for (; ip < index_end; ip++)
tty->print_cr(" (%d ?)", ip[0]);
tty->print_cr(" @" INTPTR_FORMAT ": index_size=%d", ip, *ip);
ip++;
tty->print_cr("reloc_end @" INTPTR_FORMAT ":", ip);
}
}
}
void nmethod::print_pcs() {
ResourceMark m; // in case methods get printed via debugger
tty->print_cr("pc-bytecode offsets:");
for (PcDesc* p = scopes_pcs_begin(); p < scopes_pcs_end(); p++) {
p->print(this);
}
}
#endif // PRODUCT
const char* nmethod::reloc_string_for(u_char* begin, u_char* end) {
RelocIterator iter(this, begin, end);
bool have_one = false;
while (iter.next()) {
have_one = true;
switch (iter.type()) {
case relocInfo::none: return "no_reloc";
case relocInfo::oop_type: {
stringStream st;
oop_Relocation* r = iter.oop_reloc();
oop obj = r->oop_value();
st.print("oop(");
if (obj == NULL) st.print("NULL");
else obj->print_value_on(&st);
st.print(")");
return st.as_string();
}
case relocInfo::metadata_type: {
stringStream st;
metadata_Relocation* r = iter.metadata_reloc();
Metadata* obj = r->metadata_value();
st.print("metadata(");
if (obj == NULL) st.print("NULL");
else obj->print_value_on(&st);
st.print(")");
return st.as_string();
}
case relocInfo::virtual_call_type: return "virtual_call";
case relocInfo::opt_virtual_call_type: return "optimized virtual_call";
case relocInfo::static_call_type: return "static_call";
case relocInfo::static_stub_type: return "static_stub";
case relocInfo::runtime_call_type: return "runtime_call";
case relocInfo::external_word_type: return "external_word";
case relocInfo::internal_word_type: return "internal_word";
case relocInfo::section_word_type: return "section_word";
case relocInfo::poll_type: return "poll";
case relocInfo::poll_return_type: return "poll_return";
case relocInfo::type_mask: return "type_bit_mask";
}
}
return have_one ? "other" : NULL;
}
ScopeDesc* nmethod::scope_desc_in(address begin, address end) {
PcDesc* p = pc_desc_near(begin+1);
if (p != NULL && p->real_pc(this) <= end) {
return new ScopeDesc(this, p->scope_decode_offset(),
p->obj_decode_offset(), p->should_reexecute(),
p->return_oop());
}
return NULL;
}
void nmethod::print_nmethod_labels(outputStream* stream, address block_begin) const {
if (block_begin == entry_point()) stream->print_cr("[Entry Point]");
if (block_begin == verified_entry_point()) stream->print_cr("[Verified Entry Point]");
if (block_begin == exception_begin()) stream->print_cr("[Exception Handler]");
if (block_begin == stub_begin()) stream->print_cr("[Stub Code]");
if (block_begin == deopt_handler_begin()) stream->print_cr("[Deopt Handler Code]");
if (has_method_handle_invokes())
if (block_begin == deopt_mh_handler_begin()) stream->print_cr("[Deopt MH Handler Code]");
if (block_begin == consts_begin()) stream->print_cr("[Constants]");
if (block_begin == entry_point()) {
methodHandle m = method();
if (m.not_null()) {
stream->print(" # ");
m->print_value_on(stream);
stream->cr();
}
if (m.not_null() && !is_osr_method()) {
ResourceMark rm;
int sizeargs = m->size_of_parameters();
BasicType* sig_bt = NEW_RESOURCE_ARRAY(BasicType, sizeargs);
VMRegPair* regs = NEW_RESOURCE_ARRAY(VMRegPair, sizeargs);
{
int sig_index = 0;
if (!m->is_static())
sig_bt[sig_index++] = T_OBJECT; // 'this'
for (SignatureStream ss(m->signature()); !ss.at_return_type(); ss.next()) {
BasicType t = ss.type();
sig_bt[sig_index++] = t;
if (type2size[t] == 2) {
sig_bt[sig_index++] = T_VOID;
} else {
assert(type2size[t] == 1, "size is 1 or 2");
}
}
assert(sig_index == sizeargs, "");
}
const char* spname = "sp"; // make arch-specific?
intptr_t out_preserve = SharedRuntime::java_calling_convention(sig_bt, regs, sizeargs, false);
int stack_slot_offset = this->frame_size() * wordSize;
int tab1 = 14, tab2 = 24;
int sig_index = 0;
int arg_index = (m->is_static() ? 0 : -1);
bool did_old_sp = false;
for (SignatureStream ss(m->signature()); !ss.at_return_type(); ) {
bool at_this = (arg_index == -1);
bool at_old_sp = false;
BasicType t = (at_this ? T_OBJECT : ss.type());
assert(t == sig_bt[sig_index], "sigs in sync");
if (at_this)
stream->print(" # this: ");
else
stream->print(" # parm%d: ", arg_index);
stream->move_to(tab1);
VMReg fst = regs[sig_index].first();
VMReg snd = regs[sig_index].second();
if (fst->is_reg()) {
stream->print("%s", fst->name());
if (snd->is_valid()) {
stream->print(":%s", snd->name());
}
} else if (fst->is_stack()) {
int offset = fst->reg2stack() * VMRegImpl::stack_slot_size + stack_slot_offset;
if (offset == stack_slot_offset) at_old_sp = true;
stream->print("[%s+0x%x]", spname, offset);
} else {
stream->print("reg%d:%d??", (int)(intptr_t)fst, (int)(intptr_t)snd);
}
stream->print(" ");
stream->move_to(tab2);
stream->print("= ");
if (at_this) {
m->method_holder()->print_value_on(stream);
} else {
bool did_name = false;
if (!at_this && ss.is_object()) {
Symbol* name = ss.as_symbol_or_null();
if (name != NULL) {
name->print_value_on(stream);
did_name = true;
}
}
if (!did_name)
stream->print("%s", type2name(t));
}
if (at_old_sp) {
stream->print(" (%s of caller)", spname);
did_old_sp = true;
}
stream->cr();
sig_index += type2size[t];
arg_index += 1;
if (!at_this) ss.next();
}
if (!did_old_sp) {
stream->print(" # ");
stream->move_to(tab1);
stream->print("[%s+0x%x]", spname, stack_slot_offset);
stream->print(" (%s of caller)", spname);
stream->cr();
}
}
}
}
void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin, u_char* end) {
address base = code_begin();
OopMapSet* oms = oop_maps();
if (oms != NULL) {
for (int i = 0, imax = oms->size(); i < imax; i++) {
OopMap* om = oms->at(i);
address pc = base + om->offset();
if (pc > begin) {
if (pc <= end) {
st->move_to(column);
st->print("; ");
om->print_on(st);
}
break;
}
}
}
ScopeDesc* sd = scope_desc_in(begin, end);
if (sd != NULL) {
st->move_to(column);
if (sd->bci() == SynchronizationEntryBCI) {
st->print(";*synchronization entry");
} else {
if (sd->method() == NULL) {
st->print("method is NULL");
} else if (sd->method()->is_native()) {
st->print("method is native");
} else {
Bytecodes::Code bc = sd->method()->java_code_at(sd->bci());
st->print(";*%s", Bytecodes::name(bc));
switch (bc) {
case Bytecodes::_invokevirtual:
case Bytecodes::_invokespecial:
case Bytecodes::_invokestatic:
case Bytecodes::_invokeinterface:
{
Bytecode_invoke invoke(sd->method(), sd->bci());
st->print(" ");
if (invoke.name() != NULL)
invoke.name()->print_symbol_on(st);
else
st->print("<UNKNOWN>");
break;
}
case Bytecodes::_getfield:
case Bytecodes::_putfield:
case Bytecodes::_getstatic:
case Bytecodes::_putstatic:
{
Bytecode_field field(sd->method(), sd->bci());
st->print(" ");
if (field.name() != NULL)
field.name()->print_symbol_on(st);
else
st->print("<UNKNOWN>");
}
}
}
}
for (;sd != NULL; sd = sd->sender()) {
st->move_to(column);
st->print("; -");
if (sd->method() == NULL) {
st->print("method is NULL");
} else {
sd->method()->print_short_name(st);
}
int lineno = sd->method()->line_number_from_bci(sd->bci());
if (lineno != -1) {
st->print("@%d (line %d)", sd->bci(), lineno);
} else {
st->print("@%d", sd->bci());
}
st->cr();
}
}
const char* str = reloc_string_for(begin, end);
if (str != NULL) {
if (sd != NULL) st->cr();
st->move_to(column);
st->print("; {%s}", str);
}
int cont_offset = ImplicitExceptionTable(this).at(begin - code_begin());
if (cont_offset != 0) {
st->move_to(column);
st->print("; implicit exception: dispatches to " INTPTR_FORMAT, code_begin() + cont_offset);
}
}
#ifndef PRODUCT
void nmethod::print_value_on(outputStream* st) const {
st->print("nmethod");
print_on(st, NULL);
}
void nmethod::print_calls(outputStream* st) {
RelocIterator iter(this);
while (iter.next()) {
switch (iter.type()) {
case relocInfo::virtual_call_type:
case relocInfo::opt_virtual_call_type: {
VerifyMutexLocker mc(CompiledIC_lock);
CompiledIC_at(&iter)->print();
break;
}
case relocInfo::static_call_type:
st->print_cr("Static call at " INTPTR_FORMAT, iter.reloc()->addr());
compiledStaticCall_at(iter.reloc())->print();
break;
}
}
}
void nmethod::print_handler_table() {
ExceptionHandlerTable(this).print();
}
void nmethod::print_nul_chk_table() {
ImplicitExceptionTable(this).print(code_begin());
}
void nmethod::print_statistics() {
ttyLocker ttyl;
if (xtty != NULL) xtty->head("statistics type='nmethod'");
nmethod_stats.print_native_nmethod_stats();
nmethod_stats.print_nmethod_stats();
DebugInformationRecorder::print_statistics();
nmethod_stats.print_pc_stats();
Dependencies::print_statistics();
if (xtty != NULL) xtty->tail("statistics");
}
#endif // PRODUCT
C:\hotspot-69087d08d473\src\share\vm/code/nmethod.hpp
#ifndef SHARE_VM_CODE_NMETHOD_HPP
#define SHARE_VM_CODE_NMETHOD_HPP
#include "code/codeBlob.hpp"
#include "code/pcDesc.hpp"
#include "oops/metadata.hpp"
class ExceptionCache : public CHeapObj<mtCode> {
friend class VMStructs;
private:
enum { cache_size = 16 };
Klass* _exception_type;
address _pc[cache_size];
address _handler[cache_size];
volatile int _count;
ExceptionCache* _next;
address pc_at(int index) { assert(index >= 0 && index < count(),""); return _pc[index]; }
void set_pc_at(int index, address a) { assert(index >= 0 && index < cache_size,""); _pc[index] = a; }
address handler_at(int index) { assert(index >= 0 && index < count(),""); return _handler[index]; }
void set_handler_at(int index, address a) { assert(index >= 0 && index < cache_size,""); _handler[index] = a; }
int count() { return OrderAccess::load_acquire(&_count); }
void increment_count() { OrderAccess::release_store(&_count, _count + 1); }
public:
ExceptionCache(Handle exception, address pc, address handler);
Klass* exception_type() { return _exception_type; }
ExceptionCache* next() { return _next; }
void set_next(ExceptionCache *ec) { _next = ec; }
address match(Handle exception, address pc);
bool match_exception_with_space(Handle exception) ;
address test_address(address addr);
bool add_address_and_handler(address addr, address handler) ;
};
class PcDescCache VALUE_OBJ_CLASS_SPEC {
friend class VMStructs;
private:
enum { cache_size = 4 };
typedef PcDesc* PcDescPtr;
volatile PcDescPtr _pc_descs[cache_size]; // last cache_size pc_descs found
public:
PcDescCache() { debug_only(_pc_descs[0] = NULL); }
void reset_to(PcDesc* initial_pc_desc);
PcDesc* find_pc_desc(int pc_offset, bool approximate);
void add_pc_desc(PcDesc* pc_desc);
PcDesc* last_pc_desc() { return _pc_descs[0]; }
};
class Dependencies;
class ExceptionHandlerTable;
class ImplicitExceptionTable;
class AbstractCompiler;
class xmlStream;
class nmethod : public CodeBlob {
friend class VMStructs;
friend class NMethodSweeper;
friend class CodeCache; // scavengable oops
private:
static unsigned char _global_unloading_clock;
Method* _method;
int _entry_bci; // != InvocationEntryBci if this nmethod is an on-stack replacement method
jmethodID _jmethod_id; // Cache of method()->jmethod_id()
nmethod* _osr_link; // from InstanceKlass::osr_nmethods_head
union {
nmethod* _unloading_next;
nmethod* _scavenge_root_link; // from CodeCache::scavenge_root_nmethods
};
static nmethod* volatile _oops_do_mark_nmethods;
nmethod* volatile _oops_do_mark_link;
AbstractCompiler* _compiler; // The compiler which compiled this nmethod
address _entry_point; // entry point with class check
address _verified_entry_point; // entry point without class check
address _osr_entry_point; // entry point for on stack replacement
int _exception_offset;
int _deoptimize_offset;
int _deoptimize_mh_offset;
int _unwind_handler_offset;
#ifdef HAVE_DTRACE_H
int _trap_offset;
#endif // def HAVE_DTRACE_H
int _consts_offset;
int _stub_offset;
int _oops_offset; // offset to where embedded oop table begins (inside data)
int _metadata_offset; // embedded meta data table
int _scopes_data_offset;
int _scopes_pcs_offset;
int _dependencies_offset;
int _handler_table_offset;
int _nul_chk_table_offset;
int _nmethod_end_offset;
int _orig_pc_offset;
int _compile_id; // which compilation made this nmethod
int _comp_level; // compilation level
bool _has_flushed_dependencies; // Used for maintenance of dependencies (CodeCache_lock)
bool _marked_for_reclamation; // Used by NMethodSweeper (set only by sweeper)
bool _marked_for_deoptimization; // Used for stack deoptimization
bool _unload_reported;
unsigned int _has_unsafe_access:1; // May fault due to unsafe access.
unsigned int _has_method_handle_invokes:1; // Has this method MethodHandle invokes?
unsigned int _lazy_critical_native:1; // Lazy JNI critical native
unsigned int _has_wide_vectors:1; // Preserve wide vectors at safepoints
volatile unsigned char _state; // {alive, not_entrant, zombie, unloaded}
volatile unsigned char _unloading_clock; // Incremented after GC unloaded/cleaned the nmethod
#ifdef ASSERT
bool _oops_are_stale; // indicates that it's no longer safe to access oops section
#endif
enum { in_use = 0, // executable nmethod
not_entrant = 1, // marked for deoptimization but activations may still exist,
zombie = 2, // no activations exist, nmethod is ready for purge
unloaded = 3 }; // there should be no activations, should not be called,
jbyte _scavenge_root_state;
#if INCLUDE_RTM_OPT
RTMState _rtm_state;
#endif
jint _lock_count;
long _stack_traversal_mark;
int _hotness_counter;
ExceptionCache * volatile _exception_cache;
PcDescCache _pc_desc_cache;
ByteSize _native_receiver_sp_offset;
ByteSize _native_basic_lock_sp_offset;
friend class nmethodLocker;
nmethod(Method* method,
int nmethod_size,
int compile_id,
CodeOffsets* offsets,
CodeBuffer *code_buffer,
int frame_size,
ByteSize basic_lock_owner_sp_offset, /* synchronized natives only */
ByteSize basic_lock_sp_offset, /* synchronized natives only */
OopMapSet* oop_maps);
#ifdef HAVE_DTRACE_H
nmethod(Method* method,
int nmethod_size,
CodeOffsets* offsets,
CodeBuffer *code_buffer,
int frame_size);
#endif // def HAVE_DTRACE_H
nmethod(Method* method,
int nmethod_size,
int compile_id,
int entry_bci,
CodeOffsets* offsets,
int orig_pc_offset,
DebugInformationRecorder *recorder,
Dependencies* dependencies,
CodeBuffer *code_buffer,
int frame_size,
OopMapSet* oop_maps,
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* nul_chk_table,
AbstractCompiler* compiler,
int comp_level);
void* operator new(size_t size, int nmethod_size) throw();
const char* reloc_string_for(u_char* begin, u_char* end);
bool make_not_entrant_or_zombie(unsigned int state);
void inc_decompile_count();
void add_exception_cache_entry(ExceptionCache* new_entry);
ExceptionCache* exception_cache_entry_for_exception(Handle exception);
void post_compiled_method_unload();
void init_defaults();
public:
static nmethod* new_nmethod(methodHandle method,
int compile_id,
int entry_bci,
CodeOffsets* offsets,
int orig_pc_offset,
DebugInformationRecorder* recorder,
Dependencies* dependencies,
CodeBuffer *code_buffer,
int frame_size,
OopMapSet* oop_maps,
ExceptionHandlerTable* handler_table,
ImplicitExceptionTable* nul_chk_table,
AbstractCompiler* compiler,
int comp_level);
static nmethod* new_native_nmethod(methodHandle method,
int compile_id,
CodeBuffer *code_buffer,
int vep_offset,
int frame_complete,
int frame_size,
ByteSize receiver_sp_offset,
ByteSize basic_lock_sp_offset,
OopMapSet* oop_maps);
#ifdef HAVE_DTRACE_H
static nmethod* new_dtrace_nmethod(methodHandle method,
CodeBuffer *code_buffer,
int vep_offset,
int trap_offset,
int frame_complete,
int frame_size);
int trap_offset() const { return _trap_offset; }
address trap_address() const { return insts_begin() + _trap_offset; }
#endif // def HAVE_DTRACE_H
Method* method() const { return _method; }
AbstractCompiler* compiler() const { return _compiler; }
bool is_nmethod() const { return true; }
bool is_java_method() const { return !method()->is_native(); }
bool is_native_method() const { return method()->is_native(); }
bool is_osr_method() const { return _entry_bci != InvocationEntryBci; }
bool is_compiled_by_c1() const;
bool is_compiled_by_c2() const;
bool is_compiled_by_shark() const;
address consts_begin () const { return header_begin() + _consts_offset ; }
address consts_end () const { return header_begin() + code_offset() ; }
address insts_begin () const { return header_begin() + code_offset() ; }
address insts_end () const { return header_begin() + _stub_offset ; }
address stub_begin () const { return header_begin() + _stub_offset ; }
address stub_end () const { return header_begin() + _oops_offset ; }
address exception_begin () const { return header_begin() + _exception_offset ; }
address deopt_handler_begin () const { return header_begin() + _deoptimize_offset ; }
address deopt_mh_handler_begin() const { return header_begin() + _deoptimize_mh_offset ; }
address unwind_handler_begin () const { return _unwind_handler_offset != -1 ? (header_begin() + _unwind_handler_offset) : NULL; }
oop* oops_begin () const { return (oop*) (header_begin() + _oops_offset) ; }
oop* oops_end () const { return (oop*) (header_begin() + _metadata_offset) ; }
Metadata** metadata_begin () const { return (Metadata**) (header_begin() + _metadata_offset) ; }
Metadata** metadata_end () const { return (Metadata**) (header_begin() + _scopes_data_offset) ; }
address scopes_data_begin () const { return header_begin() + _scopes_data_offset ; }
address scopes_data_end () const { return header_begin() + _scopes_pcs_offset ; }
PcDesc* scopes_pcs_begin () const { return (PcDesc*)(header_begin() + _scopes_pcs_offset ); }
PcDesc* scopes_pcs_end () const { return (PcDesc*)(header_begin() + _dependencies_offset) ; }
address dependencies_begin () const { return header_begin() + _dependencies_offset ; }
address dependencies_end () const { return header_begin() + _handler_table_offset ; }
address handler_table_begin () const { return header_begin() + _handler_table_offset ; }
address handler_table_end () const { return header_begin() + _nul_chk_table_offset ; }
address nul_chk_table_begin () const { return header_begin() + _nul_chk_table_offset ; }
address nul_chk_table_end () const { return header_begin() + _nmethod_end_offset ; }
int consts_size () const { return consts_end () - consts_begin (); }
int insts_size () const { return insts_end () - insts_begin (); }
int stub_size () const { return stub_end () - stub_begin (); }
int oops_size () const { return (address) oops_end () - (address) oops_begin (); }
int metadata_size () const { return (address) metadata_end () - (address) metadata_begin (); }
int scopes_data_size () const { return scopes_data_end () - scopes_data_begin (); }
int scopes_pcs_size () const { return (intptr_t) scopes_pcs_end () - (intptr_t) scopes_pcs_begin (); }
int dependencies_size () const { return dependencies_end () - dependencies_begin (); }
int handler_table_size() const { return handler_table_end() - handler_table_begin(); }
int nul_chk_table_size() const { return nul_chk_table_end() - nul_chk_table_begin(); }
int total_size () const;
void dec_hotness_counter() { _hotness_counter--; }
void set_hotness_counter(int val) { _hotness_counter = val; }
int hotness_counter() const { return _hotness_counter; }
bool consts_contains (address addr) const { return consts_begin () <= addr && addr < consts_end (); }
bool insts_contains (address addr) const { return insts_begin () <= addr && addr < insts_end (); }
bool stub_contains (address addr) const { return stub_begin () <= addr && addr < stub_end (); }
bool oops_contains (oop* addr) const { return oops_begin () <= addr && addr < oops_end (); }
bool metadata_contains (Metadata** addr) const { return metadata_begin () <= addr && addr < metadata_end (); }
bool scopes_data_contains (address addr) const { return scopes_data_begin () <= addr && addr < scopes_data_end (); }
bool scopes_pcs_contains (PcDesc* addr) const { return scopes_pcs_begin () <= addr && addr < scopes_pcs_end (); }
bool handler_table_contains(address addr) const { return handler_table_begin() <= addr && addr < handler_table_end(); }
bool nul_chk_table_contains(address addr) const { return nul_chk_table_begin() <= addr && addr < nul_chk_table_end(); }
address entry_point() const { return _entry_point; } // normal entry point
address verified_entry_point() const { return _verified_entry_point; } // if klass is correct
bool is_in_use() const { return _state == in_use; }
bool is_alive() const { unsigned char s = _state; return s == in_use || s == not_entrant; }
bool is_not_entrant() const { return _state == not_entrant; }
bool is_zombie() const { return _state == zombie; }
bool is_unloaded() const { return _state == unloaded; }
#if INCLUDE_RTM_OPT
RTMState rtm_state() const { return _rtm_state; }
void set_rtm_state(RTMState state) { _rtm_state = state; }
#endif
bool make_not_entrant() {
assert(!method()->is_method_handle_intrinsic(), "Cannot make MH intrinsic not entrant");
return make_not_entrant_or_zombie(not_entrant);
}
bool make_zombie() { return make_not_entrant_or_zombie(zombie); }
bool unload_reported() { return _unload_reported; }
void set_unload_reported() { _unload_reported = true; }
void set_unloading_next(nmethod* next) { _unloading_next = next; }
nmethod* unloading_next() { return _unloading_next; }
static unsigned char global_unloading_clock() { return _global_unloading_clock; }
static void increase_unloading_clock();
void set_unloading_clock(unsigned char unloading_clock);
unsigned char unloading_clock();
bool is_marked_for_deoptimization() const { return _marked_for_deoptimization; }
void mark_for_deoptimization() { _marked_for_deoptimization = true; }
void make_unloaded(BoolObjectClosure* is_alive, oop cause);
bool has_dependencies() { return dependencies_size() != 0; }
void flush_dependencies(BoolObjectClosure* is_alive);
bool has_flushed_dependencies() { return _has_flushed_dependencies; }
void set_has_flushed_dependencies() {
assert(!has_flushed_dependencies(), "should only happen once");
_has_flushed_dependencies = 1;
}
bool is_marked_for_reclamation() const { return _marked_for_reclamation; }
void mark_for_reclamation() { _marked_for_reclamation = 1; }
bool has_unsafe_access() const { return _has_unsafe_access; }
void set_has_unsafe_access(bool z) { _has_unsafe_access = z; }
bool has_method_handle_invokes() const { return _has_method_handle_invokes; }
void set_has_method_handle_invokes(bool z) { _has_method_handle_invokes = z; }
bool is_lazy_critical_native() const { return _lazy_critical_native; }
void set_lazy_critical_native(bool z) { _lazy_critical_native = z; }
bool has_wide_vectors() const { return _has_wide_vectors; }
void set_has_wide_vectors(bool z) { _has_wide_vectors = z; }
int comp_level() const { return _comp_level; }
oop oop_at(int index) const { return index == 0 ? (oop) NULL: *oop_addr_at(index); }
oop* oop_addr_at(int index) const { // for GC
assert(index > 0 && index <= oops_size(), "must be a valid non-zero index");
assert(!_oops_are_stale, "oops are stale");
return &oops_begin()[index - 1];
}
Metadata* metadata_at(int index) const { return index == 0 ? NULL: *metadata_addr_at(index); }
Metadata** metadata_addr_at(int index) const { // for GC
assert(index > 0 && index <= metadata_size(), "must be a valid non-zero index");
return &metadata_begin()[index - 1];
}
void copy_values(GrowableArray<jobject>* oops);
void copy_values(GrowableArray<Metadata*>* metadata);
private:
void fix_oop_relocations(address begin, address end, bool initialize_immediates);
inline void initialize_immediate_oop(oop* dest, jobject handle);
public:
void fix_oop_relocations(address begin, address end) { fix_oop_relocations(begin, end, false); }
void fix_oop_relocations() { fix_oop_relocations(NULL, NULL, false); }
void verify_oop_relocations();
bool is_at_poll_return(address pc);
bool is_at_poll_or_poll_return(address pc);
bool on_scavenge_root_list() const { return (_scavenge_root_state & 1) != 0; }
protected:
enum { sl_on_list = 0x01, sl_marked = 0x10 };
void set_on_scavenge_root_list() { _scavenge_root_state = sl_on_list; }
void clear_on_scavenge_root_list() { _scavenge_root_state = 0; }
#ifndef PRODUCT
void set_scavenge_root_marked() { _scavenge_root_state |= sl_marked; }
void clear_scavenge_root_marked() { _scavenge_root_state &= ~sl_marked; }
bool scavenge_root_not_marked() { return (_scavenge_root_state &~ sl_on_list) == 0; }
#endif //PRODUCT
nmethod* scavenge_root_link() const { return _scavenge_root_link; }
void set_scavenge_root_link(nmethod *n) { _scavenge_root_link = n; }
public:
long stack_traversal_mark() { return _stack_traversal_mark; }
void set_stack_traversal_mark(long l) { _stack_traversal_mark = l; }
ExceptionCache* exception_cache() const { return _exception_cache; }
void set_exception_cache(ExceptionCache *ec) { _exception_cache = ec; }
void release_set_exception_cache(ExceptionCache *ec) { OrderAccess::release_store_ptr(&_exception_cache, ec); }
address handler_for_exception_and_pc(Handle exception, address pc);
void add_handler_for_exception_and_pc(Handle exception, address pc, address handler);
void clean_exception_cache(BoolObjectClosure* is_alive);
address continuation_for_implicit_exception(address pc);
int osr_entry_bci() const { assert(is_osr_method(), "wrong kind of nmethod"); return _entry_bci; }
address osr_entry() const { assert(is_osr_method(), "wrong kind of nmethod"); return _osr_entry_point; }
void invalidate_osr_method();
nmethod* osr_link() const { return _osr_link; }
void set_osr_link(nmethod *n) { _osr_link = n; }
bool can_be_deoptimized() const { return is_java_method(); }
void clear_inline_caches();
void clear_ic_stubs();
void cleanup_inline_caches();
bool inlinecache_check_contains(address addr) const {
return (addr >= code_begin() && addr < verified_entry_point());
}
void verify_clean_inline_caches();
int verify_icholder_relocations();
void verify_metadata_loaders(address low_boundary, BoolObjectClosure* is_alive);
protected:
void flush();
public:
bool is_locked_by_vm() const { return _lock_count >0; }
void mark_as_seen_on_stack();
bool can_convert_to_zombie();
void set_method(Method* method) { _method = method; }
void do_unloading(BoolObjectClosure* is_alive, bool unloading_occurred);
bool do_unloading_parallel(BoolObjectClosure* is_alive, bool unloading_occurred);
void do_unloading_parallel_postponed(BoolObjectClosure* is_alive, bool unloading_occurred);
private:
bool can_unload(BoolObjectClosure* is_alive, oop* root, bool unloading_occurred);
bool unload_if_dead_at(RelocIterator *iter_at_oop, BoolObjectClosure* is_alive, bool unloading_occurred);
void mark_metadata_on_stack_at(RelocIterator* iter_at_metadata);
void mark_metadata_on_stack_non_relocs();
public:
void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map,
OopClosure* f);
void oops_do(OopClosure* f) { oops_do(f, false); }
void oops_do(OopClosure* f, bool allow_zombie);
bool detect_scavenge_root_oops();
void verify_scavenge_root_oops() PRODUCT_RETURN;
bool test_set_oops_do_mark();
static void oops_do_marking_prologue();
static void oops_do_marking_epilogue();
static bool oops_do_marking_is_active() { return _oops_do_mark_nmethods != NULL; }
bool test_oops_do_mark() { return _oops_do_mark_link != NULL; }
ScopeDesc* scope_desc_at(address pc);
private:
ScopeDesc* scope_desc_in(address begin, address end);
address* orig_pc_addr(const frame* fr) { return (address*) ((address)fr->unextended_sp() + _orig_pc_offset); }
PcDesc* find_pc_desc_internal(address pc, bool approximate);
PcDesc* find_pc_desc(address pc, bool approximate) {
PcDesc* desc = _pc_desc_cache.last_pc_desc();
if (desc != NULL && desc->pc_offset() == pc - code_begin()) {
return desc;
}
return find_pc_desc_internal(pc, approximate);
}
public:
PcDesc* pc_desc_at(address pc) { return find_pc_desc(pc, false); }
PcDesc* pc_desc_near(address pc) { return find_pc_desc(pc, true); }
public:
void copy_scopes_pcs(PcDesc* pcs, int count);
void copy_scopes_data(address buffer, int size);
bool is_deopt_pc (address pc) { return is_deopt_entry(pc) || is_deopt_mh_entry(pc); }
bool is_deopt_entry (address pc) { return pc == deopt_handler_begin(); }
bool is_deopt_mh_entry(address pc) { return pc == deopt_mh_handler_begin(); }
address get_original_pc(const frame* fr) { return *orig_pc_addr(fr); }
void set_original_pc(const frame* fr, address pc) { *orig_pc_addr(fr) = pc; }
static address get_deopt_original_pc(const frame* fr);
bool is_method_handle_return(address return_pc);
void post_compiled_method_load_event();
jmethodID get_and_cache_jmethod_id();
void verify();
void verify_scopes();
void verify_interrupt_point(address interrupt_point);
void print() const;
void print_relocations() PRODUCT_RETURN;
void print_pcs() PRODUCT_RETURN;
void print_scopes() PRODUCT_RETURN;
void print_dependencies() PRODUCT_RETURN;
void print_value_on(outputStream* st) const PRODUCT_RETURN;
void print_calls(outputStream* st) PRODUCT_RETURN;
void print_handler_table() PRODUCT_RETURN;
void print_nul_chk_table() PRODUCT_RETURN;
void print_nmethod(bool print_code);
virtual void print_on(outputStream* st) const { CodeBlob::print_on(st); }
void print_on(outputStream* st, const char* msg) const;
void log_identity(xmlStream* log) const;
void log_new_nmethod() const;
void log_state_change() const;
virtual void print_block_comment(outputStream* stream, address block_begin) const {
print_nmethod_labels(stream, block_begin);
CodeBlob::print_block_comment(stream, block_begin);
}
void print_nmethod_labels(outputStream* stream, address block_begin) const;
void print_code_comment_on(outputStream* st, int column, address begin, address end);
static void print_statistics() PRODUCT_RETURN;
int compile_id() const { return _compile_id; }
const char* compile_kind() const;
oop embeddedOop_at(address p);
bool check_all_dependencies();
bool check_dependency_on(DepChange& changes);
bool is_evol_dependent_on(Klass* dependee);
bool is_dependent_on_method(Method* dependee);
bool is_patchable_at(address instr_address);
ByteSize native_receiver_sp_offset() {
return _native_receiver_sp_offset;
}
ByteSize native_basic_lock_sp_offset() {
return _native_basic_lock_sp_offset;
}
static int verified_entry_point_offset() { return offset_of(nmethod, _verified_entry_point); }
static int osr_entry_point_offset() { return offset_of(nmethod, _osr_entry_point); }
static int entry_bci_offset() { return offset_of(nmethod, _entry_bci); }
static void mark_on_stack(nmethod* nm) {
nm->metadata_do(Metadata::mark_on_stack);
}
void metadata_do(void f(Metadata*));
};
class nmethodLocker : public StackObj {
nmethod* _nm;
public:
static void lock_nmethod(nmethod* nm, bool zombie_ok = false);
static void unlock_nmethod(nmethod* nm); // (ditto)
nmethodLocker(address pc); // derive nm from pc
nmethodLocker(nmethod *nm) { _nm = nm; lock_nmethod(_nm); }
nmethodLocker() { _nm = NULL; }
~nmethodLocker() { unlock_nmethod(_nm); }
nmethod* code() { return _nm; }
void set_code(nmethod* new_nm) {
unlock_nmethod(_nm); // note: This works even if _nm==new_nm.
_nm = new_nm;
lock_nmethod(_nm);
}
};
#endif // SHARE_VM_CODE_NMETHOD_HPP
C:\hotspot-69087d08d473\src\share\vm/code/oopRecorder.cpp
#include "precompiled.hpp"
#include "ci/ciEnv.hpp"
#include "ci/ciInstance.hpp"
#include "ci/ciMetadata.hpp"
#include "code/oopRecorder.hpp"
#include "memory/allocation.inline.hpp"
#include "oops/oop.inline.hpp"
#ifdef ASSERT
template <class T> int ValueRecorder<T>::_find_index_calls = 0;
template <class T> int ValueRecorder<T>::_hit_indexes = 0;
template <class T> int ValueRecorder<T>::_missed_indexes = 0;
#endif //ASSERT
template <class T> ValueRecorder<T>::ValueRecorder(Arena* arena) {
_handles = NULL;
_indexes = NULL;
_arena = arena;
_complete = false;
}
template <class T> template <class X> ValueRecorder<T>::IndexCache<X>::IndexCache() {
assert(first_index > 0, "initial zero state of cache must be invalid index");
Copy::zero_to_bytes(&_cache[0], sizeof(_cache));
}
template <class T> int ValueRecorder<T>::size() {
_complete = true;
if (_handles == NULL) return 0;
return _handles->length() * sizeof(T);
}
template <class T> void ValueRecorder<T>::copy_values_to(nmethod* nm) {
assert(_complete, "must be frozen");
maybe_initialize(); // get non-null handles, even if we have no oops
nm->copy_values(_handles);
}
template <class T> void ValueRecorder<T>::maybe_initialize() {
if (_handles == NULL) {
if (_arena != NULL) {
_handles = new(_arena) GrowableArray<T>(_arena, 10, 0, 0);
_no_finds = new(_arena) GrowableArray<int>( _arena, 10, 0, 0);
} else {
_handles = new GrowableArray<T>(10, 0, 0);
_no_finds = new GrowableArray<int>( 10, 0, 0);
}
}
}
template <class T> T ValueRecorder<T>::at(int index) {
if (index == null_index) return NULL;
return _handles->at(index - first_index);
}
template <class T> int ValueRecorder<T>::add_handle(T h, bool make_findable) {
assert(!_complete, "cannot allocate more elements after size query");
maybe_initialize();
int index = _handles->length() + first_index;
_handles->append(h);
assert(!(make_findable && !is_real(h)), "nulls are not findable");
if (make_findable) {
if (_indexes != NULL) {
int* cloc = _indexes->cache_location(h);
_indexes->set_cache_location_index(cloc, index);
} else if (index == index_cache_threshold && _arena != NULL) {
_indexes = new(_arena) IndexCache<T>();
for (int i = 0; i < _handles->length(); i++) {
int index0 = i + first_index;
if (_no_finds->contains(index0)) continue;
int* cloc = _indexes->cache_location(_handles->at(i));
_indexes->set_cache_location_index(cloc, index0);
}
}
} else if (is_real(h)) {
_no_finds->append(index);
}
return index;
}
template <class T> int ValueRecorder<T>::maybe_find_index(T h) {
debug_only(_find_index_calls++);
assert(!_complete, "cannot allocate more elements after size query");
maybe_initialize();
if (h == NULL) return null_index;
assert(is_real(h), "must be valid");
int* cloc = (_indexes == NULL)? NULL: _indexes->cache_location(h);
if (cloc != NULL) {
int cindex = _indexes->cache_location_index(cloc);
if (cindex == 0) {
return -1; // We know this handle is completely new.
}
if (cindex >= first_index && _handles->at(cindex - first_index) == h) {
debug_only(_hit_indexes++);
return cindex;
}
if (!_indexes->cache_location_collision(cloc)) {
return -1; // We know the current cache occupant is unique to that cloc.
}
}
for (int i = _handles->length() - 1; i >= 0; i--) {
if (_handles->at(i) == h) {
int findex = i + first_index;
if (_no_finds->contains(findex)) continue; // oops; skip this one
if (cloc != NULL) {
_indexes->set_cache_location_index(cloc, findex);
}
debug_only(_missed_indexes++);
return findex;
}
}
return -1;
}
template class ValueRecorder<Metadata*>;
template class ValueRecorder<jobject>;
C:\hotspot-69087d08d473\src\share\vm/code/oopRecorder.hpp
#ifndef SHARE_VM_CODE_OOPRECORDER_HPP
#define SHARE_VM_CODE_OOPRECORDER_HPP
#include "memory/universe.hpp"
#include "runtime/handles.hpp"
#include "utilities/growableArray.hpp"
class CodeBlob;
template <class T> class ValueRecorder : public StackObj {
public:
ValueRecorder(Arena* arena = NULL);
int allocate_index(T h) {
return add_handle(h, false);
}
int find_index(T h) {
int index = maybe_find_index(h);
if (index < 0) { // previously unallocated
index = add_handle(h, true);
}
return index;
}
int size();
T at(int index);
int count() {
if (_handles == NULL) return 0;
return _handles->length() + first_index;
}
bool is_real(T h) {
return h != NULL && h != (T)Universe::non_oop_word();
}
void copy_values_to(nmethod* nm);
bool is_unused() { return _handles == NULL && !_complete; }
#ifdef ASSERT
bool is_complete() { return _complete; }
#endif
private:
int maybe_find_index(T h);
template <class X> class IndexCache : public ResourceObj {
friend class ValueRecorder;
enum {
_log_cache_size = 9,
_cache_size = (1<<_log_cache_size),
_collision_bit_shift = 0,
_collision_bit = 1,
_index_shift = _collision_bit_shift+1
};
int _cache[_cache_size];
static juint cache_index(X handle) {
juint ci = (int) (intptr_t) handle;
ci ^= ci >> (BitsPerByte*2);
ci += ci >> (BitsPerByte*1);
return ci & (_cache_size-1);
}
int* cache_location(X handle) {
return &_cache[ cache_index(handle) ];
}
static bool cache_location_collision(int* cloc) {
return ((*cloc) & _collision_bit) != 0;
}
static int cache_location_index(int* cloc) {
return (*cloc) >> _index_shift;
}
static void set_cache_location_index(int* cloc, int index) {
int cval0 = (*cloc);
int cval1 = (index << _index_shift);
if (cval0 != 0 && cval1 != cval0) cval1 += _collision_bit;
(*cloc) = cval1;
}
IndexCache();
};
void maybe_initialize();
int add_handle(T h, bool make_findable);
enum { null_index = 0, first_index = 1, index_cache_threshold = 20 };
GrowableArray<T>* _handles; // ordered list (first is always NULL)
GrowableArray<int>* _no_finds; // all unfindable indexes; usually empty
IndexCache<T>* _indexes; // map: handle -> its probable index
Arena* _arena;
bool _complete;
#ifdef ASSERT
static int _find_index_calls, _hit_indexes, _missed_indexes;
#endif
};
class OopRecorder : public ResourceObj {
private:
ValueRecorder<jobject> _oops;
ValueRecorder<Metadata*> _metadata;
public:
OopRecorder(Arena* arena = NULL): _oops(arena), _metadata(arena) {}
int allocate_oop_index(jobject h) {
return _oops.allocate_index(h);
}
int find_index(jobject h) {
return _oops.find_index(h);
}
jobject oop_at(int index) {
return _oops.at(index);
}
int oop_size() {
return _oops.size();
}
int oop_count() {
return _oops.count();
}
bool is_real(jobject h) {
return _oops.is_real(h);
}
int allocate_metadata_index(Metadata* oop) {
return _metadata.allocate_index(oop);
}
int find_index(Metadata* h) {
return _metadata.find_index(h);
}
Metadata* metadata_at(int index) {
return _metadata.at(index);
}
int metadata_size() {
return _metadata.size();
}
int metadata_count() {
return _metadata.count();
}
bool is_real(Metadata* h) {
return _metadata.is_real(h);
}
bool is_unused() {
return _oops.is_unused() && _metadata.is_unused();
}
void freeze() {
_oops.size();
_metadata.size();
}
void copy_values_to(nmethod* nm) {
if (!_oops.is_unused()) {
_oops.copy_values_to(nm);
}
if (!_metadata.is_unused()) {
_metadata.copy_values_to(nm);
}
}
#ifdef ASSERT
bool is_complete() {
assert(_oops.is_complete() == _metadata.is_complete(), "must agree");
return _oops.is_complete();
}
#endif
};
#endif // SHARE_VM_CODE_OOPRECORDER_HPP
C:\hotspot-69087d08d473\src\share\vm/code/pcDesc.cpp
#include "precompiled.hpp"
#include "code/debugInfoRec.hpp"
#include "code/nmethod.hpp"
#include "code/pcDesc.hpp"
#include "code/scopeDesc.hpp"
#include "memory/resourceArea.hpp"
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
PcDesc::PcDesc(int pc_offset, int scope_decode_offset, int obj_decode_offset) {
_pc_offset = pc_offset;
_scope_decode_offset = scope_decode_offset;
_obj_decode_offset = obj_decode_offset;
_flags = 0;
}
address PcDesc::real_pc(const nmethod* code) const {
return code->code_begin() + pc_offset();
}
void PcDesc::print(nmethod* code) {
#ifndef PRODUCT
ResourceMark rm;
tty->print_cr("PcDesc(pc=0x%lx offset=%x bits=%x):", real_pc(code), pc_offset(), _flags);
if (scope_decode_offset() == DebugInformationRecorder::serialized_null) {
return;
}
for (ScopeDesc* sd = code->scope_desc_at(real_pc(code));
sd != NULL;
sd = sd->sender()) {
tty->print(" ");
sd->method()->print_short_name(tty);
tty->print(" @%d", sd->bci());
if (sd->should_reexecute())
tty->print(" reexecute=true");
tty->cr();
}
#endif
}
bool PcDesc::verify(nmethod* code) {
return true;
}
C:\hotspot-69087d08d473\src\share\vm/code/pcDesc.hpp
#ifndef SHARE_VM_CODE_PCDESC_HPP
#define SHARE_VM_CODE_PCDESC_HPP
#include "memory/allocation.hpp"
class nmethod;
class PcDesc VALUE_OBJ_CLASS_SPEC {
friend class VMStructs;
private:
int _pc_offset; // offset from start of nmethod
int _scope_decode_offset; // offset for scope in nmethod
int _obj_decode_offset;
enum {
PCDESC_reexecute = 1 << 0,
PCDESC_is_method_handle_invoke = 1 << 1,
PCDESC_return_oop = 1 << 2
};
int _flags;
void set_flag(int mask, bool z) {
_flags = z ? (_flags | mask) : (_flags & ~mask);
}
public:
int pc_offset() const { return _pc_offset; }
int scope_decode_offset() const { return _scope_decode_offset; }
int obj_decode_offset() const { return _obj_decode_offset; }
void set_pc_offset(int x) { _pc_offset = x; }
void set_scope_decode_offset(int x) { _scope_decode_offset = x; }
void set_obj_decode_offset(int x) { _obj_decode_offset = x; }
PcDesc(int pc_offset, int scope_decode_offset, int obj_decode_offset);
enum {
lower_offset_limit = -1,
upper_offset_limit = (unsigned int)-1 >> 1
};
bool should_reexecute() const { return (_flags & PCDESC_reexecute) != 0; }
void set_should_reexecute(bool z) { set_flag(PCDESC_reexecute, z); }
bool is_same_info(const PcDesc* pd) {
return _scope_decode_offset == pd->_scope_decode_offset &&
_obj_decode_offset == pd->_obj_decode_offset &&
_flags == pd->_flags;
}
bool is_method_handle_invoke() const { return (_flags & PCDESC_is_method_handle_invoke) != 0; }
void set_is_method_handle_invoke(bool z) { set_flag(PCDESC_is_method_handle_invoke, z); }
bool return_oop() const { return (_flags & PCDESC_return_oop) != 0; }
void set_return_oop(bool z) { set_flag(PCDESC_return_oop, z); }
address real_pc(const nmethod* code) const;
void print(nmethod* code);
bool verify(nmethod* code);
};
#endif // SHARE_VM_CODE_PCDESC_HPP
C:\hotspot-69087d08d473\src\share\vm/code/relocInfo.cpp
#include "precompiled.hpp"
#include "code/codeCache.hpp"
#include "code/compiledIC.hpp"
#include "code/nmethod.hpp"
#include "code/relocInfo.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/stubCodeGenerator.hpp"
#include "utilities/copy.hpp"
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
const RelocationHolder RelocationHolder::none; // its type is relocInfo::none
#ifdef ASSERT
relocInfo::relocInfo(relocType t, int off, int f) {
assert(t != data_prefix_tag, "cannot build a prefix this way");
assert((t & type_mask) == t, "wrong type");
assert((f & format_mask) == f, "wrong format");
assert(off >= 0 && off < offset_limit(), "offset out off bounds");
assert((off & (offset_unit-1)) == 0, "misaligned offset");
(*this) = relocInfo(t, RAW_BITS, off, f);
}
#endif
void relocInfo::initialize(CodeSection* dest, Relocation* reloc) {
relocInfo* data = this+1; // here's where the data might go
dest->set_locs_end(data); // sync end: the next call may read dest.locs_end
reloc->pack_data_to(dest); // maybe write data into locs, advancing locs_end
relocInfo* data_limit = dest->locs_end();
if (data_limit > data) {
relocInfo suffix = (*this);
data_limit = this->finish_prefix((short*) data_limit);
dest->set_locs_end(data_limit+1);
}
}
relocInfo* relocInfo::finish_prefix(short* prefix_limit) {
assert(sizeof(relocInfo) == sizeof(short), "change this code");
short* p = (short*)(this+1);
assert(prefix_limit >= p, "must be a valid span of data");
int plen = prefix_limit - p;
if (plen == 0) {
debug_only(_value = 0xFFFF);
return this; // no data: remove self completely
}
if (plen == 1 && fits_into_immediate(p[0])) {
(*this) = immediate_relocInfo(p[0]); // move data inside self
return this+1;
}
(*this) = prefix_relocInfo(plen); // write new datalen
assert(data() + datalen() == prefix_limit, "pointers must line up");
return (relocInfo*)prefix_limit;
}
void relocInfo::set_type(relocType t) {
int old_offset = addr_offset();
int old_format = format();
(*this) = relocInfo(t, old_offset, old_format);
assert(type()==(int)t, "sanity check");
assert(addr_offset()==old_offset, "sanity check");
assert(format()==old_format, "sanity check");
}
void relocInfo::set_format(int f) {
int old_offset = addr_offset();
assert((f & format_mask) == f, "wrong format");
_value = (_value & ~(format_mask << offset_width)) | (f << offset_width);
assert(addr_offset()==old_offset, "sanity check");
}
void relocInfo::change_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type, relocType new_type) {
bool found = false;
while (itr->next() && !found) {
if (itr->addr() == pc) {
assert(itr->type()==old_type, "wrong relocInfo type found");
itr->current()->set_type(new_type);
found=true;
}
}
assert(found, "no relocInfo found for pc");
}
void relocInfo::remove_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type) {
change_reloc_info_for_address(itr, pc, old_type, none);
}
void RelocIterator::initialize(nmethod* nm, address begin, address limit) {
initialize_misc();
if (nm == NULL && begin != NULL) {
CodeBlob* cb = CodeCache::find_blob(begin);
nm = (cb != NULL) ? cb->as_nmethod_or_null() : NULL;
}
guarantee(nm != NULL, "must be able to deduce nmethod from other arguments");
_code = nm;
_current = nm->relocation_begin() - 1;
_end = nm->relocation_end();
_addr = nm->content_begin();
_section_start[CodeBuffer::SECT_CONSTS] = nm->consts_begin();
_section_start[CodeBuffer::SECT_INSTS ] = nm->insts_begin() ;
_section_start[CodeBuffer::SECT_STUBS ] = nm->stub_begin() ;
_section_end [CodeBuffer::SECT_CONSTS] = nm->consts_end() ;
_section_end [CodeBuffer::SECT_INSTS ] = nm->insts_end() ;
_section_end [CodeBuffer::SECT_STUBS ] = nm->stub_end() ;
assert(!has_current(), "just checking");
assert(begin == NULL || begin >= nm->code_begin(), "in bounds");
assert(limit == NULL || limit <= nm->code_end(), "in bounds");
set_limits(begin, limit);
}
RelocIterator::RelocIterator(CodeSection* cs, address begin, address limit) {
initialize_misc();
_current = cs->locs_start()-1;
_end = cs->locs_end();
_addr = cs->start();
_code = NULL; // Not cb->blob();
CodeBuffer* cb = cs->outer();
assert((int) SECT_LIMIT == CodeBuffer::SECT_LIMIT, "my copy must be equal");
for (int n = (int) CodeBuffer::SECT_FIRST; n < (int) CodeBuffer::SECT_LIMIT; n++) {
CodeSection* cs = cb->code_section(n);
_section_start[n] = cs->start();
_section_end [n] = cs->end();
}
assert(!has_current(), "just checking");
assert(begin == NULL || begin >= cs->start(), "in bounds");
assert(limit == NULL || limit <= cs->end(), "in bounds");
set_limits(begin, limit);
}
enum { indexCardSize = 128 };
struct RelocIndexEntry {
jint addr_offset; // offset from header_end of an addr()
jint reloc_offset; // offset from header_end of a relocInfo (prefix)
};
bool RelocIterator::addr_in_const() const {
const int n = CodeBuffer::SECT_CONSTS;
return section_start(n) <= addr() && addr() < section_end(n);
}
static inline int num_cards(int code_size) {
return (code_size-1) / indexCardSize;
}
int RelocIterator::locs_and_index_size(int code_size, int locs_size) {
if (!UseRelocIndex) return locs_size; // no index
code_size = round_to(code_size, oopSize);
locs_size = round_to(locs_size, oopSize);
int index_size = num_cards(code_size) * sizeof(RelocIndexEntry);
return locs_size + index_size + BytesPerInt;
}
void RelocIterator::create_index(relocInfo* dest_begin, int dest_count, relocInfo* dest_end) {
address relocation_begin = (address)dest_begin;
address relocation_end = (address)dest_end;
int total_size = relocation_end - relocation_begin;
int locs_size = dest_count * sizeof(relocInfo);
if (!UseRelocIndex) {
Copy::fill_to_bytes(relocation_begin + locs_size, total_size-locs_size, 0);
return;
}
int index_size = total_size - locs_size - BytesPerInt; // find out how much space is left
int ncards = index_size / sizeof(RelocIndexEntry);
assert(total_size == locs_size + index_size + BytesPerInt, "checkin'");
assert(index_size >= 0 && index_size % sizeof(RelocIndexEntry) == 0, "checkin'");
jint* index_size_addr = (jint*)relocation_end - 1;
assert(sizeof(jint) == BytesPerInt, "change this code");
if (index_size != 0) {
assert(index_size > 0, "checkin'");
RelocIndexEntry* index = (RelocIndexEntry *)(relocation_begin + locs_size);
assert(index == (RelocIndexEntry*)index_size_addr - ncards, "checkin'");
RelocIterator iter;
const address initial_addr = NULL;
relocInfo* const initial_current = dest_begin - 1; // biased by -1 like elsewhere
iter._code = NULL;
iter._addr = initial_addr;
iter._limit = (address)(intptr_t)(ncards * indexCardSize);
iter._current = initial_current;
iter._end = dest_begin + dest_count;
int i = 0;
address next_card_addr = (address)indexCardSize;
int addr_offset = 0;
int reloc_offset = 0;
while (true) {
addr_offset = iter._addr - initial_addr;
reloc_offset = iter._current - initial_current;
if (!iter.next()) break;
while (iter.addr() >= next_card_addr) {
index[i].addr_offset = addr_offset;
index[i].reloc_offset = reloc_offset;
i++;
next_card_addr += indexCardSize;
}
}
while (i < ncards) {
index[i].addr_offset = addr_offset;
index[i].reloc_offset = reloc_offset;
i++;
}
}
}
void RelocIterator::set_limits(address begin, address limit) {
int index_size = 0;
if (UseRelocIndex && _code != NULL) {
index_size = ((jint*)_end)[-1];
_end = (relocInfo*)( (address)_end - index_size - BytesPerInt );
}
_limit = limit;
if (begin != NULL) {
#ifdef ASSERT
address addrCheck = _addr;
relocInfo* infoCheck = _current;
#endif // ASSERT
if (index_size > 0) {
RelocIndexEntry* index = (RelocIndexEntry*)_end;
RelocIndexEntry* index_limit = (RelocIndexEntry*)((address)index + index_size);
assert(_addr == _code->code_begin(), "_addr must be unadjusted");
int card = (begin - _addr) / indexCardSize;
if (card > 0) {
if (index+card-1 < index_limit) index += card-1;
else index = index_limit - 1;
#ifdef ASSERT
addrCheck = _addr + index->addr_offset;
infoCheck = _current + index->reloc_offset;
#else
_addr += index->addr_offset;
_current += index->reloc_offset;
#endif // ASSERT
}
}
relocInfo* backup;
address backup_addr;
while (true) {
backup = _current;
backup_addr = _addr;
#ifdef ASSERT
if (backup == infoCheck) {
assert(backup_addr == addrCheck, "must match"); addrCheck = NULL; infoCheck = NULL;
} else {
assert(addrCheck == NULL || backup_addr <= addrCheck, "must not pass addrCheck");
}
#endif // ASSERT
if (!next() || addr() >= begin) break;
}
assert(addrCheck == NULL || addrCheck == backup_addr, "must have matched addrCheck");
assert(infoCheck == NULL || infoCheck == backup, "must have matched infoCheck");
_current = backup;
_addr = backup_addr;
set_has_current(false);
}
}
void RelocIterator::set_limit(address limit) {
address code_end = (address)code() + code()->size();
assert(limit == NULL || limit <= code_end, "in bounds");
_limit = limit;
}
void RelocIterator::advance_over_prefix() {
if (_current->is_datalen()) {
_data = (short*) _current->data();
_datalen = _current->datalen();
_current += _datalen + 1; // skip the embedded data & header
} else {
_databuf = _current->immediate();
_data = &_databuf;
_datalen = 1;
_current++; // skip the header
}
}
void RelocIterator::initialize_misc() {
set_has_current(false);
for (int i = (int) CodeBuffer::SECT_FIRST; i < (int) CodeBuffer::SECT_LIMIT; i++) {
_section_start[i] = NULL; // these will be lazily computed, if needed
_section_end [i] = NULL;
}
}
Relocation* RelocIterator::reloc() {
relocInfo::relocType t = type();
if (false) {}
#define EACH_TYPE(name) \
else if (t == relocInfo::name##_type) { \
return name##_reloc(); \
}
APPLY_TO_RELOCATIONS(EACH_TYPE);
#undef EACH_TYPE
assert(t == relocInfo::none, "must be padding");
return new(_rh) Relocation();
}
RelocationHolder RelocationHolder::plus(int offset) const {
if (offset != 0) {
switch (type()) {
case relocInfo::none:
break;
case relocInfo::oop_type:
{
oop_Relocation* r = (oop_Relocation*)reloc();
return oop_Relocation::spec(r->oop_index(), r->offset() + offset);
}
case relocInfo::metadata_type:
{
metadata_Relocation* r = (metadata_Relocation*)reloc();
return metadata_Relocation::spec(r->metadata_index(), r->offset() + offset);
}
default:
ShouldNotReachHere();
}
}
return (*this);
}
void Relocation::guarantee_size() {
guarantee(false, "Make _relocbuf bigger!");
}
address Relocation::value() {
ShouldNotReachHere();
return NULL;
}
void Relocation::set_value(address x) {
ShouldNotReachHere();
}
RelocationHolder Relocation::spec_simple(relocInfo::relocType rtype) {
if (rtype == relocInfo::none) return RelocationHolder::none;
relocInfo ri = relocInfo(rtype, 0);
RelocIterator itr;
itr.set_current(ri);
itr.reloc();
return itr._rh;
}
int32_t Relocation::runtime_address_to_index(address runtime_address) {
assert(!is_reloc_index((intptr_t)runtime_address), "must not look like an index");
if (runtime_address == NULL) return 0;
StubCodeDesc* p = StubCodeDesc::desc_for(runtime_address);
if (p != NULL && p->begin() == runtime_address) {
assert(is_reloc_index(p->index()), "there must not be too many stubs");
return (int32_t)p->index();
} else {
if (PrintRelocations) {
tty->print_cr("random unregistered address in relocInfo: " INTPTR_FORMAT, runtime_address);
}
#ifndef _LP64
return (int32_t) (intptr_t)runtime_address;
#else
return -1;
#endif /* _LP64 */
}
}
address Relocation::index_to_runtime_address(int32_t index) {
if (index == 0) return NULL;
if (is_reloc_index(index)) {
StubCodeDesc* p = StubCodeDesc::desc_for_index(index);
assert(p != NULL, "there must be a stub for this index");
return p->begin();
} else {
#ifndef _LP64
return (address) ((intptr_t) index);
#else
fatal("Relocation::index_to_runtime_address, int32_t not pointer sized");
return NULL;
#endif /* _LP64 */
}
}
address Relocation::old_addr_for(address newa,
const CodeBuffer* src, CodeBuffer* dest) {
int sect = dest->section_index_of(newa);
guarantee(sect != CodeBuffer::SECT_NONE, "lost track of this address");
address ostart = src->code_section(sect)->start();
address nstart = dest->code_section(sect)->start();
return ostart + (newa - nstart);
}
address Relocation::new_addr_for(address olda,
const CodeBuffer* src, CodeBuffer* dest) {
debug_only(const CodeBuffer* src0 = src);
int sect = CodeBuffer::SECT_NONE;
for (; src != NULL; src = src->before_expand()) {
sect = src->section_index_of(olda);
if (sect != CodeBuffer::SECT_NONE) break;
}
guarantee(sect != CodeBuffer::SECT_NONE, "lost track of this address");
address ostart = src->code_section(sect)->start();
address nstart = dest->code_section(sect)->start();
return nstart + (olda - ostart);
}
void Relocation::normalize_address(address& addr, const CodeSection* dest, bool allow_other_sections) {
address addr0 = addr;
if (addr0 == NULL || dest->allocates2(addr0)) return;
CodeBuffer* cb = dest->outer();
addr = new_addr_for(addr0, cb, cb);
assert(allow_other_sections || dest->contains2(addr),
"addr must be in required section");
}
void CallRelocation::set_destination(address x) {
pd_set_call_destination(x);
}
void CallRelocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) {
address orig_addr = old_addr_for(addr(), src, dest);
address callee = pd_call_destination(orig_addr);
pd_set_call_destination(callee);
}
void oop_Relocation::pack_data_to(CodeSection* dest) {
short* p = (short*) dest->locs_end();
p = pack_2_ints_to(p, _oop_index, _offset);
dest->set_locs_end((relocInfo*) p);
}
void oop_Relocation::unpack_data() {
unpack_2_ints(_oop_index, _offset);
}
void metadata_Relocation::pack_data_to(CodeSection* dest) {
short* p = (short*) dest->locs_end();
p = pack_2_ints_to(p, _metadata_index, _offset);
dest->set_locs_end((relocInfo*) p);
}
void metadata_Relocation::unpack_data() {
unpack_2_ints(_metadata_index, _offset);
}
void virtual_call_Relocation::pack_data_to(CodeSection* dest) {
short* p = (short*) dest->locs_end();
address point = dest->locs_point();
normalize_address(_cached_value, dest);
jint x0 = scaled_offset_null_special(_cached_value, point);
p = pack_1_int_to(p, x0);
dest->set_locs_end((relocInfo*) p);
}
void virtual_call_Relocation::unpack_data() {
jint x0 = unpack_1_int();
address point = addr();
_cached_value = x0==0? NULL: address_from_scaled_offset(x0, point);
}
void static_stub_Relocation::pack_data_to(CodeSection* dest) {
short* p = (short*) dest->locs_end();
CodeSection* insts = dest->outer()->insts();
normalize_address(_static_call, insts);
p = pack_1_int_to(p, scaled_offset(_static_call, insts->start()));
dest->set_locs_end((relocInfo*) p);
}
void static_stub_Relocation::unpack_data() {
address base = binding()->section_start(CodeBuffer::SECT_INSTS);
_static_call = address_from_scaled_offset(unpack_1_int(), base);
}
void trampoline_stub_Relocation::pack_data_to(CodeSection* dest ) {
short* p = (short*) dest->locs_end();
CodeSection* insts = dest->outer()->insts();
normalize_address(_owner, insts);
p = pack_1_int_to(p, scaled_offset(_owner, insts->start()));
dest->set_locs_end((relocInfo*) p);
}
void trampoline_stub_Relocation::unpack_data() {
address base = binding()->section_start(CodeBuffer::SECT_INSTS);
_owner = address_from_scaled_offset(unpack_1_int(), base);
}
void external_word_Relocation::pack_data_to(CodeSection* dest) {
short* p = (short*) dest->locs_end();
int32_t index = runtime_address_to_index(_target);
#ifndef _LP64
p = pack_1_int_to(p, index);
#else
if (is_reloc_index(index)) {
p = pack_2_ints_to(p, index, 0);
} else {
jlong t = (jlong) _target;
int32_t lo = low(t);
int32_t hi = high(t);
p = pack_2_ints_to(p, lo, hi);
DEBUG_ONLY(jlong t1 = jlong_from(hi, lo));
assert(!is_reloc_index(t1) && (address) t1 == _target, "not symmetric");
}
#endif /* _LP64 */
dest->set_locs_end((relocInfo*) p);
}
void external_word_Relocation::unpack_data() {
#ifndef _LP64
_target = index_to_runtime_address(unpack_1_int());
#else
int32_t lo, hi;
unpack_2_ints(lo, hi);
jlong t = jlong_from(hi, lo);;
if (is_reloc_index(t)) {
_target = index_to_runtime_address(t);
} else {
_target = (address) t;
}
#endif /* _LP64 */
}
void internal_word_Relocation::pack_data_to(CodeSection* dest) {
short* p = (short*) dest->locs_end();
normalize_address(_target, dest, true);
int sindex = _section;
if (sindex == CodeBuffer::SECT_NONE && _target != NULL
&& (!dest->allocates(_target) || _target == dest->locs_point())) {
sindex = dest->outer()->section_index_of(_target);
guarantee(sindex != CodeBuffer::SECT_NONE, "must belong somewhere");
relocInfo* base = dest->locs_end() - 1;
assert(base->type() == this->type(), "sanity");
base->set_type(relocInfo::section_word_type);
}
if (sindex == CodeBuffer::SECT_NONE) {
assert(type() == relocInfo::internal_word_type, "must be base class");
guarantee(_target == NULL || dest->allocates2(_target), "must be within the given code section");
jint x0 = scaled_offset_null_special(_target, dest->locs_point());
assert(!(x0 == 0 && _target != NULL), "correct encoding of null target");
p = pack_1_int_to(p, x0);
} else {
assert(_target != NULL, "sanity");
CodeSection* sect = dest->outer()->code_section(sindex);
guarantee(sect->allocates2(_target), "must be in correct section");
address base = sect->start();
jint offset = scaled_offset(_target, base);
assert((uint)sindex < (uint)CodeBuffer::SECT_LIMIT, "sanity");
assert(CodeBuffer::SECT_LIMIT <= (1 << section_width), "section_width++");
p = pack_1_int_to(p, (offset << section_width) | sindex);
}
dest->set_locs_end((relocInfo*) p);
}
void internal_word_Relocation::unpack_data() {
jint x0 = unpack_1_int();
_target = x0==0? NULL: address_from_scaled_offset(x0, addr());
_section = CodeBuffer::SECT_NONE;
}
void section_word_Relocation::unpack_data() {
jint x = unpack_1_int();
jint offset = (x >> section_width);
int sindex = (x & ((1<<section_width)-1));
address base = binding()->section_start(sindex);
_section = sindex;
_target = address_from_scaled_offset(offset, base);
}
oop* oop_Relocation::oop_addr() {
int n = _oop_index;
if (n == 0) {
return (oop*) pd_address_in_code();
} else {
return code()->oop_addr_at(n);
}
}
oop oop_Relocation::oop_value() {
oop v = *oop_addr();
if (v == (oop)Universe::non_oop_word()) v = NULL;
return v;
}
void oop_Relocation::fix_oop_relocation() {
if (!oop_is_immediate()) {
set_value(value());
}
}
void oop_Relocation::verify_oop_relocation() {
if (!oop_is_immediate()) {
verify_value(value());
}
}
Metadata** metadata_Relocation::metadata_addr() {
int n = _metadata_index;
if (n == 0) {
return (Metadata**) pd_address_in_code();
} else {
return code()->metadata_addr_at(n);
}
}
Metadata* metadata_Relocation::metadata_value() {
Metadata* v = *metadata_addr();
if (v == (Metadata*)Universe::non_oop_word()) v = NULL;
return v;
}
void metadata_Relocation::fix_metadata_relocation() {
if (!metadata_is_immediate()) {
pd_fix_value(value());
}
}
void metadata_Relocation::verify_metadata_relocation() {
if (!metadata_is_immediate()) {
verify_value(value());
}
}
address virtual_call_Relocation::cached_value() {
assert(_cached_value != NULL && _cached_value < addr(), "must precede ic_call");
return _cached_value;
}
void virtual_call_Relocation::clear_inline_cache() {
ResourceMark rm;
CompiledIC* icache = CompiledIC_at(this);
icache->set_to_clean();
}
void opt_virtual_call_Relocation::clear_inline_cache() {
ResourceMark rm;
CompiledIC* icache = CompiledIC_at(this);
icache->set_to_clean();
}
address opt_virtual_call_Relocation::static_stub() {
address static_call_addr = addr();
RelocIterator iter(code());
while (iter.next()) {
if (iter.type() == relocInfo::static_stub_type) {
if (iter.static_stub_reloc()->static_call() == static_call_addr) {
return iter.addr();
}
}
}
return NULL;
}
void static_call_Relocation::clear_inline_cache() {
CompiledStaticCall* handler = compiledStaticCall_at(this);
handler->set_to_clean();
}
address static_call_Relocation::static_stub() {
address static_call_addr = addr();
RelocIterator iter(code());
while (iter.next()) {
if (iter.type() == relocInfo::static_stub_type) {
if (iter.static_stub_reloc()->static_call() == static_call_addr) {
return iter.addr();
}
}
}
return NULL;
}
address trampoline_stub_Relocation::get_trampoline_for(address call, nmethod* code) {
if (code->relocation_size() == 0)
return NULL;
RelocIterator iter(code, call);
while (iter.next()) {
if (iter.type() == relocInfo::trampoline_stub_type) {
if (iter.trampoline_stub_reloc()->owner() == call) {
return iter.addr();
}
}
}
return NULL;
}
void static_stub_Relocation::clear_inline_cache() {
CompiledStaticCall::set_stub_to_clean(this);
}
void external_word_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) {
address target = _target;
if (target == NULL) {
return;
}
assert(src->section_index_of(target) == CodeBuffer::SECT_NONE, "sanity");
set_value(target);
}
address external_word_Relocation::target() {
address target = _target;
if (target == NULL) {
target = pd_get_address_from_code();
}
return target;
}
void internal_word_Relocation::fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) {
address target = _target;
if (target == NULL) {
target = new_addr_for(this->target(), src, dest);
}
set_value(target);
}
address internal_word_Relocation::target() {
address target = _target;
if (target == NULL) {
if (addr_in_const()) {
target = *(address*)addr();
} else {
target = pd_get_address_from_code();
}
}
return target;
}
#ifndef PRODUCT
static const char* reloc_type_string(relocInfo::relocType t) {
switch (t) {
#define EACH_CASE(name) \
case relocInfo::name##_type: \
return #name;
APPLY_TO_RELOCATIONS(EACH_CASE);
#undef EACH_CASE
case relocInfo::none:
return "none";
case relocInfo::data_prefix_tag:
return "prefix";
default:
return "UNKNOWN RELOC TYPE";
}
}
void RelocIterator::print_current() {
if (!has_current()) {
tty->print_cr("(no relocs)");
return;
}
tty->print("relocInfo@" INTPTR_FORMAT " [type=%d(%s) addr=" INTPTR_FORMAT " offset=%d",
_current, type(), reloc_type_string((relocInfo::relocType) type()), _addr, _current->addr_offset());
if (current()->format() != 0)
tty->print(" format=%d", current()->format());
if (datalen() == 1) {
tty->print(" data=%d", data()[0]);
} else if (datalen() > 0) {
tty->print(" data={");
for (int i = 0; i < datalen(); i++) {
tty->print("%04x", data()[i] & 0xFFFF);
}
tty->print("}");
}
tty->print("]");
switch (type()) {
case relocInfo::oop_type:
{
oop_Relocation* r = oop_reloc();
oop* oop_addr = NULL;
oop raw_oop = NULL;
oop oop_value = NULL;
if (code() != NULL || r->oop_is_immediate()) {
oop_addr = r->oop_addr();
raw_oop = *oop_addr;
oop_value = r->oop_value();
}
tty->print(" | [oop_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT " offset=%d]",
oop_addr, (address)raw_oop, r->offset());
if (WizardMode && oop_value != NULL) {
tty->print("oop_value=" INTPTR_FORMAT ": ", (address)oop_value);
oop_value->print_value_on(tty);
}
break;
}
case relocInfo::metadata_type:
{
metadata_Relocation* r = metadata_reloc();
Metadata** metadata_addr = NULL;
Metadata* raw_metadata = NULL;
Metadata* metadata_value = NULL;
if (code() != NULL || r->metadata_is_immediate()) {
metadata_addr = r->metadata_addr();
raw_metadata = *metadata_addr;
metadata_value = r->metadata_value();
}
tty->print(" | [metadata_addr=" INTPTR_FORMAT " *=" INTPTR_FORMAT " offset=%d]",
metadata_addr, (address)raw_metadata, r->offset());
if (metadata_value != NULL) {
tty->print("metadata_value=" INTPTR_FORMAT ": ", (address)metadata_value);
metadata_value->print_value_on(tty);
}
break;
}
case relocInfo::external_word_type:
case relocInfo::internal_word_type:
case relocInfo::section_word_type:
{
DataRelocation* r = (DataRelocation*) reloc();
tty->print(" | [target=" INTPTR_FORMAT "]", r->value()); //value==target
break;
}
case relocInfo::static_call_type:
case relocInfo::runtime_call_type:
{
CallRelocation* r = (CallRelocation*) reloc();
tty->print(" | [destination=" INTPTR_FORMAT "]", r->destination());
break;
}
case relocInfo::virtual_call_type:
{
virtual_call_Relocation* r = (virtual_call_Relocation*) reloc();
tty->print(" | [destination=" INTPTR_FORMAT " cached_value=" INTPTR_FORMAT "]",
r->destination(), r->cached_value());
break;
}
case relocInfo::static_stub_type:
{
static_stub_Relocation* r = (static_stub_Relocation*) reloc();
tty->print(" | [static_call=" INTPTR_FORMAT "]", r->static_call());
break;
}
case relocInfo::trampoline_stub_type:
{
trampoline_stub_Relocation* r = (trampoline_stub_Relocation*) reloc();
tty->print(" | [trampoline owner=" INTPTR_FORMAT "]", r->owner());
break;
}
}
tty->cr();
}
void RelocIterator::print() {
RelocIterator save_this = (*this);
relocInfo* scan = _current;
if (!has_current()) scan += 1; // nothing to scan here!
bool skip_next = has_current();
bool got_next;
while (true) {
got_next = (skip_next || next());
skip_next = false;
tty->print(" @" INTPTR_FORMAT ": ", scan);
relocInfo* newscan = _current+1;
if (!has_current()) newscan -= 1; // nothing to scan here!
while (scan < newscan) {
tty->print("%04x", *(short*)scan & 0xFFFF);
scan++;
}
tty->cr();
if (!got_next) break;
print_current();
}
(*this) = save_this;
}
extern "C"
void print_blob_locs(nmethod* nm) {
nm->print();
RelocIterator iter(nm);
iter.print();
}
extern "C"
void print_buf_locs(CodeBuffer* cb) {
FlagSetting fs(PrintRelocations, true);
cb->print();
}
#endif // !PRODUCT
C:\hotspot-69087d08d473\src\share\vm/code/relocInfo.hpp
#ifndef SHARE_VM_CODE_RELOCINFO_HPP
#define SHARE_VM_CODE_RELOCINFO_HPP
#include "memory/allocation.hpp"
#include "utilities/top.hpp"
class NativeMovConstReg;
class Relocation;
class CodeBuffer;
class CodeSection;
class RelocIterator;
class relocInfo VALUE_OBJ_CLASS_SPEC {
friend class RelocIterator;
public:
enum relocType {
none = 0, // Used when no relocation should be generated
oop_type = 1, // embedded oop
virtual_call_type = 2, // a standard inline cache call for a virtual send
opt_virtual_call_type = 3, // a virtual call that has been statically bound (i.e., no IC cache)
static_call_type = 4, // a static send
static_stub_type = 5, // stub-entry for static send (takes care of interpreter case)
runtime_call_type = 6, // call to fixed external routine
external_word_type = 7, // reference to fixed external address
internal_word_type = 8, // reference within the current code blob
section_word_type = 9, // internal, but a cross-section reference
poll_type = 10, // polling instruction for safepoints
poll_return_type = 11, // polling instruction for safepoints at return
metadata_type = 12, // metadata that used to be oops
trampoline_stub_type = 13, // stub-entry for trampoline
yet_unused_type_1 = 14, // Still unused
data_prefix_tag = 15, // tag for a prefix (carries data arguments)
type_mask = 15 // A mask which selects only the above values
};
protected:
unsigned short _value;
enum RawBitsToken { RAW_BITS };
relocInfo(relocType type, RawBitsToken ignore, int bits)
: _value((type << nontype_width) + bits) { }
relocInfo(relocType type, RawBitsToken ignore, int off, int f)
: _value((type << nontype_width) + (off / (unsigned)offset_unit) + (f << offset_width)) { }
public:
relocInfo(relocType type, int offset, int format = 0)
#ifndef ASSERT
{
(*this) = relocInfo(type, RAW_BITS, offset, format);
}
#else
;
#endif
#define APPLY_TO_RELOCATIONS(visitor) \
visitor(oop) \
visitor(metadata) \
visitor(virtual_call) \
visitor(opt_virtual_call) \
visitor(static_call) \
visitor(static_stub) \
visitor(runtime_call) \
visitor(external_word) \
visitor(internal_word) \
visitor(poll) \
visitor(poll_return) \
visitor(section_word) \
visitor(trampoline_stub) \
public:
enum {
value_width = sizeof(unsigned short) * BitsPerByte,
type_width = 4, // == log2(type_mask+1)
nontype_width = value_width - type_width,
datalen_width = nontype_width-1,
datalen_tag = 1 << datalen_width, // or-ed into _value
datalen_limit = 1 << datalen_width,
datalen_mask = (1 << datalen_width)-1
};
public:
relocType type() const { return (relocType)((unsigned)_value >> nontype_width); }
int format() const { return format_mask==0? 0: format_mask &
((unsigned)_value >> offset_width); }
int addr_offset() const { assert(!is_prefix(), "must have offset");
return (_value & offset_mask)*offset_unit; }
protected:
const short* data() const { assert(is_datalen(), "must have data");
return (const short*)(this + 1); }
int datalen() const { assert(is_datalen(), "must have data");
return (_value & datalen_mask); }
int immediate() const { assert(is_immediate(), "must have immed");
return (_value & datalen_mask); }
public:
static int addr_unit() { return offset_unit; }
static int offset_limit() { return (1 << offset_width) * offset_unit; }
void set_type(relocType type);
void set_format(int format);
void remove() { set_type(none); }
protected:
bool is_none() const { return type() == none; }
bool is_prefix() const { return type() == data_prefix_tag; }
bool is_datalen() const { assert(is_prefix(), "must be prefix");
return (_value & datalen_tag) != 0; }
bool is_immediate() const { assert(is_prefix(), "must be prefix");
return (_value & datalen_tag) == 0; }
public:
inline friend relocInfo filler_relocInfo();
inline friend relocInfo prefix_relocInfo(int datalen);
protected:
static relocInfo immediate_relocInfo(int data0) {
assert(fits_into_immediate(data0), "data0 in limits");
return relocInfo(relocInfo::data_prefix_tag, RAW_BITS, data0);
}
static bool fits_into_immediate(int data0) {
return (data0 >= 0 && data0 < datalen_limit);
}
public:
void initialize(CodeSection* dest, Relocation* reloc);
relocInfo* finish_prefix(short* prefix_limit);
static int data0_from_int(jint x) { return x >> value_width; }
static int data1_from_int(jint x) { return (short)x; }
static jint jint_from_data(short* data) {
return (data[0] << value_width) + (unsigned short)data[1];
}
static jint short_data_at(int n, short* data, int datalen) {
return datalen > n ? data[n] : 0;
}
static jint jint_data_at(int n, short* data, int datalen) {
return datalen > n+1 ? jint_from_data(&data[n]) : short_data_at(n, data, datalen);
}
static void change_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type, relocType new_type);
static void remove_reloc_info_for_address(RelocIterator *itr, address pc, relocType old_type);
#ifdef TARGET_ARCH_x86
# include "relocInfo_x86.hpp"
#endif
#ifdef TARGET_ARCH_aarch64
# include "relocInfo_aarch64.hpp"
#endif
#ifdef TARGET_ARCH_sparc
# include "relocInfo_sparc.hpp"
#endif
#ifdef TARGET_ARCH_zero
# include "relocInfo_zero.hpp"
#endif
#ifdef TARGET_ARCH_arm
# include "relocInfo_arm.hpp"
#endif
#ifdef TARGET_ARCH_ppc
# include "relocInfo_ppc.hpp"
#endif
protected:
enum {
offset_width = nontype_width - format_width,
offset_mask = (1<<offset_width) - 1,
format_mask = (1<<format_width) - 1
};
public:
enum {
length_limit = 1 + 1 + (3*BytesPerWord/BytesPerShort) + 1,
have_format = format_width > 0
};
};
#define FORWARD_DECLARE_EACH_CLASS(name) \
class name##_Relocation;
APPLY_TO_RELOCATIONS(FORWARD_DECLARE_EACH_CLASS)
#undef FORWARD_DECLARE_EACH_CLASS
inline relocInfo filler_relocInfo() {
return relocInfo(relocInfo::none, relocInfo::offset_limit() - relocInfo::offset_unit);
}
inline relocInfo prefix_relocInfo(int datalen = 0) {
assert(relocInfo::fits_into_immediate(datalen), "datalen in limits");
return relocInfo(relocInfo::data_prefix_tag, relocInfo::RAW_BITS, relocInfo::datalen_tag | datalen);
}
class RelocationHolder VALUE_OBJ_CLASS_SPEC {
friend class Relocation;
friend class CodeSection;
private:
enum { _relocbuf_size = 5 };
void* _relocbuf[ _relocbuf_size ];
public:
Relocation* reloc() const { return (Relocation*) &_relocbuf[0]; }
inline relocInfo::relocType type() const;
RelocationHolder plus(int offset) const;
inline RelocationHolder(); // initializes type to none
inline RelocationHolder(Relocation* r); // make a copy
static const RelocationHolder none;
};
class RelocIterator : public StackObj {
enum { SECT_LIMIT = 3 }; // must be equal to CodeBuffer::SECT_LIMIT, checked in ctor
friend class Relocation;
friend class relocInfo; // for change_reloc_info_for_address only
typedef relocInfo::relocType relocType;
private:
address _limit; // stop producing relocations after this _addr
relocInfo* _current; // the current relocation information
relocInfo* _end; // end marker; we're done iterating when _current == _end
nmethod* _code; // compiled method containing _addr
address _addr; // instruction to which the relocation applies
short _databuf; // spare buffer for compressed data
short* _data; // pointer to the relocation's data
short _datalen; // number of halfwords in _data
char _format; // position within the instruction
address _section_start[SECT_LIMIT];
address _section_end [SECT_LIMIT];
void set_has_current(bool b) {
_datalen = !b ? -1 : 0;
debug_only(_data = NULL);
}
void set_current(relocInfo& ri) {
_current = &ri;
set_has_current(true);
}
RelocationHolder _rh; // where the current relocation is allocated
relocInfo* current() const { assert(has_current(), "must have current");
return _current; }
void set_limits(address begin, address limit);
void advance_over_prefix(); // helper method
void initialize_misc();
void initialize(nmethod* nm, address begin, address limit);
RelocIterator() { initialize_misc(); }
public:
RelocIterator(nmethod* nm, address begin = NULL, address limit = NULL);
RelocIterator(CodeSection* cb, address begin = NULL, address limit = NULL);
bool next() {
_current++;
assert(_current <= _end, "must not overrun relocInfo");
if (_current == _end) {
set_has_current(false);
return false;
}
set_has_current(true);
if (_current->is_prefix()) {
advance_over_prefix();
assert(!current()->is_prefix(), "only one prefix at a time");
}
_addr += _current->addr_offset();
if (_limit != NULL && _addr >= _limit) {
set_has_current(false);
return false;
}
if (relocInfo::have_format) _format = current()->format();
return true;
}
address limit() const { return _limit; }
void set_limit(address x);
relocType type() const { return current()->type(); }
int format() const { return (relocInfo::have_format) ? current()->format() : 0; }
address addr() const { return _addr; }
nmethod* code() const { return _code; }
short* data() const { return _data; }
int datalen() const { return _datalen; }
bool has_current() const { return _datalen >= 0; }
void set_addr(address addr) { _addr = addr; }
bool addr_in_const() const;
address section_start(int n) const {
assert(_section_start[n], "must be initialized");
return _section_start[n];
}
address section_end(int n) const {
assert(_section_end[n], "must be initialized");
return _section_end[n];
}
#define EACH_TYPE(name) \
inline name##_Relocation* name##_reloc();
APPLY_TO_RELOCATIONS(EACH_TYPE)
#undef EACH_TYPE
Relocation* reloc();
static int locs_and_index_size(int code_size, int locs_size);
static void create_index(relocInfo* dest_begin, int dest_count, relocInfo* dest_end);
#ifndef PRODUCT
public:
void print();
void print_current();
#endif
};
class Relocation VALUE_OBJ_CLASS_SPEC {
friend class RelocationHolder;
friend class RelocIterator;
private:
static void guarantee_size();
RelocIterator* _binding;
protected:
RelocIterator* binding() const {
assert(_binding != NULL, "must be bound");
return _binding;
}
void set_binding(RelocIterator* b) {
assert(_binding == NULL, "must be unbound");
_binding = b;
assert(_binding != NULL, "must now be bound");
}
Relocation() {
_binding = NULL;
}
static RelocationHolder newHolder() {
return RelocationHolder();
}
public:
void* operator new(size_t size, const RelocationHolder& holder) throw() {
if (size > sizeof(holder._relocbuf)) guarantee_size();
assert((void* const *)holder.reloc() == &holder._relocbuf[0], "ptrs must agree");
return holder.reloc();
}
static RelocationHolder spec_simple(relocInfo::relocType rtype);
virtual void pack_data_to(CodeSection* dest) { }
virtual void unpack_data() {
assert(datalen()==0 || type()==relocInfo::none, "no data here");
}
static bool is_reloc_index(intptr_t index) {
return 0 < index && index < os::vm_page_size();
}
protected:
static bool is_short(jint x) { return x == (short)x; }
static short* add_short(short* p, int x) { *p++ = x; return p; }
static short* add_jint (short* p, jint x) {
return p;
}
static short* add_var_int(short* p, jint x) { // add a variable-width int
if (is_short(x)) p = add_short(p, x);
else p = add_jint (p, x);
return p;
}
static short* pack_1_int_to(short* p, jint x0) {
if (x0 != 0) p = add_var_int(p, x0);
return p;
}
int unpack_1_int() {
assert(datalen() <= 2, "too much data");
return relocInfo::jint_data_at(0, data(), datalen());
}
short* pack_2_ints_to(short* p, jint x0, jint x1) {
if (x0 == 0 && x1 == 0) {
} else if (is_short(x0) && is_short(x1)) {
p = add_short(p, x0); if (x1!=0) p = add_short(p, x1);
} else {
p = add_jint(p, x0); p = add_var_int(p, x1);
}
return p;
}
void unpack_2_ints(jint& x0, jint& x1) {
int dlen = datalen();
short* dp = data();
if (dlen <= 2) {
x0 = relocInfo::short_data_at(0, dp, dlen);
x1 = relocInfo::short_data_at(1, dp, dlen);
} else {
assert(dlen <= 4, "too much data");
x0 = relocInfo::jint_data_at(0, dp, dlen);
x1 = relocInfo::jint_data_at(2, dp, dlen);
}
}
protected:
void pd_set_data_value (address x, intptr_t off, bool verify_only = false); // a set or mem-ref
void pd_verify_data_value (address x, intptr_t off) { pd_set_data_value(x, off, true); }
address pd_call_destination (address orig_addr = NULL);
void pd_set_call_destination (address x);
address* pd_address_in_code ();
address pd_get_address_from_code ();
static jint scaled_offset(address x, address base) {
int byte_offset = x - base;
int offset = -byte_offset / relocInfo::addr_unit();
assert(address_from_scaled_offset(offset, base) == x, "just checkin'");
return offset;
}
static jint scaled_offset_null_special(address x, address base) {
if (x == NULL) return 0;
assert(x != base, "offset must not be zero");
return scaled_offset(x, base);
}
static address address_from_scaled_offset(jint offset, address base) {
int byte_offset = -( offset * relocInfo::addr_unit() );
return base + byte_offset;
}
static int32_t runtime_address_to_index(address runtime_address);
static address index_to_runtime_address(int32_t index);
address old_addr_for(address newa, const CodeBuffer* src, CodeBuffer* dest);
address new_addr_for(address olda, const CodeBuffer* src, CodeBuffer* dest);
void normalize_address(address& addr, const CodeSection* dest, bool allow_other_sections = false);
public:
address addr() const { return binding()->addr(); }
nmethod* code() const { return binding()->code(); }
bool addr_in_const() const { return binding()->addr_in_const(); }
protected:
short* data() const { return binding()->data(); }
int datalen() const { return binding()->datalen(); }
int format() const { return binding()->format(); }
public:
virtual relocInfo::relocType type() { return relocInfo::none; }
virtual bool is_call() { return false; }
virtual bool is_data() { return false; }
virtual address value();
virtual void set_value(address x);
virtual void clear_inline_cache() { }
virtual void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest) { }
void print();
};
inline RelocationHolder::RelocationHolder() {
new(*this) Relocation();
}
inline RelocationHolder::RelocationHolder(Relocation* r) {
for (int i = 0; i < _relocbuf_size; i++) {
_relocbuf[i] = ((void**)r)[i];
}
}
relocInfo::relocType RelocationHolder::type() const {
return reloc()->type();
}
class DataRelocation : public Relocation {
public:
bool is_data() { return true; }
virtual int offset() { return 0; }
address value() = 0;
void set_value(address x) { set_value(x, offset()); }
void set_value(address x, intptr_t o) {
if (addr_in_const())
else
pd_set_data_value(x, o);
}
void verify_value(address x) {
if (addr_in_const())
assert(*(address*)addr() == x, "must agree");
else
pd_verify_data_value(x, offset());
}
};
class CallRelocation : public Relocation {
public:
bool is_call() { return true; }
address destination() { return pd_call_destination(); }
void set_destination(address x); // pd_set_call_destination
void fix_relocation_after_move(const CodeBuffer* src, CodeBuffer* dest);
address value() { return destination(); }
void set_value(address x) { set_destination(x); }
};
class oop_Relocation : public DataRelocation {
relocInfo::relocType type() { return relocInfo::oop_type; }
public:
static RelocationHolder spec(int oop_index, int offset = 0) {
assert(oop_index > 0, "must be a pool-resident oop");
RelocationHolder rh = newHolder();
new(rh) oop_Relocation(oop_index, offset);
return rh;
}
static RelocationHolder spec_for_immediate() {
const int oop_index = 0;
const int offset = 0; // if you want an offset, use the oop pool
RelocationHolder rh = newHolder();
new(rh) oop_Relocation(oop_index, offset);
return rh;
}
private:
jint _oop_index; // if > 0, index into CodeBlob::oop_at
jint _offset; // byte offset to apply to the oop itself
oop_Relocation(int oop_index, int offset) {
_oop_index = oop_index; _offset = offset;
}
friend class RelocIterator;
oop_Relocation() { }
public:
int oop_index() { return _oop_index; }
int offset() { return _offset; }
void pack_data_to(CodeSection* dest);
void unpack_data();
void fix_oop_relocation(); // reasserts oop value
void verify_oop_relocation();
address value() { return (address) *oop_addr(); }
bool oop_is_immediate() { return oop_index() == 0; }
oop* oop_addr(); // addr or &pool[jint_data]
oop oop_value(); // *oop_addr
};
class metadata_Relocation : public DataRelocation {
relocInfo::relocType type() { return relocInfo::metadata_type; }
public:
static RelocationHolder spec(int metadata_index, int offset = 0) {
assert(metadata_index > 0, "must be a pool-resident metadata");
RelocationHolder rh = newHolder();
new(rh) metadata_Relocation(metadata_index, offset);
return rh;
}
static RelocationHolder spec_for_immediate() {
const int metadata_index = 0;
const int offset = 0; // if you want an offset, use the metadata pool
RelocationHolder rh = newHolder();
new(rh) metadata_Relocation(metadata_index, offset);
return rh;
}
private:
jint _metadata_index; // if > 0, index into nmethod::metadata_at
jint _offset; // byte offset to apply to the metadata itself
metadata_Relocation(int metadata_index, int offset) {
_metadata_index = metadata_index; _offset = offset;
}
friend class RelocIterator;
metadata_Relocation() { }
void pd_fix_value(address x);
public:
int metadata_index() { return _metadata_index; }
int offset() { return _offset; }
void pack_data_to(CodeSection* dest);
void unpack_data();
void fix_metadata_relocation(); // reasserts metadata value
void verify_metadata_relocation();
address value() { return (address) *metadata_addr(); }
bool metadata_is_immediate() { return metadata_index() == 0; }
Metadata** metadata_addr(); // addr or &pool[jint_data]
Metadata* metadata_value(); // *metadata_addr
};
class virtual_call_Relocation : public CallRelocation {
relocInfo::relocType type() { return relocInfo::virtual_call_type; }
public:
static RelocationHolder spec(address cached_value) {
RelocationHolder rh = newHolder();
new(rh) virtual_call_Relocation(cached_value);
return rh;
}
virtual_call_Relocation(address cached_value) {
_cached_value = cached_value;
assert(cached_value != NULL, "first oop address must be specified");
}
private:
address _cached_value; // location of set-value instruction
friend class RelocIterator;
virtual_call_Relocation() { }
public:
address cached_value();
void pack_data_to(CodeSection* dest);
void unpack_data();
void clear_inline_cache();
};
class opt_virtual_call_Relocation : public CallRelocation {
relocInfo::relocType type() { return relocInfo::opt_virtual_call_type; }
public:
static RelocationHolder spec() {
RelocationHolder rh = newHolder();
new(rh) opt_virtual_call_Relocation();
return rh;
}
private:
friend class RelocIterator;
opt_virtual_call_Relocation() { }
public:
void clear_inline_cache();
address static_stub();
};
class static_call_Relocation : public CallRelocation {
relocInfo::relocType type() { return relocInfo::static_call_type; }
public:
static RelocationHolder spec() {
RelocationHolder rh = newHolder();
new(rh) static_call_Relocation();
return rh;
}
private:
friend class RelocIterator;
static_call_Relocation() { }
public:
void clear_inline_cache();
address static_stub();
};
class static_stub_Relocation : public Relocation {
relocInfo::relocType type() { return relocInfo::static_stub_type; }
public:
static RelocationHolder spec(address static_call) {
RelocationHolder rh = newHolder();
new(rh) static_stub_Relocation(static_call);
return rh;
}
private:
address _static_call; // location of corresponding static_call
static_stub_Relocation(address static_call) {
_static_call = static_call;
}
friend class RelocIterator;
static_stub_Relocation() { }
public:
void clear_inline_cache();
address static_call() { return _static_call; }
void pack_data_to(CodeSection* dest);
void unpack_data();
};
class runtime_call_Relocation : public CallRelocation {
relocInfo::relocType type() { return relocInfo::runtime_call_type; }
public:
static RelocationHolder spec() {
RelocationHolder rh = newHolder();
new(rh) runtime_call_Relocation();
return rh;
}
private:
friend class RelocIterator;
runtime_call_Relocation() { }
public:
};
class trampoline_stub_Relocation : public Relocation {
relocInfo::relocType type() { return relocInfo::trampoline_stub_type; }
public:
static RelocationHolder spec(address static_call) {
RelocationHolder rh = newHolder();
return (new (rh) trampoline_stub_Relocation(static_call));
}
private:
address _owner; // Address of the NativeCall that owns the trampoline.
trampoline_stub_Relocation(address owner) {
_owner = owner;
}
friend class RelocIterator;
trampoline_stub_Relocation() { }
public:
address owner() { return _owner; }
void pack_data_to(CodeSection * dest);
void unpack_data();
static address get_trampoline_for(address call, nmethod* code);
};
ssssssss20
最新推荐文章于 2024-08-01 15:05:06 发布