< 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"

*** 171,10 ***
--- 177,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';
  }
  

*** 291,10 ***
--- 299,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() {

*** 668,11 ***
  ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
                                               int index, int obj_index,
                                               ciInstanceKlass* accessor) {
    if (obj_index >= 0) {
      ciConstant con = get_resolved_constant(cpool, obj_index);
!     if (con.is_valid()) {
        return con;
      }
    }
    constantTag tag = cpool->tag_at(index);
    if (tag.is_int()) {
--- 678,11 ---
  ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
                                               int index, int obj_index,
                                               ciInstanceKlass* accessor) {
    if (obj_index >= 0) {
      ciConstant con = get_resolved_constant(cpool, obj_index);
!     if (con.should_be_constant()) {
        return con;
      }
    }
    constantTag tag = cpool->tag_at(index);
    if (tag.is_int()) {

*** 869,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 && ReplayCompiles && !ciReplay::is_loaded(m)) {
          m = nullptr;
        }
        if (m != nullptr) {
--- 879,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) {
!         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) {

*** 954,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
  
    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;
--- 964,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) {
!   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;

*** 981,26 ***
                              int frame_words,
                              OopMapSet* oop_map_set,
                              ExceptionHandlerTable* handler_table,
                              ImplicitExceptionTable* inc_table,
                              AbstractCompiler* compiler,
                              bool has_unsafe_access,
                              bool has_wide_vectors,
                              bool has_monitors,
                              bool has_scoped_access,
!                             int immediate_oops_patched) {
    VM_ENTRY_MARK;
    nmethod* nm = nullptr;
    {
      methodHandle method(THREAD, target->get_Method());
  
      // We require method counters to store some method state (max compilation levels) required by the compilation policy.
!     if (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.
        code_buffer->free_blob();
        return;
      }
  
      // Check if memory should be freed before allocation
--- 991,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,
                              bool has_scoped_access,
!                             int immediate_oops_patched,
+                             bool install_code,
+                             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 (!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

*** 1013,10 ***
--- 1029,20 ---
      // 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) {
+       // Invalid compilation states:
+       //  - SCCache is closed, SCC entry is garbage.
+       //  - SCC entry indicates this shared code was marked invalid while it was loaded.
+       if (!SCCache::is_on() || scc_entry->not_entrant()) {
+         code_buffer->free_blob();
+         return;
+       }
+     }
+ 
      // Change in Jvmti state may invalidate compilation.
      if (!failing() && jvmti_state_changed()) {
        record_failure("Jvmti state change invalidated dependencies");
      }
  

*** 1025,26 ***
          ( (!dtrace_method_probes() && DTraceMethodProbes) ||
            (!dtrace_alloc_probes() && DTraceAllocProbes) )) {
        record_failure("DTrace flags change invalidated dependencies");
      }
  
!     if (!failing() && target->needs_clinit_barrier() &&
          target->holder()->is_in_error_state()) {
        record_failure("method holder is in error state");
      }
  
!     if (!failing()) {
        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
        validate_compile_task_dependencies(target);
      }
  
      if (failing()) {
        // While not a true deoptimization, it is a preemptive decompile.
        MethodData* mdo = method()->method_data();
--- 1051,33 ---
          ( (!dtrace_method_probes() && DTraceMethodProbes) ||
            (!dtrace_alloc_probes() && DTraceAllocProbes) )) {
        record_failure("DTrace flags change invalidated dependencies");
      }
  
!     if (!preload && !failing() && target->needs_clinit_barrier() &&
          target->holder()->is_in_error_state()) {
        record_failure("method holder is in error state");
      }
  
!     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_info(scc)("preload code for '%s' failed dependency check", method_name);
+       }
      }
  
      if (failing()) {
        // While not a true deoptimization, it is a preemptive decompile.
        MethodData* mdo = method()->method_data();

*** 1060,28 ***
      }
  
      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()));
! 
      // 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_has_scoped_access(has_scoped_access);
        assert(!method->is_synchronized() || nm->has_monitors(), "");
  
        if (entry_bci == InvocationEntryBci) {
          if (TieredCompilation) {
            // If there is an old version we're done with it
--- 1093,62 ---
      }
  
      assert(offsets->value(CodeOffsets::Deopt) != -1, "must have deopt entry");
      assert(offsets->value(CodeOffsets::Exceptions) != -1, "must have exception entry");
  
!     if (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,
+                              has_scoped_access);
+       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_has_scoped_access(has_scoped_access);
+       nm->set_preloaded(preload);
+       nm->set_has_clinit_barriers(has_clinit_barriers);
        assert(!method->is_synchronized() || nm->has_monitors(), "");
  
        if (entry_bci == InvocationEntryBci) {
          if (TieredCompilation) {
            // If there is an old version we're done with it

*** 1098,25 ***
  
          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);
          }
          // 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);
          }
        } 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);
          }
          MutexLocker ml(NMethodState_lock, Mutex::_no_safepoint_check_flag);
          if (nm->make_in_use()) {
            method->method_holder()->add_osr_nmethod(nm);
          }
--- 1165,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 (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()) {
! #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 (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);
          }

*** 1127,13 ***
    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 {
      // The CodeCache is full.
      record_failure("code cache is full");
    }
  
    // safepoints are allowed again
  }
  
--- 1211,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 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
  }
  

*** 1160,10 ***
--- 1246,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 {

*** 1684,5 ***
--- 1777,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 >