Merge "dex2oat: Pack likely-dirty objects together when generating the boot image"
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index 591d461..9c84bc1 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -92,16 +92,29 @@
       break;
     case kRegister:
       if (is_shift_) {
+        uint32_t shift_type;
+        switch (shift_) {
+          case arm::Shift::ROR:
+            shift_type = static_cast<uint32_t>(shift_);
+            CHECK_NE(immed_, 0U);
+            break;
+          case arm::Shift::RRX:
+            shift_type = static_cast<uint32_t>(arm::Shift::ROR);  // Same encoding as ROR.
+            CHECK_EQ(immed_, 0U);
+            break;
+          default:
+            shift_type = static_cast<uint32_t>(shift_);
+        }
         // Shifted immediate or register.
         if (rs_ == kNoRegister) {
           // Immediate shift.
           return immed_ << kShiftImmShift |
-                          static_cast<uint32_t>(shift_) << kShiftShift |
+                          shift_type << kShiftShift |
                           static_cast<uint32_t>(rm_);
         } else {
           // Register shift.
           return static_cast<uint32_t>(rs_) << kShiftRegisterShift |
-              static_cast<uint32_t>(shift_) << kShiftShift | (1 << 4) |
+              shift_type << kShiftShift | (1 << 4) |
               static_cast<uint32_t>(rm_);
         }
       } else {
diff --git a/compiler/utils/arm/assembler_arm32_test.cc b/compiler/utils/arm/assembler_arm32_test.cc
index 277a9eb..fe12a22 100644
--- a/compiler/utils/arm/assembler_arm32_test.cc
+++ b/compiler/utils/arm/assembler_arm32_test.cc
@@ -16,49 +16,208 @@
 
 #include "assembler_arm32.h"
 
+#include <functional>
+#include <type_traits>
+
+#include "base/macros.h"
 #include "base/stl_util.h"
-#include "utils/assembler_test.h"
+#include "utils/arm/assembler_arm_test.h"
 
 namespace art {
 
-class AssemblerArm32Test : public AssemblerTest<arm::Arm32Assembler,
-                                                arm::Register, arm::SRegister,
-                                                uint32_t> {
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using std::placeholders::_5;
+
+// To speed up tests, don't use all register combinations.
+static constexpr bool kUseSparseRegisterList = true;
+
+// To speed up tests, don't use all condition codes.
+static constexpr bool kUseSparseConditionList = true;
+
+// To speed up tests, don't use all shift immediates.
+static constexpr bool kUseSparseShiftImmediates = true;
+
+class AssemblerArm32Test : public AssemblerArmTest<arm::Arm32Assembler,
+                                                   arm::Register, arm::SRegister,
+                                                   uint32_t, arm::ShifterOperand, arm::Condition> {
  protected:
   std::string GetArchitectureString() OVERRIDE {
     return "arm";
   }
 
+  std::string GetAssemblerParameters() OVERRIDE {
+    return " -march=armv7ve";  // Arm-v7a with virtualization extension (means we have sdiv).
+  }
+
+  const char* GetAssemblyHeader() OVERRIDE {
+    return kArm32AssemblyHeader;
+  }
+
   std::string GetDisassembleParameters() OVERRIDE {
     return " -D -bbinary -marm --no-show-raw-insn";
   }
 
   void SetUpHelpers() OVERRIDE {
     if (registers_.size() == 0) {
-      registers_.insert(end(registers_),
-                        {  // NOLINT(whitespace/braces)
-                          new arm::Register(arm::R0),
-                          new arm::Register(arm::R1),
-                          new arm::Register(arm::R2),
-                          new arm::Register(arm::R3),
-                          new arm::Register(arm::R4),
-                          new arm::Register(arm::R5),
-                          new arm::Register(arm::R6),
-                          new arm::Register(arm::R7),
-                          new arm::Register(arm::R8),
-                          new arm::Register(arm::R9),
-                          new arm::Register(arm::R10),
-                          new arm::Register(arm::R11),
-                          new arm::Register(arm::R12),
-                          new arm::Register(arm::R13),
-                          new arm::Register(arm::R14),
-                          new arm::Register(arm::R15)
-                        });
+      if (kUseSparseRegisterList) {
+        registers_.insert(end(registers_),
+                          {  // NOLINT(whitespace/braces)
+                              new arm::Register(arm::R0),
+                              new arm::Register(arm::R1),
+                              new arm::Register(arm::R4),
+                              new arm::Register(arm::R8),
+                              new arm::Register(arm::R11),
+                              new arm::Register(arm::R12),
+                              new arm::Register(arm::R13),
+                              new arm::Register(arm::R14),
+                              new arm::Register(arm::R15)
+                          });
+      } else {
+        registers_.insert(end(registers_),
+                          {  // NOLINT(whitespace/braces)
+                              new arm::Register(arm::R0),
+                              new arm::Register(arm::R1),
+                              new arm::Register(arm::R2),
+                              new arm::Register(arm::R3),
+                              new arm::Register(arm::R4),
+                              new arm::Register(arm::R5),
+                              new arm::Register(arm::R6),
+                              new arm::Register(arm::R7),
+                              new arm::Register(arm::R8),
+                              new arm::Register(arm::R9),
+                              new arm::Register(arm::R10),
+                              new arm::Register(arm::R11),
+                              new arm::Register(arm::R12),
+                              new arm::Register(arm::R13),
+                              new arm::Register(arm::R14),
+                              new arm::Register(arm::R15)
+                          });
+      }
+    }
+
+    if (!kUseSparseConditionList) {
+      conditions_.push_back(arm::Condition::EQ);
+      conditions_.push_back(arm::Condition::NE);
+      conditions_.push_back(arm::Condition::CS);
+      conditions_.push_back(arm::Condition::CC);
+      conditions_.push_back(arm::Condition::MI);
+      conditions_.push_back(arm::Condition::PL);
+      conditions_.push_back(arm::Condition::VS);
+      conditions_.push_back(arm::Condition::VC);
+      conditions_.push_back(arm::Condition::HI);
+      conditions_.push_back(arm::Condition::LS);
+      conditions_.push_back(arm::Condition::GE);
+      conditions_.push_back(arm::Condition::LT);
+      conditions_.push_back(arm::Condition::GT);
+      conditions_.push_back(arm::Condition::LE);
+      conditions_.push_back(arm::Condition::AL);
+    } else {
+      conditions_.push_back(arm::Condition::EQ);
+      conditions_.push_back(arm::Condition::NE);
+      conditions_.push_back(arm::Condition::CC);
+      conditions_.push_back(arm::Condition::VC);
+      conditions_.push_back(arm::Condition::HI);
+      conditions_.push_back(arm::Condition::LT);
+      conditions_.push_back(arm::Condition::AL);
+    }
+
+    shifter_operands_.push_back(arm::ShifterOperand(0));
+    shifter_operands_.push_back(arm::ShifterOperand(1));
+    shifter_operands_.push_back(arm::ShifterOperand(2));
+    shifter_operands_.push_back(arm::ShifterOperand(3));
+    shifter_operands_.push_back(arm::ShifterOperand(4));
+    shifter_operands_.push_back(arm::ShifterOperand(5));
+    shifter_operands_.push_back(arm::ShifterOperand(127));
+    shifter_operands_.push_back(arm::ShifterOperand(128));
+    shifter_operands_.push_back(arm::ShifterOperand(254));
+    shifter_operands_.push_back(arm::ShifterOperand(255));
+
+    if (!kUseSparseRegisterList) {
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R0));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R1));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R2));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R3));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R4));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R5));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R6));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R7));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R8));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R9));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R10));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R11));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R12));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R13));
+    } else {
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R0));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R1));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R4));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R8));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R11));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R12));
+      shifter_operands_.push_back(arm::ShifterOperand(arm::R13));
+    }
+
+    std::vector<arm::Shift> shifts {
+      arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR, arm::Shift::ROR, arm::Shift::RRX
+    };
+
+    // ShifterOperands of form "reg shift-type imm."
+    for (arm::Shift shift : shifts) {
+      for (arm::Register* reg : registers_) {  // Note: this will pick up the sparse set.
+        if (*reg == arm::R15) {  // Skip PC.
+          continue;
+        }
+        if (shift != arm::Shift::RRX) {
+          if (!kUseSparseShiftImmediates) {
+            for (uint32_t imm = 1; imm < 32; ++imm) {
+              shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, imm));
+            }
+          } else {
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 1));
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 2));
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 3));
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 7));
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 15));
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 16));
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 30));
+            shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 31));
+          }
+        } else {
+          // RRX doesn't have an immediate.
+          shifter_operands_.push_back(arm::ShifterOperand(*reg, shift, 0));
+        }
+      }
     }
   }
 
+  std::vector<arm::ShifterOperand> CreateRegisterShifts(std::vector<arm::Register*>& base_regs,
+                                                        int32_t shift_min, int32_t shift_max) {
+    std::vector<arm::ShifterOperand> res;
+    static constexpr arm::Shift kShifts[] = { arm::Shift::LSL, arm::Shift::LSR, arm::Shift::ASR,
+                                              arm::Shift::ROR };
+
+    for (arm::Shift shift : kShifts) {
+      for (arm::Register* reg : base_regs) {
+        // Take the min, the max, and three values in between.
+        res.push_back(arm::ShifterOperand(*reg, shift, shift_min));
+        if (shift_min != shift_max) {
+          res.push_back(arm::ShifterOperand(*reg, shift, shift_max));
+          int32_t middle = (shift_min + shift_max) / 2;
+          res.push_back(arm::ShifterOperand(*reg, shift, middle));
+          res.push_back(arm::ShifterOperand(*reg, shift, middle - 1));
+          res.push_back(arm::ShifterOperand(*reg, shift, middle + 1));
+        }
+      }
+    }
+
+    return res;
+  }
+
   void TearDown() OVERRIDE {
-    AssemblerTest::TearDown();
+    AssemblerArmTest::TearDown();
     STLDeleteElements(&registers_);
   }
 
@@ -70,8 +229,280 @@
     return imm_value;
   }
 
+  std::vector<arm::Condition>& GetConditions() OVERRIDE {
+    return conditions_;
+  }
+
+  std::string GetConditionString(arm::Condition c) OVERRIDE {
+    std::ostringstream oss;
+    oss << c;
+    return oss.str();
+  }
+
+  arm::Register GetPCRegister() OVERRIDE {
+    return arm::R15;
+  }
+
+  std::vector<arm::ShifterOperand>& GetShiftOperands() OVERRIDE {
+    return shifter_operands_;
+  }
+
+  std::string GetShiftString(arm::ShifterOperand sop) OVERRIDE {
+    std::ostringstream oss;
+    if (sop.IsShift()) {
+      // Not a rotate...
+      if (sop.GetShift() == arm::Shift::RRX) {
+        oss << sop.GetRegister() << ", " << sop.GetShift();
+      } else {
+        oss << sop.GetRegister() << ", " << sop.GetShift() << " #" << sop.GetImmediate();
+      }
+    } else if (sop.IsRegister()) {
+      oss << sop.GetRegister();
+    } else {
+      CHECK(sop.IsImmediate());
+      oss << "#" << sop.GetImmediate();
+    }
+    return oss.str();
+  }
+
+  static const char* GetRegTokenFromDepth(int depth) {
+    switch (depth) {
+      case 0:
+        return Base::REG1_TOKEN;
+      case 1:
+        return Base::REG2_TOKEN;
+      case 2:
+        return REG3_TOKEN;
+      case 3:
+        return REG4_TOKEN;
+      default:
+        LOG(FATAL) << "Depth problem.";
+        UNREACHABLE();
+    }
+  }
+
+  void ExecuteAndPrint(std::function<void()> f, std::string fmt, std::ostringstream& oss) {
+    if (first_) {
+      first_ = false;
+    } else {
+      oss << "\n";
+    }
+    oss << fmt;
+
+    f();
+  }
+
+  void TemplateHelper(std::function<void(arm::Register)> f, int depth ATTRIBUTE_UNUSED,
+                      bool without_pc,
+                      std::string fmt, std::ostringstream& oss) {
+    std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters();
+    for (auto reg : registers) {
+      std::string after_reg = fmt;
+
+      std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg);
+      size_t reg_index;
+      const char* reg_token = GetRegTokenFromDepth(depth);
+
+      while ((reg_index = after_reg.find(reg_token)) != std::string::npos) {
+        after_reg.replace(reg_index, strlen(reg_token), reg_string);
+      }
+
+      ExecuteAndPrint([&] () { f(*reg); }, after_reg, oss);
+    }
+  }
+
+  void TemplateHelper(std::function<void(const arm::ShifterOperand&)> f, int depth ATTRIBUTE_UNUSED,
+                      bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) {
+    for (const arm::ShifterOperand& shift : GetShiftOperands()) {
+      std::string after_shift = fmt;
+
+      std::string shift_string = GetShiftString(shift);
+      size_t shift_index;
+      while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) {
+        after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+      }
+
+      ExecuteAndPrint([&] () { f(shift); }, after_shift, oss);
+    }
+  }
+
+  void TemplateHelper(std::function<void(arm::Condition)> f, int depth ATTRIBUTE_UNUSED,
+                      bool without_pc ATTRIBUTE_UNUSED, std::string fmt, std::ostringstream& oss) {
+    for (arm::Condition c : GetConditions()) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      ExecuteAndPrint([&] () { f(c); }, after_cond, oss);
+    }
+  }
+
+  template <typename... Args>
+  void TemplateHelper(std::function<void(arm::Register, Args...)> f, int depth, bool without_pc,
+                      std::string fmt, std::ostringstream& oss) {
+    std::vector<arm::Register*> registers = without_pc ? GetRegistersWithoutPC() : GetRegisters();
+    for (auto reg : registers) {
+      std::string after_reg = fmt;
+
+      std::string reg_string = GetRegName<RegisterView::kUsePrimaryName>(*reg);
+      size_t reg_index;
+      const char* reg_token = GetRegTokenFromDepth(depth);
+
+      while ((reg_index = after_reg.find(reg_token)) != std::string::npos) {
+        after_reg.replace(reg_index, strlen(reg_token), reg_string);
+      }
+
+      auto lambda = [&] (Args... args) { f(*reg, args...); };  // NOLINT [readability/braces] [4]
+      TemplateHelper(std::function<void(Args...)>(lambda), depth + 1, without_pc,
+          after_reg, oss);
+    }
+  }
+
+  template <typename... Args>
+  void TemplateHelper(std::function<void(const arm::ShifterOperand&, Args...)> f, int depth,
+                      bool without_pc, std::string fmt, std::ostringstream& oss) {
+    for (const arm::ShifterOperand& shift : GetShiftOperands()) {
+      std::string after_shift = fmt;
+
+      std::string shift_string = GetShiftString(shift);
+      size_t shift_index;
+      while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) {
+        after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+      }
+
+      auto lambda = [&] (Args... args) { f(shift, args...); };  // NOLINT [readability/braces] [4]
+      TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc,
+          after_shift, oss);
+    }
+  }
+
+  template <typename... Args>
+  void TemplateHelper(std::function<void(arm::Condition, Args...)> f, int depth, bool without_pc,
+                      std::string fmt, std::ostringstream& oss) {
+    for (arm::Condition c : GetConditions()) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      auto lambda = [&] (Args... args) { f(c, args...); };  // NOLINT [readability/braces] [4]
+      TemplateHelper(std::function<void(Args...)>(lambda), depth, without_pc,
+          after_cond, oss);
+    }
+  }
+
+  template <typename T1, typename T2>
+  std::function<void(T1, T2)> GetBoundFunction2(void (arm::Arm32Assembler::*f)(T1, T2)) {
+    return std::bind(f, GetAssembler(), _1, _2);
+  }
+
+  template <typename T1, typename T2, typename T3>
+  std::function<void(T1, T2, T3)> GetBoundFunction3(void (arm::Arm32Assembler::*f)(T1, T2, T3)) {
+    return std::bind(f, GetAssembler(), _1, _2, _3);
+  }
+
+  template <typename T1, typename T2, typename T3, typename T4>
+  std::function<void(T1, T2, T3, T4)> GetBoundFunction4(
+      void (arm::Arm32Assembler::*f)(T1, T2, T3, T4)) {
+    return std::bind(f, GetAssembler(), _1, _2, _3, _4);
+  }
+
+  template <typename T1, typename T2, typename T3, typename T4, typename T5>
+  std::function<void(T1, T2, T3, T4, T5)> GetBoundFunction5(
+      void (arm::Arm32Assembler::*f)(T1, T2, T3, T4, T5)) {
+    return std::bind(f, GetAssembler(), _1, _2, _3, _4, _5);
+  }
+
+  template <typename... Args>
+  void GenericTemplateHelper(std::function<void(Args...)> f, bool without_pc,
+                             std::string fmt, std::string test_name) {
+    WarnOnCombinations(CountHelper<Args...>(without_pc));
+
+    std::ostringstream oss;
+
+    TemplateHelper(f, 0, without_pc, fmt, oss);
+
+    oss << "\n";  // Trailing newline.
+
+    DriverStr(oss.str(), test_name);
+  }
+
+  template <typename... Args>
+  void T2Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+                std::string test_name) {
+    GenericTemplateHelper(GetBoundFunction2(f), without_pc, fmt, test_name);
+  }
+
+  template <typename... Args>
+  void T3Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+      std::string test_name) {
+    GenericTemplateHelper(GetBoundFunction3(f), without_pc, fmt, test_name);
+  }
+
+  template <typename... Args>
+  void T4Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+      std::string test_name) {
+    GenericTemplateHelper(GetBoundFunction4(f), without_pc, fmt, test_name);
+  }
+
+  template <typename... Args>
+  void T5Helper(void (arm::Arm32Assembler::*f)(Args...), bool without_pc, std::string fmt,
+      std::string test_name) {
+    GenericTemplateHelper(GetBoundFunction5(f), without_pc, fmt, test_name);
+  }
+
  private:
+  template <typename T>
+  size_t CountHelper(bool without_pc) {
+    size_t tmp;
+    if (std::is_same<T, arm::Register>::value) {
+      tmp = GetRegisters().size();
+      if (without_pc) {
+        tmp--;;  // Approximation...
+      }
+      return tmp;
+    } else if (std::is_same<T, const arm::ShifterOperand&>::value) {
+      return GetShiftOperands().size();
+    } else if (std::is_same<T, arm::Condition>::value) {
+      return GetConditions().size();
+    } else {
+      LOG(WARNING) << "Unknown type while counting.";
+      return 1;
+    }
+  }
+
+  template <typename T1, typename T2, typename... Args>
+  size_t CountHelper(bool without_pc) {
+    size_t tmp;
+    if (std::is_same<T1, arm::Register>::value) {
+      tmp = GetRegisters().size();
+      if (without_pc) {
+        tmp--;;  // Approximation...
+      }
+    } else if (std::is_same<T1, const arm::ShifterOperand&>::value) {
+      tmp =  GetShiftOperands().size();
+    } else if (std::is_same<T1, arm::Condition>::value) {
+      tmp = GetConditions().size();
+    } else {
+      LOG(WARNING) << "Unknown type while counting.";
+      tmp = 1;
+    }
+    size_t rec = CountHelper<T2, Args...>(without_pc);
+    return rec * tmp;
+  }
+
+  bool first_;
+
+  static constexpr const char* kArm32AssemblyHeader = ".arm\n";
+
   std::vector<arm::Register*> registers_;
+  std::vector<arm::Condition> conditions_;
+  std::vector<arm::ShifterOperand> shifter_operands_;
 };
 
 
@@ -79,77 +510,181 @@
   EXPECT_TRUE(CheckTools());
 }
 
-
 TEST_F(AssemblerArm32Test, Sbfx) {
-  GetAssembler()->sbfx(arm::R0, arm::R1, 0, 1);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 0, 8);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 0, 16);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 0, 32);
+  std::vector<std::pair<uint32_t, uint32_t>> immediates;
+  immediates.push_back({0, 1});
+  immediates.push_back({0, 8});
+  immediates.push_back({0, 15});
+  immediates.push_back({0, 16});
+  immediates.push_back({0, 31});
+  immediates.push_back({0, 32});
 
-  GetAssembler()->sbfx(arm::R0, arm::R1, 8, 1);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 8, 8);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 8, 16);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 8, 24);
+  immediates.push_back({1, 1});
+  immediates.push_back({1, 15});
+  immediates.push_back({1, 31});
 
-  GetAssembler()->sbfx(arm::R0, arm::R1, 16, 1);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 16, 8);
-  GetAssembler()->sbfx(arm::R0, arm::R1, 16, 16);
+  immediates.push_back({8, 1});
+  immediates.push_back({8, 15});
+  immediates.push_back({8, 16});
+  immediates.push_back({8, 24});
 
-  GetAssembler()->sbfx(arm::R0, arm::R1, 31, 1);
+  immediates.push_back({31, 1});
 
-  const char* expected =
-      "sbfx r0, r1, #0, #1\n"
-      "sbfx r0, r1, #0, #8\n"
-      "sbfx r0, r1, #0, #16\n"
-      "sbfx r0, r1, #0, #32\n"
-
-      "sbfx r0, r1, #8, #1\n"
-      "sbfx r0, r1, #8, #8\n"
-      "sbfx r0, r1, #8, #16\n"
-      "sbfx r0, r1, #8, #24\n"
-
-      "sbfx r0, r1, #16, #1\n"
-      "sbfx r0, r1, #16, #8\n"
-      "sbfx r0, r1, #16, #16\n"
-
-      "sbfx r0, r1, #31, #1\n";
-  DriverStr(expected, "sbfx");
+  DriverStr(RepeatRRiiC(&arm::Arm32Assembler::sbfx, immediates,
+                        "sbfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "sbfx");
 }
 
 TEST_F(AssemblerArm32Test, Ubfx) {
-  GetAssembler()->ubfx(arm::R0, arm::R1, 0, 1);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 0, 8);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 0, 16);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 0, 32);
+  std::vector<std::pair<uint32_t, uint32_t>> immediates;
+  immediates.push_back({0, 1});
+  immediates.push_back({0, 8});
+  immediates.push_back({0, 15});
+  immediates.push_back({0, 16});
+  immediates.push_back({0, 31});
+  immediates.push_back({0, 32});
 
-  GetAssembler()->ubfx(arm::R0, arm::R1, 8, 1);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 8, 8);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 8, 16);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 8, 24);
+  immediates.push_back({1, 1});
+  immediates.push_back({1, 15});
+  immediates.push_back({1, 31});
 
-  GetAssembler()->ubfx(arm::R0, arm::R1, 16, 1);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 16, 8);
-  GetAssembler()->ubfx(arm::R0, arm::R1, 16, 16);
+  immediates.push_back({8, 1});
+  immediates.push_back({8, 15});
+  immediates.push_back({8, 16});
+  immediates.push_back({8, 24});
 
-  GetAssembler()->ubfx(arm::R0, arm::R1, 31, 1);
+  immediates.push_back({31, 1});
 
-  const char* expected =
-      "ubfx r0, r1, #0, #1\n"
-      "ubfx r0, r1, #0, #8\n"
-      "ubfx r0, r1, #0, #16\n"
-      "ubfx r0, r1, #0, #32\n"
+  DriverStr(RepeatRRiiC(&arm::Arm32Assembler::ubfx, immediates,
+                        "ubfx{cond} {reg1}, {reg2}, #{imm1}, #{imm2}"), "ubfx");
+}
 
-      "ubfx r0, r1, #8, #1\n"
-      "ubfx r0, r1, #8, #8\n"
-      "ubfx r0, r1, #8, #16\n"
-      "ubfx r0, r1, #8, #24\n"
+TEST_F(AssemblerArm32Test, Mul) {
+  T4Helper(&arm::Arm32Assembler::mul, true, "mul{cond} {reg1}, {reg2}, {reg3}", "mul");
+}
 
-      "ubfx r0, r1, #16, #1\n"
-      "ubfx r0, r1, #16, #8\n"
-      "ubfx r0, r1, #16, #16\n"
+TEST_F(AssemblerArm32Test, Mla) {
+  T5Helper(&arm::Arm32Assembler::mla, true, "mla{cond} {reg1}, {reg2}, {reg3}, {reg4}", "mul");
+}
 
-      "ubfx r0, r1, #31, #1\n";
-  DriverStr(expected, "ubfx");
+/* TODO: Needs support to filter out register combinations, as rdhi must not be equal to rdlo.
+TEST_F(AssemblerArm32Test, Umull) {
+  T5Helper(&arm::Arm32Assembler::umull, true, "umull{cond} {reg1}, {reg2}, {reg3}, {reg4}",
+           "umull");
+}
+*/
+
+TEST_F(AssemblerArm32Test, Sdiv) {
+  T4Helper(&arm::Arm32Assembler::sdiv, true, "sdiv{cond} {reg1}, {reg2}, {reg3}", "sdiv");
+}
+
+TEST_F(AssemblerArm32Test, Udiv) {
+  T4Helper(&arm::Arm32Assembler::udiv, true, "udiv{cond} {reg1}, {reg2}, {reg3}", "udiv");
+}
+
+TEST_F(AssemblerArm32Test, And) {
+  T4Helper(&arm::Arm32Assembler::and_, true, "and{cond} {reg1}, {reg2}, {shift}", "and");
+}
+
+TEST_F(AssemblerArm32Test, Eor) {
+  T4Helper(&arm::Arm32Assembler::eor, true, "eor{cond} {reg1}, {reg2}, {shift}", "eor");
+}
+
+TEST_F(AssemblerArm32Test, Orr) {
+  T4Helper(&arm::Arm32Assembler::orr, true, "orr{cond} {reg1}, {reg2}, {shift}", "orr");
+}
+
+TEST_F(AssemblerArm32Test, Orrs) {
+  T4Helper(&arm::Arm32Assembler::orrs, true, "orr{cond}s {reg1}, {reg2}, {shift}", "orrs");
+}
+
+TEST_F(AssemblerArm32Test, Bic) {
+  T4Helper(&arm::Arm32Assembler::bic, true, "bic{cond} {reg1}, {reg2}, {shift}", "bic");
+}
+
+TEST_F(AssemblerArm32Test, Mov) {
+  T3Helper(&arm::Arm32Assembler::mov, true, "mov{cond} {reg1}, {shift}", "mov");
+}
+
+TEST_F(AssemblerArm32Test, Movs) {
+  T3Helper(&arm::Arm32Assembler::movs, true, "mov{cond}s {reg1}, {shift}", "movs");
+}
+
+TEST_F(AssemblerArm32Test, Mvn) {
+  T3Helper(&arm::Arm32Assembler::mvn, true, "mvn{cond} {reg1}, {shift}", "mvn");
+}
+
+TEST_F(AssemblerArm32Test, Mvns) {
+  T3Helper(&arm::Arm32Assembler::mvns, true, "mvn{cond}s {reg1}, {shift}", "mvns");
+}
+
+TEST_F(AssemblerArm32Test, Add) {
+  T4Helper(&arm::Arm32Assembler::add, false, "add{cond} {reg1}, {reg2}, {shift}", "add");
+}
+
+TEST_F(AssemblerArm32Test, Adds) {
+  T4Helper(&arm::Arm32Assembler::adds, false, "add{cond}s {reg1}, {reg2}, {shift}", "adds");
+}
+
+TEST_F(AssemblerArm32Test, Adc) {
+  T4Helper(&arm::Arm32Assembler::adc, false, "adc{cond} {reg1}, {reg2}, {shift}", "adc");
+}
+
+TEST_F(AssemblerArm32Test, Sub) {
+  T4Helper(&arm::Arm32Assembler::sub, false, "sub{cond} {reg1}, {reg2}, {shift}", "sub");
+}
+
+TEST_F(AssemblerArm32Test, Subs) {
+  T4Helper(&arm::Arm32Assembler::subs, false, "sub{cond}s {reg1}, {reg2}, {shift}", "subs");
+}
+
+TEST_F(AssemblerArm32Test, Sbc) {
+  T4Helper(&arm::Arm32Assembler::sbc, false, "sbc{cond} {reg1}, {reg2}, {shift}", "sbc");
+}
+
+TEST_F(AssemblerArm32Test, Rsb) {
+  T4Helper(&arm::Arm32Assembler::rsb, true, "rsb{cond} {reg1}, {reg2}, {shift}", "rsb");
+}
+
+TEST_F(AssemblerArm32Test, Rsbs) {
+  T4Helper(&arm::Arm32Assembler::rsbs, true, "rsb{cond}s {reg1}, {reg2}, {shift}", "rsbs");
+}
+
+TEST_F(AssemblerArm32Test, Rsc) {
+  T4Helper(&arm::Arm32Assembler::rsc, true, "rsc{cond} {reg1}, {reg2}, {shift}", "rsc");
+}
+
+/* TODO: Needs support to filter out register combinations, as reg1 must not be equal to reg3.
+TEST_F(AssemblerArm32Test, Strex) {
+  RRRCWithoutPCHelper(&arm::Arm32Assembler::strex, "strex{cond} {reg1}, {reg2}, [{reg3}]", "strex");
+}
+*/
+
+TEST_F(AssemblerArm32Test, Clz) {
+  T3Helper(&arm::Arm32Assembler::clz, true, "clz{cond} {reg1}, {reg2}", "clz");
+}
+
+TEST_F(AssemblerArm32Test, Tst) {
+  T3Helper(&arm::Arm32Assembler::tst, true, "tst{cond} {reg1}, {shift}", "tst");
+}
+
+TEST_F(AssemblerArm32Test, Teq) {
+  T3Helper(&arm::Arm32Assembler::teq, true, "teq{cond} {reg1}, {shift}", "teq");
+}
+
+TEST_F(AssemblerArm32Test, Cmp) {
+  T3Helper(&arm::Arm32Assembler::cmp, true, "cmp{cond} {reg1}, {shift}", "cmp");
+}
+
+TEST_F(AssemblerArm32Test, Cmn) {
+  T3Helper(&arm::Arm32Assembler::cmn, true, "cmn{cond} {reg1}, {shift}", "cmn");
+}
+
+TEST_F(AssemblerArm32Test, Blx) {
+  T2Helper(&arm::Arm32Assembler::blx, true, "blx{cond} {reg1}", "blx");
+}
+
+TEST_F(AssemblerArm32Test, Bx) {
+  T2Helper(&arm::Arm32Assembler::bx, true, "bx{cond} {reg1}", "bx");
 }
 
 }  // namespace art
diff --git a/compiler/utils/arm/assembler_arm_test.h b/compiler/utils/arm/assembler_arm_test.h
new file mode 100644
index 0000000..34475b2
--- /dev/null
+++ b/compiler/utils/arm/assembler_arm_test.h
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_
+#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_
+
+#include "utils/assembler_test.h"
+
+namespace art {
+
+template<typename Ass, typename Reg, typename FPReg, typename Imm, typename SOp, typename Cond>
+class AssemblerArmTest : public AssemblerTest<Ass, Reg, FPReg, Imm> {
+ public:
+  typedef AssemblerTest<Ass, Reg, FPReg, Imm> Base;
+
+  using Base::GetRegisters;
+  using Base::GetRegName;
+  using Base::CreateImmediate;
+  using Base::WarnOnCombinations;
+
+  static constexpr int64_t kFullImmRangeThreshold = 32;
+
+  virtual void FillImmediates(std::vector<Imm>& immediates, int64_t imm_min, int64_t imm_max) {
+    // Small range: do completely.
+    if (imm_max - imm_min <= kFullImmRangeThreshold) {
+      for (int64_t i = imm_min; i <= imm_max; ++i) {
+        immediates.push_back(CreateImmediate(i));
+      }
+    } else {
+      immediates.push_back(CreateImmediate(imm_min));
+      immediates.push_back(CreateImmediate(imm_max));
+      if (imm_min < imm_max - 1) {
+        immediates.push_back(CreateImmediate(imm_min + 1));
+      }
+      if (imm_min < imm_max - 2) {
+        immediates.push_back(CreateImmediate(imm_min + 2));
+      }
+      if (imm_min < imm_max - 3) {
+        immediates.push_back(CreateImmediate(imm_max - 1));
+      }
+      if (imm_min < imm_max - 4) {
+        immediates.push_back(CreateImmediate((imm_min + imm_max) / 2));
+      }
+    }
+  }
+
+  std::string RepeatRRIIC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond),
+                          int64_t imm1_min, int64_t imm1_max,
+                          int64_t imm2_min, int64_t imm2_max,
+                          std::string fmt) {
+    return RepeatTemplatedRRIIC(f, GetRegisters(), GetRegisters(),
+                                &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+                                &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+                                imm1_min, imm1_max, imm2_min, imm2_max,
+                                fmt);
+  }
+
+  template <typename Reg1, typename Reg2>
+  std::string RepeatTemplatedRRIIC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond),
+                                   const std::vector<Reg1*> reg1_registers,
+                                   const std::vector<Reg2*> reg2_registers,
+                                   std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+                                   std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+                                   int64_t imm1_min, int64_t imm1_max,
+                                   int64_t imm2_min, int64_t imm2_max,
+                                   std::string fmt) {
+    std::vector<Imm> immediates1;
+    FillImmediates(immediates1, imm1_min, imm1_max);
+    std::vector<Imm> immediates2;
+    FillImmediates(immediates2, imm2_min, imm2_max);
+
+    std::vector<Cond>& cond = GetConditions();
+
+    WarnOnCombinations(cond.size() * immediates1.size() * immediates2.size() *
+                       reg1_registers.size() * reg2_registers.size());
+
+    std::ostringstream oss;
+    bool first = true;
+    for (Cond& c : cond) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      for (Imm i : immediates1) {
+        std::string base = after_cond;
+
+        size_t imm1_index = base.find(IMM1_TOKEN);
+        if (imm1_index != std::string::npos) {
+          std::ostringstream sreg;
+          sreg << i;
+          std::string imm_string = sreg.str();
+          base.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string);
+        }
+
+        for (Imm j : immediates2) {
+          std::string base2 = base;
+
+          size_t imm2_index = base2.find(IMM2_TOKEN);
+          if (imm2_index != std::string::npos) {
+            std::ostringstream sreg;
+            sreg << j;
+            std::string imm_string = sreg.str();
+            base2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string);
+          }
+
+          for (auto reg1 : reg1_registers) {
+            std::string base3 = base2;
+
+            std::string reg1_string = (this->*GetName1)(*reg1);
+            size_t reg1_index;
+            while ((reg1_index = base3.find(Base::REG1_TOKEN)) != std::string::npos) {
+              base3.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+            }
+
+            for (auto reg2 : reg2_registers) {
+              std::string base4 = base3;
+
+              std::string reg2_string = (this->*GetName2)(*reg2);
+              size_t reg2_index;
+              while ((reg2_index = base4.find(Base::REG2_TOKEN)) != std::string::npos) {
+                base4.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+              }
+
+              if (first) {
+                first = false;
+              } else {
+                oss << "\n";
+              }
+              oss << base4;
+
+              (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c);
+            }
+          }
+        }
+      }
+    }
+    // Add a newline at the end.
+    oss << "\n";
+
+    return oss.str();
+  }
+
+  std::string RepeatRRiiC(void (Ass::*f)(Reg, Reg, Imm, Imm, Cond),
+                          std::vector<std::pair<Imm, Imm>>& immediates,
+                          std::string fmt) {
+    return RepeatTemplatedRRiiC(f, GetRegisters(), GetRegisters(),
+        &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+        immediates, fmt);
+  }
+
+  template <typename Reg1, typename Reg2>
+  std::string RepeatTemplatedRRiiC(void (Ass::*f)(Reg1, Reg2, Imm, Imm, Cond),
+        const std::vector<Reg1*> reg1_registers,
+        const std::vector<Reg2*> reg2_registers,
+        std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+        std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+        std::vector<std::pair<Imm, Imm>>& immediates,
+        std::string fmt) {
+    std::vector<Cond>& cond = GetConditions();
+
+    WarnOnCombinations(cond.size() * immediates.size() * reg1_registers.size() *
+                       reg2_registers.size());
+
+    std::ostringstream oss;
+    bool first = true;
+    for (Cond& c : cond) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      for (std::pair<Imm, Imm>& pair : immediates) {
+        Imm i = pair.first;
+        Imm j = pair.second;
+        std::string after_imm1 = after_cond;
+
+        size_t imm1_index = after_imm1.find(IMM1_TOKEN);
+        if (imm1_index != std::string::npos) {
+          std::ostringstream sreg;
+          sreg << i;
+          std::string imm_string = sreg.str();
+          after_imm1.replace(imm1_index, ConstexprStrLen(IMM1_TOKEN), imm_string);
+        }
+
+        std::string after_imm2 = after_imm1;
+
+        size_t imm2_index = after_imm2.find(IMM2_TOKEN);
+        if (imm2_index != std::string::npos) {
+          std::ostringstream sreg;
+          sreg << j;
+          std::string imm_string = sreg.str();
+          after_imm2.replace(imm2_index, ConstexprStrLen(IMM2_TOKEN), imm_string);
+        }
+
+        for (auto reg1 : reg1_registers) {
+          std::string after_reg1 = after_imm2;
+
+          std::string reg1_string = (this->*GetName1)(*reg1);
+          size_t reg1_index;
+          while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+            after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+          }
+
+          for (auto reg2 : reg2_registers) {
+            std::string after_reg2 = after_reg1;
+
+            std::string reg2_string = (this->*GetName2)(*reg2);
+            size_t reg2_index;
+            while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+              after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+            }
+
+            if (first) {
+              first = false;
+            } else {
+              oss << "\n";
+            }
+            oss << after_reg2;
+
+            (Base::GetAssembler()->*f)(*reg1, *reg2, i, j, c);
+          }
+        }
+      }
+    }
+    // Add a newline at the end.
+    oss << "\n";
+
+    return oss.str();
+  }
+
+  std::string RepeatRRC(void (Ass::*f)(Reg, Reg, Cond), std::string fmt) {
+    return RepeatTemplatedRRC(f, GetRegisters(), GetRegisters(), GetConditions(),
+        &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+        &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+        fmt);
+  }
+
+  template <typename Reg1, typename Reg2>
+  std::string RepeatTemplatedRRC(void (Ass::*f)(Reg1, Reg2, Cond),
+                                 const std::vector<Reg1*>& reg1_registers,
+                                 const std::vector<Reg2*>& reg2_registers,
+                                 const std::vector<Cond>& cond,
+                                 std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+                                 std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+                                 std::string fmt) {
+    WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size());
+
+    std::ostringstream oss;
+    bool first = true;
+    for (const Cond& c : cond) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      for (auto reg1 : reg1_registers) {
+        std::string after_reg1 = after_cond;
+
+        std::string reg1_string = (this->*GetName1)(*reg1);
+        size_t reg1_index;
+        while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+          after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+        }
+
+        for (auto reg2 : reg2_registers) {
+          std::string after_reg2 = after_reg1;
+
+          std::string reg2_string = (this->*GetName2)(*reg2);
+          size_t reg2_index;
+          while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+            after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+          }
+
+          if (first) {
+            first = false;
+          } else {
+            oss << "\n";
+          }
+          oss << after_reg2;
+
+          (Base::GetAssembler()->*f)(*reg1, *reg2, c);
+        }
+      }
+    }
+    // Add a newline at the end.
+    oss << "\n";
+
+    return oss.str();
+  }
+
+  std::string RepeatRRRC(void (Ass::*f)(Reg, Reg, Reg, Cond), std::string fmt) {
+    return RepeatTemplatedRRRC(f, GetRegisters(), GetRegisters(), GetRegisters(), GetConditions(),
+                               &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+                               &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+                               &AssemblerArmTest::template GetRegName<RegisterView::kUsePrimaryName>,
+                               fmt);
+  }
+
+  template <typename Reg1, typename Reg2, typename Reg3>
+  std::string RepeatTemplatedRRRC(void (Ass::*f)(Reg1, Reg2, Reg3, Cond),
+                                  const std::vector<Reg1*>& reg1_registers,
+                                  const std::vector<Reg2*>& reg2_registers,
+                                  const std::vector<Reg3*>& reg3_registers,
+                                  const std::vector<Cond>& cond,
+                                  std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+                                  std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+                                  std::string (AssemblerArmTest::*GetName3)(const Reg3&),
+                                  std::string fmt) {
+    WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() *
+                       reg3_registers.size());
+
+    std::ostringstream oss;
+    bool first = true;
+    for (const Cond& c : cond) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      for (auto reg1 : reg1_registers) {
+        std::string after_reg1 = after_cond;
+
+        std::string reg1_string = (this->*GetName1)(*reg1);
+        size_t reg1_index;
+        while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+          after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+        }
+
+        for (auto reg2 : reg2_registers) {
+          std::string after_reg2 = after_reg1;
+
+          std::string reg2_string = (this->*GetName2)(*reg2);
+          size_t reg2_index;
+          while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+            after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+          }
+
+          for (auto reg3 : reg3_registers) {
+            std::string after_reg3 = after_reg2;
+
+            std::string reg3_string = (this->*GetName3)(*reg3);
+            size_t reg3_index;
+            while ((reg3_index = after_reg3.find(REG3_TOKEN)) != std::string::npos) {
+              after_reg3.replace(reg3_index, ConstexprStrLen(REG3_TOKEN), reg3_string);
+            }
+
+            if (first) {
+              first = false;
+            } else {
+              oss << "\n";
+            }
+            oss << after_reg3;
+
+            (Base::GetAssembler()->*f)(*reg1, *reg2, *reg3, c);
+          }
+        }
+      }
+    }
+    // Add a newline at the end.
+    oss << "\n";
+
+    return oss.str();
+  }
+
+  template <typename RegT>
+  std::string RepeatTemplatedRSC(void (Ass::*f)(RegT, SOp, Cond),
+                                 const std::vector<RegT*>& registers,
+                                 const std::vector<SOp>& shifts,
+                                 const std::vector<Cond>& cond,
+                                 std::string (AssemblerArmTest::*GetName)(const RegT&),
+                                 std::string fmt) {
+    WarnOnCombinations(cond.size() * registers.size() * shifts.size());
+
+    std::ostringstream oss;
+    bool first = true;
+    for (const Cond& c : cond) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      for (const SOp& shift : shifts) {
+        std::string after_shift = after_cond;
+
+        std::string shift_string = GetShiftString(shift);
+        size_t shift_index;
+        while ((shift_index = after_shift.find(Base::SHIFT_TOKEN)) != std::string::npos) {
+          after_shift.replace(shift_index, ConstexprStrLen(Base::SHIFT_TOKEN), shift_string);
+        }
+
+        for (auto reg : registers) {
+          std::string after_reg = after_shift;
+
+          std::string reg_string = (this->*GetName)(*reg);
+          size_t reg_index;
+          while ((reg_index = after_reg.find(Base::REG_TOKEN)) != std::string::npos) {
+            after_reg.replace(reg_index, ConstexprStrLen(Base::REG_TOKEN), reg_string);
+          }
+
+          if (first) {
+            first = false;
+          } else {
+            oss << "\n";
+          }
+          oss << after_reg;
+
+          (Base::GetAssembler()->*f)(*reg, shift, c);
+        }
+      }
+    }
+    // Add a newline at the end.
+    oss << "\n";
+
+    return oss.str();
+  }
+
+  template <typename Reg1, typename Reg2>
+  std::string RepeatTemplatedRRSC(void (Ass::*f)(Reg1, Reg2, const SOp&, Cond),
+                                  const std::vector<Reg1*>& reg1_registers,
+                                  const std::vector<Reg2*>& reg2_registers,
+                                  const std::vector<SOp>& shifts,
+                                  const std::vector<Cond>& cond,
+                                  std::string (AssemblerArmTest::*GetName1)(const Reg1&),
+                                  std::string (AssemblerArmTest::*GetName2)(const Reg2&),
+                                  std::string fmt) {
+    WarnOnCombinations(cond.size() * reg1_registers.size() * reg2_registers.size() * shifts.size());
+
+    std::ostringstream oss;
+    bool first = true;
+    for (const Cond& c : cond) {
+      std::string after_cond = fmt;
+
+      size_t cond_index = after_cond.find(COND_TOKEN);
+      if (cond_index != std::string::npos) {
+        after_cond.replace(cond_index, ConstexprStrLen(IMM1_TOKEN), GetConditionString(c));
+      }
+
+      for (const SOp& shift : shifts) {
+        std::string after_shift = after_cond;
+
+        std::string shift_string = GetShiftString(shift);
+        size_t shift_index;
+        while ((shift_index = after_shift.find(SHIFT_TOKEN)) != std::string::npos) {
+          after_shift.replace(shift_index, ConstexprStrLen(SHIFT_TOKEN), shift_string);
+        }
+
+        for (auto reg1 : reg1_registers) {
+          std::string after_reg1 = after_shift;
+
+          std::string reg1_string = (this->*GetName1)(*reg1);
+          size_t reg1_index;
+          while ((reg1_index = after_reg1.find(Base::REG1_TOKEN)) != std::string::npos) {
+            after_reg1.replace(reg1_index, ConstexprStrLen(Base::REG1_TOKEN), reg1_string);
+          }
+
+          for (auto reg2 : reg2_registers) {
+            std::string after_reg2 = after_reg1;
+
+            std::string reg2_string = (this->*GetName2)(*reg2);
+            size_t reg2_index;
+            while ((reg2_index = after_reg2.find(Base::REG2_TOKEN)) != std::string::npos) {
+              after_reg2.replace(reg2_index, ConstexprStrLen(Base::REG2_TOKEN), reg2_string);
+            }
+
+            if (first) {
+              first = false;
+            } else {
+              oss << "\n";
+            }
+            oss << after_reg2;
+
+            (Base::GetAssembler()->*f)(*reg1, *reg2, shift, c);
+          }
+        }
+      }
+    }
+    // Add a newline at the end.
+    oss << "\n";
+
+    return oss.str();
+  }
+
+ protected:
+  explicit AssemblerArmTest() {}
+
+  virtual std::vector<Cond>& GetConditions() = 0;
+  virtual std::string GetConditionString(Cond c) = 0;
+
+  virtual std::vector<SOp>& GetShiftOperands() = 0;
+  virtual std::string GetShiftString(SOp sop) = 0;
+
+  virtual Reg GetPCRegister() = 0;
+  virtual std::vector<Reg*> GetRegistersWithoutPC() {
+    std::vector<Reg*> without_pc = GetRegisters();
+    Reg pc_reg = GetPCRegister();
+
+    for (auto it = without_pc.begin(); it != without_pc.end(); ++it) {
+      if (**it == pc_reg) {
+        without_pc.erase(it);
+        break;
+      }
+    }
+
+    return without_pc;
+  }
+
+  static constexpr const char* IMM1_TOKEN = "{imm1}";
+  static constexpr const char* IMM2_TOKEN = "{imm2}";
+  static constexpr const char* REG3_TOKEN = "{reg3}";
+  static constexpr const char* REG4_TOKEN = "{reg4}";
+  static constexpr const char* COND_TOKEN = "{cond}";
+  static constexpr const char* SHIFT_TOKEN = "{shift}";
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AssemblerArmTest);
+};
+
+}  // namespace art
+
+#endif  // ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_TEST_H_
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index 9d3fa01..1fadb91 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -38,14 +38,14 @@
 // temp directory.
 static std::string tmpnam_;
 
+enum class RegisterView {  // private
+  kUsePrimaryName,
+  kUseSecondaryName
+};
+
 template<typename Ass, typename Reg, typename FPReg, typename Imm>
 class AssemblerTest : public testing::Test {
  public:
-  enum class RegisterView {  // private
-    kUsePrimaryName,
-    kUseSecondaryName
-  };
-
   Ass* GetAssembler() {
     return assembler_.get();
   }
@@ -159,6 +159,9 @@
                       bool as_uint = false) {
     std::string str;
     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes, as_uint);
+
+    WarnOnCombinations(imms.size());
+
     for (int64_t imm : imms) {
       Imm new_imm = CreateImmediate(imm);
       (assembler_.get()->*f)(new_imm);
@@ -184,12 +187,12 @@
 
   // This is intended to be run as a test.
   bool CheckTools() {
-    if (!FileExists(GetAssemblerCommand())) {
+    if (!FileExists(FindTool(GetAssemblerCmdName()))) {
       return false;
     }
     LOG(INFO) << "Chosen assembler command: " << GetAssemblerCommand();
 
-    if (!FileExists(GetObjdumpCommand())) {
+    if (!FileExists(FindTool(GetObjdumpCmdName()))) {
       return false;
     }
     LOG(INFO) << "Chosen objdump command: " << GetObjdumpCommand();
@@ -197,7 +200,7 @@
     // Disassembly is optional.
     std::string disassembler = GetDisassembleCommand();
     if (disassembler.length() != 0) {
-      if (!FileExists(disassembler)) {
+      if (!FileExists(FindTool(GetDisassembleCmdName()))) {
         return false;
       }
       LOG(INFO) << "Chosen disassemble command: " << GetDisassembleCommand();
@@ -271,7 +274,7 @@
 
     resolved_assembler_cmd_ = line + GetAssemblerParameters();
 
-    return line;
+    return resolved_assembler_cmd_;
   }
 
   // Get the name of the objdump, e.g., "objdump" by default.
@@ -298,7 +301,7 @@
 
     resolved_objdump_cmd_ = line + GetObjdumpParameters();
 
-    return line;
+    return resolved_objdump_cmd_;
   }
 
   // Get the name of the objdump, e.g., "objdump" by default.
@@ -324,7 +327,7 @@
 
     resolved_disassemble_cmd_ = line + GetDisassembleParameters();
 
-    return line;
+    return resolved_disassemble_cmd_;
   }
 
   // Create a couple of immediate values up to the number of bytes given.
@@ -406,6 +409,8 @@
                                        std::string (AssemblerTest::*GetName1)(const Reg1&),
                                        std::string (AssemblerTest::*GetName2)(const Reg2&),
                                        std::string fmt) {
+    WarnOnCombinations(reg1_registers.size() * reg2_registers.size());
+
     std::string str;
     for (auto reg1 : reg1_registers) {
       for (auto reg2 : reg2_registers) {
@@ -435,7 +440,6 @@
     return str;
   }
 
- private:
   template <RegisterView kRegView>
   std::string GetRegName(const Reg& reg) {
     std::ostringstream sreg;
@@ -457,12 +461,32 @@
     return sreg.str();
   }
 
+  // If the assembly file needs a header, return it in a sub-class.
+  virtual const char* GetAssemblyHeader() {
+    return nullptr;
+  }
+
+  void WarnOnCombinations(size_t count) {
+    if (count > kWarnManyCombinationsThreshold) {
+      GTEST_LOG_(WARNING) << "Many combinations (" << count << "), test generation might be slow.";
+    }
+  }
+
+  static constexpr const char* REG_TOKEN = "{reg}";
+  static constexpr const char* REG1_TOKEN = "{reg1}";
+  static constexpr const char* REG2_TOKEN = "{reg2}";
+  static constexpr const char* IMM_TOKEN = "{imm}";
+
+ private:
   template <RegisterView kRegView>
   std::string RepeatRegisterImm(void (Ass::*f)(Reg, const Imm&), size_t imm_bytes,
                                   std::string fmt) {
     const std::vector<Reg*> registers = GetRegisters();
     std::string str;
     std::vector<int64_t> imms = CreateImmediateValues(imm_bytes);
+
+    WarnOnCombinations(registers.size() * imms.size());
+
     for (auto reg : registers) {
       for (int64_t imm : imms) {
         Imm new_imm = CreateImmediate(imm);
@@ -547,7 +571,7 @@
 
   // Compile the assembly file from_file to a binary file to_file. Returns true on success.
   bool Assemble(const char* from_file, const char* to_file, std::string* error_msg) {
-    bool have_assembler = FileExists(GetAssemblerCommand());
+    bool have_assembler = FileExists(FindTool(GetAssemblerCmdName()));
     EXPECT_TRUE(have_assembler) << "Cannot find assembler:" << GetAssemblerCommand();
     if (!have_assembler) {
       return false;
@@ -569,13 +593,20 @@
     args.push_back("-c");
     args.push_back(cmd);
 
-    return Exec(args, error_msg);
+    bool success = Exec(args, error_msg);
+    if (!success) {
+      LOG(INFO) << "Assembler command line:";
+      for (std::string arg : args) {
+        LOG(INFO) << arg;
+      }
+    }
+    return success;
   }
 
   // Runs objdump -h on the binary file and extracts the first line with .text.
   // Returns "" on failure.
   std::string Objdump(std::string file) {
-    bool have_objdump = FileExists(GetObjdumpCommand());
+    bool have_objdump = FileExists(FindTool(GetObjdumpCmdName()));
     EXPECT_TRUE(have_objdump) << "Cannot find objdump: " << GetObjdumpCommand();
     if (!have_objdump) {
       return "";
@@ -652,10 +683,10 @@
 
     // If you want to take a look at the differences between the ART assembler and GCC, comment
     // out the removal code.
-    std::remove(data_name.c_str());
-    std::remove(as_name.c_str());
-    std::remove((data_name + ".dis").c_str());
-    std::remove((as_name + ".dis").c_str());
+//    std::remove(data_name.c_str());
+//    std::remove(as_name.c_str());
+//    std::remove((data_name + ".dis").c_str());
+//    std::remove((as_name + ".dis").c_str());
 
     return result;
   }
@@ -714,6 +745,10 @@
     // TODO: Lots of error checking.
 
     std::ofstream s_out(res->base_name + ".S");
+    const char* header = GetAssemblyHeader();
+    if (header != nullptr) {
+      s_out << header;
+    }
     s_out << assembly_code;
     s_out.close();
 
@@ -862,13 +897,9 @@
     return tmpnam_;
   }
 
+  static constexpr size_t kWarnManyCombinationsThreshold = 500;
   static constexpr size_t OBJDUMP_SECTION_LINE_MIN_TOKENS = 6;
 
-  static constexpr const char* REG_TOKEN = "{reg}";
-  static constexpr const char* REG1_TOKEN = "{reg1}";
-  static constexpr const char* REG2_TOKEN = "{reg2}";
-  static constexpr const char* IMM_TOKEN = "{imm}";
-
   std::unique_ptr<Ass> assembler_;
 
   std::string resolved_assembler_cmd_;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 927c5f5..7d4b726 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1220,6 +1220,8 @@
 
   // Write out the generated code part. Calls the OatWriter and ElfBuilder. Also prepares the
   // ImageWriter, if necessary.
+  // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure
+  //       case (when the file will be explicitly erased).
   bool CreateOatFile() {
     CHECK(key_value_store_.get() != nullptr);
 
@@ -1277,15 +1279,6 @@
       }
     }
 
-    // Flush result to disk.
-    {
-      TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
-      if (oat_file_->FlushCloseOrErase() != 0) {
-        PLOG(ERROR) << "Failed to flush ELF file " << oat_file_->GetPath();
-        return false;
-      }
-    }
-
     VLOG(compiler) << "Oat file written successfully (unstripped): " << oat_location_;
     return true;
   }
@@ -1302,20 +1295,19 @@
     return true;
   }
 
-  // Strip the oat file, if requested. This first creates a copy from unstripped to stripped, and
-  // then runs the ElfStripper. Currently only relevant for the portable compiler.
-  bool Strip() {
+  // Create a copy from unstripped to stripped.
+  bool CopyUnstrippedToStripped() {
     // If we don't want to strip in place, copy from unstripped location to stripped location.
     // We need to strip after image creation because FixupElf needs to use .strtab.
     if (oat_unstripped_ != oat_stripped_) {
-      TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
-      if (kUsePortableCompiler) {
-        if (oat_file_->FlushCloseOrErase() != 0) {
-          PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_;
-          return EXIT_FAILURE;
+      // If the oat file is still open, flush it.
+      if (oat_file_.get() != nullptr && oat_file_->IsOpened()) {
+        if (!FlushCloseOatFile()) {
+          return false;
         }
-        oat_file_.reset();
       }
+
+      TimingLogger::ScopedTiming t("dex2oat OatFile copy", timings_);
       std::unique_ptr<File> in(OS::OpenFileForReading(oat_unstripped_.c_str()));
       std::unique_ptr<File> out(OS::CreateEmptyFile(oat_stripped_.c_str()));
       size_t buffer_size = 8192;
@@ -1328,14 +1320,27 @@
         bool write_ok = out->WriteFully(buffer.get(), bytes_read);
         CHECK(write_ok);
       }
-      oat_file_.reset(out.release());
+      if (kUsePortableCompiler) {
+        oat_file_.reset(out.release());
+      } else {
+        if (out->FlushCloseOrErase() != 0) {
+          PLOG(ERROR) << "Failed to flush and close copied oat file: " << oat_stripped_;
+          return false;
+        }
+      }
       VLOG(compiler) << "Oat file copied successfully (stripped): " << oat_stripped_;
     }
+    return true;
+  }
 
+  // Run the ElfStripper. Currently only relevant for the portable compiler.
+  bool Strip() {
     if (kUsePortableCompiler) {
       // Portable includes debug symbols unconditionally. If we are not supposed to create them,
       // strip them now. Quick generates debug symbols only when the flag(s) are set.
       if (!compiler_options_->GetIncludeDebugSymbols()) {
+        CHECK(oat_file_.get() != nullptr && oat_file_->IsOpened());
+
         TimingLogger::ScopedTiming t("dex2oat ElfStripper", timings_);
         // Strip unneeded sections for target
         off_t seek_actual = lseek(oat_file_->Fd(), 0, SEEK_SET);
@@ -1347,23 +1352,40 @@
           return false;
         }
 
+        if (!FlushCloseOatFile()) {
+          return false;
+        }
+
         // We wrote the oat file successfully, and want to keep it.
         VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location_;
       } else {
         VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location_;
       }
-      if (oat_file_->FlushCloseOrErase() != 0) {
-        PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_;
-        return EXIT_FAILURE;
-      }
-      oat_file_.reset(nullptr);
     }
 
+    return true;
+  }
+
+  bool FlushOatFile() {
     if (oat_file_.get() != nullptr) {
-      if (oat_file_->FlushCloseOrErase() != 0) {
-        PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_ << "/"
-                    << oat_filename_;
-        return EXIT_FAILURE;
+      TimingLogger::ScopedTiming t2("dex2oat Flush ELF", timings_);
+      if (oat_file_->Flush() != 0) {
+        PLOG(ERROR) << "Failed to flush oat file: " << oat_location_ << " / "
+            << oat_filename_;
+        oat_file_->Erase();
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool FlushCloseOatFile() {
+    if (oat_file_.get() != nullptr) {
+      std::unique_ptr<File> tmp(oat_file_.release());
+      if (tmp->FlushCloseOrErase() != 0) {
+        PLOG(ERROR) << "Failed to flush and close oat file: " << oat_location_ << " / "
+            << oat_filename_;
+        return false;
       }
     }
     return true;
@@ -1382,6 +1404,10 @@
     return compiler_options_.get();
   }
 
+  bool IsImage() const {
+    return image_;
+  }
+
   bool IsHost() const {
     return is_host_;
   }
@@ -1641,6 +1667,94 @@
 #endif
 }
 
+static int CompileImage(Dex2Oat& dex2oat) {
+  dex2oat.Compile();
+
+  // Create the boot.oat.
+  if (!dex2oat.CreateOatFile()) {
+    return EXIT_FAILURE;
+  }
+
+  // Flush and close the boot.oat. We always expect the output file by name, and it will be
+  // re-opened from the unstripped name.
+  if (!dex2oat.FlushCloseOatFile()) {
+    return EXIT_FAILURE;
+  }
+
+  // Creates the boot.art and patches the boot.oat.
+  if (!dex2oat.HandleImage()) {
+    return EXIT_FAILURE;
+  }
+
+  // When given --host, finish early without stripping.
+  if (dex2oat.IsHost()) {
+    dex2oat.DumpTiming();
+    return EXIT_SUCCESS;
+  }
+
+  // Copy unstripped to stripped location, if necessary.
+  if (!dex2oat.CopyUnstrippedToStripped()) {
+    return EXIT_FAILURE;
+  }
+
+  // Strip, if necessary.
+  if (!dex2oat.Strip()) {
+    return EXIT_FAILURE;
+  }
+
+  // FlushClose again, as stripping might have re-opened the oat file.
+  if (!dex2oat.FlushCloseOatFile()) {
+    return EXIT_FAILURE;
+  }
+
+  dex2oat.DumpTiming();
+  return EXIT_SUCCESS;
+}
+
+static int CompileApp(Dex2Oat& dex2oat) {
+  dex2oat.Compile();
+
+  // Create the app oat.
+  if (!dex2oat.CreateOatFile()) {
+    return EXIT_FAILURE;
+  }
+
+  // Do not close the oat file here. We might haven gotten the output file by file descriptor,
+  // which we would lose.
+  if (!dex2oat.FlushOatFile()) {
+    return EXIT_FAILURE;
+  }
+
+  // When given --host, finish early without stripping.
+  if (dex2oat.IsHost()) {
+    if (!dex2oat.FlushCloseOatFile()) {
+      return EXIT_FAILURE;
+    }
+
+    dex2oat.DumpTiming();
+    return EXIT_SUCCESS;
+  }
+
+  // Copy unstripped to stripped location, if necessary. This will implicitly flush & close the
+  // unstripped version. If this is given, we expect to be able to open writable files by name.
+  if (!dex2oat.CopyUnstrippedToStripped()) {
+    return EXIT_FAILURE;
+  }
+
+  // Strip, if necessary.
+  if (!dex2oat.Strip()) {
+    return EXIT_FAILURE;
+  }
+
+  // Flush and close the file.
+  if (!dex2oat.FlushCloseOatFile()) {
+    return EXIT_FAILURE;
+  }
+
+  dex2oat.DumpTiming();
+  return EXIT_SUCCESS;
+}
+
 static int dex2oat(int argc, char** argv) {
   b13564922();
 
@@ -1662,27 +1776,11 @@
     return EXIT_FAILURE;
   }
 
-  dex2oat.Compile();
-
-  if (!dex2oat.CreateOatFile()) {
-    return EXIT_FAILURE;
+  if (dex2oat.IsImage()) {
+    return CompileImage(dex2oat);
+  } else {
+    return CompileApp(dex2oat);
   }
-
-  if (!dex2oat.HandleImage()) {
-    return EXIT_FAILURE;
-  }
-
-  if (dex2oat.IsHost()) {
-    dex2oat.DumpTiming();
-    return EXIT_SUCCESS;
-  }
-
-  if (!dex2oat.Strip()) {
-    return EXIT_FAILURE;
-  }
-
-  dex2oat.DumpTiming();
-  return EXIT_SUCCESS;
 }
 }  // namespace art
 
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 991b956..7c2474f 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -1772,7 +1772,7 @@
     if (i < 4) {
       numOfPages[i] = 1;
     } else if (i < 8) {
-      numOfPages[i] = 2;
+      numOfPages[i] = 1;
     } else if (i < 16) {
       numOfPages[i] = 4;
     } else if (i < 32) {
diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h
index 431686a..3269e10 100644
--- a/runtime/gc/allocator/rosalloc.h
+++ b/runtime/gc/allocator/rosalloc.h
@@ -414,8 +414,7 @@
 
   // We use thread-local runs for the size Brackets whose indexes
   // are less than this index. We use shared (current) runs for the rest.
-
-  static const size_t kNumThreadLocalSizeBrackets = 11;
+  static const size_t kNumThreadLocalSizeBrackets = 8;
 
  private:
   // The base address of the memory region that's managed by this allocator.
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 9a4c875..ad46be6 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1120,13 +1120,20 @@
 
 void DumpNativeStack(std::ostream& os, pid_t tid, const char* prefix,
     mirror::ArtMethod* current_method) {
-  // TODO: enable on __linux__ b/15446488.
-#if 0
+#if __linux__
   // b/18119146
   if (RUNNING_ON_VALGRIND != 0) {
     return;
   }
 
+#if !defined(HAVE_ANDROID_OS)
+  if (GetTid() != tid) {
+    // TODO: dumping of other threads is disabled to avoid crashes during stress testing.
+    //       b/15446488.
+    return;
+  }
+#endif
+
   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
   if (!backtrace->Unwind(0)) {
     os << prefix << "(backtrace::Unwind failed for thread " << tid << ")\n";