[vm, gc] Don't double-visit remembered cards to decide if they should remain remembered.

TEST=ci
Change-Id: Ib92dbbabf90f80c6e984c21b9eaee67fd4205f8f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365543
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
diff --git a/runtime/vm/heap/page.cc b/runtime/vm/heap/page.cc
index a0b83b9..08e998e 100644
--- a/runtime/vm/heap/page.cc
+++ b/runtime/vm/heap/page.cc
@@ -207,7 +207,7 @@
   ASSERT(obj_addr == end_addr);
 }
 
-void Page::VisitRememberedCards(ObjectPointerVisitor* visitor) {
+void Page::VisitRememberedCards(PredicateObjectPointerVisitor* visitor) {
   ASSERT(Thread::Current()->OwnsGCSafepoint() ||
          (Thread::Current()->task_kind() == Thread::kScavengerTask));
   NoSafepointScope no_safepoint;
@@ -258,15 +258,9 @@
         card_to = obj_to;
       }
 
-      visitor->VisitCompressedPointers(heap_base, card_from, card_to);
+      bool has_new_target = visitor->PredicateVisitCompressedPointers(
+          heap_base, card_from, card_to);
 
-      bool has_new_target = false;
-      for (CompressedObjectPtr* slot = card_from; slot <= card_to; slot++) {
-        if ((*slot)->IsNewObjectMayBeSmi()) {
-          has_new_target = true;
-          break;
-        }
-      }
       if (!has_new_target) {
         cell ^= bit_mask;
       }
diff --git a/runtime/vm/heap/page.h b/runtime/vm/heap/page.h
index 4be0412..9ecc2ea 100644
--- a/runtime/vm/heap/page.h
+++ b/runtime/vm/heap/page.h
@@ -173,7 +173,7 @@
     return IsCardRemembered(reinterpret_cast<uword>(slot));
   }
 #endif
-  void VisitRememberedCards(ObjectPointerVisitor* visitor);
+  void VisitRememberedCards(PredicateObjectPointerVisitor* visitor);
   void ResetProgressBar();
 
   Thread* owner() const { return owner_; }
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index f929af1..d63bf22 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -676,7 +676,8 @@
   }
 }
 
-void PageSpace::VisitRememberedCards(ObjectPointerVisitor* visitor) const {
+void PageSpace::VisitRememberedCards(
+    PredicateObjectPointerVisitor* visitor) const {
   ASSERT(Thread::Current()->OwnsGCSafepoint() ||
          (Thread::Current()->task_kind() == Thread::kScavengerTask));
 
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 71c5ef1..fe0bc18 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -230,7 +230,7 @@
   void VisitObjectsUnsafe(ObjectVisitor* visitor) const;
   void VisitObjectPointers(ObjectPointerVisitor* visitor) const;
 
-  void VisitRememberedCards(ObjectPointerVisitor* visitor) const;
+  void VisitRememberedCards(PredicateObjectPointerVisitor* visitor) const;
   void ResetProgressBars() const;
 
   // Collect the garbage in the page space using mark-sweep or mark-compact.
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index 5f25361..64e96f2 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -127,7 +127,8 @@
 }
 
 template <bool parallel>
-class ScavengerVisitorBase : public ObjectPointerVisitor {
+class ScavengerVisitorBase : public ObjectPointerVisitor,
+                             public PredicateObjectPointerVisitor {
  public:
   explicit ScavengerVisitorBase(IsolateGroup* isolate_group,
                                 Scavenger* scavenger,
@@ -226,14 +227,35 @@
     }
   }
 
+  bool PredicateVisitPointers(ObjectPtr* first, ObjectPtr* last) override {
+    bool has_new_target = false;
+    for (ObjectPtr* current = first; current <= last; current++) {
+      has_new_target |= PredicateScavengePointer(current);
+    }
+    return has_new_target;
+  }
+
 #if defined(DART_COMPRESSED_POINTERS)
+  bool PredicateVisitCompressedPointers(uword heap_base,
+                                        CompressedObjectPtr* first,
+                                        CompressedObjectPtr* last) override {
+    bool has_new_target = false;
+    for (CompressedObjectPtr* current = first; current <= last; current++) {
+      has_new_target |= PredicateScavengeCompressedPointer(heap_base, current);
+    }
+    return has_new_target;
+  }
+
   void VisitCompressedPointers(uword heap_base,
                                CompressedObjectPtr* first,
                                CompressedObjectPtr* last) override {
-    ASSERT(Utils::IsAligned(first, sizeof(*first)));
-    ASSERT(Utils::IsAligned(last, sizeof(*last)));
-    for (CompressedObjectPtr* current = first; current <= last; current++) {
-      ScavengeCompressedPointer(heap_base, current);
+    if (PredicateVisitCompressedPointers(heap_base, first, last)) {
+      // Update the store buffer as needed.
+      ObjectPtr visiting_object = visiting_old_object_;
+      if (visiting_object != nullptr &&
+          visiting_object->untag()->TryAcquireRememberedBit()) {
+        thread_->StoreBufferAddObjectGC(visiting_object);
+      }
     }
   }
 #endif
@@ -353,19 +375,24 @@
 
  private:
   DART_FORCE_INLINE
-  void ScavengePointer(ObjectPtr* p) {
+  bool PredicateScavengePointer(ObjectPtr* p) {
     // ScavengePointer cannot be called recursively.
     ObjectPtr obj = *p;
 
     if (obj->IsImmediateOrOldObject()) {
-      return;
+      return false;
     }
 
     ObjectPtr new_obj = ScavengeObject(obj);
 
     // Update the reference.
     *p = new_obj;
-    if (new_obj->IsNewObject()) {
+    return new_obj->IsNewObject();
+  }
+
+  DART_FORCE_INLINE
+  void ScavengePointer(ObjectPtr* p) {
+    if (PredicateScavengePointer(p)) {
       // Update the store buffer as needed.
       ObjectPtr visiting_object = visiting_old_object_;
       if (visiting_object != nullptr &&
@@ -376,20 +403,26 @@
   }
 
   DART_FORCE_INLINE
-  void ScavengeCompressedPointer(uword heap_base, CompressedObjectPtr* p) {
+  bool PredicateScavengeCompressedPointer(uword heap_base,
+                                          CompressedObjectPtr* p) {
     // ScavengePointer cannot be called recursively.
     ObjectPtr obj = p->Decompress(heap_base);
 
     // Could be tested without decompression.
     if (obj->IsImmediateOrOldObject()) {
-      return;
+      return false;
     }
 
     ObjectPtr new_obj = ScavengeObject(obj);
 
     // Update the reference.
     *p = new_obj;
-    if (new_obj->IsNewObject()) {
+    return new_obj->IsNewObject();
+  }
+
+  DART_FORCE_INLINE
+  void ScavengeCompressedPointer(uword heap_base, CompressedObjectPtr* p) {
+    if (PredicateScavengeCompressedPointer(heap_base, p)) {
       // Update the store buffer as needed.
       ObjectPtr visiting_object = visiting_old_object_;
       if (visiting_object != nullptr &&
diff --git a/runtime/vm/visitor.h b/runtime/vm/visitor.h
index 59790ad..e306164 100644
--- a/runtime/vm/visitor.h
+++ b/runtime/vm/visitor.h
@@ -87,6 +87,24 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectPointerVisitor);
 };
 
+class PredicateObjectPointerVisitor {
+ public:
+  PredicateObjectPointerVisitor() {}
+  virtual ~PredicateObjectPointerVisitor() {}
+  virtual bool PredicateVisitPointers(ObjectPtr* first, ObjectPtr* last) = 0;
+#if defined(DART_COMPRESSED_POINTERS)
+  virtual bool PredicateVisitCompressedPointers(uword heap_base,
+                                                CompressedObjectPtr* first,
+                                                CompressedObjectPtr* last) = 0;
+#else
+  bool PredicateVisitCompressedPointers(uword heap_base,
+                                        CompressedObjectPtr* first,
+                                        CompressedObjectPtr* last) {
+    return PredicateVisitPointers(first, last);
+  }
+#endif
+};
+
 // An object visitor interface.
 class ObjectVisitor {
  public: