ronnie
2022-10-14 1504bb53e29d3d46222c0b3ea994fc494b48e153
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
/*
 * Copyright (C) 2017 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_RUNTIME_SUBTYPE_CHECK_H_
#define ART_RUNTIME_SUBTYPE_CHECK_H_
 
#include "subtype_check_bits_and_status.h"
#include "subtype_check_info.h"
 
#include "base/locks.h"
#include "mirror/class.h"
#include "runtime.h"
 
// Build flag for the bitstring subtype check runtime hooks.
constexpr bool kBitstringSubtypeCheckEnabled = false;
 
/**
 * Any node in a tree can have its path (from the root to the node) represented as a string by
 * concatenating the path of the parent to that of the current node.
 *
 * We can annotate each node with a `sibling-label` which is some value unique amongst all of the
 * node's siblings. As a special case, the root is empty.
 *
 *           (none)
 *        /    |     \
 *       A     B      C
 *     /   \
 *    A’    B’
 *          |
 *          A’’
 *          |
 *          A’’’
 *          |
 *          A’’’’
 *
 * Given these sibling-labels, we can now encode the path from any node to the root by starting at
 * the node and going up to the root, marking each node with this `path-label`. The special
 * character $ means "end of path".
 *
 *             $
 *        /    |      \
 *       A$    B$     C$
 *     /    \
 *   A’A$   B’A$
 *           |
 *           A’’B’A$
 *           |
 *           A’’’A’’B’A$
 *           |
 *           A’’’’A’’B’A$
 *
 * Given the above `path-label` we can express if any two nodes are an offspring of the other
 * through a O(1) expression:
 *
 *    x <: y :=
 *      suffix(x, y) == y
 *
 * In the above example suffix(x,y) means the suffix of x that is as long as y (right-padded with
 * $s if x is shorter than y) :
 *
 *    suffix(x,y) := x(x.length - y.length .. 0]
 *                     + repeat($, max(y.length - x.length, 0))
 *
 * A few generalities here to elaborate:
 *
 * - There can be at most D levels in the tree.
 * - Each level L has an alphabet A, and the maximum number of
 *   nodes is determined by |A|
 * - The alphabet A can be a subset, superset, equal, or unique with respect to the other alphabets
 *   without loss of generality. (In practice it would almost always be a subset of the previous
 *   level’s alphabet as we assume most classes have less children the deeper they are.)
 * - The `sibling-label` doesn’t need to be stored as an explicit value. It can a temporary when
 *   visiting every immediate child of a node. Only the `path-label` needs to be actually stored for
 *   every node.
 *
 * The path can also be reversed, and use a prefix instead of a suffix to define the subchild
 * relation.
 *
 *             $
 *        /    |      \    \
 *       A$    B$     C$    D$
 *     /    \
 *   AA’$   AB’$
 *            |
 *            AB’A’’$
 *            |
 *            AB’A’’A’’’$
 *            |
 *            AB’A’’A’’’A’’’’$
 *
 *    x <: y :=
 *      prefix(x, y) == y
 *
 *    prefix(x,y) := x[0 .. y.length)
 *                     + repeat($, max(y.length - x.length, 0))
 *
 * In a dynamic tree, new nodes can be inserted at any time. This means if a minimal alphabet is
 * selected to contain the initial tree hierarchy, later node insertions will be illegal because
 * there is no more room to encode the path.
 *
 * In this simple example with an alphabet A,B,C and max level 1:
 *
 *     Level
 *     0:               $
 *              /     |     \     \
 *     1:      A$     B$     C$    D$   (illegal)
 *              |
 *     2:      AA$  (illegal)
 *
 * Attempting to insert the sibling “D” at Level 1 would be illegal because the Alphabet(1) is
 * {A,B,C} and inserting an extra node would mean the `sibling-label` is no longer unique.
 * Attempting to insert “AA$” is illegal because the level 2 is more than the max level 1.
 *
 * One solution to this would be to revisit the entire graph, select a larger alphabet to that
 * every `sibling-label` is unique, pick a larger max level count, and then store the updated
 * `path-label` accordingly.
 *
 * The more common approach would instead be to select a set of alphabets and max levels statically,
 * with large enough sizes, for example:
 *
 *     Alphabets = {{A,B,C,D}, {A,B,C}, {A,B}, {A}}
 *     Max Levels = |Alphabets|
 *
 * Which would allow up to 4 levels with each successive level having 1 less max siblings.
 *
 * Attempting to insert a new node into the graph which does not fit into that level’s alphabet
 * would be represented by re-using the `path-label` of the parent. Such a `path_label` would be
 * considered truncated (because it would only have a prefix of the full path from the root to the
 * node).
 *
 *    Level
 *    0:             $
 *             /     |     \     \
 *    1:      A$     B$     C$    $   (same as parent)
 *             |
 *    2:      A$ (same as parent)
 *
 * The updated relation for offspring is then:
 *
 *    x <: y :=
 *      if !truncated_path(y):
 *        return prefix(x, y) == y               // O(1)
 *      else:
 *        return slow_check_is_offspring(x, y)   // worse than O(1)
 *
 * (Example definition of truncated_path -- any semantically equivalent way to check that the
 *  sibling's `sibling-label` is not unique will do)
 *
 *    truncated_path(y) :=
 *      return y == parent(y)
 *
 * (Example definition. Any slower-than-O(1) definition will do here. This is the traversing
 * superclass hierarchy solution)
 *
 *    slow_check_is_offspring(x, y) :=
 *      if not x: return false
 *      else: return x == y || recursive_is_offspring(parent(x), y)
 *
 * In which case slow_check_is_offspring is some non-O(1) way to check if x and is an offspring of y.
 *
 * In addition, note that it doesn’t matter if the "x" from above is a unique sibling or not; the
 * relation will still be correct.
 *
 * ------------------------------------------------------------------------------------------------
 *
 * Leveraging truncated paths to minimize path lengths.
 *
 * As observed above, for any x <: y, it is sufficient to have a full path only for y,
 * and x can be truncated (to its nearest ancestor's full path).
 *
 * We call a node that stores a full path "Assigned", and a node that stores a truncated path
 * either "Initialized" or "Overflowed."
 *
 * "Initialized" means it is still possible to assign a full path to the node, and "Overflowed"
 * means there is insufficient characters in the alphabet left.
 *
 * In this example, assume that we attempt to "Assign" all non-leafs if possible. Leafs
 * always get truncated (as either Initialized or Overflowed).
 *
 *     Alphabets = {{A,B,C,D}, {A,B}}
 *     Max Levels = |Alphabets|
 *
 *    Level
 *    0:             $
 *             /     |     \     \     \
 *    1:      A$     B$     C$    D$    $ (Overflowed: Too wide)
 *            |             |
 *    2:     AA$            C$ (Initialized)
 *            |
 *    3:     AA$ (Overflowed: Too deep)
 *
 * (All un-annotated nodes are "Assigned").
 * Above, the node at level 3 becomes overflowed because it exceeds the max levels. The
 * right-most node at level 1 becomes overflowed because there's no characters in the alphabet
 * left in that level.
 *
 * The "C$" node is Initialized at level 2, but it can still be promoted to "Assigned" later on
 * if we wanted to.
 *
 * In particular, this is the strategy we use in our implementation
 * (SubtypeCheck::EnsureInitialized, SubtypeCheck::EnsureAssigned).
 *
 * Since the # of characters in our alphabet (BitString) is very limited, we want to avoid
 * allocating a character to a node until its absolutely necessary.
 *
 * All node targets (in `src <: target`) get Assigned, and any parent of an Initialized
 * node also gets Assigned.
 */
namespace art {
 
struct MockSubtypeCheck;  // Forward declaration for testing.
 
// This class is using a template parameter to enable testability without losing performance.
// ClassPtr is almost always `mirror::Class*` or `ObjPtr<mirror::Class>`.
template <typename ClassPtr /* Pointer-like type to Class */>
struct SubtypeCheck {
  // Force this class's SubtypeCheckInfo state into at least Initialized.
  // As a side-effect, all parent classes also become Assigned|Overflowed.
  //
  // Cost: O(Depth(Class))
  //
  // Post-condition: State is >= Initialized.
  // Returns: The precise SubtypeCheckInfo::State.
  static SubtypeCheckInfo::State EnsureInitialized(ClassPtr klass)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    return InitializeOrAssign(klass, /*assign=*/false).GetState();
  }
 
  // Force this class's SubtypeCheckInfo state into Assigned|Overflowed.
  // As a side-effect, all parent classes also become Assigned|Overflowed.
  //
  // Cost: O(Depth(Class))
  //
  // Post-condition: State is Assigned|Overflowed.
  // Returns: The precise SubtypeCheckInfo::State.
  static SubtypeCheckInfo::State EnsureAssigned(ClassPtr klass)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    return InitializeOrAssign(klass, /*assign=*/true).GetState();
  }
 
  // Resets the SubtypeCheckInfo into the Uninitialized state.
  //
  // Intended only for the AOT image writer.
  // This is a static function to avoid calling klass.Depth(), which is unsupported
  // in some portions of the image writer.
  //
  // Cost: O(1).
  //
  // Returns: A state that is always Uninitialized.
  static SubtypeCheckInfo::State ForceUninitialize(ClassPtr klass)
    REQUIRES(Locks::subtype_check_lock_)
    REQUIRES_SHARED(Locks::mutator_lock_) {
    // Trying to do this in a real runtime will break thread safety invariants
    // of existing live objects in the class hierarchy.
    // This is only safe as the last step when the classes are about to be
    // written out as an image and IsSubClass is never used again.
    DCHECK(Runtime::Current() == nullptr || Runtime::Current()->IsAotCompiler())
      << "This only makes sense when compiling an app image.";
 
    // Directly read/write the class field here.
    // As this method is used by image_writer on a copy,
    // the Class* there is not a real class and using it for anything
    // more complicated (e.g. ObjPtr or Depth call) will fail dchecks.
 
    // OK. zero-initializing subtype_check_info_ puts us into the kUninitialized state.
    SubtypeCheckBits scb_uninitialized = SubtypeCheckBits{};
    WriteSubtypeCheckBits(klass, scb_uninitialized);
 
    // Do not use "SubtypeCheckInfo" API here since that requires Depth()
    // which would cause a dcheck failure.
    return SubtypeCheckInfo::kUninitialized;
  }
 
  // Retrieve the state of this class's SubtypeCheckInfo.
  //
  // Cost: O(Depth(Class)).
  //
  // Returns: The precise SubtypeCheckInfo::State.
  static SubtypeCheckInfo::State GetState(ClassPtr klass)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    return GetSubtypeCheckInfo(klass).GetState();
  }
 
  // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
  // be used by a fast check "encoded_src & mask_target == encoded_target".
  //
  // Cost: O(Depth(Class)).
  //
  // Returns the encoded_src value. Must be >= Initialized (EnsureInitialized).
  static BitString::StorageType GetEncodedPathToRootForSource(ClassPtr klass)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    DCHECK_NE(SubtypeCheckInfo::kUninitialized, GetSubtypeCheckInfo(klass).GetState());
    return GetSubtypeCheckInfo(klass).GetEncodedPathToRoot();
  }
 
  // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
  // be used by a fast check "encoded_src & mask_target == encoded_target".
  //
  // Cost: O(Depth(Class)).
  //
  // Returns the encoded_target value. Must be Assigned (EnsureAssigned).
  static BitString::StorageType GetEncodedPathToRootForTarget(ClassPtr klass)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
    DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
    return sci.GetEncodedPathToRoot();
  }
 
  // Retrieve the path to root bitstring mask as a plain uintN_t value that is amenable to
  // be used by a fast check "encoded_src & mask_target == encoded_target".
  //
  // Cost: O(Depth(Class)).
  //
  // Returns the mask_target value. Must be Assigned (EnsureAssigned).
  static BitString::StorageType GetEncodedPathToRootMask(ClassPtr klass)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass);
    DCHECK_EQ(SubtypeCheckInfo::kAssigned, sci.GetState());
    return sci.GetEncodedPathToRootMask();
  }
 
  // Is the source class a subclass of the target?
  //
  // The source state must be at least Initialized, and the target state
  // must be Assigned, otherwise the result will return kUnknownSubtypeOf.
  //
  // See EnsureInitialized and EnsureAssigned. Ideally,
  // EnsureInitialized will be called previously on all possible sources,
  // and EnsureAssigned will be called previously on all possible targets.
  //
  // Runtime cost: O(Depth(Class)), but would be O(1) if depth was known.
  //
  // If the result is known, return kSubtypeOf or kNotSubtypeOf.
  static SubtypeCheckInfo::Result IsSubtypeOf(ClassPtr source, ClassPtr target)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    SubtypeCheckInfo sci = GetSubtypeCheckInfo(source);
    SubtypeCheckInfo target_sci = GetSubtypeCheckInfo(target);
 
    return sci.IsSubtypeOf(target_sci);
  }
 
  // Print SubtypeCheck bitstring and overflow to a stream (e.g. for oatdump).
  static std::ostream& Dump(ClassPtr klass, std::ostream& os)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    return os << GetSubtypeCheckInfo(klass);
  }
 
  static void WriteStatus(ClassPtr klass, ClassStatus status)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    WriteStatusImpl(klass, status);
  }
 
 private:
  static ClassPtr GetParentClass(ClassPtr klass)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    DCHECK(klass->HasSuperClass());
    return ClassPtr(klass->GetSuperClass());
  }
 
  static SubtypeCheckInfo InitializeOrAssign(ClassPtr klass, bool assign)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(!klass->HasSuperClass())) {
      // Object root always goes directly from Uninitialized -> Assigned.
 
      const SubtypeCheckInfo root_sci = GetSubtypeCheckInfo(klass);
      if (root_sci.GetState() != SubtypeCheckInfo::kUninitialized) {
        return root_sci;  // No change needed.
      }
 
      const SubtypeCheckInfo new_root_sci = root_sci.CreateRoot();
      SetSubtypeCheckInfo(klass, new_root_sci);
 
      // The object root is always in the Uninitialized|Assigned state.
      DCHECK_EQ(SubtypeCheckInfo::kAssigned, GetSubtypeCheckInfo(klass).GetState())
          << "Invalid object root state, must be Assigned";
      return new_root_sci;
    }
 
    // Force all ancestors to Assigned | Overflowed.
    ClassPtr parent_klass = GetParentClass(klass);
    size_t parent_depth = InitializeOrAssign(parent_klass, /*assign=*/true).GetDepth();
    if (kIsDebugBuild) {
      SubtypeCheckInfo::State parent_state = GetSubtypeCheckInfo(parent_klass).GetState();
      DCHECK(parent_state == SubtypeCheckInfo::kAssigned ||
          parent_state == SubtypeCheckInfo::kOverflowed)
          << "Expected parent Assigned|Overflowed, but was: " << parent_state;
    }
 
    // Read.
    SubtypeCheckInfo sci = GetSubtypeCheckInfo(klass, parent_depth + 1u);
    SubtypeCheckInfo parent_sci = GetSubtypeCheckInfo(parent_klass, parent_depth);
 
    // Modify.
    const SubtypeCheckInfo::State sci_state = sci.GetState();
    // Skip doing any work if the state is already up-to-date.
    //   - assign == false -> Initialized or higher.
    //   - assign == true  -> Assigned or higher.
    if (sci_state == SubtypeCheckInfo::kUninitialized ||
        (sci_state == SubtypeCheckInfo::kInitialized && assign)) {
      // Copy parent path into the child.
      //
      // If assign==true, this also appends Parent.Next value to the end.
      // Then the Parent.Next value is incremented to avoid allocating
      // the same value again to another node.
      sci = parent_sci.CreateChild(assign);  // Note: Parent could be mutated.
    } else {
      // Nothing to do, already >= Initialized.
      return sci;
    }
 
    // Post-condition: EnsureAssigned -> Assigned|Overflowed.
    // Post-condition: EnsureInitialized -> Not Uninitialized.
    DCHECK_NE(sci.GetState(), SubtypeCheckInfo::kUninitialized);
 
    if (assign) {
      DCHECK_NE(sci.GetState(), SubtypeCheckInfo::kInitialized);
    }
 
    // Write.
    SetSubtypeCheckInfo(klass, sci);                     // self
    SetSubtypeCheckInfo(parent_klass, parent_sci);       // parent
 
    return sci;
  }
 
  static SubtypeCheckBitsAndStatus ReadField(ClassPtr klass)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    SubtypeCheckBitsAndStatus current_bits_and_status;
 
    int32_t int32_data = klass->GetField32Volatile(klass->StatusOffset());
    current_bits_and_status.int32_alias_ = int32_data;
 
    if (kIsDebugBuild) {
      SubtypeCheckBitsAndStatus tmp;
      memcpy(&tmp, &int32_data, sizeof(tmp));
      DCHECK_EQ(0, memcmp(&tmp, &current_bits_and_status, sizeof(tmp))) << int32_data;
    }
    return current_bits_and_status;
  }
 
  static void WriteSubtypeCheckBits(ClassPtr klass, const SubtypeCheckBits& new_bits)
      REQUIRES(Locks::subtype_check_lock_)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    // Use a "CAS" to write the SubtypeCheckBits in the class.
    // Although we have exclusive access to the bitstrings, because
    // ClassStatus and SubtypeCheckBits share the same word, another thread could
    // potentially overwrite that word still.
 
    SubtypeCheckBitsAndStatus new_value;
    ClassStatus old_status;
    SubtypeCheckBitsAndStatus full_old;
    while (true) {
      // TODO: Atomic compare-and-swap does not update the 'expected' parameter,
      // so we have to read it as a separate step instead.
      SubtypeCheckBitsAndStatus old_value = ReadField(klass);
 
      {
        SubtypeCheckBits old_bits = old_value.subtype_check_info_;
        if (memcmp(&old_bits, &new_bits, sizeof(old_bits)) == 0) {
          // Avoid dirtying memory when the data hasn't changed.
          return;
        }
      }
 
      full_old = old_value;
      old_status = old_value.status_;
 
      new_value = old_value;
      new_value.subtype_check_info_ = new_bits;
 
      if (kIsDebugBuild) {
        int32_t int32_data = 0;
        memcpy(&int32_data, &new_value, sizeof(int32_t));
        DCHECK_EQ(int32_data, new_value.int32_alias_) << int32_data;
 
        DCHECK_EQ(old_status, new_value.status_)
          << "full new: " << bit_cast<uint32_t>(new_value)
          << ", full old: " << bit_cast<uint32_t>(full_old);
      }
 
      if (CasFieldWeakSequentiallyConsistent32(klass,
                                               klass->StatusOffset(),
                                               old_value.int32_alias_,
                                               new_value.int32_alias_)) {
        break;
      }
    }
  }
 
  static void WriteStatusImpl(ClassPtr klass, ClassStatus status)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    // Despite not having a lock annotation, this is done with mutual exclusion.
    // See Class::SetStatus for more details.
    SubtypeCheckBitsAndStatus new_value;
    ClassStatus old_status;
    while (true) {
      // TODO: Atomic compare-and-swap does not update the 'expected' parameter,
      // so we have to read it as a separate step instead.
      SubtypeCheckBitsAndStatus old_value = ReadField(klass);
      old_status = old_value.status_;
 
      if (memcmp(&old_status, &status, sizeof(status)) == 0) {
        // Avoid dirtying memory when the data hasn't changed.
        return;
      }
 
      new_value = old_value;
      new_value.status_ = status;
 
      if (CasFieldWeakSequentiallyConsistent32(klass,
                                               klass->StatusOffset(),
                                               old_value.int32_alias_,
                                               new_value.int32_alias_)) {
        break;
      }
    }
  }
 
  static bool CasFieldWeakSequentiallyConsistent32(ClassPtr klass,
                                                   MemberOffset offset,
                                                   int32_t old_value,
                                                   int32_t new_value)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (Runtime::Current() != nullptr && Runtime::Current()->IsActiveTransaction()) {
      return klass->template CasField32</*kTransactionActive=*/true>(offset,
                                                                     old_value,
                                                                     new_value,
                                                                     CASMode::kWeak,
                                                                     std::memory_order_seq_cst);
    } else {
      return klass->template CasField32</*kTransactionActive=*/false>(offset,
                                                                      old_value,
                                                                      new_value,
                                                                      CASMode::kWeak,
                                                                      std::memory_order_seq_cst);
    }
  }
 
  // Get the SubtypeCheckInfo for a klass. O(Depth(Class)) since
  // it also requires calling klass->Depth.
  //
  // Anything calling this function will also be O(Depth(Class)).
  static SubtypeCheckInfo GetSubtypeCheckInfo(ClassPtr klass)
        REQUIRES_SHARED(Locks::mutator_lock_) {
    return GetSubtypeCheckInfo(klass, klass->Depth());
  }
 
  // Get the SubtypeCheckInfo for a klass with known depth.
  static SubtypeCheckInfo GetSubtypeCheckInfo(ClassPtr klass, size_t depth)
        REQUIRES_SHARED(Locks::mutator_lock_) {
    DCHECK_EQ(depth, klass->Depth());
    SubtypeCheckBitsAndStatus current_bits_and_status = ReadField(klass);
 
    const SubtypeCheckInfo current =
        SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth);
    return current;
  }
 
  static void SetSubtypeCheckInfo(ClassPtr klass, const SubtypeCheckInfo& new_sci)
        REQUIRES(Locks::subtype_check_lock_)
        REQUIRES_SHARED(Locks::mutator_lock_) {
    SubtypeCheckBits new_bits = new_sci.GetSubtypeCheckBits();
    WriteSubtypeCheckBits(klass, new_bits);
  }
 
  // Tests can inherit this class. Normal code should use static methods.
  SubtypeCheck() = default;
  SubtypeCheck(const SubtypeCheck& other) = default;
  SubtypeCheck(SubtypeCheck&& other) = default;
  ~SubtypeCheck() = default;
 
  friend struct MockSubtypeCheck;
};
 
}  // namespace art
 
#endif  // ART_RUNTIME_SUBTYPE_CHECK_H_