Revert "Revert "Inline across dex files.""

This reverts commit 6a816cf624ba56bf2872916d7b65b18fd9a411ef.

Change-Id: I36cb524108786dd7996f2aea0443675be1f1b859
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h
index 4a35e9f..8babc28 100644
--- a/compiler/driver/compiler_driver-inl.h
+++ b/compiler/driver/compiler_driver-inl.h
@@ -39,6 +39,22 @@
   return soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader());
 }
 
+inline mirror::Class* CompilerDriver::ResolveClass(
+    const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
+    Handle<mirror::ClassLoader> class_loader, uint16_t cls_index,
+    const DexCompilationUnit* mUnit) {
+  DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile());
+  DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
+  mirror::Class* cls = mUnit->GetClassLinker()->ResolveType(
+      *mUnit->GetDexFile(), cls_index, dex_cache, class_loader);
+  DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending());
+  if (UNLIKELY(cls == nullptr)) {
+    // Clean up any exception left by type resolution.
+    soa.Self()->ClearException();
+  }
+  return cls;
+}
+
 inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass(
     const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
     Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) {
@@ -46,14 +62,7 @@
   DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
   const DexFile::MethodId& referrer_method_id =
       mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex());
-  mirror::Class* referrer_class = mUnit->GetClassLinker()->ResolveType(
-      *mUnit->GetDexFile(), referrer_method_id.class_idx_, dex_cache, class_loader);
-  DCHECK_EQ(referrer_class == nullptr, soa.Self()->IsExceptionPending());
-  if (UNLIKELY(referrer_class == nullptr)) {
-    // Clean up any exception left by type resolution.
-    soa.Self()->ClearException();
-  }
-  return referrer_class;
+  return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit);
 }
 
 inline mirror::ArtField* CompilerDriver::ResolveFieldWithDexFile(
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 9463c2c..b825293 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -228,6 +228,12 @@
       Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  mirror::Class* ResolveClass(
+      const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache,
+      Handle<mirror::ClassLoader> class_loader, uint16_t type_index,
+      const DexCompilationUnit* mUnit)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Resolve a field. Returns nullptr on failure, including incompatible class change.
   // NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static.
   mirror::ArtField* ResolveField(
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index a21c311..8786ed4 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -616,8 +616,8 @@
     DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
            || compiler_driver_->GetCompilerOptions().GetCompilePic());
     bool is_recursive =
-        (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex());
-    DCHECK(!is_recursive || (target_method.dex_file == outer_compilation_unit_->GetDexFile()));
+        (target_method.dex_method_index == dex_compilation_unit_->GetDexMethodIndex());
+    DCHECK(!is_recursive || (target_method.dex_file == dex_compilation_unit_->GetDexFile()));
     invoke = new (arena_) HInvokeStaticOrDirect(
         arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
         is_recursive, optimized_invoke_type);
@@ -704,6 +704,34 @@
   return true;
 }
 
+mirror::Class* HGraphBuilder::GetOutermostCompilingClass() const {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<2> hs(soa.Self());
+  const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+  Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+      outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file)));
+
+  return compiler_driver_->ResolveCompilingMethodsClass(
+      soa, outer_dex_cache, class_loader, outer_compilation_unit_);
+}
+
+bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const {
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<4> hs(soa.Self());
+  Handle<mirror::DexCache> dex_cache(hs.NewHandle(
+      dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader())));
+  Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass(
+      soa, dex_cache, class_loader, type_index, dex_compilation_unit_)));
+  Handle<mirror::Class> compiling_class(hs.NewHandle(GetOutermostCompilingClass()));
+
+  return compiling_class.Get() == cls.Get();
+}
+
+
 bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
                                            uint32_t dex_pc,
                                            bool is_put) {
@@ -711,7 +739,7 @@
   uint16_t field_index = instruction.VRegB_21c();
 
   ScopedObjectAccess soa(Thread::Current());
-  StackHandleScope<4> hs(soa.Self());
+  StackHandleScope<5> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(
       dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile())));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
@@ -724,23 +752,36 @@
     return false;
   }
 
-  Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass(
-      soa, dex_cache, class_loader, outer_compilation_unit_)));
+  const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile();
+  Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle(
+      outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file)));
+  Handle<mirror::Class> referrer_class(hs.NewHandle(GetOutermostCompilingClass()));
 
   // The index at which the field's class is stored in the DexCache's type array.
   uint32_t storage_index;
-  std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
-      dex_cache.Get(), referrer_class.Get(), resolved_field.Get(), field_index, &storage_index);
-  bool can_easily_access = is_put ? pair.second : pair.first;
-  if (!can_easily_access) {
+  bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
+  if (is_referrer_class) {
+    storage_index = referrer_class->GetDexTypeIndex();
+  } else if (outer_dex_cache.Get() != dex_cache.Get()) {
+    // The compiler driver cannot currently understand multple dex caches involved. Just bailout.
     return false;
+  } else {
+    std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField(
+        outer_dex_cache.Get(),
+        referrer_class.Get(),
+        resolved_field.Get(),
+        field_index,
+        &storage_index);
+    bool can_easily_access = is_put ? pair.second : pair.first;
+    if (!can_easily_access) {
+      return false;
+    }
   }
 
   // TODO: find out why this check is needed.
   bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache(
       *outer_compilation_unit_->GetDexFile(), storage_index);
   bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache;
-  bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass());
 
   HLoadClass* constant = new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc);
   current_block_->AddInstruction(constant);
@@ -966,7 +1007,7 @@
   // `CanAccessTypeWithoutChecks` will tell whether the method being
   // built is trying to access its own class, so that the generated
   // code can optimize for this case. However, the optimization does not
-  // work for inlining, so we use `IsCompilingClass` instead.
+  // work for inlining, so we use `IsOutermostCompilingClass` instead.
   bool dont_use_is_referrers_class;
   bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
       dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
@@ -976,7 +1017,8 @@
     return false;
   }
   HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
-  HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc);
+  HLoadClass* cls = new (arena_) HLoadClass(
+      type_index, IsOutermostCompilingClass(type_index), dex_pc);
   current_block_->AddInstruction(cls);
   // The class needs a temporary before being used by the type check.
   Temporaries temps(graph_);
@@ -1971,7 +2013,7 @@
       // `CanAccessTypeWithoutChecks` will tell whether the method being
       // built is trying to access its own class, so that the generated
       // code can optimize for this case. However, the optimization does not
-      // work for inlining, so we use `IsCompilingClass` instead.
+      // work for inlining, so we use `IsOutermostCompilingClass` instead.
       bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
           dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
           &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
@@ -1980,7 +2022,7 @@
         return false;
       }
       current_block_->AddInstruction(
-          new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc));
+          new (arena_) HLoadClass(type_index, IsOutermostCompilingClass(type_index), dex_pc));
       UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
       break;
     }
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index c70170b..b206660 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -225,13 +225,10 @@
 
   void MaybeRecordStat(MethodCompilationStat compilation_stat);
 
+  mirror::Class* GetOutermostCompilingClass() const;
+
   // Returns whether `type_index` points to the outer-most compiling method's class.
-  bool IsCompilingClass(uint16_t type_index) const {
-    uint32_t referrer_index = outer_compilation_unit_->GetDexMethodIndex();
-    const DexFile::MethodId& method_id =
-        outer_compilation_unit_->GetDexFile()->GetMethodId(referrer_index);
-    return method_id.class_idx_ == type_index;
-  }
+  bool IsOutermostCompilingClass(uint16_t type_index) const;
 
   ArenaAllocator* const arena_;
 
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 968fe3e..256e85b 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -68,87 +68,87 @@
                          uint32_t method_index,
                          InvokeType invoke_type) const {
   ScopedObjectAccess soa(Thread::Current());
-  const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
-  VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, outer_dex_file);
+  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+  VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, caller_dex_file);
 
   StackHandleScope<3> hs(soa.Self());
   Handle<mirror::DexCache> dex_cache(
-      hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file)));
+      hs.NewHandle(caller_compilation_unit_.GetClassLinker()->FindDexCache(caller_dex_file)));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
-      soa.Decode<mirror::ClassLoader*>(outer_compilation_unit_.GetClassLoader())));
+      soa.Decode<mirror::ClassLoader*>(caller_compilation_unit_.GetClassLoader())));
   Handle<mirror::ArtMethod> resolved_method(hs.NewHandle(
       compiler_driver_->ResolveMethod(
-          soa, dex_cache, class_loader, &outer_compilation_unit_, method_index, invoke_type)));
+          soa, dex_cache, class_loader, &caller_compilation_unit_, method_index, invoke_type)));
 
   if (resolved_method.Get() == nullptr) {
-    VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file);
+    VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, caller_dex_file);
     return false;
   }
 
+  bool can_use_dex_cache = true;
+  const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
   if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) {
-    VLOG(compiler) << "Did not inline "
-                   << PrettyMethod(method_index, outer_dex_file)
-                   << " because it is in a different dex file";
-    return false;
+    can_use_dex_cache = false;
   }
 
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
 
   if (code_item == nullptr) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " is not inlined because it is native";
     return false;
   }
 
   if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " is too big to inline";
     return false;
   }
 
   if (code_item->tries_size_ != 0) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " is not inlined because of try block";
     return false;
   }
 
   if (!resolved_method->GetDeclaringClass()->IsVerified()) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " is not inlined because its class could not be verified";
     return false;
   }
 
   if (resolved_method->ShouldNotInline()) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " was already flagged as non inlineable";
     return false;
   }
 
-  if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index)) {
+  if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, can_use_dex_cache)) {
     resolved_method->SetShouldNotInline();
     return false;
   }
 
-  VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file);
+  VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, caller_dex_file);
   MaybeRecordStat(kInlinedInvoke);
   return true;
 }
 
 bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,
                                  HInvoke* invoke_instruction,
-                                 uint32_t method_index) const {
+                                 uint32_t method_index,
+                                 bool can_use_dex_cache) const {
   ScopedObjectAccess soa(Thread::Current());
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
-  const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile();
+  const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
 
   DexCompilationUnit dex_compilation_unit(
     nullptr,
-    outer_compilation_unit_.GetClassLoader(),
-    outer_compilation_unit_.GetClassLinker(),
-    outer_dex_file,
+    caller_compilation_unit_.GetClassLoader(),
+    caller_compilation_unit_.GetClassLinker(),
+    *resolved_method->GetDexFile(),
     code_item,
     resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
-    method_index,
+    resolved_method->GetDexMethodIndex(),
     resolved_method->GetAccessFlags(),
     nullptr);
 
@@ -159,25 +159,25 @@
   HGraphBuilder builder(callee_graph,
                         &dex_compilation_unit,
                         &outer_compilation_unit_,
-                        &outer_dex_file,
+                        resolved_method->GetDexFile(),
                         compiler_driver_,
                         &inline_stats);
 
   if (!builder.BuildGraph(*code_item)) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " could not be built, so cannot be inlined";
     return false;
   }
 
   if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph,
                                                   compiler_driver_->GetInstructionSet())) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " cannot be inlined because of the register allocator";
     return false;
   }
 
   if (!callee_graph->TryBuildingSsa()) {
-    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                    << " could not be transformed to SSA";
     return false;
   }
@@ -199,8 +199,12 @@
   }
 
   if (depth_ + 1 < kDepthLimit) {
-    HInliner inliner(
-        callee_graph, outer_compilation_unit_, compiler_driver_, stats_, depth_ + 1);
+    HInliner inliner(callee_graph,
+                     outer_compilation_unit_,
+                     dex_compilation_unit,
+                     compiler_driver_,
+                     stats_,
+                     depth_ + 1);
     inliner.Run();
   }
 
@@ -209,7 +213,7 @@
   for (; !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
     if (block->IsLoopHeader()) {
-      VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+      VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                      << " could not be inlined because it contains a loop";
       return false;
     }
@@ -223,18 +227,25 @@
       }
 
       if (current->CanThrow()) {
-        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                        << " could not be inlined because " << current->DebugName()
                        << " can throw";
         return false;
       }
 
       if (current->NeedsEnvironment()) {
-        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
                        << " could not be inlined because " << current->DebugName()
                        << " needs an environment";
         return false;
       }
+
+      if (!can_use_dex_cache && current->NeedsDexCache()) {
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file)
+                       << " could not be inlined because " << current->DebugName()
+                       << " it is in a different dex file and requires access to the dex cache";
+        return false;
+      }
     }
   }
 
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 1251977..1dbc7d3 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -32,11 +32,13 @@
  public:
   HInliner(HGraph* outer_graph,
            const DexCompilationUnit& outer_compilation_unit,
+           const DexCompilationUnit& caller_compilation_unit,
            CompilerDriver* compiler_driver,
            OptimizingCompilerStats* stats,
            size_t depth = 0)
       : HOptimization(outer_graph, true, kInlinerPassName, stats),
         outer_compilation_unit_(outer_compilation_unit),
+        caller_compilation_unit_(caller_compilation_unit),
         compiler_driver_(compiler_driver),
         depth_(depth) {}
 
@@ -48,9 +50,11 @@
   bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
   bool TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method,
                          HInvoke* invoke_instruction,
-                         uint32_t method_index) const;
+                         uint32_t method_index,
+                         bool can_use_dex_cache) const;
 
   const DexCompilationUnit& outer_compilation_unit_;
+  const DexCompilationUnit& caller_compilation_unit_;
   CompilerDriver* const compiler_driver_;
   const size_t depth_;
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index db7873b..a38ee45 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1220,6 +1220,8 @@
     return NeedsEnvironment() || IsLoadClass() || IsLoadString();
   }
 
+  virtual bool NeedsDexCache() const { return false; }
+
  protected:
   virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
   virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
@@ -2114,6 +2116,7 @@
 
   InvokeType GetInvokeType() const { return invoke_type_; }
   bool IsRecursive() const { return is_recursive_; }
+  bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); }
 
   DECLARE_INSTRUCTION(InvokeStaticOrDirect);
 
@@ -2996,6 +2999,8 @@
     return loaded_class_rti_.IsExact();
   }
 
+  bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; }
+
   DECLARE_INSTRUCTION(LoadClass);
 
  private:
@@ -3031,6 +3036,7 @@
 
   // TODO: Can we deopt or debug when we resolve a string?
   bool NeedsEnvironment() const OVERRIDE { return false; }
+  bool NeedsDexCache() const OVERRIDE { return true; }
 
   DECLARE_INSTRUCTION(LoadString);
 
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index eaa30df..5ce73ba 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -316,7 +316,7 @@
   InstructionSimplifier simplify1(graph, stats);
   HBooleanSimplifier boolean_not(graph);
 
-  HInliner inliner(graph, dex_compilation_unit, driver, stats);
+  HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, stats);
 
   HConstantFolding fold2(graph);
   SideEffectsAnalysis side_effects(graph);
diff --git a/test/462-checker-inlining-across-dex-files/expected.txt b/test/462-checker-inlining-across-dex-files/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/expected.txt
diff --git a/test/462-checker-inlining-across-dex-files/info.txt b/test/462-checker-inlining-across-dex-files/info.txt
new file mode 100644
index 0000000..57008c3
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/info.txt
@@ -0,0 +1 @@
+Check our inlining heuristics across dex files in optimizing.
diff --git a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
new file mode 100644
index 0000000..cee8e0f
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java
@@ -0,0 +1,64 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public class OtherDex {
+  public static void emptyMethod() {
+  }
+
+  public static int returnIntMethod() {
+    return 38;
+  }
+
+  public static int returnOtherDexStatic() {
+    return myStatic;
+  }
+
+  public static int returnMainStatic() {
+    return Main.myStatic;
+  }
+
+  public static int recursiveCall() {
+    return recursiveCall();
+  }
+
+  public static String returnString() {
+    return "OtherDex";
+  }
+
+  public static Class returnOtherDexClass() {
+    return OtherDex.class;
+  }
+
+  public static Class returnMainClass() {
+    return Main.class;
+  }
+
+  private static Class returnOtherDexClass2() {
+    return OtherDex.class;
+  }
+
+  public static Class returnOtherDexClassStaticCall() {
+    // Do not call returnOtherDexClass, as it may have been flagged
+    // as non-inlineable.
+    return returnOtherDexClass2();
+  }
+
+  public static Class returnOtherDexCallingMain() {
+    return Main.getOtherClass();
+  }
+
+  static int myStatic = 1;
+}
diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java
new file mode 100644
index 0000000..d5563b8
--- /dev/null
+++ b/test/462-checker-inlining-across-dex-files/src/Main.java
@@ -0,0 +1,202 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+// Add a class that will be the first entry in the dex cache, to
+// avoid having the OtherDex and Main classes share the same cache index.
+class AAA {
+}
+
+public class Main {
+
+  // CHECK-START: void Main.inlineEmptyMethod() inliner (before)
+  // CHECK-DAG:     [[Invoke:v\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      ReturnVoid
+
+  // CHECK-START: void Main.inlineEmptyMethod() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  public static void inlineEmptyMethod() {
+    OtherDex.emptyMethod();
+  }
+
+  // CHECK-START: int Main.inlineReturnIntMethod() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineReturnIntMethod() inliner (after)
+  // CHECK-DAG:     [[Const38:i\d+]] IntConstant 38
+  // CHECK-DAG:                      Return [ [[Const38]] ]
+
+  public static int inlineReturnIntMethod() {
+    return OtherDex.returnIntMethod();
+  }
+
+  // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (after)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  public static int dontInlineOtherDexStatic() {
+    return OtherDex.returnOtherDexStatic();
+  }
+
+  // CHECK-START: int Main.inlineMainStatic() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.inlineMainStatic() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: int Main.inlineMainStatic() inliner (after)
+  // CHECK-DAG:     [[Static:i\d+]]  StaticFieldGet
+  // CHECK-DAG:                      Return [ [[Static]] ]
+
+  public static int inlineMainStatic() {
+    return OtherDex.returnMainStatic();
+  }
+
+  // CHECK-START: int Main.dontInlineRecursiveCall() inliner (before)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: int Main.dontInlineRecursiveCall() inliner (after)
+  // CHECK-DAG:     [[Invoke:i\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  public static int dontInlineRecursiveCall() {
+    return OtherDex.recursiveCall();
+  }
+
+  // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (before)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (after)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  public static String dontInlineReturnString() {
+    return OtherDex.returnString();
+  }
+
+  // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (before)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (after)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  public static Class dontInlineOtherDexClass() {
+    return OtherDex.returnOtherDexClass();
+  }
+
+  // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (before)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after)
+  // CHECK-DAG:     [[Class:l\d+]]  LoadClass
+  // CHECK-DAG:                     Return [ [[Class]] ]
+
+  public static Class inlineMainClass() {
+    return OtherDex.returnMainClass();
+  }
+
+  // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (before)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (after)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  public static Class dontInlineOtherDexClassStaticCall() {
+    return OtherDex.returnOtherDexClassStaticCall();
+  }
+
+  // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (before)
+  // CHECK-DAG:     [[Invoke:l\d+]]  InvokeStaticOrDirect
+  // CHECK-DAG:                      Return [ [[Invoke]] ]
+
+  // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
+  // CHECK-NOT:                      InvokeStaticOrDirect
+
+  // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after)
+  // CHECK-DAG:     [[Class:l\d+]]  LoadClass
+  // CHECK-DAG:                     Return [ [[Class]] ]
+
+  public static Class inlineOtherDexCallingMain() {
+    return OtherDex.returnOtherDexCallingMain();
+  }
+
+  public static Class getOtherClass() {
+    return Main.class;
+  }
+
+  public static void main(String[] args) {
+    inlineEmptyMethod();
+    if (inlineReturnIntMethod() != 38) {
+      throw new Error("Expected 38");
+    }
+
+    if (dontInlineOtherDexStatic() != 1) {
+      throw new Error("Expected 1");
+    }
+
+    if (inlineMainStatic() != 42) {
+      throw new Error("Expected 42");
+    }
+
+    if (dontInlineReturnString() != "OtherDex") {
+      throw new Error("Expected OtherDex");
+    }
+
+    if (dontInlineOtherDexClass() != OtherDex.class) {
+      throw new Error("Expected " + OtherDex.class);
+    }
+
+    if (dontInlineOtherDexClassStaticCall() != OtherDex.class) {
+      throw new Error("Expected " + OtherDex.class);
+    }
+
+    if (inlineMainClass() != Main.class) {
+      throw new Error("Expected " + Main.class);
+    }
+
+    if (inlineOtherDexCallingMain() != Main.class) {
+      throw new Error("Expected " + Main.class);
+    }
+  }
+
+  // Reference the AAA class to ensure it is in the dex cache.
+  public static Class<?> cls = AAA.class;
+
+  // Add a field that will be the first entry in the dex cache, to
+  // avoid having the OtherDex.myStatic and Main.myStatic fields
+  // share the same cache index.
+  public static int aaa = 32;
+  public static int myStatic = 42;
+}
diff --git a/test/etc/default-build b/test/etc/default-build
index 58c9564..fbe97f9 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -39,7 +39,7 @@
 fi
 
 mkdir classes
-${JAVAC} -d classes `find src -name '*.java'`
+${JAVAC} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
 
 if [ -d src2 ]; then
   ${JAVAC} -d classes `find src2 -name '*.java'`
@@ -72,6 +72,15 @@
   fi
 fi
 
-if [ ${NEED_DEX} = "true" ]; then
+# Create a single jar with two dex files for multidex.
+if [ -d src-multidex ]; then
+  mkdir classes2
+  ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
+  if [ ${NEED_DEX} = "true" ]; then
+    ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
+      --dump-width=1000 ${DX_FLAGS} classes2
+    zip $TEST_NAME.jar classes.dex classes2.dex
+  fi
+elif [ ${NEED_DEX} = "true" ]; then
   zip $TEST_NAME.jar classes.dex
 fi