Merge "ART: Refactor ImageSpace loading"
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index 0e2a672..492a12d 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -133,7 +133,7 @@
   qpoints->pReadBarrierMarkReg09 = art_quick_read_barrier_mark_reg09;
   qpoints->pReadBarrierMarkReg10 = art_quick_read_barrier_mark_reg10;
   qpoints->pReadBarrierMarkReg11 = art_quick_read_barrier_mark_reg11;
-  qpoints->pReadBarrierMarkReg12 = art_quick_read_barrier_mark_reg12;
+  qpoints->pReadBarrierMarkReg12 = nullptr;  // Cannot use register 12 (IP) to pass arguments.
   qpoints->pReadBarrierMarkReg13 = nullptr;  // Cannot use register 13 (SP) to pass arguments.
   qpoints->pReadBarrierMarkReg14 = nullptr;  // Cannot use register 14 (LR) to pass arguments.
   qpoints->pReadBarrierMarkReg15 = nullptr;  // Cannot use register 15 (PC) to pass arguments.
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index 3d0da80..c4ec726 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1246,9 +1246,15 @@
     ldr    r2, [r2, r0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
                                                               // Read barrier for class load.
     ldr    r3, [r9, #THREAD_IS_GC_MARKING_OFFSET]
-    cbnz   r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path
+    cbnz   r3, .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking
 .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit:
     ALLOC_OBJECT_TLAB_FAST_PATH .Lart_quick_alloc_object_region_tlab_slow_path
+.Lart_quick_alloc_object_region_tlab_class_load_read_barrier_marking:
+    cbz    r2, .Lart_quick_alloc_object_region_tlab_slow_path  // Null check for loading lock word.
+    // Check lock word for mark bit, if marked do the allocation.
+    ldr r3, [r2, MIRROR_OBJECT_LOCK_WORD_OFFSET]
+    ands r3, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
+    bne .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path_exit
 .Lart_quick_alloc_object_region_tlab_class_load_read_barrier_slow_path:
                                                               // The read barrier slow path. Mark
                                                               // the class.
@@ -1817,6 +1823,39 @@
     pop   {pc}
 END art_quick_l2f
 
+.macro CONDITIONAL_CBZ reg, reg_if, dest
+.ifc \reg, \reg_if
+    cbz \reg, \dest
+.endif
+.endm
+
+.macro CONDITIONAL_CMPBZ reg, reg_if, dest
+.ifc \reg, \reg_if
+    cmp \reg, #0
+    beq \dest
+.endif
+.endm
+
+// Use CBZ if the register is in {r0, r7} otherwise compare and branch.
+.macro SMART_CBZ reg, dest
+    CONDITIONAL_CBZ \reg, r0, \dest
+    CONDITIONAL_CBZ \reg, r1, \dest
+    CONDITIONAL_CBZ \reg, r2, \dest
+    CONDITIONAL_CBZ \reg, r3, \dest
+    CONDITIONAL_CBZ \reg, r4, \dest
+    CONDITIONAL_CBZ \reg, r5, \dest
+    CONDITIONAL_CBZ \reg, r6, \dest
+    CONDITIONAL_CBZ \reg, r7, \dest
+    CONDITIONAL_CMPBZ \reg, r8, \dest
+    CONDITIONAL_CMPBZ \reg, r9, \dest
+    CONDITIONAL_CMPBZ \reg, r10, \dest
+    CONDITIONAL_CMPBZ \reg, r11, \dest
+    CONDITIONAL_CMPBZ \reg, r12, \dest
+    CONDITIONAL_CMPBZ \reg, r13, \dest
+    CONDITIONAL_CMPBZ \reg, r14, \dest
+    CONDITIONAL_CMPBZ \reg, r15, \dest
+.endm
+
     /*
      * Create a function `name` calling the ReadBarrier::Mark routine,
      * getting its argument and returning its result through register
@@ -1835,28 +1874,25 @@
 .macro READ_BARRIER_MARK_REG name, reg
 ENTRY \name
     // Null check so that we can load the lock word.
-    cmp \reg, #0
-    beq .Lret_rb_\name
-    // Check lock word for mark bit, if marked return.
-    push {r0}
-    ldr r0, [\reg, MIRROR_OBJECT_LOCK_WORD_OFFSET]
-    and r0, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
-    cbz r0, .Lslow_rb_\name
-    // Restore LR and return.
-    pop   {r0}
-    bx    lr
+    SMART_CBZ \reg, .Lret_rb_\name
+    // Check lock word for mark bit, if marked return. Use IP for scratch since it is blocked.
+    ldr ip, [\reg, MIRROR_OBJECT_LOCK_WORD_OFFSET]
+    ands ip, #LOCK_WORD_MARK_BIT_MASK_SHIFTED
+    beq .Lslow_rb_\name
+    // Already marked, return right away.
+    bx lr
 
 .Lslow_rb_\name:
-    pop   {r0}
-    push  {r0-r4, r9, r12, lr}          @ save return address and core caller-save registers
+    push  {r0-r5, r9, lr}               @ save return address and core caller-save registers
+                                        @ also save callee save r5 for 16 byte alignment
     .cfi_adjust_cfa_offset 32
     .cfi_rel_offset r0, 0
     .cfi_rel_offset r1, 4
     .cfi_rel_offset r2, 8
     .cfi_rel_offset r3, 12
     .cfi_rel_offset r4, 16
-    .cfi_rel_offset r9, 20
-    .cfi_rel_offset r12, 24
+    .cfi_rel_offset r5, 20
+    .cfi_rel_offset r9, 24
     .cfi_rel_offset lr, 28
     vpush {s0-s15}                      @ save floating-point caller-save registers
     .cfi_adjust_cfa_offset 64
@@ -1865,48 +1901,11 @@
       mov   r0, \reg                    @ pass arg1 - obj from `reg`
     .endif
     bl    artReadBarrierMark            @ r0 <- artReadBarrierMark(obj)
-
+    mov ip, r0                          @ Save result in IP
     vpop {s0-s15}                       @ restore floating-point registers
     .cfi_adjust_cfa_offset -64
-    @ If `reg` is a caller-save register, save the result to its
-    @ corresponding stack slot; it will be restored by the "pop"
-    @ instruction below. Otherwise, move result into `reg`.
-    @
-    @ (Note that saving `reg` to its stack slot will overwrite the value
-    @ previously stored by the "push" instruction above. That is
-    @ alright, as in that case we know that `reg` is not a live
-    @ register, as it is used to pass the argument and return the result
-    @ of this function.)
-    .ifc \reg, r0
-      PUSH_REG r0, 0                    @ copy result to r0's stack location
-    .else
-      .ifc \reg, r1
-        PUSH_REG r0, 4                  @ copy result to r1's stack location
-      .else
-        .ifc \reg, r2
-          PUSH_REG r0, 8                @ copy result to r2's stack location
-        .else
-          .ifc \reg, r3
-            PUSH_REG r0, 12             @ copy result to r3's stack location
-          .else
-            .ifc \reg, r4
-              PUSH_REG r0, 16           @ copy result to r4's stack location
-            .else
-              .ifc \reg, r9
-                PUSH_REG r0, 20         @ copy result to r9's stack location
-              .else
-                .ifc \reg, r12
-                  PUSH_REG r0, 24       @ copy result to r12's stack location
-                .else
-                  mov   \reg, r0        @ return result into `reg`
-                .endif
-              .endif
-            .endif
-          .endif
-        .endif
-      .endif
-    .endif
-    pop   {r0-r4, r9, r12, pc}          @ restore caller-save registers and return
+    pop   {r0-r5, r9, lr}               @ restore caller-save registers
+    mov \reg, ip                        @ copy result to reg
 .Lret_rb_\name:
     bx lr
 END \name
@@ -1924,4 +1923,3 @@
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg09, r9
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11
-READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg12, r12
diff --git a/runtime/arch/arm64/entrypoints_init_arm64.cc b/runtime/arch/arm64/entrypoints_init_arm64.cc
index cc5bf29..55b09c3 100644
--- a/runtime/arch/arm64/entrypoints_init_arm64.cc
+++ b/runtime/arch/arm64/entrypoints_init_arm64.cc
@@ -149,7 +149,7 @@
   qpoints->pReadBarrierMarkReg13 = art_quick_read_barrier_mark_reg13;
   qpoints->pReadBarrierMarkReg14 = art_quick_read_barrier_mark_reg14;
   qpoints->pReadBarrierMarkReg15 = art_quick_read_barrier_mark_reg15;
-  qpoints->pReadBarrierMarkReg16 = art_quick_read_barrier_mark_reg16;
+  qpoints->pReadBarrierMarkReg16 = nullptr;  // IP0 is used as a temp by the asm stub.
   qpoints->pReadBarrierMarkReg17 = art_quick_read_barrier_mark_reg17;
   qpoints->pReadBarrierMarkReg18 = art_quick_read_barrier_mark_reg18;
   qpoints->pReadBarrierMarkReg19 = art_quick_read_barrier_mark_reg19;
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 35f5c56..4289cab 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -2751,7 +2751,7 @@
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg13, w13, x13
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg14, w14, x14
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg15, w15, x15
-READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg16, w16, x16
+// READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg16, w16, x16 ip0 is blocked
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg17, w17, x17
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg18, w18, x18
 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg19, w19, x19
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index 743fbb9..d717ec0 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -137,7 +137,20 @@
   }
 
   bool GetCachedImageFile(/*out*/std::string* image, std::string* error_msg) const {
-    std::string cache = GetDalvikCache(GetInstructionSetString(kRuntimeISA), true);
+    std::string cache;
+    bool have_android_data;
+    bool dalvik_cache_exists;
+    bool is_global_cache;
+    GetDalvikCache(GetInstructionSetString(kRuntimeISA),
+                   true,
+                   &cache,
+                   &have_android_data,
+                   &dalvik_cache_exists,
+                   &is_global_cache);
+    if (!dalvik_cache_exists) {
+      *error_msg = "Failed to create dalvik cache";
+      return false;
+    }
     return GetDalvikCacheFilename(GetImageLocation().c_str(), cache.c_str(), image, error_msg);
   }
 
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index fc9a3cf..fa941c0 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -89,9 +89,11 @@
 static void PruneDalvikCache(InstructionSet isa) {
   CHECK_NE(isa, kNone);
   // Prune the base /data/dalvik-cache.
-  impl::DeleteDirectoryContents(GetDalvikCacheOrDie(".", false), false);
+  // Note: GetDalvikCache may return the empty string if the directory doesn't
+  // exist. It is safe to pass "" to DeleteDirectoryContents, so this is okay.
+  impl::DeleteDirectoryContents(GetDalvikCache("."), false);
   // Prune /data/dalvik-cache/<isa>.
-  impl::DeleteDirectoryContents(GetDalvikCacheOrDie(GetInstructionSetString(isa), false), false);
+  impl::DeleteDirectoryContents(GetDalvikCache(GetInstructionSetString(isa)), false);
 
   // Be defensive. There should be a runtime created here, but this may be called in a test.
   if (Runtime::Current() != nullptr) {
@@ -104,7 +106,8 @@
 // present, it usually means the boot didn't complete. We wipe the entire dalvik
 // cache if that's the case.
 static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
-  const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
+  const std::string isa_subdir = GetDalvikCache(GetInstructionSetString(isa));
+  CHECK(!isa_subdir.empty()) << "Dalvik cache not found";
   const std::string boot_marker = isa_subdir + "/.booting";
   const char* file_name = boot_marker.c_str();
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index c190158..fe6332d 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -713,21 +713,15 @@
   CHECK(oat_filename != nullptr);
   CHECK(error_msg != nullptr);
 
-  // TODO: The work done in GetDalvikCache is overkill for what we need.
-  // Ideally a new API for getting the DalvikCacheDirectory the way we want
-  // (without existence testing, creation, or death) is provided with the rest
-  // of the GetDalvikCache family of functions. Until such an API is in place,
-  // we use GetDalvikCache to avoid duplicating the logic for determining the
-  // dalvik cache directory.
-  std::string dalvik_cache_dir;
-  bool ignored;
-  GetDalvikCache("", false, &dalvik_cache_dir, &ignored, &ignored, &ignored);
+  std::string cache_dir = GetDalvikCache(GetInstructionSetString(isa));
+  if (cache_dir.empty()) {
+    *error_msg = "Dalvik cache directory does not exist";
+    return false;
+  }
 
   // TODO: The oat file assistant should be the definitive place for
   // determining the oat file name from the dex location, not
   // GetDalvikCacheFilename.
-  std::string cache_dir = StringPrintf("%s%s",
-      dalvik_cache_dir.c_str(), GetInstructionSetString(isa));
   return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg);
 }
 
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 515ba9f..8ab8cd2 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1087,58 +1087,18 @@
   }
 }
 
-static std::string GetDalvikCacheImpl(const char* subdir,
-                                      const bool create_if_absent,
-                                      const bool abort_on_error) {
+std::string GetDalvikCache(const char* subdir) {
   CHECK(subdir != nullptr);
   const char* android_data = GetAndroidData();
   const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data));
   const std::string dalvik_cache = dalvik_cache_root + subdir;
   if (!OS::DirectoryExists(dalvik_cache.c_str())) {
-    if (!create_if_absent) {
-      // TODO: Check callers. Traditional behavior is to not to abort, even when abort_on_error.
-      return "";
-    }
-
-    // Don't create the system's /data/dalvik-cache/... because it needs special permissions.
-    if (strcmp(android_data, "/data") == 0) {
-      if (abort_on_error) {
-        LOG(FATAL) << "Failed to find dalvik-cache directory " << dalvik_cache
-                   << ", cannot create /data dalvik-cache.";
-        UNREACHABLE();
-      }
-      return "";
-    }
-
-    int result = mkdir(dalvik_cache_root.c_str(), 0700);
-    if (result != 0 && errno != EEXIST) {
-      if (abort_on_error) {
-        PLOG(FATAL) << "Failed to create dalvik-cache root directory " << dalvik_cache_root;
-        UNREACHABLE();
-      }
-      return "";
-    }
-
-    result = mkdir(dalvik_cache.c_str(), 0700);
-    if (result != 0) {
-      if (abort_on_error) {
-        PLOG(FATAL) << "Failed to create dalvik-cache directory " << dalvik_cache;
-        UNREACHABLE();
-      }
-      return "";
-    }
+    // TODO: Check callers. Traditional behavior is to not abort.
+    return "";
   }
   return dalvik_cache;
 }
 
-std::string GetDalvikCache(const char* subdir, const bool create_if_absent) {
-  return GetDalvikCacheImpl(subdir, create_if_absent, false);
-}
-
-std::string GetDalvikCacheOrDie(const char* subdir, const bool create_if_absent) {
-  return GetDalvikCacheImpl(subdir, create_if_absent, true);
-}
-
 bool GetDalvikCacheFilename(const char* location, const char* cache_location,
                             std::string* filename, std::string* error_msg) {
   if (location[0] != '/') {
diff --git a/runtime/utils.h b/runtime/utils.h
index 699b732..fd1ba23 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -251,11 +251,8 @@
 const char* GetAndroidDataSafe(std::string* error_msg);
 
 // Returns the dalvik-cache location, with subdir appended. Returns the empty string if the cache
-// could not be found (or created).
-std::string GetDalvikCache(const char* subdir, bool create_if_absent = true);
-// Returns the dalvik-cache location, or dies trying. subdir will be
-// appended to the cache location.
-std::string GetDalvikCacheOrDie(const char* subdir, bool create_if_absent = true);
+// could not be found.
+std::string GetDalvikCache(const char* subdir);
 // Return true if we found the dalvik cache and stored it in the dalvik_cache argument.
 // have_android_data will be set to true if we have an ANDROID_DATA that exists,
 // dalvik_cache_exists will be true if there is a dalvik-cache directory that is present.
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 55b6e01..d2100d1 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -337,11 +337,9 @@
 }
 
 TEST_F(UtilsTest, GetDalvikCache) {
-  EXPECT_STREQ("", GetDalvikCache("should-not-exist123", false).c_str());
+  EXPECT_STREQ("", GetDalvikCache("should-not-exist123").c_str());
 
-  EXPECT_STREQ((android_data_ + "/dalvik-cache/.").c_str(), GetDalvikCache(".", false).c_str());
-  EXPECT_STREQ((android_data_ + "/dalvik-cache/should-not-be-there").c_str(),
-               GetDalvikCache("should-not-be-there", true).c_str());
+  EXPECT_STREQ((android_data_ + "/dalvik-cache/.").c_str(), GetDalvikCache(".").c_str());
 }