Inlining support in optimizing.

Currently only inlines simple things that don't require an
environment, such as:
- Returning a constant.
- Returning a parameter.
- Returning an arithmetic operation.

Change-Id: Ie844950cb44f69e104774a3cf7a8dea66bc85661
diff --git a/compiler/Android.mk b/compiler/Android.mk
index 18d6cf7..8bcc2f9 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -96,6 +96,7 @@
 	optimizing/graph_checker.cc \
 	optimizing/graph_visualizer.cc \
 	optimizing/gvn.cc \
+	optimizing/inliner.cc \
 	optimizing/instruction_simplifier.cc \
 	optimizing/locations.cc \
 	optimizing/nodes.cc \
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index cd9280c..051b310 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1373,8 +1373,7 @@
   if (resolved_method != nullptr) {
     *vtable_idx = GetResolvedMethodVTableIndex(resolved_method, orig_invoke_type);
 
-    if (enable_devirtualization) {
-      DCHECK(mUnit->GetVerifiedMethod() != nullptr);
+    if (enable_devirtualization && mUnit->GetVerifiedMethod() != nullptr) {
       const MethodReference* devirt_target = mUnit->GetVerifiedMethod()->GetDevirtTarget(dex_pc);
 
       stats_flags = IsFastInvoke(
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index d39f1c7..f9054e0 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -259,13 +259,13 @@
   return false;
 }
 
-HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) {
+HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item, int start_instruction_id) {
   const uint16_t* code_ptr = code_item.insns_;
   const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_;
   code_start_ = code_ptr;
 
   // Setup the graph with the entry block and exit block.
-  graph_ = new (arena_) HGraph(arena_);
+  graph_ = new (arena_) HGraph(arena_, start_instruction_id);
   entry_block_ = new (arena_) HBasicBlock(graph_, 0);
   graph_->AddBlock(entry_block_);
   exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc);
@@ -273,7 +273,7 @@
   graph_->SetExitBlock(exit_block_);
 
   InitializeLocals(code_item.registers_size_);
-  graph_->UpdateMaximumNumberOfOutVRegs(code_item.outs_size_);
+  graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_);
 
   // Compute the number of dex instructions, blocks, and branches. We will
   // check these values against limits given to the compiler.
@@ -613,9 +613,9 @@
     // Sharpening to kDirect only works if we compile PIC.
     DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect)
            || compiler_driver_->GetCompilerOptions().GetCompilePic());
-    // Treat invoke-direct like static calls for now.
-    invoke = new (arena_) HInvokeStatic(
-        arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index);
+    invoke = new (arena_) HInvokeStaticOrDirect(
+        arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index,
+        optimized_invoke_type);
   }
 
   size_t start_index = 0;
@@ -709,42 +709,53 @@
   uint32_t source_or_dest_reg = instruction.VRegA_21c();
   uint16_t field_index = instruction.VRegB_21c();
 
-  uint32_t storage_index;
-  bool is_referrers_class;
-  bool is_initialized;
-  bool is_volatile;
-  MemberOffset field_offset(0u);
-  Primitive::Type field_type;
+  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::ArtField> resolved_field(hs.NewHandle(compiler_driver_->ResolveField(
+      soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true)));
 
-  bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index,
-                                                            dex_compilation_unit_,
-                                                            is_put,
-                                                            &field_offset,
-                                                            &storage_index,
-                                                            &is_referrers_class,
-                                                            &is_volatile,
-                                                            &is_initialized,
-                                                            &field_type);
-  if (!fast_path) {
+  if (resolved_field.Get() == nullptr) {
     MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField);
     return false;
   }
 
-  if (is_volatile) {
+  if (resolved_field->IsVolatile()) {
     MaybeRecordStat(MethodCompilationStat::kNotCompiledVolatile);
     return false;
   }
 
-  HLoadClass* constant = new (arena_) HLoadClass(
-      storage_index, is_referrers_class, dex_pc);
+  Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass(
+      soa, dex_cache, class_loader, outer_compilation_unit_)));
+
+  // 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) {
+    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);
 
   HInstruction* cls = constant;
-  if (!is_initialized) {
+  if (!is_initialized && !is_referrer_class) {
     cls = new (arena_) HClinitCheck(constant, dex_pc);
     current_block_->AddInstruction(cls);
   }
 
+  Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType();
   if (is_put) {
     // We need to keep the class alive before loading the value.
     Temporaries temps(graph_);
@@ -752,9 +763,10 @@
     HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
     DCHECK_EQ(value->GetType(), field_type);
     current_block_->AddInstruction(
-        new (arena_) HStaticFieldSet(cls, value, field_type, field_offset));
+        new (arena_) HStaticFieldSet(cls, value, field_type, resolved_field->GetOffset()));
   } else {
-    current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset));
+    current_block_->AddInstruction(
+        new (arena_) HStaticFieldGet(cls, field_type, resolved_field->GetOffset()));
     UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
   }
   return true;
@@ -949,16 +961,20 @@
                                    uint32_t dex_pc) {
   bool type_known_final;
   bool type_known_abstract;
-  bool is_referrers_class;
+  // `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.
+  bool dont_use_is_referrers_class;
   bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
       dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
-      &type_known_final, &type_known_abstract, &is_referrers_class);
+      &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
   if (!can_access) {
     MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
     return false;
   }
   HInstruction* object = LoadLocal(reference, Primitive::kPrimNot);
-  HLoadClass* cls = new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc);
+  HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc);
   current_block_->AddInstruction(cls);
   // The class needs a temporary before being used by the type check.
   Temporaries temps(graph_);
@@ -1929,16 +1945,20 @@
       uint16_t type_index = instruction.VRegB_21c();
       bool type_known_final;
       bool type_known_abstract;
-      bool is_referrers_class;
+      bool dont_use_is_referrers_class;
+      // `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.
       bool can_access = compiler_driver_->CanAccessTypeWithoutChecks(
           dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index,
-          &type_known_final, &type_known_abstract, &is_referrers_class);
+          &type_known_final, &type_known_abstract, &dont_use_is_referrers_class);
       if (!can_access) {
         MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType);
         return false;
       }
       current_block_->AddInstruction(
-          new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc));
+          new (arena_) HLoadClass(type_index, IsCompilingClass(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 75c8634..cc5f6a0 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -36,6 +36,7 @@
  public:
   HGraphBuilder(ArenaAllocator* arena,
                 DexCompilationUnit* dex_compilation_unit,
+                const DexCompilationUnit* const outer_compilation_unit,
                 const DexFile* dex_file,
                 CompilerDriver* driver,
                 OptimizingCompilerStats* compiler_stats)
@@ -51,6 +52,7 @@
         dex_file_(dex_file),
         dex_compilation_unit_(dex_compilation_unit),
         compiler_driver_(driver),
+        outer_compilation_unit_(outer_compilation_unit),
         return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])),
         code_start_(nullptr),
         latest_result_(nullptr),
@@ -70,12 +72,13 @@
         dex_file_(nullptr),
         dex_compilation_unit_(nullptr),
         compiler_driver_(nullptr),
+        outer_compilation_unit_(nullptr),
         return_type_(return_type),
         code_start_(nullptr),
         latest_result_(nullptr),
         compilation_stats_(nullptr) {}
 
-  HGraph* BuildGraph(const DexFile::CodeItem& code);
+  HGraph* BuildGraph(const DexFile::CodeItem& code, int start_instruction_id = 0);
 
  private:
   // Analyzes the dex instruction and adds HInstruction to the graph
@@ -225,6 +228,14 @@
 
   void MaybeRecordStat(MethodCompilationStat compilation_stat);
 
+  // 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;
+  }
+
   ArenaAllocator* const arena_;
 
   // A list of the size of the dex code holding block information for
@@ -242,9 +253,21 @@
   HIntConstant* constant0_;
   HIntConstant* constant1_;
 
+  // The dex file where the method being compiled is.
   const DexFile* const dex_file_;
+
+  // The compilation unit of the current method being compiled. Note that
+  // it can be an inlined method.
   DexCompilationUnit* const dex_compilation_unit_;
+
   CompilerDriver* const compiler_driver_;
+
+  // The compilation unit of the outermost method being compiled. That is the
+  // method being compiled (and not inlined), and potentially inlining other
+  // methods.
+  const DexCompilationUnit* const outer_compilation_unit_;
+
+  // The return type of the method being compiled.
   const Primitive::Type return_type_;
 
   // The pointer in the dex file where the instructions of the code item
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 461409d..6f424ce 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -565,10 +565,19 @@
           stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
           ++i;
           DCHECK_LT(i, environment_size);
-        } else {
-          DCHECK(current->IsIntConstant());
+        } else if (current->IsDoubleConstant()) {
+          int64_t value = bit_cast<double, int64_t>(current->AsDoubleConstant()->GetValue());
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value));
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value));
+          ++i;
+          DCHECK_LT(i, environment_size);
+        } else if (current->IsIntConstant()) {
           int32_t value = current->AsIntConstant()->GetValue();
           stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
+        } else {
+          DCHECK(current->IsFloatConstant());
+          int32_t value = bit_cast<float, int32_t>(current->AsFloatConstant()->GetValue());
+          stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value);
         }
         break;
       }
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index cbe5f0c..002d9d4 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1171,7 +1171,7 @@
   codegen_->GenerateFrameExit();
 }
 
-void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   HandleInvoke(invoke);
 }
 
@@ -1179,7 +1179,7 @@
   __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
 }
 
-void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
 
   // TODO: Implement all kinds of calls:
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b048c07..c7517d3 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1796,11 +1796,11 @@
   HandleInvoke(invoke);
 }
 
-void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   HandleInvoke(invoke);
 }
 
-void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0));
   // Make sure that ArtMethod* is passed in W0 as per the calling convention
   DCHECK(temp.Is(w0));
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 4757235..e7edd8a 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1115,11 +1115,11 @@
   __ ret();
 }
 
-void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   HandleInvoke(invoke);
 }
 
-void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>();
 
   // TODO: Implement all kinds of calls:
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index f8651f6..ff7fcdc 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1122,11 +1122,11 @@
   return Location();
 }
 
-void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   HandleInvoke(invoke);
 }
 
-void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
+void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
   CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>();
   // TODO: Implement all kinds of calls:
   // 1) boot -> boot
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index a56b9d9..cad6683 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -38,8 +38,7 @@
   HGraph* graph = CreateCFG(&allocator, data, return_type);
   ASSERT_NE(graph, nullptr);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TryBuildingSsa();
 
   StringPrettyPrinter printer_before(graph);
   printer_before.VisitInsertionOrder();
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index 5d4b9cb..3dbd04e 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -32,8 +32,7 @@
   HGraph* graph = CreateCFG(&allocator, data);
   ASSERT_NE(graph, nullptr);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TryBuildingSsa();
 
   StringPrettyPrinter printer_before(graph);
   printer_before.VisitInsertionOrder();
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index 39def82..923468f 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -62,7 +62,7 @@
   ASSERT_NE(graph, nullptr);
 
   graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TransformToSsa();
 
   SSAChecker ssa_checker(&allocator, graph);
   ssa_checker.Run();
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4ed2156..5d1703e 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -17,7 +17,6 @@
 #include "graph_visualizer.h"
 
 #include "code_generator.h"
-#include "driver/dex_compilation_unit.h"
 #include "nodes.h"
 #include "ssa_liveness_analysis.h"
 
@@ -270,39 +269,20 @@
                                    HGraph* graph,
                                    const char* string_filter,
                                    const CodeGenerator& codegen,
-                                   const DexCompilationUnit& cu)
+                                   const char* method_name)
     : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
   if (output == nullptr) {
     return;
   }
-  std::string pretty_name = PrettyMethod(cu.GetDexMethodIndex(), *cu.GetDexFile());
-  if (pretty_name.find(string_filter) == std::string::npos) {
+  if (strstr(method_name, string_filter) == nullptr) {
     return;
   }
 
   is_enabled_ = true;
   HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
   printer.StartTag("compilation");
-  printer.PrintProperty("name", pretty_name.c_str());
-  printer.PrintProperty("method", pretty_name.c_str());
-  printer.PrintTime("date");
-  printer.EndTag("compilation");
-}
-
-HGraphVisualizer::HGraphVisualizer(std::ostream* output,
-                                   HGraph* graph,
-                                   const CodeGenerator& codegen,
-                                   const char* name)
-    : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) {
-  if (output == nullptr) {
-    return;
-  }
-
-  is_enabled_ = true;
-  HGraphVisualizerPrinter printer(graph, *output_, "", codegen_);
-  printer.StartTag("compilation");
-  printer.PrintProperty("name", name);
-  printer.PrintProperty("method", name);
+  printer.PrintProperty("name", method_name);
+  printer.PrintProperty("method", method_name);
   printer.PrintTime("date");
   printer.EndTag("compilation");
 }
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 60d996b..b5baed9 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -47,16 +47,7 @@
                    HGraph* graph,
                    const char* string_filter,
                    const CodeGenerator& codegen,
-                   const DexCompilationUnit& cu);
-
-  /**
-   * Version of `HGraphVisualizer` for unit testing, that is when a
-   * `DexCompilationUnit` is not available.
-   */
-  HGraphVisualizer(std::ostream* output,
-                   HGraph* graph,
-                   const CodeGenerator& codegen,
-                   const char* name);
+                   const char* method_name);
 
   /**
    * If this visualizer is enabled, emit the compilation information
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index a6a68ca..94ff192 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -59,8 +59,7 @@
   ASSERT_EQ(different_offset->GetBlock(), block);
   ASSERT_EQ(use_after_kill->GetBlock(), block);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TryBuildingSsa();
   GlobalValueNumberer(&allocator, graph).Run();
 
   ASSERT_TRUE(to_remove->GetBlock() == nullptr);
@@ -108,8 +107,7 @@
       new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42)));
   join->AddInstruction(new (&allocator) HExit());
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
+  graph->TryBuildingSsa();
   GlobalValueNumberer(&allocator, graph).Run();
 
   // Check that all field get instructions have been GVN'ed.
@@ -173,9 +171,7 @@
   ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body);
   ASSERT_EQ(field_get_in_exit->GetBlock(), exit);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
   GlobalValueNumberer(&allocator, graph).Run();
 
   // Check that all field get instructions are still there.
@@ -237,9 +233,7 @@
   inner_loop_exit->AddInstruction(new (&allocator) HGoto());
   outer_loop_exit->AddInstruction(new (&allocator) HExit());
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
 
   ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn(
       *outer_loop_header->GetLoopInformation()));
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
new file mode 100644
index 0000000..1de5b78
--- /dev/null
+++ b/compiler/optimizing/inliner.cc
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "inliner.h"
+
+#include "builder.h"
+#include "class_linker.h"
+#include "constant_folding.h"
+#include "dead_code_elimination.h"
+#include "driver/compiler_driver-inl.h"
+#include "driver/dex_compilation_unit.h"
+#include "instruction_simplifier.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache.h"
+#include "nodes.h"
+#include "ssa_phi_elimination.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
+
+namespace art {
+
+static constexpr int kMaxInlineCodeUnits = 100;
+static constexpr int kMaxInlineNumberOfBlocks = 3;
+
+void HInliner::Run() {
+  for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
+    for (HInstructionIterator instr_it(it.Current()->GetInstructions());
+         !instr_it.Done();
+         instr_it.Advance()) {
+      HInvokeStaticOrDirect* current = instr_it.Current()->AsInvokeStaticOrDirect();
+      if (current != nullptr) {
+        if (!TryInline(current, current->GetIndexInDexCache(), current->GetInvokeType())) {
+          if (kIsDebugBuild) {
+            std::string callee_name =
+                PrettyMethod(current->GetIndexInDexCache(), *outer_compilation_unit_.GetDexFile());
+            bool should_inline = callee_name.find("$inline$") != std::string::npos;
+            CHECK(!should_inline) << "Could not inline " << callee_name;
+          }
+        }
+      }
+    }
+  }
+}
+
+bool HInliner::TryInline(HInvoke* invoke_instruction,
+                         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);
+
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::DexCache> dex_cache(
+      hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file)));
+  Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
+      soa.Decode<mirror::ClassLoader*>(outer_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)));
+
+  if (resolved_method.Get() == nullptr) {
+    VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file);
+    return false;
+  }
+
+  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;
+  }
+
+  const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
+
+  if (code_item == nullptr) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_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)
+                   << " is too big to inline";
+    return false;
+  }
+
+  if (code_item->tries_size_ != 0) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_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)
+                   << " is not inlined because its class could not be verified";
+    return false;
+  }
+
+  DexCompilationUnit dex_compilation_unit(
+    nullptr,
+    outer_compilation_unit_.GetClassLoader(),
+    outer_compilation_unit_.GetClassLinker(),
+    outer_dex_file,
+    code_item,
+    resolved_method->GetDeclaringClass()->GetDexClassDefIndex(),
+    method_index,
+    resolved_method->GetAccessFlags(),
+    nullptr);
+
+  OptimizingCompilerStats inline_stats;
+  HGraphBuilder builder(graph_->GetArena(),
+                        &dex_compilation_unit,
+                        &outer_compilation_unit_,
+                        &outer_dex_file,
+                        compiler_driver_,
+                        &inline_stats);
+  HGraph* callee_graph = builder.BuildGraph(*code_item, graph_->GetCurrentInstructionId());
+
+  if (callee_graph == nullptr) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " could not be built, so cannot be inlined";
+    return false;
+  }
+
+  if (callee_graph->GetBlocks().Size() > kMaxInlineNumberOfBlocks) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " has too many blocks to be inlined: "
+                   << callee_graph->GetBlocks().Size();
+    return false;
+  }
+
+  if (!callee_graph->TryBuildingSsa()) {
+    VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                   << " could not be transformed to SSA";
+    return false;
+  }
+
+  HReversePostOrderIterator it(*callee_graph);
+  it.Advance();  // Past the entry block to avoid seeing the suspend check.
+  for (; !it.Done(); it.Advance()) {
+    HBasicBlock* block = it.Current();
+    if (block->IsLoopHeader()) {
+      VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file)
+                     << " could not be inlined because it contains a loop";
+      return false;
+    }
+
+    for (HInstructionIterator instr_it(block->GetInstructions());
+         !instr_it.Done();
+         instr_it.Advance()) {
+      HInstruction* current = instr_it.Current();
+      if (current->CanThrow()) {
+        VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_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)
+                       << " could not be inlined because " << current->DebugName()
+                       << " needs an environment";
+        return false;
+      }
+    }
+  }
+
+  // Run simple optimizations on the graph.
+  SsaRedundantPhiElimination redundant_phi(callee_graph);
+  SsaDeadPhiElimination dead_phi(callee_graph);
+  HDeadCodeElimination dce(callee_graph);
+  HConstantFolding fold(callee_graph);
+  InstructionSimplifier simplify(callee_graph);
+
+  HOptimization* optimizations[] = {
+    &redundant_phi,
+    &dead_phi,
+    &dce,
+    &fold,
+    &simplify,
+  };
+
+  for (size_t i = 0; i < arraysize(optimizations); ++i) {
+    HOptimization* optimization = optimizations[i];
+    optimization->Run();
+  }
+
+  callee_graph->InlineInto(graph_, invoke_instruction);
+  VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file);
+  outer_stats_->RecordStat(kInlinedInvoke);
+  return true;
+}
+
+}  // namespace art
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
new file mode 100644
index 0000000..370e33c
--- /dev/null
+++ b/compiler/optimizing/inliner.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
+#define ART_COMPILER_OPTIMIZING_INLINER_H_
+
+#include "invoke_type.h"
+#include "optimization.h"
+
+namespace art {
+
+class CompilerDriver;
+class DexCompilationUnit;
+class HGraph;
+class HInvoke;
+class OptimizingCompilerStats;
+
+class HInliner : public HOptimization {
+ public:
+  HInliner(HGraph* outer_graph,
+           const DexCompilationUnit& outer_compilation_unit,
+           CompilerDriver* compiler_driver,
+           OptimizingCompilerStats* stats)
+      : HOptimization(outer_graph, true, "inliner"),
+        outer_compilation_unit_(outer_compilation_unit),
+        compiler_driver_(compiler_driver),
+        outer_stats_(stats) {}
+
+  void Run() OVERRIDE;
+
+ private:
+  bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const;
+
+  const DexCompilationUnit& outer_compilation_unit_;
+  CompilerDriver* const compiler_driver_;
+  OptimizingCompilerStats* const outer_stats_;
+
+  DISALLOW_COPY_AND_ASSIGN(HInliner);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_OPTIMIZING_INLINER_H_
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 28ca5e8..59404dc 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -42,9 +42,7 @@
   HGraph* graph = builder.BuildGraph(*item);
   ASSERT_NE(graph, nullptr);
 
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
 
   x86::CodeGeneratorX86 codegen(graph);
   SsaLivenessAnalysis liveness(*graph, &codegen);
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index 5c7e6f0..007c43e 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -36,9 +36,7 @@
   // Suspend checks implementation may change in the future, and this test relies
   // on how instructions are ordered.
   RemoveSuspendChecks(graph);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
   // `Inline` conditions into ifs.
   PrepareForRegisterAllocation(graph).Run();
   return graph;
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index 4b69e57..6f706c3 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -48,9 +48,7 @@
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
   HGraph* graph = builder.BuildGraph(*item);
   ASSERT_NE(graph, nullptr);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
   // `Inline` conditions into ifs.
   PrepareForRegisterAllocation(graph).Run();
   x86::CodeGeneratorX86 codegen(graph);
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index ba4dccf..fb941b5 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -167,7 +167,7 @@
   }
 }
 
-void HGraph::TransformToSSA() {
+void HGraph::TransformToSsa() {
   DCHECK(!reverse_post_order_.IsEmpty());
   SsaBuilder ssa_builder(this);
   ssa_builder.BuildSsa();
@@ -682,4 +682,81 @@
   return os;
 }
 
+void HInstruction::InsertBefore(HInstruction* cursor) {
+  next_->previous_ = previous_;
+  if (previous_ != nullptr) {
+    previous_->next_ = next_;
+  }
+  if (block_->instructions_.first_instruction_ == this) {
+    block_->instructions_.first_instruction_ = next_;
+  }
+
+  previous_ = cursor->previous_;
+  if (previous_ != nullptr) {
+    previous_->next_ = this;
+  }
+  next_ = cursor;
+  cursor->previous_ = this;
+  block_ = cursor->block_;
+}
+
+void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
+  // We currently only support graphs with one entry block, one body block, and one exit block.
+  DCHECK_EQ(GetBlocks().Size(), 3u);
+
+  // Walk over the entry block and:
+  // - Move constants from the entry block to the outer_graph's entry block,
+  // - Replace HParameterValue instructions with their real value.
+  // - Remove suspend checks, that hold an environment.
+  int parameter_index = 0;
+  for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) {
+    HInstruction* current = it.Current();
+    if (current->IsConstant()) {
+      current->InsertBefore(outer_graph->GetEntryBlock()->GetLastInstruction());
+    } else if (current->IsParameterValue()) {
+      current->ReplaceWith(invoke->InputAt(parameter_index++));
+    } else {
+      DCHECK(current->IsGoto() || current->IsSuspendCheck());
+      entry_block_->RemoveInstruction(current);
+    }
+  }
+
+  // Insert the body's instructions except the last, just after the `invoke`
+  // instruction.
+  HBasicBlock* body = GetBlocks().Get(1);
+  DCHECK(!body->IsExitBlock());
+  HInstruction* last = body->GetLastInstruction();
+  HInstruction* first = body->GetFirstInstruction();
+
+  if (first != last) {
+    HInstruction* antelast = last->GetPrevious();
+
+    // Update the instruction list of the body to only contain the last
+    // instruction.
+    last->previous_ = nullptr;
+    body->instructions_.first_instruction_ = last;
+    body->instructions_.last_instruction_ = last;
+
+    // Update the instruction list of the `invoke`'s block to now contain the
+    // body's instructions.
+    antelast->next_ = invoke->GetNext();
+    antelast->next_->previous_ = antelast;
+    first->previous_ = invoke;
+    invoke->next_ = first;
+
+    // Update the block pointer of all instructions.
+    for (HInstruction* current = antelast; current != invoke; current = current->GetPrevious()) {
+      current->SetBlock(invoke->GetBlock());
+    }
+  }
+
+  // Finally, replace the invoke with the return value of the inlined graph.
+  if (last->IsReturn()) {
+    invoke->ReplaceWith(last->InputAt(0));
+    body->RemoveInstruction(last);
+  } else {
+    DCHECK(last->IsReturnVoid());
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 8a25de1..c963b70 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -17,6 +17,7 @@
 #ifndef ART_COMPILER_OPTIMIZING_NODES_H_
 #define ART_COMPILER_OPTIMIZING_NODES_H_
 
+#include "invoke_type.h"
 #include "locations.h"
 #include "offsets.h"
 #include "primitive.h"
@@ -30,6 +31,7 @@
 class HEnvironment;
 class HInstruction;
 class HIntConstant;
+class HInvoke;
 class HGraphVisitor;
 class HPhi;
 class HSuspendCheck;
@@ -75,6 +77,8 @@
   HInstruction* last_instruction_;
 
   friend class HBasicBlock;
+  friend class HGraph;
+  friend class HInstruction;
   friend class HInstructionIterator;
   friend class HBackwardInstructionIterator;
 
@@ -84,7 +88,7 @@
 // Control-flow graph of a method. Contains a list of basic blocks.
 class HGraph : public ArenaObject<kArenaAllocMisc> {
  public:
-  explicit HGraph(ArenaAllocator* arena)
+  HGraph(ArenaAllocator* arena, int start_instruction_id = 0)
       : arena_(arena),
         blocks_(arena, kDefaultNumberOfBlocks),
         reverse_post_order_(arena, kDefaultNumberOfBlocks),
@@ -94,7 +98,7 @@
         number_of_vregs_(0),
         number_of_in_vregs_(0),
         temporaries_vreg_slots_(0),
-        current_instruction_id_(0) {}
+        current_instruction_id_(start_instruction_id) {}
 
   ArenaAllocator* GetArena() const { return arena_; }
   const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; }
@@ -108,8 +112,16 @@
 
   void AddBlock(HBasicBlock* block);
 
+  // Try building the SSA form of this graph, with dominance computation and loop
+  // recognition. Returns whether it was successful in doing all these steps.
+  bool TryBuildingSsa() {
+    BuildDominatorTree();
+    TransformToSsa();
+    return AnalyzeNaturalLoops();
+  }
+
   void BuildDominatorTree();
-  void TransformToSSA();
+  void TransformToSsa();
   void SimplifyCFG();
 
   // Analyze all natural loops in this graph. Returns false if one
@@ -117,19 +129,31 @@
   // back edge.
   bool AnalyzeNaturalLoops() const;
 
+  // Inline this graph in `outer_graph`, replacing the given `invoke` instruction.
+  void InlineInto(HGraph* outer_graph, HInvoke* invoke);
+
   void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
   void SimplifyLoop(HBasicBlock* header);
 
-  int GetNextInstructionId() {
+  int32_t GetNextInstructionId() {
+    DCHECK_NE(current_instruction_id_, INT32_MAX);
     return current_instruction_id_++;
   }
 
+  int32_t GetCurrentInstructionId() const {
+    return current_instruction_id_;
+  }
+
+  void SetCurrentInstructionId(int32_t id) {
+    current_instruction_id_ = id;
+  }
+
   uint16_t GetMaximumNumberOfOutVRegs() const {
     return maximum_number_of_out_vregs_;
   }
 
-  void UpdateMaximumNumberOfOutVRegs(uint16_t new_value) {
-    maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_);
+  void SetMaximumNumberOfOutVRegs(uint16_t new_value) {
+    maximum_number_of_out_vregs_ = new_value;
   }
 
   void UpdateTemporariesVRegSlots(size_t slots) {
@@ -152,10 +176,6 @@
     number_of_in_vregs_ = value;
   }
 
-  uint16_t GetNumberOfInVRegs() const {
-    return number_of_in_vregs_;
-  }
-
   uint16_t GetNumberOfLocalVRegs() const {
     return number_of_vregs_ - number_of_in_vregs_;
   }
@@ -200,8 +220,9 @@
   size_t temporaries_vreg_slots_;
 
   // The current id to assign to a newly added instruction. See HInstruction.id_.
-  int current_instruction_id_;
+  int32_t current_instruction_id_;
 
+  ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1);
   DISALLOW_COPY_AND_ASSIGN(HGraph);
 };
 
@@ -474,6 +495,9 @@
   size_t lifetime_end_;
   bool is_catch_block_;
 
+  friend class HGraph;
+  friend class HInstruction;
+
   DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
 };
 
@@ -503,7 +527,7 @@
   M(InstanceOf, Instruction)                                            \
   M(IntConstant, Constant)                                              \
   M(InvokeInterface, Invoke)                                            \
-  M(InvokeStatic, Invoke)                                               \
+  M(InvokeStaticOrDirect, Invoke)                                       \
   M(InvokeVirtual, Invoke)                                              \
   M(LessThan, Condition)                                                \
   M(LessThanOrEqual, Condition)                                         \
@@ -748,6 +772,9 @@
   void ReplaceWith(HInstruction* instruction);
   void ReplaceInput(HInstruction* replacement, size_t index);
 
+  // Insert `this` instruction in `cursor`'s graph, just before `cursor`.
+  void InsertBefore(HInstruction* cursor);
+
   bool HasOnlyOneUse() const {
     return uses_ != nullptr && uses_->GetTail() == nullptr;
   }
@@ -836,6 +863,7 @@
   const SideEffects side_effects_;
 
   friend class HBasicBlock;
+  friend class HGraph;
   friend class HInstructionList;
 
   DISALLOW_COPY_AND_ASSIGN(HInstruction);
@@ -1595,24 +1623,28 @@
   DISALLOW_COPY_AND_ASSIGN(HInvoke);
 };
 
-class HInvokeStatic : public HInvoke {
+class HInvokeStaticOrDirect : public HInvoke {
  public:
-  HInvokeStatic(ArenaAllocator* arena,
-                uint32_t number_of_arguments,
-                Primitive::Type return_type,
-                uint32_t dex_pc,
-                uint32_t index_in_dex_cache)
+  HInvokeStaticOrDirect(ArenaAllocator* arena,
+                        uint32_t number_of_arguments,
+                        Primitive::Type return_type,
+                        uint32_t dex_pc,
+                        uint32_t index_in_dex_cache,
+                        InvokeType invoke_type)
       : HInvoke(arena, number_of_arguments, return_type, dex_pc),
-        index_in_dex_cache_(index_in_dex_cache) {}
+        index_in_dex_cache_(index_in_dex_cache),
+        invoke_type_(invoke_type) {}
 
   uint32_t GetIndexInDexCache() const { return index_in_dex_cache_; }
+  InvokeType GetInvokeType() const { return invoke_type_; }
 
-  DECLARE_INSTRUCTION(InvokeStatic);
+  DECLARE_INSTRUCTION(InvokeStaticOrDirect);
 
  private:
   const uint32_t index_in_dex_cache_;
+  const InvokeType invoke_type_;
 
-  DISALLOW_COPY_AND_ASSIGN(HInvokeStatic);
+  DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect);
 };
 
 class HInvokeVirtual : public HInvoke {
@@ -2425,7 +2457,7 @@
   DISALLOW_COPY_AND_ASSIGN(HLoadString);
 };
 
-// TODO: Pass this check to HInvokeStatic nodes.
+// TODO: Pass this check to HInvokeStaticOrDirect nodes.
 /**
  * Performs an initialization check on its Class object input.
  */
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index d47217f..deebaf7 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -30,6 +30,7 @@
 #include "elf_writer_quick.h"
 #include "graph_visualizer.h"
 #include "gvn.h"
+#include "inliner.h"
 #include "instruction_simplifier.h"
 #include "jni/quick/jni_compiler.h"
 #include "mirror/art_method-inl.h"
@@ -181,25 +182,33 @@
   return code_item.tries_size_ == 0;
 }
 
-static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) {
-  HDeadCodeElimination opt1(graph);
-  HConstantFolding opt2(graph);
-  SsaRedundantPhiElimination opt3(graph);
-  SsaDeadPhiElimination opt4(graph);
-  InstructionSimplifier opt5(graph);
-  GVNOptimization opt6(graph);
+static void RunOptimizations(HGraph* graph,
+                             CompilerDriver* driver,
+                             OptimizingCompilerStats* stats,
+                             const DexCompilationUnit& dex_compilation_unit,
+                             const HGraphVisualizer& visualizer) {
+  SsaRedundantPhiElimination redundant_phi(graph);
+  SsaDeadPhiElimination dead_phi(graph);
+  HDeadCodeElimination dce(graph);
+  HConstantFolding fold(graph);
+  InstructionSimplifier simplify1(graph);
+
+  HInliner inliner(graph, dex_compilation_unit, driver, stats);
+
+  GVNOptimization gvn(graph);
   BoundsCheckElimination bce(graph);
-  InstructionSimplifier opt8(graph);
+  InstructionSimplifier simplify2(graph);
 
   HOptimization* optimizations[] = {
-    &opt1,
-    &opt2,
-    &opt3,
-    &opt4,
-    &opt5,
-    &opt6,
+    &redundant_phi,
+    &dead_phi,
+    &dce,
+    &fold,
+    &simplify1,
+    &inliner,
+    &gvn,
     &bce,
-    &opt8
+    &simplify2
   };
 
   for (size_t i = 0; i < arraysize(optimizations); ++i) {
@@ -210,23 +219,6 @@
   }
 }
 
-static bool TryBuildingSsa(HGraph* graph,
-                           const DexCompilationUnit& dex_compilation_unit,
-                           const HGraphVisualizer& visualizer) {
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-
-  if (!graph->AnalyzeNaturalLoops()) {
-    LOG(INFO) << "Skipping compilation of "
-              << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(),
-                              *dex_compilation_unit.GetDexFile())
-              << ": it contains a non natural loop";
-    return false;
-  }
-  visualizer.DumpGraph("ssa transform");
-  return true;
-}
-
 // The stack map we generate must be 4-byte aligned on ARM. Since existing
 // maps are generated alongside these stack maps, we must also align them.
 static std::vector<uint8_t>& AlignVectorSize(std::vector<uint8_t>& vector) {
@@ -270,19 +262,23 @@
     class_def_idx, method_idx, access_flags,
     GetCompilerDriver()->GetVerifiedMethod(&dex_file, method_idx));
 
+  std::string method_name = PrettyMethod(method_idx, dex_file);
+
   // For testing purposes, we put a special marker on method names that should be compiled
   // with this compiler. This makes sure we're not regressing.
-  bool shouldCompile = dex_compilation_unit.GetSymbol().find("00024opt_00024") != std::string::npos;
-  bool shouldOptimize =
-      dex_compilation_unit.GetSymbol().find("00024reg_00024") != std::string::npos;
+  bool shouldCompile = method_name.find("$opt$") != std::string::npos;
+  bool shouldOptimize = method_name.find("$opt$reg$") != std::string::npos;
 
   ArenaPool pool;
   ArenaAllocator arena(&pool);
   HGraphBuilder builder(&arena,
                         &dex_compilation_unit,
-                        &dex_file, GetCompilerDriver(),
+                        &dex_compilation_unit,
+                        &dex_file,
+                        GetCompilerDriver(),
                         &compilation_stats_);
 
+  VLOG(compiler) << "Building " << PrettyMethod(method_idx, dex_file);
   HGraph* graph = builder.BuildGraph(*code_item);
   if (graph == nullptr) {
     CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
@@ -297,7 +293,7 @@
   }
 
   HGraphVisualizer visualizer(
-      visualizer_output_.get(), graph, kStringFilter, *codegen, dex_compilation_unit);
+      visualizer_output_.get(), graph, kStringFilter, *codegen, method_name.c_str());
   visualizer.DumpGraph("builder");
 
   CodeVectorAllocator allocator;
@@ -306,12 +302,16 @@
   bool can_allocate_registers = RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set);
   if (run_optimizations_ && can_optimize && can_allocate_registers) {
     VLOG(compiler) << "Optimizing " << PrettyMethod(method_idx, dex_file);
-    if (!TryBuildingSsa(graph, dex_compilation_unit, visualizer)) {
+    if (!graph->TryBuildingSsa()) {
+      LOG(INFO) << "Skipping compilation of "
+                << PrettyMethod(method_idx, dex_file)
+                << ": it contains a non natural loop";
       // We could not transform the graph to SSA, bailout.
       compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledCannotBuildSSA);
       return nullptr;
     }
-    RunOptimizations(graph, visualizer);
+    RunOptimizations(
+        graph, GetCompilerDriver(), &compilation_stats_, dex_compilation_unit, visualizer);
 
     PrepareForRegisterAllocation(graph).Run();
     SsaLivenessAnalysis liveness(*graph, codegen);
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 829982e..7993b19 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -28,6 +28,7 @@
   kAttemptCompilation = 0,
   kCompiledBaseline,
   kCompiledOptimized,
+  kInlinedInvoke,
   kNotCompiledUnsupportedIsa,
   kNotCompiledPathological,
   kNotCompiledHugeMethod,
@@ -65,10 +66,10 @@
       std::ostringstream oss;
       oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: "
           << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, "
-          << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.\n";
+          << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.";
       for (int i = 0; i < kLastStat; i++) {
         if (compile_stats_[i] != 0) {
-          oss << PrintMethodCompilationStat(i) << ": " << compile_stats_[i] << "\n";
+          oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i];
         }
       }
       LOG(INFO) << oss.str();
@@ -81,6 +82,7 @@
       case kAttemptCompilation : return "kAttemptCompilation";
       case kCompiledBaseline : return "kCompiledBaseline";
       case kCompiledOptimized : return "kCompiledOptimized";
+      case kInlinedInvoke : return "kInlinedInvoke";
       case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa";
       case kNotCompiledPathological : return "kNotCompiledPathological";
       case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod";
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 8d75db9..f677e84 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -39,9 +39,7 @@
   HGraphBuilder builder(&allocator);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
   HGraph* graph = builder.BuildGraph(*item);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
   x86::CodeGeneratorX86 codegen(graph);
   SsaLivenessAnalysis liveness(*graph, &codegen);
   liveness.Analyze();
@@ -253,9 +251,7 @@
   HGraphBuilder builder(allocator);
   const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
   HGraph* graph = builder.BuildGraph(*item);
-  graph->BuildDominatorTree();
-  graph->TransformToSSA();
-  graph->AnalyzeNaturalLoops();
+  graph->TryBuildingSsa();
   return graph;
 }
 
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 6174dd4..6b6bf05 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -87,7 +87,7 @@
   // Suspend checks implementation may change in the future, and this test relies
   // on how instructions are ordered.
   RemoveSuspendChecks(graph);
-  graph->TransformToSSA();
+  graph->TransformToSsa();
   ReNumberInstructions(graph);
 
   // Test that phis had their type set.
diff --git a/test/437-inline/expected.txt b/test/437-inline/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/437-inline/expected.txt
diff --git a/test/437-inline/info.txt b/test/437-inline/info.txt
new file mode 100644
index 0000000..6487a21
--- /dev/null
+++ b/test/437-inline/info.txt
@@ -0,0 +1 @@
+Tests inlining in the compiler.
diff --git a/test/437-inline/src/Main.java b/test/437-inline/src/Main.java
new file mode 100644
index 0000000..ccddab7
--- /dev/null
+++ b/test/437-inline/src/Main.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014 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 Main {
+  public static void main(String[] args) {
+    if ($opt$inline$returnInt() != 4) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnParameter(42) != 42) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnWide() != 12L) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnWideParameter(0x100000001L) != 0x100000001L) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnReferenceParameter(Main.class) != Main.class) {
+      throw new Error();
+    }
+
+    $opt$inline$returnVoid();
+    $opt$inline$returnVoidWithOneParameter(32);
+
+    if ($opt$inline$returnAdd(42, 1) != 43) {
+      throw new Error();
+    }
+
+    if ($opt$inline$returnSub(42, 1) != 41) {
+      throw new Error();
+    }
+  }
+
+  public static int $opt$inline$returnParameter(int a) {
+    return a;
+  }
+
+  public static int $opt$inline$returnAdd(int a, int b) {
+    return a + b;
+  }
+
+  public static int $opt$inline$returnSub(int a, int b) {
+    return a - b;
+  }
+
+  public static int $opt$inline$returnInt() {
+    return 4;
+  }
+
+  public static long $opt$inline$returnWideParameter(long a) {
+    return a;
+  }
+
+  public static long $opt$inline$returnWide() {
+    return 12L;
+  }
+
+  public static Object $opt$inline$returnReferenceParameter(Object o) {
+    return o;
+  }
+
+  public static void $opt$inline$returnVoid() {
+    return;
+  }
+
+  public static void $opt$inline$returnVoidWithOneParameter(int a) {
+    return;
+  }
+}