Reduce calls to DescriptorEquals

Store the low 3 bits of the descriptor hash inside of class set
entries. Compare these bits before comparing descriptors.

Simpleperf interpret-only compile of facebook:
mirror::Class::DescriptorEquals(char const*): 3.66% -> 1.03%

Bug: 32641252

Test: test-art-host

Change-Id: I8d898d4ac7c95383c49401fbcd85bfde226e026c
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index 3e54a64..229cd47 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -26,8 +26,8 @@
 void ClassTable::VisitRoots(Visitor& visitor) {
   ReaderMutexLock mu(Thread::Current(), lock_);
   for (ClassSet& class_set : classes_) {
-    for (GcRoot<mirror::Class>& root : class_set) {
-      visitor.VisitRoot(root.AddressWithoutBarrier());
+    for (TableSlot& table_slot : class_set) {
+      table_slot.VisitRoot(visitor);
     }
   }
   for (GcRoot<mirror::Object>& root : strong_roots_) {
@@ -44,8 +44,8 @@
 void ClassTable::VisitRoots(const Visitor& visitor) {
   ReaderMutexLock mu(Thread::Current(), lock_);
   for (ClassSet& class_set : classes_) {
-    for (GcRoot<mirror::Class>& root : class_set) {
-      visitor.VisitRoot(root.AddressWithoutBarrier());
+    for (TableSlot& table_slot : class_set) {
+      table_slot.VisitRoot(visitor);
     }
   }
   for (GcRoot<mirror::Object>& root : strong_roots_) {
@@ -62,8 +62,8 @@
 bool ClassTable::Visit(Visitor& visitor) {
   ReaderMutexLock mu(Thread::Current(), lock_);
   for (ClassSet& class_set : classes_) {
-    for (GcRoot<mirror::Class>& root : class_set) {
-      if (!visitor(root.Read())) {
+    for (TableSlot& table_slot : class_set) {
+      if (!visitor(table_slot.Read())) {
         return false;
       }
     }
@@ -71,6 +71,51 @@
   return true;
 }
 
+template<ReadBarrierOption kReadBarrierOption>
+inline mirror::Class* ClassTable::TableSlot::Read() const {
+  const uint32_t before = data_.LoadRelaxed();
+  ObjPtr<mirror::Class> const before_ptr(ExtractPtr(before));
+  ObjPtr<mirror::Class> const after_ptr(
+      GcRoot<mirror::Class>(before_ptr).Read<kReadBarrierOption>());
+  if (kReadBarrierOption != kWithoutReadBarrier && before_ptr != after_ptr) {
+    // If another thread raced and updated the reference, do not store the read barrier updated
+    // one.
+    data_.CompareExchangeStrongRelaxed(before, Encode(after_ptr, MaskHash(before)));
+  }
+  return after_ptr.Ptr();
+}
+
+template<typename Visitor>
+inline void ClassTable::TableSlot::VisitRoot(const Visitor& visitor) const {
+  const uint32_t before = data_.LoadRelaxed();
+  ObjPtr<mirror::Class> before_ptr(ExtractPtr(before));
+  GcRoot<mirror::Class> root(before_ptr);
+  visitor.VisitRoot(root.AddressWithoutBarrier());
+  ObjPtr<mirror::Class> after_ptr(root.Read<kWithoutReadBarrier>());
+  if (before_ptr != after_ptr) {
+    // If another thread raced and updated the reference, do not store the read barrier updated
+    // one.
+    data_.CompareExchangeStrongRelaxed(before, Encode(after_ptr, MaskHash(before)));
+  }
+}
+
+inline ObjPtr<mirror::Class> ClassTable::TableSlot::ExtractPtr(uint32_t data) {
+  return reinterpret_cast<mirror::Class*>(data & ~kHashMask);
+}
+
+inline uint32_t ClassTable::TableSlot::Encode(ObjPtr<mirror::Class> klass, uint32_t hash_bits) {
+  DCHECK_LE(hash_bits, kHashMask);
+  return reinterpret_cast<uintptr_t>(klass.Ptr()) | hash_bits;
+}
+
+inline ClassTable::TableSlot::TableSlot(ObjPtr<mirror::Class> klass, uint32_t descriptor_hash)
+    : data_(Encode(klass, MaskHash(descriptor_hash))) {
+  if (kIsDebugBuild) {
+    std::string temp;
+    const uint32_t hash = ComputeModifiedUtf8Hash(klass->GetDescriptor(&temp));
+    CHECK_EQ(descriptor_hash, hash);
+  }
+}
 
 }  // namespace art