cppgc: Establish marking invariants

This is a revival of https://chromium-review.googlesource.com/c/v8/v8/+/2228332

The CL establishes the following:
*) Objects are marked before being pushed to the worklists.
*) Live bytes are always accounted after tracing an object (i.e. move
   from Gray to Black below).
*) Previously not fully constructed objects are traced immediately
   instead of pushed to the marking worklist.

This establishes the following invariants for all marking worklists:
1) White = !object.is_marked() && !worklist.contains(object)
2) Gray = object.is_marked() && worklist.contains(object)
3) Black = object.is_marked() && !worklist.contains(object)

Bug: chromium:1056170
Change-Id: I821573b3fbc057e6ffb836154271ff986ecb4d2b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2336797
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Commit-Queue: Omer Katz <omerkatz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69268}
diff --git a/src/heap/cppgc/marker.cc b/src/heap/cppgc/marker.cc
index 8a42b78..104d4d2 100644
--- a/src/heap/cppgc/marker.cc
+++ b/src/heap/cppgc/marker.cc
@@ -98,6 +98,15 @@
   return true;
 }
 
+void TraceMarkedObject(Visitor* visitor, const HeapObjectHeader* header) {
+  DCHECK(header);
+  DCHECK(!header->IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>());
+  DCHECK(header->IsMarked<HeapObjectHeader::AccessMode::kNonAtomic>());
+  const GCInfo& gcinfo =
+      GlobalGCInfoTable::GCInfoFromIndex(header->GetGCInfoIndex());
+  gcinfo.trace(visitor, header->Payload());
+}
+
 }  // namespace
 
 MarkerBase::MarkerBase(HeapBase& heap)
@@ -115,15 +124,12 @@
   if (!marking_worklists_.not_fully_constructed_worklist()->IsEmpty()) {
 #if DEBUG
     DCHECK_NE(MarkingConfig::StackState::kNoHeapPointers, config_.stack_state);
-    MarkingWorklists::NotFullyConstructedItem item;
+    HeapObjectHeader* header;
     MarkingWorklists::NotFullyConstructedWorklist::View view(
         marking_worklists_.not_fully_constructed_worklist(),
         MarkingWorklists::kMutatorThreadId);
-    while (view.Pop(&item)) {
-      const HeapObjectHeader& header =
-          BasePage::FromPayload(item)->ObjectHeaderFromInnerAddress(
-              static_cast<ConstAddress>(item));
-      DCHECK(header.IsMarked());
+    while (view.Pop(&header)) {
+      DCHECK(header->IsMarked());
     }
 #else
     marking_worklists_.not_fully_constructed_worklist()->Clear();
@@ -204,9 +210,9 @@
     if (!DrainWorklistWithDeadline(
             deadline,
             marking_worklists_.previously_not_fully_constructed_worklist(),
-            [this](MarkingWorklists::NotFullyConstructedItem& item) {
-              mutator_marking_state_.DynamicallyMarkAddress(
-                  reinterpret_cast<ConstAddress>(item));
+            [this](HeapObjectHeader* header) {
+              TraceMarkedObject(&visitor(), header);
+              mutator_marking_state_.AccountMarkedBytes(*header);
             },
             MarkingWorklists::kMutatorThreadId))
       return false;
@@ -218,6 +224,8 @@
                   HeapObjectHeader::FromPayload(item.base_object_payload);
               DCHECK(!header.IsInConstruction<
                       HeapObjectHeader::AccessMode::kNonAtomic>());
+              DCHECK(
+                  header.IsMarked<HeapObjectHeader::AccessMode::kNonAtomic>());
               item.callback(&visitor(), item.base_object_payload);
               mutator_marking_state_.AccountMarkedBytes(header);
             },
@@ -227,12 +235,7 @@
     if (!DrainWorklistWithDeadline(
             deadline, marking_worklists_.write_barrier_worklist(),
             [this](HeapObjectHeader* header) {
-              DCHECK(header);
-              DCHECK(!header->IsInConstruction<
-                      HeapObjectHeader::AccessMode::kNonAtomic>());
-              const GCInfo& gcinfo =
-                  GlobalGCInfoTable::GCInfoFromIndex(header->GetGCInfoIndex());
-              gcinfo.trace(&visitor(), header->Payload());
+              TraceMarkedObject(&visitor(), header);
               mutator_marking_state_.AccountMarkedBytes(*header);
             },
             MarkingWorklists::kMutatorThreadId))
@@ -244,12 +247,16 @@
 }
 
 void MarkerBase::MarkNotFullyConstructedObjects() {
-  MarkingWorklists::NotFullyConstructedItem item;
+  HeapObjectHeader* header;
   MarkingWorklists::NotFullyConstructedWorklist::View view(
       marking_worklists_.not_fully_constructed_worklist(),
       MarkingWorklists::kMutatorThreadId);
-  while (view.Pop(&item)) {
-    conservative_visitor().TraceConservativelyIfNeeded(item);
+  while (view.Pop(&header)) {
+    DCHECK(header);
+    DCHECK(header->IsMarked<HeapObjectHeader::AccessMode::kNonAtomic>());
+    // TraceConservativelyIfNeeded will either push to a worklist
+    // or trace conservatively and call AccountMarkedBytes.
+    conservative_visitor().TraceConservativelyIfNeeded(*header);
   }
 }
 
diff --git a/src/heap/cppgc/marker.h b/src/heap/cppgc/marker.h
index 32d5018..80a056c 100644
--- a/src/heap/cppgc/marker.h
+++ b/src/heap/cppgc/marker.h
@@ -82,7 +82,7 @@
 
   void ProcessWeakness();
 
-  inline void WriteBarrierForInConstructionObject(ConstAddress);
+  inline void WriteBarrierForInConstructionObject(HeapObjectHeader&);
   inline void WriteBarrierForObject(HeapObjectHeader&);
 
   HeapBase& heap() { return heap_; }
@@ -128,12 +128,12 @@
   ConservativeMarkingVisitor conservative_marking_visitor_;
 };
 
-void MarkerBase::WriteBarrierForInConstructionObject(ConstAddress payload) {
+void MarkerBase::WriteBarrierForInConstructionObject(HeapObjectHeader& header) {
   MarkingWorklists::NotFullyConstructedWorklist::View
       not_fully_constructed_worklist(
           marking_worklists_.not_fully_constructed_worklist(),
           MarkingWorklists::kMutatorThreadId);
-  not_fully_constructed_worklist.Push(payload);
+  not_fully_constructed_worklist.Push(&header);
 }
 
 void MarkerBase::WriteBarrierForObject(HeapObjectHeader& header) {
diff --git a/src/heap/cppgc/marking-state.h b/src/heap/cppgc/marking-state.h
index 923da04..b279569 100644
--- a/src/heap/cppgc/marking-state.h
+++ b/src/heap/cppgc/marking-state.h
@@ -81,9 +81,11 @@
 void MarkingState::MarkAndPush(HeapObjectHeader& header, TraceDescriptor desc) {
   DCHECK_NOT_NULL(desc.callback);
 
+  if (!MarkNoPush(header)) return;
+
   if (header.IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>()) {
-    not_fully_constructed_worklist_.Push(header.Payload());
-  } else if (MarkNoPush(header)) {
+    not_fully_constructed_worklist_.Push(&header);
+  } else {
     marking_worklist_.Push(desc);
   }
 }
diff --git a/src/heap/cppgc/marking-worklists.h b/src/heap/cppgc/marking-worklists.h
index 1404b8d..b38d777 100644
--- a/src/heap/cppgc/marking-worklists.h
+++ b/src/heap/cppgc/marking-worklists.h
@@ -21,7 +21,6 @@
   static constexpr int kMutatorThreadId = 0;
 
   using MarkingItem = cppgc::TraceDescriptor;
-  using NotFullyConstructedItem = const void*;
   struct WeakCallbackItem {
     cppgc::WeakCallback callback;
     const void* parameter;
@@ -32,7 +31,7 @@
   using MarkingWorklist =
       Worklist<MarkingItem, 512 /* local entries */, kNumMarkers>;
   using NotFullyConstructedWorklist =
-      Worklist<NotFullyConstructedItem, 16 /* local entries */, kNumMarkers>;
+      Worklist<HeapObjectHeader*, 16 /* local entries */, kNumMarkers>;
   using WeakCallbackWorklist =
       Worklist<WeakCallbackItem, 64 /* local entries */, kNumMarkers>;
   using WriteBarrierWorklist =
diff --git a/src/heap/cppgc/visitor.cc b/src/heap/cppgc/visitor.cc
index 3afdd5a..61eedf3 100644
--- a/src/heap/cppgc/visitor.cc
+++ b/src/heap/cppgc/visitor.cc
@@ -63,13 +63,18 @@
 
   if (!header) return;
 
-  if (!header->IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>()) {
+  TraceConservativelyIfNeeded(*header);
+}
+
+void ConservativeTracingVisitor::TraceConservativelyIfNeeded(
+    HeapObjectHeader& header) {
+  if (!header.IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>()) {
     visitor_.Visit(
-        header->Payload(),
-        {header->Payload(),
-         GlobalGCInfoTable::GCInfoFromIndex(header->GetGCInfoIndex()).trace});
+        header.Payload(),
+        {header.Payload(),
+         GlobalGCInfoTable::GCInfoFromIndex(header.GetGCInfoIndex()).trace});
   } else {
-    VisitConservatively(*header, TraceConservatively);
+    VisitConservatively(header, TraceConservatively);
   }
 }
 
diff --git a/src/heap/cppgc/visitor.h b/src/heap/cppgc/visitor.h
index bcfb396..c8395ff 100644
--- a/src/heap/cppgc/visitor.h
+++ b/src/heap/cppgc/visitor.h
@@ -54,6 +54,7 @@
       delete;
 
   void TraceConservativelyIfNeeded(const void*);
+  void TraceConservativelyIfNeeded(HeapObjectHeader&);
 
  protected:
   using TraceConservativelyCallback = void(ConservativeTracingVisitor*,
diff --git a/src/heap/cppgc/write-barrier.cc b/src/heap/cppgc/write-barrier.cc
index 200ad09..4a076e8 100644
--- a/src/heap/cppgc/write-barrier.cc
+++ b/src/heap/cppgc/write-barrier.cc
@@ -37,10 +37,7 @@
   if (V8_UNLIKELY(
           header
               .IsInConstruction<HeapObjectHeader::AccessMode::kNonAtomic>())) {
-    // It is assumed that objects on not_fully_constructed_worklist_ are not
-    // marked.
-    header.Unmark();
-    marker->WriteBarrierForInConstructionObject(header.Payload());
+    marker->WriteBarrierForInConstructionObject(header);
     return;
   }
 
diff --git a/test/unittests/heap/cppgc/marker-unittest.cc b/test/unittests/heap/cppgc/marker-unittest.cc
index a07f370..8944372 100644
--- a/test/unittests/heap/cppgc/marker-unittest.cc
+++ b/test/unittests/heap/cppgc/marker-unittest.cc
@@ -224,7 +224,7 @@
         Member<GCedWithCallback> member(obj);
         marker.VisitorForTesting().Trace(member);
       });
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(object).IsMarked());
+  EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
   marker.FinishMarking({MarkingConfig::CollectionType::kMajor,
                         MarkingConfig::StackState::kMayContainHeapPointers});
   EXPECT_TRUE(HeapObjectHeader::FromPayload(object).IsMarked());
@@ -240,7 +240,7 @@
       GetAllocationHandle(), [&marker](GCedWithCallback* obj) {
         Member<GCedWithCallback> member(obj);
         marker.VisitorForTesting().Trace(member);
-        EXPECT_FALSE(HeapObjectHeader::FromPayload(obj).IsMarked());
+        EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked());
         marker.FinishMarking(config);
         EXPECT_TRUE(HeapObjectHeader::FromPayload(obj).IsMarked());
       });
diff --git a/test/unittests/heap/cppgc/marking-visitor-unittest.cc b/test/unittests/heap/cppgc/marking-visitor-unittest.cc
index 3e99658..799d44b 100644
--- a/test/unittests/heap/cppgc/marking-visitor-unittest.cc
+++ b/test/unittests/heap/cppgc/marking-visitor-unittest.cc
@@ -205,7 +205,7 @@
 
 }  // namespace
 
-TEST_F(MarkingVisitorTest, DontMarkMemberInConstruction) {
+TEST_F(MarkingVisitorTest, MarkMemberInConstruction) {
   TestMarkingVisitor visitor(GetMarker());
   GCedWithInConstructionCallback* gced =
       MakeGarbageCollected<GCedWithInConstructionCallback>(
@@ -214,10 +214,10 @@
             Member<GCedWithInConstructionCallback> object(obj);
             visitor.Trace(object);
           });
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
+  EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
 }
 
-TEST_F(MarkingVisitorTest, DontMarkMemberMixinInConstruction) {
+TEST_F(MarkingVisitorTest, MarkMemberMixinInConstruction) {
   TestMarkingVisitor visitor(GetMarker());
   GCedWithMixinWithInConstructionCallback* gced =
       MakeGarbageCollected<GCedWithMixinWithInConstructionCallback>(
@@ -226,7 +226,7 @@
             Member<MixinWithInConstructionCallback> mixin(obj);
             visitor.Trace(mixin);
           });
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
+  EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
 }
 
 TEST_F(MarkingVisitorTest, DontMarkWeakMemberInConstruction) {
@@ -253,7 +253,7 @@
   EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
 }
 
-TEST_F(MarkingVisitorTest, DontMarkPersistentInConstruction) {
+TEST_F(MarkingVisitorTest, MarkPersistentInConstruction) {
   TestMarkingVisitor visitor(GetMarker());
   GCedWithInConstructionCallback* gced =
       MakeGarbageCollected<GCedWithInConstructionCallback>(
@@ -262,10 +262,10 @@
             Persistent<GCedWithInConstructionCallback> object(obj);
             visitor.TraceRootForTesting(object, SourceLocation::Current());
           });
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
+  EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
 }
 
-TEST_F(MarkingVisitorTest, DontMarkPersistentMixinInConstruction) {
+TEST_F(MarkingVisitorTest, MarkPersistentMixinInConstruction) {
   TestMarkingVisitor visitor(GetMarker());
   GCedWithMixinWithInConstructionCallback* gced =
       MakeGarbageCollected<GCedWithMixinWithInConstructionCallback>(
@@ -274,7 +274,7 @@
             Persistent<MixinWithInConstructionCallback> mixin(obj);
             visitor.TraceRootForTesting(mixin, SourceLocation::Current());
           });
-  EXPECT_FALSE(HeapObjectHeader::FromPayload(gced).IsMarked());
+  EXPECT_TRUE(HeapObjectHeader::FromPayload(gced).IsMarked());
 }
 
 }  // namespace internal