dex_cache: rewrite StringDexCachePair as DexCachePair<T>

.. with [T = mirror::String]. This is in preparation for introducing
a dex cache array for MethodTypes, which will be treated the same way.

Test: make test-art-host
bug: 30550796
Change-Id: Ief4455b4c6e4c9dd897f2c40b14b843a57b1dc8e
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 7d4021f..d81dedc 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -35,9 +35,9 @@
 
 class String;
 
-struct PACKED(8) StringDexCachePair {
-  GcRoot<String> string_pointer;
-  uint32_t string_index;
+template <typename T> struct PACKED(8) DexCachePair {
+  GcRoot<T> object;
+  uint32_t index;
   // The array is initially [ {0,0}, {0,0}, {0,0} ... ]
   // We maintain the invariant that once a dex cache entry is populated,
   // the pointer is always non-0
@@ -45,47 +45,51 @@
   // {non-0, non-0} OR {0,0}
   //
   // It's generally sufficiently enough then to check if the
-  // lookup string index matches the stored string index (for a >0 string index)
+  // lookup index matches the stored index (for a >0 lookup index)
   // because if it's true the pointer is also non-null.
   //
   // For the 0th entry which is a special case, the value is either
   // {0,0} (initial state) or {non-0, 0} which indicates
-  // that a valid string is stored at that index for a dex string id of 0.
+  // that a valid object is stored at that index for a dex section id of 0.
   //
-  // As an optimization, we want to avoid branching on the string pointer since
-  // it's always non-null if the string id branch succeeds (except for the 0th string id).
+  // As an optimization, we want to avoid branching on the object pointer since
+  // it's always non-null if the id branch succeeds (except for the 0th id).
   // Set the initial state for the 0th entry to be {0,1} which is guaranteed to fail
-  // the lookup string id == stored id branch.
-  StringDexCachePair(String* string, uint32_t string_idx)
-      : string_pointer(string),
-        string_index(string_idx) {}
-  StringDexCachePair() = default;
-  StringDexCachePair(const StringDexCachePair&) = default;
-  StringDexCachePair& operator=(const StringDexCachePair&) = default;
+  // the lookup id == stored id branch.
+  DexCachePair(T* object, uint32_t index)
+      : object(object),
+        index(index) {}
+  DexCachePair() = default;
+  DexCachePair(const DexCachePair<T>&) = default;
+  DexCachePair& operator=(const DexCachePair<T>&) = default;
 
-  static void Initialize(StringDexCacheType* strings) {
-    mirror::StringDexCachePair first_elem;
-    first_elem.string_pointer = GcRoot<String>(nullptr);
-    first_elem.string_index = InvalidStringIndexForSlot(0);
-    strings[0].store(first_elem, std::memory_order_relaxed);
+  static void Initialize(std::atomic<DexCachePair<T>>* dex_cache) {
+    DexCachePair<T> first_elem;
+    first_elem.object = GcRoot<T>(nullptr);
+    first_elem.index = InvalidIndexForSlot(0);
+    dex_cache[0].store(first_elem, std::memory_order_relaxed);
   }
 
-  static GcRoot<String> LookupString(StringDexCacheType* dex_cache,
-                                     uint32_t string_idx,
-                                     uint32_t cache_size) {
-    StringDexCachePair index_string = dex_cache[string_idx % cache_size]
-        .load(std::memory_order_relaxed);
-    if (string_idx != index_string.string_index) return GcRoot<String>(nullptr);
-    DCHECK(!index_string.string_pointer.IsNull());
-    return index_string.string_pointer;
+  static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache,
+                          uint32_t idx,
+                          uint32_t cache_size) {
+    DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed);
+    if (idx != element.index) {
+      return GcRoot<T>(nullptr);
+    }
+
+    DCHECK(!element.object.IsNull());
+    return element.object;
   }
 
-  static uint32_t InvalidStringIndexForSlot(uint32_t slot) {
+  static uint32_t InvalidIndexForSlot(uint32_t slot) {
     // Since the cache size is a power of two, 0 will always map to slot 0.
     // Use 1 for slot 0 and 0 for all other slots.
     return (slot == 0) ? 1u : 0u;
   }
 };
+
+using StringDexCachePair = DexCachePair<mirror::String>;
 using StringDexCacheType = std::atomic<StringDexCachePair>;
 
 // C++ mirror of java.lang.DexCache.