< prev index next >

src/hotspot/share/ci/ciEnv.cpp

Print this page
@@ -21,10 +21,12 @@
   * questions.
   *
   */
  
  #include "precompiled.hpp"
+ #include "cds/archiveBuilder.hpp"
+ #include "cds/cdsConfig.hpp"
  #include "ci/ciConstant.hpp"
  #include "ci/ciEnv.hpp"
  #include "ci/ciField.hpp"
  #include "ci/ciInstance.hpp"
  #include "ci/ciInstanceKlass.hpp"

@@ -38,18 +40,20 @@
  #include "classfile/systemDictionary.hpp"
  #include "classfile/vmClasses.hpp"
  #include "classfile/vmSymbols.hpp"
  #include "code/codeCache.hpp"
  #include "code/scopeDesc.hpp"
+ #include "code/SCCache.hpp"
  #include "compiler/compilationLog.hpp"
  #include "compiler/compilationPolicy.hpp"
  #include "compiler/compileBroker.hpp"
  #include "compiler/compilerEvent.hpp"
  #include "compiler/compileLog.hpp"
  #include "compiler/compileTask.hpp"
  #include "compiler/disassembler.hpp"
  #include "gc/shared/collectedHeap.inline.hpp"
+ #include "gc/shared/barrierSetNMethod.hpp"
  #include "interpreter/bytecodeStream.hpp"
  #include "interpreter/linkResolver.hpp"
  #include "jfr/jfrEvents.hpp"
  #include "jvm.h"
  #include "logging/log.hpp"

@@ -64,13 +68,15 @@
  #include "oops/objArrayKlass.hpp"
  #include "oops/objArrayOop.inline.hpp"
  #include "oops/oop.inline.hpp"
  #include "oops/resolvedIndyEntry.hpp"
  #include "oops/symbolHandle.hpp"
+ #include "oops/trainingData.hpp"
  #include "prims/jvmtiExport.hpp"
  #include "prims/methodHandles.hpp"
  #include "runtime/fieldDescriptor.inline.hpp"
+ #include "runtime/flags/flagSetting.hpp"
  #include "runtime/handles.inline.hpp"
  #include "runtime/init.hpp"
  #include "runtime/javaThread.hpp"
  #include "runtime/jniHandles.inline.hpp"
  #include "runtime/reflection.hpp"

@@ -169,10 +175,12 @@
    _jvmti_can_hotswap_or_post_breakpoint = false;
    _jvmti_can_access_local_variables = false;
    _jvmti_can_post_on_exceptions = false;
    _jvmti_can_pop_frame = false;
  
+   _scc_clinit_barriers_entry = nullptr;
+ 
    _dyno_klasses = nullptr;
    _dyno_locs = nullptr;
    _dyno_name[0] = '\0';
  }
  

@@ -289,10 +297,12 @@
    _jvmti_can_hotswap_or_post_breakpoint = false;
    _jvmti_can_access_local_variables = false;
    _jvmti_can_post_on_exceptions = false;
    _jvmti_can_pop_frame = false;
  
+   _scc_clinit_barriers_entry = nullptr;
+ 
    _dyno_klasses = nullptr;
    _dyno_locs = nullptr;
  }
  
  ciEnv::~ciEnv() {

@@ -361,33 +371,10 @@
    // Need lock?
    _dtrace_method_probes = DTraceMethodProbes;
    _dtrace_alloc_probes  = DTraceAllocProbes;
  }
  
- // ------------------------------------------------------------------
- // helper for lazy exception creation
- ciInstance* ciEnv::get_or_create_exception(jobject& handle, Symbol* name) {
-   VM_ENTRY_MARK;
-   if (handle == nullptr) {
-     // Cf. universe.cpp, creation of Universe::_null_ptr_exception_instance.
-     InstanceKlass* ik = SystemDictionary::find_instance_klass(THREAD, name, Handle(), Handle());
-     jobject objh = nullptr;
-     if (ik != nullptr) {
-       oop obj = ik->allocate_instance(THREAD);
-       if (!HAS_PENDING_EXCEPTION)
-         objh = JNIHandles::make_global(Handle(THREAD, obj));
-     }
-     if (HAS_PENDING_EXCEPTION) {
-       CLEAR_PENDING_EXCEPTION;
-     } else {
-       handle = objh;
-     }
-   }
-   oop obj = JNIHandles::resolve(handle);
-   return obj == nullptr? nullptr: get_object(obj)->as_instance();
- }
- 
  ciInstanceKlass* ciEnv::get_box_klass_for_primitive_type(BasicType type) {
    switch (type) {
      case T_BOOLEAN: return Boolean_klass();
      case T_BYTE   : return Byte_klass();
      case T_CHAR   : return Character_klass();

@@ -403,29 +390,26 @@
    }
  }
  
  ciInstance* ciEnv::ArrayIndexOutOfBoundsException_instance() {
    if (_ArrayIndexOutOfBoundsException_instance == nullptr) {
-     _ArrayIndexOutOfBoundsException_instance
-           = get_or_create_exception(_ArrayIndexOutOfBoundsException_handle,
-           vmSymbols::java_lang_ArrayIndexOutOfBoundsException());
+     VM_ENTRY_MARK;
+     _ArrayIndexOutOfBoundsException_instance = get_object(Universe::array_index_oob_exception_instance())->as_instance();
    }
    return _ArrayIndexOutOfBoundsException_instance;
  }
  ciInstance* ciEnv::ArrayStoreException_instance() {
    if (_ArrayStoreException_instance == nullptr) {
-     _ArrayStoreException_instance
-           = get_or_create_exception(_ArrayStoreException_handle,
-           vmSymbols::java_lang_ArrayStoreException());
+     VM_ENTRY_MARK;
+     _ArrayStoreException_instance = get_object(Universe::array_store_exception_instance())->as_instance();
    }
    return _ArrayStoreException_instance;
  }
  ciInstance* ciEnv::ClassCastException_instance() {
    if (_ClassCastException_instance == nullptr) {
-     _ClassCastException_instance
-           = get_or_create_exception(_ClassCastException_handle,
-           vmSymbols::java_lang_ClassCastException());
+     VM_ENTRY_MARK;
+     _ClassCastException_instance = get_object(Universe::class_cast_exception_instance())->as_instance();
    }
    return _ClassCastException_instance;
  }
  
  ciInstance* ciEnv::the_null_string() {

@@ -915,15 +899,15 @@
  
      if (holder_is_accessible) {  // Our declared holder is loaded.
        constantTag tag = cpool->tag_ref_at(index, bc);
        assert(accessor->get_instanceKlass() == cpool->pool_holder(), "not the pool holder?");
        Method* m = lookup_method(accessor, holder, name_sym, sig_sym, bc, tag);
-       if (m != nullptr &&
-           (bc == Bytecodes::_invokestatic
-            ?  m->method_holder()->is_not_initialized()
-            : !m->method_holder()->is_loaded())) {
-         m = nullptr;
+       if (m != nullptr) {
+         ciInstanceKlass* cik = get_instance_klass(m->method_holder());
+         if ((bc == Bytecodes::_invokestatic && cik->is_not_initialized()) || !cik->is_loaded()) {
+           m = nullptr;
+         }
        }
        if (m != nullptr && ReplayCompiles && !ciReplay::is_loaded(m)) {
          m = nullptr;
        }
        if (m != nullptr) {

@@ -1000,11 +984,11 @@
  // ciEnv::validate_compile_task_dependencies
  //
  // Check for changes during compilation (e.g. class loads, evolution,
  // breakpoints, call site invalidation).
  void ciEnv::validate_compile_task_dependencies(ciMethod* target) {
-   if (failing())  return;  // no need for further checks
+   assert(!failing(), "should not call this when failing");
  
    Dependencies::DepType result = dependencies()->validate_dependencies(_task);
    if (result != Dependencies::end_marker) {
      if (result == Dependencies::call_site_target_value) {
        _inc_decompile_count_on_failure = false;

@@ -1027,26 +1011,32 @@
                              int frame_words,
                              OopMapSet* oop_map_set,
                              ExceptionHandlerTable* handler_table,
                              ImplicitExceptionTable* inc_table,
                              AbstractCompiler* compiler,
+                             bool has_clinit_barriers,
+                             bool for_preload,
                              bool has_unsafe_access,
                              bool has_wide_vectors,
                              bool has_monitors,
                              int immediate_oops_patched,
-                             RTMState  rtm_state) {
+                             bool install_code,
+                             RTMState  rtm_state,
+                             SCCEntry* scc_entry) {
    VM_ENTRY_MARK;
    nmethod* nm = nullptr;
    {
      methodHandle method(THREAD, target->get_Method());
+     bool preload = task()->preload(); // Code is preloaded before Java method execution
  
      // We require method counters to store some method state (max compilation levels) required by the compilation policy.
-     if (method->get_method_counters(THREAD) == nullptr) {
+     if (!preload && method->get_method_counters(THREAD) == nullptr) {
        record_failure("can't create method counters");
        // All buffers in the CodeBuffer are allocated in the CodeCache.
        // If the code buffer is created on each compile attempt
        // as in C2, then it must be freed.
+       // But keep shared code.
        code_buffer->free_blob();
        return;
      }
  
      // Check if memory should be freed before allocation

@@ -1059,10 +1049,16 @@
      // and invalidating our dependencies until we install this method.
      // No safepoints are allowed. Otherwise, class redefinition can occur in between.
      MutexLocker ml(Compile_lock);
      NoSafepointVerifier nsv;
  
+     if (scc_entry != nullptr && scc_entry->not_entrant()) {
+       // This shared code was marked invalid while it was loaded
+       code_buffer->free_blob();
+       return;
+     }
+ 
      // Change in Jvmti state may invalidate compilation.
      if (!failing() && jvmti_state_changed()) {
        record_failure("Jvmti state change invalidated dependencies");
      }
  

@@ -1071,26 +1067,33 @@
          ( (!dtrace_method_probes() && DTraceMethodProbes) ||
            (!dtrace_alloc_probes() && DTraceAllocProbes) )) {
        record_failure("DTrace flags change invalidated dependencies");
      }
  
-     if (!failing() && target->needs_clinit_barrier() &&
+     if (!preload && !failing() && target->needs_clinit_barrier() &&
          target->holder()->is_in_error_state()) {
        record_failure("method holder is in error state");
      }
  
-     if (!failing()) {
+     if (!failing() && (scc_entry == nullptr)) {
        if (log() != nullptr) {
          // Log the dependencies which this compilation declares.
          dependencies()->log_all_dependencies();
        }
  
        // Encode the dependencies now, so we can check them right away.
        dependencies()->encode_content_bytes();
- 
+     }
+     // Check for {class loads, evolution, breakpoints, ...} during compilation
+     if (!failing() && install_code) {
        // Check for {class loads, evolution, breakpoints, ...} during compilation
        validate_compile_task_dependencies(target);
+       if (failing() && preload) {
+         ResourceMark rm;
+         char *method_name = method->name_and_sig_as_C_string();
+         log_warning(scc)("preload code for '%s' failed dependency check", method_name);
+       }
      }
  #if INCLUDE_RTM_OPT
      if (!failing() && (rtm_state != NoRTM) &&
          (method()->method_data() != nullptr) &&
          (method()->method_data()->rtm_state() != rtm_state)) {

@@ -1114,27 +1117,60 @@
      }
  
      assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry");
      assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
  
-     nm =  nmethod::new_nmethod(method,
-                                compile_id(),
-                                entry_bci,
-                                offsets,
-                                orig_pc_offset,
-                                debug_info(), dependencies(), code_buffer,
-                                frame_words, oop_map_set,
-                                handler_table, inc_table,
-                                compiler, CompLevel(task()->comp_level()));
- 
+     if (rtm_state == NoRTM && scc_entry == nullptr) {
+       scc_entry = SCCache::store_nmethod(method,
+                              compile_id(),
+                              entry_bci,
+                              offsets,
+                              orig_pc_offset,
+                              debug_info(), dependencies(), code_buffer,
+                              frame_words, oop_map_set,
+                              handler_table, inc_table,
+                              compiler,
+                              CompLevel(task()->comp_level()),
+                              has_clinit_barriers,
+                              for_preload,
+                              has_unsafe_access,
+                              has_wide_vectors,
+                              has_monitors);
+       if (scc_entry != nullptr) {
+         scc_entry->set_inlined_bytecodes(num_inlined_bytecodes());
+         if (has_clinit_barriers) {
+           set_scc_clinit_barriers_entry(scc_entry); // Record it
+           // Build second version of code without class initialization barriers
+           code_buffer->free_blob();
+           return;
+         } else if (!for_preload) {
+           SCCEntry* previous_entry = scc_clinit_barriers_entry();
+           scc_entry->set_next(previous_entry); // Link it for case of deoptimization
+         }
+       }
+     }
+     if (install_code) {
+       nm =  nmethod::new_nmethod(method,
+                                  compile_id(),
+                                  entry_bci,
+                                  offsets,
+                                  orig_pc_offset,
+                                  debug_info(), dependencies(), code_buffer,
+                                  frame_words, oop_map_set,
+                                  handler_table, inc_table,
+                                  compiler, CompLevel(task()->comp_level()),
+                                  scc_entry);
+     }
      // Free codeBlobs
      code_buffer->free_blob();
  
      if (nm != nullptr) {
        nm->set_has_unsafe_access(has_unsafe_access);
        nm->set_has_wide_vectors(has_wide_vectors);
        nm->set_has_monitors(has_monitors);
+       nm->set_preloaded(preload);
+       nm->set_has_clinit_barriers(has_clinit_barriers);
        assert(!method->is_synchronized() || nm->has_monitors(), "");
  #if INCLUDE_RTM_OPT
        nm->set_rtm_state(rtm_state);
  #endif
  

@@ -1154,25 +1190,42 @@
  
          LogTarget(Info, nmethod, install) lt;
          if (lt.is_enabled()) {
            ResourceMark rm;
            char *method_name = method->name_and_sig_as_C_string();
-           lt.print("Installing method (%d) %s ",
-                     task()->comp_level(), method_name);
+           lt.print("Installing method (L%d) %s id=%d scc=%s%s%u",
+                     task()->comp_level(), method_name, compile_id(),
+                     task()->is_scc() ? "A" : "", preload ? "P" : "",
+                     (scc_entry != nullptr ? scc_entry->offset() : 0));
          }
          // Allow the code to be executed
          MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
          if (nm->make_in_use()) {
-           method->set_code(method, nm);
+ #ifdef ASSERT
+           BarrierSetNMethod* bs_nm = BarrierSet::barrier_set()->barrier_set_nmethod();
+           if (bs_nm != nullptr && bs_nm->supports_entry_barrier(nm)) {
+             if (!bs_nm->is_armed(nm)) {
+               log_info(init)("nmethod %d %d not armed", nm->compile_id(), nm->comp_level());
+             }
+           }
+ #endif // ASSERT
+           if (preload) {
+             method->set_preload_code(nm);
+           }
+           if (!preload || target->holder()->is_linked()) {
+             method->set_code(method, nm);
+           }
          }
        } else {
          LogTarget(Info, nmethod, install) lt;
          if (lt.is_enabled()) {
            ResourceMark rm;
            char *method_name = method->name_and_sig_as_C_string();
-           lt.print("Installing osr method (%d) %s @ %d",
-                     task()->comp_level(), method_name, entry_bci);
+           lt.print("Installing osr method (L%d) %s @ %d id=%u scc=%s%u",
+                    task()->comp_level(), method_name, entry_bci, compile_id(),
+                    task()->is_scc() ? "A" : "",
+                    (scc_entry != nullptr ? scc_entry->offset() : 0));
          }
          MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
          if (nm->make_in_use()) {
            method->method_holder()->add_osr_nmethod(nm);
          }

@@ -1183,13 +1236,15 @@
    NoSafepointVerifier nsv;
    if (nm != nullptr) {
      // Compilation succeeded, post what we know about it
      nm->post_compiled_method(task());
      task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
-   } else {
+   } else if (install_code) {
      // The CodeCache is full.
      record_failure("code cache is full");
+   } else {
+     task()->set_num_inlined_bytecodes(num_inlined_bytecodes());
    }
  
    // safepoints are allowed again
  }
  

@@ -1216,10 +1271,17 @@
  
  // ------------------------------------------------------------------
  // ciEnv::notice_inlined_method()
  void ciEnv::notice_inlined_method(ciMethod* method) {
    _num_inlined_bytecodes += method->code_size_for_inlining();
+   CompileTrainingData* tdata = task()->training_data();
+   if (tdata != nullptr) {
+     GUARDED_VM_ENTRY({
+       methodHandle mh(Thread::current(), method->get_Method());
+       tdata->notice_inlined_method(task(), mh);
+     });
+   }
  }
  
  // ------------------------------------------------------------------
  // ciEnv::num_inlined_bytecodes()
  int ciEnv::num_inlined_bytecodes() const {

@@ -1737,5 +1799,76 @@
  }
  
  void ciEnv::dump_replay_data_version(outputStream* out) {
    out->print_cr("version %d", REPLAY_VERSION);
  }
+ 
+ bool ciEnv::is_precompiled() {
+   return (task() != nullptr) && (task()->compile_reason() == CompileTask::Reason_Precompile          ||
+                                  task()->compile_reason() == CompileTask::Reason_PrecompileForPreload);
+ }
+ 
+ bool ciEnv::is_fully_initialized(InstanceKlass* ik) {
+   assert(is_precompiled(), "");
+   if (task()->method()->method_holder() == ik) {
+     return true; // FIXME: may be too strong; being_initialized, at least
+   }
+   switch (task()->compile_reason()) {
+     case CompileTask::Reason_Precompile: {
+       // check init dependencies
+       MethodTrainingData* mtd = TrainingData::lookup_for(task()->method());
+       if (mtd != nullptr) {
+         CompileTrainingData* ctd = mtd->last_toplevel_compile(task()->comp_level());
+         if (ctd != nullptr) {
+           for (int i = 0; i < ctd->init_dep_count(); i++) {
+             KlassTrainingData* ktd = ctd->init_dep(i);
+             if (ktd->has_holder() && (ktd->holder() == ik)) {
+               log_trace(precompile)("%d: init_dependency: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+               return true; // init dependency present
+             }
+           }
+         }
+       }
+       return false; // no init dependency
+     }
+     case CompileTask::Reason_PrecompileForPreload: {
+       // FIXME: assumes that all shared classes are initialized
+       if (ik->is_shared()) {
+         return true; // class init barriers
+       }
+       if (CDSConfig::is_dumping_final_static_archive() && ArchiveBuilder::is_active() &&
+           ArchiveBuilder::current()->has_been_archived((address)ik)) {
+         return true; // class init barriers
+       }
+       return false;
+     }
+     default: fatal("%s", CompileTask::reason_name(task()->compile_reason()));
+   }
+   return false;
+ }
+ 
+ InstanceKlass::ClassState ciEnv::compute_init_state_for_precompiled(InstanceKlass* ik) {
+   ASSERT_IN_VM;
+   assert(is_precompiled(), "");
+   ResourceMark rm;
+   if (is_fully_initialized(ik)) {
+     log_trace(precompile)("%d: fully_initialized: %s", task()->compile_id(), ik->external_name());
+     return InstanceKlass::ClassState::fully_initialized;
+   } else if (MetaspaceObj::is_shared(ik)) {
+     guarantee(ik->is_loaded(), ""); // FIXME: assumes pre-loading by CDS; ik->is_linked() requires pre-linking
+     log_trace(precompile)("%d: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+     return ik->init_state(); // not yet initialized
+   } else if (CDSConfig::is_dumping_final_static_archive() && ArchiveBuilder::is_active()) {
+     if (!ArchiveBuilder::current()->has_been_archived((address)ik)) {
+       fatal("New workflow: should not compile code for unarchived class: %s", ik->external_name());
+     }
+     guarantee(ik->is_loaded(), "");
+     log_trace(precompile)("%d: %s: %s", task()->compile_id(), InstanceKlass::state2name(ik->init_state()), ik->external_name());
+     return ik->init_state(); // not yet initialized
+   } else {
+     // Not present in the archive.
+     fatal("unloaded: %s", ik->external_name());
+ //    guarantee(SystemDictionaryShared::lookup_init_state(ik) == ik->init_state(), "");
+     log_trace(precompile)("%d: allocated: %s", task()->compile_id(), ik->external_name());
+     return InstanceKlass::ClassState::allocated; // not yet linked
+   }
+ }
< prev index next >