Optimize spread call for sealed, frozen objects

Also add mjsunit test for spread call with non-extensible objects

Micro-benchmark JSTests/ObjectFreeze shows ~7x improvement

Before:
SpreadCall
SpreadCall-Numbers(Score): 239

After:
SpreadCall
SpreadCall-Numbers(Score): 1461

Bug: v8:6831
Change-Id: Icefd89ad790ac159b7f0617d0a012eefd90d3b1d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1614296
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#61669}
diff --git a/src/builtins/builtins-call-gen.cc b/src/builtins/builtins-call-gen.cc
index 0721bb7..e793481 100644
--- a/src/builtins/builtins-call-gen.cc
+++ b/src/builtins/builtins-call-gen.cc
@@ -151,7 +151,7 @@
 
     TNode<Int32T> kind = LoadMapElementsKind(arguments_list_map);
 
-    GotoIf(Int32GreaterThan(kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
+    GotoIf(IsElementsKindGreaterThan(kind, LAST_FROZEN_ELEMENTS_KIND),
            &if_runtime);
     Branch(Word32And(kind, Int32Constant(1)), &if_holey_array, &if_done);
   }
@@ -306,11 +306,13 @@
     var_elements = LoadElements(spread_array);
 
     // Check elements kind of {spread}.
-    GotoIf(Int32LessThan(spread_kind, Int32Constant(PACKED_DOUBLE_ELEMENTS)),
+    GotoIf(IsElementsKindLessThanOrEqual(spread_kind, HOLEY_ELEMENTS),
            &if_smiorobject);
+    GotoIf(IsElementsKindLessThanOrEqual(spread_kind, LAST_FAST_ELEMENTS_KIND),
+           &if_double);
     Branch(
-        Int32GreaterThan(spread_kind, Int32Constant(LAST_FAST_ELEMENTS_KIND)),
-        &if_generic, &if_double);
+        IsElementsKindLessThanOrEqual(spread_kind, LAST_FROZEN_ELEMENTS_KIND),
+        &if_smiorobject, &if_generic);
   }
 
   BIND(&if_generic);
diff --git a/test/mjsunit/compiler/spread-call.js b/test/mjsunit/compiler/spread-call.js
index 0a8527e..12234ed 100644
--- a/test/mjsunit/compiler/spread-call.js
+++ b/test/mjsunit/compiler/spread-call.js
@@ -14,6 +14,9 @@
   assertEquals(3, countArgs(...[1.1, 2, 3]));                     // Double
   assertEquals(4, countArgs(...[1.1, 2, , 3]));                   // HoleyDouble
   assertEquals(3, countArgs(...[{valueOf: () => 0}, 1.1, '2']));  // Object
+  assertEquals(3, countArgs(...Object.freeze([{valueOf: () => 0}, 1.1, '2'])));  // Frozen Object
+  assertEquals(3, countArgs(...Object.seal([{valueOf: () => 0}, 1.1, '2'])));  // Sealed Object
+  assertEquals(3, countArgs(...Object.preventExtensions([{valueOf: () => 0}, 1.1, '2'])));  // Non-extensible Object
   assertEquals(
       4, countArgs(...[{valueOf: () => 0}, 1.1, , '2']));  // HoleyObject
 
diff --git a/test/mjsunit/object-freeze.js b/test/mjsunit/object-freeze.js
index 78259b1..1929760 100644
--- a/test/mjsunit/object-freeze.js
+++ b/test/mjsunit/object-freeze.js
@@ -756,3 +756,24 @@
 assertEquals(arr.length, 2);
 arr.length = 0;
 assertEquals(arr.length, 2);
+
+// Spread with array
+var arr = ['a', 'b', 'c'];
+Object.freeze(arr);
+var arrSpread = [...arr];
+assertEquals(arrSpread.length, arr.length);
+assertEquals(arrSpread[0], 'a');
+assertEquals(arrSpread[1], 'b');
+assertEquals(arrSpread[2], 'c');
+
+// Spread with array-like
+function returnArgs() {
+  return Object.freeze(arguments);
+}
+var arrLike = returnArgs('a', 'b', 'c');
+assertTrue(Object.isFrozen(arrLike));
+var arrSpread = [...arrLike];
+assertEquals(arrSpread.length, arrLike.length);
+assertEquals(arrSpread[0], 'a');
+assertEquals(arrSpread[1], 'b');
+assertEquals(arrSpread[2], 'c');
diff --git a/test/mjsunit/object-prevent-extensions.js b/test/mjsunit/object-prevent-extensions.js
index d84a84a..ccbf32f 100644
--- a/test/mjsunit/object-prevent-extensions.js
+++ b/test/mjsunit/object-prevent-extensions.js
@@ -420,3 +420,24 @@
 assertEquals(arr.length, 3);
 arr.length = 0;
 assertEquals(arr.length, 0);
+
+// Spread with array
+var arr = ['a', 'b', 'c'];
+Object.preventExtensions(arr);
+var arrSpread = [...arr];
+assertEquals(arrSpread.length, arr.length);
+assertEquals(arrSpread[0], 'a');
+assertEquals(arrSpread[1], 'b');
+assertEquals(arrSpread[2], 'c');
+
+// Spread with array-like
+function returnArgs() {
+  return Object.preventExtensions(arguments);
+}
+var arrLike = returnArgs('a', 'b', 'c');
+assertFalse(Object.isExtensible(arrLike));
+var arrSpread = [...arrLike];
+assertEquals(arrSpread.length, arrLike.length);
+assertEquals(arrSpread[0], 'a');
+assertEquals(arrSpread[1], 'b');
+assertEquals(arrSpread[2], 'c');
diff --git a/test/mjsunit/object-seal.js b/test/mjsunit/object-seal.js
index 0563557..895605a 100644
--- a/test/mjsunit/object-seal.js
+++ b/test/mjsunit/object-seal.js
@@ -728,3 +728,24 @@
 assertEquals(arr.length, 3);
 arr.length = 0;
 assertEquals(arr.length, 1);
+
+// Spread with array
+var arr = ['a', 'b', 'c'];
+Object.seal(arr);
+var arrSpread = [...arr];
+assertEquals(arrSpread.length, arr.length);
+assertEquals(arrSpread[0], 'a');
+assertEquals(arrSpread[1], 'b');
+assertEquals(arrSpread[2], 'c');
+
+// Spread with array-like
+function returnArgs() {
+  return Object.seal(arguments);
+}
+var arrLike = returnArgs('a', 'b', 'c');
+assertTrue(Object.isSealed(arrLike));
+var arrSpread = [...arrLike];
+assertEquals(arrSpread.length, arrLike.length);
+assertEquals(arrSpread[0], 'a');
+assertEquals(arrSpread[1], 'b');
+assertEquals(arrSpread[2], 'c');