[vm] Check for safepoints while initializing or copying large arrays.

TEST=ci
Change-Id: I5ca25d3996e51210464b06492c0b8b6119c4242c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/246304
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
diff --git a/runtime/vm/message_snapshot.cc b/runtime/vm/message_snapshot.cc
index 48f6aec..a24ad62 100644
--- a/runtime/vm/message_snapshot.cc
+++ b/runtime/vm/message_snapshot.cc
@@ -2848,7 +2848,7 @@
     const intptr_t count = d->ReadUnsigned();
     for (intptr_t i = 0; i < count; i++) {
       intptr_t length = d->ReadUnsigned();
-      d->AssignRef(Array::New(cid_, length));
+      d->AssignRef(Array::NewUninitialized(cid_, length));
     }
   }
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 46d3e1b..52de6b1 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2638,7 +2638,21 @@
         initial_value |= initial_value << 32;
       }
 #endif
-      needs_init = true;
+      if (class_id == kArrayCid) {
+        // If the size is greater than both kNewAllocatableSize and
+        // kAllocatablePageSize, the object must have been allocated to a new
+        // large page, which must already have been zero initialized by the OS.
+        // Zero is a GC-safe value. The caller will initialize the fields to
+        // null with safepoint checks to avoid blocking for the full duration of
+        // initializing this array.
+        needs_init = Heap::IsAllocatableInNewSpace(size) ||
+                     Heap::IsAllocatableViaFreeLists(size);
+        if (!needs_init) {
+          initial_value = 0;  // For ASSERT below.
+        }
+      } else {
+        needs_init = true;
+      }
     }
     if (needs_init) {
       while (cur < end) {
@@ -24321,17 +24335,6 @@
   return hash;
 }
 
-ArrayPtr Array::New(intptr_t len, Heap::Space space) {
-  ASSERT(IsolateGroup::Current()->object_store()->array_class() !=
-         Class::null());
-  ArrayPtr result = New(kClassId, len, space);
-  if (UseCardMarkingForAllocation(len)) {
-    ASSERT(result->IsOldObject());
-    result->untag()->SetCardRememberedBitUnsynchronized();
-  }
-  return result;
-}
-
 ArrayPtr Array::New(intptr_t len,
                     const AbstractType& element_type,
                     Heap::Space space) {
@@ -24345,7 +24348,9 @@
   return result.ptr();
 }
 
-ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+ArrayPtr Array::NewUninitialized(intptr_t class_id,
+                                 intptr_t len,
+                                 Heap::Space space) {
   if (!IsValidLength(len)) {
     // This should be caught before we reach here.
     FATAL1("Fatal error in Array::New: invalid len %" Pd "\n", len);
@@ -24356,23 +24361,56 @@
                          Array::ContainsCompressedPointers()));
     NoSafepointScope no_safepoint;
     raw->untag()->set_length(Smi::New(len));
+    if (UseCardMarkingForAllocation(len)) {
+      ASSERT(raw->IsOldObject());
+      raw->untag()->SetCardRememberedBitUnsynchronized();
+    }
     return raw;
   }
 }
 
+ArrayPtr Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
+  if (!UseCardMarkingForAllocation(len)) {
+    return NewUninitialized(class_id, len, space);
+  }
+
+  Thread* thread = Thread::Current();
+  Array& result =
+      Array::Handle(thread->zone(), NewUninitialized(class_id, len, space));
+  result.SetTypeArguments(Object::null_type_arguments());
+  for (intptr_t i = 0; i < len; i++) {
+    result.SetAt(i, Object::null_object(), thread);
+    if (((i + 1) % KB) == 0) {
+      thread->CheckForSafepoint();
+    }
+  }
+  return result.ptr();
+}
+
 ArrayPtr Array::Slice(intptr_t start,
                       intptr_t count,
                       bool with_type_argument) const {
-  // TODO(vegorov) introduce an array allocation method that fills newly
-  // allocated array with values from the given source array instead of
-  // null-initializing all elements.
-  Array& dest = Array::Handle(Array::New(count));
-  dest.StoreArrayPointers(dest.ObjectAddr(0), ObjectAddr(start), count);
-
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+  const Array& dest = Array::Handle(zone, Array::NewUninitialized(count));
   if (with_type_argument) {
-    dest.SetTypeArguments(TypeArguments::Handle(GetTypeArguments()));
+    dest.SetTypeArguments(TypeArguments::Handle(zone, GetTypeArguments()));
+  } else {
+    dest.SetTypeArguments(Object::null_type_arguments());
   }
-
+  if (!UseCardMarkingForAllocation(count)) {
+    NoSafepointScope no_safepoint(thread);
+    for (int i = 0; i < count; i++) {
+      dest.untag()->set_element(i, untag()->element(i + start), thread);
+    }
+  } else {
+    for (int i = 0; i < count; i++) {
+      dest.untag()->set_element(i, untag()->element(i + start), thread);
+      if (((i + 1) % KB) == 0) {
+        thread->CheckForSafepoint();
+      }
+    }
+  }
   return dest.ptr();
 }
 
@@ -24397,19 +24435,30 @@
                      Heap::Space space) {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  const Array& result = Array::Handle(zone, Array::New(new_length, space));
+  const Array& result =
+      Array::Handle(zone, Array::NewUninitialized(new_length, space));
   intptr_t len = 0;
   if (!source.IsNull()) {
     len = source.Length();
     result.SetTypeArguments(
         TypeArguments::Handle(zone, source.GetTypeArguments()));
+  } else {
+    result.SetTypeArguments(Object::null_type_arguments());
   }
   ASSERT(new_length >= len);  // Cannot copy 'source' into new array.
   ASSERT(new_length != len);  // Unnecessary copying of array.
-  PassiveObject& obj = PassiveObject::Handle(zone);
-  for (int i = 0; i < len; i++) {
-    obj = source.At(i);
-    result.SetAt(i, obj, thread);
+  if (!UseCardMarkingForAllocation(len)) {
+    NoSafepointScope no_safepoint(thread);
+    for (int i = 0; i < len; i++) {
+      result.untag()->set_element(i, source.untag()->element(i), thread);
+    }
+  } else {
+    for (int i = 0; i < len; i++) {
+      result.untag()->set_element(i, source.untag()->element(i), thread);
+      if (((i + 1) % KB) == 0) {
+        thread->CheckForSafepoint();
+      }
+    }
   }
   return result.ptr();
 }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 6f226c3..17080ec 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -10419,7 +10419,15 @@
   // to ImmutableArray.
   void MakeImmutable() const;
 
-  static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew);
+  static ArrayPtr New(intptr_t len, Heap::Space space = Heap::kNew) {
+    return New(kArrayCid, len, space);
+  }
+  // The result's type arguments and elements are GC-safe but not initialized to
+  // null.
+  static ArrayPtr NewUninitialized(intptr_t len,
+                                   Heap::Space space = Heap::kNew) {
+    return NewUninitialized(kArrayCid, len, space);
+  }
   static ArrayPtr New(intptr_t len,
                       const AbstractType& element_type,
                       Heap::Space space = Heap::kNew);
@@ -10457,6 +10465,9 @@
   static ArrayPtr New(intptr_t class_id,
                       intptr_t len,
                       Heap::Space space = Heap::kNew);
+  static ArrayPtr NewUninitialized(intptr_t class_id,
+                                   intptr_t len,
+                                   Heap::Space space = Heap::kNew);
 
  private:
   CompressedObjectPtr const* ObjectAddr(intptr_t index) const {
@@ -10477,25 +10488,6 @@
     ptr()->untag()->StoreArrayPointer<type, order, value_type>(addr, value);
   }
 
-  // Store a range of pointers [from, from + count) into [to, to + count).
-  // TODO(koda): Use this to fix Object::Clone's broken store buffer logic.
-  void StoreArrayPointers(CompressedObjectPtr const* to,
-                          CompressedObjectPtr const* from,
-                          intptr_t count) {
-    ASSERT(Contains(reinterpret_cast<uword>(to)));
-    if (ptr()->IsNewObject()) {
-      memmove(const_cast<CompressedObjectPtr*>(to), from,
-              count * kBytesPerElement);
-    } else {
-      Thread* thread = Thread::Current();
-      const uword heap_base = ptr()->heap_base();
-      for (intptr_t i = 0; i < count; ++i) {
-        untag()->StoreArrayPointer(&to[i], from[i].Decompress(heap_base),
-                                   thread);
-      }
-    }
-  }
-
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Array, Instance);
   friend class Class;
   friend class ImmutableArray;