Adds a DEX-to-DEX compilation level.

This CL adds a DEX-to-DEX compilation level which allows the DEX-to-DEX
compiler to ensure correctness on classes with soft-failed verification.

Bug: 9307738
Change-Id: If051336bf81370bca55872c8c75ccd573d8ca391
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 3c491ce..1ee29cb 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -36,9 +36,11 @@
 class DexCompiler {
  public:
   DexCompiler(art::CompilerDriver& compiler,
-              const DexCompilationUnit& unit)
+              const DexCompilationUnit& unit,
+              DexToDexCompilationLevel dex_to_dex_compilation_level)
     : driver_(compiler),
-      unit_(unit) {}
+      unit_(unit),
+      dex_to_dex_compilation_level_(dex_to_dex_compilation_level) {}
 
   ~DexCompiler() {}
 
@@ -55,6 +57,10 @@
     return *const_cast<DexFile*>(unit_.GetDexFile());
   }
 
+  bool PerformOptimizations() const {
+    return dex_to_dex_compilation_level_ >= kOptimize;
+  }
+
   // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where
   // a barrier is required.
   void CompileReturnVoid(Instruction* inst, uint32_t dex_pc);
@@ -84,6 +90,7 @@
 
   CompilerDriver& driver_;
   const DexCompilationUnit& unit_;
+  const DexToDexCompilationLevel dex_to_dex_compilation_level_;
 
   DISALLOW_COPY_AND_ASSIGN(DexCompiler);
 };
@@ -138,6 +145,7 @@
 };
 
 void DexCompiler::Compile() {
+  DCHECK_GE(dex_to_dex_compilation_level_, kRequired);
   const DexFile::CodeItem* code_item = unit_.GetCodeItem();
   const uint16_t* insns = code_item->insns_;
   const uint32_t insns_size = code_item->insns_size_in_code_units_;
@@ -220,7 +228,7 @@
 }
 
 Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) {
-  if (!kEnableCheckCastEllision) {
+  if (!kEnableCheckCastEllision || !PerformOptimizations()) {
     return inst;
   }
   MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex());
@@ -253,7 +261,7 @@
                                              uint32_t dex_pc,
                                              Instruction::Code new_opcode,
                                              bool is_put) {
-  if (!kEnableQuickening) {
+  if (!kEnableQuickening || !PerformOptimizations()) {
     return;
   }
   uint32_t field_idx = inst->VRegC_22c();
@@ -280,7 +288,7 @@
                                 uint32_t dex_pc,
                                 Instruction::Code new_opcode,
                                 bool is_range) {
-  if (!kEnableQuickening) {
+  if (!kEnableQuickening || !PerformOptimizations()) {
     return;
   }
   uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c();
@@ -320,14 +328,15 @@
 }  // namespace optimizer
 }  // namespace art
 
-extern "C" art::CompiledMethod*
-    ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
+extern "C" void ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item,
                   uint32_t access_flags, art::InvokeType invoke_type,
                   uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
-                  const art::DexFile& dex_file) {
-  art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
-                               dex_file, code_item, class_def_idx, method_idx, access_flags);
-  art::optimizer::DexCompiler dex_compiler(compiler, unit);
-  dex_compiler.Compile();
-  return NULL;
+                  const art::DexFile& dex_file,
+                  art::DexToDexCompilationLevel dex_to_dex_compilation_level) {
+  if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) {
+    art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(),
+                                 dex_file, code_item, class_def_idx, method_idx, access_flags);
+    art::optimizer::DexCompiler dex_compiler(compiler, unit, dex_to_dex_compilation_level);
+    dex_compiler.Compile();
+  }
 }
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 49aba4d..38d00a0 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -369,7 +369,7 @@
     compiler_ = reinterpret_cast<CompilerFn>(ArtQuickCompileMethod);
   }
 
-  dex_to_dex_compiler_ = reinterpret_cast<CompilerFn>(ArtCompileDEX);
+  dex_to_dex_compiler_ = reinterpret_cast<DexToDexCompilerFn>(ArtCompileDEX);
 
 #ifdef ART_SEA_IR_MODE
   sea_ir_compiler_ = NULL;
@@ -505,16 +505,10 @@
   }
 }
 
-static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader,
-                                         const DexFile& dex_file,
-                                         const DexFile::ClassDef& class_def)
+static DexToDexCompilationLevel GetDexToDexCompilationlevel(mirror::ClassLoader* class_loader,
+                                                            const DexFile& dex_file,
+                                                            const DexFile::ClassDef& class_def)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-  // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the
-  // verifier from passing on "quick" instruction at compilation time. It must
-  // only pass on quick instructions at runtime.
-  if (class_loader == NULL) {
-    return false;
-  }
   const char* descriptor = dex_file.GetClassDescriptor(class_def);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   mirror::Class* klass = class_linker->FindClass(descriptor, class_loader);
@@ -522,10 +516,27 @@
     Thread* self = Thread::Current();
     CHECK(self->IsExceptionPending());
     self->ClearException();
-    return false;
+    return kDontDexToDexCompile;
   }
-  // DEX-to-DEX compilation is only allowed on preverified classes.
-  return klass->IsVerified();
+  // The verifier can only run on "quick" instructions at runtime (see usage of
+  // FindAccessedFieldAtDexPc and FindInvokedMethodAtDexPc in ThrowNullPointerExceptionFromDexPC
+  // function). Since image classes can be verified again while compiling an application,
+  // we must prevent the DEX-to-DEX compiler from introducing them.
+  // TODO: find a way to enable "quick" instructions for image classes and remove this check.
+  bool compiling_image_classes = (class_loader == NULL);
+  if (compiling_image_classes) {
+    return kRequired;
+  } else if (klass->IsVerified()) {
+    // Class is verified so we can enable DEX-to-DEX compilation for performance.
+    return kOptimize;
+  } else if (klass->IsCompileTimeVerified()) {
+    // Class verification has soft-failed. Anyway, ensure at least correctness.
+    DCHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
+    return kRequired;
+  } else {
+    // Class verification has failed: do not run DEX-to-DEX compilation.
+    return kDontDexToDexCompile;
+  }
 }
 
 void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, base::TimingLogger& timings) {
@@ -556,15 +567,15 @@
   uint32_t method_idx = method->GetDexMethodIndex();
   const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset());
   // Can we run DEX-to-DEX compiler on this class ?
-  bool allow_dex_compilation;
+  DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile;
   {
     ScopedObjectAccess soa(Thread::Current());
     const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
     mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
-    allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def);
+    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def);
   }
   CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(),
-                class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation);
+                class_def_idx, method_idx, jclass_loader, *dex_file, dex_to_dex_compilation_level);
 
   self->GetJniEnv()->DeleteGlobalRef(jclass_loader);
 
@@ -2171,11 +2182,11 @@
     return;
   }
   // Can we run DEX-to-DEX compiler on this class ?
-  bool allow_dex_compilation;
+  DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile;
   {
     ScopedObjectAccess soa(Thread::Current());
     mirror::ClassLoader* class_loader = soa.Decode<mirror::ClassLoader*>(jclass_loader);
-    allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def);
+    dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def);
   }
   ClassDataItemIterator it(dex_file, class_data);
   // Skip fields
@@ -2198,7 +2209,7 @@
     previous_direct_method_idx = method_idx;
     manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
                                           it.GetMethodInvokeType(class_def), class_def_index,
-                                          method_idx, jclass_loader, dex_file, allow_dex_compilation);
+                                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
     it.Next();
   }
   // Compile virtual methods
@@ -2214,7 +2225,7 @@
     previous_virtual_method_idx = method_idx;
     manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(),
                                           it.GetMethodInvokeType(class_def), class_def_index,
-                                          method_idx, jclass_loader, dex_file, allow_dex_compilation);
+                                          method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level);
     it.Next();
   }
   DCHECK(!it.HasNext());
@@ -2231,7 +2242,7 @@
                                    InvokeType invoke_type, uint32_t class_def_idx,
                                    uint32_t method_idx, jobject class_loader,
                                    const DexFile& dex_file,
-                                   bool allow_dex_to_dex_compilation) {
+                                   DexToDexCompilationLevel dex_to_dex_compilation_level) {
   CompiledMethod* compiled_method = NULL;
   uint64_t start_ns = NanoTime();
 
@@ -2253,13 +2264,12 @@
       compiled_method = (*compiler)(*this, code_item, access_flags, invoke_type, class_def_idx,
                                     method_idx, class_loader, dex_file);
       CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file);
-    } else if (allow_dex_to_dex_compilation) {
+    } else if (dex_to_dex_compilation_level != kDontDexToDexCompile) {
       // TODO: add a mode to disable DEX-to-DEX compilation ?
-      compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags,
-                                                invoke_type, class_def_idx,
-                                                method_idx, class_loader, dex_file);
-      // No native code is generated.
-      CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file);
+      (*dex_to_dex_compiler_)(*this, code_item, access_flags,
+                              invoke_type, class_def_idx,
+                              method_idx, class_loader, dex_file,
+                              dex_to_dex_compilation_level);
     }
   }
   uint64_t duration_ns = NanoTime() - start_ns;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index a7a47ed..18f852d 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -48,6 +48,12 @@
   kNoBackend
 };
 
+enum DexToDexCompilationLevel {
+  kDontDexToDexCompile,   // Only meaning wrt image time interpretation.
+  kRequired,              // Dex-to-dex compilation required for correctness.
+  kOptimize               // Perform required transformation and peep-hole optimizations.
+};
+
 // Thread-local storage compiler worker threads
 class CompilerTls {
   public:
@@ -324,7 +330,7 @@
   void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags,
                      InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx,
                      jobject class_loader, const DexFile& dex_file,
-                     bool allow_dex_to_dex_compilation)
+                     DexToDexCompilationLevel dex_to_dex_compilation_level)
       LOCKS_EXCLUDED(compiled_methods_lock_);
 
   static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index)
@@ -375,12 +381,19 @@
                                         uint32_t access_flags, InvokeType invoke_type,
                                         uint32_t class_dex_idx, uint32_t method_idx,
                                         jobject class_loader, const DexFile& dex_file);
+
+  typedef void (*DexToDexCompilerFn)(CompilerDriver& driver,
+                                     const DexFile::CodeItem* code_item,
+                                     uint32_t access_flags, InvokeType invoke_type,
+                                     uint32_t class_dex_idx, uint32_t method_idx,
+                                     jobject class_loader, const DexFile& dex_file,
+                                     DexToDexCompilationLevel dex_to_dex_compilation_level);
   CompilerFn compiler_;
 #ifdef ART_SEA_IR_MODE
   CompilerFn sea_ir_compiler_;
 #endif
 
-  CompilerFn dex_to_dex_compiler_;
+  DexToDexCompilerFn dex_to_dex_compiler_;
 
   void* compiler_context_;