[vm, gc] Use relaxed loads when reading the header of an ephemeron's key.

The ephemeron and its key may be processed by different workers, so there is a race when accessing the key's header. This race is harmless: if the key becomes a survivor, it will trigger another round of ephemeron processing.

TEST=tsan, splay_ephemeron_test
Bug: https://github.com/dart-lang/sdk/issues/47412
Change-Id: I20339c2215f2d37ad8b9687aebe62492b36a8a61
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/216340
Reviewed-by: Liam Appelbe <liama@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index b8e3e6c..5b236c2 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -103,6 +103,18 @@
   } while (size > 0);
 }
 
+DART_FORCE_INLINE
+static uword ReadHeaderRelaxed(ObjectPtr raw_obj) {
+  return reinterpret_cast<std::atomic<uword>*>(UntaggedObject::ToAddr(raw_obj))
+      ->load(std::memory_order_relaxed);
+}
+
+DART_FORCE_INLINE
+static void WriteHeaderRelaxed(ObjectPtr raw_obj, uword header) {
+  reinterpret_cast<std::atomic<uword>*>(UntaggedObject::ToAddr(raw_obj))
+      ->store(header, std::memory_order_relaxed);
+}
+
 template <bool parallel>
 class ScavengerVisitorBase : public ObjectPointerVisitor {
  public:
@@ -154,16 +166,14 @@
     }
 
     // Validate 'this' is a typed data view.
-    const uword view_header =
-        *reinterpret_cast<uword*>(UntaggedObject::ToAddr(view));
+    const uword view_header = ReadHeaderRelaxed(view);
     ASSERT(!IsForwarding(view_header) || view->IsOldObject());
     ASSERT(IsTypedDataViewClassId(view->GetClassIdMayBeSmi()));
 
     // Validate that the backing store is not a forwarding word.
     TypedDataBasePtr td = view->untag()->typed_data();
     ASSERT(td->IsHeapObject());
-    const uword td_header =
-        *reinterpret_cast<uword*>(UntaggedObject::ToAddr(td));
+    const uword td_header = ReadHeaderRelaxed(td);
     ASSERT(!IsForwarding(td_header) || td->IsOldObject());
 
     if (!parallel) {
@@ -356,8 +366,7 @@
     ASSERT(from_->Contains(raw_addr));
     // Read the header word of the object and determine if the object has
     // already been copied.
-    uword header = reinterpret_cast<std::atomic<uword>*>(raw_addr)->load(
-        std::memory_order_relaxed);
+    uword header = ReadHeaderRelaxed(raw_obj);
     ObjectPtr new_obj;
     if (IsForwarding(header)) {
       // Get the new location of the object.
@@ -1306,7 +1315,7 @@
     ASSERT(raw_key->IsNewObject());
     uword raw_addr = UntaggedObject::ToAddr(raw_key);
     ASSERT(from_->Contains(raw_addr));
-    uword header = *reinterpret_cast<uword*>(raw_addr);
+    uword header = ReadHeaderRelaxed(raw_key);
     // Reset the next pointer in the weak property.
     cur_weak->untag()->next_ = WeakProperty::null();
     if (IsForwarding(header)) {
@@ -1351,8 +1360,7 @@
   ASSERT(raw_weak->IsNewObject());
   ASSERT(raw_weak->IsWeakProperty());
 #if defined(DEBUG)
-  uword raw_addr = UntaggedObject::ToAddr(raw_weak);
-  uword header = *reinterpret_cast<uword*>(raw_addr);
+  uword header = ReadHeaderRelaxed(raw_weak);
   ASSERT(!IsForwarding(header));
 #endif  // defined(DEBUG)
   ASSERT(raw_weak->untag()->next_ ==
@@ -1369,8 +1377,7 @@
     // The fate of the weak property is determined by its key.
     ObjectPtr raw_key = raw_weak->untag()->key();
     if (raw_key->IsHeapObject() && raw_key->IsNewObject()) {
-      uword raw_addr = UntaggedObject::ToAddr(raw_key);
-      uword header = *reinterpret_cast<uword*>(raw_addr);
+      uword header = ReadHeaderRelaxed(raw_key);
       if (!IsForwarding(header)) {
         // Key is white.  Enqueue the weak property.
         EnqueueWeakProperty(raw_weak);
@@ -1714,20 +1721,11 @@
   TIMELINE_FUNCTION_GC_DURATION(thread, "ReverseScavenge");
 
   class ReverseFromForwardingVisitor : public ObjectVisitor {
-    uword ReadHeader(ObjectPtr raw_obj) {
-      return reinterpret_cast<std::atomic<uword>*>(
-                 UntaggedObject::ToAddr(raw_obj))
-          ->load(std::memory_order_relaxed);
-    }
-    void WriteHeader(ObjectPtr raw_obj, uword header) {
-      reinterpret_cast<std::atomic<uword>*>(UntaggedObject::ToAddr(raw_obj))
-          ->store(header, std::memory_order_relaxed);
-    }
     void VisitObject(ObjectPtr from_obj) {
-      uword from_header = ReadHeader(from_obj);
+      uword from_header = ReadHeaderRelaxed(from_obj);
       if (IsForwarding(from_header)) {
         ObjectPtr to_obj = ForwardedObj(from_header);
-        uword to_header = ReadHeader(to_obj);
+        uword to_header = ReadHeaderRelaxed(to_obj);
         intptr_t size = to_obj->untag()->HeapSize();
 
         // Reset the ages bits in case this was a promotion.
@@ -1739,7 +1737,7 @@
         from_header =
             UntaggedObject::OldAndNotMarkedBit::update(false, from_header);
 
-        WriteHeader(from_obj, from_header);
+        WriteHeaderRelaxed(from_obj, from_header);
 
         ForwardingCorpse::AsForwarder(UntaggedObject::ToAddr(to_obj), size)
             ->set_target(from_obj);