Avoid reloading storage pointer for indexed properties unnecessarily
https://bugs.webkit.org/show_bug.cgi?id=74136

Reviewed by Filip Pizlo.

Add a node to represent loading property storage for indexed properties.
This allows us to reduce code generated for sequential access of arrays,
strings, etc.  This results in up to 5% improvement in code that is
very heavy on indexed reads, such as matrix operations in typed arrays
and 20% faster on microbenchmarks.

Currently this is only supported by GetByVal and other similar indexed reads.

* bytecode/PredictedType.h:
(JSC::isFixedIndexedStorageObjectPrediction):
* dfg/DFGAbstractState.cpp:
(JSC::DFG::AbstractState::execute):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleIntrinsic):
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGNode.h:
* dfg/DFGPropagator.cpp:
(JSC::DFG::Propagator::propagateNodePredictions):
(JSC::DFG::Propagator::fixupNode):
(JSC::DFG::Propagator::getIndexedPropertyStorageLoadElimination):
(JSC::DFG::Propagator::performNodeCSE):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileGetCharCodeAt):
(JSC::DFG::SpeculativeJIT::compileGetByValOnString):
(JSC::DFG::SpeculativeJIT::compileGetByValOnByteArray):
(JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
(JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
(JSC::DFG::SpeculativeJIT::compileGetIndexedPropertyStorage):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@102442 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 5bc0f7d..f9ce406 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,44 @@
+2011-12-09  Oliver Hunt  <oliver@apple.com>
+
+        Avoid reloading storage pointer for indexed properties unnecessarily
+        https://bugs.webkit.org/show_bug.cgi?id=74136
+
+        Reviewed by Filip Pizlo.
+
+        Add a node to represent loading property storage for indexed properties.
+        This allows us to reduce code generated for sequential access of arrays,
+        strings, etc.  This results in up to 5% improvement in code that is 
+        very heavy on indexed reads, such as matrix operations in typed arrays
+        and 20% faster on microbenchmarks.
+
+        Currently this is only supported by GetByVal and other similar indexed reads.
+
+        * bytecode/PredictedType.h:
+        (JSC::isFixedIndexedStorageObjectPrediction):
+        * dfg/DFGAbstractState.cpp:
+        (JSC::DFG::AbstractState::execute):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleIntrinsic):
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGNode.h:
+        * dfg/DFGPropagator.cpp:
+        (JSC::DFG::Propagator::propagateNodePredictions):
+        (JSC::DFG::Propagator::fixupNode):
+        (JSC::DFG::Propagator::getIndexedPropertyStorageLoadElimination):
+        (JSC::DFG::Propagator::performNodeCSE):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::SpeculativeJIT::compileGetCharCodeAt):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnString):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnByteArray):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnIntTypedArray):
+        (JSC::DFG::SpeculativeJIT::compileGetByValOnFloatTypedArray):
+        (JSC::DFG::SpeculativeJIT::compileGetIndexedPropertyStorage):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+
 2011-12-08  Fady Samuel  <fsamuel@chromium.org>
 
         [Chromium] Enable viewport metatag
diff --git a/Source/JavaScriptCore/bytecode/PredictedType.h b/Source/JavaScriptCore/bytecode/PredictedType.h
index 5720398..f41f336 100644
--- a/Source/JavaScriptCore/bytecode/PredictedType.h
+++ b/Source/JavaScriptCore/bytecode/PredictedType.h
@@ -60,6 +60,7 @@
 static const PredictedType PredictBoolean       = 0x00020000; // It's definitely a Boolean.
 static const PredictedType PredictOther         = 0x40000000; // It's definitely none of the above.
 static const PredictedType PredictTop           = 0x7fffffff; // It can be any of the above.
+static const PredictedType FixedIndexedStorageMask = PredictByteArray | PredictInt8Array | PredictInt16Array | PredictInt32Array | PredictUint8Array | PredictUint16Array | PredictUint32Array | PredictFloat32Array | PredictFloat64Array;
 
 typedef bool (*PredictionChecker)(PredictedType);
 
@@ -83,6 +84,11 @@
     return !!(value & (PredictFinalObject | PredictOther)) && !(value & ~(PredictFinalObject | PredictOther));
 }
 
+inline bool isFixedIndexedStorageObjectPrediction(PredictedType value)
+{
+    return (value & FixedIndexedStorageMask) == value;
+}
+
 inline bool isStringPrediction(PredictedType value)
 {
     return value == PredictString;
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
index 373a0e0..b06a27c 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
+++ b/Source/JavaScriptCore/dfg/DFGAbstractState.cpp
@@ -758,12 +758,71 @@
         forNode(node.child1()).set(node.structureTransitionData().newStructure);
         m_haveStructures = true;
         break;
-            
     case GetPropertyStorage:
         forNode(node.child1()).filter(PredictCell);
         forNode(nodeIndex).clear(); // The result is not a JS value.
         break;
-            
+    case GetIndexedPropertyStorage: {
+        PredictedType basePrediction = m_graph[node.child2()].prediction();
+        if (!(basePrediction & PredictInt32) && basePrediction) {
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].prediction() == PredictString) {
+            forNode(node.child1()).filter(PredictString);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateByteArray()) {
+            forNode(node.child1()).filter(PredictByteArray);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        
+        if (m_graph[node.child1()].shouldSpeculateInt8Array()) {
+            forNode(node.child1()).filter(PredictInt8Array);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateInt16Array()) {
+            forNode(node.child1()).filter(PredictInt16Array);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateInt32Array()) {
+            forNode(node.child1()).filter(PredictInt32Array);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateUint8Array()) {
+            forNode(node.child1()).filter(PredictUint8Array);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateUint16Array()) {
+            forNode(node.child1()).filter(PredictUint16Array);
+            forNode(nodeIndex).set(PredictOther);
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateUint32Array()) {
+            forNode(node.child1()).filter(PredictUint32Array);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateFloat32Array()) {
+            forNode(node.child1()).filter(PredictFloat32Array);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        if (m_graph[node.child1()].shouldSpeculateFloat64Array()) {
+            forNode(node.child1()).filter(PredictFloat64Array);
+            forNode(nodeIndex).clear();
+            break;
+        }
+        forNode(node.child1()).filter(PredictArray);
+        forNode(nodeIndex).clear();
+        break; 
+    }
     case GetByOffset:
         forNode(node.child1()).filter(PredictCell);
         forNode(nodeIndex).makeTop();
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index 47287fb3..e8d80e0 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -1207,8 +1207,11 @@
     case CharCodeAtIntrinsic: {
         if (firstArg + 1 != lastArg)
             return false;
-
-        NodeIndex charCode = addToGraph(StringCharCodeAt, get(firstArg), getToInt32(firstArg + 1));
+        if (!(m_graph[get(firstArg)].prediction() & PredictString))
+            return false;
+        
+        NodeIndex storage = addToGraph(GetIndexedPropertyStorage, get(firstArg), getToInt32(firstArg + 1));
+        NodeIndex charCode = addToGraph(StringCharCodeAt, get(firstArg), getToInt32(firstArg + 1), storage);
         if (usesResult)
             set(resultOperand, charCode);
         return true;
@@ -1217,8 +1220,11 @@
     case CharAtIntrinsic: {
         if (firstArg + 1 != lastArg)
             return false;
-        
-        NodeIndex charCode = addToGraph(StringCharAt, get(firstArg), getToInt32(firstArg + 1));
+        if (!(m_graph[get(firstArg)].prediction() & PredictString))
+            return false;
+
+        NodeIndex storage = addToGraph(GetIndexedPropertyStorage, get(firstArg), getToInt32(firstArg + 1));
+        NodeIndex charCode = addToGraph(StringCharAt, get(firstArg), getToInt32(firstArg + 1), storage);
         if (usesResult)
             set(resultOperand, charCode);
         return true;
@@ -1611,8 +1617,8 @@
             
             NodeIndex base = get(currentInstruction[2].u.operand);
             NodeIndex property = get(currentInstruction[3].u.operand);
-
-            NodeIndex getByVal = addToGraph(GetByVal, OpInfo(0), OpInfo(prediction), base, property);
+            NodeIndex propertyStorage = addToGraph(GetIndexedPropertyStorage, base, property);
+            NodeIndex getByVal = addToGraph(GetByVal, OpInfo(0), OpInfo(prediction), base, property, propertyStorage);
             set(currentInstruction[1].u.operand, getByVal);
 
             NEXT_OPCODE(op_get_by_val);
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h
index a827983..1045bdc 100644
--- a/Source/JavaScriptCore/dfg/DFGNode.h
+++ b/Source/JavaScriptCore/dfg/DFGNode.h
@@ -233,6 +233,7 @@
     macro(CheckStructure, NodeMustGenerate) \
     macro(PutStructure, NodeMustGenerate | NodeClobbersWorld) \
     macro(GetPropertyStorage, NodeResultStorage) \
+    macro(GetIndexedPropertyStorage, NodeMustGenerate | NodeResultStorage) \
     macro(GetByOffset, NodeResultJS) \
     macro(PutByOffset, NodeMustGenerate | NodeClobbersWorld) \
     macro(GetArrayLength, NodeResultInt32) \
diff --git a/Source/JavaScriptCore/dfg/DFGPropagator.cpp b/Source/JavaScriptCore/dfg/DFGPropagator.cpp
index 87b2715..764abe0 100644
--- a/Source/JavaScriptCore/dfg/DFGPropagator.cpp
+++ b/Source/JavaScriptCore/dfg/DFGPropagator.cpp
@@ -472,11 +472,12 @@
             break;
         }
             
-        case GetPropertyStorage: {
+        case GetPropertyStorage: 
+        case GetIndexedPropertyStorage: {
             changed |= setPrediction(PredictOther);
             break;
         }
-            
+
         case GetByOffset: {
             if (node.getHeapPrediction())
                 changed |= mergePrediction(node.getHeapPrediction());
@@ -809,7 +810,23 @@
                 ASSERT_NOT_REACHED();
             break;
         }
-            
+        case GetIndexedPropertyStorage: {
+            PredictedType basePrediction = m_graph[node.child2()].prediction();
+            if (!(basePrediction & PredictInt32) && basePrediction) {
+                node.op = Phantom;
+                node.children.fixed.child1 = NoNode;
+                node.children.fixed.child2 = NoNode;
+                node.children.fixed.child3 = NoNode;
+            }
+            break;
+        }
+        case GetByVal:
+        case StringCharAt:
+        case StringCharCodeAt: {
+            if (node.child3() != NoNode && m_graph[node.child3()].op == Phantom)
+                node.children.fixed.child3 = NoNode;
+            break;
+        }
         default:
             break;
         }
@@ -1214,6 +1231,41 @@
         }
         return NoNode;
     }
+
+    NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, bool hasIntegerIndexPrediction)
+    {
+        NodeIndex start = startIndexForChildren(child1);
+        for (NodeIndex index = m_compileIndex; index-- > start;) {
+            Node& node = m_graph[index];
+            switch (node.op) {
+            case GetIndexedPropertyStorage: {
+                PredictedType basePrediction = m_graph[node.child2()].prediction();
+                bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
+                if (node.child1() == child1 && hasIntegerIndexPrediction == nodeHasIntegerIndexPrediction)
+                    return index;
+                break;
+            }
+
+            case PutByOffset:
+            case PutStructure:
+                // Changing the structure or putting to the storage cannot
+                // change the property storage pointer.
+                break;
+
+            case PutByVal:
+            case PutByValAlias:
+                if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && byValIsPure(node))
+                    break;
+                return NoNode;
+
+            default:
+                if (clobbersWorld(index))
+                    return NoNode;
+                break;
+            }
+        }
+        return NoNode;
+    }
     
     NodeIndex getScopeChainLoadElimination(unsigned depth)
     {
@@ -1403,11 +1455,18 @@
             if (checkFunctionElimination(node.function(), node.child1()))
                 eliminate();
             break;
+                
+        case GetIndexedPropertyStorage: {
+            PredictedType basePrediction = m_graph[node.child2()].prediction();
+            bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
+            setReplacement(getIndexedPropertyStorageLoadElimination(node.child1(), nodeHasIntegerIndexPrediction));
+            break;
+        }
 
         case GetPropertyStorage:
             setReplacement(getPropertyStorageLoadElimination(node.child1()));
             break;
-            
+
         case GetByOffset:
             setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1()));
             break;
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index f8648c0d..51a64ca 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -1345,12 +1345,18 @@
     ASSERT(node.child3() == NoNode);
     SpeculateCellOperand string(this, node.child1());
     SpeculateStrictInt32Operand index(this, node.child2());
+    StorageOperand storage(this, node.child3());
 
     GPRReg stringReg = string.gpr();
     GPRReg indexReg = index.gpr();
-
-    if (!isStringPrediction(m_state.forNode(node.child1()).m_type))
-        speculationCheck(JSValueSource::unboxedCell(stringReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(stringReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsStringVPtr)));
+    GPRReg storageReg = storage.gpr();
+    
+    if (!isStringPrediction(m_state.forNode(node.child1()).m_type)) {
+        ASSERT(!(at(node.child1()).prediction() & PredictString));
+        terminateSpeculativeExecution(JSValueRegs(), NoNode);
+        noResult(m_compileIndex);
+        return;
+    }
 
     // unsigned comparison so we can filter out negative indices and indices that are too large
     speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, indexReg, MacroAssembler::Address(stringReg, JSString::offsetOfLength())));
@@ -1360,20 +1366,15 @@
 
     m_jit.loadPtr(MacroAssembler::Address(stringReg, JSString::offsetOfValue()), scratchReg);
 
-    // Speculate that we're not accessing a rope
-    speculationCheck(JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, scratchReg));
-
     // Load the character into scratchReg
     JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit()));
 
-    m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
-    m_jit.load8(MacroAssembler::BaseIndex(scratchReg, indexReg, MacroAssembler::TimesOne, 0), scratchReg);
+    m_jit.load8(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesOne, 0), scratchReg);
     JITCompiler::Jump cont8Bit = m_jit.jump();
 
     is16Bit.link(&m_jit);
 
-    m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
-    m_jit.load16(MacroAssembler::BaseIndex(scratchReg, indexReg, MacroAssembler::TimesTwo, 0), scratchReg);
+    m_jit.load16(MacroAssembler::BaseIndex(storageReg, indexReg, MacroAssembler::TimesTwo, 0), scratchReg);
 
     cont8Bit.link(&m_jit);
 
@@ -1382,15 +1383,19 @@
 
 void SpeculativeJIT::compileGetByValOnString(Node& node)
 {
-    ASSERT(node.child3() == NoNode);
     SpeculateCellOperand base(this, node.child1());
     SpeculateStrictInt32Operand property(this, node.child2());
-
+    StorageOperand storage(this, node.child3());
     GPRReg baseReg = base.gpr();
     GPRReg propertyReg = property.gpr();
+    GPRReg storageReg = storage.gpr();
 
-    if (!isStringPrediction(m_state.forNode(node.child1()).m_type))
-        speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsStringVPtr)));
+    if (!isStringPrediction(m_state.forNode(node.child1()).m_type)) {
+        ASSERT(!(at(node.child1()).prediction() & PredictString));
+        terminateSpeculativeExecution(JSValueRegs(), NoNode);
+        noResult(m_compileIndex);
+        return;
+    }
 
     // unsigned comparison so we can filter out negative indices and indices that are too large
     speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSString::offsetOfLength())));
@@ -1400,20 +1405,15 @@
 
     m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), scratchReg);
 
-    // Speculate that we're not accessing a rope
-    speculationCheck(JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, scratchReg));
-
     // Load the character into scratchReg
     JITCompiler::Jump is16Bit = m_jit.branchTest32(MacroAssembler::Zero, MacroAssembler::Address(scratchReg, StringImpl::flagsOffset()), TrustedImm32(StringImpl::flagIs8Bit()));
 
-    m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
-    m_jit.load8(MacroAssembler::BaseIndex(scratchReg, propertyReg, MacroAssembler::TimesOne, 0), scratchReg);
+    m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne, 0), scratchReg);
     JITCompiler::Jump cont8Bit = m_jit.jump();
 
     is16Bit.link(&m_jit);
 
-    m_jit.loadPtr(MacroAssembler::Address(scratchReg, StringImpl::dataOffset()), scratchReg);
-    m_jit.load16(MacroAssembler::BaseIndex(scratchReg, propertyReg, MacroAssembler::TimesTwo, 0), scratchReg);
+    m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo, 0), scratchReg);
 
     // We only support ascii characters
     speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, scratchReg, TrustedImm32(0x100)));
@@ -1555,15 +1555,17 @@
 
 void SpeculativeJIT::compileGetByValOnByteArray(Node& node)
 {
-    ASSERT(node.child3() == NoNode);
     SpeculateCellOperand base(this, node.child1());
     SpeculateStrictInt32Operand property(this, node.child2());
-    
+
     GPRReg baseReg = base.gpr();
     GPRReg propertyReg = property.gpr();
-    
-    if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type))
-        speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr)));
+
+    if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type)) {
+        terminateSpeculativeExecution(JSValueRegs(), NoNode);
+        noResult(m_compileIndex);
+        return;
+    }
 
     // Load the character into scratchReg
     GPRTemporary storage(this);
@@ -1595,48 +1597,51 @@
 
 void SpeculativeJIT::compileGetByValOnIntTypedArray(const TypedArrayDescriptor& descriptor, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements, TypedArraySignedness signedness)
 {
-    ASSERT(node.child3() == NoNode);
     SpeculateCellOperand base(this, node.child1());
     SpeculateStrictInt32Operand property(this, node.child2());
+    StorageOperand storage(this, node.child3());
+
     GPRReg baseReg = base.gpr();
     GPRReg propertyReg = property.gpr();
-        
-    if (speculationRequirements != NoTypedArrayTypeSpecCheck)
-        speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
-    
-    // Load the character into scratchReg
-    GPRTemporary storage(this);
     GPRReg storageReg = storage.gpr();
-    m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
-    
-    ASSERT(speculationRequirements != NoTypedArraySpecCheck);
+
+    GPRTemporary result(this);
+    GPRReg resultReg = result.gpr();
+
+    if (speculationRequirements != NoTypedArrayTypeSpecCheck) {
+        ASSERT_NOT_REACHED();
+        terminateSpeculativeExecution(JSValueRegs(), NoNode);
+        noResult(m_compileIndex);
+        return;
+    }
+
     MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset));
-    m_jit.xorPtr(storageReg, storageReg);
+    m_jit.xorPtr(resultReg, resultReg);
     MacroAssembler::Jump outOfBounds = m_jit.jump();
     inBounds.link(&m_jit);
     switch (elementSize) {
     case 1:
-        m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne), storageReg);
+        m_jit.load8(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesOne), resultReg);
         break;
     case 2:
-        m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), storageReg);
+        m_jit.load16(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesTwo), resultReg);
         break;
     case 4:
-        m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), storageReg);
+        m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesFour), resultReg);
         break;
     default:
         ASSERT_NOT_REACHED();
     }
     outOfBounds.link(&m_jit);
     if (elementSize < 4 || signedness == SignedTypedArray)
-        integerResult(storageReg, m_compileIndex);
+        integerResult(resultReg, m_compileIndex);
     else {
-        FPRTemporary result(this);
-        m_jit.convertInt32ToDouble(storageReg, result.fpr());
-        JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, storageReg, TrustedImm32(0));
-        m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), result.fpr());
+        FPRTemporary fresult(this);
+        m_jit.convertInt32ToDouble(resultReg, fresult.fpr());
+        JITCompiler::Jump positive = m_jit.branch32(MacroAssembler::GreaterThanOrEqual, resultReg, TrustedImm32(0));
+        m_jit.addDouble(JITCompiler::AbsoluteAddress(&twoToThe32), fresult.fpr());
         positive.link(&m_jit);
-        doubleResult(result.fpr(), m_compileIndex);
+        doubleResult(fresult.fpr(), m_compileIndex);
     }
 }
 
@@ -1718,26 +1723,27 @@
 
 void SpeculativeJIT::compileGetByValOnFloatTypedArray(const TypedArrayDescriptor& descriptor, Node& node, size_t elementSize, TypedArraySpeculationRequirements speculationRequirements)
 {
-    ASSERT(node.child3() == NoNode);
     SpeculateCellOperand base(this, node.child1());
     SpeculateStrictInt32Operand property(this, node.child2());
+    StorageOperand storage(this, node.child3());
+
     GPRReg baseReg = base.gpr();
     GPRReg propertyReg = property.gpr();
-    
-    if (speculationRequirements != NoTypedArrayTypeSpecCheck)
-        speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
-    
-    // Load the character into scratchReg
-    GPRTemporary storage(this);
     GPRReg storageReg = storage.gpr();
-    m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    
+    if (speculationRequirements != NoTypedArrayTypeSpecCheck) {
+        ASSERT_NOT_REACHED();
+        terminateSpeculativeExecution(JSValueRegs(), NoNode);
+        noResult(m_compileIndex);
+        return;
+    }
+
     FPRTemporary result(this);
     FPRReg resultReg = result.fpr();
     ASSERT(speculationRequirements != NoTypedArraySpecCheck);
-    GPRTemporary scratch(this);
     MacroAssembler::Jump inBounds = m_jit.branch32(MacroAssembler::Below, propertyReg, MacroAssembler::Address(baseReg, descriptor.m_lengthOffset));
-    m_jit.move(MacroAssembler::Imm32(0), scratch.gpr());
-    m_jit.convertInt32ToDouble(scratch.gpr(), resultReg);
+    static const double zero = 0;
+    m_jit.loadDouble(&zero, resultReg);
     MacroAssembler::Jump outOfBounds = m_jit.jump();
     inBounds.link(&m_jit);
     switch (elementSize) {
@@ -2364,6 +2370,83 @@
     return nonSpeculativeStrictEq(node);
 }
 
+void SpeculativeJIT::compileGetIndexedPropertyStorage(Node& node)
+{
+    SpeculateCellOperand base(this, node.child1());
+    GPRReg baseReg = base.gpr();
+    
+    PredictedType basePrediction = at(node.child2()).prediction();
+    if (!(basePrediction & PredictInt32) && basePrediction) {
+        ASSERT_NOT_REACHED();
+        terminateSpeculativeExecution(JSValueRegs(), NoNode);
+        noResult(m_compileIndex);
+        return;
+    }
+    
+    GPRTemporary storage(this);
+    GPRReg storageReg = storage.gpr();
+    if (at(node.child1()).prediction() == PredictString) {
+        if (!isStringPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsStringVPtr)));
+
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, JSString::offsetOfValue()), storageReg);
+        
+        // Speculate that we're not accessing a rope
+        speculationCheck(JSValueRegs(), NoNode, m_jit.branchTest32(MacroAssembler::Zero, storageReg));
+
+        m_jit.loadPtr(MacroAssembler::Address(storageReg, StringImpl::dataOffset()), storageReg);
+    } else if (at(node.child1()).shouldSpeculateByteArray()) {
+        if (!isByteArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsByteArrayVPtr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, JSByteArray::offsetOfStorage()), storageReg);
+    } else if (at(node.child1()).shouldSpeculateInt8Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->int8ArrayDescriptor();
+        if (!isInt8ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else if (at(node.child1()).shouldSpeculateInt16Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->int16ArrayDescriptor();
+        if (!isInt16ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else if (at(node.child1()).shouldSpeculateInt32Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->int32ArrayDescriptor();
+        if (!isInt32ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else if (at(node.child1()).shouldSpeculateUint8Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint8ArrayDescriptor();
+        if (!isUint8ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else if (at(node.child1()).shouldSpeculateUint16Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint16ArrayDescriptor();
+        if (!isUint16ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else if (at(node.child1()).shouldSpeculateUint32Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->uint32ArrayDescriptor();
+        if (!isUint32ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else if (at(node.child1()).shouldSpeculateFloat32Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->float32ArrayDescriptor();
+        if (!isFloat32ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else if (at(node.child1()).shouldSpeculateFloat64Array()) {
+        const TypedArrayDescriptor& descriptor = m_jit.globalData()->float64ArrayDescriptor();
+        if (!isFloat64ArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(descriptor.m_vptr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, descriptor.m_storageOffset), storageReg);
+    } else {
+        if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
+            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
+        m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
+    }
+    storageResult(storageReg, m_compileIndex);
+}
+
 } } // namespace JSC::DFG
 
 #endif
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
index 2d109ba..7b03fbe 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
@@ -1967,6 +1967,7 @@
         SignedTypedArray,
         UnsignedTypedArray
     };
+    void compileGetIndexedPropertyStorage(Node&);
     void compileGetByValOnIntTypedArray(const TypedArrayDescriptor&, Node&, size_t elementSize, TypedArraySpeculationRequirements, TypedArraySignedness);
     void compilePutByValForIntTypedArray(const TypedArrayDescriptor&, GPRReg base, GPRReg property, Node&, size_t elementSize, TypedArraySpeculationRequirements, TypedArraySignedness);
     void compileGetByValOnFloatTypedArray(const TypedArrayDescriptor&, Node&, size_t elementSize, TypedArraySpeculationRequirements);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index 74b4126..654b3f64f 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -2684,37 +2684,35 @@
             break;            
         }
 
-        ASSERT(node.child3() == NoNode);
-        SpeculateCellOperand base(this, node.child1());
         SpeculateStrictInt32Operand property(this, node.child2());
-        GPRTemporary storage(this);
+        StorageOperand storage(this, node.child3());
         GPRTemporary resultTag(this);
-
-        GPRReg baseReg = base.gpr();
+        GPRTemporary resultPayload(this);
+        
         GPRReg propertyReg = property.gpr();
         GPRReg storageReg = storage.gpr();
         
         if (!m_compileOkay)
             return;
 
-        // Get the array storage. We haven't yet checked this is a JSArray, so this is only safe if
-        // an access with offset JSArray::storageOffset() is valid for all JSCells!
-        m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
-
         // Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
         // If we have predicted the base to be type array, we can skip the check.
-        if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
-            speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
-        speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
+        {
+            SpeculateCellOperand base(this, node.child1());
+            GPRReg baseReg = base.gpr();
+            if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
+                speculationCheck(JSValueSource::unboxedCell(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
+            speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
+        }
 
         // FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache
         // the storage pointer - especially if there happens to be another register free right now. If we do so,
         // then we'll need to allocate a new temporary for result.
         m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag.gpr());
         speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::Equal, resultTag.gpr(), TrustedImm32(JSValue::EmptyValueTag)));
-        m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), storageReg);
+        m_jit.load32(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload.gpr());
 
-        jsValueResult(resultTag.gpr(), storageReg, m_compileIndex);
+        jsValueResult(resultTag.gpr(), resultPayload.gpr(), m_compileIndex);
         break;
     }
 
@@ -3658,7 +3656,12 @@
         storageResult(resultGPR, m_compileIndex);
         break;
     }
-        
+
+    case GetIndexedPropertyStorage: {
+        compileGetIndexedPropertyStorage(node);
+        break;
+    }
+
     case GetByOffset: {
         StorageOperand storage(this, node.child1());
         GPRTemporary resultTag(this, storage);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index 532700c..1b5810b 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -2689,10 +2689,9 @@
             break;            
         }
 
-        ASSERT(node.child3() == NoNode);
         SpeculateCellOperand base(this, node.child1());
         SpeculateStrictInt32Operand property(this, node.child2());
-        GPRTemporary storage(this);
+        StorageOperand storage(this, node.child3());
 
         GPRReg baseReg = base.gpr();
         GPRReg propertyReg = property.gpr();
@@ -2701,12 +2700,6 @@
         if (!m_compileOkay)
             return;
 
-        // Get the array storage. We haven't yet checked this is a JSArray, so this is only safe if
-        // an access with offset JSArray::storageOffset() is valid for all JSCells!
-        m_jit.loadPtr(MacroAssembler::Address(baseReg, JSArray::storageOffset()), storageReg);
-
-        // Check that base is an array, and that property is contained within m_vector (< m_vectorLength).
-        // If we have predicted the base to be type array, we can skip the check.
         if (!isArrayPrediction(m_state.forNode(node.child1()).m_type))
             speculationCheck(JSValueRegs(baseReg), node.child1(), m_jit.branchPtr(MacroAssembler::NotEqual, MacroAssembler::Address(baseReg), MacroAssembler::TrustedImmPtr(m_jit.globalData()->jsArrayVPtr)));
         speculationCheck(JSValueRegs(), NoNode, m_jit.branch32(MacroAssembler::AboveOrEqual, propertyReg, MacroAssembler::Address(baseReg, JSArray::vectorLengthOffset())));
@@ -2714,7 +2707,7 @@
         // FIXME: In cases where there are subsequent by_val accesses to the same base it might help to cache
         // the storage pointer - especially if there happens to be another register free right now. If we do so,
         // then we'll need to allocate a new temporary for result.
-        GPRTemporary& result = storage;
+        GPRTemporary result(this);
         m_jit.loadPtr(MacroAssembler::BaseIndex(storageReg, propertyReg, MacroAssembler::ScalePtr, OBJECT_OFFSETOF(ArrayStorage, m_vector[0])), result.gpr());
         speculationCheck(JSValueRegs(), NoNode, m_jit.branchTestPtr(MacroAssembler::Zero, result.gpr()));
 
@@ -2864,7 +2857,7 @@
     case PutByValAlias: {
         PredictedType basePrediction = at(node.child2()).prediction();
         ASSERT_UNUSED(basePrediction, (basePrediction & PredictInt32) || !basePrediction);
-            
+
         SpeculateCellOperand base(this, node.child1());
         SpeculateStrictInt32Operand property(this, node.child2());
         if (at(node.child1()).shouldSpeculateByteArray()) {
@@ -3610,6 +3603,11 @@
         storageResult(resultGPR, m_compileIndex);
         break;
     }
+
+    case GetIndexedPropertyStorage: {
+        compileGetIndexedPropertyStorage(node);
+        break;
+    }
         
     case GetByOffset: {
         StorageOperand storage(this, node.child1());