Document when we can avoid read barriers.

Test: Rely on TreeHugger.
Bug: 119486698
Change-Id: I6d5e18709ff7d624eea7a083f39a56ed8f8ffa49
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 2fdf69e..22597dd 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -809,8 +809,9 @@
 
 template<VerifyObjectFlags kVerifyFlags>
 inline bool Class::IsClassClass() {
-  // OK to look at from-space copies since java.lang.Class.class is not movable.
-  // See b/114413743
+  // OK to look at from-space copies since java.lang.Class.class is non-moveable
+  // (even when running without boot image, see ClassLinker::InitWithoutImage())
+  // and we're reading it for comparison only. See ReadBarrierOption.
   ObjPtr<Class> java_lang_Class = GetClass<kVerifyFlags, kWithoutReadBarrier>();
   return this == java_lang_Class;
 }
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index 005e272..814d86a 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -139,8 +139,9 @@
 
 template<VerifyObjectFlags kVerifyFlags>
 inline bool Object::IsClass() {
-  // OK to look at from-space copies since java.lang.Class.class is not movable.
-  // See b/114413743
+  // OK to look at from-space copies since java.lang.Class.class is non-moveable
+  // (even when running without boot image, see ClassLinker::InitWithoutImage())
+  // and we're reading constant references for comparison only. See ReadBarrierOption.
   ObjPtr<Class> klass = GetClass<kVerifyFlags, kWithoutReadBarrier>();
   ObjPtr<Class> java_lang_Class = klass->GetClass<kVerifyFlags, kWithoutReadBarrier>();
   return klass == java_lang_Class;
@@ -194,8 +195,8 @@
 
 template<VerifyObjectFlags kVerifyFlags, Primitive::Type kType>
 ALWAYS_INLINE bool Object::IsSpecificPrimitiveArray() {
-  // We do not need a read barrier here as the primitive type is constant,
-  // both from-space and to-space component type classes shall yield the same result.
+  // We do not need a read barrier here as the primitive type is constant, both from-space
+  // and to-space component type classes shall yield the same result. See ReadBarrierOption.
   ObjPtr<Class> klass = GetClass<kVerifyFlags, kWithoutReadBarrier>();
   constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
   ObjPtr<Class> const component_type = klass->GetComponentType<kNewFlags, kWithoutReadBarrier>();
diff --git a/runtime/read_barrier_option.h b/runtime/read_barrier_option.h
index 7de8b8a..d92dc41 100644
--- a/runtime/read_barrier_option.h
+++ b/runtime/read_barrier_option.h
@@ -19,6 +19,42 @@
 namespace art {
 
 // Options for performing a read barrier or not.
+//
+// Besides disabled GC and GC's internal usage, there are a few cases where the read
+// barrier is unnecessary and can be avoided to reduce code size and improve performance.
+// In the following cases, the result of the operation or chain of operations shall be the
+// same whether we go through the from-space or to-space:
+//
+// 1. We're reading a reference known to point to an un-reclaimable immune space object.
+//    (For example boot image class and string references, read by compiled code from
+//    .data.bimg.rel.ro . Similarly, such references constructed using position independent
+//    code in the compiled boot image code do not need a read barrier.)
+// 2. We're reading the reference for comparison involving a non-moving space reference.
+//    (Whether the non-moving space reference is the one we're reading or the one we shall
+//    compare it with, the result is the same with and without read barrier.)
+// 3. We're reading the reference for comparison with null.
+//    (Similar to 2 above, given that null is "non-moving".)
+// 4. We're reading a reference to a holder from which we shall read
+//      - constant primitive field, or
+//      - constant reference field known to point to an un-reclaimable immune space object, or
+//      - constant reference field for comparison involving a non-moving space reference, or
+//      - constant reference field for comparison with null, or
+//      - constant reference fields in a chain leading to one or more of the above purposes;
+//        the entire chain needs to be read without read barrier.
+//    The term "constant" refers to fields set to their final value between allocating
+//    the holder and the next opportunity for the holder to be moved by the GC, i.e.
+//    before the first suspend point after the allocation, or seen by another thread.
+//    This includes several fields in the Class object, such as the primitive type or
+//    component type but not the superclass.
+//
+// References read without a read barrier must not remain live at the next suspend point,
+// with the exception of references to un-reclaimable immune space objects.
+//
+// For un-reclaimable immune space objects, we rely on graying dirty objects in the FlipCallback
+// pause (we try to gray them just before flipping thread roots but the FlipCallback has to re-scan
+// for newly dirtied objects) and clean objects conceptually become black at that point
+// (marking them through is a no-op as all reference fields must also point to immune spaces),
+// so mutator threads can never miss a read barrier as they never see white immune space object.
 enum ReadBarrierOption {
   kWithReadBarrier,     // Perform a read barrier.
   kWithoutReadBarrier,  // Don't perform a read barrier.