< prev index next > src/hotspot/share/oops/method.cpp
Print this page
#include "oops/methodData.hpp"
#include "oops/objArrayKlass.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
+ #include "oops/inlineKlass.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/methodHandles.hpp"
#include "runtime/atomic.hpp"
#include "runtime/arguments.hpp"
#include "runtime/continuationEntry.hpp"
if (access_flags.is_native()) {
clear_native_function();
set_signature_handler(nullptr);
}
-
NOT_PRODUCT(set_compiled_invocation_count(0);)
// Name is very useful for debugging.
NOT_PRODUCT(_name = name;)
}
address Method::get_c2i_entry() {
assert(adapter() != nullptr, "must have");
return adapter()->get_c2i_entry();
}
+ address Method::get_c2i_inline_entry() {
+ assert(adapter() != nullptr, "must have");
+ return adapter()->get_c2i_inline_entry();
+ }
+
address Method::get_c2i_unverified_entry() {
assert(adapter() != nullptr, "must have");
return adapter()->get_c2i_unverified_entry();
}
+ address Method::get_c2i_unverified_inline_entry() {
+ assert(adapter() != nullptr, "must have");
+ return adapter()->get_c2i_unverified_inline_entry();
+ }
+
address Method::get_c2i_no_clinit_check_entry() {
assert(VM_Version::supports_fast_class_init_checks(), "");
assert(adapter() != nullptr, "must have");
return adapter()->get_c2i_no_clinit_check_entry();
}
}
void Method::metaspace_pointers_do(MetaspaceClosure* it) {
log_trace(cds)("Iter(Method): %p", this);
! if (!method_holder()->is_rewritten()) {
it->push(&_constMethod, MetaspaceClosure::_writable);
} else {
it->push(&_constMethod);
}
it->push(&_method_data);
}
void Method::metaspace_pointers_do(MetaspaceClosure* it) {
log_trace(cds)("Iter(Method): %p", this);
! if (!method_holder()->is_rewritten() || CDSConfig::is_valhalla_preview()) {
it->push(&_constMethod, MetaspaceClosure::_writable);
} else {
it->push(&_constMethod);
}
it->push(&_method_data);
int Method::extra_stack_words() {
// not an inline function, to avoid a header dependency on Interpreter
return extra_stack_entries() * Interpreter::stackElementSize;
}
+ // InlineKlass the method is declared to return. This must not
+ // safepoint as it is called with references live on the stack at
+ // locations the GC is unaware of.
+ InlineKlass* Method::returns_inline_type(Thread* thread) const {
+ assert(InlineTypeReturnedAsFields, "Inline types should never be returned as fields");
+ if (is_native()) {
+ return nullptr;
+ }
+ NoSafepointVerifier nsv;
+ SignatureStream ss(signature());
+ while (!ss.at_return_type()) {
+ ss.next();
+ }
+ return ss.as_inline_klass(method_holder());
+ }
+
bool Method::compute_has_loops_flag() {
BytecodeStream bcs(methodHandle(Thread::current(), this));
Bytecodes::Code bc;
while ((bc = bcs.next()) >= 0) {
case Bytecodes::_areturn:
break;
default:
return false;
}
+ if (has_scalarized_return()) {
+ // Don't treat this as (trivial) getter method because the
+ // inline type should be returned in a scalarized form.
+ return false;
+ }
return true;
}
bool Method::is_setter() const {
if (code_size() != 6) return false;
default:
return false;
}
if (java_code_at(2) != Bytecodes::_putfield) return false;
if (java_code_at(5) != Bytecodes::_return) return false;
return true;
}
bool Method::is_constant_getter() const {
int last_index = code_size() - 1;
// Check if the first 1-3 bytecodes are a constant push
// and the last bytecode is a return.
return (2 <= code_size() && code_size() <= 4 &&
Bytecodes::is_const(java_code_at(0)) &&
Bytecodes::length_for(java_code_at(0)) == last_index &&
! Bytecodes::is_return(java_code_at(last_index)));
}
! bool Method::has_valid_initializer_flags() const {
- return (is_static() ||
- method_holder()->major_version() < 51);
- }
-
- bool Method::is_static_initializer() const {
// For classfiles version 51 or greater, ensure that the clinit method is
// static. Non-static methods with the name "<clinit>" are not static
// initializers. (older classfiles exempted for backward compatibility)
! return name() == vmSymbols::class_initializer_name() &&
! has_valid_initializer_flags();
}
! bool Method::is_object_initializer() const {
! return name() == vmSymbols::object_initializer_name();
}
bool Method::needs_clinit_barrier() const {
return is_static() && !method_holder()->is_initialized();
}
default:
return false;
}
if (java_code_at(2) != Bytecodes::_putfield) return false;
if (java_code_at(5) != Bytecodes::_return) return false;
+ if (has_scalarized_args()) {
+ // Don't treat this as (trivial) setter method because the
+ // inline type argument should be passed in a scalarized form.
+ return false;
+ }
return true;
}
bool Method::is_constant_getter() const {
int last_index = code_size() - 1;
// Check if the first 1-3 bytecodes are a constant push
// and the last bytecode is a return.
return (2 <= code_size() && code_size() <= 4 &&
Bytecodes::is_const(java_code_at(0)) &&
Bytecodes::length_for(java_code_at(0)) == last_index &&
! Bytecodes::is_return(java_code_at(last_index)) &&
+ !has_scalarized_args());
}
! bool Method::is_class_initializer() const {
// For classfiles version 51 or greater, ensure that the clinit method is
// static. Non-static methods with the name "<clinit>" are not static
// initializers. (older classfiles exempted for backward compatibility)
! return (name() == vmSymbols::class_initializer_name() &&
! (is_static() ||
+ method_holder()->major_version() < 51));
}
! // A method named <init>, is a classic object constructor.
! bool Method::is_object_constructor() const {
+ return name() == vmSymbols::object_initializer_name();
}
bool Method::needs_clinit_barrier() const {
return is_static() && !method_holder()->is_initialized();
}
return best_line;
}
bool Method::is_klass_loaded_by_klass_index(int klass_index) const {
! if( constants()->tag_at(klass_index).is_unresolved_klass() ) {
Thread *thread = Thread::current();
Symbol* klass_name = constants()->klass_name_at(klass_index);
Handle loader(thread, method_holder()->class_loader());
Handle prot (thread, method_holder()->protection_domain());
return SystemDictionary::find_instance_klass(thread, klass_name, loader, prot) != nullptr;
return best_line;
}
bool Method::is_klass_loaded_by_klass_index(int klass_index) const {
! if( constants()->tag_at(klass_index).is_unresolved_klass()) {
Thread *thread = Thread::current();
Symbol* klass_name = constants()->klass_name_at(klass_index);
Handle loader(thread, method_holder()->class_loader());
Handle prot (thread, method_holder()->protection_domain());
return SystemDictionary::find_instance_klass(thread, klass_name, loader, prot) != nullptr;
bool Method::is_klass_loaded(int refinfo_index, Bytecodes::Code bc, bool must_be_resolved) const {
int klass_index = constants()->klass_ref_index_at(refinfo_index, bc);
if (must_be_resolved) {
// Make sure klass is resolved in constantpool.
! if (constants()->tag_at(klass_index).is_unresolved_klass()) return false;
}
return is_klass_loaded_by_klass_index(klass_index);
}
bool Method::is_klass_loaded(int refinfo_index, Bytecodes::Code bc, bool must_be_resolved) const {
int klass_index = constants()->klass_ref_index_at(refinfo_index, bc);
if (must_be_resolved) {
// Make sure klass is resolved in constantpool.
! if (constants()->tag_at(klass_index).is_unresolved_klass()) {
+ return false;
+ }
}
return is_klass_loaded_by_klass_index(klass_index);
}
void Method::clear_code() {
// this may be null if c2i adapters have not been made yet
// Only should happen at allocate time.
if (adapter() == nullptr) {
_from_compiled_entry = nullptr;
+ _from_compiled_inline_entry = nullptr;
+ _from_compiled_inline_ro_entry = nullptr;
} else {
_from_compiled_entry = adapter()->get_c2i_entry();
+ _from_compiled_inline_entry = adapter()->get_c2i_inline_entry();
+ _from_compiled_inline_ro_entry = adapter()->get_c2i_inline_ro_entry();
}
OrderAccess::storestore();
_from_interpreted_entry = _i2i_entry;
OrderAccess::storestore();
_code = nullptr;
assert(CDSConfig::is_dumping_archive(), "sanity");
_code = nullptr;
_adapter = nullptr;
_i2i_entry = nullptr;
_from_compiled_entry = nullptr;
+ _from_compiled_inline_entry = nullptr;
+ _from_compiled_inline_ro_entry = nullptr;
_from_interpreted_entry = nullptr;
if (is_native()) {
*native_function_addr() = nullptr;
set_signature_handler(nullptr);
set_queued_for_compilation(false);
set_is_not_c2_compilable(false);
set_is_not_c1_compilable(false);
set_is_not_c2_osr_compilable(false);
set_on_stack_flag(false);
+ set_has_scalarized_args(false);
+ set_has_scalarized_return(false);
}
#endif
// Called when the method_holder is getting linked. Setup entrypoints so the method
// is ready to be called from interpreter, compiler, and vtables.
if (is_native() && !has_native_function()) {
set_native_function(
SharedRuntime::native_method_throw_unsatisfied_link_error_entry(),
!native_bind_event_is_interesting);
}
+ if (InlineTypeReturnedAsFields && returns_inline_type(THREAD) && !has_scalarized_return()) {
+ set_has_scalarized_return();
+ }
// Setup compiler entrypoint. This is made eagerly, so we do not need
// special handling of vtables. An alternative is to make adapters more
// lazily by calling make_adapter() from from_compiled_entry() for the
// normal calls. For vtable calls life gets more complicated. When a
}
}
mh->set_adapter_entry(adapter);
mh->_from_compiled_entry = adapter->get_c2i_entry();
+ mh->_from_compiled_inline_entry = adapter->get_c2i_inline_entry();
+ mh->_from_compiled_inline_ro_entry = adapter->get_c2i_inline_ro_entry();
return adapter->get_c2i_entry();
}
// The verified_code_entry() must be called when a invoke is resolved
// on this method.
debug_only(NoSafepointVerifier nsv;)
assert(_from_compiled_entry != nullptr, "must be set");
return _from_compiled_entry;
}
+ address Method::verified_inline_code_entry() {
+ debug_only(NoSafepointVerifier nsv;)
+ assert(_from_compiled_inline_entry != nullptr, "must be set");
+ return _from_compiled_inline_entry;
+ }
+
+ address Method::verified_inline_ro_code_entry() {
+ debug_only(NoSafepointVerifier nsv;)
+ assert(_from_compiled_inline_ro_entry != nullptr, "must be set");
+ return _from_compiled_inline_ro_entry;
+ }
+
// Check that if an nmethod ref exists, it has a backlink to this or no backlink at all
// (could be racing a deopt).
// Not inline to avoid circular ref.
bool Method::check_code() const {
// cached in a register or local. There's a race on the value of the field.
mh->set_highest_comp_level(comp_level);
}
OrderAccess::storestore();
mh->_from_compiled_entry = code->verified_entry_point();
+ mh->_from_compiled_inline_entry = code->verified_inline_entry_point();
+ mh->_from_compiled_inline_ro_entry = code->verified_inline_ro_entry_point();
OrderAccess::storestore();
if (mh->is_continuation_native_intrinsic()) {
assert(mh->_from_interpreted_entry == nullptr, "initialized incorrectly"); // see link_method
} else {
return false;
}
}
+ bool Method::is_scalarized_arg(int idx) const {
+ if (!has_scalarized_args()) {
+ return false;
+ }
+ // Search through signature and check if argument is wrapped in T_METADATA/T_VOID
+ int depth = 0;
+ const GrowableArray<SigEntry>* sig = adapter()->get_sig_cc();
+ for (int i = 0; i < sig->length(); i++) {
+ BasicType bt = sig->at(i)._bt;
+ if (bt == T_METADATA) {
+ depth++;
+ }
+ if (idx == 0) {
+ break; // Argument found
+ }
+ if (bt == T_VOID && (sig->at(i-1)._bt != T_LONG && sig->at(i-1)._bt != T_DOUBLE)) {
+ depth--;
+ }
+ if (depth == 0 && bt != T_LONG && bt != T_DOUBLE) {
+ idx--; // Advance to next argument
+ }
+ }
+ return depth != 0;
+ }
+
#ifndef PRODUCT
void Method::print_jmethod_ids_count(const ClassLoaderData* loader_data, outputStream* out) {
out->print("%d", loader_data->jmethod_ids()->count_methods());
}
#endif // PRODUCT
if (intrinsic_id() != vmIntrinsics::_none)
st->print_cr(" - intrinsic id: %d %s", vmIntrinsics::as_int(intrinsic_id()), vmIntrinsics::name_at(intrinsic_id()));
if (highest_comp_level() != CompLevel_none)
st->print_cr(" - highest level: %d", highest_comp_level());
st->print_cr(" - vtable index: %d", _vtable_index);
st->print_cr(" - i2i entry: " PTR_FORMAT, p2i(interpreter_entry()));
st->print( " - adapters: ");
AdapterHandlerEntry* a = ((Method*)this)->adapter();
if (a == nullptr)
st->print_cr(PTR_FORMAT, p2i(a));
else
a->print_adapter_on(st);
! st->print_cr(" - compiled entry " PTR_FORMAT, p2i(from_compiled_entry()));
st->print_cr(" - code size: %d", code_size());
if (code_size() != 0) {
st->print_cr(" - code start: " PTR_FORMAT, p2i(code_base()));
st->print_cr(" - code end (excl): " PTR_FORMAT, p2i(code_base() + code_size()));
}
if (intrinsic_id() != vmIntrinsics::_none)
st->print_cr(" - intrinsic id: %d %s", vmIntrinsics::as_int(intrinsic_id()), vmIntrinsics::name_at(intrinsic_id()));
if (highest_comp_level() != CompLevel_none)
st->print_cr(" - highest level: %d", highest_comp_level());
st->print_cr(" - vtable index: %d", _vtable_index);
+ #ifdef ASSERT
+ if (valid_itable_index())
+ st->print_cr(" - itable index: %d", itable_index());
+ #endif
st->print_cr(" - i2i entry: " PTR_FORMAT, p2i(interpreter_entry()));
st->print( " - adapters: ");
AdapterHandlerEntry* a = ((Method*)this)->adapter();
if (a == nullptr)
st->print_cr(PTR_FORMAT, p2i(a));
else
a->print_adapter_on(st);
! st->print_cr(" - compiled entry " PTR_FORMAT, p2i(from_compiled_entry()));
+ st->print_cr(" - compiled inline entry " PTR_FORMAT, p2i(from_compiled_inline_entry()));
+ st->print_cr(" - compiled inline ro entry " PTR_FORMAT, p2i(from_compiled_inline_ro_entry()));
st->print_cr(" - code size: %d", code_size());
if (code_size() != 0) {
st->print_cr(" - code start: " PTR_FORMAT, p2i(code_base()));
st->print_cr(" - code end (excl): " PTR_FORMAT, p2i(code_base() + code_size()));
}
void Method::print_value_on(outputStream* st) const {
assert(is_method(), "must be method");
st->print("%s", internal_name());
print_address_on(st);
st->print(" ");
+ if (WizardMode) access_flags().print_on(st);
name()->print_value_on(st);
st->print(" ");
signature()->print_value_on(st);
st->print(" in ");
method_holder()->print_value_on(st);
< prev index next >