Add compiler stats, fix super call slow path

Add statistics of when the compiler framework has allowed AOT
compilation to be optimistic.
Fix bug that was forcing many calls to be unnecessarily slow path.

Change-Id: I8f0f6ab4738b11161855cf0dc424babed774c998
diff --git a/src/compiler.cc b/src/compiler.cc
index 8210c9b..b1a7066 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -36,7 +36,7 @@
 
 namespace art {
 
-CompiledMethod* oatCompileMethod(const Compiler& compiler, const DexFile::CodeItem* code_item,
+CompiledMethod* oatCompileMethod(Compiler& compiler, const DexFile::CodeItem* code_item,
                                  uint32_t access_flags, uint32_t method_idx,
                                  const ClassLoader* class_loader,
                                  const DexFile& dex_file, InstructionSet);
@@ -54,6 +54,115 @@
   ByteArray* CreateJniDlsymLookupStub();
 }
 
+static double Percentage(size_t x, size_t y) {
+  return 100.0 * ((double)x) / ((double)(x + y));
+}
+
+static void DumpStat(size_t x, size_t y, const char* str) {
+  if (x == 0 && y == 0) {
+    return;
+  }
+  LOG(INFO) << Percentage(x, y) << "% of " << str << " for " << (x + y) << " cases";
+}
+
+void AOTCompilationStats::Dump() {
+  DumpStat(types_in_dex_cache_, types_not_in_dex_cache_, "types known to be in dex cache");
+  DumpStat(strings_in_dex_cache_, strings_not_in_dex_cache_, "strings known to be in dex cache");
+  DumpStat(resolved_types_, unresolved_types_, "types resolved");
+  DumpStat(resolved_instance_fields_, unresolved_instance_fields_, "instance fields resolved");
+  DumpStat(resolved_local_static_fields_ + resolved_static_fields_, unresolved_static_fields_,
+           "static fields resolved");
+  DumpStat(resolved_local_static_fields_, resolved_static_fields_ + unresolved_static_fields_,
+           "static fields local to a class");
+  DumpStat(resolved_virtual_methods_, unresolved_virtual_methods_, "resolved virtual methods");
+  DumpStat(resolved_super_methods_, unresolved_super_methods_, "resolved super-class methods");
+  DumpStat(resolved_interface_methods_, unresolved_interface_methods_, "resolved interface methods");
+}
+
+// Allow lossy statistics in non-debug builds
+#ifndef NDEBUG
+#define STATS_LOCK() MutexLock mu(stats_lock_)
+#else
+#define STATS_LOCK()
+#endif
+
+void AOTCompilationStats::TypeInDexCache() {
+  STATS_LOCK();
+  types_in_dex_cache_++;
+}
+
+void AOTCompilationStats::TypeNotInDexCache() {
+  STATS_LOCK();
+  types_not_in_dex_cache_++;
+}
+
+void AOTCompilationStats::StringInDexCache() {
+  STATS_LOCK();
+  strings_in_dex_cache_++;
+}
+
+void AOTCompilationStats::StringNotInDexCache() {
+  STATS_LOCK();
+  strings_not_in_dex_cache_++;
+}
+
+void AOTCompilationStats::TypeDoesntNeedAccessCheck() {
+  STATS_LOCK();
+  resolved_types_++;
+}
+
+void AOTCompilationStats::TypeNeedsAccessCheck() {
+  STATS_LOCK();
+  unresolved_types_++;
+}
+
+void AOTCompilationStats::ResolvedInstanceField() {
+  STATS_LOCK();
+  resolved_instance_fields_++;
+}
+
+void AOTCompilationStats::UnresolvedInstanceField(){
+  STATS_LOCK();
+  unresolved_instance_fields_++;
+}
+
+void AOTCompilationStats::ResolvedLocalStaticField() {
+  STATS_LOCK();
+  resolved_local_static_fields_++;
+}
+
+void AOTCompilationStats::ResolvedStaticField() {
+  STATS_LOCK();
+  resolved_static_fields_++;
+}
+
+void AOTCompilationStats::UnresolvedStaticField() {
+  STATS_LOCK();
+  unresolved_static_fields_++;
+}
+
+void AOTCompilationStats::ResolvedMethod(bool is_interface, bool is_super) {
+  STATS_LOCK();
+  if (is_interface) {
+    resolved_interface_methods_++;
+  } else if (is_super) {
+    resolved_super_methods_++;
+  } else {
+    resolved_virtual_methods_++;
+  }
+}
+
+void AOTCompilationStats::UnresolvedMethod(bool is_interface, bool is_super) {
+  STATS_LOCK();
+  if (is_interface) {
+    unresolved_interface_methods_++;
+  } else if (is_super) {
+    unresolved_super_methods_++;
+  } else {
+    unresolved_virtual_methods_++;
+  }
+}
+
 Compiler::Compiler(InstructionSet instruction_set, bool image, size_t thread_count,
                    const std::set<std::string>* image_classes)
     : instruction_set_(instruction_set),
@@ -136,6 +245,8 @@
   if (timings.GetTotalNs() > MsToNs(1000)) {
     timings.Dump();
   }
+
+  stats_.Dump();
 }
 
 void Compiler::CompileOne(const Method* method) {
@@ -193,61 +304,91 @@
 }
 
 bool Compiler::CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache,
-                                                uint32_t type_idx) const {
+                                                uint32_t type_idx) {
   if (!IsImage()) {
+    stats_.TypeNotInDexCache();
     return false;
   }
   Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == NULL) {
+    stats_.TypeNotInDexCache();
     return false;
   }
-  return IsImageClass(ClassHelper(resolved_class).GetDescriptor());
+  bool result = IsImageClass(ClassHelper(resolved_class).GetDescriptor());
+  if (result) {
+    stats_.TypeInDexCache();
+  } else {
+    stats_.TypeNotInDexCache();
+  }
+  return result;
 }
 
 bool Compiler::CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache,
-                                                  uint32_t string_idx) const {
+                                                  uint32_t string_idx) {
   // TODO: Add support for loading strings referenced by image_classes_
   // See also Compiler::ResolveDexFile
 
   // The following is a test saying that if we're building the image without a restricted set of
   // image classes then we can assume the string is present in the dex cache if it is there now
-  return IsImage() && image_classes_ == NULL && dex_cache->GetResolvedString(string_idx) != NULL;
+  bool result = IsImage() && image_classes_ == NULL && dex_cache->GetResolvedString(string_idx) != NULL;
+  if (result) {
+    stats_.StringInDexCache();
+  } else {
+    stats_.StringNotInDexCache();
+  }
+  return result;
 }
 
 bool Compiler::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
-                                          const DexFile& dex_file, uint32_t type_idx) const {
+                                          const DexFile& dex_file, uint32_t type_idx) {
   // Get type from dex cache assuming it was populated by the verifier
   Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == NULL) {
+    stats_.TypeNeedsAccessCheck();
     return false;  // Unknown class needs access checks.
   }
   const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
   Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
   if (referrer_class == NULL) {
+    stats_.TypeNeedsAccessCheck();
     return false;  // Incomplete referrer knowledge needs access check.
   }
   // Perform access check, will return true if access is ok or false if we're going to have to
   // check this at runtime (for example for class loaders).
-  return referrer_class->CanAccess(resolved_class);
+  bool result = referrer_class->CanAccess(resolved_class);
+  if (result) {
+    stats_.TypeDoesntNeedAccessCheck();
+  } else {
+    stats_.TypeNeedsAccessCheck();
+  }
+  return result;
 }
 
 bool Compiler::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx,
                                                       const DexCache* dex_cache,
                                                       const DexFile& dex_file,
-                                                      uint32_t type_idx) const {
+                                                      uint32_t type_idx) {
   // Get type from dex cache assuming it was populated by the verifier.
   Class* resolved_class = dex_cache->GetResolvedType(type_idx);
   if (resolved_class == NULL) {
+    stats_.TypeNeedsAccessCheck();
     return false;  // Unknown class needs access checks.
   }
   const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx);
   Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_);
   if (referrer_class == NULL) {
+    stats_.TypeNeedsAccessCheck();
     return false;  // Incomplete referrer knowledge needs access check.
   }
   // Perform access and instantiable checks, will return true if access is ok or false if we're
   // going to have to check this at runtime (for example for class loaders).
-  return referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable();
+  bool result = referrer_class->CanAccess(resolved_class) && resolved_class->IsInstantiable();
+  if (result) {
+    stats_.TypeDoesntNeedAccessCheck();
+  } else {
+    stats_.TypeNeedsAccessCheck();
+  }
+  return result;
 }
 
 static Class* ComputeReferrerClass(CompilationUnit* cUnit) {
@@ -268,7 +409,7 @@
 }
 
 bool Compiler::ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
-                                        int& field_offset, bool& is_volatile) const {
+                                        int& field_offset, bool& is_volatile) {
   // Conservative defaults
   field_offset = -1;
   is_volatile = true;
@@ -284,6 +425,7 @@
                                         resolved_field->GetAccessFlags())) {
       field_offset = resolved_field->GetOffset().Int32Value();
       is_volatile = resolved_field->IsVolatile();
+      stats_.ResolvedInstanceField();
       return true;  // Fast path.
     }
   }
@@ -292,12 +434,13 @@
   if (thread->IsExceptionPending()) {
       thread->ClearException();
   }
+  stats_.UnresolvedInstanceField();
   return false;  // Incomplete knowledge needs slow path.
 }
 
 bool Compiler::ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
                                       int& field_offset, int& ssb_index,
-                                      bool& is_referrers_class, bool& is_volatile) const {
+                                      bool& is_referrers_class, bool& is_volatile) {
   // Conservative defaults
   field_offset = -1;
   ssb_index = -1;
@@ -313,6 +456,7 @@
         is_referrers_class = true;  // implies no worrying about class initialization
         field_offset = resolved_field->GetOffset().Int32Value();
         is_volatile = resolved_field->IsVolatile();
+        stats_.ResolvedLocalStaticField();
         return true;  // fast path
       } else {
         Class* fields_class = resolved_field->GetDeclaringClass();
@@ -329,6 +473,7 @@
             ssb_index = fields_class->GetDexTypeIndex();
             field_offset = resolved_field->GetOffset().Int32Value();
             is_volatile = resolved_field->IsVolatile();
+            stats_.ResolvedStaticField();
             return true;
           }
           // Search dex file for localized ssb index
@@ -343,6 +488,7 @@
               ssb_index = cUnit->dex_file->GetIndexForTypeId(*type_id);
               field_offset = resolved_field->GetOffset().Int32Value();
               is_volatile = resolved_field->IsVolatile();
+              stats_.ResolvedStaticField();
               return true;
             }
           }
@@ -355,12 +501,13 @@
   if (thread->IsExceptionPending()) {
       thread->ClearException();
   }
+  stats_.UnresolvedStaticField();
   return false;  // Incomplete knowledge needs slow path.
 }
 
 bool Compiler::ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit,
                                  bool is_interface, bool is_super,
-                                 int& vtable_idx) const {
+                                 int& vtable_idx) {
   vtable_idx = -1;
   Method* resolved_method = ComputeReferrerMethod(cUnit, method_idx);
   if (resolved_method != NULL) {
@@ -369,7 +516,7 @@
       Class* methods_class = resolved_method->GetDeclaringClass();
       if (!referrer_class->CanAccess(methods_class) ||
           !referrer_class->CanAccessMember(methods_class,
-                                          resolved_method->GetAccessFlags())) {
+                                           resolved_method->GetAccessFlags())) {
         // The referring class can't access the resolved method, this may occur as a result of a
         // protected method being made public by implementing an interface that re-declares the
         // method public. Resort to the dex file to determine the correct class for the access check
@@ -384,14 +531,15 @@
           referrer_class->CanAccessMember(methods_class,
                                           resolved_method->GetAccessFlags())) {
         vtable_idx = resolved_method->GetMethodIndex();
-        if (is_interface || is_super) {
+        if (is_interface || !is_super) {
           // nothing left to do for virtual/interface dispatch
+          stats_.ResolvedMethod(is_interface, is_super);
           return true;
         } else {
           // ensure the vtable index will be correct to dispatch in the vtable of the super class
-          Class* super_class = methods_class->GetSuperClass();
-          if (super_class != NULL && vtable_idx <= super_class->GetVTable()->GetLength()) {
-            vtable_idx = resolved_method->GetMethodIndex();
+          if (referrer_class->IsSubClass(methods_class) &&
+              vtable_idx < methods_class->GetVTable()->GetLength()) {
+            stats_.ResolvedMethod(is_interface, is_super);
             return true;
           }
         }
@@ -403,6 +551,7 @@
   if (thread->IsExceptionPending()) {
       thread->ClearException();
   }
+  stats_.UnresolvedMethod(is_interface, is_super);
   return false;  // Incomplete knowledge needs slow path.
 }
 
diff --git a/src/compiler.h b/src/compiler.h
index b55960b..1c71330 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -36,6 +36,69 @@
 class Context;
 class TimingLogger;
 typedef struct CompilationUnit CompilationUnit;
+class AOTCompilationStats {
+ public:
+  AOTCompilationStats() : stats_lock_("AOT compilation statistics lock"),
+     types_in_dex_cache_(0), types_not_in_dex_cache_(0),
+     strings_in_dex_cache_(0), strings_not_in_dex_cache_(0),
+     resolved_types_(0), unresolved_types_(0),
+     resolved_instance_fields_(0), unresolved_instance_fields_(0),
+     resolved_local_static_fields_(0), resolved_static_fields_(0), unresolved_static_fields_(0),
+     resolved_virtual_methods_(0), unresolved_virtual_methods_(0),
+     resolved_super_methods_(0), unresolved_super_methods_(0),
+     resolved_interface_methods_(0), unresolved_interface_methods_(0) {}
+
+  void Dump();
+
+  void TypeInDexCache();
+  void TypeNotInDexCache();
+
+  void StringInDexCache();
+  void StringNotInDexCache();
+
+  void TypeDoesntNeedAccessCheck();
+  void TypeNeedsAccessCheck();
+
+  void ResolvedInstanceField();
+  void UnresolvedInstanceField();
+
+  void ResolvedLocalStaticField();
+  void ResolvedStaticField();
+  void UnresolvedStaticField();
+
+  void ResolvedMethod(bool is_interface, bool is_super);
+  void UnresolvedMethod(bool is_interface, bool is_super);
+
+ private:
+  Mutex stats_lock_;
+
+  size_t types_in_dex_cache_;
+  size_t types_not_in_dex_cache_;
+
+  size_t strings_in_dex_cache_;
+  size_t strings_not_in_dex_cache_;
+
+  size_t resolved_types_;
+  size_t unresolved_types_;
+
+  size_t resolved_instance_fields_;
+  size_t unresolved_instance_fields_;
+
+  size_t resolved_local_static_fields_;
+  size_t resolved_static_fields_;
+  size_t unresolved_static_fields_;
+
+  size_t resolved_virtual_methods_;
+  size_t unresolved_virtual_methods_;
+
+  size_t resolved_super_methods_;
+  size_t unresolved_super_methods_;
+
+  size_t resolved_interface_methods_;
+  size_t unresolved_interface_methods_;
+
+  DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats);;
+};
 
 class Compiler {
  public:
@@ -87,31 +150,31 @@
 
   // Callbacks from OAT/ART compiler to see what runtime checks must be generated
 
-  bool CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, uint32_t type_idx) const;
+  bool CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, uint32_t type_idx);
 
-  bool CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache, uint32_t string_idx) const;
+  bool CanAssumeStringIsPresentInDexCache(const DexCache* dex_cache, uint32_t string_idx);
 
   // Are runtime access checks necessary in the compiled code?
   bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
-                                  const DexFile& dex_file, uint32_t type_idx) const;
+                                  const DexFile& dex_file, uint32_t type_idx);
 
   // Are runtime access and instantiable checks necessary in the code?
   bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, const DexCache* dex_cache,
-                                              const DexFile& dex_file, uint32_t type_idx) const;
+                                              const DexFile& dex_file, uint32_t type_idx);
 
   // Can we fast path instance field access? Computes field's offset and volatility
   bool ComputeInstanceFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
-                                int& field_offset, bool& is_volatile) const;
+                                int& field_offset, bool& is_volatile);
 
   // Can we fastpath static field access? Computes field's offset, volatility and whether the
   // field is within the referrer (which can avoid checking class initialization)
   bool ComputeStaticFieldInfo(uint32_t field_idx, CompilationUnit* cUnit,
                               int& field_offset, int& ssb_index,
-                              bool& is_referrers_class, bool& is_volatile) const;
+                              bool& is_referrers_class, bool& is_volatile);
 
   // Can we fastpath a interface, super class or virtual method call? Computes method's vtable index
   bool ComputeInvokeInfo(uint32_t method_idx, CompilationUnit* cUnit, bool is_interface,
-                         bool is_super, int& vtable_idx) const;
+                         bool is_super, int& vtable_idx);
 
  private:
 
@@ -177,6 +240,8 @@
   size_t thread_count_;
   uint64_t start_ns_;
 
+  AOTCompilationStats stats_;
+
   const std::set<std::string>* image_classes_;
 
   DISALLOW_COPY_AND_ASSIGN(Compiler);
diff --git a/src/compiler/Compiler.h b/src/compiler/Compiler.h
index 451fae5..16a48df 100644
--- a/src/compiler/Compiler.h
+++ b/src/compiler/Compiler.h
@@ -173,7 +173,7 @@
 void oatArchDump(void);
 bool oatStartup(void);
 void oatShutdown(void);
-CompiledMethod* oatCompileMethod(const Compiler& compiler, bool is_direct,
+CompiledMethod* oatCompileMethod(Compiler& compiler, bool is_direct,
                                  uint32_t method_idx, const ClassLoader* class_loader,
                                  const DexFile& dex_file, OatInstructionSetType);
 void oatDumpStats(void);
diff --git a/src/compiler/CompilerIR.h b/src/compiler/CompilerIR.h
index ade478f..9a992a1 100644
--- a/src/compiler/CompilerIR.h
+++ b/src/compiler/CompilerIR.h
@@ -212,7 +212,7 @@
     int numInsts;
     int numBlocks;
     GrowableList blockList;
-    const Compiler* compiler;           // Compiler driving this compiler
+    Compiler* compiler;            // Compiler driving this compiler
     ClassLinker* class_linker;     // Linker to resolve fields and methods
     const DexFile* dex_file;       // DexFile containing the method being compiled
     DexCache* dex_cache;           // DexFile's corresponding cache
diff --git a/src/compiler/Frontend.cc b/src/compiler/Frontend.cc
index 873567c..3b03a35 100644
--- a/src/compiler/Frontend.cc
+++ b/src/compiler/Frontend.cc
@@ -736,7 +736,7 @@
 /*
  * Compile a method.
  */
-CompiledMethod* oatCompileMethod(const Compiler& compiler, const DexFile::CodeItem* code_item,
+CompiledMethod* oatCompileMethod(Compiler& compiler, const DexFile::CodeItem* code_item,
                                  uint32_t access_flags, uint32_t method_idx,
                                  const ClassLoader* class_loader,
                                  const DexFile& dex_file, InstructionSet insnSet)