[JSC] Add support for typed arrays to the Array profiling
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Apr 2015 04:16:21 +0000 (04:16 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 28 Apr 2015 04:16:21 +0000 (04:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143913

Patch by Benjamin Poulain <bpoulain@apple.com> on 2015-04-27
Reviewed by Filip Pizlo.

Source/JavaScriptCore:

This patch adds ArrayModes for every typed arrays. Having that information
let us generate better GetByVal and PutByVal when the type speculation
are not good enough.

A typical case where this is useful is any basic block for which the type
of the object is always more restrictive than the speculation (for example,
a basic block gated by a branch only taken for on type).

* bytecode/ArrayProfile.cpp:
(JSC::dumpArrayModes):
* bytecode/ArrayProfile.h:
(JSC::arrayModeFromStructure):
* dfg/DFGArrayMode.cpp:
(JSC::DFG::ArrayMode::fromObserved):
(JSC::DFG::ArrayMode::refine):
Maintain the refine() semantic. We do not support OutOfBounds access
for GetByVal on typed array.

* runtime/IndexingType.h:
* tests/stress/typed-array-get-by-val-profiling.js: Added.
(testArray.testCode):
(testArray):
* tests/stress/typed-array-put-by-val-profiling.js: Added.
(testArray.testCode):
(testArray):

LayoutTests:

* js/regress/script-tests/typed-array-get-set-by-val-profiling.js: Added.
* js/regress/typed-array-get-set-by-val-profiling.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@183450 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/js/regress/script-tests/typed-array-get-set-by-val-profiling.js [new file with mode: 0644]
LayoutTests/js/regress/typed-array-get-set-by-val-profiling.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/ArrayProfile.cpp
Source/JavaScriptCore/bytecode/ArrayProfile.h
Source/JavaScriptCore/dfg/DFGArrayMode.cpp
Source/JavaScriptCore/runtime/IndexingType.h
Source/JavaScriptCore/tests/stress/typed-array-get-by-val-profiling.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/typed-array-put-by-val-profiling.js [new file with mode: 0644]

index 4d81ba4..aa9d8ad 100644 (file)
@@ -1,3 +1,13 @@
+2015-04-27  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Add support for typed arrays to the Array profiling
+        https://bugs.webkit.org/show_bug.cgi?id=143913
+
+        Reviewed by Filip Pizlo.
+
+        * js/regress/script-tests/typed-array-get-set-by-val-profiling.js: Added.
+        * js/regress/typed-array-get-set-by-val-profiling.html: Added.
+
 2015-04-27  Doug Russell  <d_russell@apple.com>
 
         Break up select text into multiple tests
diff --git a/LayoutTests/js/regress/script-tests/typed-array-get-set-by-val-profiling.js b/LayoutTests/js/regress/script-tests/typed-array-get-set-by-val-profiling.js
new file mode 100644 (file)
index 0000000..8e12c67
--- /dev/null
@@ -0,0 +1,78 @@
+// The type of arrayObject is polymorphic, but the access we do on it are not.
+function nonPolymorphicUint8ClampedArraySetter(arrayObject, isTypedArray) {
+    if (isTypedArray) {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            arrayObject[i] = i;
+        }
+    } else {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            arrayObject[i] = i;
+        }
+    }
+}
+noInline(nonPolymorphicUint8ClampedArraySetter);
+
+function nonPolymorphicFloat64ArraySetter(arrayObject, isTypedArray) {
+    if (isTypedArray) {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            arrayObject[i] = i + 0.5;
+        }
+    } else {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            arrayObject[i] = i + 0.5;
+        }
+    }
+}
+noInline(nonPolymorphicFloat64ArraySetter);
+
+function nonPolymorphicUint8ClampedArrayGetter(arrayObject, isTypedArray) {
+    var output = 0;
+    if (isTypedArray) {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            output += arrayObject[i];
+        }
+    } else {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            output += arrayObject[i];
+        }
+    }
+    return output;
+}
+noInline(nonPolymorphicUint8ClampedArrayGetter);
+
+function nonPolymorphicFloat64ArrayGetter(arrayObject, isTypedArray) {
+    var output = 0;
+    if (isTypedArray) {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            output += arrayObject[i];
+        }
+    } else {
+        for (var i = 0; i < arrayObject.length; ++i) {
+            output += arrayObject[i];
+        }
+    }
+    return output
+}
+noInline(nonPolymorphicFloat64ArrayGetter);
+
+function test() {
+    var uint8ClampedArray = new Uint8ClampedArray(1024);
+    var float64Array = new Float64Array(1024);
+    var regularArray = new Array(32);
+
+    var output = 0;
+    for (var i = 0; i < 5000; ++i) {
+        nonPolymorphicUint8ClampedArraySetter(uint8ClampedArray, true);
+        nonPolymorphicUint8ClampedArraySetter(regularArray, false);
+        nonPolymorphicFloat64ArraySetter(float64Array, true);
+        nonPolymorphicFloat64ArraySetter(regularArray, false);
+
+        output += nonPolymorphicUint8ClampedArrayGetter(uint8ClampedArray, true);
+        output += nonPolymorphicUint8ClampedArrayGetter(regularArray, false);
+        output += nonPolymorphicFloat64ArrayGetter(float64Array, true);
+        output += nonPolymorphicFloat64ArrayGetter(regularArray, false);
+    }
+    return output;
+}
+
+test();
diff --git a/LayoutTests/js/regress/typed-array-get-set-by-val-profiling.html b/LayoutTests/js/regress/typed-array-get-set-by-val-profiling.html
new file mode 100644 (file)
index 0000000..b310c50
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/typed-array-get-set-by-val-profiling.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 3e0d2b8..4e79006 100644 (file)
@@ -1,3 +1,36 @@
+2015-04-27  Benjamin Poulain  <bpoulain@apple.com>
+
+        [JSC] Add support for typed arrays to the Array profiling
+        https://bugs.webkit.org/show_bug.cgi?id=143913
+
+        Reviewed by Filip Pizlo.
+
+        This patch adds ArrayModes for every typed arrays. Having that information
+        let us generate better GetByVal and PutByVal when the type speculation
+        are not good enough.
+
+        A typical case where this is useful is any basic block for which the type
+        of the object is always more restrictive than the speculation (for example, 
+        a basic block gated by a branch only taken for on type).
+
+        * bytecode/ArrayProfile.cpp:
+        (JSC::dumpArrayModes):
+        * bytecode/ArrayProfile.h:
+        (JSC::arrayModeFromStructure):
+        * dfg/DFGArrayMode.cpp:
+        (JSC::DFG::ArrayMode::fromObserved):
+        (JSC::DFG::ArrayMode::refine):
+        Maintain the refine() semantic. We do not support OutOfBounds access
+        for GetByVal on typed array.
+
+        * runtime/IndexingType.h:
+        * tests/stress/typed-array-get-by-val-profiling.js: Added.
+        (testArray.testCode):
+        (testArray):
+        * tests/stress/typed-array-put-by-val-profiling.js: Added.
+        (testArray.testCode):
+        (testArray):
+
 2015-04-27  Filip Pizlo  <fpizlo@apple.com>
 
         Unreviewed, roll out r183438 "RegExp matches arrays should use contiguous indexing". It
index ef49f20..1c00c23 100644 (file)
@@ -73,6 +73,25 @@ void dumpArrayModes(PrintStream& out, ArrayModes arrayModes)
         out.print(comma, "ArrayWithArrayStorage");
     if (arrayModes & asArrayModes(ArrayWithSlowPutArrayStorage))
         out.print(comma, "ArrayWithSlowPutArrayStorage");
+
+    if (arrayModes & Int8ArrayMode)
+        out.print(comma, "Int8ArrayMode");
+    if (arrayModes & Int16ArrayMode)
+        out.print(comma, "Int16ArrayMode");
+    if (arrayModes & Int32ArrayMode)
+        out.print(comma, "Int32ArrayMode");
+    if (arrayModes & Uint8ArrayMode)
+        out.print(comma, "Uint8ArrayMode");
+    if (arrayModes & Uint8ClampedArrayMode)
+        out.print(comma, "Uint8ClampedArrayMode");
+    if (arrayModes & Uint16ArrayMode)
+        out.print(comma, "Uint16ArrayMode");
+    if (arrayModes & Uint32ArrayMode)
+        out.print(comma, "Uint32ArrayMode");
+    if (arrayModes & Float32ArrayMode)
+        out.print(comma, "Float32ArrayMode");
+    if (arrayModes & Float64ArrayMode)
+        out.print(comma, "Float64ArrayMode");
 }
 
 void ArrayProfile::computeUpdatedPrediction(const ConcurrentJITLocker&, CodeBlock* codeBlock)
index 302365f..70a386e 100644 (file)
@@ -37,20 +37,44 @@ namespace JSC {
 class CodeBlock;
 class LLIntOffsetsExtractor;
 
-// This is a bitfield where each bit represents an IndexingType that we have seen.
-// There are 32 indexing types, so an unsigned is enough.
+// This is a bitfield where each bit represents an type of array access that we have seen.
+// There are 16 indexing types that use the lower bits.
+// There are 9 typed array types taking the bits 16 to 25.
 typedef unsigned ArrayModes;
 
+const ArrayModes Int8ArrayMode = 1 << 16;
+const ArrayModes Int16ArrayMode = 1 << 17;
+const ArrayModes Int32ArrayMode = 1 << 18;
+const ArrayModes Uint8ArrayMode = 1 << 19;
+const ArrayModes Uint8ClampedArrayMode = 1 << 20;
+const ArrayModes Uint16ArrayMode = 1 << 21;
+const ArrayModes Uint32ArrayMode = 1 << 22;
+const ArrayModes Float32ArrayMode = 1 << 23;
+const ArrayModes Float64ArrayMode = 1 << 24;
+
 #define asArrayModes(type) \
     (static_cast<unsigned>(1) << static_cast<unsigned>(type))
 
+#define ALL_TYPED_ARRAY_MODES \
+    (Int8ArrayMode            \
+    | Int16ArrayMode          \
+    | Int32ArrayMode          \
+    | Uint8ArrayMode          \
+    | Uint8ClampedArrayMode   \
+    | Uint16ArrayMode         \
+    | Uint32ArrayMode         \
+    | Float32ArrayMode        \
+    | Float64ArrayMode        \
+    )
+
 #define ALL_NON_ARRAY_ARRAY_MODES                       \
     (asArrayModes(NonArray)                             \
     | asArrayModes(NonArrayWithInt32)                   \
     | asArrayModes(NonArrayWithDouble)                  \
     | asArrayModes(NonArrayWithContiguous)              \
     | asArrayModes(NonArrayWithArrayStorage)            \
-    | asArrayModes(NonArrayWithSlowPutArrayStorage))
+    | asArrayModes(NonArrayWithSlowPutArrayStorage)     \
+    | ALL_TYPED_ARRAY_MODES)
 
 #define ALL_ARRAY_ARRAY_MODES                           \
     (asArrayModes(ArrayClass)                           \
@@ -65,6 +89,29 @@ typedef unsigned ArrayModes;
 
 inline ArrayModes arrayModeFromStructure(Structure* structure)
 {
+    switch (structure->classInfo()->typedArrayStorageType) {
+    case TypeInt8:
+        return Int8ArrayMode;
+    case TypeUint8:
+        return Uint8ArrayMode;
+    case TypeUint8Clamped:
+        return Uint8ClampedArrayMode;
+    case TypeInt16:
+        return Int16ArrayMode;
+    case TypeUint16:
+        return Uint16ArrayMode;
+    case TypeInt32:
+        return Int32ArrayMode;
+    case TypeUint32:
+        return Uint32ArrayMode;
+    case TypeFloat32:
+        return Float32ArrayMode;
+    case TypeFloat64:
+        return Float64ArrayMode;
+    case TypeDataView:
+    case NotTypedArray:
+        break;
+    }
     return asArrayModes(structure->indexingType());
 }
 
index a5994fb..7f31140 100644 (file)
@@ -97,6 +97,24 @@ ArrayMode ArrayMode::fromObserved(const ConcurrentJITLocker& locker, ArrayProfil
     case asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
     case asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage):
         return ArrayMode(Array::SlowPutArrayStorage, Array::PossiblyArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Int8ArrayMode:
+        return ArrayMode(Array::Int8Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Int16ArrayMode:
+        return ArrayMode(Array::Int16Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Int32ArrayMode:
+        return ArrayMode(Array::Int32Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Uint8ArrayMode:
+        return ArrayMode(Array::Uint8Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Uint8ClampedArrayMode:
+        return ArrayMode(Array::Uint8ClampedArray, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Uint16ArrayMode:
+        return ArrayMode(Array::Uint16Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Uint32ArrayMode:
+        return ArrayMode(Array::Uint32Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Float32ArrayMode:
+        return ArrayMode(Array::Float32Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
+    case Float64ArrayMode:
+        return ArrayMode(Array::Float64Array, nonArray, Array::AsIs).withProfile(locker, profile, makeSafe);
 
     default:
         if ((observed & asArrayModes(NonArray)) && profile->mayInterceptIndexedAccesses(locker))
@@ -189,7 +207,25 @@ ArrayMode ArrayMode::refine(
         if (doesConversion() && (flags & NodeBytecodeUsesAsInt))
             return withConversion(Array::RageConvert);
         return *this;
-        
+
+    case Array::Int8Array:
+    case Array::Int16Array:
+    case Array::Int32Array:
+    case Array::Uint8Array:
+    case Array::Uint8ClampedArray:
+    case Array::Uint16Array:
+    case Array::Uint32Array:
+    case Array::Float32Array:
+    case Array::Float64Array:
+        switch (node->op()) {
+        case PutByVal:
+            if (graph.hasExitSite(node->origin.semantic, OutOfBounds) || !isInBounds())
+                return withSpeculation(Array::OutOfBounds);
+            return withSpeculation(Array::InBounds);
+        default:
+            return withSpeculation(Array::InBounds);
+        }
+        return *this;
     case Array::Unprofiled:
     case Array::SelectUsingPredictions: {
         base &= ~SpecOther;
index b28b962..3f9fdd1 100644 (file)
@@ -38,8 +38,8 @@ namespace JSC {
 
     struct IndexingType {
         uint8_t isArray:1;                    // bit 0
-        uint8_t shape:4;                      // bit 1 - 4
-        uint8_t mayHaveIndexedAccessors:1;    // bit 5
+        uint8_t shape:4;                      // bit 1 - 3
+        uint8_t mayHaveIndexedAccessors:1;    // bit 4
     };
 
     The shape values (e.g. Int32Shape, ContiguousShape, etc) are an enumeration of
@@ -53,21 +53,21 @@ typedef uint8_t IndexingType;
 static const IndexingType IsArray                  = 0x01;
 
 // The shape of the indexed property storage.
-static const IndexingType IndexingShapeMask        = 0x1E;
+static const IndexingType IndexingShapeMask        = 0x0E;
 static const IndexingType NoIndexingShape          = 0x00;
 static const IndexingType UndecidedShape           = 0x02; // Only useful for arrays.
-static const IndexingType Int32Shape               = 0x14;
-static const IndexingType DoubleShape              = 0x16;
-static const IndexingType ContiguousShape          = 0x1A;
-static const IndexingType ArrayStorageShape        = 0x1C;
-static const IndexingType SlowPutArrayStorageShape = 0x1E;
+static const IndexingType Int32Shape               = 0x04;
+static const IndexingType DoubleShape              = 0x06;
+static const IndexingType ContiguousShape          = 0x08;
+static const IndexingType ArrayStorageShape        = 0x0A;
+static const IndexingType SlowPutArrayStorageShape = 0x0C;
 
 static const IndexingType IndexingShapeShift       = 1;
-static const IndexingType NumberOfIndexingShapes   = 16;
+static const IndexingType NumberOfIndexingShapes   = 7;
 
 // Additional flags for tracking the history of the type. These are usually
 // masked off unless you ask for them directly.
-static const IndexingType MayHaveIndexedAccessors  = 0x20;
+static const IndexingType MayHaveIndexedAccessors  = 0x10;
 
 // List of acceptable array types.
 static const IndexingType NonArray                        = 0x0;
@@ -162,10 +162,10 @@ void dumpIndexingType(PrintStream&, IndexingType);
 MAKE_PRINT_ADAPTOR(IndexingTypeDump, IndexingType, dumpIndexingType);
 
 // Mask of all possible types.
-static const IndexingType AllArrayTypes            = 31;
+static const IndexingType AllArrayTypes            = IndexingShapeMask | IsArray;
 
 // Mask of all possible types including the history.
-static const IndexingType AllArrayTypesAndHistory  = 127;
+static const IndexingType AllArrayTypesAndHistory  = AllArrayTypes | MayHaveIndexedAccessors;
 
 } // namespace JSC
 
diff --git a/Source/JavaScriptCore/tests/stress/typed-array-get-by-val-profiling.js b/Source/JavaScriptCore/tests/stress/typed-array-get-by-val-profiling.js
new file mode 100644 (file)
index 0000000..f0ea641
--- /dev/null
@@ -0,0 +1,91 @@
+function testArray(arrayType)
+{
+    var testCode =
+        `
+        // We make this look like a polymorphic types for incomingObject but the GetByVal are never actually
+        // polymorphic. The boolean isTypedArray let us differentiate the types.
+        function ${ arrayType }AndObjectSpeculationInBounds(incomingObject, iterationLength, isTypedArray) {
+            var output = 0;
+            output += incomingObject.length;
+
+            if (isTypedArray) {
+                for (var i = 0; i < iterationLength; ++i) {
+                    output += incomingObject[i];
+                }
+            } else {
+                for (var i = 0; i < iterationLength; ++i) {
+                    output += incomingObject[i];
+                }
+            }
+            return output;
+        }
+        noInline(${ arrayType }AndObjectSpeculationInBounds);
+
+        var typedArray = new ${ arrayType }(64);
+        var regularArray = new Array(64);
+        for (var i = 0; i < 64; ++i) {
+            typedArray[i] = i;
+            regularArray[i] = i;
+        }
+
+        // Access in bounds.
+        for (var i = 0; i < 1e4; ++i) {
+            var output = ${ arrayType }AndObjectSpeculationInBounds(typedArray, 64, true);
+            if (output !== 32 * 65)
+                throw "${ arrayType }AndObjectSpeculationInBounds(typedArray, 64, true) failed, value = " + output;
+
+            var output = ${ arrayType }AndObjectSpeculationInBounds(regularArray, 64, false);
+            if (output !== 32 * 65)
+                throw "${ arrayType }AndObjectSpeculationInBounds(regularArray, 64, false) failed, value = " + output;
+        }
+
+        // One out of bounds on top of the in bounds profile.
+        {
+            var output = ${ arrayType }AndObjectSpeculationInBounds(typedArray, 128, true);
+            if (output === output)
+                throw "${ arrayType }AndObjectSpeculationInBounds(typedArray, 128, true) failed, value = " + output;
+
+            var output = ${ arrayType }AndObjectSpeculationInBounds(regularArray, 128, false);
+            if (output === output)
+                throw "${ arrayType }AndObjectSpeculationInBounds(regularArray, 128, false) failed, value = " + output;
+        }
+
+        // Same but here we make out-of-bounds a normal case.
+        function ${ arrayType }AndObjectSpeculationOutOfBounds(incomingObject, iterationLength, isTypedArray) {
+            var output = 0;
+            output += incomingObject.length;
+
+            if (isTypedArray) {
+                for (var i = 0; i < iterationLength; ++i) {
+                    output += incomingObject[i]|0;
+                }
+            } else {
+                for (var i = 0; i < iterationLength; ++i) {
+                    output += incomingObject[i]|0;
+                }
+            }
+            return output;
+        }
+        noInline(${ arrayType }AndObjectSpeculationOutOfBounds);
+
+        for (var i = 0; i < 1e4; ++i) {
+            var output = ${ arrayType }AndObjectSpeculationOutOfBounds(typedArray, 128, true);
+            if (output !== 32 * 65)
+                throw "${ arrayType }AndObjectSpeculationOutOfBounds(typedArray, 128, true) failed, value = " + output;
+
+            var output = ${ arrayType }AndObjectSpeculationOutOfBounds(regularArray, 128, false);
+            if (output !== 32 * 65)
+                throw "${ arrayType }AndObjectSpeculationOutOfBounds(regularArray, 128, false) failed, value = " + output;
+        }`
+    eval(testCode);
+}
+
+testArray("Int8Array");
+testArray("Uint8Array");
+testArray("Uint8ClampedArray");
+testArray("Int16Array");
+testArray("Uint16Array");
+testArray("Int32Array");
+testArray("Uint32Array");
+testArray("Float32Array");
+testArray("Float64Array");
diff --git a/Source/JavaScriptCore/tests/stress/typed-array-put-by-val-profiling.js b/Source/JavaScriptCore/tests/stress/typed-array-put-by-val-profiling.js
new file mode 100644 (file)
index 0000000..9f70a23
--- /dev/null
@@ -0,0 +1,98 @@
+function testArray(arrayType)
+{
+    var testCode =
+        `
+        function testOutOfBoundsValues(regularArray, typedArray) {
+            for (var i = 0; i < 16; ++i) {
+                var typedArrayValue = typedArray[i]
+                if (typedArrayValue !== i) {
+                    throw "Failed ${ arrayType }AndObjectSpeculationInBounds, typedArrayValue = " + typedArrayValue + " for i = " + i;
+                }
+                var regularArrayValue = regularArray[i];
+                if (regularArrayValue !== i) {
+                    throw "Failed ${ arrayType }AndObjectSpeculationInBounds, regularArrayValue = " + regularArrayValue + " for i = " + i;
+                }
+            }
+            for (var i = 16; i < 24; ++i) {
+                var typedArrayValue = typedArray[i]
+                if (typedArrayValue !== undefined) {
+                    throw "Failed ${ arrayType }AndObjectSpeculationInBounds, typedArrayValue = " + typedArrayValue + " for i = " + i;
+                }
+                var regularArrayValue = regularArray[i];
+                if (regularArrayValue !== i) {
+                    throw "Failed ${ arrayType }AndObjectSpeculationInBounds, regularArrayValue = " + regularArrayValue + " for i = " + i;
+                }
+            }
+        }
+
+        // We make this look like a polymorphic types for incomingObject but the GetByVal are never actually
+        // polymorphic. The boolean isTypedArray let us differentiate the types.
+        function ${ arrayType }AndObjectSpeculationInBounds(incomingObject, iterationLength, isTypedArray) {
+            if (isTypedArray) {
+                for (var i = 0; i < iterationLength; ++i) {
+                    incomingObject[i] = i;
+                }
+            } else {
+                for (var i = 0; i < iterationLength; ++i) {
+                    incomingObject[i] = i;
+                }
+            }
+        }
+        noInline(${ arrayType }AndObjectSpeculationInBounds);
+
+        var typedArray = new ${ arrayType }(16);
+        var regularArray = new Array(16);
+
+        // Access in bounds.
+        for (var i = 0; i < 1e4; ++i) {
+            ${ arrayType }AndObjectSpeculationInBounds(regularArray, 16, false);
+            ${ arrayType }AndObjectSpeculationInBounds(typedArray, 16, true);
+        }
+        for (var i = 0; i < 16; ++i) {
+            var typedArrayValue = typedArray[i]
+            if (typedArrayValue !== i) {
+                throw "Failed ${ arrayType }AndObjectSpeculationInBounds, typedArrayValue = " + typedArrayValue + " for i = " + i;
+            }
+            var regularArrayValue = regularArray[i];
+            if (regularArrayValue !== i) {
+                throw "Failed ${ arrayType }AndObjectSpeculationInBounds, regularArrayValue = " + regularArrayValue + " for i = " + i;
+            }
+        }
+
+        // One "out of bounds" on top of the in bounds profile.
+        ${ arrayType }AndObjectSpeculationInBounds(regularArray, 24, false);
+        ${ arrayType }AndObjectSpeculationInBounds(typedArray, 24, true);
+        testOutOfBoundsValues(regularArray, typedArray);
+
+        // Same but here we make out-of-bounds a normal case.
+        function ${ arrayType }AndObjectSpeculationOutOfBounds(incomingObject, iterationLength, isTypedArray) {
+            if (isTypedArray) {
+                for (var i = 0; i < iterationLength; ++i) {
+                    incomingObject[i] = i;
+                }
+            } else {
+                for (var i = 0; i < iterationLength; ++i) {
+                    incomingObject[i] = i;
+                }
+            }
+        }
+        noInline(${ arrayType }AndObjectSpeculationOutOfBounds);
+
+        var typedArray = new ${ arrayType }(16);
+        var regularArray = new Array(16);
+        for (var i = 0; i < 1e4; ++i) {
+            ${ arrayType }AndObjectSpeculationInBounds(regularArray, 24, false);
+            ${ arrayType }AndObjectSpeculationInBounds(typedArray, 24, true);
+        }`
+    eval(testCode);
+}
+
+testArray("Int8Array");
+testArray("Uint8Array");
+testArray("Uint8ClampedArray");
+testArray("Int16Array");
+testArray("Uint16Array");
+testArray("Int32Array");
+testArray("Uint32Array");
+testArray("Float32Array");
+testArray("Float64Array");