Merge "Fix SEGV when dumping MIR CFG"
diff --git a/Android.mk b/Android.mk
index 0492d3c..612fc40 100644
--- a/Android.mk
+++ b/Android.mk
@@ -264,9 +264,9 @@
 .PHONY: oat-target-$(1)
 oat-target-$(1): $$(OUT_OAT_FILE)
 
-$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(TARGET_BOOT_IMG_OUT) $(DEX2OAT_DEPENDENCY)
+$$(OUT_OAT_FILE): $(PRODUCT_OUT)/$(1) $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(DEX2OAT_DEPENDENCY)
 	@mkdir -p $$(dir $$@)
-	$(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(TARGET_BOOT_IMG_OUT) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system
+	$(DEX2OAT) --runtime-arg -Xms64m --runtime-arg -Xmx64m --boot-image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --dex-file=$(PRODUCT_OUT)/$(1) --dex-location=/$(1) --oat-file=$$@ --host-prefix=$(PRODUCT_OUT) --instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --android-root=$(PRODUCT_OUT)/system
 
 endif
 
@@ -275,12 +275,12 @@
 
 $(foreach file,\
   $(filter-out\
-    $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(TARGET_BOOT_JARS))),\
+    $(addprefix $(TARGET_OUT_JAVA_LIBRARIES)/,$(addsuffix .jar,$(LIBART_TARGET_BOOT_JARS))),\
     $(wildcard $(TARGET_OUT_APPS)/*.apk) $(wildcard $(TARGET_OUT_JAVA_LIBRARIES)/*.jar)),\
   $(eval $(call declare-oat-target-target,$(subst $(PRODUCT_OUT)/,,$(file)))))
 
 .PHONY: oat-target
-oat-target: $(ART_TARGET_DEPENDENCIES) $(TARGET_BOOT_OAT_OUT) $(OAT_TARGET_TARGETS)
+oat-target: $(ART_TARGET_DEPENDENCIES) $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE) $(OAT_TARGET_TARGETS)
 
 .PHONY: oat-target-sync
 oat-target-sync: oat-target
@@ -303,6 +303,11 @@
 
 ART_DUMP_OAT_PATH ?= $(OUT_DIR)
 
+OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX)
+OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX)
+# TODO: for now, override with debug version for better error reporting
+OATDUMP := $(OATDUMPD)
+
 .PHONY: dump-oat
 dump-oat: dump-oat-core dump-oat-boot
 
@@ -325,14 +330,14 @@
 
 .PHONY: dump-oat-boot
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-dump-oat-boot: $(TARGET_BOOT_IMG_OUT) $(OATDUMP)
-	$(OATDUMP) --image=$(TARGET_BOOT_IMG_OUT) --output=$(ART_DUMP_OAT_PATH)/boot.oatdump.txt
+dump-oat-boot: $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP)
+	$(OATDUMP) --image=$(DEFAULT_DEX_PREOPT_BUILT_IMAGE) --output=$(ART_DUMP_OAT_PATH)/boot.oatdump.txt
 	@echo Output in $(ART_DUMP_OAT_PATH)/boot.oatdump.txt
 endif
 
 .PHONY: dump-oat-Calculator
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
-dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(TARGET_BOOT_IMG_OUT) $(OATDUMP)
+dump-oat-Calculator: $(TARGET_OUT_APPS)/Calculator.odex $(DEFAULT_DEX_PREOPT_BUILT_IMAGE) $(OATDUMP)
 	$(OATDUMP) --oat-file=$< --output=$(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt
 	@echo Output in $(ART_DUMP_OAT_PATH)/Calculator.oatdump.txt
 endif
diff --git a/build/Android.common.mk b/build/Android.common.mk
index a2c2e1a..30d7dcb 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -140,19 +140,10 @@
 	-DDYNAMIC_ANNOTATIONS_ENABLED=1 \
 	-UNDEBUG
 
-# start of image reserved address space
-IMG_HOST_BASE_ADDRESS   := 0x60000000
-
-ifeq ($(TARGET_ARCH),mips)
-IMG_TARGET_BASE_ADDRESS := 0x30000000
-else
-IMG_TARGET_BASE_ADDRESS := 0x60000000
-endif
-
-ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(IMG_HOST_BASE_ADDRESS) 
+ART_HOST_CFLAGS := $(art_cflags) -DANDROID_SMP=1 -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) 
 ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default
 
-ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(IMG_TARGET_BASE_ADDRESS)
+ART_TARGET_CFLAGS := $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS)
 ifeq ($(TARGET_CPU_SMP),true)
   ART_TARGET_CFLAGS += -DANDROID_SMP=1
 else
@@ -205,10 +196,6 @@
 
 ART_TARGET_DEBUG_CFLAGS := $(art_debug_cflags)
 
-ifeq ($(ART_USE_PORTABLE_COMPILER),true)
-PARALLEL_ART_COMPILE_JOBS := -j8
-endif
-
 ART_BUILD_TARGET := false
 ART_BUILD_HOST := false
 ART_BUILD_NDEBUG := false
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index c04b38b..ec6efbc 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -14,31 +14,12 @@
 # limitations under the License.
 #
 
-# DEX2OAT defined in build/core/config.mk
-DEX2OATD := $(HOST_OUT_EXECUTABLES)/dex2oatd$(HOST_EXECUTABLE_SUFFIX)
-
-LIBART_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libart-compiler$(HOST_SHLIB_SUFFIX)
-LIBARTD_COMPILER := $(HOST_OUT_SHARED_LIBRARIES)/libartd-compiler$(HOST_SHLIB_SUFFIX)
-
-# TODO: for now, override with debug version for better error reporting
-DEX2OAT := $(DEX2OATD)
-LIBART_COMPILER := $(LIBARTD_COMPILER)
-
-# By default, do not run rerun dex2oat if the tool changes.
-# Comment out the | to force dex2oat to rerun on after all changes.
-DEX2OAT_DEPENDENCY := |
-DEX2OAT_DEPENDENCY += $(DEX2OAT)
-DEX2OAT_DEPENDENCY += $(LIBART_COMPILER)
-
-OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX)
-OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX)
-# TODO: for now, override with debug version for better error reporting
-OATDUMP := $(OATDUMPD)
-
-PRELOADED_CLASSES := frameworks/base/preloaded-classes
-
 ########################################################################
-# A smaller libcore only oat file
+# Rules to build a smaller "core" image to support core libraries
+# (that is, non-Android frameworks) testing on the host and target
+#
+# The main rules to build the default "boot" image are in
+# build/core/dex_preopt_libart.mk
 TARGET_CORE_JARS := core-libart conscrypt okhttp core-junit bouncycastle
 HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS))
 
@@ -62,17 +43,17 @@
 $(HOST_CORE_IMG_OUT): $(HOST_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY)
 	@echo "host dex2oat: $@ ($?)"
 	@mkdir -p $(dir $@)
-	$(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
+	$(hide) $(DEX2OAT) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
 		--dex-file=,$(HOST_CORE_DEX_FILES)) $(addprefix --dex-location=,$(HOST_CORE_DEX_LOCATIONS)) --oat-file=$(HOST_CORE_OAT_OUT) \
-		--oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(IMG_HOST_BASE_ADDRESS) \
+		--oat-location=$(HOST_CORE_OAT) --image=$(HOST_CORE_IMG_OUT) --base=$(LIBART_IMG_HOST_BASE_ADDRESS) \
 		--instruction-set=$(HOST_ARCH) --host --android-root=$(HOST_OUT)
 
 $(TARGET_CORE_IMG_OUT): $(TARGET_CORE_DEX_FILES) $(DEX2OAT_DEPENDENCY)
 	@echo "target dex2oat: $@ ($?)"
 	@mkdir -p $(dir $@)
-	$(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
+	$(hide) $(DEX2OAT) --runtime-arg -Xms16m --runtime-arg -Xmx16m --image-classes=$(PRELOADED_CLASSES) $(addprefix \
 		--dex-file=,$(TARGET_CORE_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_CORE_DEX_LOCATIONS)) --oat-file=$(TARGET_CORE_OAT_OUT) \
-		--oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \
+		--oat-location=$(TARGET_CORE_OAT) --image=$(TARGET_CORE_IMG_OUT) --base=$(LIBART_IMG_TARGET_BASE_ADDRESS) \
 		--instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
 
 $(HOST_CORE_OAT_OUT): $(HOST_CORE_IMG_OUT)
@@ -100,32 +81,6 @@
 LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_CORE_IMG_OUT)
 include $(BUILD_PHONY_PACKAGE)
 endif
-endif
-
-########################################################################
-# The full system boot classpath
-TARGET_BOOT_JARS := $(subst :, ,$(DEXPREOPT_BOOT_JARS))
-TARGET_BOOT_JARS := $(foreach jar,$(TARGET_BOOT_JARS),$(patsubst core, core-libart,$(jar)))
-TARGET_BOOT_DEX_LOCATIONS := $(foreach jar,$(TARGET_BOOT_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar)
-TARGET_BOOT_DEX_FILES := $(foreach jar,$(TARGET_BOOT_JARS),$(call intermediates-dir-for,JAVA_LIBRARIES,$(jar),,COMMON)/javalib.jar)
-
-TARGET_BOOT_IMG_OUT := $(DEFAULT_DEX_PREOPT_IMAGE)
-TARGET_BOOT_OAT_OUT := $(patsubst %.art,%.oat,$(TARGET_BOOT_IMG_OUT))
-TARGET_BOOT_OAT := $(subst $(PRODUCT_OUT),,$(TARGET_BOOT_OAT_OUT))
-TARGET_BOOT_OAT_UNSTRIPPED_OUT := $(TARGET_OUT_UNSTRIPPED)$(TARGET_BOOT_OAT)
-
-$(TARGET_BOOT_IMG_OUT): $(TARGET_BOOT_DEX_FILES) $(DEX2OAT_DEPENDENCY)
-	@echo "target dex2oat: $@ ($?)"
-	@mkdir -p $(dir $@)
-	@mkdir -p $(dir $(TARGET_BOOT_OAT_UNSTRIPPED_OUT))
-	$(hide) $(DEX2OAT) $(PARALLEL_ART_COMPILE_JOBS) --runtime-arg -Xms256m --runtime-arg -Xmx256m --image-classes=$(PRELOADED_CLASSES) $(addprefix --dex-file=,$(TARGET_BOOT_DEX_FILES)) $(addprefix --dex-location=,$(TARGET_BOOT_DEX_LOCATIONS)) \
-		--oat-symbols=$(TARGET_BOOT_OAT_UNSTRIPPED_OUT) --oat-file=$(TARGET_BOOT_OAT_OUT) \
-		--oat-location=$(TARGET_BOOT_OAT) --image=$(TARGET_BOOT_IMG_OUT) --base=$(IMG_TARGET_BASE_ADDRESS) \
-		--instruction-set=$(TARGET_ARCH) --instruction-set-features=$(TARGET_INSTRUCTION_SET_FEATURES) --host-prefix=$(PRODUCT_OUT) --android-root=$(PRODUCT_OUT)/system
-
-$(TARGET_BOOT_OAT_UNSTRIPPED_OUT): $(TARGET_BOOT_IMG_OUT)
-
-$(TARGET_BOOT_OAT_OUT): $(TARGET_BOOT_OAT_UNSTRIPPED_OUT)
 
 ifeq ($(ART_BUILD_TARGET_NDEBUG),true)
 include $(CLEAR_VARS)
@@ -133,6 +88,7 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
 LOCAL_ADDITIONAL_DEPENDENCIES += art/build/Android.oat.mk
-LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_BOOT_IMG_OUT) $(TARGET_BOOT_OAT_OUT)
+LOCAL_ADDITIONAL_DEPENDENCIES += $(DEFAULT_DEX_PREOPT_INSTALLED_IMAGE)
 include $(BUILD_PHONY_PACKAGE)
 endif
+endif
diff --git a/compiler/Android.mk b/compiler/Android.mk
index a2419d5..d9a573f 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -57,6 +57,7 @@
 	dex/frontend.cc \
 	dex/mir_graph.cc \
 	dex/mir_analysis.cc \
+	dex/verified_methods_data.cc \
 	dex/vreg_analysis.cc \
 	dex/ssa_transformation.cc \
 	driver/compiler_driver.cc \
diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h
index d73f148..35d04ae 100644
--- a/compiler/dex/compiler_enums.h
+++ b/compiler/dex/compiler_enums.h
@@ -302,27 +302,6 @@
   kThrowStackOverflow,
 };
 
-enum SpecialCaseHandler {
-  kNoHandler,
-  kNullMethod,
-  kConstFunction,
-  kIGet,
-  kIGetBoolean,
-  kIGetObject,
-  kIGetByte,
-  kIGetChar,
-  kIGetShort,
-  kIGetWide,
-  kIPut,
-  kIPutBoolean,
-  kIPutObject,
-  kIPutByte,
-  kIPutChar,
-  kIPutShort,
-  kIPutWide,
-  kIdentity,
-};
-
 enum DividePattern {
   DivideNone,
   Divide3,
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index 6aabb2a..adfbf2f 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -63,21 +63,12 @@
 LLVMInfo::~LLVMInfo() {
 }
 
-QuickCompilerContext::QuickCompilerContext()
-  : inliner_map_(new DexFileToMethodInlinerMap()) {
-}
-
-QuickCompilerContext::~QuickCompilerContext() {
-}
-
 extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver) {
   CHECK(driver.GetCompilerContext() == NULL);
-  driver.SetCompilerContext(new QuickCompilerContext());
 }
 
 extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver) {
-  delete reinterpret_cast<QuickCompilerContext*>(driver.GetCompilerContext());
-  driver.SetCompilerContext(NULL);
+  CHECK(driver.GetCompilerContext() == NULL);
 }
 
 /* Default optimizer/debug setting for the compiler. */
diff --git a/compiler/dex/frontend.h b/compiler/dex/frontend.h
index bcb8bf0..8eb6684 100644
--- a/compiler/dex/frontend.h
+++ b/compiler/dex/frontend.h
@@ -113,19 +113,6 @@
     UniquePtr<art::llvm::IRBuilder> ir_builder_;
 };
 
-class QuickCompilerContext {
-  public:
-    QuickCompilerContext();
-    ~QuickCompilerContext();
-
-    DexFileToMethodInlinerMap* GetInlinerMap() {
-      return inliner_map_.get();
-    }
-
-  private:
-    UniquePtr<DexFileToMethodInlinerMap> inliner_map_;
-};
-
 struct CompilationUnit;
 struct BasicBlock;
 
diff --git a/compiler/dex/mir_analysis.cc b/compiler/dex/mir_analysis.cc
index 89af06e..ab55333 100644
--- a/compiler/dex/mir_analysis.cc
+++ b/compiler/dex/mir_analysis.cc
@@ -16,6 +16,8 @@
 
 #include "compiler_internals.h"
 #include "dataflow_iterator-inl.h"
+#include "dex/quick/dex_file_method_inliner.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
 
 namespace art {
 
@@ -1052,7 +1054,9 @@
   }
 
   // Filter 3: if this method is a special pattern, go ahead and emit the canned pattern.
-  if (IsSpecialCase()) {
+  if (cu_->compiler_driver->GetMethodInlinerMap() != nullptr &&
+      cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
+          ->IsSpecial(cu_->method_idx)) {
     return false;
   }
 
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 2a18280..79edb87 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -20,40 +20,13 @@
 #include "leb128.h"
 #include "mir_graph.h"
 
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/quick/dex_file_method_inliner.h"
+
 namespace art {
 
 #define MAX_PATTERN_LEN 5
 
-struct CodePattern {
-  const Instruction::Code opcodes[MAX_PATTERN_LEN];
-  const SpecialCaseHandler handler_code;
-};
-
-static const CodePattern special_patterns[] = {
-  {{Instruction::RETURN_VOID}, kNullMethod},
-  {{Instruction::CONST, Instruction::RETURN}, kConstFunction},
-  {{Instruction::CONST_4, Instruction::RETURN}, kConstFunction},
-  {{Instruction::CONST_4, Instruction::RETURN_OBJECT}, kConstFunction},
-  {{Instruction::CONST_16, Instruction::RETURN}, kConstFunction},
-  {{Instruction::IGET, Instruction:: RETURN}, kIGet},
-  {{Instruction::IGET_BOOLEAN, Instruction::RETURN}, kIGetBoolean},
-  {{Instruction::IGET_OBJECT, Instruction::RETURN_OBJECT}, kIGetObject},
-  {{Instruction::IGET_BYTE, Instruction::RETURN}, kIGetByte},
-  {{Instruction::IGET_CHAR, Instruction::RETURN}, kIGetChar},
-  {{Instruction::IGET_SHORT, Instruction::RETURN}, kIGetShort},
-  {{Instruction::IGET_WIDE, Instruction::RETURN_WIDE}, kIGetWide},
-  {{Instruction::IPUT, Instruction::RETURN_VOID}, kIPut},
-  {{Instruction::IPUT_BOOLEAN, Instruction::RETURN_VOID}, kIPutBoolean},
-  {{Instruction::IPUT_OBJECT, Instruction::RETURN_VOID}, kIPutObject},
-  {{Instruction::IPUT_BYTE, Instruction::RETURN_VOID}, kIPutByte},
-  {{Instruction::IPUT_CHAR, Instruction::RETURN_VOID}, kIPutChar},
-  {{Instruction::IPUT_SHORT, Instruction::RETURN_VOID}, kIPutShort},
-  {{Instruction::IPUT_WIDE, Instruction::RETURN_VOID}, kIPutWide},
-  {{Instruction::RETURN}, kIdentity},
-  {{Instruction::RETURN_OBJECT}, kIdentity},
-  {{Instruction::RETURN_WIDE}, kIdentity},
-};
-
 const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = {
   "Phi",
   "Copy",
@@ -107,7 +80,6 @@
       method_sreg_(0),
       attributes_(METHOD_IS_LEAF),  // Start with leaf assumption, change on encountering invoke.
       checkstats_(NULL),
-      special_case_(kNoHandler),
       arena_(arena),
       backward_branches_(0),
       forward_branches_(0) {
@@ -612,13 +584,6 @@
     /* Identify code range in try blocks and set up the empty catch blocks */
   ProcessTryCatchBlocks();
 
-  /* Set up for simple method detection */
-  int num_patterns = sizeof(special_patterns)/sizeof(special_patterns[0]);
-  bool live_pattern = (num_patterns > 0) && !(cu_->disable_opt & (1 << kMatch));
-  bool* dead_pattern =
-      static_cast<bool*>(arena_->Alloc(sizeof(bool) * num_patterns, ArenaAllocator::kAllocMisc));
-  int pattern_pos = 0;
-
   /* Parse all instructions and put them into containing basic blocks */
   while (code_ptr < code_end) {
     MIR *insn = static_cast<MIR *>(arena_->Alloc(sizeof(MIR), ArenaAllocator::kAllocMIR));
@@ -631,23 +596,6 @@
       opcode_count_[static_cast<int>(opcode)]++;
     }
 
-    /* Possible simple method? */
-    if (live_pattern) {
-      live_pattern = false;
-      special_case_ = kNoHandler;
-      for (int i = 0; i < num_patterns; i++) {
-        if (!dead_pattern[i]) {
-          if (special_patterns[i].opcodes[pattern_pos] == opcode) {
-            live_pattern = true;
-            special_case_ = special_patterns[i].handler_code;
-          } else {
-             dead_pattern[i] = true;
-          }
-        }
-      }
-    pattern_pos++;
-    }
-
     int flags = Instruction::FlagsOf(insn->dalvikInsn.opcode);
 
     uint64_t df_flags = oat_data_flow_attributes_[insn->dalvikInsn.opcode];
@@ -736,6 +684,7 @@
       cur_block = next_block;
     }
   }
+
   if (cu_->enable_debug & (1 << kDebugDumpCFG)) {
     DumpCFG("/sdcard/1_post_parse_cfg/", true);
   }
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 76c13f3..a80c32d 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -577,14 +577,6 @@
     return reg_location_[method_sreg_];
   }
 
-  bool IsSpecialCase() {
-    return special_case_ != kNoHandler;
-  }
-
-  SpecialCaseHandler GetSpecialCase() {
-    return special_case_;
-  }
-
   bool IsBackedge(BasicBlock* branch_bb, BasicBlockId target_bb_id) {
     return ((target_bb_id != NullBasicBlockId) &&
             (GetBasicBlock(target_bb_id)->start_offset <= branch_bb->start_offset));
@@ -789,7 +781,6 @@
   int method_sreg_;
   unsigned int attributes_;
   Checkstats* checkstats_;
-  SpecialCaseHandler special_case_;
   ArenaAllocator* arena_;
   int backward_branches_;
   int forward_branches_;
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 23ea407..8226b24 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -18,6 +18,7 @@
 
 #include "arm_lir.h"
 #include "codegen_arm.h"
+#include "dex/quick/dex_file_method_inliner.h"
 #include "dex/quick/mir_to_lir-inl.h"
 #include "entrypoints/quick/quick_entrypoints.h"
 
@@ -217,58 +218,43 @@
  * Special-case code genration for simple non-throwing leaf methods.
  */
 void ArmMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
-                                SpecialCaseHandler special_case) {
+                                const InlineMethod& special) {
+  // TODO: Generate the method using only the data in special. (Requires FastInstance() field
+  // validation in DexFileMethodInliner::AnalyseIGetMethod()/AnalyseIPutMethod().)
+  DCHECK(special.flags & kInlineSpecial);
   current_dalvik_offset_ = mir->offset;
   MIR* next_mir = NULL;
-  switch (special_case) {
-    case kNullMethod:
+  switch (special.opcode) {
+    case kInlineOpNop:
       DCHECK(mir->dalvikInsn.opcode == Instruction::RETURN_VOID);
       next_mir = mir;
       break;
-    case kConstFunction:
+    case kInlineOpConst:
       ArmMir2Lir::GenPrintLabel(mir);
-      LoadConstant(rARM_RET0, mir->dalvikInsn.vB);
+      LoadConstant(rARM_RET0, special.data);
       next_mir = GetNextMir(&bb, mir);
       break;
-    case kIGet:
-      next_mir = SpecialIGet(&bb, mir, kWord, false, false);
+    case kInlineOpIGet: {
+      InlineIGetIPutData data;
+      data.data = special.data;
+      OpSize op_size = static_cast<OpSize>(data.d.op_size);
+      DCHECK_NE(data.d.op_size, kDouble);  // The inliner doesn't distinguish kDouble, uses kLong.
+      bool long_or_double = (data.d.op_size == kLong);
+      bool is_object = data.d.is_object;
+      next_mir = SpecialIGet(&bb, mir, op_size, long_or_double, is_object);
       break;
-    case kIGetBoolean:
-    case kIGetByte:
-      next_mir = SpecialIGet(&bb, mir, kUnsignedByte, false, false);
+    }
+    case kInlineOpIPut: {
+      InlineIGetIPutData data;
+      data.data = special.data;
+      OpSize op_size = static_cast<OpSize>(data.d.op_size);
+      DCHECK_NE(data.d.op_size, kDouble);  // The inliner doesn't distinguish kDouble, uses kLong.
+      bool long_or_double = (data.d.op_size == kLong);
+      bool is_object = data.d.is_object;
+      next_mir = SpecialIPut(&bb, mir, op_size, long_or_double, is_object);
       break;
-    case kIGetObject:
-      next_mir = SpecialIGet(&bb, mir, kWord, false, true);
-      break;
-    case kIGetChar:
-      next_mir = SpecialIGet(&bb, mir, kUnsignedHalf, false, false);
-      break;
-    case kIGetShort:
-      next_mir = SpecialIGet(&bb, mir, kSignedHalf, false, false);
-      break;
-    case kIGetWide:
-      next_mir = SpecialIGet(&bb, mir, kLong, true, false);
-      break;
-    case kIPut:
-      next_mir = SpecialIPut(&bb, mir, kWord, false, false);
-      break;
-    case kIPutBoolean:
-    case kIPutByte:
-      next_mir = SpecialIPut(&bb, mir, kUnsignedByte, false, false);
-      break;
-    case kIPutObject:
-      next_mir = SpecialIPut(&bb, mir, kWord, false, true);
-      break;
-    case kIPutChar:
-      next_mir = SpecialIPut(&bb, mir, kUnsignedHalf, false, false);
-      break;
-    case kIPutShort:
-      next_mir = SpecialIPut(&bb, mir, kSignedHalf, false, false);
-      break;
-    case kIPutWide:
-      next_mir = SpecialIPut(&bb, mir, kLong, true, false);
-      break;
-    case kIdentity:
+    }
+    case kInlineOpReturnArg:
       next_mir = SpecialIdentity(mir);
       break;
     default:
@@ -276,7 +262,7 @@
   }
   if (next_mir != NULL) {
     current_dalvik_offset_ = next_mir->offset;
-    if (special_case != kIdentity) {
+    if (special.opcode != kInlineOpReturnArg) {
       ArmMir2Lir::GenPrintLabel(next_mir);
     }
     NewLIR1(kThumbBx, rARM_LR);
diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h
index 25ddc94..c04f1d6 100644
--- a/compiler/dex/quick/arm/codegen_arm.h
+++ b/compiler/dex/quick/arm/codegen_arm.h
@@ -135,7 +135,7 @@
     void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
     void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
     void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
-    void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+    void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
 
     // Required for target - single operation generators.
     LIR* OpUnconditionalBranch(LIR* target);
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 5d78ed5..65286d5 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -19,6 +19,9 @@
 #include "gc_map.h"
 #include "mapping_table.h"
 #include "mir_to_lir-inl.h"
+#include "dex/quick/dex_file_method_inliner.h"
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+#include "dex/verified_methods_data.h"
 #include "verifier/dex_gc_map.h"
 #include "verifier/method_verifier.h"
 
@@ -746,7 +749,8 @@
     }
   }
   MethodReference method_ref(cu_->dex_file, cu_->method_idx);
-  const std::vector<uint8_t>* gc_map_raw = verifier::MethodVerifier::GetDexGcMap(method_ref);
+  const std::vector<uint8_t>* gc_map_raw =
+      cu_->compiler_driver->GetVerifiedMethodsData()->GetDexGcMap(method_ref);
   verifier::DexPcToReferenceMap dex_gc_map(&(*gc_map_raw)[0]);
   DCHECK_EQ(gc_map_raw->size(), dex_gc_map.RawSize());
   // Compute native offset to references size.
@@ -981,8 +985,7 @@
       core_spill_mask_(0),
       fp_spill_mask_(0),
       first_lir_insn_(NULL),
-      last_lir_insn_(NULL),
-      inliner_(nullptr) {
+      last_lir_insn_(NULL) {
   promotion_map_ = static_cast<PromotionMap*>
       (arena_->Alloc((cu_->num_dalvik_registers  + cu_->num_compiler_temps + 1) *
                       sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc));
@@ -998,15 +1001,16 @@
   /* Allocate Registers using simple local allocation scheme */
   SimpleRegAlloc();
 
-  if (mir_graph_->IsSpecialCase()) {
-      /*
-       * Custom codegen for special cases.  If for any reason the
-       * special codegen doesn't succeed, first_lir_insn_ will
-       * set to NULL;
-       */
-      cu_->NewTimingSplit("SpecialMIR2LIR");
-      SpecialMIR2LIR(mir_graph_->GetSpecialCase());
-    }
+  /*
+   * Custom codegen for special cases.  If for any reason the
+   * special codegen doesn't succeed, first_lir_insn_ will
+   * set to NULL;
+   */
+  // TODO: Clean up GenSpecial() and return true only if special implementation is emitted.
+  // Currently, GenSpecial() returns IsSpecial() but doesn't check after SpecialMIR2LIR().
+  DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
+  cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
+      ->GenSpecial(this, cu_->method_idx);
 
   /* Convert MIR to LIR, etc. */
   if (first_lir_insn_ == NULL) {
diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc
index b21e37e..c21ddcc 100644
--- a/compiler/dex/quick/dex_file_method_inliner.cc
+++ b/compiler/dex/quick/dex_file_method_inliner.cc
@@ -16,14 +16,21 @@
 
 #include <algorithm>
 #include "base/macros.h"
+#include "base/mutex.h"
+#include "base/mutex-inl.h"
+#include "locks.h"
+#include "thread.h"
+#include "thread-inl.h"
 #include "dex/mir_graph.h"
+#include "dex_instruction.h"
+#include "dex_instruction-inl.h"
 
 #include "dex_file_method_inliner.h"
 
 namespace art {
 
 const uint32_t DexFileMethodInliner::kIndexUnresolved;
-const char* DexFileMethodInliner::kClassCacheNames[] = {
+const char* const DexFileMethodInliner::kClassCacheNames[] = {
     "Z",                       // kClassCacheBoolean
     "B",                       // kClassCacheByte
     "C",                       // kClassCacheChar
@@ -47,7 +54,7 @@
     "Lsun/misc/Unsafe;",       // kClassCacheSunMiscUnsafe
 };
 
-const char* DexFileMethodInliner::kNameCacheNames[] = {
+const char* const DexFileMethodInliner::kNameCacheNames[] = {
     "reverseBytes",          // kNameCacheReverseBytes
     "doubleToRawLongBits",   // kNameCacheDoubleToRawLongBits
     "longBitsToDouble",      // kNameCacheLongBitsToDouble
@@ -160,7 +167,7 @@
 
 const DexFileMethodInliner::IntrinsicDef DexFileMethodInliner::kIntrinsicMethods[] = {
 #define INTRINSIC(c, n, p, o, d) \
-    { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, d } }
+    { { kClassCache ## c, kNameCache ## n, kProtoCache ## p }, { o, kInlineIntrinsic, d } }
 
     INTRINSIC(JavaLangDouble, DoubleToRawLongBits, D_J, kIntrinsicDoubleCvt, 0),
     INTRINSIC(JavaLangDouble, LongBitsToDouble, J_D, kIntrinsicDoubleCvt, 0),
@@ -228,7 +235,8 @@
 };
 
 DexFileMethodInliner::DexFileMethodInliner()
-    : dex_file_(NULL) {
+    : lock_("DexFileMethodInliner lock", kDexFileMethodInlinerLock),
+      dex_file_(NULL) {
   COMPILE_ASSERT(kClassCacheFirst == 0, kClassCacheFirst_not_0);
   COMPILE_ASSERT(arraysize(kClassCacheNames) == kClassCacheLast, bad_arraysize_kClassCacheNames);
   COMPILE_ASSERT(kNameCacheFirst == 0, kNameCacheFirst_not_0);
@@ -240,16 +248,75 @@
 DexFileMethodInliner::~DexFileMethodInliner() {
 }
 
-bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) const {
-  return intrinsics_.find(method_index) != intrinsics_.end();
+bool DexFileMethodInliner::AnalyseMethodCode(uint32_t method_idx,
+                                             const DexFile::CodeItem* code_item) {
+  // We currently support only plain return or 2-instruction methods.
+
+  DCHECK_NE(code_item->insns_size_in_code_units_, 0u);
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  Instruction::Code opcode = instruction->Opcode();
+
+  switch (opcode) {
+    case Instruction::RETURN_VOID:
+      return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0);
+    case Instruction::RETURN:
+    case Instruction::RETURN_OBJECT:
+      return AnalyseReturnMethod(method_idx, code_item, kWord);
+    case Instruction::RETURN_WIDE:
+      return AnalyseReturnMethod(method_idx, code_item, kLong);
+    case Instruction::CONST:
+    case Instruction::CONST_4:
+    case Instruction::CONST_16:
+    case Instruction::CONST_HIGH16:
+      // TODO: Support wide constants (RETURN_WIDE).
+      return AnalyseConstMethod(method_idx, code_item);
+    case Instruction::IGET:
+      return AnalyseIGetMethod(method_idx, code_item, kWord, false);
+    case Instruction::IGET_OBJECT:
+      return AnalyseIGetMethod(method_idx, code_item, kWord, true);
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE:
+      return AnalyseIGetMethod(method_idx, code_item, kSignedByte, false);
+    case Instruction::IGET_CHAR:
+      return AnalyseIGetMethod(method_idx, code_item, kUnsignedHalf, false);
+    case Instruction::IGET_SHORT:
+      return AnalyseIGetMethod(method_idx, code_item, kSignedHalf, false);
+    case Instruction::IGET_WIDE:
+      return AnalyseIGetMethod(method_idx, code_item, kLong, false);
+    case Instruction::IPUT:
+      return AnalyseIPutMethod(method_idx, code_item, kWord, false);
+    case Instruction::IPUT_OBJECT:
+      return AnalyseIPutMethod(method_idx, code_item, kWord, true);
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+      return AnalyseIPutMethod(method_idx, code_item, kSignedByte, false);
+    case Instruction::IPUT_CHAR:
+      return AnalyseIPutMethod(method_idx, code_item, kUnsignedHalf, false);
+    case Instruction::IPUT_SHORT:
+      return AnalyseIPutMethod(method_idx, code_item, kSignedHalf, false);
+    case Instruction::IPUT_WIDE:
+      return AnalyseIPutMethod(method_idx, code_item, kLong, false);
+    default:
+      return false;
+    }
 }
 
-bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) const {
-  auto it = intrinsics_.find(info->index);
-  if (it == intrinsics_.end()) {
-    return false;
+bool DexFileMethodInliner::IsIntrinsic(uint32_t method_index) {
+  ReaderMutexLock mu(Thread::Current(), lock_);
+  auto it = inline_methods_.find(method_index);
+  return it != inline_methods_.end() && (it->second.flags & kInlineIntrinsic) != 0;
+}
+
+bool DexFileMethodInliner::GenIntrinsic(Mir2Lir* backend, CallInfo* info) {
+  InlineMethod intrinsic;
+  {
+    ReaderMutexLock mu(Thread::Current(), lock_);
+    auto it = inline_methods_.find(info->index);
+    if (it == inline_methods_.end() || (it->second.flags & kInlineIntrinsic) == 0) {
+      return false;
+    }
+    intrinsic = it->second;
   }
-  const Intrinsic& intrinsic = it->second;
   switch (intrinsic.opcode) {
     case kIntrinsicDoubleCvt:
       return backend->GenInlinedDoubleCvt(info);
@@ -296,6 +363,27 @@
   }
 }
 
+bool DexFileMethodInliner::IsSpecial(uint32_t method_index) {
+  ReaderMutexLock mu(Thread::Current(), lock_);
+  auto it = inline_methods_.find(method_index);
+  return it != inline_methods_.end() && (it->second.flags & kInlineSpecial) != 0;
+}
+
+bool DexFileMethodInliner::GenSpecial(Mir2Lir* backend, uint32_t method_idx) {
+  InlineMethod special;
+  {
+    ReaderMutexLock mu(Thread::Current(), lock_);
+    auto it = inline_methods_.find(method_idx);
+    if (it == inline_methods_.end() || (it->second.flags & kInlineSpecial) == 0) {
+      return false;
+    }
+    special = it->second;
+  }
+  // TODO: Return true only if special implementation is emitted.
+  backend->SpecialMIR2LIR(special);
+  return true;
+}
+
 uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCache* cache,
                                               ClassCacheIndex index) {
   uint32_t* class_index = &cache->class_indexes[index];
@@ -408,13 +496,150 @@
   DCHECK(dex_file_ == nullptr);
   IndexCache cache;
   for (const IntrinsicDef& def : kIntrinsicMethods) {
-    uint32_t method_id = FindMethodIndex(dex_file, &cache, def.method_def);
-    if (method_id != kIndexNotFound) {
-      DCHECK(intrinsics_.find(method_id) == intrinsics_.end());
-      intrinsics_[method_id] = def.intrinsic;
+    uint32_t method_idx = FindMethodIndex(dex_file, &cache, def.method_def);
+    if (method_idx != kIndexNotFound) {
+      DCHECK(inline_methods_.find(method_idx) == inline_methods_.end());
+      inline_methods_[method_idx] = def.intrinsic;
     }
   }
   dex_file_ = dex_file;
 }
 
+bool DexFileMethodInliner::AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode,
+                                           uint16_t flags, uint32_t data) {
+  WriterMutexLock mu(Thread::Current(), lock_);
+  InlineMethod* im = &inline_methods_[method_idx];
+  if (im->flags == 0) {
+    im->opcode = opcode;
+    im->flags = flags;
+    im->data = data;
+    return true;
+  } else {
+    // TODO: Warning about a method being already inlined?
+    LOG(WARNING) << "Inliner: " << PrettyMethod(method_idx, *dex_file_) << " already inline, "
+        << im->flags;
+    return false;
+  }
+}
+
+bool DexFileMethodInliner::AnalyseReturnMethod(int32_t method_idx,
+                                               const DexFile::CodeItem* code_item, OpSize size) {
+  const Instruction* return_instruction = Instruction::At(code_item->insns_);
+  if (return_instruction->Opcode() == Instruction::RETURN_VOID) {
+    return AddInlineMethod(method_idx, kInlineOpNop, kInlineSpecial, 0);
+  }
+  uint32_t reg = return_instruction->VRegA_11x();
+  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+  DCHECK_GE(reg, arg_start);
+  DCHECK_LT(size == kLong ? reg + 1 : reg, code_item->registers_size_);
+
+  InlineReturnArgData data;
+  data.d.arg = reg - arg_start;
+  data.d.op_size = size;
+  data.d.reserved = 0;
+  return AddInlineMethod(method_idx, kInlineOpReturnArg, kInlineSpecial, data.data);
+}
+
+bool DexFileMethodInliner::AnalyseConstMethod(int32_t method_idx,
+                                              const DexFile::CodeItem* code_item) {
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  const Instruction* return_instruction = instruction->Next();
+  Instruction::Code return_opcode = return_instruction->Opcode();
+  if (return_opcode != Instruction::RETURN &&
+      return_opcode != Instruction::RETURN_OBJECT) {
+    return false;
+  }
+
+  uint32_t return_reg = return_instruction->VRegA_11x();
+  DCHECK_LT(return_reg, code_item->registers_size_);
+
+  uint32_t vA, vB, dummy;
+  uint64_t dummy_wide;
+  instruction->Decode(vA, vB, dummy_wide, dummy, nullptr);
+  if (instruction->Opcode() == Instruction::CONST_HIGH16) {
+    vB <<= 16;
+  }
+  DCHECK_LT(vA, code_item->registers_size_);
+  if (vA != return_reg) {
+    return false;  // Not returning the value set by const?
+  }
+  if (return_opcode == Instruction::RETURN_OBJECT && vB != 0) {
+    return false;  // Returning non-null reference constant?
+  }
+  return AddInlineMethod(method_idx, kInlineOpConst, kInlineSpecial, vB);
+}
+
+bool DexFileMethodInliner::AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+                                             OpSize size, bool is_object) {
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  Instruction::Code opcode = instruction->Opcode();
+  const Instruction* return_instruction = instruction->Next();
+  Instruction::Code return_opcode = return_instruction->Opcode();
+  if (!(return_opcode == Instruction::RETURN && size != kLong) &&
+      !(return_opcode == Instruction::RETURN_WIDE && size == kLong) &&
+      !(return_opcode == Instruction::RETURN_OBJECT && opcode == Instruction::IGET_OBJECT)) {
+    return false;
+  }
+
+  uint32_t return_reg = return_instruction->VRegA_11x();
+  DCHECK_LT(return_opcode == Instruction::RETURN_WIDE ? return_reg + 1 : return_reg,
+            code_item->registers_size_);
+
+  uint32_t vA, vB, vC;
+  uint64_t dummy_wide;
+  instruction->Decode(vA, vB, dummy_wide, vC, nullptr);
+  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+  DCHECK_GE(vB, arg_start);
+  DCHECK_LT(vB, code_item->registers_size_);
+  DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_);
+  if (vA != return_reg) {
+    return false;  // Not returning the value retrieved by iget?
+  }
+
+  // TODO: Check that the field is FastInstance().
+
+  InlineIGetIPutData data;
+  data.d.field = vC;
+  data.d.op_size = size;
+  data.d.is_object = is_object;
+  data.d.object_arg = vB - arg_start;  // Allow iget on any register, not just "this"
+  data.d.src_arg = 0;
+  data.d.reserved = 0;
+  return AddInlineMethod(method_idx, kInlineOpIGet, kInlineSpecial, data.data);
+}
+
+bool DexFileMethodInliner::AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+                                             OpSize size, bool is_object) {
+  const Instruction* instruction = Instruction::At(code_item->insns_);
+  const Instruction* return_instruction = instruction->Next();
+  if (return_instruction->Opcode() != Instruction::RETURN_VOID) {
+    // TODO: Support returning an argument.
+    // This is needed by builder classes and generated accessor setters.
+    //    builder.setX(value): iput value, this, fieldX; return-object this;
+    //    object.access$nnn(value): iput value, this, fieldX; return value;
+    // Use InlineIGetIPutData::d::reserved to hold the information.
+    return false;
+  }
+
+  uint32_t vA, vB, vC;
+  uint64_t dummy_wide;
+  instruction->Decode(vA, vB, dummy_wide, vC, nullptr);
+  uint32_t arg_start = code_item->registers_size_ - code_item->ins_size_;
+  DCHECK_GE(vB, arg_start);
+  DCHECK_GE(vA, arg_start);
+  DCHECK_LT(vB, code_item->registers_size_);
+  DCHECK_LT(size == kLong ? vA + 1 : vA, code_item->registers_size_);
+
+  // TODO: Check that the field (vC) is FastInstance().
+
+  InlineIGetIPutData data;
+  data.d.field = vC;
+  data.d.op_size = size;
+  data.d.is_object = is_object;
+  data.d.object_arg = vB - arg_start;  // Allow iput on any register, not just "this"
+  data.d.src_arg = vA - arg_start;
+  data.d.reserved = 0;
+  return AddInlineMethod(method_idx, kInlineOpIPut, kInlineSpecial, data.data);
+}
+
 }  // namespace art
diff --git a/compiler/dex/quick/dex_file_method_inliner.h b/compiler/dex/quick/dex_file_method_inliner.h
index 948f4bb..1d99731 100644
--- a/compiler/dex/quick/dex_file_method_inliner.h
+++ b/compiler/dex/quick/dex_file_method_inliner.h
@@ -19,14 +19,18 @@
 
 #include <stdint.h>
 #include <map>
+#include "base/mutex.h"
+#include "base/macros.h"
+#include "dex/compiler_enums.h"
+#include "dex_file.h"
+#include "locks.h"
 
 namespace art {
 
 class CallInfo;
-class DexFile;
 class Mir2Lir;
 
-enum IntrinsicOpcode {
+enum InlineMethodOpcode : uint16_t {
   kIntrinsicDoubleCvt,
   kIntrinsicFloatCvt,
   kIntrinsicReverseBytes,
@@ -44,8 +48,26 @@
   kIntrinsicCas,
   kIntrinsicUnsafeGet,
   kIntrinsicUnsafePut,
+
+  kInlineOpNop,
+  kInlineOpReturnArg,
+  kInlineOpConst,
+  kInlineOpIGet,
+  kInlineOpIPut,
 };
 
+enum InlineMethodFlags {
+  kInlineIntrinsic = 0x0001,
+  kInlineSpecial   = 0x0002,
+};
+
+struct InlineMethod {
+  uint16_t opcode;
+  uint16_t flags;
+  uint32_t data;
+};
+
+// IntrinsicFlags are stored in InlineMethod::data
 enum IntrinsicFlags {
   kIntrinsicFlagNone = 0,
 
@@ -70,10 +92,32 @@
   kIntrinsicFlagIsOrdered  = 8,
 };
 
-struct Intrinsic {
-  IntrinsicOpcode opcode;
+// Check that OpSize fits into 3 bits (at least the values the inliner uses).
+COMPILE_ASSERT(kWord < 8 && kLong < 8 && kSingle < 8 && kDouble < 8 && kUnsignedHalf < 8 &&
+               kSignedHalf < 8 && kUnsignedByte < 8 && kSignedByte < 8, op_size_field_too_narrow);
+
+union InlineIGetIPutData {
   uint32_t data;
+  struct {
+    uint16_t field;
+    uint32_t op_size : 3;  // OpSize
+    uint32_t is_object : 1;
+    uint32_t object_arg : 4;
+    uint32_t src_arg : 4;  // iput only
+    uint32_t reserved : 4;
+  } d;
 };
+COMPILE_ASSERT(sizeof(InlineIGetIPutData) == sizeof(uint32_t), InvalidSizeOfInlineIGetIPutData);
+
+union InlineReturnArgData {
+  uint32_t data;
+  struct {
+    uint16_t arg;
+    uint32_t op_size : 3;  // OpSize
+    uint32_t reserved : 13;
+  } d;
+};
+COMPILE_ASSERT(sizeof(InlineReturnArgData) == sizeof(uint32_t), InvalidSizeOfInlineReturnArgData);
 
 /**
  * Handles inlining of methods from a particular DexFile.
@@ -93,14 +137,34 @@
     ~DexFileMethodInliner();
 
     /**
+     * Analyse method code to determine if the method is a candidate for inlining.
+     * If it is, record its data for later.
+     *
+     * @param method_idx the index of the inlining candidate.
+     * @param code_item a previously verified code item of the method.
+     */
+    bool AnalyseMethodCode(uint32_t method_idx,
+                           const DexFile::CodeItem* code_item) LOCKS_EXCLUDED(lock_);
+
+    /**
      * Check whether a particular method index corresponds to an intrinsic function.
      */
-    bool IsIntrinsic(uint32_t method_index) const;
+    bool IsIntrinsic(uint32_t method_index) LOCKS_EXCLUDED(lock_);
 
     /**
      * Generate code for an intrinsic function invocation.
      */
-    bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) const;
+    bool GenIntrinsic(Mir2Lir* backend, CallInfo* info) LOCKS_EXCLUDED(lock_);
+
+    /**
+     * Check whether a particular method index corresponds to a special function.
+     */
+    bool IsSpecial(uint32_t method_index) LOCKS_EXCLUDED(lock_);
+
+    /**
+     * Generate code for a special function.
+     */
+    bool GenSpecial(Mir2Lir* backend, uint32_t method_idx);
 
   private:
     /**
@@ -258,7 +322,7 @@
      */
     struct IntrinsicDef {
       MethodDef method_def;
-      Intrinsic intrinsic;
+      InlineMethod intrinsic;
     };
 
     /**
@@ -278,8 +342,8 @@
       uint32_t proto_indexes[kProtoCacheLast - kProtoCacheFirst];
     };
 
-    static const char* kClassCacheNames[];
-    static const char* kNameCacheNames[];
+    static const char* const kClassCacheNames[];
+    static const char* const kNameCacheNames[];
     static const ProtoDef kProtoCacheDefs[];
     static const IntrinsicDef kIntrinsicMethods[];
 
@@ -300,14 +364,27 @@
      *
      * Only DexFileToMethodInlinerMap may call this function to initialize the inliner.
      */
-    void FindIntrinsics(const DexFile* dex_file);
+    void FindIntrinsics(const DexFile* dex_file) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
     friend class DexFileToMethodInlinerMap;
 
+    bool AddInlineMethod(int32_t method_idx, InlineMethodOpcode opcode,
+                         uint16_t flags, uint32_t data) LOCKS_EXCLUDED(lock_);
+
+    bool AnalyseReturnMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+                             OpSize size) LOCKS_EXCLUDED(lock_);
+    bool AnalyseConstMethod(int32_t method_idx, const DexFile::CodeItem* code_item)
+                            LOCKS_EXCLUDED(lock_);
+    bool AnalyseIGetMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+                           OpSize size, bool is_object) LOCKS_EXCLUDED(lock_);
+    bool AnalyseIPutMethod(int32_t method_idx, const DexFile::CodeItem* code_item,
+                           OpSize size, bool is_object) LOCKS_EXCLUDED(lock_);
+
+    ReaderWriterMutex lock_;
     /*
      * Maps method indexes (for the particular DexFile) to Intrinsic defintions.
      */
-    std::map<uint32_t, Intrinsic> intrinsics_;
+    std::map<uint32_t, InlineMethod> inline_methods_ GUARDED_BY(lock_);
     const DexFile* dex_file_;
 
     DISALLOW_COPY_AND_ASSIGN(DexFileMethodInliner);
diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.cc b/compiler/dex/quick/dex_file_to_method_inliner_map.cc
index 0107ed3..2fec183 100644
--- a/compiler/dex/quick/dex_file_to_method_inliner_map.cc
+++ b/compiler/dex/quick/dex_file_to_method_inliner_map.cc
@@ -28,7 +28,7 @@
 namespace art {
 
 DexFileToMethodInlinerMap::DexFileToMethodInlinerMap()
-    : lock_("inline_helper_mutex") {
+    : lock_("DexFileToMethodInlinerMap lock", kDexFileToMethodInlinerMapLock) {
 }
 
 DexFileToMethodInlinerMap::~DexFileToMethodInlinerMap() {
@@ -37,26 +37,37 @@
   }
 }
 
-const DexFileMethodInliner& DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) {
+DexFileMethodInliner* DexFileToMethodInlinerMap::GetMethodInliner(const DexFile* dex_file) {
   Thread* self = Thread::Current();
   {
-    ReaderMutexLock lock(self, lock_);
+    ReaderMutexLock mu(self, lock_);
     auto it = inliners_.find(dex_file);
     if (it != inliners_.end()) {
-      return *it->second;
+      return it->second;
     }
   }
 
-  WriterMutexLock lock(self, lock_);
-  DexFileMethodInliner** inliner = &inliners_[dex_file];  // inserts new entry if not found
-  if (*inliner) {
-    return **inliner;
+  // We need to acquire our lock_ to modify inliners_ but we want to release it
+  // before we initialize the new inliner. However, we need to acquire the
+  // new inliner's lock_ before we release our lock_ to prevent another thread
+  // from using the uninitialized inliner. This requires explicit calls to
+  // ExclusiveLock()/ExclusiveUnlock() on one of the locks, the other one
+  // can use WriterMutexLock.
+  DexFileMethodInliner* locked_inliner;
+  {
+    WriterMutexLock mu(self, lock_);
+    DexFileMethodInliner** inliner = &inliners_[dex_file];  // inserts new entry if not found
+    if (*inliner) {
+      return *inliner;
+    }
+    *inliner = new DexFileMethodInliner;
+    DCHECK(*inliner != nullptr);
+    locked_inliner = *inliner;
+    locked_inliner->lock_.ExclusiveLock(self);  // Acquire inliner's lock_ before releasing lock_.
   }
-  *inliner = new DexFileMethodInliner();
-  DCHECK(*inliner != nullptr);
-  // TODO: per-dex file locking for the intrinsics container filling.
-  (*inliner)->FindIntrinsics(dex_file);
-  return **inliner;
+  locked_inliner->FindIntrinsics(dex_file);
+  locked_inliner->lock_.ExclusiveUnlock(self);
+  return locked_inliner;
 }
 
 }  // namespace art
diff --git a/compiler/dex/quick/dex_file_to_method_inliner_map.h b/compiler/dex/quick/dex_file_to_method_inliner_map.h
index 476f002..6d5b889 100644
--- a/compiler/dex/quick/dex_file_to_method_inliner_map.h
+++ b/compiler/dex/quick/dex_file_to_method_inliner_map.h
@@ -40,7 +40,7 @@
     DexFileToMethodInlinerMap();
     ~DexFileToMethodInlinerMap();
 
-    const DexFileMethodInliner& GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_);
+    DexFileMethodInliner* GetMethodInliner(const DexFile* dex_file) LOCKS_EXCLUDED(lock_);
 
   private:
     ReaderWriterMutex lock_;
diff --git a/compiler/dex/quick/gen_invoke.cc b/compiler/dex/quick/gen_invoke.cc
index e66d4ea..82a1932 100644
--- a/compiler/dex/quick/gen_invoke.cc
+++ b/compiler/dex/quick/gen_invoke.cc
@@ -1239,12 +1239,9 @@
 
 void Mir2Lir::GenInvoke(CallInfo* info) {
   if (!(info->opt_flags & MIR_INLINED)) {
-    if (inliner_ == nullptr) {
-      QuickCompilerContext* context = reinterpret_cast<QuickCompilerContext*>(
-          cu_->compiler_driver->GetCompilerContext());
-      inliner_ = &context->GetInlinerMap()->GetMethodInliner(cu_->dex_file);
-    }
-    if (inliner_->GenIntrinsic(this, info)) {
+    DCHECK(cu_->compiler_driver->GetMethodInlinerMap() != nullptr);
+    if (cu_->compiler_driver->GetMethodInlinerMap()->GetMethodInliner(cu_->dex_file)
+        ->GenIntrinsic(this, info)) {
       return;
     }
   }
diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc
index 21d5563..14f49aa 100644
--- a/compiler/dex/quick/mips/call_mips.cc
+++ b/compiler/dex/quick/mips/call_mips.cc
@@ -24,7 +24,7 @@
 namespace art {
 
 void MipsMir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
-                                 SpecialCaseHandler special_case) {
+                                 const InlineMethod& special) {
     // TODO
 }
 
diff --git a/compiler/dex/quick/mips/codegen_mips.h b/compiler/dex/quick/mips/codegen_mips.h
index 450a44f..97dc2b3 100644
--- a/compiler/dex/quick/mips/codegen_mips.h
+++ b/compiler/dex/quick/mips/codegen_mips.h
@@ -133,7 +133,7 @@
     void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
     void GenPackedSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
     void GenSparseSwitch(MIR* mir, uint32_t table_offset, RegLocation rl_src);
-    void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+    void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
 
     // Required for target - single operation generators.
     LIR* OpUnconditionalBranch(LIR* target);
diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc
index 19d04be..c5bbae1 100644
--- a/compiler/dex/quick/mir_to_lir.cc
+++ b/compiler/dex/quick/mir_to_lir.cc
@@ -789,7 +789,8 @@
   return false;
 }
 
-void Mir2Lir::SpecialMIR2LIR(SpecialCaseHandler special_case) {
+void Mir2Lir::SpecialMIR2LIR(const InlineMethod& special) {
+  cu_->NewTimingSplit("SpecialMIR2LIR");
   // Find the first DalvikByteCode block.
   int num_reachable_blocks = mir_graph_->GetNumReachableBlocks();
   BasicBlock*bb = NULL;
@@ -815,7 +816,7 @@
   ResetDefTracking();
   ClobberAllRegs();
 
-  GenSpecialCase(bb, mir, special_case);
+  GenSpecialCase(bb, mir, special);
 }
 
 void Mir2Lir::MethodMIR2LIR() {
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 2a54eb3..2a25f2f 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -105,6 +105,7 @@
 struct BasicBlock;
 struct CallInfo;
 struct CompilationUnit;
+struct InlineMethod;
 struct MIR;
 struct LIR;
 struct RegLocation;
@@ -582,7 +583,7 @@
     void CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list);
     void HandleExtendedMethodMIR(BasicBlock* bb, MIR* mir);
     bool MethodBlockCodeGen(BasicBlock* bb);
-    void SpecialMIR2LIR(SpecialCaseHandler special_case);
+    void SpecialMIR2LIR(const InlineMethod& special);
     void MethodMIR2LIR();
 
 
@@ -703,7 +704,7 @@
     virtual void GenSparseSwitch(MIR* mir, DexOffset table_offset,
                                  RegLocation rl_src) = 0;
     virtual void GenSpecialCase(BasicBlock* bb, MIR* mir,
-                                SpecialCaseHandler special_case) = 0;
+                                const InlineMethod& special) = 0;
     virtual void GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array,
                              RegLocation rl_index, RegLocation rl_dest, int scale) = 0;
     virtual void GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array,
@@ -826,8 +827,6 @@
     unsigned int fp_spill_mask_;
     LIR* first_lir_insn_;
     LIR* last_lir_insn_;
-    // Lazily retrieved method inliner for intrinsics.
-    const DexFileMethodInliner* inliner_;
 };  // Class Mir2Lir
 
 }  // namespace art
diff --git a/compiler/dex/quick/x86/assemble_x86.cc b/compiler/dex/quick/x86/assemble_x86.cc
index e4e345c..c24f0e3 100644
--- a/compiler/dex/quick/x86/assemble_x86.cc
+++ b/compiler/dex/quick/x86/assemble_x86.cc
@@ -246,9 +246,9 @@
   UNARY_ENCODING_MAP(Idivmod, 0x7, 0, SETS_CCODES, DaR, kRegRegReg, IS_UNARY_OP | REG_USE0, DaM, kRegRegMem, IS_BINARY_OP | REG_USE0, DaA, kRegRegArray, IS_QUAD_OP | REG_USE01, 0, REG_DEFA_USEA, REG_DEFAD_USEAD, REG_DEFAD_USEAD, "ah:al,ax,", "dx:ax,dx:ax,", "edx:eax,edx:eax,"),
 #undef UNARY_ENCODING_MAP
 
-  { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0,       { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" },
-  { kX86Push32R,  kRegOpcode, IS_UNARY_OP | REG_USE0 | IS_STORE, { 0, 0, 0x50, 0,    0, 0, 0, 0 }, "Push32R",  "!0r" },
-  { kX86Pop32R,   kRegOpcode, IS_UNARY_OP | REG_DEF0 | IS_LOAD,  { 0, 0, 0x58, 0,    0, 0, 0, 0 }, "Pop32R",   "!0r" },
+  { kX86Bswap32R, kRegOpcode, IS_UNARY_OP | REG_DEF0_USE0,                                 { 0, 0, 0x0F, 0xC8, 0, 0, 0, 0 }, "Bswap32R", "!0r" },
+  { kX86Push32R,  kRegOpcode, IS_UNARY_OP | REG_USE0 | REG_USE_SP | REG_DEF_SP | IS_STORE, { 0, 0, 0x50, 0,    0, 0, 0, 0 }, "Push32R",  "!0r" },
+  { kX86Pop32R,   kRegOpcode, IS_UNARY_OP | REG_DEF0 | REG_USE_SP | REG_DEF_SP | IS_LOAD,  { 0, 0, 0x58, 0,    0, 0, 0, 0 }, "Pop32R",   "!0r" },
 
 #define EXT_0F_ENCODING_MAP(opname, prefix, opcode, reg_def) \
 { kX86 ## opname ## RR, kRegReg,             IS_BINARY_OP   | reg_def | REG_USE01,  { prefix, 0, 0x0F, opcode, 0, 0, 0, 0 }, #opname "RR", "!0r,!1r" }, \
@@ -289,7 +289,7 @@
   { kX86PsrlqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 2, 0, 1 }, "PsrlqRI", "!0r,!1d" },
   { kX86PsllqRI, kRegImm, IS_BINARY_OP | REG_DEF0_USE0, { 0x66, 0, 0x0F, 0x73, 0, 6, 0, 1 }, "PsllqRI", "!0r,!1d" },
   { kX86SqrtsdRR, kRegReg, IS_BINARY_OP | REG_DEF0_USE1, { 0xF2, 0, 0x0F, 0x51, 0, 0, 0, 0 }, "SqrtsdRR", "!0r,!1r" },
-  { kX86FSTPdM, kMem, IS_STORE | IS_BINARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0 }, "FstpdM", "[!0r,!1d]" },
+  { kX86FstpdM, kMem, IS_STORE | IS_BINARY_OP | REG_USE0, { 0x0, 0, 0xDD, 0x00, 0, 3, 0, 0 }, "FstpdM", "[!0r,!1d]" },
 
   EXT_0F_ENCODING_MAP(Movdxr,    0x66, 0x6E, REG_DEF0),
   { kX86MovdrxRR, kRegRegStore, IS_BINARY_OP | REG_DEF0   | REG_USE01,  { 0x66, 0, 0x0F, 0x7E, 0, 0, 0, 0 }, "MovdrxRR", "!0r,!1r" },
@@ -772,6 +772,13 @@
   EmitImm(entry, imm);
 }
 
+void X86Mir2Lir::EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm) {
+  EmitPrefixAndOpcode(entry);
+  EmitModrmDisp(entry->skeleton.modrm_opcode, base, disp);
+  DCHECK_EQ(0, entry->skeleton.ax_opcode);
+  EmitImm(entry, imm);
+}
+
 void X86Mir2Lir::EmitThreadImm(const X86EncodingMap* entry, int disp, int imm) {
   EmitPrefixAndOpcode(entry);
   uint8_t modrm = (0 << 6) | (entry->skeleton.modrm_opcode << 3) | rBP;
@@ -1127,6 +1134,9 @@
       case kMemReg:  // lir operands - 0: base, 1: disp, 2: reg
         EmitMemReg(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
         break;
+      case kMemImm:  // lir operands - 0: base, 1: disp, 2: immediate
+        EmitMemImm(entry, lir->operands[0], lir->operands[1], lir->operands[2]);
+        break;
       case kArrayReg:  // lir operands - 0: base, 1: index, 2: scale, 3: disp, 4: reg
         EmitArrayReg(entry, lir->operands[0], lir->operands[1], lir->operands[2],
                      lir->operands[3], lir->operands[4]);
diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc
index 17924b0..4267b5b 100644
--- a/compiler/dex/quick/x86/call_x86.cc
+++ b/compiler/dex/quick/x86/call_x86.cc
@@ -23,7 +23,7 @@
 namespace art {
 
 void X86Mir2Lir::GenSpecialCase(BasicBlock* bb, MIR* mir,
-                                SpecialCaseHandler special_case) {
+                                const InlineMethod& special) {
   // TODO
 }
 
diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h
index 6552607..22c4452 100644
--- a/compiler/dex/quick/x86/codegen_x86.h
+++ b/compiler/dex/quick/x86/codegen_x86.h
@@ -115,6 +115,8 @@
     void GenXorLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
     LIR* GenRegMemCheck(ConditionCode c_code, int reg1, int base, int offset,
                                 ThrowKind kind);
+    LIR* GenMemImmedCheck(ConditionCode c_code, int base, int offset, int check_value,
+                          ThrowKind kind);
     RegLocation GenDivRem(RegLocation rl_dest, int reg_lo, int reg_hi, bool is_div);
     RegLocation GenDivRemLit(RegLocation rl_dest, int reg_lo, int lit, bool is_div);
     void GenCmpLong(RegLocation rl_dest, RegLocation rl_src1, RegLocation rl_src2);
@@ -133,7 +135,7 @@
     void GenNegFloat(RegLocation rl_dest, RegLocation rl_src);
     void GenPackedSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
     void GenSparseSwitch(MIR* mir, DexOffset table_offset, RegLocation rl_src);
-    void GenSpecialCase(BasicBlock* bb, MIR* mir, SpecialCaseHandler special_case);
+    void GenSpecialCase(BasicBlock* bb, MIR* mir, const InlineMethod& special);
 
     // Single operation generators.
     LIR* OpUnconditionalBranch(LIR* target);
@@ -184,6 +186,7 @@
     void EmitOpArray(const X86EncodingMap* entry, uint8_t base, uint8_t index,
                      int scale, int disp);
     void EmitMemReg(const X86EncodingMap* entry, uint8_t base, int disp, uint8_t reg);
+    void EmitMemImm(const X86EncodingMap* entry, uint8_t base, int disp, int32_t imm);
     void EmitRegMem(const X86EncodingMap* entry, uint8_t reg, uint8_t base, int disp);
     void EmitRegArray(const X86EncodingMap* entry, uint8_t reg, uint8_t base, uint8_t index,
                       int scale, int disp);
@@ -206,6 +209,8 @@
                    int scale, int table_or_disp);
     void EmitMacro(const X86EncodingMap* entry, uint8_t reg, int offset);
     void EmitUnimplemented(const X86EncodingMap* entry, LIR* lir);
+    void GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
+                                  int64_t val, ConditionCode ccode);
 };
 
 }  // namespace art
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index 0133a0a..56cf7e9 100644
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -38,6 +38,20 @@
 }
 
 /*
+ * Perform a compare of memory to immediate value
+ */
+LIR* X86Mir2Lir::GenMemImmedCheck(ConditionCode c_code,
+                                int base, int offset, int check_value, ThrowKind kind) {
+  LIR* tgt = RawLIR(0, kPseudoThrowTarget, kind,
+                    current_dalvik_offset_, base, check_value, 0);
+  NewLIR3(IS_SIMM8(check_value) ? kX86Cmp32MI8 : kX86Cmp32MI, base, offset, check_value);
+  LIR* branch = OpCondBranch(c_code, tgt);
+  // Remember branch target - will process later
+  throw_launchpads_.Insert(tgt);
+  return branch;
+}
+
+/*
  * Compare two 64-bit values
  *    x = y     return  0
  *    x < y     return -1
@@ -169,11 +183,23 @@
   LIR* taken = &block_label_list_[bb->taken];
   RegLocation rl_src1 = mir_graph_->GetSrcWide(mir, 0);
   RegLocation rl_src2 = mir_graph_->GetSrcWide(mir, 2);
+  ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
+
+  if (rl_src1.is_const) {
+    std::swap(rl_src1, rl_src2);
+    ccode = FlipComparisonOrder(ccode);
+  }
+  if (rl_src2.is_const) {
+    // Do special compare/branch against simple const operand
+    int64_t val = mir_graph_->ConstantValueWide(rl_src2);
+    GenFusedLongCmpImmBranch(bb, rl_src1, val, ccode);
+    return;
+  }
+
   FlushAllRegs();
   LockCallTemps();  // Prepare for explicit register usage
   LoadValueDirectWideFixed(rl_src1, r0, r1);
   LoadValueDirectWideFixed(rl_src2, r2, r3);
-  ConditionCode ccode = static_cast<ConditionCode>(mir->dalvikInsn.arg[0]);
   // Swap operands and condition code to prevent use of zero flag.
   if (ccode == kCondLe || ccode == kCondGt) {
     // Compute (r3:r2) = (r3:r2) - (r1:r0)
@@ -204,6 +230,56 @@
   OpCondBranch(ccode, taken);
 }
 
+void X86Mir2Lir::GenFusedLongCmpImmBranch(BasicBlock* bb, RegLocation rl_src1,
+                                          int64_t val, ConditionCode ccode) {
+  int32_t val_lo = Low32Bits(val);
+  int32_t val_hi = High32Bits(val);
+  LIR* taken = &block_label_list_[bb->taken];
+  LIR* not_taken = &block_label_list_[bb->fall_through];
+  rl_src1 = LoadValueWide(rl_src1, kCoreReg);
+  int32_t low_reg = rl_src1.low_reg;
+  int32_t high_reg = rl_src1.high_reg;
+
+  if (val == 0 && (ccode == kCondEq || ccode == kCondNe)) {
+    int t_reg = AllocTemp();
+    OpRegRegReg(kOpOr, t_reg, low_reg, high_reg);
+    FreeTemp(t_reg);
+    OpCondBranch(ccode, taken);
+    return;
+  }
+
+  OpRegImm(kOpCmp, high_reg, val_hi);
+  switch (ccode) {
+    case kCondEq:
+    case kCondNe:
+      OpCondBranch(kCondNe, (ccode == kCondEq) ? not_taken : taken);
+      break;
+    case kCondLt:
+      OpCondBranch(kCondLt, taken);
+      OpCondBranch(kCondGt, not_taken);
+      ccode = kCondUlt;
+      break;
+    case kCondLe:
+      OpCondBranch(kCondLt, taken);
+      OpCondBranch(kCondGt, not_taken);
+      ccode = kCondLs;
+      break;
+    case kCondGt:
+      OpCondBranch(kCondGt, taken);
+      OpCondBranch(kCondLt, not_taken);
+      ccode = kCondHi;
+      break;
+    case kCondGe:
+      OpCondBranch(kCondGt, taken);
+      OpCondBranch(kCondLt, not_taken);
+      ccode = kCondUge;
+      break;
+    default:
+      LOG(FATAL) << "Unexpected ccode: " << ccode;
+  }
+  OpCmpImmBranch(ccode, low_reg, val_lo, taken);
+}
+
 RegLocation X86Mir2Lir::GenDivRemLit(RegLocation rl_dest, int reg_lo,
                                      int lit, bool is_div) {
   LOG(FATAL) << "Unexpected use of GenDivRemLit for x86";
@@ -295,16 +371,17 @@
   if (is_long) {
     FlushAllRegs();
     LockCallTemps();
+    LoadValueDirectWideFixed(rl_src_expected, rAX, rDX);
+    LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX);
     NewLIR1(kX86Push32R, rDI);
     MarkTemp(rDI);
     LockTemp(rDI);
     NewLIR1(kX86Push32R, rSI);
     MarkTemp(rSI);
     LockTemp(rSI);
-    LoadValueDirectFixed(rl_src_obj, rDI);
-    LoadValueDirectFixed(rl_src_offset, rSI);
-    LoadValueDirectWideFixed(rl_src_expected, rAX, rDX);
-    LoadValueDirectWideFixed(rl_src_new_value, rBX, rCX);
+    const int push_offset = 4 /* push edi */ + 4 /* push esi */;
+    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_obj.s_reg_low) + push_offset, rDI);
+    LoadWordDisp(TargetReg(kSp), SRegOffset(rl_src_offset.s_reg_low) + push_offset, rSI);
     NewLIR4(kX86LockCmpxchg8bA, rDI, rSI, 0, 0);
     FreeTemp(rSI);
     UnmarkTemp(rSI);
@@ -520,41 +597,49 @@
                              RegLocation rl_index, RegLocation rl_dest, int scale) {
   RegisterClass reg_class = oat_reg_class_by_size(size);
   int len_offset = mirror::Array::LengthOffset().Int32Value();
-  int data_offset;
   RegLocation rl_result;
   rl_array = LoadValue(rl_array, kCoreReg);
-  rl_index = LoadValue(rl_index, kCoreReg);
 
+  int data_offset;
   if (size == kLong || size == kDouble) {
     data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Int32Value();
   } else {
     data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value();
   }
 
+  bool constant_index = rl_index.is_const;
+  int32_t constant_index_value = 0;
+  if (!constant_index) {
+    rl_index = LoadValue(rl_index, kCoreReg);
+  } else {
+    constant_index_value = mir_graph_->ConstantValue(rl_index);
+    // If index is constant, just fold it into the data offset
+    data_offset += constant_index_value << scale;
+    // treat as non array below
+    rl_index.low_reg = INVALID_REG;
+  }
+
   /* null object? */
   GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
 
   if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
-    /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
-    GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
-                   len_offset, kThrowArrayBounds);
+    if (constant_index) {
+      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
+                       constant_index_value, kThrowConstantArrayBounds);
+    } else {
+      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
+                     len_offset, kThrowArrayBounds);
+    }
   }
+  rl_result = EvalLoc(rl_dest, reg_class, true);
   if ((size == kLong) || (size == kDouble)) {
-    int reg_addr = AllocTemp();
-    OpLea(reg_addr, rl_array.low_reg, rl_index.low_reg, scale, data_offset);
-    FreeTemp(rl_array.low_reg);
-    FreeTemp(rl_index.low_reg);
-    rl_result = EvalLoc(rl_dest, reg_class, true);
-    LoadBaseIndexedDisp(reg_addr, INVALID_REG, 0, 0, rl_result.low_reg,
+    LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale, data_offset, rl_result.low_reg,
                         rl_result.high_reg, size, INVALID_SREG);
     StoreValueWide(rl_dest, rl_result);
   } else {
-    rl_result = EvalLoc(rl_dest, reg_class, true);
-
     LoadBaseIndexedDisp(rl_array.low_reg, rl_index.low_reg, scale,
                         data_offset, rl_result.low_reg, INVALID_REG, size,
                         INVALID_SREG);
-
     StoreValue(rl_dest, rl_result);
   }
 }
@@ -576,14 +661,29 @@
   }
 
   rl_array = LoadValue(rl_array, kCoreReg);
-  rl_index = LoadValue(rl_index, kCoreReg);
+  bool constant_index = rl_index.is_const;
+  int32_t constant_index_value = 0;
+  if (!constant_index) {
+    rl_index = LoadValue(rl_index, kCoreReg);
+  } else {
+    // If index is constant, just fold it into the data offset
+    constant_index_value = mir_graph_->ConstantValue(rl_index);
+    data_offset += constant_index_value << scale;
+    // treat as non array below
+    rl_index.low_reg = INVALID_REG;
+  }
 
   /* null object? */
   GenNullCheck(rl_array.s_reg_low, rl_array.low_reg, opt_flags);
 
   if (!(opt_flags & MIR_IGNORE_RANGE_CHECK)) {
-    /* if (rl_index >= [rl_array + len_offset]) goto kThrowArrayBounds */
-    GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg, len_offset, kThrowArrayBounds);
+    if (constant_index) {
+      GenMemImmedCheck(kCondLs, rl_array.low_reg, len_offset,
+                       constant_index_value, kThrowConstantArrayBounds);
+    } else {
+      GenRegMemCheck(kCondUge, rl_index.low_reg, rl_array.low_reg,
+                     len_offset, kThrowArrayBounds);
+    }
   }
   if ((size == kLong) || (size == kDouble)) {
     rl_src = LoadValueWide(rl_src, reg_class);
@@ -602,7 +702,9 @@
   }
   if (card_mark) {
     // Free rl_index if its a temp. Ensures there are 2 free regs for card mark.
-    FreeTemp(rl_index.low_reg);
+    if (!constant_index) {
+      FreeTemp(rl_index.low_reg);
+    }
     MarkGCCard(rl_src.low_reg, rl_array.low_reg);
   }
 }
diff --git a/compiler/dex/quick/x86/x86_lir.h b/compiler/dex/quick/x86/x86_lir.h
index ca5a234..a2d5c3e 100644
--- a/compiler/dex/quick/x86/x86_lir.h
+++ b/compiler/dex/quick/x86/x86_lir.h
@@ -349,7 +349,7 @@
   kX86PsrlqRI,                  // right shift of floating point registers
   kX86PsllqRI,                  // left shift of floating point registers
   kX86SqrtsdRR,                 // sqrt of floating point register
-  kX86FSTPdM,                   // Store and pop top x87 fp stack
+  kX86FstpdM,                   // Store and pop top x87 fp stack
   Binary0fOpCode(kX86Movdxr),   // move into xmm from gpr
   kX86MovdrxRR, kX86MovdrxMR, kX86MovdrxAR,  // move into reg from xmm
   kX86Set8R, kX86Set8M, kX86Set8A,  // set byte depending on condition operand
diff --git a/compiler/dex/verified_methods_data.cc b/compiler/dex/verified_methods_data.cc
new file mode 100644
index 0000000..e6c4dda
--- /dev/null
+++ b/compiler/dex/verified_methods_data.cc
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2013 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 "base/stl_util.h"
+#include "dex_file.h"
+#include "dex_instruction.h"
+#include "dex_instruction-inl.h"
+#include "base/mutex.h"
+#include "base/mutex-inl.h"
+#include "mirror/art_method.h"
+#include "mirror/art_method-inl.h"
+#include "mirror/class.h"
+#include "mirror/class-inl.h"
+#include "mirror/dex_cache.h"
+#include "mirror/dex_cache-inl.h"
+#include "mirror/object.h"
+#include "mirror/object-inl.h"
+#include "verified_methods_data.h"
+#include "verifier/dex_gc_map.h"
+#include "verifier/method_verifier.h"
+#include "verifier/method_verifier-inl.h"
+#include "verifier/register_line.h"
+#include "verifier/register_line-inl.h"
+
+namespace art {
+
+VerifiedMethodsData::VerifiedMethodsData()
+    : dex_gc_maps_lock_("compiler GC maps lock"),
+      dex_gc_maps_(),
+      safecast_map_lock_("compiler Cast Elision lock"),
+      safecast_map_(),
+      devirt_maps_lock_("compiler Devirtualization lock"),
+      devirt_maps_(),
+      rejected_classes_lock_("compiler rejected classes lock"),
+      rejected_classes_() {
+}
+
+VerifiedMethodsData::~VerifiedMethodsData() {
+  Thread* self = Thread::Current();
+  {
+    WriterMutexLock mu(self, dex_gc_maps_lock_);
+    STLDeleteValues(&dex_gc_maps_);
+  }
+  {
+    WriterMutexLock mu(self, safecast_map_lock_);
+    STLDeleteValues(&safecast_map_);
+  }
+  {
+    WriterMutexLock mu(self, devirt_maps_lock_);
+    STLDeleteValues(&devirt_maps_);
+  }
+}
+
+bool VerifiedMethodsData::ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier) {
+  MethodReference ref = method_verifier->GetMethodReference();
+  bool compile = IsCandidateForCompilation(ref, method_verifier->GetAccessFlags());
+  if (compile) {
+    /* Generate a register map and add it to the method. */
+    const std::vector<uint8_t>* dex_gc_map = GenerateGcMap(method_verifier);
+    if (dex_gc_map == NULL) {
+      DCHECK(method_verifier->HasFailures());
+      return false;  // Not a real failure, but a failure to encode
+    }
+    if (kIsDebugBuild) {
+      VerifyGcMap(method_verifier, *dex_gc_map);
+    }
+    SetDexGcMap(ref, dex_gc_map);
+
+    // TODO: move this out when DEX-to-DEX supports devirtualization.
+    if (method_verifier->HasVirtualOrInterfaceInvokes()) {
+      PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(method_verifier);
+      if (pc_to_concrete_method != NULL) {
+        SetDevirtMap(ref, pc_to_concrete_method);
+      }
+    }
+  }
+
+  if (method_verifier->HasCheckCasts()) {
+    MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(method_verifier);
+    if (method_to_safe_casts != NULL) {
+      SetSafeCastMap(ref, method_to_safe_casts);
+    }
+  }
+  return true;
+}
+
+const std::vector<uint8_t>* VerifiedMethodsData::GetDexGcMap(MethodReference ref) {
+  ReaderMutexLock mu(Thread::Current(), dex_gc_maps_lock_);
+  DexGcMapTable::const_iterator it = dex_gc_maps_.find(ref);
+  CHECK(it != dex_gc_maps_.end())
+    << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file);
+  CHECK(it->second != NULL);
+  return it->second;
+}
+
+const MethodReference* VerifiedMethodsData::GetDevirtMap(const MethodReference& ref,
+                                                                    uint32_t dex_pc) {
+  ReaderMutexLock mu(Thread::Current(), devirt_maps_lock_);
+  DevirtualizationMapTable::const_iterator it = devirt_maps_.find(ref);
+  if (it == devirt_maps_.end()) {
+    return NULL;
+  }
+
+  // Look up the PC in the map, get the concrete method to execute and return its reference.
+  PcToConcreteMethodMap::const_iterator pc_to_concrete_method = it->second->find(dex_pc);
+  if (pc_to_concrete_method != it->second->end()) {
+    return &(pc_to_concrete_method->second);
+  } else {
+    return NULL;
+  }
+}
+
+bool VerifiedMethodsData::IsSafeCast(MethodReference ref, uint32_t pc) {
+  ReaderMutexLock mu(Thread::Current(), safecast_map_lock_);
+  SafeCastMap::const_iterator it = safecast_map_.find(ref);
+  if (it == safecast_map_.end()) {
+    return false;
+  }
+
+  // Look up the cast address in the set of safe casts
+  // Use binary_search for lookup in the sorted vector.
+  return std::binary_search(it->second->begin(), it->second->end(), pc);
+}
+
+void VerifiedMethodsData::AddRejectedClass(ClassReference ref) {
+  {
+    WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
+    rejected_classes_.insert(ref);
+  }
+  DCHECK(IsClassRejected(ref));
+}
+
+bool VerifiedMethodsData::IsClassRejected(ClassReference ref) {
+  ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_);
+  return (rejected_classes_.find(ref) != rejected_classes_.end());
+}
+
+bool VerifiedMethodsData::IsCandidateForCompilation(MethodReference& method_ref,
+                                                    const uint32_t access_flags) {
+#ifdef ART_SEA_IR_MODE
+    bool use_sea = Runtime::Current()->IsSeaIRMode();
+    use_sea = use_sea && (std::string::npos != PrettyMethod(
+                          method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
+    if (use_sea) return true;
+#endif
+  // Don't compile class initializers, ever.
+  if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
+    return false;
+  }
+  return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
+}
+
+const std::vector<uint8_t>* VerifiedMethodsData::GenerateGcMap(
+    verifier::MethodVerifier* method_verifier) {
+  size_t num_entries, ref_bitmap_bits, pc_bits;
+  ComputeGcMapSizes(method_verifier, &num_entries, &ref_bitmap_bits, &pc_bits);
+  // There's a single byte to encode the size of each bitmap
+  if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
+    // TODO: either a better GC map format or per method failures
+    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+        << "Cannot encode GC map for method with " << ref_bitmap_bits << " registers";
+    return NULL;
+  }
+  size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
+  // There are 2 bytes to encode the number of entries
+  if (num_entries >= 65536) {
+    // TODO: either a better GC map format or per method failures
+    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+        << "Cannot encode GC map for method with " << num_entries << " entries";
+    return NULL;
+  }
+  size_t pc_bytes;
+  verifier::RegisterMapFormat format;
+  if (pc_bits <= 8) {
+    format = verifier::kRegMapFormatCompact8;
+    pc_bytes = 1;
+  } else if (pc_bits <= 16) {
+    format = verifier::kRegMapFormatCompact16;
+    pc_bytes = 2;
+  } else {
+    // TODO: either a better GC map format or per method failures
+    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+        << "Cannot encode GC map for method with "
+        << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
+    return NULL;
+  }
+  size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
+  std::vector<uint8_t>* table = new std::vector<uint8_t>;
+  if (table == NULL) {
+    method_verifier->Fail(verifier::VERIFY_ERROR_BAD_CLASS_HARD)
+        << "Failed to encode GC map (size=" << table_size << ")";
+    return NULL;
+  }
+  table->reserve(table_size);
+  // Write table header
+  table->push_back(format | ((ref_bitmap_bytes & ~0xFF) >> 5));
+  table->push_back(ref_bitmap_bytes & 0xFF);
+  table->push_back(num_entries & 0xFF);
+  table->push_back((num_entries >> 8) & 0xFF);
+  // Write table data
+  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+  for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
+    if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
+      table->push_back(i & 0xFF);
+      if (pc_bytes == 2) {
+        table->push_back((i >> 8) & 0xFF);
+      }
+      verifier::RegisterLine* line = method_verifier->GetRegLine(i);
+      line->WriteReferenceBitMap(*table, ref_bitmap_bytes);
+    }
+  }
+  DCHECK_EQ(table->size(), table_size);
+  return table;
+}
+
+void VerifiedMethodsData::VerifyGcMap(verifier::MethodVerifier* method_verifier,
+                                      const std::vector<uint8_t>& data) {
+  // Check that for every GC point there is a map entry, there aren't entries for non-GC points,
+  // that the table data is well formed and all references are marked (or not) in the bitmap
+  verifier::DexPcToReferenceMap map(&data[0]);
+  DCHECK_EQ(data.size(), map.RawSize());
+  size_t map_index = 0;
+  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+  for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
+    const uint8_t* reg_bitmap = map.FindBitMap(i, false);
+    if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
+      CHECK_LT(map_index, map.NumEntries());
+      CHECK_EQ(map.GetDexPc(map_index), i);
+      CHECK_EQ(map.GetBitMap(map_index), reg_bitmap);
+      map_index++;
+      verifier::RegisterLine* line = method_verifier->GetRegLine(i);
+      for (size_t j = 0; j < code_item->registers_size_; j++) {
+        if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) {
+          CHECK_LT(j / 8, map.RegWidth());
+          CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
+        } else if ((j / 8) < map.RegWidth()) {
+          CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0);
+        } else {
+          // If a register doesn't contain a reference then the bitmap may be shorter than the line
+        }
+      }
+    } else {
+      CHECK(reg_bitmap == NULL);
+    }
+  }
+}
+
+void VerifiedMethodsData::ComputeGcMapSizes(verifier::MethodVerifier* method_verifier,
+                                            size_t* gc_points, size_t* ref_bitmap_bits,
+                                            size_t* log2_max_gc_pc) {
+  size_t local_gc_points = 0;
+  size_t max_insn = 0;
+  size_t max_ref_reg = -1;
+  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+  for (size_t i = 0; i < code_item->insns_size_in_code_units_; i++) {
+    if (method_verifier->GetInstructionFlags(i).IsCompileTimeInfoPoint()) {
+      local_gc_points++;
+      max_insn = i;
+      verifier::RegisterLine* line = method_verifier->GetRegLine(i);
+      max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg);
+    }
+  }
+  *gc_points = local_gc_points;
+  *ref_bitmap_bits = max_ref_reg + 1;  // if max register is 0 we need 1 bit to encode (ie +1)
+  size_t i = 0;
+  while ((1U << i) <= max_insn) {
+    i++;
+  }
+  *log2_max_gc_pc = i;
+}
+
+void VerifiedMethodsData::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* gc_map) {
+  DCHECK(Runtime::Current()->IsCompiler());
+  {
+    WriterMutexLock mu(Thread::Current(), dex_gc_maps_lock_);
+    DexGcMapTable::iterator it = dex_gc_maps_.find(ref);
+    if (it != dex_gc_maps_.end()) {
+      delete it->second;
+      dex_gc_maps_.erase(it);
+    }
+    dex_gc_maps_.Put(ref, gc_map);
+  }
+  DCHECK(GetDexGcMap(ref) != NULL);
+}
+
+VerifiedMethodsData::MethodSafeCastSet* VerifiedMethodsData::GenerateSafeCastSet(
+    verifier::MethodVerifier* method_verifier) {
+  /*
+   * Walks over the method code and adds any cast instructions in which
+   * the type cast is implicit to a set, which is used in the code generation
+   * to elide these casts.
+   */
+  if (method_verifier->HasFailures()) {
+    return NULL;
+  }
+  UniquePtr<MethodSafeCastSet> mscs;
+  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+  const Instruction* inst = Instruction::At(code_item->insns_);
+  const Instruction* end = Instruction::At(code_item->insns_ +
+                                           code_item->insns_size_in_code_units_);
+
+  for (; inst < end; inst = inst->Next()) {
+    Instruction::Code code = inst->Opcode();
+    if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) {
+      uint32_t dex_pc = inst->GetDexPc(code_item->insns_);
+      const verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
+      bool is_safe_cast = false;
+      if (code == Instruction::CHECK_CAST) {
+        const verifier::RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
+        const verifier::RegType& cast_type =
+            method_verifier->ResolveCheckedClass(inst->VRegB_21c());
+        is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type);
+      } else {
+        const verifier::RegType& array_type(line->GetRegisterType(inst->VRegB_23x()));
+        // We only know its safe to assign to an array if the array type is precise. For example,
+        // an Object[] can have any type of object stored in it, but it may also be assigned a
+        // String[] in which case the stores need to be of Strings.
+        if (array_type.IsPreciseReference()) {
+          const verifier::RegType& value_type(line->GetRegisterType(inst->VRegA_23x()));
+          const verifier::RegType& component_type = method_verifier->GetRegTypeCache()
+              ->GetComponentType(array_type, method_verifier->GetClassLoader());
+          is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
+        }
+      }
+      if (is_safe_cast) {
+        if (mscs.get() == nullptr) {
+          mscs.reset(new MethodSafeCastSet());
+        } else {
+          DCHECK_LT(mscs->back(), dex_pc);  // Verify ordering for push_back() to the sorted vector.
+        }
+        mscs->push_back(dex_pc);
+      }
+    }
+  }
+  return mscs.release();
+}
+
+void  VerifiedMethodsData::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) {
+  WriterMutexLock mu(Thread::Current(), safecast_map_lock_);
+  SafeCastMap::iterator it = safecast_map_.find(ref);
+  if (it != safecast_map_.end()) {
+    delete it->second;
+    safecast_map_.erase(it);
+  }
+  safecast_map_.Put(ref, cast_set);
+  DCHECK(safecast_map_.find(ref) != safecast_map_.end());
+}
+
+VerifiedMethodsData::PcToConcreteMethodMap* VerifiedMethodsData::GenerateDevirtMap(
+    verifier::MethodVerifier* method_verifier) {
+  // It is risky to rely on reg_types for sharpening in cases of soft
+  // verification, we might end up sharpening to a wrong implementation. Just abort.
+  if (method_verifier->HasFailures()) {
+    return NULL;
+  }
+
+  UniquePtr<PcToConcreteMethodMap> pc_to_concrete_method_map;
+  const DexFile::CodeItem* code_item = method_verifier->CodeItem();
+  const uint16_t* insns = code_item->insns_;
+  const Instruction* inst = Instruction::At(insns);
+  const Instruction* end = Instruction::At(insns + code_item->insns_size_in_code_units_);
+
+  for (; inst < end; inst = inst->Next()) {
+    bool is_virtual   = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
+        (inst->Opcode() ==  Instruction::INVOKE_VIRTUAL_RANGE);
+    bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
+        (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
+
+    if (!is_interface && !is_virtual) {
+      continue;
+    }
+    // Get reg type for register holding the reference to the object that will be dispatched upon.
+    uint32_t dex_pc = inst->GetDexPc(insns);
+    verifier::RegisterLine* line = method_verifier->GetRegLine(dex_pc);
+    bool is_range = (inst->Opcode() ==  Instruction::INVOKE_VIRTUAL_RANGE) ||
+        (inst->Opcode() ==  Instruction::INVOKE_INTERFACE_RANGE);
+    const verifier::RegType&
+        reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
+
+    if (!reg_type.HasClass()) {
+      // We will compute devirtualization information only when we know the Class of the reg type.
+      continue;
+    }
+    mirror::Class* reg_class = reg_type.GetClass();
+    if (reg_class->IsInterface()) {
+      // We can't devirtualize when the known type of the register is an interface.
+      continue;
+    }
+    if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) {
+      // We can't devirtualize abstract classes except on arrays of abstract classes.
+      continue;
+    }
+    mirror::ArtMethod* abstract_method = method_verifier->GetDexCache()->GetResolvedMethod(
+        is_range ? inst->VRegB_3rc() : inst->VRegB_35c());
+    if (abstract_method == NULL) {
+      // If the method is not found in the cache this means that it was never found
+      // by ResolveMethodAndCheckAccess() called when verifying invoke_*.
+      continue;
+    }
+    // Find the concrete method.
+    mirror::ArtMethod* concrete_method = NULL;
+    if (is_interface) {
+      concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method);
+    }
+    if (is_virtual) {
+      concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method);
+    }
+    if (concrete_method == NULL || concrete_method->IsAbstract()) {
+      // In cases where concrete_method is not found, or is abstract, continue to the next invoke.
+      continue;
+    }
+    if (reg_type.IsPreciseReference() || concrete_method->IsFinal() ||
+        concrete_method->GetDeclaringClass()->IsFinal()) {
+      // If we knew exactly the class being dispatched upon, or if the target method cannot be
+      // overridden record the target to be used in the compiler driver.
+      if (pc_to_concrete_method_map.get() == NULL) {
+        pc_to_concrete_method_map.reset(new PcToConcreteMethodMap());
+      }
+      MethodReference concrete_ref(
+          concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(),
+          concrete_method->GetDexMethodIndex());
+      pc_to_concrete_method_map->Put(dex_pc, concrete_ref);
+    }
+  }
+  return pc_to_concrete_method_map.release();
+}
+
+void  VerifiedMethodsData::SetDevirtMap(MethodReference ref,
+                                   const PcToConcreteMethodMap* devirt_map) {
+  WriterMutexLock mu(Thread::Current(), devirt_maps_lock_);
+  DevirtualizationMapTable::iterator it = devirt_maps_.find(ref);
+  if (it != devirt_maps_.end()) {
+    delete it->second;
+    devirt_maps_.erase(it);
+  }
+
+  devirt_maps_.Put(ref, devirt_map);
+  DCHECK(devirt_maps_.find(ref) != devirt_maps_.end());
+}
+
+}  // namespace art
diff --git a/compiler/dex/verified_methods_data.h b/compiler/dex/verified_methods_data.h
new file mode 100644
index 0000000..d495dff
--- /dev/null
+++ b/compiler/dex/verified_methods_data.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2013 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_DEX_VERIFIED_METHODS_DATA_H_
+#define ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_
+
+#include <stdint.h>
+#include <set>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "class_reference.h"
+#include "method_reference.h"
+#include "safe_map.h"
+
+namespace art {
+
+namespace verifier {
+class MethodVerifier;
+}  // namespace verifier
+
+class VerifiedMethodsData {
+  public:
+    VerifiedMethodsData();
+    ~VerifiedMethodsData();
+
+    bool ProcessVerifiedMethod(verifier::MethodVerifier* method_verifier)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+        LOCKS_EXCLUDED(dex_gc_maps_lock_, devirt_maps_lock_, safecast_map_lock_);
+
+    const std::vector<uint8_t>* GetDexGcMap(MethodReference ref)
+        LOCKS_EXCLUDED(dex_gc_maps_lock_);
+
+    const MethodReference* GetDevirtMap(const MethodReference& ref, uint32_t dex_pc)
+        LOCKS_EXCLUDED(devirt_maps_lock_);
+
+    // Returns true if the cast can statically be verified to be redundant
+    // by using the check-cast elision peephole optimization in the verifier
+    bool IsSafeCast(MethodReference ref, uint32_t pc) LOCKS_EXCLUDED(safecast_map_lock_);
+
+    void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
+    bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_);
+
+    static bool IsCandidateForCompilation(MethodReference& method_ref,
+                                          const uint32_t access_flags);
+
+  private:
+    /*
+     * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
+     * verification). For type-precise determination we have all the data we need, so we just need to
+     * encode it in some clever fashion.
+     * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
+     */
+    const std::vector<uint8_t>* GenerateGcMap(verifier::MethodVerifier* method_verifier);
+
+    // Verify that the GC map associated with method_ is well formed
+    void VerifyGcMap(verifier::MethodVerifier* method_verifier, const std::vector<uint8_t>& data);
+
+    // Compute sizes for GC map data
+    void ComputeGcMapSizes(verifier::MethodVerifier* method_verifier,
+                           size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
+
+    // All the GC maps that the verifier has created
+    typedef SafeMap<const MethodReference, const std::vector<uint8_t>*,
+        MethodReferenceComparator> DexGcMapTable;
+    ReaderWriterMutex dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+    DexGcMapTable dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_);
+    void SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* dex_gc_map)
+        LOCKS_EXCLUDED(dex_gc_maps_lock_);
+
+    // Cast elision types.
+    // Since we're adding the dex PCs to the set in increasing order, a sorted vector
+    // is better for performance (not just memory usage), especially for large sets.
+    typedef std::vector<uint32_t> MethodSafeCastSet;
+    typedef SafeMap<MethodReference, const MethodSafeCastSet*,
+        MethodReferenceComparator> SafeCastMap;
+    MethodSafeCastSet* GenerateSafeCastSet(verifier::MethodVerifier* method_verifier)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+    void SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* mscs)
+        LOCKS_EXCLUDED(safecast_map_lock_);
+    ReaderWriterMutex safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+    SafeCastMap safecast_map_ GUARDED_BY(safecast_map_lock_);
+
+    // Devirtualization map.
+    typedef SafeMap<uint32_t, MethodReference> PcToConcreteMethodMap;
+    typedef SafeMap<MethodReference, const PcToConcreteMethodMap*,
+        MethodReferenceComparator> DevirtualizationMapTable;
+    PcToConcreteMethodMap* GenerateDevirtMap(verifier::MethodVerifier* method_verifier)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+    ReaderWriterMutex devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+    DevirtualizationMapTable devirt_maps_ GUARDED_BY(devirt_maps_lock_);
+    void SetDevirtMap(MethodReference ref, const PcToConcreteMethodMap* pc_method_map)
+          LOCKS_EXCLUDED(devirt_maps_lock_);
+
+    // Rejected classes
+    typedef std::set<ClassReference> RejectedClassesTable;
+    ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+    RejectedClassesTable rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_DEX_VERIFIED_METHODS_DATA_H_
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 309af1d..1124541 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -27,6 +27,7 @@
 #include "class_linker.h"
 #include "dex_compilation_unit.h"
 #include "dex_file-inl.h"
+#include "dex/verified_methods_data.h"
 #include "jni_internal.h"
 #include "object_utils.h"
 #include "runtime.h"
@@ -284,10 +285,10 @@
 };
 
 extern "C" void ArtInitCompilerContext(art::CompilerDriver& driver);
-extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& compiler);
+extern "C" void ArtInitQuickCompilerContext(art::CompilerDriver& driver);
 
 extern "C" void ArtUnInitCompilerContext(art::CompilerDriver& driver);
-extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& compiler);
+extern "C" void ArtUnInitQuickCompilerContext(art::CompilerDriver& driver);
 
 extern "C" art::CompiledMethod* ArtCompileMethod(art::CompilerDriver& driver,
                                                  const art::DexFile::CodeItem* code_item,
@@ -335,11 +336,15 @@
 extern "C" void compilerLLVMSetBitcodeFileName(art::CompilerDriver& driver,
                                                std::string const& filename);
 
-CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+CompilerDriver::CompilerDriver(VerifiedMethodsData* verified_methods_data,
+                               DexFileToMethodInlinerMap* method_inliner_map,
+                               CompilerBackend compiler_backend, InstructionSet instruction_set,
                                InstructionSetFeatures instruction_set_features,
                                bool image, DescriptorSet* image_classes, size_t thread_count,
                                bool dump_stats)
-    : compiler_backend_(compiler_backend),
+    : verified_methods_data_(verified_methods_data),
+      method_inliner_map_(method_inliner_map),
+      compiler_backend_(compiler_backend),
       instruction_set_(instruction_set),
       instruction_set_features_(instruction_set_features),
       freezing_constructor_lock_("freezing constructor lock"),
@@ -1253,7 +1258,7 @@
           // Did the verifier record a more precise invoke target based on its type information?
           const MethodReference caller_method(mUnit->GetDexFile(), mUnit->GetDexMethodIndex());
           const MethodReference* devirt_map_target =
-              verifier::MethodVerifier::GetDevirtMap(caller_method, dex_pc);
+              verified_methods_data_->GetDevirtMap(caller_method, dex_pc);
           if (devirt_map_target != NULL) {
             SirtRef<mirror::DexCache> target_dex_cache(soa.Self(), mUnit->GetClassLinker()->FindDexCache(*devirt_map_target->dex_file));
             SirtRef<mirror::ClassLoader> class_loader(soa.Self(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()));
@@ -1301,7 +1306,7 @@
 }
 
 bool CompilerDriver::IsSafeCast(const MethodReference& mr, uint32_t dex_pc) {
-  bool result = verifier::MethodVerifier::IsSafeCast(mr, dex_pc);
+  bool result = verified_methods_data_->IsSafeCast(mr, dex_pc);
   if (result) {
     stats_->SafeCast();
   } else {
@@ -2240,7 +2245,7 @@
   }
   ClassReference ref(&dex_file, class_def_index);
   // Skip compiling classes with generic verifier failures since they will still fail at runtime
-  if (verifier::MethodVerifier::IsClassRejected(ref)) {
+  if (manager->GetCompiler()->verified_methods_data_->IsClassRejected(ref)) {
     return;
   }
   const byte* class_data = dex_file.GetClassData(class_def);
@@ -2323,7 +2328,7 @@
   } else if ((access_flags & kAccAbstract) != 0) {
   } else {
     MethodReference method_ref(&dex_file, method_idx);
-    bool compile = verifier::MethodVerifier::IsCandidateForCompilation(method_ref, access_flags);
+    bool compile = VerifiedMethodsData::IsCandidateForCompilation(method_ref, access_flags);
 
     if (compile) {
       CompilerFn compiler = compiler_;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 7e81849..f4cc84d 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -41,8 +41,10 @@
 class AOTCompilationStats;
 class ParallelCompilationManager;
 class DexCompilationUnit;
+class DexFileToMethodInlinerMap;
 class OatWriter;
 class TimingLogger;
+class VerifiedMethodsData;
 
 enum CompilerBackend {
   kQuick,
@@ -90,7 +92,9 @@
   // enabled.  "image_classes" lets the compiler know what classes it
   // can assume will be in the image, with NULL implying all available
   // classes.
-  explicit CompilerDriver(CompilerBackend compiler_backend, InstructionSet instruction_set,
+  explicit CompilerDriver(VerifiedMethodsData* verified_methods_data,
+                          DexFileToMethodInlinerMap* method_inliner_map,
+                          CompilerBackend compiler_backend, InstructionSet instruction_set,
                           InstructionSetFeatures instruction_set_features,
                           bool image, DescriptorSet* image_classes,
                           size_t thread_count, bool dump_stats);
@@ -105,6 +109,14 @@
   void CompileOne(const mirror::ArtMethod* method, TimingLogger& timings)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  VerifiedMethodsData* GetVerifiedMethodsData() const {
+    return verified_methods_data_;
+  }
+
+  DexFileToMethodInlinerMap* GetMethodInlinerMap() const {
+    return method_inliner_map_;
+  }
+
   const InstructionSet& GetInstructionSet() const {
     return instruction_set_;
   }
@@ -390,6 +402,9 @@
   std::vector<const PatchInformation*> code_to_patch_;
   std::vector<const PatchInformation*> methods_to_patch_;
 
+  VerifiedMethodsData* verified_methods_data_;
+  DexFileToMethodInlinerMap* method_inliner_map_;
+
   CompilerBackend compiler_backend_;
 
   const InstructionSet instruction_set_;
diff --git a/compiler/llvm/compiler_llvm.cc b/compiler/llvm/compiler_llvm.cc
index d59afd4..35d1ecd 100644
--- a/compiler/llvm/compiler_llvm.cc
+++ b/compiler/llvm/compiler_llvm.cc
@@ -20,6 +20,7 @@
 #include "base/stl_util.h"
 #include "class_linker.h"
 #include "compiled_method.h"
+#include "dex/verified_methods_data.h"
 #include "driver/compiler_driver.h"
 #include "driver/dex_compilation_unit.h"
 #include "globals.h"
@@ -155,7 +156,8 @@
   MethodReference mref(dex_compilation_unit->GetDexFile(),
                        dex_compilation_unit->GetDexMethodIndex());
   return new CompiledMethod(*compiler_driver_, compiler_driver_->GetInstructionSet(),
-                            cunit->GetElfObject(), *verifier::MethodVerifier::GetDexGcMap(mref),
+                            cunit->GetElfObject(),
+                            *compiler_driver_->GetVerifiedMethodsData()->GetDexGcMap(mref),
                             cunit->GetDexCompilationUnit()->GetSymbol());
 }
 
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index fd0a69d..2434262 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -79,7 +79,12 @@
   InstructionSet insn_set = kIsTargetBuild ? kThumb2 : kX86;
 
   InstructionSetFeatures insn_features;
-  compiler_driver_.reset(new CompilerDriver(compiler_backend, insn_set,
+  verified_methods_data_.reset(new VerifiedMethodsData);
+  method_inliner_map_.reset(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr);
+  callbacks_.Reset(verified_methods_data_.get(), method_inliner_map_.get());
+  compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(),
+                                            method_inliner_map_.get(),
+                                            compiler_backend, insn_set,
                                             insn_features, false, NULL, 2, true));
   jobject class_loader = NULL;
   if (kCompile) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 8382469..199a2b8 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -23,6 +23,7 @@
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
 #include "dex_file-inl.h"
+#include "dex/verified_methods_data.h"
 #include "gc/space/space.h"
 #include "mirror/art_method-inl.h"
 #include "mirror/array.h"
@@ -217,7 +218,7 @@
       mirror::Class::Status status;
       if (compiled_class != NULL) {
         status = compiled_class->GetStatus();
-      } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) {
+      } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) {
         status = mirror::Class::kStatusError;
       } else {
         status = mirror::Class::kStatusNotReady;
@@ -432,7 +433,7 @@
       mirror::Class::Status status;
       if (compiled_class != NULL) {
         status = compiled_class->GetStatus();
-      } else if (verifier::MethodVerifier::IsClassRejected(class_ref)) {
+      } else if (compiler_driver_->GetVerifiedMethodsData()->IsClassRejected(class_ref)) {
         status = mirror::Class::kStatusError;
       } else {
         status = mirror::Class::kStatusNotReady;
diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc
index 3512911..6c779c8 100644
--- a/compiler/sea_ir/frontend.cc
+++ b/compiler/sea_ir/frontend.cc
@@ -59,7 +59,7 @@
   std::string llvm_code = llvm_data->GetElf(compiler.GetInstructionSet());
   CompiledMethod* compiled_method =
       new CompiledMethod(compiler, compiler.GetInstructionSet(), llvm_code,
-                         *verifier::MethodVerifier::GetDexGcMap(mref), symbol);
+                         *compiler.GetVerifiedMethodsData()->GetDexGcMap(mref), symbol);
   LOG(INFO) << "Compiled SEA IR method " << PrettyMethod(method_idx, dex_file) << ".";
   return compiled_method;
 }
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 28d6649..5662f36 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -30,7 +30,9 @@
 #include "base/timing_logger.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
+#include "compiler_callbacks.h"
 #include "dex_file-inl.h"
+#include "dex/verified_methods_data.h"
 #include "driver/compiler_driver.h"
 #include "elf_fixup.h"
 #include "elf_stripper.h"
@@ -51,9 +53,13 @@
 #include "scoped_thread_state_change.h"
 #include "sirt_ref.h"
 #include "vector_output_stream.h"
+#include "verifier/method_verifier.h"
+#include "verifier/method_verifier-inl.h"
 #include "well_known_classes.h"
 #include "zip_archive.h"
 
+#include "dex/quick/dex_file_to_method_inliner_map.h"
+
 namespace art {
 
 static void UsageErrorV(const char* fmt, va_list ap) {
@@ -162,12 +168,13 @@
                      InstructionSetFeatures instruction_set_features,
                      size_t thread_count)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
-    if (!CreateRuntime(options, instruction_set)) {
+    UniquePtr<Dex2Oat> dex2oat(new Dex2Oat(compiler_backend, instruction_set,
+                                           instruction_set_features, thread_count));
+    if (!dex2oat->CreateRuntime(options, instruction_set)) {
       *p_dex2oat = NULL;
       return false;
     }
-    *p_dex2oat = new Dex2Oat(Runtime::Current(), compiler_backend, instruction_set,
-        instruction_set_features, thread_count);
+    *p_dex2oat = dex2oat.release();
     return true;
   }
 
@@ -261,7 +268,9 @@
       Runtime::Current()->SetCompileTimeClassPath(class_loader, class_path_files);
     }
 
-    UniquePtr<CompilerDriver> driver(new CompilerDriver(compiler_backend_,
+    UniquePtr<CompilerDriver> driver(new CompilerDriver(verified_methods_data_.get(),
+                                                        method_inliner_map_.get(),
+                                                        compiler_backend_,
                                                         instruction_set_,
                                                         instruction_set_features_,
                                                         image,
@@ -337,21 +346,52 @@
   }
 
  private:
-  explicit Dex2Oat(Runtime* runtime,
-                   CompilerBackend compiler_backend,
+  class Dex2OatCompilerCallbacks : public CompilerCallbacks {
+    public:
+      Dex2OatCompilerCallbacks(VerifiedMethodsData* verified_methods_data,
+                               DexFileToMethodInlinerMap* method_inliner_map)
+          : verified_methods_data_(verified_methods_data),
+            method_inliner_map_(method_inliner_map) { }
+      virtual ~Dex2OatCompilerCallbacks() { }
+
+      virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+          SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+        bool result = verified_methods_data_->ProcessVerifiedMethod(verifier);
+        if (result && method_inliner_map_ != nullptr) {
+          MethodReference ref = verifier->GetMethodReference();
+          method_inliner_map_->GetMethodInliner(ref.dex_file)
+              ->AnalyseMethodCode(ref.dex_method_index, verifier->CodeItem());
+        }
+        return result;
+      }
+      virtual void ClassRejected(ClassReference ref) {
+        verified_methods_data_->AddRejectedClass(ref);
+      }
+
+    private:
+      VerifiedMethodsData* verified_methods_data_;
+      DexFileToMethodInlinerMap* method_inliner_map_;
+  };
+
+  explicit Dex2Oat(CompilerBackend compiler_backend,
                    InstructionSet instruction_set,
                    InstructionSetFeatures instruction_set_features,
                    size_t thread_count)
       : compiler_backend_(compiler_backend),
         instruction_set_(instruction_set),
         instruction_set_features_(instruction_set_features),
-        runtime_(runtime),
+        verified_methods_data_(new VerifiedMethodsData),
+        method_inliner_map_(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr),
+        callbacks_(verified_methods_data_.get(), method_inliner_map_.get()),
+        runtime_(nullptr),
         thread_count_(thread_count),
         start_ns_(NanoTime()) {
   }
 
-  static bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
+  bool CreateRuntime(Runtime::Options& options, InstructionSet instruction_set)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_) {
+    options.push_back(
+        std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
     if (!Runtime::Create(options, false)) {
       LOG(ERROR) << "Failed to create runtime";
       return false;
@@ -364,6 +404,7 @@
       }
     }
     runtime->GetClassLinker()->FixupDexCaches(runtime->GetResolutionMethod());
+    runtime_ = runtime;
     return true;
   }
 
@@ -405,6 +446,9 @@
   const InstructionSet instruction_set_;
   const InstructionSetFeatures instruction_set_features_;
 
+  UniquePtr<VerifiedMethodsData> verified_methods_data_;
+  UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
+  Dex2OatCompilerCallbacks callbacks_;
   Runtime* runtime_;
   size_t thread_count_;
   uint64_t start_ns_;
@@ -899,7 +943,6 @@
   }
 
   Runtime::Options options;
-  options.push_back(std::make_pair("compiler", reinterpret_cast<void*>(NULL)));
   std::vector<const DexFile*> boot_class_path;
   if (boot_image_option.empty()) {
     size_t failure_count = OpenDexFiles(dex_filenames, dex_locations, boot_class_path);
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index bdadc51..2aa6716 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -36,4 +36,6 @@
 GENERATE_ALLOC_ENTRYPOINTS _instrumented, Instrumented
 GENERATE_ALLOC_ENTRYPOINTS _bump_pointer, BumpPointer
 GENERATE_ALLOC_ENTRYPOINTS _bump_pointer_instrumented, BumpPointerInstrumented
+GENERATE_ALLOC_ENTRYPOINTS _tlab, TLAB
+GENERATE_ALLOC_ENTRYPOINTS _tlab_instrumented, TLABInstrumented
 .endm
diff --git a/runtime/arch/quick_alloc_entrypoints.cc b/runtime/arch/quick_alloc_entrypoints.cc
index 192b124..4cdb3f2 100644
--- a/runtime/arch/quick_alloc_entrypoints.cc
+++ b/runtime/arch/quick_alloc_entrypoints.cc
@@ -53,6 +53,7 @@
 // Generate the entrypoint functions.
 GENERATE_ENTRYPOINTS();
 GENERATE_ENTRYPOINTS(_bump_pointer);
+GENERATE_ENTRYPOINTS(_tlab);
 
 static bool entry_points_instrumented = false;
 static gc::AllocatorType entry_points_allocator = kMovingCollector ?
@@ -76,6 +77,10 @@
       SetQuickAllocEntryPoints_bump_pointer(qpoints, entry_points_instrumented);
       break;
     }
+    case gc::kAllocatorTypeTLAB: {
+      SetQuickAllocEntryPoints_tlab(qpoints, entry_points_instrumented);
+      break;
+    }
     default: {
       LOG(FATAL) << "Unimplemented";
     }
diff --git a/runtime/arch/x86/entrypoints_init_x86.cc b/runtime/arch/x86/entrypoints_init_x86.cc
index b1d031a..589c7d9 100644
--- a/runtime/arch/x86/entrypoints_init_x86.cc
+++ b/runtime/arch/x86/entrypoints_init_x86.cc
@@ -176,7 +176,7 @@
   // points->pCmplDouble = NULL;  // Not needed on x86.
   // points->pCmplFloat = NULL;  // Not needed on x86.
   qpoints->pFmod = art_quick_fmod;
-  // qpoints->pSqrt = NULL; // Not needed on x86.
+  // qpoints->pSqrt = NULL;  // Not needed on x86.
   qpoints->pL2d = art_quick_l2d;
   qpoints->pFmodf = art_quick_fmodf;
   qpoints->pL2f = art_quick_l2f;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 643c183..fbb47bd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -32,6 +32,7 @@
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker-inl.h"
+#include "compiler_callbacks.h"
 #include "debugger.h"
 #include "dex_file-inl.h"
 #include "gc/accounting/card_table-inl.h"
@@ -2490,7 +2491,9 @@
         self->GetException(nullptr)->SetCause(cause.get());
       }
       ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex());
-      verifier::MethodVerifier::AddRejectedClass(ref);
+      if (Runtime::Current()->IsCompiler()) {
+        Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+      }
       klass->SetStatus(mirror::Class::kStatusError, self);
       return;
     }
diff --git a/runtime/common_test.h b/runtime/common_test.h
index 57cf71a..a3cbde3 100644
--- a/runtime/common_test.h
+++ b/runtime/common_test.h
@@ -25,12 +25,15 @@
 #include <fstream>
 
 #include "../../external/icu4c/common/unicode/uvernum.h"
+#include "../compiler/dex/quick/dex_file_to_method_inliner_map.h"
+#include "../compiler/dex/verified_methods_data.h"
+#include "../compiler/driver/compiler_driver.h"
 #include "base/macros.h"
 #include "base/stl_util.h"
 #include "base/stringprintf.h"
 #include "base/unix_file/fd_file.h"
 #include "class_linker.h"
-#include "compiler/driver/compiler_driver.h"
+#include "compiler_callbacks.h"
 #include "dex_file-inl.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "gc/heap.h"
@@ -46,6 +49,8 @@
 #include "ScopedLocalRef.h"
 #include "thread.h"
 #include "UniquePtr.h"
+#include "verifier/method_verifier.h"
+#include "verifier/method_verifier-inl.h"
 #include "well_known_classes.h"
 
 namespace art {
@@ -158,7 +163,7 @@
 
 #include <signal.h>
 #include <asm/sigcontext.h>
-#include <asm/ucontext.h>
+#include <asm-generic/ucontext.h>
 
 
 // A signal handler called when have an illegal instruction.  We record the fact in
@@ -401,8 +406,18 @@
     std::string min_heap_string(StringPrintf("-Xms%zdm", gc::Heap::kDefaultInitialSize / MB));
     std::string max_heap_string(StringPrintf("-Xmx%zdm", gc::Heap::kDefaultMaximumSize / MB));
 
+    // TODO: make selectable
+#if defined(ART_USE_PORTABLE_COMPILER)
+    CompilerBackend compiler_backend = kPortable;
+#else
+    CompilerBackend compiler_backend = kQuick;
+#endif
+
+    verified_methods_data_.reset(new VerifiedMethodsData);
+    method_inliner_map_.reset(compiler_backend == kQuick ? new DexFileToMethodInlinerMap : nullptr);
+    callbacks_.Reset(verified_methods_data_.get(), method_inliner_map_.get());
     Runtime::Options options;
-    options.push_back(std::make_pair("compiler", reinterpret_cast<void*>(NULL)));
+    options.push_back(std::make_pair("compilercallbacks", static_cast<CompilerCallbacks*>(&callbacks_)));
     options.push_back(std::make_pair("bootclasspath", &boot_class_path_));
     options.push_back(std::make_pair("-Xcheck:jni", reinterpret_cast<void*>(NULL)));
     options.push_back(std::make_pair(min_heap_string.c_str(), reinterpret_cast<void*>(NULL)));
@@ -439,13 +454,6 @@
       instruction_set = kX86;
 #endif
 
-      // TODO: make selectable
-#if defined(ART_USE_PORTABLE_COMPILER)
-      CompilerBackend compiler_backend = kPortable;
-#else
-      CompilerBackend compiler_backend = kQuick;
-#endif
-
       for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
         Runtime::CalleeSaveType type = Runtime::CalleeSaveType(i);
         if (!runtime_->HasCalleeSaveMethod(type)) {
@@ -454,7 +462,9 @@
         }
       }
       class_linker_->FixupDexCaches(runtime_->GetResolutionMethod());
-      compiler_driver_.reset(new CompilerDriver(compiler_backend, instruction_set,
+      compiler_driver_.reset(new CompilerDriver(verified_methods_data_.get(),
+                                                method_inliner_map_.get(),
+                                                compiler_backend, instruction_set,
                                                 instruction_set_features,
                                                 true, new CompilerDriver::DescriptorSet,
                                                 2, true));
@@ -502,6 +512,9 @@
     (*icu_cleanup_fn)();
 
     compiler_driver_.reset();
+    callbacks_.Reset(nullptr, nullptr);
+    method_inliner_map_.reset();
+    verified_methods_data_.reset();
     STLDeleteElements(&opened_dex_files_);
 
     Runtime::Current()->GetHeap()->VerifyHeap();  // Check for heap corruption after the test
@@ -627,6 +640,36 @@
     image_reservation_.reset();
   }
 
+  class TestCompilerCallbacks : public CompilerCallbacks {
+   public:
+    TestCompilerCallbacks() : verified_methods_data_(nullptr), method_inliner_map_(nullptr) { }
+
+    void Reset(VerifiedMethodsData* verified_methods_data,
+               DexFileToMethodInlinerMap* method_inliner_map) {
+        verified_methods_data_ = verified_methods_data;
+        method_inliner_map_ = method_inliner_map;
+    }
+
+    virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+      CHECK(verified_methods_data_);
+      bool result = verified_methods_data_->ProcessVerifiedMethod(verifier);
+      if (result && method_inliner_map_ != nullptr) {
+        MethodReference ref = verifier->GetMethodReference();
+        method_inliner_map_->GetMethodInliner(ref.dex_file)
+            ->AnalyseMethodCode(ref.dex_method_index, verifier->CodeItem());
+      }
+      return result;
+    }
+    virtual void ClassRejected(ClassReference ref) {
+      verified_methods_data_->AddRejectedClass(ref);
+    }
+
+   private:
+    VerifiedMethodsData* verified_methods_data_;
+    DexFileToMethodInlinerMap* method_inliner_map_;
+  };
+
   std::string android_data_;
   std::string dalvik_cache_;
   const DexFile* java_lang_dex_file_;  // owned by runtime_
@@ -634,6 +677,9 @@
   UniquePtr<Runtime> runtime_;
   // Owned by the runtime
   ClassLinker* class_linker_;
+  UniquePtr<VerifiedMethodsData> verified_methods_data_;
+  UniquePtr<DexFileToMethodInlinerMap> method_inliner_map_;
+  TestCompilerCallbacks callbacks_;
   UniquePtr<CompilerDriver> compiler_driver_;
 
  private:
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
new file mode 100644
index 0000000..9045f3a
--- /dev/null
+++ b/runtime/compiler_callbacks.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013 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_RUNTIME_COMPILER_CALLBACKS_H_
+#define ART_RUNTIME_COMPILER_CALLBACKS_H_
+
+#include "class_reference.h"
+
+namespace art {
+
+namespace verifier {
+
+class MethodVerifier;
+
+}  // namespace verifier
+
+class CompilerCallbacks {
+  public:
+    virtual ~CompilerCallbacks() { }
+
+    virtual bool MethodVerified(verifier::MethodVerifier* verifier)
+        SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;
+    virtual void ClassRejected(ClassReference ref) = 0;
+
+  protected:
+    CompilerCallbacks() { }
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_COMPILER_CALLBACKS_H_
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 9155088..1ae39ab 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -81,5 +81,6 @@
 
 GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(, gc::kAllocatorTypeFreeList)
 GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(BumpPointer, gc::kAllocatorTypeBumpPointer)
+GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(TLAB, gc::kAllocatorTypeTLAB)
 
 }  // namespace art
diff --git a/runtime/gc/accounting/card_table-inl.h b/runtime/gc/accounting/card_table-inl.h
index 45ab214..7bd53df 100644
--- a/runtime/gc/accounting/card_table-inl.h
+++ b/runtime/gc/accounting/card_table-inl.h
@@ -29,14 +29,15 @@
 
 static inline bool byte_cas(byte old_value, byte new_value, byte* address) {
   // Little endian means most significant byte is on the left.
-  const size_t shift = reinterpret_cast<uintptr_t>(address) % sizeof(uintptr_t);
+  const size_t shift_in_bytes = reinterpret_cast<uintptr_t>(address) % sizeof(uintptr_t);
   // Align the address down.
-  address -= shift;
+  address -= shift_in_bytes;
+  const size_t shift_in_bits = shift_in_bytes * kBitsPerByte;
   int32_t* word_address = reinterpret_cast<int32_t*>(address);
   // Word with the byte we are trying to cas cleared.
-  const int32_t cur_word = *word_address & ~(0xFF << shift);
-  const int32_t old_word = cur_word | (static_cast<int32_t>(old_value) << shift);
-  const int32_t new_word = cur_word | (static_cast<int32_t>(new_value) << shift);
+  const int32_t cur_word = *word_address & ~(0xFF << shift_in_bits);
+  const int32_t old_word = cur_word | (static_cast<int32_t>(old_value) << shift_in_bits);
+  const int32_t new_word = cur_word | (static_cast<int32_t>(new_value) << shift_in_bits);
   bool success = android_atomic_cas(old_word, new_word, word_address) == 0;
   return success;
 }
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 469b098..8ae61a3 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -37,12 +37,16 @@
 size_t RosAlloc::threadLocalFreeBitMapOffsets[kNumOfSizeBrackets];
 bool RosAlloc::initialized_ = false;
 
-RosAlloc::RosAlloc(void* base, size_t capacity)
+RosAlloc::RosAlloc(void* base, size_t capacity,
+                   PageReleaseMode page_release_mode, size_t page_release_size_threshold)
     : base_(reinterpret_cast<byte*>(base)), footprint_(capacity),
       capacity_(capacity),
       lock_("rosalloc global lock", kRosAllocGlobalLock),
-      bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock) {
+      bulk_free_lock_("rosalloc bulk free lock", kRosAllocBulkFreeLock),
+      page_release_mode_(page_release_mode),
+      page_release_size_threshold_(page_release_size_threshold) {
   DCHECK(RoundUp(capacity, kPageSize) == capacity);
+  CHECK(IsAligned<kPageSize>(page_release_size_threshold_));
   if (!initialized_) {
     Initialize();
   }
@@ -65,7 +69,9 @@
   }
   free_pages->SetByteSize(this, capacity_);
   DCHECK_EQ(capacity_ % kPageSize, static_cast<size_t>(0));
+  DCHECK(free_pages->IsFree());
   free_pages->ReleasePages(this);
+  DCHECK(free_pages->IsFree());
   free_page_runs_.insert(free_pages);
   if (kTraceRosAlloc) {
     LOG(INFO) << "RosAlloc::RosAlloc() : Inserted run 0x" << std::hex
@@ -387,7 +393,9 @@
   // Insert it.
   DCHECK_EQ(fpr->ByteSize(this) % kPageSize, static_cast<size_t>(0));
   DCHECK(free_page_runs_.find(fpr) == free_page_runs_.end());
+  DCHECK(fpr->IsFree());
   fpr->ReleasePages(this);
+  DCHECK(fpr->IsFree());
   free_page_runs_.insert(fpr);
   DCHECK(free_page_runs_.find(fpr) != free_page_runs_.end());
   if (kTraceRosAlloc) {
@@ -404,20 +412,26 @@
     MutexLock mu(self, lock_);
     r = AllocPages(self, num_pages, kPageMapLargeObject);
   }
+  if (UNLIKELY(r == nullptr)) {
+    if (kTraceRosAlloc) {
+      LOG(INFO) << "RosAlloc::AllocLargeObject() : NULL";
+    }
+    return nullptr;
+  }
   if (bytes_allocated != NULL) {
     *bytes_allocated = num_pages * kPageSize;
   }
   if (kTraceRosAlloc) {
-    if (r != NULL) {
-      LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast<intptr_t>(r)
-                << "-0x" << (reinterpret_cast<intptr_t>(r) + num_pages * kPageSize)
-                << "(" << std::dec << (num_pages * kPageSize) << ")";
-    } else {
-      LOG(INFO) << "RosAlloc::AllocLargeObject() : NULL";
-    }
+    LOG(INFO) << "RosAlloc::AllocLargeObject() : 0x" << std::hex << reinterpret_cast<intptr_t>(r)
+              << "-0x" << (reinterpret_cast<intptr_t>(r) + num_pages * kPageSize)
+              << "(" << std::dec << (num_pages * kPageSize) << ")";
+  }
+  if (!DoesReleaseAllPages()) {
+    // If it does not release all pages, pages may not be zeroed out.
+    memset(r, 0, size);
   }
   // Check if the returned memory is really all zero.
-  if (kCheckZeroMemory && r != NULL) {
+  if (kCheckZeroMemory) {
     byte* bytes = reinterpret_cast<byte*>(r);
     for (size_t i = 0; i < size; ++i) {
       DCHECK_EQ(bytes[i], 0);
@@ -1366,7 +1380,12 @@
         size_t fpr_size = fpr->ByteSize(this);
         DCHECK(IsAligned<kPageSize>(fpr_size));
         void* start = fpr;
-        void* end = reinterpret_cast<byte*>(start) + fpr_size;
+        if (kIsDebugBuild) {
+          // In the debug build, the first page of a free page run
+          // contains a magic number for debugging. Exclude it.
+          start = reinterpret_cast<byte*>(fpr) + kPageSize;
+        }
+        void* end = reinterpret_cast<byte*>(fpr) + fpr_size;
         handler(start, end, 0, arg);
         size_t num_pages = fpr_size / kPageSize;
         if (kIsDebugBuild) {
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index d5b6de1..4eb13315 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -91,18 +91,50 @@
       byte* end = fpr_base + ByteSize(rosalloc);
       return end;
     }
+    bool IsLargerThanPageReleaseThreshold(RosAlloc* rosalloc)
+        EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+      return ByteSize(rosalloc) >= rosalloc->page_release_size_threshold_;
+    }
+    bool IsAtEndOfSpace(RosAlloc* rosalloc)
+        EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+      return reinterpret_cast<byte*>(this) + ByteSize(rosalloc) == rosalloc->base_ + rosalloc->footprint_;
+    }
+    bool ShouldReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+      switch (rosalloc->page_release_mode_) {
+        case kPageReleaseModeNone:
+          return false;
+        case kPageReleaseModeEnd:
+          return IsAtEndOfSpace(rosalloc);
+        case kPageReleaseModeSize:
+          return IsLargerThanPageReleaseThreshold(rosalloc);
+        case kPageReleaseModeSizeAndEnd:
+          return IsLargerThanPageReleaseThreshold(rosalloc) && IsAtEndOfSpace(rosalloc);
+        case kPageReleaseModeAll:
+          return true;
+        default:
+          LOG(FATAL) << "Unexpected page release mode ";
+          return false;
+      }
+    }
     void ReleasePages(RosAlloc* rosalloc) EXCLUSIVE_LOCKS_REQUIRED(rosalloc->lock_) {
+      byte* start = reinterpret_cast<byte*>(this);
       size_t byte_size = ByteSize(rosalloc);
       DCHECK_EQ(byte_size % kPageSize, static_cast<size_t>(0));
+      bool release_pages = ShouldReleasePages(rosalloc);
       if (kIsDebugBuild) {
         // Exclude the first page that stores the magic number.
         DCHECK_GE(byte_size, static_cast<size_t>(kPageSize));
+        start += kPageSize;
         byte_size -= kPageSize;
         if (byte_size > 0) {
-          madvise(reinterpret_cast<byte*>(this) + kPageSize, byte_size, MADV_DONTNEED);
+          if (release_pages) {
+            madvise(start, byte_size, MADV_DONTNEED);
+          }
         }
       } else {
-        madvise(this, byte_size, MADV_DONTNEED);
+        if (release_pages) {
+          madvise(start, byte_size, MADV_DONTNEED);
+        }
       }
     }
   };
@@ -363,6 +395,21 @@
     }
   };
 
+ public:
+  // Different page release modes.
+  enum PageReleaseMode {
+    kPageReleaseModeNone,         // Release no empty pages.
+    kPageReleaseModeEnd,          // Release empty pages at the end of the space.
+    kPageReleaseModeSize,         // Release empty pages that are larger than the threshold.
+    kPageReleaseModeSizeAndEnd,   // Release empty pages that are larger than the threshold or
+                                  // at the end of the space.
+    kPageReleaseModeAll,          // Release all empty pages.
+  };
+
+  // The default value for page_release_size_threshold_.
+  static constexpr size_t kDefaultPageReleaseSizeThreshold = 4 * MB;
+
+ private:
   // The base address of the memory region that's managed by this allocator.
   byte* base_;
 
@@ -412,6 +459,12 @@
   // allowing multiple individual frees at the same time.
   ReaderWriterMutex bulk_free_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
 
+  // The page release mode.
+  const PageReleaseMode page_release_mode_;
+  // Under kPageReleaseModeSize(AndEnd), if the free page run size is
+  // greater than or equal to this value, release pages.
+  const size_t page_release_size_threshold_;
+
   // The base address of the memory region that's managed by this allocator.
   byte* Begin() { return base_; }
   // The end address of the memory region that's managed by this allocator.
@@ -439,7 +492,9 @@
   void* AllocLargeObject(Thread* self, size_t size, size_t* bytes_allocated) LOCKS_EXCLUDED(lock_);
 
  public:
-  RosAlloc(void* base, size_t capacity);
+  RosAlloc(void* base, size_t capacity,
+           PageReleaseMode page_release_mode,
+           size_t page_release_size_threshold = kDefaultPageReleaseSizeThreshold);
   void* Alloc(Thread* self, size_t size, size_t* bytes_allocated)
       LOCKS_EXCLUDED(lock_);
   void Free(Thread* self, void* ptr)
@@ -480,6 +535,10 @@
   // allocated and objects allocated, respectively.
   static void BytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
   static void ObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+
+  bool DoesReleaseAllPages() const {
+    return page_release_mode_ == kPageReleaseModeAll;
+  }
 };
 
 }  // namespace allocator
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index cf301fe..6baee54 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -83,9 +83,9 @@
     uint64_t pause_start = NanoTime();
     ATRACE_BEGIN("Application threads suspended");
     thread_list->SuspendAll();
+    GetHeap()->RevokeAllThreadLocalBuffers();
     MarkingPhase();
     ReclaimPhase();
-    GetHeap()->RevokeAllThreadLocalBuffers();
     thread_list->ResumeAll();
     ATRACE_END();
     RegisterPause(NanoTime() - pause_start);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 923560e..f29eadb 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -14,22 +14,6 @@
  * limitations under the License.
  */
 
-/*
- * Copyright (C) 2011 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 "semi_space.h"
 
 #include <functional>
@@ -337,7 +321,7 @@
       if (forward_address == nullptr) {
         // Otherwise, we need to move the object and add it to the markstack for processing.
         size_t object_size = obj->SizeOf();
-        size_t dummy = 0;
+        size_t bytes_allocated = 0;
         if (kEnableSimplePromo && reinterpret_cast<byte*>(obj) < last_gc_to_space_end_) {
           // If it's allocated before the last GC (older), move (pseudo-promote) it to
           // the non-moving space (as sort of an old generation.)
@@ -346,7 +330,7 @@
           forward_address = non_moving_space->Alloc(self_, object_size, &bytes_promoted);
           if (forward_address == nullptr) {
             // If out of space, fall back to the to-space.
-            forward_address = to_space_->Alloc(self_, object_size, &dummy);
+            forward_address = to_space_->Alloc(self_, object_size, &bytes_allocated);
           } else {
             GetHeap()->num_bytes_allocated_.fetch_add(bytes_promoted);
             bytes_promoted_ += bytes_promoted;
@@ -364,7 +348,7 @@
           DCHECK(forward_address != nullptr);
         } else {
           // If it's allocated after the last GC (younger), copy it to the to-space.
-          forward_address = to_space_->Alloc(self_, object_size, &dummy);
+          forward_address = to_space_->Alloc(self_, object_size, &bytes_allocated);
         }
         // Copy over the object and add it to the mark stack since we still need to update it's
         // references.
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 99f084a..9fb5760 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -32,7 +32,7 @@
 namespace art {
 namespace gc {
 
-template <bool kInstrumented, typename PreFenceVisitor>
+template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
 inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self, mirror::Class* klass,
                                                       size_t byte_count, AllocatorType allocator,
                                                       const PreFenceVisitor& pre_fence_visitor) {
@@ -43,13 +43,13 @@
   self->AssertThreadSuspensionIsAllowable();
   // Need to check that we arent the large object allocator since the large object allocation code
   // path this function. If we didn't check we would have an infinite loop.
-  if (allocator != kAllocatorTypeLOS && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
+  if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
     return AllocLargeObject<kInstrumented, PreFenceVisitor>(self, klass, byte_count,
                                                             pre_fence_visitor);
   }
   mirror::Object* obj;
-  size_t bytes_allocated;
   AllocationTimer alloc_timer(this, &obj);
+  size_t bytes_allocated;
   obj = TryToAllocate<kInstrumented, false>(self, allocator, byte_count, &bytes_allocated);
   if (UNLIKELY(obj == nullptr)) {
     obj = AllocateInternalWithGc(self, allocator, byte_count, &bytes_allocated, &klass);
@@ -89,7 +89,11 @@
   } else {
     DCHECK(!Dbg::IsAllocTrackingEnabled());
   }
-  if (concurrent_gc_) {
+  // concurrent_gc_ isn't known at compile time so we can optimize by not checking it for
+  // the BumpPointer or TLAB allocators. This is nice since it allows the entire if statement to be
+  // optimized out. And for the other allocators, AllocatorMayHaveConcurrentGC is a constant since
+  // the allocator_type should be constant propagated.
+  if (AllocatorMayHaveConcurrentGC(allocator) && concurrent_gc_) {
     CheckConcurrentGC(self, new_num_bytes_allocated, obj);
   }
   if (kIsDebugBuild) {
@@ -105,15 +109,15 @@
 inline mirror::Object* Heap::AllocLargeObject(Thread* self, mirror::Class* klass,
                                               size_t byte_count,
                                               const PreFenceVisitor& pre_fence_visitor) {
-  return AllocObjectWithAllocator<kInstrumented, PreFenceVisitor>(self, klass, byte_count,
-                                                                  kAllocatorTypeLOS,
-                                                                  pre_fence_visitor);
+  return AllocObjectWithAllocator<kInstrumented, false, PreFenceVisitor>(self, klass, byte_count,
+                                                                         kAllocatorTypeLOS,
+                                                                         pre_fence_visitor);
 }
 
 template <const bool kInstrumented, const bool kGrow>
 inline mirror::Object* Heap::TryToAllocate(Thread* self, AllocatorType allocator_type,
                                            size_t alloc_size, size_t* bytes_allocated) {
-  if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(alloc_size))) {
+  if (UNLIKELY(IsOutOfMemoryOnAllocation<kGrow>(allocator_type, alloc_size))) {
     return nullptr;
   }
   if (kInstrumented) {
@@ -153,6 +157,21 @@
       DCHECK(ret == nullptr || large_object_space_->Contains(ret));
       break;
     }
+    case kAllocatorTypeTLAB: {
+      alloc_size = RoundUp(alloc_size, space::BumpPointerSpace::kAlignment);
+      if (UNLIKELY(self->TLABSize() < alloc_size)) {
+        // Try allocating a new thread local buffer, if the allocaiton fails the space must be
+        // full so return nullptr.
+        if (!bump_pointer_space_->AllocNewTLAB(self, alloc_size + kDefaultTLABSize)) {
+          return nullptr;
+        }
+      }
+      // The allocation can't fail.
+      ret = self->AllocTLAB(alloc_size);
+      DCHECK(ret != nullptr);
+      *bytes_allocated = alloc_size;
+      break;
+    }
     default: {
       LOG(FATAL) << "Invalid allocator type";
       ret = nullptr;
@@ -194,14 +213,14 @@
   return byte_count >= kLargeObjectThreshold && have_zygote_space_ && c->IsPrimitiveArray();
 }
 
-template <const bool kGrow>
-inline bool Heap::IsOutOfMemoryOnAllocation(size_t alloc_size) {
+template <bool kGrow>
+inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size) {
   size_t new_footprint = num_bytes_allocated_ + alloc_size;
   if (UNLIKELY(new_footprint > max_allowed_footprint_)) {
     if (UNLIKELY(new_footprint > growth_limit_)) {
       return true;
     }
-    if (!concurrent_gc_) {
+    if (!AllocatorMayHaveConcurrentGC(allocator_type) || !concurrent_gc_) {
       if (!kGrow) {
         return true;
       }
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 11acd33..61c66e7 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -77,7 +77,7 @@
            double target_utilization, size_t capacity, const std::string& image_file_name,
            CollectorType post_zygote_collector_type, size_t parallel_gc_threads,
            size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold,
-           size_t long_gc_log_threshold, bool ignore_max_footprint)
+           size_t long_gc_log_threshold, bool ignore_max_footprint, bool use_tlab)
     : non_moving_space_(nullptr),
       concurrent_gc_(false),
       collector_type_(kCollectorTypeNone),
@@ -103,11 +103,6 @@
       native_footprint_gc_watermark_(initial_size),
       native_footprint_limit_(2 * initial_size),
       native_need_to_run_finalization_(false),
-      activity_thread_class_(NULL),
-      application_thread_class_(NULL),
-      activity_thread_(NULL),
-      application_thread_(NULL),
-      last_process_state_id_(NULL),
       // Initially assume we perceive jank in case the process state is never updated.
       process_state_(kProcessStateJankPerceptible),
       concurrent_start_bytes_(std::numeric_limits<size_t>::max()),
@@ -148,7 +143,8 @@
       total_allocation_time_(0),
       verify_object_mode_(kHeapVerificationNotPermitted),
       gc_disable_count_(0),
-      running_on_valgrind_(RUNNING_ON_VALGRIND) {
+      running_on_valgrind_(RUNNING_ON_VALGRIND),
+      use_tlab_(use_tlab) {
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
     LOG(INFO) << "Heap() entering";
   }
@@ -184,7 +180,7 @@
                                                      requested_alloc_space_begin);
   } else {
     non_moving_space_ = space::RosAllocSpace::Create(name, initial_size, growth_limit, capacity,
-                                                     requested_alloc_space_begin);
+                                                     requested_alloc_space_begin, low_memory_mode_);
   }
   if (kMovingCollector) {
     // TODO: Place bump-pointer spaces somewhere to minimize size of card table.
@@ -337,36 +333,21 @@
 }
 
 void Heap::VisitObjects(ObjectVisitorCallback callback, void* arg) {
-  // Visit objects in bump pointer space.
   Thread* self = Thread::Current();
-  // TODO: Use reference block.
-  std::vector<SirtRef<mirror::Object>*> saved_refs;
+  // GCs can move objects, so don't allow this.
+  const char* old_cause = self->StartAssertNoThreadSuspension("Visiting objects");
   if (bump_pointer_space_ != nullptr) {
-    // Need to put all these in sirts since the callback may trigger a GC. TODO: Use a better data
-    // structure.
-    mirror::Object* obj = reinterpret_cast<mirror::Object*>(bump_pointer_space_->Begin());
-    const mirror::Object* end = reinterpret_cast<const mirror::Object*>(
-        bump_pointer_space_->End());
-    while (obj < end) {
-      saved_refs.push_back(new SirtRef<mirror::Object>(self, obj));
-      obj = space::BumpPointerSpace::GetNextObject(obj);
-    }
+    // Visit objects in bump pointer space.
+    bump_pointer_space_->Walk(callback, arg);
   }
   // TODO: Switch to standard begin and end to use ranged a based loop.
   for (mirror::Object** it = allocation_stack_->Begin(), **end = allocation_stack_->End();
       it < end; ++it) {
     mirror::Object* obj = *it;
-    // Objects in the allocation stack might be in a movable space.
-    saved_refs.push_back(new SirtRef<mirror::Object>(self, obj));
+    callback(obj, arg);
   }
   GetLiveBitmap()->Walk(callback, arg);
-  for (const auto& ref : saved_refs) {
-    callback(ref->get(), arg);
-  }
-  // Need to free the sirts in reverse order they were allocated.
-  for (size_t i = saved_refs.size(); i != 0; --i) {
-    delete saved_refs[i - 1];
-  }
+  self->EndAssertNoThreadSuspension(old_cause);
 }
 
 void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
@@ -471,8 +452,6 @@
     }
   }
   uint64_t allocation_time = static_cast<uint64_t>(total_allocation_time_) * kTimeAdjust;
-  size_t total_objects_allocated = GetObjectsAllocatedEver();
-  size_t total_bytes_allocated = GetBytesAllocatedEver();
   if (total_duration != 0) {
     const double total_seconds = static_cast<double>(total_duration / 1000) / 1000000.0;
     os << "Total time spent in GC: " << PrettyDuration(total_duration) << "\n";
@@ -481,7 +460,9 @@
     os << "Mean GC object throughput: "
        << (GetObjectsFreedEver() / total_seconds) << " objects/s\n";
   }
+  size_t total_objects_allocated = GetObjectsAllocatedEver();
   os << "Total number of allocations: " << total_objects_allocated << "\n";
+  size_t total_bytes_allocated = GetBytesAllocatedEver();
   os << "Total bytes allocated " << PrettySize(total_bytes_allocated) << "\n";
   if (kMeasureAllocationTime) {
     os << "Total time spent allocating: " << PrettyDuration(allocation_time) << "\n";
@@ -698,7 +679,7 @@
     }
   }
   total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated() -
-      bump_pointer_space_->GetBytesAllocated();
+      bump_pointer_space_->Size();
   const float managed_utilization = static_cast<float>(total_alloc_space_allocated) /
       static_cast<float>(total_alloc_space_size);
   uint64_t gc_heap_end_ns = NanoTime();
@@ -867,12 +848,10 @@
 void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) {
   DCHECK_LE(freed_bytes, static_cast<size_t>(num_bytes_allocated_));
   num_bytes_allocated_.fetch_sub(freed_bytes);
-
   if (Runtime::Current()->HasStatsEnabled()) {
     RuntimeStats* thread_stats = Thread::Current()->GetStats();
     thread_stats->freed_objects += freed_objects;
     thread_stats->freed_bytes += freed_bytes;
-
     // TODO: Do this concurrently.
     RuntimeStats* global_stats = Runtime::Current()->GetStats();
     global_stats->freed_objects += freed_objects;
@@ -945,19 +924,11 @@
 }
 
 size_t Heap::GetObjectsAllocatedEver() const {
-  size_t total = 0;
-  for (space::AllocSpace* space : alloc_spaces_) {
-    total += space->GetTotalObjectsAllocated();
-  }
-  return total;
+  return GetObjectsFreedEver() + GetObjectsAllocated();
 }
 
 size_t Heap::GetBytesAllocatedEver() const {
-  size_t total = 0;
-  for (space::AllocSpace* space : alloc_spaces_) {
-    total += space->GetTotalBytesAllocated();
-  }
-  return total;
+  return GetBytesFreedEver() + GetBytesAllocated();
 }
 
 class InstanceCounter {
@@ -1102,7 +1073,11 @@
       case kCollectorTypeSS: {
         concurrent_gc_ = false;
         gc_plan_.push_back(collector::kGcTypeFull);
-        ChangeAllocator(kAllocatorTypeBumpPointer);
+        if (use_tlab_) {
+          ChangeAllocator(kAllocatorTypeTLAB);
+        } else {
+          ChangeAllocator(kAllocatorTypeBumpPointer);
+        }
         break;
       }
       case kCollectorTypeMS: {
@@ -1134,6 +1109,10 @@
   }
 }
 
+static void MarkInBitmapCallback(mirror::Object* obj, void* arg) {
+  reinterpret_cast<accounting::SpaceBitmap*>(arg)->Set(obj);
+}
+
 void Heap::PreZygoteFork() {
   static Mutex zygote_creation_lock_("zygote creation lock", kZygoteCreationLock);
   Thread* self = Thread::Current();
@@ -1158,7 +1137,7 @@
     // Compact the bump pointer space to a new zygote bump pointer space.
     temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
     Compact(&target_space, bump_pointer_space_);
-    CHECK_EQ(temp_space_->GetBytesAllocated(), 0U);
+    CHECK(temp_space_->IsEmpty());
     total_objects_freed_ever_ += semi_space_collector_->GetFreedObjects();
     total_bytes_freed_ever_ += semi_space_collector_->GetFreedBytes();
     // Update the end and write out image.
@@ -1167,17 +1146,12 @@
     accounting::SpaceBitmap* bitmap = non_moving_space_->GetLiveBitmap();
     // Record the allocations in the bitmap.
     VLOG(heap) << "Recording zygote allocations";
-    mirror::Object* obj = reinterpret_cast<mirror::Object*>(target_space.Begin());
-    const mirror::Object* end = reinterpret_cast<const mirror::Object*>(target_space.End());
-    while (obj < end) {
-      bitmap->Set(obj);
-      obj = space::BumpPointerSpace::GetNextObject(obj);
-    }
+    target_space.Walk(MarkInBitmapCallback, bitmap);
   }
   // Turn the current alloc space into a zygote space and obtain the new alloc space composed of
   // the remaining available heap memory.
   space::MallocSpace* zygote_space = non_moving_space_;
-  non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space");
+  non_moving_space_ = zygote_space->CreateZygoteSpace("alloc space", low_memory_mode_);
   non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity());
   // Change the GC retention policy of the zygote space to only collect when full.
   zygote_space->SetGcRetentionPolicy(space::kGcRetentionPolicyFullCollect);
@@ -1305,9 +1279,11 @@
 
   collector::GarbageCollector* collector = nullptr;
   // TODO: Clean this up.
-  if (current_allocator_ == kAllocatorTypeBumpPointer) {
+  if (collector_type_ == kCollectorTypeSS) {
+    DCHECK(current_allocator_ == kAllocatorTypeBumpPointer ||
+           current_allocator_ == kAllocatorTypeTLAB);
     gc_type = semi_space_collector_->GetGcType();
-    CHECK_EQ(temp_space_->GetObjectsAllocated(), 0U);
+    CHECK(temp_space_->IsEmpty());
     semi_space_collector_->SetFromSpace(bump_pointer_space_);
     semi_space_collector_->SetToSpace(temp_space_);
     mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE);
@@ -2070,10 +2046,16 @@
 
 void Heap::RevokeThreadLocalBuffers(Thread* thread) {
   non_moving_space_->RevokeThreadLocalBuffers(thread);
+  if (bump_pointer_space_ != nullptr) {
+    bump_pointer_space_->RevokeThreadLocalBuffers(thread);
+  }
 }
 
 void Heap::RevokeAllThreadLocalBuffers() {
   non_moving_space_->RevokeAllThreadLocalBuffers();
+  if (bump_pointer_space_ != nullptr) {
+    bump_pointer_space_->RevokeAllThreadLocalBuffers();
+  }
 }
 
 bool Heap::IsGCRequestPending() const {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9788064..832d5ec 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -91,6 +91,7 @@
 // Different types of allocators.
 enum AllocatorType {
   kAllocatorTypeBumpPointer,
+  kAllocatorTypeTLAB,
   kAllocatorTypeFreeList,  // ROSAlloc / dlmalloc
   kAllocatorTypeLOS,  // Large object space.
 };
@@ -139,6 +140,7 @@
   static constexpr size_t kDefaultMinFree = kDefaultMaxFree / 4;
   static constexpr size_t kDefaultLongPauseLogThreshold = MsToNs(5);
   static constexpr size_t kDefaultLongGCLogThreshold = MsToNs(100);
+  static constexpr size_t kDefaultTLABSize = 256 * KB;
 
   // Default target utilization.
   static constexpr double kDefaultTargetUtilization = 0.5;
@@ -154,24 +156,25 @@
                 const std::string& original_image_file_name, CollectorType collector_type_,
                 size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode,
                 size_t long_pause_threshold, size_t long_gc_threshold,
-                bool ignore_max_footprint);
+                bool ignore_max_footprint, bool use_tlab);
 
   ~Heap();
 
   // Allocates and initializes storage for an object instance.
-  template <const bool kInstrumented>
+  template <bool kInstrumented>
   inline mirror::Object* AllocObject(Thread* self, mirror::Class* klass, size_t num_bytes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return AllocObjectWithAllocator<kInstrumented>(self, klass, num_bytes, GetCurrentAllocator());
+    return AllocObjectWithAllocator<kInstrumented, true>(self, klass, num_bytes,
+                                                         GetCurrentAllocator());
   }
-  template <const bool kInstrumented>
+  template <bool kInstrumented>
   inline mirror::Object* AllocNonMovableObject(Thread* self, mirror::Class* klass,
                                                size_t num_bytes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return AllocObjectWithAllocator<kInstrumented>(self, klass, num_bytes,
-                                                   GetCurrentNonMovingAllocator());
+    return AllocObjectWithAllocator<kInstrumented, true>(self, klass, num_bytes,
+                                                         GetCurrentNonMovingAllocator());
   }
-  template <bool kInstrumented, typename PreFenceVisitor = VoidFunctor>
+  template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor = VoidFunctor>
   ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(
       Thread* self, mirror::Class* klass, size_t byte_count, AllocatorType allocator,
       const PreFenceVisitor& pre_fence_visitor = VoidFunctor())
@@ -507,17 +510,19 @@
   void Compact(space::ContinuousMemMapAllocSpace* target_space,
                space::ContinuousMemMapAllocSpace* source_space);
 
-  static bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
-    return allocator_type != kAllocatorTypeBumpPointer;
+  static ALWAYS_INLINE bool AllocatorHasAllocationStack(AllocatorType allocator_type) {
+    return
+        allocator_type != kAllocatorTypeBumpPointer &&
+        allocator_type != kAllocatorTypeTLAB;
   }
-  static bool AllocatorHasConcurrentGC(AllocatorType allocator_type) {
-    return allocator_type != kAllocatorTypeBumpPointer;
+  static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) {
+    return AllocatorHasAllocationStack(allocator_type);
   }
   bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const;
   ALWAYS_INLINE void CheckConcurrentGC(Thread* self, size_t new_num_bytes_allocated,
                                        mirror::Object* obj);
 
-  // We don't force this to be inline since it is a slow path.
+  // We don't force this to be inlined since it is a slow path.
   template <bool kInstrumented, typename PreFenceVisitor>
   mirror::Object* AllocLargeObject(Thread* self, mirror::Class* klass, size_t byte_count,
                                    const PreFenceVisitor& pre_fence_visitor)
@@ -544,8 +549,9 @@
 
   void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  template <const bool kGrow>
-  bool IsOutOfMemoryOnAllocation(size_t alloc_size);
+
+  template <bool kGrow>
+  bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
 
   // Pushes a list of cleared references out to the managed heap.
   void SetReferenceReferent(mirror::Object* reference, mirror::Object* referent)
@@ -721,13 +727,6 @@
   // Whether or not we need to run finalizers in the next native allocation.
   bool native_need_to_run_finalization_;
 
-  // Activity manager members.
-  jclass activity_thread_class_;
-  jclass application_thread_class_;
-  jobject activity_thread_;
-  jobject application_thread_;
-  jfieldID last_process_state_id_;
-
   // Whether or not we currently care about pause times.
   ProcessState process_state_;
 
@@ -845,6 +844,7 @@
   collector::SemiSpace* semi_space_collector_;
 
   const bool running_on_valgrind_;
+  const bool use_tlab_;
 
   friend class collector::MarkSweep;
   friend class collector::SemiSpace;
diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h
index 85ef2f4..82e96a4 100644
--- a/runtime/gc/space/bump_pointer_space-inl.h
+++ b/runtime/gc/space/bump_pointer_space-inl.h
@@ -23,8 +23,8 @@
 namespace gc {
 namespace space {
 
-inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) {
-  num_bytes = RoundUp(num_bytes, kAlignment);
+inline mirror::Object* BumpPointerSpace::AllocNonvirtualWithoutAccounting(size_t num_bytes) {
+  DCHECK(IsAligned<kAlignment>(num_bytes));
   byte* old_end;
   byte* new_end;
   do {
@@ -38,13 +38,18 @@
   } while (android_atomic_cas(reinterpret_cast<int32_t>(old_end),
                               reinterpret_cast<int32_t>(new_end),
                               reinterpret_cast<volatile int32_t*>(&end_)) != 0);
-  // TODO: Less statistics?
-  total_bytes_allocated_.fetch_add(num_bytes);
-  num_objects_allocated_.fetch_add(1);
-  total_objects_allocated_.fetch_add(1);
   return reinterpret_cast<mirror::Object*>(old_end);
 }
 
+inline mirror::Object* BumpPointerSpace::AllocNonvirtual(size_t num_bytes) {
+  mirror::Object* ret = AllocNonvirtualWithoutAccounting(num_bytes);
+  if (ret != nullptr) {
+    objects_allocated_.fetch_add(1);
+    bytes_allocated_.fetch_add(num_bytes);
+  }
+  return ret;
+}
+
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index 06ba57e..7ea202c 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -18,6 +18,7 @@
 #include "bump_pointer_space-inl.h"
 #include "mirror/object-inl.h"
 #include "mirror/class-inl.h"
+#include "thread_list.h"
 
 namespace art {
 namespace gc {
@@ -40,18 +41,27 @@
 BumpPointerSpace::BumpPointerSpace(const std::string& name, byte* begin, byte* limit)
     : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit,
                                  kGcRetentionPolicyAlwaysCollect),
-      num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0),
-      growth_end_(limit) {
+      growth_end_(limit),
+      objects_allocated_(0), bytes_allocated_(0),
+      block_lock_("Block lock"),
+      num_blocks_(0) {
+  CHECK_GE(Capacity(), sizeof(BlockHeader));
+  end_ += sizeof(BlockHeader);
 }
 
 BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map)
     : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->Begin(), mem_map->End(),
                                  kGcRetentionPolicyAlwaysCollect),
-      num_objects_allocated_(0), total_bytes_allocated_(0), total_objects_allocated_(0),
-      growth_end_(mem_map->End()) {
+      growth_end_(mem_map->End()),
+      objects_allocated_(0), bytes_allocated_(0),
+      block_lock_("Block lock"),
+      num_blocks_(0) {
+  CHECK_GE(Capacity(), sizeof(BlockHeader));
+  end_ += sizeof(BlockHeader);
 }
 
 mirror::Object* BumpPointerSpace::Alloc(Thread*, size_t num_bytes, size_t* bytes_allocated) {
+  num_bytes = RoundUp(num_bytes, kAlignment);
   mirror::Object* ret = AllocNonvirtual(num_bytes);
   if (LIKELY(ret != nullptr)) {
     *bytes_allocated = num_bytes;
@@ -68,9 +78,14 @@
   CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed";
   // Reset the end of the space back to the beginning, we move the end forward as we allocate
   // objects.
-  SetEnd(Begin());
+  SetEnd(Begin() + sizeof(BlockHeader));
+  objects_allocated_ = 0;
+  bytes_allocated_ = 0;
   growth_end_ = Limit();
-  num_objects_allocated_ = 0;
+  {
+    MutexLock mu(Thread::Current(), block_lock_);
+    num_blocks_ = 0;
+  }
 }
 
 void BumpPointerSpace::Dump(std::ostream& os) const {
@@ -83,6 +98,131 @@
   return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
 }
 
+void BumpPointerSpace::RevokeThreadLocalBuffers(Thread* thread) {
+  MutexLock mu(Thread::Current(), block_lock_);
+  RevokeThreadLocalBuffersLocked(thread);
+}
+
+void BumpPointerSpace::RevokeAllThreadLocalBuffers() {
+  Thread* self = Thread::Current();
+  MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+  MutexLock mu2(self, *Locks::thread_list_lock_);
+  // TODO: Not do a copy of the thread list?
+  std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+  for (Thread* thread : thread_list) {
+    RevokeThreadLocalBuffers(thread);
+  }
+}
+
+void BumpPointerSpace::UpdateMainBlock() {
+  BlockHeader* header = reinterpret_cast<BlockHeader*>(Begin());
+  header->size_ = Size() - sizeof(BlockHeader);
+  DCHECK_EQ(num_blocks_, 0U);
+}
+
+// Returns the start of the storage.
+byte* BumpPointerSpace::AllocBlock(size_t bytes) {
+  bytes = RoundUp(bytes, kAlignment);
+  if (!num_blocks_) {
+    UpdateMainBlock();
+  }
+  byte* storage = reinterpret_cast<byte*>(
+      AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader)));
+  if (LIKELY(storage != nullptr)) {
+    BlockHeader* header = reinterpret_cast<BlockHeader*>(storage);
+    header->size_ = bytes;  // Write out the block header.
+    storage += sizeof(BlockHeader);
+    ++num_blocks_;
+  }
+  return storage;
+}
+
+void BumpPointerSpace::Walk(ObjectVisitorCallback callback, void* arg) {
+  byte* pos = Begin();
+
+  {
+    MutexLock mu(Thread::Current(), block_lock_);
+    // If we have 0 blocks then we need to update the main header since we have bump pointer style
+    // allocation into an unbounded region (actually bounded by Capacity()).
+    if (num_blocks_ == 0) {
+      UpdateMainBlock();
+    }
+  }
+
+  while (pos < End()) {
+    BlockHeader* header = reinterpret_cast<BlockHeader*>(pos);
+    size_t block_size = header->size_;
+    pos += sizeof(BlockHeader);  // Skip the header so that we know where the objects
+    mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
+    const mirror::Object* end = reinterpret_cast<const mirror::Object*>(pos + block_size);
+    CHECK_LE(reinterpret_cast<const byte*>(end), End());
+    // We don't know how many objects are allocated in the current block. When we hit a null class
+    // assume its the end. TODO: Have a thread update the header when it flushes the block?
+    while (obj < end && obj->GetClass() != nullptr) {
+      callback(obj, arg);
+      obj = GetNextObject(obj);
+    }
+    pos += block_size;
+  }
+}
+
+bool BumpPointerSpace::IsEmpty() const {
+  return Size() == sizeof(BlockHeader);
+}
+
+uint64_t BumpPointerSpace::GetBytesAllocated() {
+  // Start out pre-determined amount (blocks which are not being allocated into).
+  uint64_t total = static_cast<uint64_t>(bytes_allocated_.load());
+  Thread* self = Thread::Current();
+  MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+  MutexLock mu2(self, *Locks::thread_list_lock_);
+  std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+  MutexLock mu3(Thread::Current(), block_lock_);
+  // If we don't have any blocks, we don't have any thread local buffers. This check is required
+  // since there can exist multiple bump pointer spaces which exist at the same time.
+  if (num_blocks_ > 0) {
+    for (Thread* thread : thread_list) {
+      total += thread->thread_local_pos_ - thread->thread_local_start_;
+    }
+  }
+  return total;
+}
+
+uint64_t BumpPointerSpace::GetObjectsAllocated() {
+  // Start out pre-determined amount (blocks which are not being allocated into).
+  uint64_t total = static_cast<uint64_t>(objects_allocated_.load());
+  Thread* self = Thread::Current();
+  MutexLock mu(self, *Locks::runtime_shutdown_lock_);
+  MutexLock mu2(self, *Locks::thread_list_lock_);
+  std::list<Thread*> thread_list = Runtime::Current()->GetThreadList()->GetList();
+  MutexLock mu3(Thread::Current(), block_lock_);
+  // If we don't have any blocks, we don't have any thread local buffers. This check is required
+  // since there can exist multiple bump pointer spaces which exist at the same time.
+  if (num_blocks_ > 0) {
+    for (Thread* thread : thread_list) {
+      total += thread->thread_local_objects_;
+    }
+  }
+  return total;
+}
+
+void BumpPointerSpace::RevokeThreadLocalBuffersLocked(Thread* thread) {
+  objects_allocated_.fetch_add(thread->thread_local_objects_);
+  bytes_allocated_.fetch_add(thread->thread_local_pos_ - thread->thread_local_start_);
+  thread->SetTLAB(nullptr, nullptr);
+}
+
+bool BumpPointerSpace::AllocNewTLAB(Thread* self, size_t bytes) {
+  MutexLock mu(Thread::Current(), block_lock_);
+  RevokeThreadLocalBuffersLocked(self);
+  byte* start = AllocBlock(bytes);
+  if (start == nullptr) {
+    return false;
+  }
+  self->SetTLAB(start, start + bytes);
+  return true;
+}
+
 }  // namespace space
 }  // namespace gc
 }  // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index 2edd3e2..0a4be8a 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -17,6 +17,7 @@
 #ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_
 #define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_
 
+#include "root_visitor.h"
 #include "space.h"
 
 namespace art {
@@ -45,12 +46,13 @@
   // Allocate num_bytes, returns nullptr if the space is full.
   virtual mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated);
   mirror::Object* AllocNonvirtual(size_t num_bytes);
+  mirror::Object* AllocNonvirtualWithoutAccounting(size_t num_bytes);
 
   // Return the storage space required by obj.
   virtual size_t AllocationSize(const mirror::Object* obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  // Nos unless we support free lists.
+  // NOPS unless we support free lists.
   virtual size_t Free(Thread*, mirror::Object*) {
     return 0;
   }
@@ -92,21 +94,12 @@
 
   void Dump(std::ostream& os) const;
 
-  uint64_t GetBytesAllocated() {
-    return Size();
-  }
+  void RevokeThreadLocalBuffers(Thread* thread);
+  void RevokeAllThreadLocalBuffers();
 
-  uint64_t GetObjectsAllocated() {
-    return num_objects_allocated_;
-  }
-
-  uint64_t GetTotalBytesAllocated() {
-    return total_bytes_allocated_;
-  }
-
-  uint64_t GetTotalObjectsAllocated() {
-    return total_objects_allocated_;
-  }
+  uint64_t GetBytesAllocated() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  uint64_t GetObjectsAllocated() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool IsEmpty() const;
 
   bool Contains(const mirror::Object* obj) const {
     const byte* byte_obj = reinterpret_cast<const byte*>(obj);
@@ -120,28 +113,55 @@
   static mirror::Object* GetNextObject(mirror::Object* obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Allocate a new TLAB, returns false if the allocation failed.
+  bool AllocNewTLAB(Thread* self, size_t bytes);
+
   virtual BumpPointerSpace* AsBumpPointerSpace() {
     return this;
   }
 
+  // Go through all of the blocks and visit the continuous objects.
+  void Walk(ObjectVisitorCallback callback, void* arg)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Object alignment within the space.
   static constexpr size_t kAlignment = 8;
 
  protected:
   BumpPointerSpace(const std::string& name, MemMap* mem_map);
 
+  // Allocate a raw block of bytes.
+  byte* AllocBlock(size_t bytes) EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
+  void RevokeThreadLocalBuffersLocked(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
+
   size_t InternalAllocationSize(const mirror::Object* obj);
   mirror::Object* AllocWithoutGrowthLocked(size_t num_bytes, size_t* bytes_allocated)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
-  // Approximate number of bytes which have been allocated into the space.
-  AtomicInteger num_objects_allocated_;
-  AtomicInteger total_bytes_allocated_;
-  AtomicInteger total_objects_allocated_;
+  // The main block is an unbounded block where objects go when there are no other blocks. This
+  // enables us to maintain tightly packed objects when you are not using thread local buffers for
+  // allocation.
+  // The main block is also the block which starts at address 0.
+  void UpdateMainBlock() EXCLUSIVE_LOCKS_REQUIRED(block_lock_);
 
   byte* growth_end_;
+  AtomicInteger objects_allocated_;  // Accumulated from revoked thread local regions.
+  AtomicInteger bytes_allocated_;  // Accumulated from revoked thread local regions.
+  Mutex block_lock_;
+
+  // The number of blocks in the space, if it is 0 then the space has one long continuous block
+  // which doesn't have an updated header.
+  size_t num_blocks_ GUARDED_BY(block_lock_);
 
  private:
+  struct BlockHeader {
+    size_t size_;  // Size of the block in bytes, does not include the header.
+    size_t unused_;  // Ensures alignment of kAlignment.
+  };
+
+  COMPILE_ASSERT(sizeof(BlockHeader) % kAlignment == 0,
+                 continuous_block_must_be_kAlignment_aligned);
+
   friend class collector::MarkSweep;
   DISALLOW_COPY_AND_ASSIGN(BumpPointerSpace);
 };
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 10e9ed8..a4e6eda 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -38,7 +38,7 @@
 DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin,
                              byte* end, byte* limit, size_t growth_limit)
     : MallocSpace(name, mem_map, begin, end, limit, growth_limit),
-      total_bytes_freed_(0), total_objects_freed_(0), mspace_(mspace), mspace_for_alloc_(mspace) {
+      mspace_(mspace), mspace_for_alloc_(mspace) {
   CHECK(mspace != NULL);
 }
 
@@ -148,9 +148,7 @@
     CHECK(ptr != NULL);
     CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this;
   }
-  const size_t bytes_freed = InternalAllocationSize(ptr);
-  total_bytes_freed_ += bytes_freed;
-  ++total_objects_freed_;
+  const size_t bytes_freed = AllocationSizeNonvirtual(ptr);
   if (kRecentFreeCount > 0) {
     RegisterRecentFree(ptr);
   }
@@ -170,7 +168,7 @@
       // The head of chunk for the allocation is sizeof(size_t) behind the allocation.
       __builtin_prefetch(reinterpret_cast<char*>(ptrs[i + look_ahead]) - sizeof(size_t));
     }
-    bytes_freed += InternalAllocationSize(ptr);
+    bytes_freed += AllocationSizeNonvirtual(ptr);
   }
 
   if (kRecentFreeCount > 0) {
@@ -196,8 +194,6 @@
 
   {
     MutexLock mu(self, lock_);
-    total_bytes_freed_ += bytes_freed;
-    total_objects_freed_ += num_ptrs;
     mspace_bulk_free(mspace_, reinterpret_cast<void**>(ptrs), num_ptrs);
     return bytes_freed;
   }
@@ -211,13 +207,8 @@
   return heap->GetNonMovingSpace()->MoreCore(increment);
 }
 
-// Virtual functions can't get inlined.
-inline size_t DlMallocSpace::InternalAllocationSize(const mirror::Object* obj) {
-  return AllocationSizeNonvirtual(obj);
-}
-
 size_t DlMallocSpace::AllocationSize(const mirror::Object* obj) {
-  return InternalAllocationSize(obj);
+  return AllocationSizeNonvirtual(obj);
 }
 
 size_t DlMallocSpace::Trim() {
diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h
index d18d4ad..73e65d4 100644
--- a/runtime/gc/space/dlmalloc_space.h
+++ b/runtime/gc/space/dlmalloc_space.h
@@ -86,12 +86,6 @@
 
   uint64_t GetBytesAllocated();
   uint64_t GetObjectsAllocated();
-  uint64_t GetTotalBytesAllocated() {
-    return GetBytesAllocated() + total_bytes_freed_;
-  }
-  uint64_t GetTotalObjectsAllocated() {
-    return GetObjectsAllocated() + total_objects_freed_;
-  }
 
   // Returns the class of a recently freed object.
   mirror::Class* FindRecentFreedObject(const mirror::Object* obj);
@@ -112,20 +106,14 @@
                 byte* limit, size_t growth_limit);
 
  private:
-  size_t InternalAllocationSize(const mirror::Object* obj);
-
   mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated)
       EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
-  void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) {
+  void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, bool /*low_memory_mode*/) {
     return CreateMspace(base, morecore_start, initial_size);
   }
   static void* CreateMspace(void* base, size_t morecore_start, size_t initial_size);
 
-  // Approximate number of bytes and objects which have been deallocated in the space.
-  size_t total_bytes_freed_;
-  size_t total_objects_freed_;
-
   // The boundary tag overhead.
   static const size_t kChunkOverhead = kWordSize;
 
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index 785b5ed..46df0a1 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -176,7 +176,7 @@
   DCHECK(temp_bitmap_.get() == NULL);
 }
 
-MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name) {
+MallocSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name, bool low_memory_mode) {
   // For RosAlloc, revoke thread local runs before creating a new
   // alloc space so that we won't mix thread local runs from different
   // alloc spaces.
@@ -213,7 +213,7 @@
   UniquePtr<MemMap> mem_map(GetMemMap()->RemapAtEnd(end_, alloc_space_name,
                                                     PROT_READ | PROT_WRITE, &error_msg));
   CHECK(mem_map.get() != nullptr) << error_msg;
-  void* allocator = CreateAllocator(end_, starting_size, initial_size);
+  void* allocator = CreateAllocator(end_, starting_size, initial_size, low_memory_mode);
   // Protect memory beyond the initial size.
   byte* end = mem_map->Begin() + starting_size;
   if (capacity - initial_size > 0) {
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 0f882d3..d25f9cb 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -127,13 +127,14 @@
   virtual MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator,
                                       byte* begin, byte* end, byte* limit, size_t growth_limit) = 0;
 
-  // Turn ourself into a zygote space and return a new alloc space which has our unused memory.
-  MallocSpace* CreateZygoteSpace(const char* alloc_space_name);
+  // Turn ourself into a zygote space and return a new alloc space
+  // which has our unused memory.  When true, the low memory mode
+  // argument specifies that the heap wishes the created space to be
+  // more aggressive in releasing unused pages.
+  MallocSpace* CreateZygoteSpace(const char* alloc_space_name, bool low_memory_mode);
 
   virtual uint64_t GetBytesAllocated() = 0;
   virtual uint64_t GetObjectsAllocated() = 0;
-  virtual uint64_t GetTotalBytesAllocated() = 0;
-  virtual uint64_t GetTotalObjectsAllocated() = 0;
 
   // Returns the old mark bitmap.
   accounting::SpaceBitmap* BindLiveToMarkBitmap();
@@ -154,7 +155,11 @@
   static MemMap* CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size,
                               size_t* growth_limit, size_t* capacity, byte* requested_begin);
 
-  virtual void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) = 0;
+  // When true the low memory mode argument specifies that the heap
+  // wishes the created allocator to be more aggressive in releasing
+  // unused pages.
+  virtual void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size,
+                                bool low_memory_mode) = 0;
 
   void RegisterRecentFree(mirror::Object* ptr) EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc
index 1f8e324..80fdb6c 100644
--- a/runtime/gc/space/rosalloc_space.cc
+++ b/runtime/gc/space/rosalloc_space.cc
@@ -45,7 +45,7 @@
 }
 
 RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit,
-                                     size_t capacity, byte* requested_begin) {
+                                     size_t capacity, byte* requested_begin, bool low_memory_mode) {
   uint64_t start_time = 0;
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
     start_time = NanoTime();
@@ -68,7 +68,8 @@
                << PrettySize(capacity);
     return NULL;
   }
-  allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size);
+  allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size,
+                                                 low_memory_mode);
   if (rosalloc == NULL) {
     LOG(ERROR) << "Failed to initialize rosalloc for alloc space (" << name << ")";
     return NULL;
@@ -97,13 +98,18 @@
   return space;
 }
 
-allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size) {
+allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size,
+                                                   bool low_memory_mode) {
   // clear errno to allow PLOG on error
   errno = 0;
   // create rosalloc using our backing storage starting at begin and
   // with a footprint of morecore_start. When morecore_start bytes of
   // memory is exhaused morecore will be called.
-  allocator::RosAlloc* rosalloc = new art::gc::allocator::RosAlloc(begin, morecore_start);
+  allocator::RosAlloc* rosalloc = new art::gc::allocator::RosAlloc(
+      begin, morecore_start,
+      low_memory_mode ?
+          art::gc::allocator::RosAlloc::kPageReleaseModeAll :
+          art::gc::allocator::RosAlloc::kPageReleaseModeSizeAndEnd);
   if (rosalloc != NULL) {
     rosalloc->SetFootprintLimit(initial_size);
   } else {
@@ -146,9 +152,7 @@
     CHECK(ptr != NULL);
     CHECK(Contains(ptr)) << "Free (" << ptr << ") not in bounds of heap " << *this;
   }
-  const size_t bytes_freed = InternalAllocationSize(ptr);
-  total_bytes_freed_atomic_.fetch_add(bytes_freed);
-  ++total_objects_freed_atomic_;
+  const size_t bytes_freed = AllocationSizeNonvirtual(ptr);
   if (kRecentFreeCount > 0) {
     MutexLock mu(self, lock_);
     RegisterRecentFree(ptr);
@@ -168,7 +172,7 @@
     if (kPrefetchDuringRosAllocFreeList && i + look_ahead < num_ptrs) {
       __builtin_prefetch(reinterpret_cast<char*>(ptrs[i + look_ahead]));
     }
-    bytes_freed += InternalAllocationSize(ptr);
+    bytes_freed += AllocationSizeNonvirtual(ptr);
   }
 
   if (kRecentFreeCount > 0) {
@@ -193,8 +197,6 @@
   }
 
   rosalloc_->BulkFree(self, reinterpret_cast<void**>(ptrs), num_ptrs);
-  total_bytes_freed_atomic_.fetch_add(bytes_freed);
-  total_objects_freed_atomic_.fetch_add(num_ptrs);
   return bytes_freed;
 }
 
@@ -206,20 +208,23 @@
   return heap->GetNonMovingSpace()->MoreCore(increment);
 }
 
-// Virtual functions can't get inlined.
-inline size_t RosAllocSpace::InternalAllocationSize(const mirror::Object* obj) {
+size_t RosAllocSpace::AllocationSize(const mirror::Object* obj) {
   return AllocationSizeNonvirtual(obj);
 }
 
-size_t RosAllocSpace::AllocationSize(const mirror::Object* obj) {
-  return InternalAllocationSize(obj);
-}
-
 size_t RosAllocSpace::Trim() {
-  MutexLock mu(Thread::Current(), lock_);
-  // Trim to release memory at the end of the space.
-  rosalloc_->Trim();
-  // No inspect_all necessary here as trimming of pages is built-in.
+  {
+    MutexLock mu(Thread::Current(), lock_);
+    // Trim to release memory at the end of the space.
+    rosalloc_->Trim();
+  }
+  // Attempt to release pages if it does not release all empty pages.
+  if (!rosalloc_->DoesReleaseAllPages()) {
+    VLOG(heap) << "RosAllocSpace::Trim() ";
+    size_t reclaimed = 0;
+    InspectAllRosAlloc(DlmallocMadviseCallback, &reclaimed);
+    return reclaimed;
+  }
   return 0;
 }
 
diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h
index 6311580..b0c07fa 100644
--- a/runtime/gc/space/rosalloc_space.h
+++ b/runtime/gc/space/rosalloc_space.h
@@ -38,7 +38,7 @@
   // the caller should call Begin on the returned space to confirm the
   // request was granted.
   static RosAllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit,
-                               size_t capacity, byte* requested_begin);
+                               size_t capacity, byte* requested_begin, bool low_memory_mode);
 
   virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes,
                                           size_t* bytes_allocated) LOCKS_EXCLUDED(lock_);
@@ -83,12 +83,6 @@
 
   uint64_t GetBytesAllocated();
   uint64_t GetObjectsAllocated();
-  uint64_t GetTotalBytesAllocated() {
-    return GetBytesAllocated() + total_bytes_freed_atomic_;
-  }
-  uint64_t GetTotalObjectsAllocated() {
-    return GetObjectsAllocated() + total_objects_freed_atomic_;
-  }
 
   void RevokeThreadLocalBuffers(Thread* thread);
   void RevokeAllThreadLocalBuffers();
@@ -112,23 +106,18 @@
                 byte* begin, byte* end, byte* limit, size_t growth_limit);
 
  private:
-  size_t InternalAllocationSize(const mirror::Object* obj);
   mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated);
 
-  void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size) {
-    return CreateRosAlloc(base, morecore_start, initial_size);
+  void* CreateAllocator(void* base, size_t morecore_start, size_t initial_size, bool low_memory_mode) {
+    return CreateRosAlloc(base, morecore_start, initial_size, low_memory_mode);
   }
-  static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size);
-
+  static allocator::RosAlloc* CreateRosAlloc(void* base, size_t morecore_start, size_t initial_size,
+                                             bool low_memory_mode);
 
   void InspectAllRosAlloc(void (*callback)(void *start, void *end, size_t num_bytes, void* callback_arg),
                           void* arg)
       LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_, Locks::thread_list_lock_);
 
-  // Approximate number of bytes and objects which have been deallocated in the space.
-  AtomicInteger total_bytes_freed_atomic_;
-  AtomicInteger total_objects_freed_atomic_;
-
   // Underlying rosalloc.
   art::gc::allocator::RosAlloc* const rosalloc_;
 
diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h
index ca39175..db3aca9 100644
--- a/runtime/gc/space/space.h
+++ b/runtime/gc/space/space.h
@@ -198,10 +198,6 @@
   virtual uint64_t GetBytesAllocated() = 0;
   // Number of objects currently allocated.
   virtual uint64_t GetObjectsAllocated() = 0;
-  // Number of bytes allocated since the space was created.
-  virtual uint64_t GetTotalBytesAllocated() = 0;
-  // Number of objects allocated since the space was created.
-  virtual uint64_t GetTotalObjectsAllocated() = 0;
 
   // Allocate num_bytes without allowing growth. If the allocation
   // succeeds, the output parameter bytes_allocated will be set to the
diff --git a/runtime/gc/space/space_test.cc b/runtime/gc/space/space_test.cc
index 60c3b1c..b1be9d8 100644
--- a/runtime/gc/space/space_test.cc
+++ b/runtime/gc/space/space_test.cc
@@ -58,7 +58,8 @@
   }
   static MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit,
                                           size_t capacity, byte* requested_begin) {
-    return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin);
+    return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin,
+                                 Runtime::Current()->GetHeap()->IsLowMemoryMode());
   }
 
   typedef MallocSpace* (*CreateSpaceFn)(const std::string& name, size_t initial_size, size_t growth_limit,
@@ -178,7 +179,7 @@
 
   // Make sure that the zygote space isn't directly at the start of the space.
   space->Alloc(self, 1U * MB, &dummy);
-  space = space->CreateZygoteSpace("alloc space");
+  space = space->CreateZygoteSpace("alloc space", Runtime::Current()->GetHeap()->IsLowMemoryMode());
 
   // Make space findable to the heap, will also delete space when runtime is cleaned up
   AddSpace(space);
diff --git a/runtime/image.cc b/runtime/image.cc
index dd9b6d7..702cc9a 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -24,7 +24,7 @@
 namespace art {
 
 const byte ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const byte ImageHeader::kImageVersion[] = { '0', '0', '5', '\0' };
+const byte ImageHeader::kImageVersion[] = { '0', '0', '6', '\0' };
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/locks.h b/runtime/locks.h
index 2308e95..72d4f65 100644
--- a/runtime/locks.h
+++ b/runtime/locks.h
@@ -41,6 +41,8 @@
   kRosAllocBracketLock,
   kRosAllocBulkFreeLock,
   kAllocSpaceLock,
+  kDexFileMethodInlinerLock,
+  kDexFileToMethodInlinerMapLock,
   kMarkSweepMarkStackLock,
   kDefaultMutexLevel,
   kMarkSweepLargeObjectLock,
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index cf4b48c..bd81bd5 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -83,9 +83,10 @@
   }
   gc::Heap* heap = Runtime::Current()->GetHeap();
   SetLengthVisitor visitor(component_count);
+  DCHECK(allocator_type != gc::kAllocatorTypeLOS);
   return down_cast<Array*>(
-      heap->AllocObjectWithAllocator<kIsInstrumented>(self, array_class, size, allocator_type,
-                                                      visitor));
+      heap->AllocObjectWithAllocator<kIsInstrumented, true>(self, array_class, size,
+                                                            allocator_type, visitor));
 }
 
 template <bool kIsInstrumented>
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 3a28974..e0fab8c 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -361,8 +361,8 @@
 inline Object* Class::Alloc(Thread* self, gc::AllocatorType allocator_type) {
   CheckObjectAlloc();
   gc::Heap* heap = Runtime::Current()->GetHeap();
-  return heap->AllocObjectWithAllocator<kIsInstrumented>(self, this, this->object_size_,
-                                                         allocator_type);
+  return heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, this, this->object_size_,
+                                                                allocator_type);
 }
 
 inline Object* Class::AllocObject(Thread* self) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 2746e1e..bd965fa 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -139,9 +139,11 @@
 // slashes (so "java.lang.String" but "[Ljava.lang.String;"). Madness.
 String* Class::ComputeName() {
   String* name = GetName();
-  if (name != NULL) {
+  if (name != nullptr) {
     return name;
   }
+  Thread* self = Thread::Current();
+  SirtRef<mirror::Class> sirt_c(self, this);
   std::string descriptor(ClassHelper(this).GetDescriptor());
   if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
     // The descriptor indicates that this is the class for
@@ -160,7 +162,7 @@
     default:
       LOG(FATAL) << "Unknown primitive type: " << PrintableChar(descriptor[0]);
     }
-    name = String::AllocFromModifiedUtf8(Thread::Current(), c_name);
+    name = String::AllocFromModifiedUtf8(self, c_name);
   } else {
     // Convert the UTF-8 name to a java.lang.String. The name must use '.' to separate package
     // components.
@@ -169,9 +171,9 @@
       descriptor.erase(descriptor.size() - 1);
     }
     std::replace(descriptor.begin(), descriptor.end(), '/', '.');
-    name = String::AllocFromModifiedUtf8(Thread::Current(), descriptor.c_str());
+    name = String::AllocFromModifiedUtf8(self, descriptor.c_str());
   }
-  SetName(name);
+  sirt_c->SetName(name);
   return name;
 }
 
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 67c4505..6a04c3a 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -272,6 +272,7 @@
       allocSize += malloc_space->GetFootprint();
       allocUsed += malloc_space->GetBytesAllocated();
     } else if (space->IsBumpPointerSpace()) {
+      ScopedObjectAccess soa(env);
       gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace();
       allocSize += bump_pointer_space->Size();
       allocUsed += bump_pointer_space->GetBytesAllocated();
diff --git a/runtime/native/dalvik_system_Zygote.cc b/runtime/native/dalvik_system_Zygote.cc
index 2e3d6a6..7fa9457 100644
--- a/runtime/native/dalvik_system_Zygote.cc
+++ b/runtime/native/dalvik_system_Zygote.cc
@@ -351,13 +351,14 @@
 
     if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
       // Mount entire external storage tree for all users
-      if (mount(source, target, NULL, MS_BIND, NULL) == -1) {
+      if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) {
         PLOG(WARNING) << "Failed to mount " << source << " to " << target;
         return false;
       }
     } else {
       // Only mount user-specific external storage
-      if (mount(source_user.c_str(), target_user.c_str(), NULL, MS_BIND, NULL) == -1) {
+      if (TEMP_FAILURE_RETRY(
+              mount(source_user.c_str(), target_user.c_str(), NULL, MS_BIND, NULL)) == -1) {
         PLOG(WARNING) << "Failed to mount " << source_user << " to " << target_user;
         return false;
       }
@@ -368,7 +369,8 @@
     }
 
     // Finally, mount user-specific path into place for legacy users
-    if (mount(target_user.c_str(), legacy, NULL, MS_BIND | MS_REC, NULL) == -1) {
+    if (TEMP_FAILURE_RETRY(
+            mount(target_user.c_str(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) {
       PLOG(WARNING) << "Failed to mount " << target_user << " to " << legacy;
       return false;
     }
@@ -407,7 +409,8 @@
                                      jint debug_flags, jobjectArray javaRlimits,
                                      jlong permittedCapabilities, jlong effectiveCapabilities,
                                      jint mount_external,
-                                     jstring java_se_info, jstring java_se_name, bool is_system_server) {
+                                     jstring java_se_info, jstring java_se_name,
+                                     bool is_system_server) {
   Runtime* runtime = Runtime::Current();
   CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote";
   if (!runtime->PreZygoteFork()) {
@@ -527,14 +530,16 @@
 }
 
 static jint Zygote_nativeForkAndSpecialize(JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
-                                           jint debug_flags, jobjectArray rlimits, jint mount_external,
-                                           jstring se_info, jstring se_name) {
-  return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0, mount_external, se_info, se_name, false);
+                                           jint debug_flags, jobjectArray rlimits,
+                                           jint mount_external, jstring se_info, jstring se_name) {
+  return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags, rlimits, 0, 0, mount_external,
+                                 se_info, se_name, false);
 }
 
 static jint Zygote_nativeForkSystemServer(JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
                                           jint debug_flags, jobjectArray rlimits,
-                                          jlong permittedCapabilities, jlong effectiveCapabilities) {
+                                          jlong permittedCapabilities,
+                                          jlong effectiveCapabilities) {
   pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
                                       debug_flags, rlimits,
                                       permittedCapabilities, effectiveCapabilities,
diff --git a/runtime/root_visitor.h b/runtime/root_visitor.h
index d52f351..78c30ff 100644
--- a/runtime/root_visitor.h
+++ b/runtime/root_visitor.h
@@ -17,6 +17,9 @@
 #ifndef ART_RUNTIME_ROOT_VISITOR_H_
 #define ART_RUNTIME_ROOT_VISITOR_H_
 
+// For size_t.
+#include <stdlib.h>
+
 namespace art {
 namespace mirror {
 class Object;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e1b4d7e..25623a1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -70,7 +70,7 @@
 Runtime* Runtime::instance_ = NULL;
 
 Runtime::Runtime()
-    : is_compiler_(false),
+    : compiler_callbacks_(nullptr),
       is_zygote_(false),
       is_concurrent_gc_enabled_(true),
       is_explicit_gc_disabled_(false),
@@ -355,7 +355,7 @@
   parsed->heap_min_free_ = gc::Heap::kDefaultMinFree;
   parsed->heap_max_free_ = gc::Heap::kDefaultMaxFree;
   parsed->heap_target_utilization_ = gc::Heap::kDefaultTargetUtilization;
-  parsed->heap_growth_limit_ = 0;  // 0 means no growth limit.
+  parsed->heap_growth_limit_ = 0;  // 0 means no growth limit .
   // Default to number of processors minus one since the main GC thread also does work.
   parsed->parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
   // Only the main GC thread, no workers.
@@ -365,8 +365,9 @@
   parsed->stack_size_ = 0;  // 0 means default.
   parsed->max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation;
   parsed->low_memory_mode_ = false;
+  parsed->use_tlab_ = false;
 
-  parsed->is_compiler_ = false;
+  parsed->compiler_callbacks_ = nullptr;
   parsed->is_zygote_ = false;
   parsed->interpreter_only_ = false;
   parsed->is_explicit_gc_disabled_ = false;
@@ -540,12 +541,15 @@
       parsed->ignore_max_footprint_ = true;
     } else if (option == "-XX:LowMemoryMode") {
       parsed->low_memory_mode_ = true;
+    } else if (option == "-XX:UseTLAB") {
+      parsed->use_tlab_ = true;
     } else if (StartsWith(option, "-D")) {
       parsed->properties_.push_back(option.substr(strlen("-D")));
     } else if (StartsWith(option, "-Xjnitrace:")) {
       parsed->jni_trace_ = option.substr(strlen("-Xjnitrace:"));
-    } else if (option == "compiler") {
-      parsed->is_compiler_ = true;
+    } else if (option == "compilercallbacks") {
+      parsed->compiler_callbacks_ =
+          reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
     } else if (option == "-Xzygote") {
       parsed->is_zygote_ = true;
     } else if (option == "-Xint") {
@@ -669,7 +673,7 @@
     parsed->boot_class_path_string_.replace(core_jar_pos, core_jar.size(), "/core-libart.jar");
   }
 
-  if (!parsed->is_compiler_ && parsed->image_.empty()) {
+  if (parsed->compiler_callbacks_ == nullptr && parsed->image_.empty()) {
     parsed->image_ += GetAndroidRoot();
     parsed->image_ += "/framework/boot.art";
   }
@@ -882,7 +886,7 @@
   class_path_string_ = options->class_path_string_;
   properties_ = options->properties_;
 
-  is_compiler_ = options->is_compiler_;
+  compiler_callbacks_ = options->compiler_callbacks_;
   is_zygote_ = options->is_zygote_;
   is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
 
@@ -925,7 +929,8 @@
                        options->low_memory_mode_,
                        options->long_pause_log_threshold_,
                        options->long_gc_log_threshold_,
-                       options->ignore_max_footprint_);
+                       options->ignore_max_footprint_,
+                       options->use_tlab_);
 
   dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_;
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 01a605a..7b57dda 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -56,6 +56,7 @@
 class MethodVerifier;
 }
 class ClassLinker;
+class CompilerCallbacks;
 class DexFile;
 class InternTable;
 struct JavaVMExt;
@@ -100,10 +101,11 @@
     std::string image_;
     bool check_jni_;
     std::string jni_trace_;
-    bool is_compiler_;
+    CompilerCallbacks* compiler_callbacks_;
     bool is_zygote_;
     bool interpreter_only_;
     bool is_explicit_gc_disabled_;
+    bool use_tlab_;
     size_t long_pause_log_threshold_;
     size_t long_gc_log_threshold_;
     bool dump_gc_performance_on_shutdown_;
@@ -147,7 +149,11 @@
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
 
   bool IsCompiler() const {
-    return is_compiler_;
+    return compiler_callbacks_ != nullptr;
+  }
+
+  CompilerCallbacks* GetCompilerCallbacks() {
+    return compiler_callbacks_;
   }
 
   bool IsZygote() const {
@@ -468,7 +474,7 @@
   // A pointer to the active runtime or NULL.
   static Runtime* instance_;
 
-  bool is_compiler_;
+  CompilerCallbacks* compiler_callbacks_;
   bool is_zygote_;
   bool is_concurrent_gc_enabled_;
   bool is_explicit_gc_disabled_;
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index e47fd37..6f3c117 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -154,6 +154,18 @@
   }
 }
 
+inline size_t Thread::TLABSize() const {
+  return thread_local_end_ - thread_local_pos_;
+}
+
+inline mirror::Object* Thread::AllocTLAB(size_t bytes) {
+  DCHECK_GE(TLABSize(), bytes);
+  ++thread_local_objects_;
+  mirror::Object* ret = reinterpret_cast<mirror::Object*>(thread_local_pos_);
+  thread_local_pos_ += bytes;
+  return ret;
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_THREAD_INL_H_
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 2861213..bc252de 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -928,7 +928,11 @@
       no_thread_suspension_(0),
       last_no_thread_suspension_cause_(NULL),
       checkpoint_function_(0),
-      thread_exit_check_count_(0) {
+      thread_exit_check_count_(0),
+      thread_local_start_(nullptr),
+      thread_local_pos_(nullptr),
+      thread_local_end_(nullptr),
+      thread_local_objects_(0) {
   CHECK_EQ((sizeof(Thread) % 4), 0U) << sizeof(Thread);
   state_and_flags_.as_struct.flags = 0;
   state_and_flags_.as_struct.state = kNative;
@@ -2179,6 +2183,14 @@
   stack_end_ = stack_begin_;
 }
 
+void Thread::SetTLAB(byte* start, byte* end) {
+  DCHECK_LE(start, end);
+  thread_local_start_ = start;
+  thread_local_pos_  = thread_local_start_;
+  thread_local_end_ = end;
+  thread_local_objects_ = 0;
+}
+
 std::ostream& operator<<(std::ostream& os, const Thread& thread) {
   thread.ShortDump(os);
   return os;
diff --git a/runtime/thread.h b/runtime/thread.h
index 44b2186..b01ec94 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -577,10 +577,8 @@
   ~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_,
                            Locks::thread_suspend_count_lock_);
   void Destroy();
-  friend class ThreadList;  // For ~Thread and Destroy.
 
   void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
-  friend class Runtime;  // For CreatePeer.
 
   // Avoid use, callers should use SetState. Used only by SignalCatcher::HandleSigQuit, ~Thread and
   // Dbg::Disconnected.
@@ -589,8 +587,6 @@
     state_and_flags_.as_struct.state = new_state;
     return old_state;
   }
-  friend class SignalCatcher;  // For SetStateUnsafe.
-  friend class Dbg;  // F or SetStateUnsafe.
 
   void VerifyStackImpl() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -731,9 +727,6 @@
   // If we're blocked in MonitorEnter, this is the object we're trying to lock.
   mirror::Object* monitor_enter_object_;
 
-  friend class Monitor;
-  friend class MonitorInfo;
-
   // Top of linked list of stack indirect reference tables or NULL for none
   StackIndirectReferenceTable* top_sirt_;
 
@@ -799,13 +792,20 @@
   PortableEntryPoints portable_entrypoints_;
   QuickEntryPoints quick_entrypoints_;
 
- private:
   // How many times has our pthread key's destructor been called?
   uint32_t thread_exit_check_count_;
 
-  friend class ScopedThreadStateChange;
+  // Thread-local allocation pointer.
+  byte* thread_local_start_;
+  byte* thread_local_pos_;
+  byte* thread_local_end_;
+  size_t thread_local_objects_;
+  // Returns the remaining space in the TLAB.
+  size_t TLABSize() const;
+  // Doesn't check that there is room.
+  mirror::Object* AllocTLAB(size_t bytes);
+  void SetTLAB(byte* start, byte* end);
 
- public:
   // Thread-local rosalloc runs. There are 34 size brackets in rosalloc
   // runs (RosAlloc::kNumOfSizeBrackets). We can't refer to the
   // RosAlloc class due to a header file circular dependency issue.
@@ -814,6 +814,15 @@
   static const size_t kRosAllocNumOfSizeBrackets = 34;
   void* rosalloc_runs_[kRosAllocNumOfSizeBrackets];
 
+ private:
+  friend class Dbg;  // F or SetStateUnsafe.
+  friend class Monitor;
+  friend class MonitorInfo;
+  friend class Runtime;  // For CreatePeer.
+  friend class ScopedThreadStateChange;
+  friend class SignalCatcher;  // For SetStateUnsafe.
+  friend class ThreadList;  // For ~Thread and Destroy.
+
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };
 
diff --git a/runtime/verifier/dex_gc_map.h b/runtime/verifier/dex_gc_map.h
index 4570ae8..d77ea65 100644
--- a/runtime/verifier/dex_gc_map.h
+++ b/runtime/verifier/dex_gc_map.h
@@ -110,8 +110,6 @@
     return data_;
   }
 
-  friend class MethodVerifier;
-
   static const int kRegMapFormatShift = 5;
   static const uint8_t kRegMapFormatMask = 0x7;
 
diff --git a/runtime/verifier/method_verifier-inl.h b/runtime/verifier/method_verifier-inl.h
new file mode 100644
index 0000000..5cf234d
--- /dev/null
+++ b/runtime/verifier/method_verifier-inl.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 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_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_
+#define ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_
+
+#include "base/logging.h"
+#include "method_verifier.h"
+#include "mirror/class_loader.h"
+#include "mirror/dex_cache.h"
+
+namespace art {
+namespace verifier {
+
+const DexFile::CodeItem* MethodVerifier::CodeItem() const {
+  return code_item_;
+}
+
+RegisterLine* MethodVerifier::GetRegLine(uint32_t dex_pc) {
+  return reg_table_.GetLine(dex_pc);
+}
+
+const InstructionFlags& MethodVerifier::GetInstructionFlags(size_t index) const {
+  return insn_flags_[index];
+}
+
+mirror::ClassLoader* MethodVerifier::GetClassLoader() {
+  return class_loader_->get();
+}
+
+mirror::DexCache* MethodVerifier::GetDexCache() {
+  return dex_cache_->get();
+}
+
+MethodReference MethodVerifier::GetMethodReference() const {
+  return MethodReference(dex_file_, dex_method_idx_);
+}
+
+uint32_t MethodVerifier::GetAccessFlags() const {
+  return method_access_flags_;
+}
+
+bool MethodVerifier::HasCheckCasts() const {
+  return has_check_casts_;
+}
+
+bool MethodVerifier::HasVirtualOrInterfaceInvokes() const {
+  return has_virtual_or_interface_invokes_;
+}
+
+bool MethodVerifier::HasFailures() const {
+  return !failure_messages_.empty();
+}
+
+const RegType& MethodVerifier::ResolveCheckedClass(uint32_t class_idx) {
+  DCHECK(!HasFailures());
+  const RegType& result = ResolveClassAndCheckAccess(class_idx);
+  DCHECK(!HasFailures());
+  return result;
+}
+
+}  // namespace verifier
+}  // namespace art
+
+#endif  // ART_RUNTIME_VERIFIER_METHOD_VERIFIER_INL_H_
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 9183b5f..f03cdcd 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -22,6 +22,7 @@
 #include "base/mutex-inl.h"
 #include "base/stringpiece.h"
 #include "class_linker.h"
+#include "compiler_callbacks.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 #include "dex_instruction_visitor.h"
@@ -90,28 +91,28 @@
   if (klass->IsVerified()) {
     return kNoFailure;
   }
-  mirror::Class* super = klass->GetSuperClass();
-  if (super == NULL && strcmp("Ljava/lang/Object;", ClassHelper(klass).GetDescriptor()) != 0) {
-    *error = "Verifier rejected class ";
-    *error += PrettyDescriptor(klass);
-    *error += " that has no super class";
-    return kHardFailure;
-  }
-  if (super != NULL && super->IsFinal()) {
-    *error = "Verifier rejected class ";
-    *error += PrettyDescriptor(klass);
-    *error += " that attempts to sub-class final class ";
-    *error += PrettyDescriptor(super);
-    return kHardFailure;
-  }
+  bool early_failure = false;
+  std::string failure_message;
   ClassHelper kh(klass);
   const DexFile& dex_file = kh.GetDexFile();
   const DexFile::ClassDef* class_def = kh.GetClassDef();
-  if (class_def == NULL) {
-    *error = "Verifier rejected class ";
-    *error += PrettyDescriptor(klass);
-    *error += " that isn't present in dex file ";
-    *error += dex_file.GetLocation();
+  mirror::Class* super = klass->GetSuperClass();
+  if (super == NULL && strcmp("Ljava/lang/Object;", kh.GetDescriptor()) != 0) {
+    early_failure = true;
+    failure_message = " that has no super class";
+  } else if (super != NULL && super->IsFinal()) {
+    early_failure = true;
+    failure_message = " that attempts to sub-class final class " + PrettyDescriptor(super);
+  } else if (class_def == NULL) {
+    early_failure = true;
+    failure_message = " that isn't present in dex file " + dex_file.GetLocation();
+  }
+  if (early_failure) {
+    *error = "Verifier rejected class " + PrettyDescriptor(klass) + failure_message;
+    if (Runtime::Current()->IsCompiler()) {
+      ClassReference ref(&dex_file, klass->GetDexClassDefIndex());
+      Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+    }
     return kHardFailure;
   }
   Thread* self = Thread::Current();
@@ -441,7 +442,12 @@
   // Perform static instruction verification.
   result = result && VerifyInstructions();
   // Perform code-flow analysis and return.
-  return result && VerifyCodeFlow();
+  result = result && VerifyCodeFlow();
+  // Compute information for compiler.
+  if (result && Runtime::Current()->IsCompiler()) {
+    result = Runtime::Current()->GetCompilerCallbacks()->MethodVerified(this);
+  }
+  return result;
 }
 
 std::ostream& MethodVerifier::Fail(VerifyError error) {
@@ -480,7 +486,7 @@
     case VERIFY_ERROR_BAD_CLASS_HARD: {
       if (Runtime::Current()->IsCompiler()) {
         ClassReference ref(dex_file_, dex_file_->GetIndexForClassDef(*class_def_));
-        AddRejectedClass(ref);
+        Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
       }
       have_pending_hard_failure_ = true;
       break;
@@ -1063,38 +1069,6 @@
     DCHECK_NE(failures_.size(), 0U);
     return false;
   }
-
-  // Compute information for compiler.
-  if (Runtime::Current()->IsCompiler()) {
-    MethodReference ref(dex_file_, dex_method_idx_);
-    bool compile = IsCandidateForCompilation(ref, method_access_flags_);
-    if (compile) {
-      /* Generate a register map and add it to the method. */
-      const std::vector<uint8_t>* dex_gc_map = GenerateGcMap();
-      if (dex_gc_map == NULL) {
-        DCHECK_NE(failures_.size(), 0U);
-        return false;  // Not a real failure, but a failure to encode
-      }
-      if (kIsDebugBuild) {
-        VerifyGcMap(*dex_gc_map);
-      }
-      verifier::MethodVerifier::SetDexGcMap(ref, dex_gc_map);
-    }
-
-    if (has_check_casts_) {
-      MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet();
-      if (method_to_safe_casts != NULL) {
-        SetSafeCastMap(ref, method_to_safe_casts);
-      }
-    }
-
-    if (has_virtual_or_interface_invokes_) {
-      MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap();
-      if (pc_to_concrete_method != NULL) {
-        SetDevirtMap(ref, pc_to_concrete_method);
-      }
-    }
-  }
   return true;
 }
 
@@ -3910,325 +3884,6 @@
   return *declaring_class_;
 }
 
-void MethodVerifier::ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits,
-                                       size_t* log2_max_gc_pc) {
-  size_t local_gc_points = 0;
-  size_t max_insn = 0;
-  size_t max_ref_reg = -1;
-  for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
-    if (insn_flags_[i].IsCompileTimeInfoPoint()) {
-      local_gc_points++;
-      max_insn = i;
-      RegisterLine* line = reg_table_.GetLine(i);
-      max_ref_reg = line->GetMaxNonZeroReferenceReg(max_ref_reg);
-    }
-  }
-  *gc_points = local_gc_points;
-  *ref_bitmap_bits = max_ref_reg + 1;  // if max register is 0 we need 1 bit to encode (ie +1)
-  size_t i = 0;
-  while ((1U << i) <= max_insn) {
-    i++;
-  }
-  *log2_max_gc_pc = i;
-}
-
-MethodVerifier::MethodSafeCastSet* MethodVerifier::GenerateSafeCastSet() {
-  /*
-   * Walks over the method code and adds any cast instructions in which
-   * the type cast is implicit to a set, which is used in the code generation
-   * to elide these casts.
-   */
-  if (!failure_messages_.empty()) {
-    return NULL;
-  }
-  UniquePtr<MethodSafeCastSet> mscs;
-  const Instruction* inst = Instruction::At(code_item_->insns_);
-  const Instruction* end = Instruction::At(code_item_->insns_ +
-                                           code_item_->insns_size_in_code_units_);
-
-  for (; inst < end; inst = inst->Next()) {
-    Instruction::Code code = inst->Opcode();
-    if ((code == Instruction::CHECK_CAST) || (code == Instruction::APUT_OBJECT)) {
-      uint32_t dex_pc = inst->GetDexPc(code_item_->insns_);
-      RegisterLine* line = reg_table_.GetLine(dex_pc);
-      bool is_safe_cast = false;
-      if (code == Instruction::CHECK_CAST) {
-        const RegType& reg_type(line->GetRegisterType(inst->VRegA_21c()));
-        const RegType& cast_type = ResolveClassAndCheckAccess(inst->VRegB_21c());
-        is_safe_cast = cast_type.IsStrictlyAssignableFrom(reg_type);
-      } else {
-        const RegType& array_type(line->GetRegisterType(inst->VRegB_23x()));
-        // We only know its safe to assign to an array if the array type is precise. For example,
-        // an Object[] can have any type of object stored in it, but it may also be assigned a
-        // String[] in which case the stores need to be of Strings.
-        if (array_type.IsPreciseReference()) {
-          const RegType& value_type(line->GetRegisterType(inst->VRegA_23x()));
-          const RegType& component_type(reg_types_.GetComponentType(array_type,
-                                                                    class_loader_->get()));
-          is_safe_cast = component_type.IsStrictlyAssignableFrom(value_type);
-        }
-      }
-      if (is_safe_cast) {
-        if (mscs.get() == NULL) {
-          mscs.reset(new MethodSafeCastSet());
-        }
-        mscs->insert(dex_pc);
-      }
-    }
-  }
-  return mscs.release();
-}
-
-MethodVerifier::PcToConcreteMethodMap* MethodVerifier::GenerateDevirtMap() {
-  // It is risky to rely on reg_types for sharpening in cases of soft
-  // verification, we might end up sharpening to a wrong implementation. Just abort.
-  if (!failure_messages_.empty()) {
-    return NULL;
-  }
-
-  UniquePtr<PcToConcreteMethodMap> pc_to_concrete_method_map;
-  const uint16_t* insns = code_item_->insns_;
-  const Instruction* inst = Instruction::At(insns);
-  const Instruction* end = Instruction::At(insns + code_item_->insns_size_in_code_units_);
-
-  for (; inst < end; inst = inst->Next()) {
-    bool is_virtual   = (inst->Opcode() == Instruction::INVOKE_VIRTUAL) ||
-        (inst->Opcode() ==  Instruction::INVOKE_VIRTUAL_RANGE);
-    bool is_interface = (inst->Opcode() == Instruction::INVOKE_INTERFACE) ||
-        (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE);
-
-    if (!is_interface && !is_virtual) {
-      continue;
-    }
-    // Get reg type for register holding the reference to the object that will be dispatched upon.
-    uint32_t dex_pc = inst->GetDexPc(insns);
-    RegisterLine* line = reg_table_.GetLine(dex_pc);
-    bool is_range = (inst->Opcode() ==  Instruction::INVOKE_VIRTUAL_RANGE) ||
-        (inst->Opcode() ==  Instruction::INVOKE_INTERFACE_RANGE);
-    const RegType&
-        reg_type(line->GetRegisterType(is_range ? inst->VRegC_3rc() : inst->VRegC_35c()));
-
-    if (!reg_type.HasClass()) {
-      // We will compute devirtualization information only when we know the Class of the reg type.
-      continue;
-    }
-    mirror::Class* reg_class = reg_type.GetClass();
-    if (reg_class->IsInterface()) {
-      // We can't devirtualize when the known type of the register is an interface.
-      continue;
-    }
-    if (reg_class->IsAbstract() && !reg_class->IsArrayClass()) {
-      // We can't devirtualize abstract classes except on arrays of abstract classes.
-      continue;
-    }
-    mirror::ArtMethod* abstract_method = (*dex_cache_)->GetResolvedMethod(
-        is_range ? inst->VRegB_3rc() : inst->VRegB_35c());
-    if (abstract_method == NULL) {
-      // If the method is not found in the cache this means that it was never found
-      // by ResolveMethodAndCheckAccess() called when verifying invoke_*.
-      continue;
-    }
-    // Find the concrete method.
-    mirror::ArtMethod* concrete_method = NULL;
-    if (is_interface) {
-      concrete_method = reg_type.GetClass()->FindVirtualMethodForInterface(abstract_method);
-    }
-    if (is_virtual) {
-      concrete_method = reg_type.GetClass()->FindVirtualMethodForVirtual(abstract_method);
-    }
-    if (concrete_method == NULL || concrete_method->IsAbstract()) {
-      // In cases where concrete_method is not found, or is abstract, continue to the next invoke.
-      continue;
-    }
-    if (reg_type.IsPreciseReference() || concrete_method->IsFinal() ||
-        concrete_method->GetDeclaringClass()->IsFinal()) {
-      // If we knew exactly the class being dispatched upon, or if the target method cannot be
-      // overridden record the target to be used in the compiler driver.
-      if (pc_to_concrete_method_map.get() == NULL) {
-        pc_to_concrete_method_map.reset(new PcToConcreteMethodMap());
-      }
-      MethodReference concrete_ref(
-          concrete_method->GetDeclaringClass()->GetDexCache()->GetDexFile(),
-          concrete_method->GetDexMethodIndex());
-      pc_to_concrete_method_map->Put(dex_pc, concrete_ref);
-    }
-  }
-  return pc_to_concrete_method_map.release();
-}
-
-const std::vector<uint8_t>* MethodVerifier::GenerateGcMap() {
-  size_t num_entries, ref_bitmap_bits, pc_bits;
-  ComputeGcMapSizes(&num_entries, &ref_bitmap_bits, &pc_bits);
-  // There's a single byte to encode the size of each bitmap
-  if (ref_bitmap_bits >= (8 /* bits per byte */ * 8192 /* 13-bit size */ )) {
-    // TODO: either a better GC map format or per method failures
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
-       << ref_bitmap_bits << " registers";
-    return NULL;
-  }
-  size_t ref_bitmap_bytes = (ref_bitmap_bits + 7) / 8;
-  // There are 2 bytes to encode the number of entries
-  if (num_entries >= 65536) {
-    // TODO: either a better GC map format or per method failures
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
-       << num_entries << " entries";
-    return NULL;
-  }
-  size_t pc_bytes;
-  RegisterMapFormat format;
-  if (pc_bits <= 8) {
-    format = kRegMapFormatCompact8;
-    pc_bytes = 1;
-  } else if (pc_bits <= 16) {
-    format = kRegMapFormatCompact16;
-    pc_bytes = 2;
-  } else {
-    // TODO: either a better GC map format or per method failures
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot encode GC map for method with "
-       << (1 << pc_bits) << " instructions (number is rounded up to nearest power of 2)";
-    return NULL;
-  }
-  size_t table_size = ((pc_bytes + ref_bitmap_bytes) * num_entries) + 4;
-  std::vector<uint8_t>* table = new std::vector<uint8_t>;
-  if (table == NULL) {
-    Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Failed to encode GC map (size=" << table_size << ")";
-    return NULL;
-  }
-  table->reserve(table_size);
-  // Write table header
-  table->push_back(format | ((ref_bitmap_bytes >> DexPcToReferenceMap::kRegMapFormatShift) &
-                             ~DexPcToReferenceMap::kRegMapFormatMask));
-  table->push_back(ref_bitmap_bytes & 0xFF);
-  table->push_back(num_entries & 0xFF);
-  table->push_back((num_entries >> 8) & 0xFF);
-  // Write table data
-  for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
-    if (insn_flags_[i].IsCompileTimeInfoPoint()) {
-      table->push_back(i & 0xFF);
-      if (pc_bytes == 2) {
-        table->push_back((i >> 8) & 0xFF);
-      }
-      RegisterLine* line = reg_table_.GetLine(i);
-      line->WriteReferenceBitMap(*table, ref_bitmap_bytes);
-    }
-  }
-  DCHECK_EQ(table->size(), table_size);
-  return table;
-}
-
-void MethodVerifier::VerifyGcMap(const std::vector<uint8_t>& data) {
-  // Check that for every GC point there is a map entry, there aren't entries for non-GC points,
-  // that the table data is well formed and all references are marked (or not) in the bitmap
-  DexPcToReferenceMap map(&data[0]);
-  DCHECK_EQ(data.size(), map.RawSize());
-  size_t map_index = 0;
-  for (size_t i = 0; i < code_item_->insns_size_in_code_units_; i++) {
-    const uint8_t* reg_bitmap = map.FindBitMap(i, false);
-    if (insn_flags_[i].IsCompileTimeInfoPoint()) {
-      CHECK_LT(map_index, map.NumEntries());
-      CHECK_EQ(map.GetDexPc(map_index), i);
-      CHECK_EQ(map.GetBitMap(map_index), reg_bitmap);
-      map_index++;
-      RegisterLine* line = reg_table_.GetLine(i);
-      for (size_t j = 0; j < code_item_->registers_size_; j++) {
-        if (line->GetRegisterType(j).IsNonZeroReferenceTypes()) {
-          CHECK_LT(j / 8, map.RegWidth());
-          CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 1);
-        } else if ((j / 8) < map.RegWidth()) {
-          CHECK_EQ((reg_bitmap[j / 8] >> (j % 8)) & 1, 0);
-        } else {
-          // If a register doesn't contain a reference then the bitmap may be shorter than the line
-        }
-      }
-    } else {
-      CHECK(reg_bitmap == NULL);
-    }
-  }
-}
-
-void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* gc_map) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  {
-    WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
-    DexGcMapTable::iterator it = dex_gc_maps_->find(ref);
-    if (it != dex_gc_maps_->end()) {
-      delete it->second;
-      dex_gc_maps_->erase(it);
-    }
-    dex_gc_maps_->Put(ref, gc_map);
-  }
-  DCHECK(GetDexGcMap(ref) != NULL);
-}
-
-
-void  MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  WriterMutexLock mu(Thread::Current(), *safecast_map_lock_);
-  SafeCastMap::iterator it = safecast_map_->find(ref);
-  if (it != safecast_map_->end()) {
-    delete it->second;
-    safecast_map_->erase(it);
-  }
-  safecast_map_->Put(ref, cast_set);
-  DCHECK(safecast_map_->find(ref) != safecast_map_->end());
-}
-
-bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  ReaderMutexLock mu(Thread::Current(), *safecast_map_lock_);
-  SafeCastMap::const_iterator it = safecast_map_->find(ref);
-  if (it == safecast_map_->end()) {
-    return false;
-  }
-
-  // Look up the cast address in the set of safe casts
-  MethodVerifier::MethodSafeCastSet::const_iterator cast_it = it->second->find(pc);
-  return cast_it != it->second->end();
-}
-
-const std::vector<uint8_t>* MethodVerifier::GetDexGcMap(MethodReference ref) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_);
-  DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref);
-  CHECK(it != dex_gc_maps_->end())
-    << "Didn't find GC map for: " << PrettyMethod(ref.dex_method_index, *ref.dex_file);
-  CHECK(it->second != NULL);
-  return it->second;
-}
-
-void  MethodVerifier::SetDevirtMap(MethodReference ref,
-                                   const PcToConcreteMethodMap* devirt_map) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_);
-  DevirtualizationMapTable::iterator it = devirt_maps_->find(ref);
-  if (it != devirt_maps_->end()) {
-    delete it->second;
-    devirt_maps_->erase(it);
-  }
-
-  devirt_maps_->Put(ref, devirt_map);
-  DCHECK(devirt_maps_->find(ref) != devirt_maps_->end());
-}
-
-const MethodReference* MethodVerifier::GetDevirtMap(const MethodReference& ref,
-                                                                    uint32_t dex_pc) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_);
-  DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref);
-  if (it == devirt_maps_->end()) {
-    return NULL;
-  }
-
-  // Look up the PC in the map, get the concrete method to execute and return its reference.
-  MethodVerifier::PcToConcreteMethodMap::const_iterator pc_to_concrete_method
-      = it->second->find(dex_pc);
-  if (pc_to_concrete_method != it->second->end()) {
-    return &(pc_to_concrete_method->second);
-  } else {
-    return NULL;
-  }
-}
-
 std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_pc) {
   RegisterLine* line = reg_table_.GetLine(dex_pc);
   std::vector<int32_t> result;
@@ -4273,120 +3928,14 @@
   return result;
 }
 
-bool MethodVerifier::IsCandidateForCompilation(MethodReference& method_ref,
-                                               const uint32_t access_flags) {
-#ifdef ART_SEA_IR_MODE
-    bool use_sea = Runtime::Current()->IsSeaIRMode();
-    use_sea = use_sea && (std::string::npos != PrettyMethod(
-                          method_ref.dex_method_index, *(method_ref.dex_file)).find("fibonacci"));
-    if (use_sea) return true;
-#endif
-  // Don't compile class initializers, ever.
-  if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) {
-    return false;
-  }
-  return (Runtime::Current()->GetCompilerFilter() != Runtime::kInterpretOnly);
-}
-
-ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL;
-MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL;
-
-ReaderWriterMutex* MethodVerifier::safecast_map_lock_ = NULL;
-MethodVerifier::SafeCastMap* MethodVerifier::safecast_map_ = NULL;
-
-ReaderWriterMutex* MethodVerifier::devirt_maps_lock_ = NULL;
-MethodVerifier::DevirtualizationMapTable* MethodVerifier::devirt_maps_ = NULL;
-
-ReaderWriterMutex* MethodVerifier::rejected_classes_lock_ = NULL;
-MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL;
-
 void MethodVerifier::Init() {
-  if (Runtime::Current()->IsCompiler()) {
-    dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock");
-    Thread* self = Thread::Current();
-    {
-      WriterMutexLock mu(self, *dex_gc_maps_lock_);
-      dex_gc_maps_ = new MethodVerifier::DexGcMapTable;
-    }
-
-    safecast_map_lock_ = new ReaderWriterMutex("verifier Cast Elision lock");
-    {
-      WriterMutexLock mu(self, *safecast_map_lock_);
-      safecast_map_ = new MethodVerifier::SafeCastMap();
-    }
-
-    devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock");
-
-    {
-      WriterMutexLock mu(self, *devirt_maps_lock_);
-      devirt_maps_ = new MethodVerifier::DevirtualizationMapTable();
-    }
-
-    rejected_classes_lock_ = new ReaderWriterMutex("verifier rejected classes lock");
-    {
-      WriterMutexLock mu(self, *rejected_classes_lock_);
-      rejected_classes_ = new MethodVerifier::RejectedClassesTable;
-    }
-  }
   art::verifier::RegTypeCache::Init();
 }
 
 void MethodVerifier::Shutdown() {
-  if (Runtime::Current()->IsCompiler()) {
-    Thread* self = Thread::Current();
-    {
-      WriterMutexLock mu(self, *dex_gc_maps_lock_);
-      STLDeleteValues(dex_gc_maps_);
-      delete dex_gc_maps_;
-      dex_gc_maps_ = NULL;
-    }
-    delete dex_gc_maps_lock_;
-    dex_gc_maps_lock_ = NULL;
-
-    {
-      WriterMutexLock mu(self, *safecast_map_lock_);
-      STLDeleteValues(safecast_map_);
-      delete safecast_map_;
-      safecast_map_ = NULL;
-    }
-    delete safecast_map_lock_;
-    safecast_map_lock_ = NULL;
-
-    {
-      WriterMutexLock mu(self, *devirt_maps_lock_);
-      STLDeleteValues(devirt_maps_);
-      delete devirt_maps_;
-      devirt_maps_ = NULL;
-    }
-    delete devirt_maps_lock_;
-    devirt_maps_lock_ = NULL;
-
-    {
-      WriterMutexLock mu(self, *rejected_classes_lock_);
-      delete rejected_classes_;
-      rejected_classes_ = NULL;
-    }
-    delete rejected_classes_lock_;
-    rejected_classes_lock_ = NULL;
-  }
   verifier::RegTypeCache::ShutDown();
 }
 
-void MethodVerifier::AddRejectedClass(ClassReference ref) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  {
-    WriterMutexLock mu(Thread::Current(), *rejected_classes_lock_);
-    rejected_classes_->insert(ref);
-  }
-  CHECK(IsClassRejected(ref));
-}
-
-bool MethodVerifier::IsClassRejected(ClassReference ref) {
-  DCHECK(Runtime::Current()->IsCompiler());
-  ReaderMutexLock mu(Thread::Current(), *rejected_classes_lock_);
-  return (rejected_classes_->find(ref) != rejected_classes_->end());
-}
-
 void MethodVerifier::VisitRoots(RootVisitor* visitor, void* arg) {
   reg_types_.VisitRoots(visitor, arg);
 }
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index dffda96..ac36a7e 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -183,16 +183,6 @@
   // information
   void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static const std::vector<uint8_t>* GetDexGcMap(MethodReference ref)
-      LOCKS_EXCLUDED(dex_gc_maps_lock_);
-
-  static const MethodReference* GetDevirtMap(const MethodReference& ref, uint32_t dex_pc)
-      LOCKS_EXCLUDED(devirt_maps_lock_);
-
-  // Returns true if the cast can statically be verified to be redundant
-  // by using the check-cast elision peephole optimization in the verifier
-  static bool IsSafeCast(MethodReference ref, uint32_t pc) LOCKS_EXCLUDED(safecast_map_lock_);
-
   // Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
   // to the locks held at 'dex_pc' in method 'm'.
   static void FindLocksAtDexPc(mirror::ArtMethod* m, uint32_t dex_pc,
@@ -212,11 +202,6 @@
   static void Init() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void Shutdown();
 
-  static void AddRejectedClass(ClassReference ref)
-      LOCKS_EXCLUDED(rejected_classes_lock_);
-  static bool IsClassRejected(ClassReference ref)
-      LOCKS_EXCLUDED(rejected_classes_lock_);
-
   bool CanLoadClasses() const {
     return can_load_classes_;
   }
@@ -236,11 +221,22 @@
   // Describe VRegs at the given dex pc.
   std::vector<int32_t> DescribeVRegs(uint32_t dex_pc);
 
-  static bool IsCandidateForCompilation(MethodReference& method_ref,
-                                        const uint32_t access_flags);
-
   void VisitRoots(RootVisitor* visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Accessors used by the compiler via CompilerCallback
+  const DexFile::CodeItem* CodeItem() const;
+  RegisterLine* GetRegLine(uint32_t dex_pc);
+  const InstructionFlags& GetInstructionFlags(size_t index) const;
+  mirror::ClassLoader* GetClassLoader() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  mirror::DexCache* GetDexCache() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  MethodReference GetMethodReference() const;
+  uint32_t GetAccessFlags() const;
+  bool HasCheckCasts() const;
+  bool HasVirtualOrInterfaceInvokes() const;
+  bool HasFailures() const;
+  const RegType& ResolveCheckedClass(uint32_t class_idx)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
  private:
   // Adds the given string to the beginning of the last failure message.
   void PrependToLastFailMessage(std::string);
@@ -612,57 +608,8 @@
   // Get a type representing the declaring class of the method.
   const RegType& GetDeclaringClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  /*
-   * Generate the GC map for a method that has just been verified (i.e. we're doing this as part of
-   * verification). For type-precise determination we have all the data we need, so we just need to
-   * encode it in some clever fashion.
-   * Returns a pointer to a newly-allocated RegisterMap, or NULL on failure.
-   */
-  const std::vector<uint8_t>* GenerateGcMap();
-
-  // Verify that the GC map associated with method_ is well formed
-  void VerifyGcMap(const std::vector<uint8_t>& data);
-
-  // Compute sizes for GC map data
-  void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
-
   InstructionFlags* CurrentInsnFlags();
 
-  // All the GC maps that the verifier has created
-  typedef SafeMap<const MethodReference, const std::vector<uint8_t>*,
-      MethodReferenceComparator> DexGcMapTable;
-  static ReaderWriterMutex* dex_gc_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  static DexGcMapTable* dex_gc_maps_ GUARDED_BY(dex_gc_maps_lock_);
-  static void SetDexGcMap(MethodReference ref, const std::vector<uint8_t>* dex_gc_map)
-      LOCKS_EXCLUDED(dex_gc_maps_lock_);
-
-
-  // Cast elision types.
-  typedef std::set<uint32_t> MethodSafeCastSet;
-  typedef SafeMap<const MethodReference, const MethodSafeCastSet*,
-      MethodReferenceComparator> SafeCastMap;
-  MethodVerifier::MethodSafeCastSet* GenerateSafeCastSet()
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  static void SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* mscs);
-      LOCKS_EXCLUDED(safecast_map_lock_);
-  static ReaderWriterMutex* safecast_map_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  static SafeCastMap* safecast_map_ GUARDED_BY(safecast_map_lock_);
-
-  // Devirtualization map.
-  typedef SafeMap<const uint32_t, MethodReference> PcToConcreteMethodMap;
-  typedef SafeMap<const MethodReference, const PcToConcreteMethodMap*,
-      MethodReferenceComparator> DevirtualizationMapTable;
-  MethodVerifier::PcToConcreteMethodMap* GenerateDevirtMap()
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-
-  static ReaderWriterMutex* devirt_maps_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  static DevirtualizationMapTable* devirt_maps_ GUARDED_BY(devirt_maps_lock_);
-  static void SetDevirtMap(MethodReference ref,
-                           const PcToConcreteMethodMap* pc_method_map)
-        LOCKS_EXCLUDED(devirt_maps_lock_);
-  typedef std::set<ClassReference> RejectedClassesTable;
-  static ReaderWriterMutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
-  static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_);
 
   RegTypeCache reg_types_;
 
diff --git a/test/Android.mk b/test/Android.mk
index 1bf6074..5187724 100644
--- a/test/Android.mk
+++ b/test/Android.mk
@@ -71,7 +71,6 @@
     LOCAL_NO_STANDARD_LIBRARIES := true
     LOCAL_MODULE_PATH := $(3)
     LOCAL_DEX_PREOPT_IMAGE := $(TARGET_CORE_IMG_OUT)
-    LOCAL_DEX_PREOPT := false
     LOCAL_ADDITIONAL_DEPENDENCIES := art/build/Android.common.mk
     LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
     include $(BUILD_JAVA_LIBRARY)