Add weight to compiled/interpreter transitions.

Also:
- Cleanup logging.
- Check ArtMethod status before adding compilation requests.
- Don't request osr compilation if we know AddSamples does not come
  from a back edge.

Bug: 27865109

Change-Id: I84512f7d957b61ce2458360ed430adb151830278
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index a432782..97dbe5d 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -292,7 +292,7 @@
 
         // Pop the shadow frame before calling into compiled code.
         self->PopShadowFrame();
-        ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result);
+        ArtInterpreterToCompiledCodeBridge(self, nullptr, code_item, &shadow_frame, &result);
         // Push the shadow frame back as the caller will expect it.
         self->PushShadowFrame(&shadow_frame);
 
@@ -535,6 +535,10 @@
     return JValue();
   }
 
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr) {
+    jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod());
+  }
   return Execute(self, code_item, *shadow_frame, JValue());
 }
 
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 3453abc..12d70c5 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -503,6 +503,7 @@
                                 uint32_t vregC) ALWAYS_INLINE;
 
 void ArtInterpreterToCompiledCodeBridge(Thread* self,
+                                        ArtMethod* caller,
                                         const DexFile::CodeItem* code_item,
                                         ShadowFrame* shadow_frame,
                                         JValue* result)
@@ -530,6 +531,10 @@
   uint16_t arg_offset = (code_item == nullptr)
                             ? 0
                             : code_item->registers_size_ - code_item->ins_size_;
+  jit::Jit* jit = Runtime::Current()->GetJit();
+  if (jit != nullptr && caller != nullptr) {
+    jit->NotifyInterpreterToCompiledCodeTransition(self, caller);
+  }
   method->Invoke(self, shadow_frame->GetVRegArgs(arg_offset),
                  (shadow_frame->NumberOfVRegs() - arg_offset) * sizeof(uint32_t),
                  result, method->GetInterfaceMethodIfProxy(sizeof(void*))->GetShorty());
@@ -726,7 +731,8 @@
         target->GetEntryPointFromQuickCompiledCode())) {
       ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
     } else {
-      ArtInterpreterToCompiledCodeBridge(self, code_item, new_shadow_frame, result);
+      ArtInterpreterToCompiledCodeBridge(
+          self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result);
     }
   } else {
     UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index fb98175..e5b89e2 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -635,7 +635,7 @@
         jit->InvokeVirtualOrInterface(
             self, receiver, sf_method, shadow_frame.GetDexPC(), called_method);
       }
-      jit->AddSamples(self, sf_method, 1);
+      jit->AddSamples(self, sf_method, 1, /*with_backedges*/false);
     }
     // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
     if (type == kVirtual || type == kInterface) {
@@ -681,7 +681,7 @@
     if (jit != nullptr) {
       jit->InvokeVirtualOrInterface(
           self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method);
-      jit->AddSamples(self, shadow_frame.GetMethod(), 1);
+      jit->AddSamples(self, shadow_frame.GetMethod(), 1, /*with_backedges*/false);
     }
     instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
     // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT.
@@ -1001,8 +1001,11 @@
   return branch_offset <= 0;
 }
 
-void ArtInterpreterToCompiledCodeBridge(Thread* self, const DexFile::CodeItem* code_item,
-                                        ShadowFrame* shadow_frame, JValue* result);
+void ArtInterpreterToCompiledCodeBridge(Thread* self,
+                                        ArtMethod* caller,
+                                        const DexFile::CodeItem* code_item,
+                                        ShadowFrame* shadow_frame,
+                                        JValue* result);
 
 // Explicitly instantiate all DoInvoke functions.
 #define EXPLICIT_DO_INVOKE_TEMPLATE_DECL(_type, _is_range, _do_check)                      \
diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc
index c95af6f..13cfb98 100644
--- a/runtime/interpreter/interpreter_goto_table_impl.cc
+++ b/runtime/interpreter/interpreter_goto_table_impl.cc
@@ -78,7 +78,7 @@
 #define HOTNESS_UPDATE()                                                                       \
   do {                                                                                         \
     if (jit != nullptr) {                                                                      \
-      jit->AddSamples(self, method, 1);                                                        \
+      jit->AddSamples(self, method, 1, /*with_backedges*/ true);                               \
     }                                                                                          \
   } while (false)
 
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index ca1d635..4323d4f 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -89,7 +89,7 @@
 #define HOTNESS_UPDATE()                                                                       \
   do {                                                                                         \
     if (jit != nullptr) {                                                                      \
-      jit->AddSamples(self, method, 1);                                                        \
+      jit->AddSamples(self, method, 1, /*with_backedges*/ true);                               \
     }                                                                                          \
   } while (false)
 
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index f800683..4b3c03e 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -689,7 +689,7 @@
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (jit != nullptr) {
     int16_t count = shadow_frame->GetCachedHotnessCountdown() - shadow_frame->GetHotnessCountdown();
-    jit->AddSamples(self, method, count);
+    jit->AddSamples(self, method, count, /*with_backedges*/ true);
   }
   return MterpSetUpHotnessCountdown(method, shadow_frame);
 }
@@ -702,7 +702,7 @@
   uint32_t dex_pc = shadow_frame->GetDexPC();
   jit::Jit* jit = Runtime::Current()->GetJit();
   if ((jit != nullptr) && (offset <= 0)) {
-    jit->AddSamples(self, method, 1);
+    jit->AddSamples(self, method, 1, /*with_backedges*/ true);
   }
   int16_t countdown_value = MterpSetUpHotnessCountdown(method, shadow_frame);
   if (countdown_value == jit::kJitCheckForOSR) {
@@ -722,7 +722,7 @@
   jit::Jit* jit = Runtime::Current()->GetJit();
   if (offset <= 0) {
     // Keep updating hotness in case a compilation request was dropped.  Eventually it will retry.
-    jit->AddSamples(self, method, 1);
+    jit->AddSamples(self, method, 1, /*with_backedges*/ true);
   }
   // Assumes caller has already determined that an OSR check is appropriate.
   return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 558e443..0a6da2c 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -97,8 +97,9 @@
       LOG(FATAL) << "Priority thread weight cannot be 0.";
     }
   } else {
-    jit_options->priority_thread_weight_ =
-        std::max(jit_options->compile_threshold_ / 2000, static_cast<size_t>(1));
+    jit_options->priority_thread_weight_ = std::max(
+        jit_options->warmup_threshold_ / Jit::kDefaultPriorityThreadWeightRatio,
+        static_cast<size_t>(1));
   }
 
   return jit_options;
@@ -154,6 +155,8 @@
   jit->warm_method_threshold_ = options->GetWarmupThreshold();
   jit->osr_method_threshold_ = options->GetOsrThreshold();
   jit->priority_thread_weight_ = options->GetPriorityThreadWeight();
+  jit->transition_weight_ = std::max(
+      jit->warm_method_threshold_ / kDefaultTransitionRatio, static_cast<size_t>(1));
 
   jit->CreateThreadPool();
 
@@ -240,8 +243,17 @@
   if (!code_cache_->NotifyCompilationOf(method_to_compile, self, osr)) {
     return false;
   }
+
+  VLOG(jit) << "Compiling method "
+            << PrettyMethod(method_to_compile)
+            << " osr=" << std::boolalpha << osr;
   bool success = jit_compile_method_(jit_compiler_handle_, method_to_compile, self, osr);
   code_cache_->DoneCompiling(method_to_compile, self, osr);
+  if (!success) {
+    VLOG(jit) << "Failed to compile method "
+              << PrettyMethod(method_to_compile)
+              << " osr=" << std::boolalpha << osr;
+  }
   return success;
 }
 
@@ -520,15 +532,9 @@
   void Run(Thread* self) OVERRIDE {
     ScopedObjectAccess soa(self);
     if (kind_ == kCompile) {
-      VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_);
-      if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) {
-        VLOG(jit) << "Failed to compile method " << PrettyMethod(method_);
-      }
+      Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false);
     } else if (kind_ == kCompileOsr) {
-      VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_);
-      if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) {
-        VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_);
-      }
+      Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true);
     } else {
       DCHECK(kind_ == kAllocateProfile);
       if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) {
@@ -549,7 +555,7 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
 };
 
-void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) {
+void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count, bool with_backedges) {
   if (thread_pool_ == nullptr) {
     // Should only see this when shutting down.
     DCHECK(Runtime::Current()->IsShuttingDown(self));
@@ -573,7 +579,8 @@
   }
   int32_t new_count = starting_count + count;   // int32 here to avoid wrap-around;
   if (starting_count < warm_method_threshold_) {
-    if (new_count >= warm_method_threshold_) {
+    if ((new_count >= warm_method_threshold_) &&
+        (method->GetProfilingInfo(sizeof(void*)) == nullptr)) {
       bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false);
       if (success) {
         VLOG(jit) << "Start profiling " << PrettyMethod(method);
@@ -595,14 +602,19 @@
     // Avoid jumping more than one state at a time.
     new_count = std::min(new_count, hot_method_threshold_ - 1);
   } else if (starting_count < hot_method_threshold_) {
-    if (new_count >= hot_method_threshold_) {
+    if ((new_count >= hot_method_threshold_) &&
+        !code_cache_->ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
       DCHECK(thread_pool_ != nullptr);
       thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile));
     }
     // Avoid jumping more than one state at a time.
     new_count = std::min(new_count, osr_method_threshold_ - 1);
   } else if (starting_count < osr_method_threshold_) {
-    if (new_count >= osr_method_threshold_) {
+    if (!with_backedges) {
+      // If the samples don't contain any back edge, we don't increment the hotness.
+      return;
+    }
+    if ((new_count >= osr_method_threshold_) &&  !code_cache_->IsOsrCompiled(method)) {
       DCHECK(thread_pool_ != nullptr);
       thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr));
     }
@@ -630,7 +642,7 @@
       !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) {
     method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint());
   } else {
-    AddSamples(thread, method, 1);
+    AddSamples(thread, method, 1, /* with_backedges */false);
   }
 }
 
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 96f9608..ff3acf6 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -43,6 +43,8 @@
  public:
   static constexpr bool kStressMode = kIsDebugBuild;
   static constexpr size_t kDefaultCompileThreshold = kStressMode ? 2 : 10000;
+  static constexpr size_t kDefaultPriorityThreadWeightRatio = 1000;
+  static constexpr size_t kDefaultTransitionRatio = 100;
 
   virtual ~Jit();
   static Jit* Create(JitOptions* options, std::string* error_msg);
@@ -92,7 +94,7 @@
   void MethodEntered(Thread* thread, ArtMethod* method)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
-  void AddSamples(Thread* self, ArtMethod* method, uint16_t samples)
+  void AddSamples(Thread* self, ArtMethod* method, uint16_t samples, bool with_backedges)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
   void InvokeVirtualOrInterface(Thread* thread,
@@ -102,6 +104,16 @@
                                 ArtMethod* callee)
       SHARED_REQUIRES(Locks::mutator_lock_);
 
+  void NotifyInterpreterToCompiledCodeTransition(Thread* self, ArtMethod* caller)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    AddSamples(self, caller, transition_weight_, false);
+  }
+
+  void NotifyCompiledCodeToInterpreterTransition(Thread* self, ArtMethod* callee)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    AddSamples(self, callee, transition_weight_, false);
+  }
+
   // Starts the profile saver if the config options allow profile recording.
   // The profile will be stored in the specified `filename` and will contain
   // information collected from the given `code_paths` (a set of dex locations).
@@ -175,6 +187,7 @@
   uint16_t warm_method_threshold_;
   uint16_t osr_method_threshold_;
   uint16_t priority_thread_weight_;
+  uint16_t transition_weight_;
   std::unique_ptr<ThreadPool> thread_pool_;
 
   DISALLOW_COPY_AND_ASSIGN(Jit);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 820ae6a..1f3e08b 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -366,7 +366,7 @@
     }
     last_update_time_ns_.StoreRelease(NanoTime());
     VLOG(jit)
-        << "JIT added (osr = " << std::boolalpha << osr << std::noboolalpha << ") "
+        << "JIT added (osr=" << std::boolalpha << osr << std::noboolalpha << ") "
         << PrettyMethod(method) << "@" << method
         << " ccache_size=" << PrettySize(CodeCacheSizeLocked()) << ": "
         << " dcache_size=" << PrettySize(DataCacheSizeLocked()) << ": "
@@ -905,15 +905,18 @@
   return last_update_time_ns_.LoadAcquire();
 }
 
+bool JitCodeCache::IsOsrCompiled(ArtMethod* method) {
+  MutexLock mu(Thread::Current(), lock_);
+  return osr_code_map_.find(method) != osr_code_map_.end();
+}
+
 bool JitCodeCache::NotifyCompilationOf(ArtMethod* method, Thread* self, bool osr) {
   if (!osr && ContainsPc(method->GetEntryPointFromQuickCompiledCode())) {
-    VLOG(jit) << PrettyMethod(method) << " is already compiled";
     return false;
   }
 
   MutexLock mu(self, lock_);
   if (osr && (osr_code_map_.find(method) != osr_code_map_.end())) {
-    VLOG(jit) << PrettyMethod(method) << " is already osr compiled";
     return false;
   }
 
@@ -928,7 +931,6 @@
   }
 
   if (info->IsMethodBeingCompiled(osr)) {
-    VLOG(jit) << PrettyMethod(method) << " is already being compiled";
     return false;
   }
 
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 9f18c70..f31cc51 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -186,6 +186,8 @@
 
   void Dump(std::ostream& os) REQUIRES(!lock_);
 
+  bool IsOsrCompiled(ArtMethod* method) REQUIRES(!lock_);
+
  private:
   // Take ownership of maps.
   JitCodeCache(MemMap* code_map,