< prev index next >

src/hotspot/share/oops/method.cpp

Print this page
@@ -1,7 +1,7 @@
  /*
-  * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
+  * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.

@@ -57,10 +57,11 @@
  #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/continuationEntry.hpp"
  #include "runtime/frame.inline.hpp"

@@ -114,11 +115,10 @@
  
    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;)
  }
  

@@ -152,15 +152,25 @@
  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();
  }

@@ -384,11 +394,11 @@
  }
  
  void Method::metaspace_pointers_do(MetaspaceClosure* it) {
    log_trace(cds)("Iter(Method): %p", this);
  
-   if (!method_holder()->is_rewritten()) {
+   if (!method_holder()->is_rewritten() || CDSConfig::is_valhalla_preview()) {
      it->push(&_constMethod, MetaspaceClosure::_writable);
    } else {
      it->push(&_constMethod);
    }
    it->push(&_method_data);

@@ -661,16 +671,29 @@
  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");
+   NoSafepointVerifier nsv;
+   SignatureStream ss(signature());
+   while (!ss.at_return_type()) {
+     ss.next();
+   }
+   return ss.as_inline_klass(method_holder());
+ }
+ 
  bool Method::is_vanilla_constructor() const {
    // Returns true if this method is a vanilla constructor, i.e. an "<init>" "()V" method
    // which only calls the superclass vanilla constructor and possibly does stores of
    // zero constants to local fields:
    //
-   //   aload_0
+   //   aload_0, _fast_aload_0, or _nofast_aload_0
    //   invokespecial
    //   indexbyte1
    //   indexbyte2
    //
    // followed by an (optional) sequence of:

@@ -690,11 +713,12 @@
    int size = code_size();
    // Check if size match
    if (size == 0 || size % 5 != 0) return false;
    address cb = code_base();
    int last = size - 1;
-   if (cb[0] != Bytecodes::_aload_0 || cb[1] != Bytecodes::_invokespecial || cb[last] != Bytecodes::_return) {
+   if ((cb[0] != Bytecodes::_aload_0 && cb[0] != Bytecodes::_fast_aload_0 && cb[0] != Bytecodes::_nofast_aload_0) ||
+        cb[1] != Bytecodes::_invokespecial || cb[last] != Bytecodes::_return) {
      // Does not call superclass default constructor
      return false;
    }
    // Check optional sequence
    for (int i = 4; i < last; i += 5) {

@@ -852,10 +876,15 @@
      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;

@@ -873,42 +902,41 @@
      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)));
+           Bytecodes::is_return(java_code_at(last_index)) &&
+           !has_scalarized_args());
  }
  
- bool Method::is_initializer() const {
-   return is_object_initializer() || is_static_initializer();
- }
- 
- bool Method::has_valid_initializer_flags() const {
-   return (is_static() ||
-           method_holder()->major_version() < 51);
- }
- 
- bool Method::is_static_initializer() const {
+ 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() &&
-          has_valid_initializer_flags();
+   return (name() == vmSymbols::class_initializer_name() &&
+           (is_static() ||
+            method_holder()->major_version() < 51));
  }
  
- bool Method::is_object_initializer() const {
-    return name() == vmSymbols::object_initializer_name();
+ // 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();
  }

@@ -962,11 +990,11 @@
    return best_line;
  }
  
  
  bool Method::is_klass_loaded_by_klass_index(int klass_index) const {
-   if( constants()->tag_at(klass_index).is_unresolved_klass() ) {
+   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;

@@ -978,11 +1006,13 @@
  
  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;
+     if (constants()->tag_at(klass_index).is_unresolved_klass()) {
+       return false;
+     }
    }
    return is_klass_loaded_by_klass_index(klass_index);
  }
  
  

@@ -1147,12 +1177,16 @@
  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;

@@ -1180,10 +1214,12 @@
    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);

@@ -1233,10 +1269,13 @@
    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

@@ -1272,10 +1311,12 @@
      }
    }
  
    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.

@@ -1288,10 +1329,22 @@
    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.

@@ -1319,10 +1372,12 @@
      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
  

@@ -2309,10 +2364,35 @@
    } 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

@@ -2341,18 +2421,24 @@
    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 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()));
    }

@@ -2418,10 +2504,11 @@
  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 >