Merge "Revert "Revert "Inline across dex files."""
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 948c756..91998fa 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -167,6 +167,7 @@
   runtime/mirror/object_test.cc \
   runtime/monitor_pool_test.cc \
   runtime/monitor_test.cc \
+  runtime/oat_file_test.cc \
   runtime/oat_file_assistant_test.cc \
   runtime/parsed_options_test.cc \
   runtime/reference_table_test.cc \
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 266b7c3..c85c3b6 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -671,6 +671,9 @@
               }
               int dead_true_def = if_true->ssa_rep->defs[0];
               raw_use_counts_[dead_true_def] = use_counts_[dead_true_def] = 0;
+              // Update ending vreg->sreg map for GC maps generation.
+              int def_vreg = SRegToVReg(mir->ssa_rep->defs[0]);
+              bb->data_flow_info->vreg_to_ssa_map_exit[def_vreg] = mir->ssa_rep->defs[0];
               // We want to remove ft and tk and link bb directly to ft_ft. First, we need
               // to update all Phi inputs correctly with UpdatePredecessor(ft->id, bb->id)
               // since the live_def above comes from ft->first_mir_insn (if_false).
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 4e7919b..bd479be 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -793,33 +793,43 @@
     prev_mir = mir;
   }
 
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
+  static constexpr bool kLittleEndian = true;
+#else
+  static constexpr bool kLittleEndian = false;
+#endif
+
   // Build the GC map.
   uint32_t reg_width = static_cast<uint32_t>((max_ref_vreg + 8) / 8);
   GcMapBuilder native_gc_map_builder(&native_gc_map_,
                                      safepoints_.size(),
                                      max_native_offset, reg_width);
-#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN)
-  ArenaVector<uint8_t> references_buffer(arena_->Adapter());
-  references_buffer.resize(reg_width);
-#endif
-  for (const auto& entry : safepoints_) {
-    uint32_t native_offset = entry.first->offset;
-    MIR* mir = entry.second;
-    UpdateReferenceVRegs(mir, prev_mir, references);
-#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN)
-    // Big-endian or unknown endianness, manually translate the bit vector data.
-    const auto* raw_storage = references->GetRawStorage();
-    for (size_t i = 0; i != reg_width; ++i) {
-      references_buffer[i] = static_cast<uint8_t>(
-          raw_storage[i / sizeof(raw_storage[0])] >> (8u * (i % sizeof(raw_storage[0]))));
+  if (kLittleEndian) {
+    for (const auto& entry : safepoints_) {
+      uint32_t native_offset = entry.first->offset;
+      MIR* mir = entry.second;
+      UpdateReferenceVRegs(mir, prev_mir, references);
+      // For little-endian, the bytes comprising the bit vector's raw storage are what we need.
+      native_gc_map_builder.AddEntry(native_offset,
+                                     reinterpret_cast<const uint8_t*>(references->GetRawStorage()));
+      prev_mir = mir;
     }
-    native_gc_map_builder.AddEntry(native_offset, &references_buffer[0]);
-#else
-    // For little-endian, the bytes comprising the bit vector's raw storage are what we need.
-    native_gc_map_builder.AddEntry(native_offset,
-                                   reinterpret_cast<const uint8_t*>(references->GetRawStorage()));
-#endif
-    prev_mir = mir;
+  } else {
+    ArenaVector<uint8_t> references_buffer(arena_->Adapter());
+    references_buffer.resize(reg_width);
+    for (const auto& entry : safepoints_) {
+      uint32_t native_offset = entry.first->offset;
+      MIR* mir = entry.second;
+      UpdateReferenceVRegs(mir, prev_mir, references);
+      // Big-endian or unknown endianness, manually translate the bit vector data.
+      const auto* raw_storage = references->GetRawStorage();
+      for (size_t i = 0; i != reg_width; ++i) {
+        references_buffer[i] = static_cast<uint8_t>(
+            raw_storage[i / sizeof(raw_storage[0])] >> (8u * (i % sizeof(raw_storage[0]))));
+      }
+      native_gc_map_builder.AddEntry(native_offset, &references_buffer[0]);
+      prev_mir = mir;
+    }
   }
 }
 
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index 236bad7..cca4e5a 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -1465,9 +1465,6 @@
     virtual void GenMonitorEnter(int opt_flags, RegLocation rl_src);
     virtual void GenMonitorExit(int opt_flags, RegLocation rl_src);
 
-    // Temp workaround
-    void Workaround7250540(RegLocation rl_dest, RegStorage zero_reg);
-
     virtual LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) = 0;
 
     // Queries for backend support for vectors
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index d238b2c..c1555aa 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -133,7 +133,7 @@
     return false;
   }
   std::string error_msg;
-  oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, &error_msg);
+  oat_file_ = OatFile::OpenReadable(oat_file.get(), oat_location, nullptr, &error_msg);
   if (oat_file_ == nullptr) {
     PLOG(ERROR) << "Failed to open writable oat file " << oat_filename << " for " << oat_location
         << ": " << error_msg;
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 46aed60..c426625 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -122,7 +122,7 @@
     compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
   }
   std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
-                                                  nullptr, false, &error_msg));
+                                                  nullptr, false, nullptr, &error_msg));
   ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
   const OatHeader& oat_header = oat_file->GetOatHeader();
   ASSERT_TRUE(oat_header.IsValid());
diff --git a/compiler/optimizing/boolean_simplifier.cc b/compiler/optimizing/boolean_simplifier.cc
index ecf9fa2..0ecc0d7 100644
--- a/compiler/optimizing/boolean_simplifier.cc
+++ b/compiler/optimizing/boolean_simplifier.cc
@@ -18,15 +18,6 @@
 
 namespace art {
 
-static bool EndsWithAnIf(HBasicBlock* block) {
-  return block->GetLastInstruction()->IsIf();
-}
-
-static bool HasSinglePhi(HBasicBlock* block) {
-  return !block->GetPhis().IsEmpty()
-      && block->GetFirstPhi()->GetNext() == nullptr;
-}
-
 // Returns true if 'block1' and 'block2' are empty, merge into the same single
 // successor and the successor can only be reached from them.
 static bool BlocksDoMergeTogether(HBasicBlock* block1, HBasicBlock* block2) {
@@ -39,15 +30,15 @@
 // Returns true if the outcome of the branching matches the boolean value of
 // the branching condition.
 static bool PreservesCondition(HInstruction* input_true, HInstruction* input_false) {
-  return input_true->IsIntConstant() && input_true->AsIntConstant()->GetValue() == 1
-         && input_false->IsIntConstant() && input_false->AsIntConstant()->GetValue() == 0;
+  return input_true->IsIntConstant() && input_true->AsIntConstant()->IsOne()
+      && input_false->IsIntConstant() && input_false->AsIntConstant()->IsZero();
 }
 
 // Returns true if the outcome of the branching is exactly opposite of the
 // boolean value of the branching condition.
 static bool NegatesCondition(HInstruction* input_true, HInstruction* input_false) {
-  return input_true->IsIntConstant() && input_true->AsIntConstant()->GetValue() == 0
-         && input_false->IsIntConstant() && input_false->AsIntConstant()->GetValue() == 1;
+  return input_true->IsIntConstant() && input_true->AsIntConstant()->IsZero()
+      && input_false->IsIntConstant() && input_false->AsIntConstant()->IsOne();
 }
 
 // Returns an instruction with the opposite boolean value from 'cond'.
@@ -72,11 +63,11 @@
       return new (allocator) HLessThan(lhs, rhs);
     }
   } else if (cond->IsIntConstant()) {
-    int32_t value = cond->AsIntConstant()->GetValue();
-    if (value == 0) {
+    HIntConstant* int_const = cond->AsIntConstant();
+    if (int_const->IsZero()) {
       return graph->GetIntConstant1();
     } else {
-      DCHECK_EQ(value, 1);
+      DCHECK(int_const->IsOne());
       return graph->GetIntConstant0();
     }
   }
@@ -91,7 +82,7 @@
   // order does not matter.
   for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) {
     HBasicBlock* block = it.Current();
-    if (!EndsWithAnIf(block)) continue;
+    if (!block->EndsWithIf()) continue;
 
     // Find elements of the pattern.
     HIf* if_instruction = block->GetLastInstruction()->AsIf();
@@ -101,7 +92,7 @@
       continue;
     }
     HBasicBlock* merge_block = true_block->GetSuccessors().Get(0);
-    if (!HasSinglePhi(merge_block)) {
+    if (!merge_block->HasSinglePhi()) {
       continue;
     }
     HPhi* phi = merge_block->GetFirstPhi()->AsPhi();
diff --git a/compiler/optimizing/boolean_simplifier.h b/compiler/optimizing/boolean_simplifier.h
index 9fa9c5a..a88733e 100644
--- a/compiler/optimizing/boolean_simplifier.h
+++ b/compiler/optimizing/boolean_simplifier.h
@@ -15,7 +15,7 @@
  */
 
 // This optimization recognizes a common pattern where a boolean value is
-// either casted to an integer or negated by selecting from zero/one integer
+// either cast to an integer or negated by selecting from zero/one integer
 // constants with an If statement. Because boolean values are internally
 // represented as zero/one, we can safely replace the pattern with a suitable
 // condition instruction.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index d783903..97c470b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1388,8 +1388,8 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
 
-  // Java language does not allow treating boolean as an integral type but our
-  // bit representation makes it safe.
+  // The Java language does not allow treating boolean as an integral type but
+  // our bit representation makes it safe.
 
   switch (result_type) {
     case Primitive::kPrimByte:
@@ -2326,10 +2326,8 @@
 void LocationsBuilderARM::HandleShift(HBinaryOperation* op) {
   DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
 
-  LocationSummary::CallKind call_kind = op->GetResultType() == Primitive::kPrimLong
-      ? LocationSummary::kCall
-      : LocationSummary::kNoCall;
-  LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(op, call_kind);
+  LocationSummary* locations =
+      new (GetGraph()->GetArena()) LocationSummary(op, LocationSummary::kNoCall);
 
   switch (op->GetResultType()) {
     case Primitive::kPrimInt: {
@@ -2339,12 +2337,10 @@
       break;
     }
     case Primitive::kPrimLong: {
-      InvokeRuntimeCallingConvention calling_convention;
-      locations->SetInAt(0, Location::RegisterPairLocation(
-          calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
-      locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
-      // The runtime helper puts the output in R0,R1.
-      locations->SetOut(Location::RegisterPairLocation(R0, R1));
+      locations->SetInAt(0, Location::RequiresRegister());
+      locations->SetInAt(1, Location::RequiresRegister());
+      locations->AddTemp(Location::RequiresRegister());
+      locations->SetOut(Location::RequiresRegister());
       break;
     }
     default:
@@ -2392,24 +2388,56 @@
       break;
     }
     case Primitive::kPrimLong: {
-      // TODO: Inline the assembly instead of calling the runtime.
-      InvokeRuntimeCallingConvention calling_convention;
-      DCHECK_EQ(calling_convention.GetRegisterAt(0), first.AsRegisterPairLow<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(1), first.AsRegisterPairHigh<Register>());
-      DCHECK_EQ(calling_convention.GetRegisterAt(2), second.AsRegister<Register>());
-      DCHECK_EQ(R0, out.AsRegisterPairLow<Register>());
-      DCHECK_EQ(R1, out.AsRegisterPairHigh<Register>());
+      Register o_h = out.AsRegisterPairHigh<Register>();
+      Register o_l = out.AsRegisterPairLow<Register>();
 
-      int32_t entry_point_offset;
+      Register temp = locations->GetTemp(0).AsRegister<Register>();
+
+      Register high = first.AsRegisterPairHigh<Register>();
+      Register low = first.AsRegisterPairLow<Register>();
+
+      Register second_reg = second.AsRegister<Register>();
+
       if (op->IsShl()) {
-        entry_point_offset = QUICK_ENTRY_POINT(pShlLong);
+        // Shift the high part
+        __ and_(second_reg, second_reg, ShifterOperand(63));
+        __ Lsl(o_h, high, second_reg);
+        // Shift the low part and `or` what overflew on the high part
+        __ rsb(temp, second_reg, ShifterOperand(32));
+        __ Lsr(temp, low, temp);
+        __ orr(o_h, o_h, ShifterOperand(temp));
+        // If the shift is > 32 bits, override the high part
+        __ subs(temp, second_reg, ShifterOperand(32));
+        __ it(PL);
+        __ Lsl(o_h, low, temp, false, PL);
+        // Shift the low part
+        __ Lsl(o_l, low, second_reg);
       } else if (op->IsShr()) {
-        entry_point_offset = QUICK_ENTRY_POINT(pShrLong);
+        // Shift the low part
+        __ and_(second_reg, second_reg, ShifterOperand(63));
+        __ Lsr(o_l, low, second_reg);
+        // Shift the high part and `or` what underflew on the low part
+        __ rsb(temp, second_reg, ShifterOperand(32));
+        __ Lsl(temp, high, temp);
+        __ orr(o_l, o_l, ShifterOperand(temp));
+        // If the shift is > 32 bits, override the low part
+        __ subs(temp, second_reg, ShifterOperand(32));
+        __ it(PL);
+        __ Asr(o_l, high, temp, false, PL);
+        // Shift the high part
+        __ Asr(o_h, high, second_reg);
       } else {
-        entry_point_offset = QUICK_ENTRY_POINT(pUshrLong);
+        // same as Shr except we use `Lsr`s and not `Asr`s
+        __ and_(second_reg, second_reg, ShifterOperand(63));
+        __ Lsr(o_l, low, second_reg);
+        __ rsb(temp, second_reg, ShifterOperand(32));
+        __ Lsl(temp, high, temp);
+        __ orr(o_l, o_l, ShifterOperand(temp));
+        __ subs(temp, second_reg, ShifterOperand(32));
+        __ it(PL);
+        __ Lsr(o_l, high, temp, false, PL);
+        __ Lsr(o_h, high, second_reg);
       }
-      __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
-      __ blx(LR);
       break;
     }
     default:
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0a7d3fe..4414a65 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1370,8 +1370,8 @@
   LocationSummary* locations =
       new (GetGraph()->GetArena()) LocationSummary(conversion, call_kind);
 
-  // Java language does not allow treating boolean as an integral type but our
-  // bit representation makes it safe.
+  // The Java language does not allow treating boolean as an integral type but
+  // our bit representation makes it safe.
 
   switch (result_type) {
     case Primitive::kPrimByte:
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index bff8fc9..c1f601e 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1410,8 +1410,8 @@
   Primitive::Type input_type = conversion->GetInputType();
   DCHECK_NE(result_type, input_type);
 
-  // Java language does not allow treating boolean as an integral type but our
-  // bit representation makes it safe.
+  // The Java language does not allow treating boolean as an integral type but
+  // our bit representation makes it safe.
 
   switch (result_type) {
     case Primitive::kPrimByte:
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 6009cb5..4f6565d 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -832,6 +832,14 @@
          && (loop_info == nullptr || !loop_info->IsBackEdge(*this));
 }
 
+bool HBasicBlock::EndsWithIf() const {
+  return !GetInstructions().IsEmpty() && GetLastInstruction()->IsIf();
+}
+
+bool HBasicBlock::HasSinglePhi() const {
+  return !GetPhis().IsEmpty() && GetFirstPhi()->GetNext() == nullptr;
+}
+
 void HInstructionList::SetBlockOfInstructions(HBasicBlock* block) const {
   for (HInstruction* current = first_instruction_;
        current != nullptr;
@@ -1086,13 +1094,15 @@
 }
 
 void HGraph::MergeEmptyBranches(HBasicBlock* start_block, HBasicBlock* end_block) {
-  // Make sure this is a diamond control-flow path, find the two branches.
+  // Find the two branches of an If.
   DCHECK_EQ(start_block->GetSuccessors().Size(), 2u);
-  DCHECK_EQ(end_block->GetPredecessors().Size(), 2u);
   HBasicBlock* left_branch = start_block->GetSuccessors().Get(0);
   HBasicBlock* right_branch = start_block->GetSuccessors().Get(1);
+
+  // Make sure this is a diamond control-flow path.
   DCHECK_EQ(left_branch->GetSuccessors().Get(0), end_block);
   DCHECK_EQ(right_branch->GetSuccessors().Get(0), end_block);
+  DCHECK_EQ(end_block->GetPredecessors().Size(), 2u);
   DCHECK_EQ(start_block, end_block->GetDominator());
 
   // Disconnect the branches and merge the two blocks. This will move
@@ -1114,16 +1124,12 @@
   reverse_post_order_.Delete(right_branch);
   reverse_post_order_.Delete(end_block);
 
-  // Update loop information.
-  HLoopInformation* loop_info = start_block->GetLoopInformation();
-  if (kIsDebugBuild) {
-    if (loop_info != nullptr) {
-      DCHECK_EQ(loop_info, left_branch->GetLoopInformation());
-      DCHECK_EQ(loop_info, right_branch->GetLoopInformation());
-      DCHECK_EQ(loop_info, end_block->GetLoopInformation());
-    }
-  }
-  while (loop_info != nullptr) {
+  // Update loops which contain the code.
+  for (HLoopInformationOutwardIterator it(*start_block); !it.Done(); it.Advance()) {
+    HLoopInformation* loop_info = it.Current();
+    DCHECK(loop_info->Contains(*left_branch));
+    DCHECK(loop_info->Contains(*right_branch));
+    DCHECK(loop_info->Contains(*end_block));
     loop_info->Remove(left_branch);
     loop_info->Remove(right_branch);
     loop_info->Remove(end_block);
@@ -1131,8 +1137,6 @@
       loop_info->RemoveBackEdge(end_block);
       loop_info->AddBackEdge(start_block);
     }
-    // Move to parent loop if nested.
-    loop_info = loop_info->GetHeader()->GetDominator()->GetLoopInformation();
   }
 }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index a38ee45..08b16d9 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -602,6 +602,9 @@
   bool IsCatchBlock() const { return is_catch_block_; }
   void SetIsCatchBlock() { is_catch_block_ = true; }
 
+  bool EndsWithIf() const;
+  bool HasSinglePhi() const;
+
  private:
   HGraph* graph_;
   GrowableArray<HBasicBlock*> predecessors_;
@@ -624,6 +627,31 @@
   DISALLOW_COPY_AND_ASSIGN(HBasicBlock);
 };
 
+// Iterates over the LoopInformation of all loops which contain 'block'
+// from the innermost to the outermost.
+class HLoopInformationOutwardIterator : public ValueObject {
+ public:
+  explicit HLoopInformationOutwardIterator(const HBasicBlock& block)
+      : current_(block.GetLoopInformation()) {}
+
+  bool Done() const { return current_ == nullptr; }
+
+  void Advance() {
+    DCHECK(!Done());
+    current_ = current_->GetHeader()->GetDominator()->GetLoopInformation();
+  }
+
+  HLoopInformation* Current() const {
+    DCHECK(!Done());
+    return current_;
+  }
+
+ private:
+  HLoopInformation* current_;
+
+  DISALLOW_COPY_AND_ASSIGN(HLoopInformationOutwardIterator);
+};
+
 #define FOR_EACH_CONCRETE_INSTRUCTION(M)                                \
   M(Add, BinaryOperation)                                               \
   M(And, BinaryOperation)                                               \
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 9914ef4..5818a37 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -17,7 +17,8 @@
 #ifndef ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
 #define ART_COMPILER_OPTIMIZING_STACK_MAP_STREAM_H_
 
-#include "base/bit_vector.h"
+#include "base/arena_containers.h"
+#include "base/bit_vector-inl.h"
 #include "base/value_object.h"
 #include "memory_region.h"
 #include "nodes.h"
@@ -40,7 +41,8 @@
         stack_mask_max_(-1),
         dex_pc_max_(0),
         native_pc_offset_max_(0),
-        number_of_stack_maps_with_inline_info_(0) {}
+        number_of_stack_maps_with_inline_info_(0),
+        dex_map_hash_to_stack_map_indices_(std::less<uint32_t>(), allocator->Adapter()) {}
 
   // Compute bytes needed to encode a mask with the given maximum element.
   static uint32_t StackMaskEncodingSize(int max_element) {
@@ -59,6 +61,7 @@
     size_t dex_register_locations_start_index;
     size_t inline_infos_start_index;
     BitVector* live_dex_registers_mask;
+    uint32_t dex_register_map_hash;
   };
 
   struct InlineInfoEntry {
@@ -80,6 +83,7 @@
     entry.inlining_depth = inlining_depth;
     entry.dex_register_locations_start_index = dex_register_locations_.Size();
     entry.inline_infos_start_index = inline_infos_.Size();
+    entry.dex_register_map_hash = 0;
     if (num_dex_registers != 0) {
       entry.live_dex_registers_mask =
           new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true);
@@ -105,7 +109,7 @@
     inline_infos_.Add(entry);
   }
 
-  size_t ComputeNeededSize() const {
+  size_t ComputeNeededSize() {
     size_t size = CodeInfo::kFixedSize
         + ComputeStackMapsSize()
         + ComputeDexRegisterMapsSize()
@@ -118,7 +122,7 @@
     return StackMaskEncodingSize(stack_mask_max_);
   }
 
-  size_t ComputeStackMapsSize() const {
+  size_t ComputeStackMapsSize() {
     return stack_maps_.Size() * StackMap::ComputeStackMapSize(
         ComputeStackMaskSize(),
         ComputeInlineInfoSize(),
@@ -146,10 +150,13 @@
   }
 
   // Compute the size of all the Dex register maps.
-  size_t ComputeDexRegisterMapsSize() const {
+  size_t ComputeDexRegisterMapsSize() {
     size_t size = 0;
     for (size_t i = 0; i < stack_maps_.Size(); ++i) {
-      size += ComputeDexRegisterMapSize(stack_maps_.Get(i));
+      if (FindEntryWithTheSameDexMap(i) == kNoSameDexMapFound) {
+        // Entries with the same dex map will have the same offset.
+        size += ComputeDexRegisterMapSize(stack_maps_.Get(i));
+      }
     }
     return size;
   }
@@ -161,11 +168,11 @@
       + (number_of_stack_maps_with_inline_info_ * InlineInfo::kFixedSize);
   }
 
-  size_t ComputeDexRegisterMapsStart() const {
+  size_t ComputeDexRegisterMapsStart() {
     return CodeInfo::kFixedSize + ComputeStackMapsSize();
   }
 
-  size_t ComputeInlineInfoStart() const {
+  size_t ComputeInlineInfoStart() {
     return ComputeDexRegisterMapsStart() + ComputeDexRegisterMapsSize();
   }
 
@@ -206,38 +213,47 @@
         stack_map.SetStackMask(code_info, *entry.sp_mask);
       }
 
-      if (entry.num_dex_registers != 0) {
-        // Set the Dex register map.
-        MemoryRegion register_region =
-            dex_register_locations_region.Subregion(
-                next_dex_register_map_offset,
-                ComputeDexRegisterMapSize(entry));
-        next_dex_register_map_offset += register_region.size();
-        DexRegisterMap dex_register_map(register_region);
-        stack_map.SetDexRegisterMapOffset(
+      if (entry.num_dex_registers == 0) {
+        // No dex map available.
+        stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
+      } else {
+        // Search for an entry with the same dex map.
+        size_t entry_with_same_map = FindEntryWithTheSameDexMap(i);
+        if (entry_with_same_map != kNoSameDexMapFound) {
+          // If we have a hit reuse the offset.
+          stack_map.SetDexRegisterMapOffset(code_info,
+              code_info.GetStackMapAt(entry_with_same_map).GetDexRegisterMapOffset(code_info));
+        } else {
+          // New dex registers maps should be added to the stack map.
+          MemoryRegion register_region =
+              dex_register_locations_region.Subregion(
+                  next_dex_register_map_offset,
+                  ComputeDexRegisterMapSize(entry));
+          next_dex_register_map_offset += register_region.size();
+          DexRegisterMap dex_register_map(register_region);
+          stack_map.SetDexRegisterMapOffset(
             code_info, register_region.start() - dex_register_locations_region.start());
 
-        // Offset in `dex_register_map` where to store the next register entry.
-        size_t offset = DexRegisterMap::kFixedSize;
-        dex_register_map.SetLiveBitMask(offset,
-                                        entry.num_dex_registers,
-                                        *entry.live_dex_registers_mask);
-        offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
-        for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
-             dex_register_number < entry.num_dex_registers;
-             ++dex_register_number) {
-          if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
-            DexRegisterLocation dex_register_location = dex_register_locations_.Get(
-                entry.dex_register_locations_start_index + index_in_dex_register_locations);
-            dex_register_map.SetRegisterInfo(offset, dex_register_location);
-            offset += DexRegisterMap::EntrySize(dex_register_location);
-            ++index_in_dex_register_locations;
+          // Offset in `dex_register_map` where to store the next register entry.
+          size_t offset = DexRegisterMap::kFixedSize;
+          dex_register_map.SetLiveBitMask(offset,
+                                          entry.num_dex_registers,
+                                          *entry.live_dex_registers_mask);
+          offset += DexRegisterMap::LiveBitMaskSize(entry.num_dex_registers);
+          for (size_t dex_register_number = 0, index_in_dex_register_locations = 0;
+               dex_register_number < entry.num_dex_registers;
+               ++dex_register_number) {
+            if (entry.live_dex_registers_mask->IsBitSet(dex_register_number)) {
+              DexRegisterLocation dex_register_location = dex_register_locations_.Get(
+                  entry.dex_register_locations_start_index + index_in_dex_register_locations);
+              dex_register_map.SetRegisterInfo(offset, dex_register_location);
+              offset += DexRegisterMap::EntrySize(dex_register_location);
+              ++index_in_dex_register_locations;
+            }
           }
+          // Ensure we reached the end of the Dex registers region.
+          DCHECK_EQ(offset, register_region.size());
         }
-        // Ensure we reached the end of the Dex registers region.
-        DCHECK_EQ(offset, register_region.size());
-      } else {
-        stack_map.SetDexRegisterMapOffset(code_info, StackMap::kNoDexRegisterMap);
       }
 
       // Set the inlining info.
@@ -271,11 +287,86 @@
       DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
           << DexRegisterLocation::PrettyDescriptor(kind);
       dex_register_locations_.Add(DexRegisterLocation(kind, value));
-      stack_maps_.Get(stack_maps_.Size() - 1).live_dex_registers_mask->SetBit(dex_register);
+      StackMapEntry entry = stack_maps_.Get(stack_maps_.Size() - 1);
+      entry.live_dex_registers_mask->SetBit(dex_register);
+      entry.dex_register_map_hash += (1 << dex_register);
+      entry.dex_register_map_hash += static_cast<uint32_t>(value);
+      entry.dex_register_map_hash += static_cast<uint32_t>(kind);
+      stack_maps_.Put(stack_maps_.Size() - 1, entry);
     }
   }
 
  private:
+  // Returns the index of an entry with the same dex register map
+  // or kNoSameDexMapFound if no such entry exists.
+  size_t FindEntryWithTheSameDexMap(size_t entry_index) {
+    StackMapEntry entry = stack_maps_.Get(entry_index);
+    auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.dex_register_map_hash);
+    if (entries_it == dex_map_hash_to_stack_map_indices_.end()) {
+      // We don't have a perfect hash functions so we need a list to collect all stack maps
+      // which might have the same dex register map.
+      GrowableArray<uint32_t> stack_map_indices(allocator_, 1);
+      stack_map_indices.Add(entry_index);
+      dex_map_hash_to_stack_map_indices_.Put(entry.dex_register_map_hash, stack_map_indices);
+      return kNoSameDexMapFound;
+    }
+
+    // TODO: We don't need to add ourselves to the map if we can guarantee that
+    // FindEntryWithTheSameDexMap is called just once per stack map entry.
+    // A good way to do this is to cache the offset in the stack map entry. This
+    // is easier to do if we add markers when the stack map constructions begins
+    // and when it ends.
+
+    // We might have collisions, so we need to check whether or not we should
+    // add the entry to the map. `needs_to_be_added` keeps track of this.
+    bool needs_to_be_added = true;
+    size_t result = kNoSameDexMapFound;
+    for (size_t i = 0; i < entries_it->second.Size(); i++) {
+      size_t test_entry_index = entries_it->second.Get(i);
+      if (test_entry_index == entry_index) {
+        needs_to_be_added = false;
+      } else if (HaveTheSameDexMaps(stack_maps_.Get(test_entry_index), entry)) {
+        result = test_entry_index;
+        needs_to_be_added = false;
+        break;
+      }
+    }
+    if (needs_to_be_added) {
+      entries_it->second.Add(entry_index);
+    }
+    return result;
+  }
+
+  bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const {
+    if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) {
+      return true;
+    }
+    if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) {
+      return false;
+    }
+    if (a.num_dex_registers != b.num_dex_registers) {
+      return false;
+    }
+
+    int index_in_dex_register_locations = 0;
+    for (uint32_t i = 0; i < a.num_dex_registers; i++) {
+      if (a.live_dex_registers_mask->IsBitSet(i) != b.live_dex_registers_mask->IsBitSet(i)) {
+        return false;
+      }
+      if (a.live_dex_registers_mask->IsBitSet(i)) {
+        DexRegisterLocation a_loc = dex_register_locations_.Get(
+            a.dex_register_locations_start_index + index_in_dex_register_locations);
+        DexRegisterLocation b_loc = dex_register_locations_.Get(
+            b.dex_register_locations_start_index + index_in_dex_register_locations);
+        if (a_loc != b_loc) {
+          return false;
+        }
+        ++index_in_dex_register_locations;
+      }
+    }
+    return true;
+  }
+
   ArenaAllocator* allocator_;
   GrowableArray<StackMapEntry> stack_maps_;
   GrowableArray<DexRegisterLocation> dex_register_locations_;
@@ -285,6 +376,10 @@
   uint32_t native_pc_offset_max_;
   size_t number_of_stack_maps_with_inline_info_;
 
+  ArenaSafeMap<uint32_t, GrowableArray<uint32_t>> dex_map_hash_to_stack_map_indices_;
+
+  static constexpr uint32_t kNoSameDexMapFound = -1;
+
   ART_FRIEND_TEST(StackMapTest, Test1);
   ART_FRIEND_TEST(StackMapTest, Test2);
   ART_FRIEND_TEST(StackMapTest, TestNonLiveDexRegisters);
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index e7075c0..e5a9790 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -231,4 +231,54 @@
   ASSERT_EQ(stack_map.GetDexRegisterMapOffset(code_info), StackMap::kNoDexRegisterMapSmallEncoding);
 }
 
+TEST(StackMapTest, TestShareDexRegisterMap) {
+  ArenaPool pool;
+  ArenaAllocator arena(&pool);
+  StackMapStream stream(&arena);
+
+  ArenaBitVector sp_mask(&arena, 0, false);
+  uint32_t number_of_dex_registers = 2;
+  // First stack map.
+  stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
+  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  // Second stack map, which should share the same dex register map.
+  stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 0);
+  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+  // Third stack map (doesn't share the dex register map).
+  stream.AddStackMapEntry(0, 64, 0x3, &sp_mask, number_of_dex_registers, 0);
+  stream.AddDexRegisterEntry(0, DexRegisterLocation::Kind::kInRegister, 2);
+  stream.AddDexRegisterEntry(1, DexRegisterLocation::Kind::kConstant, -2);
+
+  size_t size = stream.ComputeNeededSize();
+  void* memory = arena.Alloc(size, kArenaAllocMisc);
+  MemoryRegion region(memory, size);
+  stream.FillIn(region);
+
+  CodeInfo ci(region);
+  // Verify first stack map.
+  StackMap sm0 = ci.GetStackMapAt(0);
+  DexRegisterMap dex_registers0 = ci.GetDexRegisterMapOf(sm0, number_of_dex_registers);
+  ASSERT_EQ(0, dex_registers0.GetMachineRegister(0, number_of_dex_registers));
+  ASSERT_EQ(-2, dex_registers0.GetConstant(1, number_of_dex_registers));
+
+  // Verify second stack map.
+  StackMap sm1 = ci.GetStackMapAt(1);
+  DexRegisterMap dex_registers1 = ci.GetDexRegisterMapOf(sm1, number_of_dex_registers);
+  ASSERT_EQ(0, dex_registers1.GetMachineRegister(0, number_of_dex_registers));
+  ASSERT_EQ(-2, dex_registers1.GetConstant(1, number_of_dex_registers));
+
+  // Verify third stack map.
+  StackMap sm2 = ci.GetStackMapAt(2);
+  DexRegisterMap dex_registers2 = ci.GetDexRegisterMapOf(sm2, number_of_dex_registers);
+  ASSERT_EQ(2, dex_registers2.GetMachineRegister(0, number_of_dex_registers));
+  ASSERT_EQ(-2, dex_registers2.GetConstant(1, number_of_dex_registers));
+
+  // Verify dex register map offsets.
+  ASSERT_EQ(sm0.GetDexRegisterMapOffset(ci), sm1.GetDexRegisterMapOffset(ci));
+  ASSERT_NE(sm0.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
+  ASSERT_NE(sm1.GetDexRegisterMapOffset(ci), sm2.GetDexRegisterMapOffset(ci));
+}
+
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 8730f52..dd0dba2 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -100,6 +100,10 @@
     return rm_;
   }
 
+  Register GetSecondRegister() const {
+    return rs_;
+  }
+
   enum Type {
     kUnknown = -1,
     kRegister,
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 6d0571e..a894319 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -825,10 +825,12 @@
   if (so.IsImmediate()) {
     // Check special cases.
     if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12))) {
-      if (opcode == SUB) {
-        thumb_opcode = 5U /* 0b0101 */;
-      } else {
-        thumb_opcode = 0;
+      if (!set_cc) {
+        if (opcode == SUB) {
+          thumb_opcode = 5U;
+        } else if (opcode == ADD) {
+          thumb_opcode = 0U;
+        }
       }
       uint32_t imm = so.GetImmediate();
 
@@ -836,13 +838,14 @@
       uint32_t imm3 = (imm >> 8) & 7U /* 0b111 */;
       uint32_t imm8 = imm & 0xff;
 
-      encoding = B31 | B30 | B29 | B28 | B25 |
-           thumb_opcode << 21 |
-           rn << 16 |
-           rd << 8 |
-           i << 26 |
-           imm3 << 12 |
-           imm8;
+      encoding = B31 | B30 | B29 | B28 |
+          (set_cc ? B20 : B25) |
+          thumb_opcode << 21 |
+          rn << 16 |
+          rd << 8 |
+          i << 26 |
+          imm3 << 12 |
+          imm8;
     } else {
       // Modified immediate.
       uint32_t imm = ModifiedImmediate(so.encodingThumb());
@@ -852,19 +855,19 @@
       }
       encoding = B31 | B30 | B29 | B28 |
           thumb_opcode << 21 |
-          (set_cc ? 1 : 0) << 20 |
+          (set_cc ? B20 : 0) |
           rn << 16 |
           rd << 8 |
           imm;
     }
   } else if (so.IsRegister()) {
-     // Register (possibly shifted)
-     encoding = B31 | B30 | B29 | B27 | B25 |
-         thumb_opcode << 21 |
-         (set_cc ? 1 : 0) << 20 |
-         rn << 16 |
-         rd << 8 |
-         so.encodingThumb();
+    // Register (possibly shifted)
+    encoding = B31 | B30 | B29 | B27 | B25 |
+        thumb_opcode << 21 |
+        (set_cc ? B20 : 0) |
+        rn << 16 |
+        rd << 8 |
+        so.encodingThumb();
   }
   Emit32(encoding);
 }
@@ -921,6 +924,8 @@
       use_immediate = true;
       immediate = so.GetImmediate();
     } else {
+      CHECK(!(so.IsRegister() && so.IsShift() && so.GetSecondRegister() != kNoRegister))
+          << "No register-shifted register instruction available in thumb";
       // Adjust rn and rd: only two registers will be emitted.
       switch (opcode) {
         case AND:
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index ebea9d4..813996b 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -227,4 +227,24 @@
   DriverStr(expected, "abs");
 }
 
+TEST_F(AssemblerThumb2Test, sub) {
+  __ subs(arm::R1, arm::R0, arm::ShifterOperand(42));
+  __ sub(arm::R1, arm::R0, arm::ShifterOperand(42));
+
+  const char* expected =
+      "subs r1, r0, #42\n"
+      "subw r1, r0, #42\n";
+  DriverStr(expected, "sub");
+}
+
+TEST_F(AssemblerThumb2Test, add) {
+  __ adds(arm::R1, arm::R0, arm::ShifterOperand(42));
+  __ add(arm::R1, arm::R0, arm::ShifterOperand(42));
+
+  const char* expected =
+      "adds r1, r0, #42\n"
+      "addw r1, r0, #42\n";
+  DriverStr(expected, "add");
+}
+
 }  // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 14bcd4b..daca971 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1453,7 +1453,9 @@
     std::string error_msg;
     const OatFile* oat_file = class_linker->FindOpenedOatFileFromOatLocation(oat_location);
     if (oat_file == nullptr) {
-      oat_file = OatFile::Open(oat_location, oat_location, nullptr, nullptr, false, &error_msg);
+      oat_file = OatFile::Open(oat_location, oat_location,
+                               nullptr, nullptr, false, nullptr,
+                               &error_msg);
       if (oat_file == nullptr) {
         os << "NOT FOUND: " << error_msg << "\n";
         return false;
@@ -2193,7 +2195,7 @@
                    std::ostream* os) {
   std::string error_msg;
   OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
-                                    &error_msg);
+                                    nullptr, &error_msg);
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
     return EXIT_FAILURE;
@@ -2209,7 +2211,7 @@
 static int SymbolizeOat(const char* oat_filename, std::string& output_name) {
   std::string error_msg;
   OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
-                                    &error_msg);
+                                    nullptr, &error_msg);
   if (oat_file == nullptr) {
     fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
     return EXIT_FAILURE;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index c8ede48..da39573 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -421,15 +421,26 @@
     }
   }
 
-  std::string GetBaseLocation() const {
-    size_t pos = location_.rfind(kMultiDexSeparator);
+  static std::string GetBaseLocation(const std::string& location) {
+    return GetBaseLocation(location.c_str());
+  }
+
+  // Returns the ':classes*.dex' part of the dex location. Returns an empty
+  // string if there is no multidex suffix for the given location.
+  // The kMultiDexSeparator is included in the returned suffix.
+  static std::string GetMultiDexSuffix(const std::string& location) {
+    size_t pos = location.rfind(kMultiDexSeparator);
     if (pos == std::string::npos) {
-      return location_;
+      return "";
     } else {
-      return location_.substr(0, pos);
+      return location.substr(pos);
     }
   }
 
+  std::string GetBaseLocation() const {
+    return GetBaseLocation(location_);
+  }
+
   // For DexFiles directly from .dex files, this is the checksum from the DexFile::Header.
   // For DexFiles opened from a zip files, this will be the ZipEntry CRC32 of classes.dex.
   uint32_t GetLocationChecksum() const {
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 7f5a181..09ef3ee 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -377,4 +377,13 @@
   ASSERT_EQ(0, unlink(dex_location_sym.c_str()));
 }
 
+TEST(DexFileUtilsTest, GetBaseLocationAndMultiDexSuffix) {
+  EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar"));
+  EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes2.dex"));
+  EXPECT_EQ("/foo/bar/baz.jar", DexFile::GetBaseLocation("/foo/bar/baz.jar:classes8.dex"));
+  EXPECT_EQ("", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar"));
+  EXPECT_EQ(":classes2.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes2.dex"));
+  EXPECT_EQ(":classes8.dex", DexFile::GetMultiDexSuffix("/foo/bar/baz.jar:classes8.dex"));
+}
+
 }  // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 14f770d..1fb3252 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -788,7 +788,8 @@
 
   OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
                                     image_header.GetOatFileBegin(),
-                                    !Runtime::Current()->IsAotCompiler(), error_msg);
+                                    !Runtime::Current()->IsAotCompiler(),
+                                    nullptr, error_msg);
   if (oat_file == NULL) {
     *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
                               oat_filename.c_str(), GetName(), error_msg->c_str());
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 356e3d2..69cb22d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -17,10 +17,11 @@
 #include "oat_file.h"
 
 #include <dlfcn.h>
-#include <sstream>
 #include <string.h>
 #include <unistd.h>
 
+#include <sstream>
+
 #include "base/bit_vector.h"
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
@@ -38,12 +39,33 @@
 
 namespace art {
 
+std::string OatFile::ResolveRelativeEncodedDexLocation(
+      const char* abs_dex_location, const std::string& rel_dex_location) {
+  if (abs_dex_location != nullptr && rel_dex_location[0] != '/') {
+    // Strip :classes<N>.dex used for secondary multidex files.
+    std::string base = DexFile::GetBaseLocation(rel_dex_location);
+    std::string multidex_suffix = DexFile::GetMultiDexSuffix(rel_dex_location);
+
+    // Check if the base is a suffix of the provided abs_dex_location.
+    std::string target_suffix = "/" + base;
+    std::string abs_location(abs_dex_location);
+    if (abs_location.size() > target_suffix.size()) {
+      size_t pos = abs_location.size() - target_suffix.size();
+      if (abs_location.compare(pos, std::string::npos, target_suffix) == 0) {
+        return abs_location + multidex_suffix;
+      }
+    }
+  }
+  return rel_dex_location;
+}
+
 void OatFile::CheckLocation(const std::string& location) {
   CHECK(!location.empty());
 }
 
 OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file,
                                   const std::string& location,
+                                  const char* abs_dex_location,
                                   std::string* error_msg) {
   std::unique_ptr<OatFile> oat_file(new OatFile(location, false));
   oat_file->elf_file_.reset(elf_file);
@@ -53,7 +75,7 @@
   oat_file->begin_ = elf_file->Begin() + offset;
   oat_file->end_ = elf_file->Begin() + size + offset;
   // Ignore the optional .bss section when opening non-executable.
-  return oat_file->Setup(error_msg) ? oat_file.release() : nullptr;
+  return oat_file->Setup(abs_dex_location, error_msg) ? oat_file.release() : nullptr;
 }
 
 OatFile* OatFile::Open(const std::string& filename,
@@ -61,6 +83,7 @@
                        uint8_t* requested_base,
                        uint8_t* oat_file_begin,
                        bool executable,
+                       const char* abs_dex_location,
                        std::string* error_msg) {
   CHECK(!filename.empty()) << location;
   CheckLocation(location);
@@ -80,7 +103,7 @@
     return nullptr;
   }
   ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
-                        error_msg));
+                        abs_dex_location, error_msg));
 
   // It would be nice to unlink here. But we might have opened the file created by the
   // ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
@@ -88,14 +111,18 @@
   return ret.release();
 }
 
-OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
+OatFile* OatFile::OpenWritable(File* file, const std::string& location,
+                               const char* abs_dex_location,
+                               std::string* error_msg) {
   CheckLocation(location);
-  return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
+  return OpenElfFile(file, location, nullptr, nullptr, true, false, abs_dex_location, error_msg);
 }
 
-OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
+OatFile* OatFile::OpenReadable(File* file, const std::string& location,
+                               const char* abs_dex_location,
+                               std::string* error_msg) {
   CheckLocation(location);
-  return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
+  return OpenElfFile(file, location, nullptr, nullptr, false, false, abs_dex_location, error_msg);
 }
 
 OatFile* OatFile::OpenElfFile(File* file,
@@ -104,10 +131,11 @@
                               uint8_t* oat_file_begin,
                               bool writable,
                               bool executable,
+                              const char* abs_dex_location,
                               std::string* error_msg) {
   std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
   bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
-                                       error_msg);
+                                       abs_dex_location, error_msg);
   if (!success) {
     CHECK(!error_msg->empty());
     return nullptr;
@@ -131,6 +159,7 @@
 
 bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin,
                           bool writable, bool executable,
+                          const char* abs_dex_location,
                           std::string* error_msg) {
   // TODO: rename requested_base to oat_data_begin
   elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
@@ -180,10 +209,10 @@
     bss_end_ += sizeof(uint32_t);
   }
 
-  return Setup(error_msg);
+  return Setup(abs_dex_location, error_msg);
 }
 
-bool OatFile::Setup(std::string* error_msg) {
+bool OatFile::Setup(const char* abs_dex_location, std::string* error_msg) {
   if (!GetOatHeader().IsValid()) {
     std::string cause = GetOatHeader().GetValidationErrorMessage();
     *error_msg = StringPrintf("Invalid oat header for '%s': %s", GetLocation().c_str(),
@@ -230,7 +259,9 @@
       return false;
     }
 
-    std::string dex_file_location(dex_file_location_data, dex_file_location_size);
+    std::string dex_file_location = ResolveRelativeEncodedDexLocation(
+        abs_dex_location,
+        std::string(dex_file_location_data, dex_file_location_size));
 
     uint32_t dex_file_checksum = *reinterpret_cast<const uint32_t*>(oat);
     oat += sizeof(dex_file_checksum);
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 564185c..51952f3 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -43,14 +43,18 @@
   // Opens an oat file contained within the given elf file. This is always opened as
   // non-executable at the moment.
   static OatFile* OpenWithElfFile(ElfFile* elf_file, const std::string& location,
+                                  const char* abs_dex_location,
                                   std::string* error_msg);
   // Open an oat file. Returns NULL on failure.  Requested base can
   // optionally be used to request where the file should be loaded.
+  // See the ResolveRelativeEncodedDexLocation for a description of how the
+  // abs_dex_location argument is used.
   static OatFile* Open(const std::string& filename,
                        const std::string& location,
                        uint8_t* requested_base,
                        uint8_t* oat_file_begin,
                        bool executable,
+                       const char* abs_dex_location,
                        std::string* error_msg);
 
   // Open an oat file from an already opened File.
@@ -58,9 +62,13 @@
   // where relocations may be required. Currently used from
   // ImageWriter which wants to open a writable version from an existing
   // file descriptor for patching.
-  static OatFile* OpenWritable(File* file, const std::string& location, std::string* error_msg);
+  static OatFile* OpenWritable(File* file, const std::string& location,
+                               const char* abs_dex_location,
+                               std::string* error_msg);
   // Opens an oat file from an already opened File. Maps it PROT_READ, MAP_PRIVATE.
-  static OatFile* OpenReadable(File* file, const std::string& location, std::string* error_msg);
+  static OatFile* OpenReadable(File* file, const std::string& location,
+                               const char* abs_dex_location,
+                               std::string* error_msg);
 
   ~OatFile();
 
@@ -279,6 +287,18 @@
   const uint8_t* BssBegin() const;
   const uint8_t* BssEnd() const;
 
+  // Returns the absolute dex location for the encoded relative dex location.
+  //
+  // If not nullptr, abs_dex_location is used to resolve the absolute dex
+  // location of relative dex locations encoded in the oat file.
+  // For example, given absolute location "/data/app/foo/base.apk", encoded
+  // dex locations "base.apk", "base.apk:classes2.dex", etc. would be resolved
+  // to "/data/app/foo/base.apk", "/data/app/foo/base.apk:classes2.dex", etc.
+  // Relative encoded dex locations that don't match the given abs_dex_location
+  // are left unchanged.
+  static std::string ResolveRelativeEncodedDexLocation(
+      const char* abs_dex_location, const std::string& rel_dex_location);
+
  private:
   static void CheckLocation(const std::string& location);
 
@@ -288,14 +308,17 @@
                               uint8_t* oat_file_begin,  // Override base if not null
                               bool writable,
                               bool executable,
+                              const char* abs_dex_location,
                               std::string* error_msg);
 
   explicit OatFile(const std::string& filename, bool executable);
   bool ElfFileOpen(File* file, uint8_t* requested_base,
                    uint8_t* oat_file_begin,  // Override where the file is loaded to if not null
                    bool writable, bool executable,
+                   const char* abs_dex_location,
                    std::string* error_msg);
-  bool Setup(std::string* error_msg);
+
+  bool Setup(const char* abs_dex_location, std::string* error_msg);
 
   // The oat file name.
   //
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f87fa4f..9a17b01 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -850,7 +850,7 @@
       std::string error_msg;
       cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
             odex_file_name.c_str(), nullptr, nullptr, load_executable_,
-            &error_msg));
+            dex_location_, &error_msg));
       if (cached_odex_file_.get() == nullptr) {
         VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
           << odex_file_name << ": " << error_msg;
@@ -875,7 +875,8 @@
       const std::string& oat_file_name = *OatFileName();
       std::string error_msg;
       cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
-            oat_file_name.c_str(), nullptr, nullptr, load_executable_, &error_msg));
+            oat_file_name.c_str(), nullptr, nullptr, load_executable_,
+            dex_location_, &error_msg));
       if (cached_oat_file_.get() == nullptr) {
         VLOG(oat) << "OatFileAssistant test for existing oat file "
           << oat_file_name << ": " << error_msg;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index be8652c..41dc2d7 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -326,12 +326,43 @@
   GenerateOatForTest(dex_location.c_str());
 
   // Verify we can load both dex files.
-  OatFileAssistant executable_oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-  std::unique_ptr<OatFile> oat_file = executable_oat_file_assistant.GetBestOatFile();
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
   EXPECT_TRUE(oat_file->IsExecutable());
   std::vector<std::unique_ptr<const DexFile>> dex_files;
-  dex_files = executable_oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+  EXPECT_EQ(2u, dex_files.size());
+}
+
+// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
+// encoded dex locations.
+// Expect: The oat file status is kUpToDate.
+TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
+  std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
+  std::string oat_location = GetISADir() + "/RelativeEncodedDexLocation.oat";
+
+  // Create the dex file
+  Copy(GetMultiDexSrc1(), dex_location);
+
+  // Create the oat file with relative encoded dex location.
+  std::vector<std::string> args;
+  args.push_back("--dex-file=" + dex_location);
+  args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar"));
+  args.push_back("--oat-file=" + oat_location);
+
+  std::string error_msg;
+  ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+
+  // Verify we can load both dex files.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(),
+                                      oat_location.c_str(),
+                                      kRuntimeISA, true);
+  std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+  ASSERT_TRUE(oat_file.get() != nullptr);
+  EXPECT_TRUE(oat_file->IsExecutable());
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
   EXPECT_EQ(2u, dex_files.size());
 }
 
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
new file mode 100644
index 0000000..f2213e9
--- /dev/null
+++ b/runtime/oat_file_test.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "oat_file.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+TEST(OatFileTest, ResolveRelativeEncodedDexLocation) {
+  EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        nullptr, "/data/app/foo/base.apk"));
+
+  EXPECT_EQ(std::string("/system/framework/base.apk"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        "/data/app/foo/base.apk", "/system/framework/base.apk"));
+
+  EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        "/data/app/foo/base.apk", "base.apk"));
+
+  EXPECT_EQ(std::string("/data/app/foo/base.apk"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        "/data/app/foo/base.apk", "foo/base.apk"));
+
+  EXPECT_EQ(std::string("/data/app/foo/base.apk:classes2.dex"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        "/data/app/foo/base.apk", "base.apk:classes2.dex"));
+
+  EXPECT_EQ(std::string("/data/app/foo/base.apk:classes11.dex"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        "/data/app/foo/base.apk", "base.apk:classes11.dex"));
+
+  EXPECT_EQ(std::string("base.apk"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        "/data/app/foo/sludge.apk", "base.apk"));
+
+  EXPECT_EQ(std::string("o/base.apk"),
+      OatFile::ResolveRelativeEncodedDexLocation(
+        "/data/app/foo/base.apk", "o/base.apk"));
+}
+
+}  // namespace art
diff --git a/runtime/primitive.h b/runtime/primitive.h
index d11f1e9..32bfdaf 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -153,8 +153,8 @@
   }
 
   static bool IsIntegralType(Type type) {
-    // Java language does not allow treating boolean as an integral type but our
-    // bit representation makes it safe.
+    // The Java language does not allow treating boolean as an integral type but
+    // our bit representation makes it safe.
     switch (type) {
       case kPrimBoolean:
       case kPrimByte:
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2dacfe2..9ca00b1 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -688,7 +688,7 @@
     return false;
   }
   std::unique_ptr<OatFile> oat_file(OatFile::OpenWithElfFile(elf_file.release(), oat_location,
-                                                             &error_msg));
+                                                             nullptr, &error_msg));
   if (oat_file.get() == nullptr) {
     LOG(INFO) << "Unable to use '" << oat_filename << "' because " << error_msg;
     return false;
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 629fc9a..6ec7cc8 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -212,6 +212,14 @@
   // Get the actual kind of the location.
   Kind GetInternalKind() const { return kind_; }
 
+  bool operator==(DexRegisterLocation other) const {
+    return kind_ == other.kind_ && value_ == other.value_;
+  }
+
+  bool operator!=(DexRegisterLocation other) const {
+    return !(*this == other);
+  }
+
  private:
   Kind kind_;
   int32_t value_;
diff --git a/test/463-checker-boolean-simplifier/src/Main.java b/test/463-checker-boolean-simplifier/src/Main.java
index 25f58b4..efe0d3f 100644
--- a/test/463-checker-boolean-simplifier/src/Main.java
+++ b/test/463-checker-boolean-simplifier/src/Main.java
@@ -116,7 +116,7 @@
   // CHECK-DAG:                       Return [ [[Cond]] ]
 
   public static boolean LessThan(int x, int y) {
-    return x < y;
+    return (x < y) ? true : false;
   }
 
   /*