Explorar o código

profile elem accesses and emit fastpaths for cache types we've seen

Michael Holman %!s(int64=8) %!d(string=hai) anos
pai
achega
181d889eb7

+ 279 - 57
lib/Backend/Lower.cpp

@@ -15435,24 +15435,25 @@ void Lowerer::InsertFloatCheckForZeroOrNanBranch(
 #endif
 }
 
-IR::IndirOpnd *
+IR::IndirOpnd*
 Lowerer::GenerateFastElemICommon(
-    IR::Instr * instr,
-    bool isStore,
-    IR::IndirOpnd * indirOpnd,
-    IR::LabelInstr * labelHelper,
-    IR::LabelInstr * labelCantUseArray,
-    IR::LabelInstr *labelFallthrough,
-    bool * pIsTypedArrayElement,
-    bool * pIsStringIndex,
-    bool *emitBailoutRef,
-    IR::Opnd** maskOpnd,
-    IR::LabelInstr **pLabelSegmentLengthIncreased /*= nullptr*/,
-    bool checkArrayLengthOverflow /*= true*/,
-    bool forceGenerateFastPath /* = false */,
-    bool returnLength,
-    IR::LabelInstr *bailOutLabelInstr /* = nullptr*/,
-    bool * indirOpndOverflowed /* = nullptr*/)
+    _In_ IR::Instr* elemInstr,
+    _In_ bool isStore,
+    _In_ IR::IndirOpnd* indirOpnd,
+    _In_ IR::LabelInstr* labelHelper,
+    _In_ IR::LabelInstr* labelCantUseArray,
+    _In_opt_ IR::LabelInstr* labelFallthrough,
+    _Out_ bool* pIsTypedArrayElement,
+    _Out_ bool* pIsStringIndex,
+    _Out_opt_ bool* emitBailoutRef,
+    _Outptr_opt_result_maybenull_ IR::Opnd** maskOpnd,
+    _Outptr_opt_result_maybenull_ IR::LabelInstr** pLabelSegmentLengthIncreased, // = nullptr
+    _In_ bool checkArrayLengthOverflow, //  = true
+    _In_ bool forceGenerateFastPath, // = false
+    _In_ bool returnLength, // = false
+    _In_opt_ IR::LabelInstr* bailOutLabelInstr, // = nullptr
+    _Out_opt_ bool* indirOpndOverflowed, // = nullptr
+    _In_ Js::FldInfoFlags flags) // = Js::FldInfo_NoInfo
 {
     *pIsTypedArrayElement = false;
     *pIsStringIndex = false;
@@ -15460,6 +15461,18 @@ Lowerer::GenerateFastElemICommon(
     {
         *pLabelSegmentLengthIncreased = nullptr;
     }
+    if (maskOpnd)
+    {
+        *maskOpnd = nullptr;
+    }
+    if (indirOpndOverflowed)
+    {
+        *indirOpndOverflowed = false;
+    }
+    if (emitBailoutRef)
+    {
+        *emitBailoutRef = false;
+    }
     IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
     AssertMsg(baseOpnd, "This shouldn't be NULL");
 
@@ -15475,14 +15488,17 @@ Lowerer::GenerateFastElemICommon(
     IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
     if (indexOpnd)
     {
+        const bool normalLocation = (flags & (Js::FldInfo_FromLocal | Js::FldInfo_FromProto | Js::FldInfo_FromLocalWithoutProperty)) != 0;
+        const bool normalSlots = (flags & (Js::FldInfo_FromAuxSlots | Js::FldInfo_FromInlineSlots)) != 0;
+        const bool generateFastpath = !baseOpnd->GetValueType().IsLikelyOptimizedTypedArray() && normalLocation && normalSlots && flags != Js::FldInfo_NoInfo;
         if (indexOpnd->GetValueType().IsLikelyString())
         {
-            if (!baseOpnd->GetValueType().IsLikelyOptimizedTypedArray())
+            if (generateFastpath)
             {
                 // If profile data says that it's a typed array - do not generate the property string fast path as the src. could be a temp and that would cause a bug.
                 *pIsTypedArrayElement = false;
                 *pIsStringIndex = true;
-                return GenerateFastElemIStringIndexCommon(instr, isStore, indirOpnd, labelHelper);
+                return GenerateFastElemIStringIndexCommon(elemInstr, isStore, indirOpnd, labelHelper, flags);
             }
             else
             {
@@ -15492,10 +15508,10 @@ Lowerer::GenerateFastElemICommon(
         }
         else if (indexOpnd->GetValueType().IsLikelySymbol())
         {
-            if (!baseOpnd->GetValueType().IsLikelyOptimizedTypedArray())
+            if (generateFastpath)
             {
                 // If profile data says that it's a typed array - do not generate the symbol fast path as the src. could be a temp and that would cause a bug.
-                return GenerateFastElemISymbolIndexCommon(instr, isStore, indirOpnd, labelHelper);
+                return GenerateFastElemISymbolIndexCommon(elemInstr, isStore, indirOpnd, labelHelper, flags);
             }
             else
             {
@@ -15506,7 +15522,7 @@ Lowerer::GenerateFastElemICommon(
     }
     return
         GenerateFastElemIIntIndexCommon(
-            instr,
+            elemInstr,
             isStore,
             indirOpnd,
             labelHelper,
@@ -15596,8 +15612,13 @@ Lowerer::GeneratePropertyStringTest(IR::RegOpnd *srcReg, IR::Instr *instrInsert,
     instrInsert->InsertBefore(propStrLoadedLabel);
 }
 
-IR::IndirOpnd *
-Lowerer::GenerateFastElemIStringIndexCommon(IR::Instr * instrInsert, bool isStore, IR::IndirOpnd * indirOpnd, IR::LabelInstr * labelHelper)
+IR::IndirOpnd*
+Lowerer::GenerateFastElemIStringIndexCommon(
+    _In_ IR::Instr* elemInstr,
+    _In_ bool isStore,
+    _In_ IR::IndirOpnd* indirOpnd,
+    _In_ IR::LabelInstr* labelHelper,
+    _In_ Js::FldInfoFlags flags)
 {
     IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
     IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
@@ -15608,16 +15629,21 @@ Lowerer::GenerateFastElemIStringIndexCommon(IR::Instr * instrInsert, bool isStor
     //      PropertyStringTest(indexOpnd, $helper)                ; verify index is string type
     //      FastElemISymbolOrStringIndexCommon(indexOpnd, baseOpnd, $helper) ; shared code with JavascriptSymbol
 
-    GeneratePropertyStringTest(indexOpnd, instrInsert, labelHelper, !isStore /*usePoison*/);
+    GeneratePropertyStringTest(indexOpnd, elemInstr, labelHelper, !isStore /*usePoison*/);
 
     const uint32 inlineCacheOffset = isStore ? Js::PropertyString::GetOffsetOfStElemInlineCache() : Js::PropertyString::GetOffsetOfLdElemInlineCache();
     const uint32 hitRateOffset = Js::PropertyString::GetOffsetOfHitRate();
 
-    return GenerateFastElemISymbolOrStringIndexCommon(instrInsert, indexOpnd, baseOpnd, inlineCacheOffset, hitRateOffset, labelHelper);
+    return GenerateFastElemISymbolOrStringIndexCommon(elemInstr, indexOpnd, baseOpnd, inlineCacheOffset, hitRateOffset, labelHelper, flags);
 }
 
-IR::IndirOpnd *
-Lowerer::GenerateFastElemISymbolIndexCommon(IR::Instr * instrInsert, bool isStore, IR::IndirOpnd * indirOpnd, IR::LabelInstr * labelHelper)
+IR::IndirOpnd*
+Lowerer::GenerateFastElemISymbolIndexCommon(
+    _In_ IR::Instr* elemInstr,
+    _In_ bool isStore,
+    _In_ IR::IndirOpnd* indirOpnd,
+    _In_ IR::LabelInstr* labelHelper,
+    _In_ Js::FldInfoFlags flags)
 {
     IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd();
     IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd();
@@ -15628,12 +15654,12 @@ Lowerer::GenerateFastElemISymbolIndexCommon(IR::Instr * instrInsert, bool isStor
     //      SymbolTest(indexOpnd, $helper)                ; verify index is symbol type
     //      FastElemISymbolOrStringIndexCommon(indexOpnd, baseOpnd, $helper) ; shared code with PropertyString
 
-    GenerateSymbolTest(indexOpnd, instrInsert, labelHelper);
+    GenerateSymbolTest(indexOpnd, elemInstr, labelHelper);
 
     const uint32 inlineCacheOffset = isStore ? Js::JavascriptSymbol::GetOffsetOfStElemInlineCache() : Js::JavascriptSymbol::GetOffsetOfLdElemInlineCache();
     const uint32 hitRateOffset = Js::JavascriptSymbol::GetOffsetOfHitRate();
 
-    return GenerateFastElemISymbolOrStringIndexCommon(instrInsert, indexOpnd, baseOpnd, inlineCacheOffset, hitRateOffset, labelHelper);
+    return GenerateFastElemISymbolOrStringIndexCommon(elemInstr, indexOpnd, baseOpnd, inlineCacheOffset, hitRateOffset, labelHelper, flags);
 }
 
 void
@@ -15649,13 +15675,20 @@ Lowerer::GenerateFastIsInSymbolOrStringIndex(IR::Instr * instrInsert, IR::RegOpn
     InsertBranch(Js::OpCode::Br, labelDone, instrInsert);
 }
 
-IR::IndirOpnd *
-Lowerer::GenerateFastElemISymbolOrStringIndexCommon(IR::Instr * instrInsert, IR::RegOpnd *indexOpnd, IR::RegOpnd *baseOpnd, const uint32 inlineCacheOffset, const uint32 hitRateOffset, IR::LabelInstr * labelHelper)
+IR::IndirOpnd*
+Lowerer::GenerateFastElemISymbolOrStringIndexCommon(
+    _In_ IR::Instr* instrInsert,
+    _In_ IR::RegOpnd* indexOpnd,
+    _In_ IR::RegOpnd* baseOpnd,
+    _In_ const uint32 inlineCacheOffset,
+    _In_ const uint32 hitRateOffset,
+    _In_ IR::LabelInstr* labelHelper,
+    _In_ Js::FldInfoFlags flags)
 {
     // Try to look up the property in the cache, or bail to helper
     IR::RegOpnd * opndSlotArray = IR::RegOpnd::New(TyMachReg, instrInsert->m_func);
     IR::RegOpnd * opndSlotIndex = IR::RegOpnd::New(TyMachReg, instrInsert->m_func);
-    GenerateLookUpInIndexCache(instrInsert, indexOpnd, baseOpnd, opndSlotArray, opndSlotIndex, inlineCacheOffset, hitRateOffset, labelHelper);
+    GenerateLookUpInIndexCache(instrInsert, indexOpnd, baseOpnd, opndSlotArray, opndSlotIndex, inlineCacheOffset, hitRateOffset, labelHelper, flags);
 
     // return [opndSlotArray + opndSlotIndex * PtrSize]
     return  IR::IndirOpnd::New(opndSlotArray, opndSlotIndex, m_lowererMD.GetDefaultIndirScale(), TyMachReg, instrInsert->m_func);
@@ -15668,7 +15701,16 @@ Lowerer::GenerateFastElemISymbolOrStringIndexCommon(IR::Instr * instrInsert, IR:
 // opndSlotArray is optional; if provided, it will receive the base address of the slot array that contains the property.
 // opndSlotIndex is optional; if provided, it will receive the index of the match within the slot array.
 void
-Lowerer::GenerateLookUpInIndexCache(IR::Instr * instrInsert, IR::RegOpnd *indexOpnd, IR::RegOpnd *baseOpnd, IR::RegOpnd *opndSlotArray, IR::RegOpnd *opndSlotIndex, const uint32 inlineCacheOffset, const uint32 hitRateOffset, IR::LabelInstr * labelHelper)
+Lowerer::GenerateLookUpInIndexCache(
+    _In_ IR::Instr* instrInsert,
+    _In_ IR::RegOpnd* indexOpnd,
+    _In_ IR::RegOpnd* baseOpnd,
+    _In_opt_ IR::RegOpnd* opndSlotArray,
+    _In_opt_ IR::RegOpnd* opndSlotIndex,
+    _In_ const uint32 inlineCacheOffset,
+    _In_ const uint32 hitRateOffset,
+    _In_ IR::LabelInstr* labelHelper,
+    _In_ Js::FldInfoFlags flags) // = Js::FldInfo_NoInfo
 {
     // Generates:
     //      MOV inlineCacheOpnd, index->inlineCache
@@ -15682,13 +15724,22 @@ Lowerer::GenerateLookUpInIndexCache(IR::Instr * instrInsert, IR::RegOpnd *indexO
     //      opndTaggedType = GenerateLoadTaggedType(objectTypeOpnd)         ; load objectTypeOpnd with InlineCacheAuxSlotTypeTag into opndTaggedType
     //      LocalInlineCacheCheck(opndTaggedType, inlineCacheOpnd, $helper) ; check for type in local aux slots, jump to $helper on failure
     //      MOV opndSlotArray, baseOpnd->auxSlots                           ; load the aux slot array
-    // $slotArrayLoadedLabel
+    // $slotIndexLoadedLabel
     //      MOV opndSlotIndex, inlineCacheOpnd->u.local.slotIndex           ; load the cached slot offset or index
     //      INC indexOpnd->hitRate
 
+    const bool fromInlineSlots = (flags & Js::FldInfo_FromInlineSlots) == Js::FldInfo_FromInlineSlots;
+    const bool fromAuxSlots = (flags & Js::FldInfo_FromAuxSlots) == Js::FldInfo_FromAuxSlots;
+    const bool fromLocal = (flags & Js::FldInfo_FromLocal) == Js::FldInfo_FromLocal;
+    const bool fromProto = (flags & Js::FldInfo_FromProto) == Js::FldInfo_FromProto;
+    const bool doAdd = (flags & Js::FldInfo_FromLocalWithoutProperty) == Js::FldInfo_FromLocalWithoutProperty;
+
+    const bool checkLocalInlineSlots = flags == Js::FldInfo_NoInfo || (fromInlineSlots && fromLocal);
+    const bool checkLocalAuxSlots = flags == Js::FldInfo_NoInfo || (fromAuxSlots && fromLocal);
+
     m_lowererMD.GenerateObjectTest(baseOpnd, instrInsert, labelHelper);
 
-    IR::RegOpnd * objectTypeOpnd = IR::RegOpnd::New(TyMachPtr, this->m_func);
+    IR::RegOpnd * objectTypeOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
     InsertMove(objectTypeOpnd, IR::IndirOpnd::New(baseOpnd, Js::RecyclableObject::GetOffsetOfType(), TyMachPtr, m_func), instrInsert);
 
     IR::RegOpnd * inlineCacheOpnd = IR::RegOpnd::New(TyMachPtr, m_func);
@@ -15696,38 +15747,204 @@ Lowerer::GenerateLookUpInIndexCache(IR::Instr * instrInsert, IR::RegOpnd *indexO
 
     GenerateDynamicLoadPolymorphicInlineCacheSlot(instrInsert, inlineCacheOpnd, objectTypeOpnd);
 
-    IR::LabelInstr * notInlineSlotsLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
-    IR::LabelInstr * slotArrayLoadedLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
+    IR::LabelInstr* slotIndexLoadedLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
 
-    GenerateLocalInlineCacheCheck(instrInsert, objectTypeOpnd, inlineCacheOpnd, notInlineSlotsLabel);
+    IR::BranchInstr* branchToPatch = nullptr;
+    IR::LabelInstr* nextLabel = nullptr;
+    IR::RegOpnd* taggedTypeOpnd = nullptr;
+    if (checkLocalInlineSlots)
+    {
+        GenerateLookUpInIndexCacheHelper<true /* CheckLocal */, true /* CheckInlineSlot */, false /* DoAdd */>(
+            instrInsert,
+            baseOpnd,
+            opndSlotArray,
+            opndSlotIndex,
+            objectTypeOpnd,
+            inlineCacheOpnd,
+            slotIndexLoadedLabel,
+            labelHelper,
+            &nextLabel,
+            &branchToPatch,
+            &taggedTypeOpnd);
+    }
+    if (checkLocalAuxSlots)
+    {
+        GenerateLookUpInIndexCacheHelper<true /* CheckLocal */, false /* CheckInlineSlot */, false /* DoAdd */>(
+            instrInsert,
+            baseOpnd,
+            opndSlotArray,
+            opndSlotIndex,
+            objectTypeOpnd,
+            inlineCacheOpnd,
+            slotIndexLoadedLabel,
+            labelHelper,
+            &nextLabel,
+            &branchToPatch,
+            &taggedTypeOpnd);
+    }
 
-    if (opndSlotArray)
+    if (fromProto)
+    {
+        if (fromInlineSlots)
+        {
+            GenerateLookUpInIndexCacheHelper<false /* CheckLocal */, true /* CheckInlineSlot */, false /* DoAdd */>(
+                instrInsert,
+                baseOpnd,
+                opndSlotArray,
+                opndSlotIndex,
+                objectTypeOpnd,
+                inlineCacheOpnd,
+                slotIndexLoadedLabel,
+                labelHelper,
+                &nextLabel,
+                &branchToPatch,
+                &taggedTypeOpnd);
+        }
+        if (fromAuxSlots)
+        {
+            GenerateLookUpInIndexCacheHelper<false /* CheckLocal */, false /* CheckInlineSlot */, false /* DoAdd */>(
+                instrInsert,
+                baseOpnd,
+                opndSlotArray,
+                opndSlotIndex,
+                objectTypeOpnd,
+                inlineCacheOpnd,
+                slotIndexLoadedLabel,
+                labelHelper,
+                &nextLabel,
+                &branchToPatch,
+                &taggedTypeOpnd);
+        }
+    }
+    if (doAdd)
     {
-        InsertMove(opndSlotArray, baseOpnd, instrInsert);
+        Assert(opndSlotArray);
+
+        if (fromInlineSlots)
+        {
+            GenerateLookUpInIndexCacheHelper<true /* CheckLocal */, true /* CheckInlineSlot */, true /* DoAdd */>(
+                instrInsert,
+                baseOpnd,
+                opndSlotArray,
+                opndSlotIndex,
+                objectTypeOpnd,
+                inlineCacheOpnd,
+                slotIndexLoadedLabel,
+                labelHelper,
+                &nextLabel,
+                &branchToPatch,
+                &taggedTypeOpnd);
+        }
+        if (fromAuxSlots)
+        {
+            GenerateLookUpInIndexCacheHelper<true /* CheckLocal */, false /* CheckInlineSlot */, true /* DoAdd */>(
+                instrInsert,
+                baseOpnd,
+                opndSlotArray,
+                opndSlotIndex,
+                objectTypeOpnd,
+                inlineCacheOpnd,
+                slotIndexLoadedLabel,
+                labelHelper,
+                &nextLabel,
+                &branchToPatch,
+                &taggedTypeOpnd);
+        }
     }
-    InsertBranch(Js::OpCode::Br, slotArrayLoadedLabel, instrInsert);
+    Assert(branchToPatch);
+    Assert(nextLabel);
 
-    instrInsert->InsertBefore(notInlineSlotsLabel);
-    IR::RegOpnd * opndTaggedType = IR::RegOpnd::New(TyMachReg, this->m_func);
-    m_lowererMD.GenerateLoadTaggedType(instrInsert, objectTypeOpnd, opndTaggedType);
-    GenerateLocalInlineCacheCheck(instrInsert, opndTaggedType, inlineCacheOpnd, labelHelper);
+    branchToPatch->SetTarget(labelHelper);
+    nextLabel->Remove();
 
-    if (opndSlotArray)
+    instrInsert->InsertBefore(slotIndexLoadedLabel);
+
+    IR::IndirOpnd * hitRateOpnd = IR::IndirOpnd::New(indexOpnd, hitRateOffset, TyInt32, m_func);
+    IR::IntConstOpnd * incOpnd = IR::IntConstOpnd::New(1, TyInt32, m_func);
+    InsertAdd(false, hitRateOpnd, hitRateOpnd, incOpnd, instrInsert);
+}
+
+template <bool CheckLocal, bool CheckInlineSlot, bool DoAdd>
+void
+Lowerer::GenerateLookUpInIndexCacheHelper(
+    _In_ IR::Instr* insertInstr,
+    _In_ IR::RegOpnd* baseOpnd,
+    _In_opt_ IR::RegOpnd* opndSlotArray,
+    _In_opt_ IR::RegOpnd* opndSlotIndex,
+    _In_ IR::RegOpnd* objectTypeOpnd,
+    _In_ IR::RegOpnd* inlineCacheOpnd,
+    _In_ IR::LabelInstr* doneLabel,
+    _In_ IR::LabelInstr* helperLabel,
+    _Outptr_ IR::LabelInstr** nextLabel,
+    _Outptr_ IR::BranchInstr** branchToPatch,
+    _Inout_ IR::RegOpnd** taggedTypeOpnd)
+{
+    CompileAssert(!DoAdd || CheckLocal);
+    AnalysisAssert(!opndSlotArray || opndSlotIndex);
+
+    *nextLabel = IR::LabelInstr::New(Js::OpCode::Label, m_func);
+
+    IR::RegOpnd* typeOpnd = nullptr;
+    if (CheckInlineSlot)
+    {
+        typeOpnd = objectTypeOpnd;
+    }
+    else
     {
-        IR::IndirOpnd * opndIndir = IR::IndirOpnd::New(baseOpnd, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, instrInsert->m_func);
-        InsertMove(opndSlotArray, opndIndir, instrInsert);
+        if (*taggedTypeOpnd == nullptr)
+        {
+            *taggedTypeOpnd = IR::RegOpnd::New(TyMachReg, m_func);
+            m_lowererMD.GenerateLoadTaggedType(insertInstr, objectTypeOpnd, *taggedTypeOpnd);
+        }
+        typeOpnd = *taggedTypeOpnd;
     }
 
-    instrInsert->InsertBefore(slotArrayLoadedLabel);
+    IR::RegOpnd* objectOpnd = nullptr;
+    if (CheckLocal)
+    {
+        *branchToPatch = GenerateLocalInlineCacheCheck(insertInstr, typeOpnd, inlineCacheOpnd, *nextLabel, DoAdd);
+        if (DoAdd)
+        {
+            if (!CheckInlineSlot)
+            {
+                GenerateAuxSlotAdjustmentRequiredCheck(insertInstr, inlineCacheOpnd, helperLabel);
+            }
+            GenerateSetObjectTypeFromInlineCache(insertInstr, baseOpnd, inlineCacheOpnd, !CheckInlineSlot);
+        }
 
-    if (opndSlotIndex)
+        objectOpnd = baseOpnd;
+    }
+    else
     {
-        InsertMove(opndSlotIndex, IR::IndirOpnd::New(inlineCacheOpnd, (int32)offsetof(Js::InlineCache, u.local.slotIndex), TyUint16, instrInsert->m_func), instrInsert);
+        *branchToPatch = GenerateProtoInlineCacheCheck(insertInstr, typeOpnd, inlineCacheOpnd, *nextLabel);
+
+        IR::RegOpnd* protoOpnd = IR::RegOpnd::New(TyMachReg, m_func);
+        int32 protoObjOffset = (int32)offsetof(Js::InlineCache, u.proto.prototypeObject);
+        IR::IndirOpnd* protoIndir = IR::IndirOpnd::New(inlineCacheOpnd, protoObjOffset, TyMachReg, m_func);
+        InsertMove(protoOpnd, protoIndir, insertInstr);
+        objectOpnd = protoOpnd;
     }
 
-    IR::IndirOpnd * hitRateOpnd = IR::IndirOpnd::New(indexOpnd, hitRateOffset, TyInt32, m_func);
-    IR::IntConstOpnd * incOpnd = IR::IntConstOpnd::New(1, TyInt32, instrInsert->m_func);
-    InsertAdd(false, hitRateOpnd, hitRateOpnd, incOpnd, instrInsert);
+    if (opndSlotArray)
+    {
+        if (CheckInlineSlot)
+        {
+            InsertMove(opndSlotArray, objectOpnd, insertInstr);
+        }
+        else
+        {
+            IR::IndirOpnd* auxIndir = IR::IndirOpnd::New(objectOpnd, Js::DynamicObject::GetOffsetOfAuxSlots(), TyMachReg, m_func);
+            InsertMove(opndSlotArray, auxIndir, insertInstr);
+        }
+
+        size_t slotIndexOffset = CheckLocal ? offsetof(Js::InlineCache, u.local.slotIndex) : offsetof(Js::InlineCache, u.proto.slotIndex);
+        IR::IndirOpnd* slotOffsetIndir = IR::IndirOpnd::New(inlineCacheOpnd, (int32)slotIndexOffset, TyUint16, m_func);
+        InsertMove(opndSlotIndex, slotOffsetIndir, insertInstr);
+    }
+
+    InsertBranch(Js::OpCode::Br, doneLabel, insertInstr);
+
+    insertInstr->InsertBefore(*nextLabel);
 }
 
 IR::IndirOpnd *
@@ -16880,6 +17097,7 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef)
     else
     {
         IR::LabelInstr * labelCantUseArray = labelHelper;
+        Js::FldInfoFlags flags = Js::FldInfo_NoInfo;
         if (isNativeArrayLoad)
         {
             if (ldElem->GetDst()->GetType() == TyVar)
@@ -16895,7 +17113,10 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef)
             labelBailOut = IR::LabelInstr::New(Js::OpCode::Label, this->m_func, true);
             labelCantUseArray = labelBailOut;
         }
-
+        if (ldElem->IsProfiledInstr())
+        {
+            flags = ldElem->AsProfiledInstr()->u.ldElemInfo->flags;
+        }
         bool isTypedArrayElement, isStringIndex, indirOpndOverflowed = false;
         IR::Opnd* maskOpnd = nullptr;
         indirOpnd =
@@ -16915,7 +17136,8 @@ Lowerer::GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef)
                 false,      /* forceGenerateFastPath */
                 false,      /* returnLength */
                 nullptr,    /* bailOutLabelInstr */
-                &indirOpndOverflowed);
+                &indirOpndOverflowed,
+                flags);
 
         IR::Opnd *dst = ldElem->GetDst();
         IRType dstType = dst->AsRegOpnd()->GetType();

+ 66 - 21
lib/Backend/Lower.h

@@ -409,23 +409,24 @@ public:
     }
 
 private:
-    IR::IndirOpnd * GenerateFastElemICommon(
-        IR::Instr * ldElem,
-        bool isStore,
-        IR::IndirOpnd * indirOpnd,
-        IR::LabelInstr * labelHelper,
-        IR::LabelInstr * labelCantUseArray,
-        IR::LabelInstr *labelFallthrough,
-        bool * pIsTypedArrayElement,
-        bool * pIsStringIndex,
-        bool *emitBailoutRef,
-        IR::Opnd** maskOpnd,
-        IR::LabelInstr **pLabelSegmentLengthIncreased = nullptr,
-        bool checkArrayLengthOverflow = true,
-        bool forceGenerateFastPath = false,
-        bool returnLength = false,
-        IR::LabelInstr *bailOutLabelInstr = nullptr,
-        bool * indirOpndOverflowed = nullptr);
+    IR::IndirOpnd* GenerateFastElemICommon(
+        _In_ IR::Instr* elemInstr,
+        _In_ bool isStore,
+        _In_ IR::IndirOpnd* indirOpnd,
+        _In_ IR::LabelInstr* labelHelper,
+        _In_ IR::LabelInstr* labelCantUseArray,
+        _In_opt_ IR::LabelInstr* labelFallthrough,
+        _Out_ bool* pIsTypedArrayElement,
+        _Out_ bool* pIsStringIndex,
+        _Out_opt_ bool* emitBailoutRef,
+        _Outptr_opt_result_maybenull_ IR::Opnd** maskOpnd,
+        _Outptr_opt_result_maybenull_ IR::LabelInstr** pLabelSegmentLengthIncreased = nullptr,
+        _In_ bool checkArrayLengthOverflow = true,
+        _In_ bool forceGenerateFastPath = false,
+        _In_ bool returnLength = false,
+        _In_opt_ IR::LabelInstr* bailOutLabelInstr = nullptr,
+        _Out_opt_ bool* indirOpndOverflowed = nullptr,
+        _In_ Js::FldInfoFlags flags = Js::FldInfo_NoInfo);
 
     IR::IndirOpnd * GenerateFastElemIIntIndexCommon(
         IR::Instr * ldElem,
@@ -444,11 +445,55 @@ private:
         IR::LabelInstr *bailOutLabelInstr = nullptr,
         bool * indirOpndOverflowed = nullptr);
 
-    IR::IndirOpnd * GenerateFastElemIStringIndexCommon(IR::Instr * ldElem, bool isStore, IR::IndirOpnd * indirOpnd, IR::LabelInstr * labelHelper);
-    IR::IndirOpnd * GenerateFastElemISymbolIndexCommon(IR::Instr * ldElem, bool isStore, IR::IndirOpnd * indirOpnd, IR::LabelInstr * labelHelper);
+    IR::IndirOpnd* GenerateFastElemIStringIndexCommon(
+        _In_ IR::Instr* elemInstr,
+        _In_ bool isStore,
+        _In_ IR::IndirOpnd* indirOpnd,
+        _In_ IR::LabelInstr* labelHelper,
+        _In_ Js::FldInfoFlags flags);
+
+    IR::IndirOpnd* GenerateFastElemISymbolIndexCommon(
+        _In_ IR::Instr* elemInstr,
+        _In_ bool isStore,
+        _In_ IR::IndirOpnd* indirOpnd,
+        _In_ IR::LabelInstr* labelHelper,
+        _In_ Js::FldInfoFlags flags);
+
+    IR::IndirOpnd* GenerateFastElemISymbolOrStringIndexCommon(
+        _In_ IR::Instr* instrInsert,
+        _In_ IR::RegOpnd* indexOpnd,
+        _In_ IR::RegOpnd* baseOpnd,
+        _In_ const uint32 inlineCacheOffset,
+        _In_ const uint32 hitRateOffset,
+        _In_ IR::LabelInstr* labelHelper,
+        _In_ Js::FldInfoFlags flags);
+
+    void GenerateLookUpInIndexCache(
+        _In_ IR::Instr* instrInsert,
+        _In_ IR::RegOpnd* indexOpnd,
+        _In_ IR::RegOpnd* baseOpnd,
+        _In_opt_ IR::RegOpnd* opndSlotArray,
+        _In_opt_ IR::RegOpnd* opndSlotIndex,
+        _In_ const uint32 inlineCacheOffset,
+        _In_ const uint32 hitRateOffset,
+        _In_ IR::LabelInstr* labelHelper,
+        _In_ Js::FldInfoFlags flags = Js::FldInfo_NoInfo);
+
+    template <bool CheckLocal, bool CheckInlineSlot, bool DoAdd>
+    void GenerateLookUpInIndexCacheHelper(
+        _In_ IR::Instr* instrInsert,
+        _In_ IR::RegOpnd* baseOpnd,
+        _In_opt_ IR::RegOpnd* opndSlotArray,
+        _In_opt_ IR::RegOpnd* opndSlotIndex,
+        _In_ IR::RegOpnd* objectTypeOpnd,
+        _In_ IR::RegOpnd* inlineCacheOpnd,
+        _In_ IR::LabelInstr* doneLabel,
+        _In_ IR::LabelInstr* helperLabel,
+        _Outptr_ IR::LabelInstr** nextLabel,
+        _Outptr_ IR::BranchInstr** branchToPatch,
+        _Inout_ IR::RegOpnd** taggedTypeOpnd);
+
     void            GenerateFastIsInSymbolOrStringIndex(IR::Instr * instrInsert, IR::RegOpnd *indexOpnd, IR::RegOpnd *baseOpnd, IR::Opnd *dest, uint32 inlineCacheOffset, const uint32 hitRateOffset, IR::LabelInstr * labelHelper, IR::LabelInstr * labelDone);
-    IR::IndirOpnd * GenerateFastElemISymbolOrStringIndexCommon(IR::Instr * instrInsert, IR::RegOpnd *indexOpnd, IR::RegOpnd *baseOpnd, const uint32 inlineCacheOffset, const uint32 hitRateOffset, IR::LabelInstr * labelHelper);
-    void            GenerateLookUpInIndexCache(IR::Instr * instrInsert, IR::RegOpnd *indexOpnd, IR::RegOpnd *baseOpnd, IR::RegOpnd *opndSlotArray, IR::RegOpnd *opndSlotIndex, const uint32 inlineCacheOffset, const uint32 hitRateOffset, IR::LabelInstr * labelHelper);
     bool            GenerateFastLdElemI(IR::Instr *& ldElem, bool *instrIsInHelperBlockRef);
     bool            GenerateFastStElemI(IR::Instr *& StElem, bool *instrIsInHelperBlockRef);
     bool            GenerateFastLdLen(IR::Instr *ldLen, bool *instrIsInHelperBlockRef);

+ 2 - 2
lib/JITIDL/JITTypes.h

@@ -251,15 +251,15 @@ typedef struct LdElemIDL
 {
     unsigned short arrayType;
     unsigned short elemType;
+    byte flags;
     byte bits;
-    IDL_PAD1(0)
 } LdElemIDL;
 
 typedef struct StElemIDL
 {
     unsigned short arrayType;
+    byte flags;
     byte bits;
-    IDL_PAD1(0)
 } StElemIDL;
 
 typedef struct ProfileDataIDL

+ 15 - 0
lib/Runtime/Language/DynamicProfileInfo.cpp

@@ -986,6 +986,21 @@ namespace Js
         stElemInfo[stElemId].wasProfiled = true;
     }
 
+    void LdElemInfo::Merge(const LdElemInfo &other)
+    {
+        arrayType = arrayType.Merge(other.arrayType);
+        elemType = elemType.Merge(other.elemType);
+        flags = DynamicProfileInfo::MergeFldInfoFlags(flags, other.flags);
+        bits |= other.bits;
+    }
+
+    void StElemInfo::Merge(const StElemInfo &other)
+    {
+        arrayType = arrayType.Merge(other.arrayType);
+        flags = DynamicProfileInfo::MergeFldInfoFlags(flags, other.flags);
+        bits |= other.bits;
+    }
+
     ArrayCallSiteInfo * DynamicProfileInfo::GetArrayCallSiteInfo(FunctionBody *functionBody, ProfileId index) const
     {
         Assert(index < functionBody->GetProfiledArrayCallSiteCount());

+ 6 - 13
lib/Runtime/Language/DynamicProfileInfo.h

@@ -218,6 +218,7 @@ namespace Js
     {
         ValueType arrayType;
         ValueType elemType;
+        FldInfoFlags flags;
 
         union
         {
@@ -230,17 +231,12 @@ namespace Js
             byte bits;
         };
 
-        LdElemInfo() : bits(0)
+        LdElemInfo() : bits(0), flags(FldInfo_NoInfo)
         {
             wasProfiled = true;
         }
 
-        void Merge(const LdElemInfo &other)
-        {
-            arrayType = arrayType.Merge(other.arrayType);
-            elemType = elemType.Merge(other.elemType);
-            bits |= other.bits;
-        }
+        void Merge(const LdElemInfo &other);
 
         ValueType GetArrayType() const
         {
@@ -271,6 +267,7 @@ namespace Js
     struct StElemInfo
     {
         ValueType arrayType;
+        FldInfoFlags flags;
 
         union
         {
@@ -287,16 +284,12 @@ namespace Js
             byte bits;
         };
 
-        StElemInfo() : bits(0)
+        StElemInfo() : bits(0), flags(FldInfo_NoInfo)
         {
             wasProfiled = true;
         }
 
-        void Merge(const StElemInfo &other)
-        {
-            arrayType = arrayType.Merge(other.arrayType);
-            bits |= other.bits;
-        }
+        void Merge(const StElemInfo &other);
 
         ValueType GetArrayType() const
         {

+ 355 - 332
lib/Runtime/Language/JavascriptOperators.cpp

@@ -1850,6 +1850,46 @@ CommonNumber:
         return FALSE;
     }
 
+    bool JavascriptOperators::GetPropertyObjectForElementAccess(
+        _In_ Var instance,
+        _In_ Var index,
+        _In_ ScriptContext* scriptContext,
+        _Out_ RecyclableObject** propertyObject,
+        _In_ rtErrors error)
+    {
+        BOOL isNullOrUndefined = !GetPropertyObject(instance, scriptContext, propertyObject);
+        Assert(*propertyObject == instance || TaggedNumber::Is(instance));
+
+        if (isNullOrUndefined)
+        {
+            if (!scriptContext->GetThreadContext()->RecordImplicitException())
+            {
+                return false;
+            }
+
+            JavascriptError::ThrowTypeError(scriptContext, error, GetPropertyDisplayNameForError(index, scriptContext));
+        }
+        return true;
+    }
+
+    bool JavascriptOperators::GetPropertyObjectForSetElementI(
+        _In_ Var instance,
+        _In_ Var index,
+        _In_ ScriptContext* scriptContext,
+        _Out_ RecyclableObject** propertyObject)
+    {
+        return GetPropertyObjectForElementAccess(instance, index, scriptContext, propertyObject, JSERR_Property_CannotSet_NullOrUndefined);
+    }
+
+    bool JavascriptOperators::GetPropertyObjectForGetElementI(
+        _In_ Var instance,
+        _In_ Var index,
+        _In_ ScriptContext* scriptContext,
+        _Out_ RecyclableObject** propertyObject)
+    {
+        return GetPropertyObjectForElementAccess(instance, index, scriptContext, propertyObject, JSERR_Property_CannotGet_NullOrUndefined);
+    }
+
     BOOL JavascriptOperators::GetPropertyObject(Var instance, ScriptContext * scriptContext, RecyclableObject** propertyObject)
     {
         Assert(propertyObject);
@@ -3370,303 +3410,310 @@ CommonNumber:
         return true;
     }
 
-    template <typename T>
-    BOOL JavascriptOperators::OP_GetElementI_ArrayFastPath(T * arr, int indexInt, Var * result, ScriptContext * scriptContext)
+    Var JavascriptOperators::GetElementIIntIndex(_In_ Var instance, _In_ Var index, _In_ ScriptContext* scriptContext)
     {
+        Assert(TaggedInt::Is(index));
+
+        switch (JavascriptOperators::GetTypeId(instance))
+        {
+        case TypeIds_Array: //fast path for array
+        {
+            Var result;
+            if (OP_GetElementI_ArrayFastPath(JavascriptArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
+            {
+                return result;
+            }
+            break;
+        }
+        case TypeIds_NativeIntArray:
+        {
 #if ENABLE_COPYONACCESS_ARRAY
-        JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arr);
+            JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
 #endif
-        if (indexInt >= 0)
-        {
-            if (!CrossSite::IsCrossSiteObjectTyped(arr))
+            Var result;
+            if (OP_GetElementI_ArrayFastPath(JavascriptNativeIntArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
             {
-                if (arr->T::DirectGetVarItemAt((uint32)indexInt, result, scriptContext))
-                {
-                    return true;
-                }
+                return result;
             }
-            else
+            break;
+        }
+        case TypeIds_NativeFloatArray:
+        {
+            Var result;
+            if (OP_GetElementI_ArrayFastPath(JavascriptNativeFloatArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
             {
-                if (arr->GetItem(arr, (uint32)indexInt, result, scriptContext))
-                {
-                    return true;
-                }
+                return result;
             }
-            return GetItemFromArrayPrototype(arr, indexInt, result, scriptContext);
+            break;
         }
-        return false;
-    }
-
-    Var JavascriptOperators::OP_GetElementI(Var instance, Var index, ScriptContext* scriptContext)
-    {
-#if ENABLE_COPYONACCESS_ARRAY
-        JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(instance);
-#endif
 
-        if (TaggedInt::Is(index))
+        case TypeIds_String: // fast path for string
         {
-        TaggedIntIndex:
-            switch (JavascriptOperators::GetTypeId(instance))
+            charcount_t indexInt = TaggedInt::ToUInt32(index);
+            JavascriptString* string = JavascriptString::UnsafeFromVar(instance);
+            Var result;
+            if (JavascriptConversion::PropertyQueryFlagsToBoolean(string->JavascriptString::GetItemQuery(instance, indexInt, &result, scriptContext)))
             {
-            case TypeIds_Array: //fast path for array
+                return result;
+            }
+            break;
+        }
+
+        case TypeIds_Int8Array:
+        {
+            // The typed array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+            if (VirtualTableInfo<Int8VirtualArray>::HasVirtualTable(instance))
             {
-                Var result;
-                if (OP_GetElementI_ArrayFastPath(JavascriptArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
+                Int8VirtualArray* int8Array = Int8VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    return result;
+                    return int8Array->DirectGetItem(indexInt);
                 }
-                break;
             }
-            case TypeIds_NativeIntArray:
+            else if (VirtualTableInfo<Int8Array>::HasVirtualTable(instance))
             {
-                Var result;
-                if (OP_GetElementI_ArrayFastPath(JavascriptNativeIntArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
+                Int8Array* int8Array = Int8Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    return result;
+                    return int8Array->DirectGetItem(indexInt);
                 }
-                break;
             }
-            case TypeIds_NativeFloatArray:
+            break;
+        }
+
+        case TypeIds_Uint8Array:
+        {
+            // The typed array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+            if (VirtualTableInfo<Uint8VirtualArray>::HasVirtualTable(instance))
             {
-                Var result;
-                if (OP_GetElementI_ArrayFastPath(JavascriptNativeFloatArray::UnsafeFromVar(instance), TaggedInt::ToInt32(index), &result, scriptContext))
+                Uint8VirtualArray* uint8Array = Uint8VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    return result;
+                    return uint8Array->DirectGetItem(indexInt);
                 }
-                break;
             }
-
-            case TypeIds_String: // fast path for string
+            else if (VirtualTableInfo<Uint8Array>::HasVirtualTable(instance))
             {
-                charcount_t indexInt = TaggedInt::ToUInt32(index);
-                JavascriptString* string = JavascriptString::UnsafeFromVar(instance);
-                Var result;
-                if (JavascriptConversion::PropertyQueryFlagsToBoolean(string->JavascriptString::GetItemQuery(instance, indexInt, &result, scriptContext)))
+                Uint8Array* uint8Array = Uint8Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    return result;
+                    return uint8Array->DirectGetItem(indexInt);
                 }
-                break;
             }
+            break;
+        }
 
-            case TypeIds_Int8Array:
+        case TypeIds_Uint8ClampedArray:
+        {
+            // The typed array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+            if (VirtualTableInfo<Uint8ClampedVirtualArray>::HasVirtualTable(instance))
             {
-                // The typed array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-                if (VirtualTableInfo<Int8VirtualArray>::HasVirtualTable(instance))
+                Uint8ClampedVirtualArray* uint8ClampedArray = Uint8ClampedVirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Int8VirtualArray* int8Array = Int8VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return int8Array->DirectGetItem(indexInt);
-                    }
+                    return uint8ClampedArray->DirectGetItem(indexInt);
                 }
-                else if (VirtualTableInfo<Int8Array>::HasVirtualTable(instance))
+            }
+            else if (VirtualTableInfo<Uint8ClampedArray>::HasVirtualTable(instance))
+            {
+                Uint8ClampedArray* uint8ClampedArray = Uint8ClampedArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Int8Array* int8Array = Int8Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return int8Array->DirectGetItem(indexInt);
-                    }
+                    return uint8ClampedArray->DirectGetItem(indexInt);
                 }
-                break;
             }
+            break;
+        }
+
+        case TypeIds_Int16Array:
+        {
+            // The type array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
 
-            case TypeIds_Uint8Array:
+            if (VirtualTableInfo<Int16VirtualArray>::HasVirtualTable(instance))
             {
-                // The typed array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-                if (VirtualTableInfo<Uint8VirtualArray>::HasVirtualTable(instance))
+                Int16VirtualArray* int16Array = Int16VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Uint8VirtualArray* uint8Array = Uint8VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint8Array->DirectGetItem(indexInt);
-                    }
+                    return int16Array->DirectGetItem(indexInt);
                 }
-                else if (VirtualTableInfo<Uint8Array>::HasVirtualTable(instance))
+            }
+            else if (VirtualTableInfo<Int16Array>::HasVirtualTable(instance))
+            {
+                Int16Array* int16Array = Int16Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Uint8Array* uint8Array = Uint8Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint8Array->DirectGetItem(indexInt);
-                    }
+                    return int16Array->DirectGetItem(indexInt);
                 }
-                break;
             }
+            break;
+        }
 
-            case TypeIds_Uint8ClampedArray:
+        case TypeIds_Uint16Array:
+        {
+            // The type array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+
+            if (VirtualTableInfo<Uint16VirtualArray>::HasVirtualTable(instance))
             {
-                // The typed array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-                if (VirtualTableInfo<Uint8ClampedVirtualArray>::HasVirtualTable(instance))
+                Uint16VirtualArray* uint16Array = Uint16VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Uint8ClampedVirtualArray* uint8ClampedArray = Uint8ClampedVirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint8ClampedArray->DirectGetItem(indexInt);
-                    }
+                    return uint16Array->DirectGetItem(indexInt);
                 }
-                else if (VirtualTableInfo<Uint8ClampedArray>::HasVirtualTable(instance))
+            }
+            else if (VirtualTableInfo<Uint16Array>::HasVirtualTable(instance))
+            {
+                Uint16Array* uint16Array = Uint16Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Uint8ClampedArray* uint8ClampedArray = Uint8ClampedArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint8ClampedArray->DirectGetItem(indexInt);
-                    }
+                    return uint16Array->DirectGetItem(indexInt);
                 }
-                break;
             }
-
-            case TypeIds_Int16Array:
+            break;
+        }
+        case TypeIds_Int32Array:
+        {
+            // The type array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+            if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(instance))
             {
-                // The type array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-
-                if (VirtualTableInfo<Int16VirtualArray>::HasVirtualTable(instance))
+                Int32VirtualArray* int32Array = Int32VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Int16VirtualArray* int16Array = Int16VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return int16Array->DirectGetItem(indexInt);
-                    }
+                    return int32Array->DirectGetItem(indexInt);
                 }
-                else if (VirtualTableInfo<Int16Array>::HasVirtualTable(instance))
+            }
+            else if (VirtualTableInfo<Int32Array>::HasVirtualTable(instance))
+            {
+                Int32Array* int32Array = Int32Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Int16Array* int16Array = Int16Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return int16Array->DirectGetItem(indexInt);
-                    }
+                    return int32Array->DirectGetItem(indexInt);
                 }
-                break;
             }
+            break;
 
-            case TypeIds_Uint16Array:
+        }
+        case TypeIds_Uint32Array:
+        {
+            // The type array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+            if (VirtualTableInfo<Uint32VirtualArray>::HasVirtualTable(instance))
             {
-                // The type array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-
-                if (VirtualTableInfo<Uint16VirtualArray>::HasVirtualTable(instance))
+                Uint32VirtualArray* uint32Array = Uint32VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Uint16VirtualArray* uint16Array = Uint16VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint16Array->DirectGetItem(indexInt);
-                    }
+                    return uint32Array->DirectGetItem(indexInt);
                 }
-                else if (VirtualTableInfo<Uint16Array>::HasVirtualTable(instance))
-                {
-                    Uint16Array* uint16Array = Uint16Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint16Array->DirectGetItem(indexInt);
-                    }
-                }
-                break;
             }
-            case TypeIds_Int32Array:
+            else if (VirtualTableInfo<Uint32Array>::HasVirtualTable(instance))
             {
-                // The type array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-                if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(instance))
+                Uint32Array* uint32Array = Uint32Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Int32VirtualArray* int32Array = Int32VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return int32Array->DirectGetItem(indexInt);
-                    }
-                }
-                else if (VirtualTableInfo<Int32Array>::HasVirtualTable(instance))
-                {
-                    Int32Array* int32Array = Int32Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return int32Array->DirectGetItem(indexInt);
-                    }
+                    return uint32Array->DirectGetItem(indexInt);
                 }
-                break;
-
             }
-            case TypeIds_Uint32Array:
+            break;
+        }
+        case TypeIds_Float32Array:
+        {
+            // The type array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+
+            if (VirtualTableInfo<Float32VirtualArray>::HasVirtualTable(instance))
             {
-                // The type array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-                if (VirtualTableInfo<Uint32VirtualArray>::HasVirtualTable(instance))
+                Float32VirtualArray* float32Array = Float32VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Uint32VirtualArray* uint32Array = Uint32VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint32Array->DirectGetItem(indexInt);
-                    }
+                    return float32Array->DirectGetItem(indexInt);
                 }
-                else if (VirtualTableInfo<Uint32Array>::HasVirtualTable(instance))
+            }
+            else if (VirtualTableInfo<Float32Array>::HasVirtualTable(instance))
+            {
+                Float32Array* float32Array = Float32Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Uint32Array* uint32Array = Uint32Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return uint32Array->DirectGetItem(indexInt);
-                    }
+                    return float32Array->DirectGetItem(indexInt);
                 }
-                break;
             }
-            case TypeIds_Float32Array:
+            break;
+        }
+        case TypeIds_Float64Array:
+        {
+            // The type array will deal with all possible values for the index
+            int32 indexInt = TaggedInt::ToInt32(index);
+            if (VirtualTableInfo<Float64VirtualArray>::HasVirtualTable(instance))
             {
-                // The type array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-
-                if (VirtualTableInfo<Float32VirtualArray>::HasVirtualTable(instance))
+                Float64VirtualArray* float64Array = Float64VirtualArray::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Float32VirtualArray* float32Array = Float32VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return float32Array->DirectGetItem(indexInt);
-                    }
+                    return float64Array->DirectGetItem(indexInt);
                 }
-                else if (VirtualTableInfo<Float32Array>::HasVirtualTable(instance))
+            }
+            else if (VirtualTableInfo<Float64Array>::HasVirtualTable(instance))
+            {
+                Float64Array* float64Array = Float64Array::UnsafeFromVar(instance);
+                if (indexInt >= 0)
                 {
-                    Float32Array* float32Array = Float32Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return float32Array->DirectGetItem(indexInt);
-                    }
+                    return float64Array->DirectGetItem(indexInt);
                 }
-                break;
             }
-            case TypeIds_Float64Array:
+            break;
+        }
+
+        default:
+            break;
+        }
+        return JavascriptOperators::GetElementIHelper(instance, index, instance, scriptContext);
+    }
+
+    template <typename T>
+    BOOL JavascriptOperators::OP_GetElementI_ArrayFastPath(T * arr, int indexInt, Var * result, ScriptContext * scriptContext)
+    {
+#if ENABLE_COPYONACCESS_ARRAY
+        JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(arr);
+#endif
+        if (indexInt >= 0)
+        {
+            if (!CrossSite::IsCrossSiteObjectTyped(arr))
             {
-                // The type array will deal with all possible values for the index
-                int32 indexInt = TaggedInt::ToInt32(index);
-                if (VirtualTableInfo<Float64VirtualArray>::HasVirtualTable(instance))
+                if (arr->T::DirectGetVarItemAt((uint32)indexInt, result, scriptContext))
                 {
-                    Float64VirtualArray* float64Array = Float64VirtualArray::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return float64Array->DirectGetItem(indexInt);
-                    }
+                    return true;
                 }
-                else if (VirtualTableInfo<Float64Array>::HasVirtualTable(instance))
+            }
+            else
+            {
+                if (arr->GetItem(arr, (uint32)indexInt, result, scriptContext))
                 {
-                    Float64Array* float64Array = Float64Array::UnsafeFromVar(instance);
-                    if (indexInt >= 0)
-                    {
-                        return float64Array->DirectGetItem(indexInt);
-                    }
+                    return true;
                 }
-                break;
             }
+            return GetItemFromArrayPrototype(arr, indexInt, result, scriptContext);
+        }
+        return false;
+    }
 
-            default:
-                break;
-            }
+    Var JavascriptOperators::OP_GetElementI(Var instance, Var index, ScriptContext* scriptContext)
+    {
+        if (TaggedInt::Is(index))
+        {
+            return GetElementIIntIndex(instance, index, scriptContext);
         }
-        else if (JavascriptNumber::Is_NoTaggedIntCheck(index))
+
+        if (JavascriptNumber::Is_NoTaggedIntCheck(index))
         {
             uint32 uint32Index = JavascriptConversion::ToUInt32(index, scriptContext);
 
             if ((double)uint32Index == JavascriptNumber::GetValue(index) && !TaggedInt::IsOverflow(uint32Index))
             {
                 index = TaggedInt::ToVarUnchecked(uint32Index);
-                goto TaggedIntIndex;
+                return GetElementIIntIndex(instance, index, scriptContext);
             }
         }
         else if (RecyclableObject::Is(instance))
@@ -3675,7 +3722,7 @@ CommonNumber:
             PropertyRecordUsageCache* propertyRecordUsageCache;
             if (GetPropertyRecordUsageCache(index, scriptContext, &propertyRecordUsageCache, &cacheOwner))
             {
-                return GetElementIWithCache(instance, cacheOwner, propertyRecordUsageCache, scriptContext);
+                return GetElementIWithCache<false /* ReturnOperationInfo */>(instance, cacheOwner, propertyRecordUsageCache, scriptContext, nullptr);
             }
         }
 
@@ -3737,13 +3784,83 @@ CommonNumber:
         return false;
     }
 
-    Var JavascriptOperators::GetElementIWithCache(Var instance, RecyclableObject* index, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext)
+    bool JavascriptOperators::SetElementIOnTaggedNumber(
+        _In_ Var receiver,
+        _In_ RecyclableObject* object,
+        _In_ Var index,
+        _In_ Var value,
+        _In_ ScriptContext* requestContext,
+        _In_ PropertyOperationFlags propertyOperationFlags)
+    {
+        Assert(TaggedNumber::Is(receiver));
+
+        uint32 indexVal = 0;
+        PropertyRecord const * propertyRecord = nullptr;
+        IndexType indexType = GetIndexType(index, requestContext, &indexVal, &propertyRecord, true);
+        if (indexType == IndexType_Number)
+        {
+            return  JavascriptOperators::SetItemOnTaggedNumber(receiver, object, indexVal, value, requestContext, propertyOperationFlags);
+        }
+        else
+        {
+            return  JavascriptOperators::SetPropertyOnTaggedNumber(receiver, object, propertyRecord->GetPropertyId(), value, requestContext, propertyOperationFlags);
+        }
+    }
+
+    template <bool ReturnOperationInfo>
+    bool JavascriptOperators::SetElementIWithCache(
+        _In_ Var receiver,
+        _In_ RecyclableObject* object,
+        _In_ RecyclableObject* index,
+        _In_ Var value,
+        _In_ PropertyRecordUsageCache* propertyRecordUsageCache,
+        _In_ ScriptContext* scriptContext,
+        _In_ PropertyOperationFlags flags,
+        _Inout_opt_ PropertyCacheOperationInfo* operationInfo)
+    {
+        if (TaggedNumber::Is(receiver))
+        {
+            return JavascriptOperators::SetElementIOnTaggedNumber(receiver, object, index, value, scriptContext, flags);
+        }
+
+        PropertyRecord const * propertyRecord = propertyRecordUsageCache->GetPropertyRecord();
+        if (propertyRecord->IsNumeric())
+        {
+            return JavascriptOperators::SetItem(receiver, object, propertyRecord->GetNumericValue(), value, scriptContext, flags);
+        }
+        PropertyValueInfo info;
+        if (receiver == object)
+        {
+            if (propertyRecordUsageCache->TrySetPropertyFromCache<ReturnOperationInfo>(object, value, scriptContext, flags, &info, index, operationInfo))
+            {
+                return true;
+            }
+        }
+        PropertyId propId = propertyRecord->GetPropertyId();
+        if (propId == PropertyIds::NaN || propId == PropertyIds::Infinity)
+        {
+            // As we no longer convert o[x] into o.x for NaN and Infinity, we need to follow SetProperty convention for these,
+            // which would check for read-only properties, strict mode, etc.
+            // Note that "-Infinity" does not qualify as property name, so we don't have to take care of it.
+            return JavascriptOperators::SetProperty(receiver, object, propId, value, scriptContext, flags);
+        }
+        return JavascriptOperators::SetPropertyWPCache(receiver, object, propId, value, scriptContext, flags, &info);
+    }
+    template bool JavascriptOperators::SetElementIWithCache<false>(Var receiver, RecyclableObject* object, RecyclableObject* index, Var value, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyOperationFlags flags, PropertyCacheOperationInfo* operationInfo);
+    template bool JavascriptOperators::SetElementIWithCache<true>(Var receiver, RecyclableObject* object, RecyclableObject* index, Var value, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyOperationFlags flags, PropertyCacheOperationInfo* operationInfo);
+
+    template <bool ReturnOperationInfo>
+    Var JavascriptOperators::GetElementIWithCache(
+        _In_ Var instance,
+        _In_ RecyclableObject* index,
+        _In_ PropertyRecordUsageCache* propertyRecordUsageCache,
+        _In_ ScriptContext* scriptContext,
+        _Inout_opt_ PropertyCacheOperationInfo* operationInfo)
     {
         RecyclableObject* object = nullptr;
-        if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
+        if (!JavascriptOperators::GetPropertyObjectForGetElementI(instance, index, scriptContext, &object))
         {
-            JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined,
-                GetPropertyDisplayNameForError(index, scriptContext));
+            return scriptContext->GetLibrary()->GetUndefined();
         }
 
         PropertyRecord const * propertyRecord = propertyRecordUsageCache->GetPropertyRecord();
@@ -3759,7 +3876,7 @@ CommonNumber:
         else
         {
             PropertyValueInfo info;
-            if (propertyRecordUsageCache->TryGetPropertyFromCache<false /* OwnPropertyOnly */, false /* OutputExistence */>(instance, object, &value, scriptContext, &info, index))
+            if (propertyRecordUsageCache->TryGetPropertyFromCache<false /* OwnPropertyOnly */, false /* OutputExistence */, ReturnOperationInfo>(instance, object, &value, scriptContext, &info, index, operationInfo))
             {
                 return value;
             }
@@ -3770,20 +3887,15 @@ CommonNumber:
         }
         return scriptContext->GetLibrary()->GetUndefined();
     }
+    template Var JavascriptOperators::GetElementIWithCache<false>(Var instance, RecyclableObject* index, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyCacheOperationInfo* operationInfo);
+    template Var JavascriptOperators::GetElementIWithCache<true>(Var instance, RecyclableObject* index, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext, PropertyCacheOperationInfo* operationInfo);
 
     Var JavascriptOperators::GetElementIHelper(Var instance, Var index, Var receiver, ScriptContext* scriptContext)
     {
         RecyclableObject* object = nullptr;
-        if (FALSE == JavascriptOperators::GetPropertyObject(instance, scriptContext, &object))
+        if (!JavascriptOperators::GetPropertyObjectForGetElementI(instance, index, scriptContext, &object))
         {
-            if (scriptContext->GetThreadContext()->RecordImplicitException())
-            {
-                JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotGet_NullOrUndefined, GetPropertyDisplayNameForError(index, scriptContext));
-            }
-            else
-            {
-                return scriptContext->GetLibrary()->GetUndefined();
-            }
+            return scriptContext->GetLibrary()->GetUndefined();
         }
 
         uint32 indexVal;
@@ -4299,7 +4411,7 @@ CommonNumber:
                     if (indexInt >= 0 && scriptContext->optimizationOverrides.IsEnabledArraySetElementFastPath())
                     {
                         JavascriptArray::UnsafeFromVar(instance)->SetItem((uint32)indexInt, value, flags);
-                        return true;
+                        return TRUE;
                     }
                     break;
                 }
@@ -4318,19 +4430,10 @@ CommonNumber:
             }
         }
 
-        RecyclableObject* object;
-        BOOL isNullOrUndefined = !GetPropertyObject(instance, scriptContext, &object);
-
-        Assert(object == instance || TaggedNumber::Is(instance));
-
-        if (isNullOrUndefined)
+        RecyclableObject* object = nullptr;
+        if (!GetPropertyObjectForSetElementI(instance, index, scriptContext, &object))
         {
-            if (!scriptContext->GetThreadContext()->RecordImplicitException())
-            {
-                return FALSE;
-            }
-
-            JavascriptError::ThrowTypeError(scriptContext, JSERR_Property_CannotSet_NullOrUndefined, GetPropertyDisplayNameForError(index, scriptContext));
+            return FALSE;
         }
 
         return JavascriptOperators::SetElementIHelper(instance, object, index, value, scriptContext, flags);
@@ -4344,100 +4447,28 @@ CommonNumber:
         JavascriptString * propertyNameString = nullptr;
         PropertyValueInfo propertyValueInfo;
 
-        if (TaggedNumber::Is(receiver))
-        {
-            indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, true);
-            if (indexType == IndexType_Number)
-            {
-                return  JavascriptOperators::SetItemOnTaggedNumber(receiver, object, indexVal, value, scriptContext, flags);
-            }
-            else
-            {
-                return  JavascriptOperators::SetPropertyOnTaggedNumber(receiver, object, propertyRecord->GetPropertyId(), value, scriptContext, flags);
-            }
-        }
-
-        // fastpath for PropertyStrings only if receiver == object
-        PropertyString * propertyString = PropertyString::TryFromVar(index);
-        if (propertyString == nullptr)
-        {
-            LiteralStringWithPropertyStringPtr * strWithPtr = LiteralStringWithPropertyStringPtr::TryFromVar(index);
-            if (strWithPtr != nullptr)
-            {
-                propertyString = strWithPtr->GetPropertyString();   // do not force create the PropertyString,
-                                                                    // if it wasn't there, it won't be efficient for now.
-                strWithPtr->GetPropertyRecord(&propertyRecord, true /* dontLookupFromDictionary */);
-                if (propertyRecord == nullptr)
-                {
-                    strWithPtr->GetPropertyRecord(&propertyRecord); // lookup-cache propertyRecord
-                                                                    // later this call, there will be a lookup anyways!
-                }
-                else if (propertyString == nullptr)
-                {
-                    propertyString = strWithPtr->GetOrAddPropertyString(); // this is the second time this property is here
-                                                                           // we already had created the propertyRecord..
-                                                                           // now create the propertyString!
-                }
-            }
-        }
-        else
+        RecyclableObject* cacheOwner;
+        PropertyRecordUsageCache* propertyRecordUsageCache;
+        if (JavascriptOperators::GetPropertyRecordUsageCache(index, scriptContext, &propertyRecordUsageCache, &cacheOwner))
         {
-            propertyString->GetPropertyRecord(&propertyRecord);
+            return JavascriptOperators::SetElementIWithCache<false>(receiver, object, cacheOwner, value, propertyRecordUsageCache, scriptContext, flags, nullptr);
         }
 
-        // fastpath for Symbols only if receiver == object
-        JavascriptSymbol * symbol = nullptr;
-
-        if (propertyString == nullptr)
+        if (TaggedNumber::Is(receiver))
         {
-            symbol = JavascriptOperators::TryFromVar<JavascriptSymbol>(index);
-            if (symbol != nullptr && propertyRecord == nullptr)
-            {
-                propertyRecord = symbol->GetValue();
-            }
+            return JavascriptOperators::SetElementIOnTaggedNumber(receiver, object, index, value, scriptContext, flags);
         }
 
-        if (propertyRecord != nullptr)
-        {
-            if (propertyRecord->IsNumeric())
-            {
-                indexType = IndexType_Number;
-                indexVal = propertyRecord->GetNumericValue();
-            }
-            else
-            {
-                if (propertyString != nullptr && receiver == object)
-                {
-                    Assert(propertyString->GetScriptContext() == scriptContext);
-                    if (propertyString->TrySetPropertyFromCache(object, value, scriptContext, flags, &propertyValueInfo))
-                    {
-                        return true;
-                    }
-                }
-                else if (symbol != nullptr && receiver == object)
-                {
-                    Assert(symbol->GetScriptContext() == scriptContext);
-                    if (symbol->GetPropertyRecordUsageCache()->TrySetPropertyFromCache(object, value, scriptContext, flags, &propertyValueInfo, symbol))
-                    {
-                        return true;
-                    }
-                }
-                indexType = IndexType_PropertyId;
-            }
-        }
-        else
-        {
 #if DBG_DUMP
-            scriptContext->forinNoCache += (!TaggedInt::Is(index) && JavascriptString::Is(index));
+        scriptContext->forinNoCache += (!TaggedInt::Is(index) && JavascriptString::Is(index));
 #endif
-            indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, &propertyNameString, false, true);
-            if (scriptContext->GetThreadContext()->IsDisableImplicitCall() &&
-                scriptContext->GetThreadContext()->GetImplicitCallFlags() != ImplicitCall_None)
-            {
-                // We hit an implicit call trying to convert the index, and implicit calls are disabled, so
-                // quit before we try to store the element.
-                return FALSE;
-            }
+        indexType = GetIndexType(index, scriptContext, &indexVal, &propertyRecord, &propertyNameString, false, true);
+        if (scriptContext->GetThreadContext()->IsDisableImplicitCall() &&
+            scriptContext->GetThreadContext()->GetImplicitCallFlags() != ImplicitCall_None)
+        {
+            // We hit an implicit call trying to convert the index, and implicit calls are disabled, so
+            // quit before we try to store the element.
+            return FALSE;
         }
 
         if (indexType == IndexType_Number)
@@ -4466,15 +4497,7 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
 
         Assert(indexType == IndexType_PropertyId || indexType == IndexType_JavascriptString);
         Assert(propertyRecord);
-        PropertyId propId = propertyRecord->GetPropertyId();
-        if (propId == PropertyIds::NaN || propId == PropertyIds::Infinity)
-        {
-            // As we no longer convert o[x] into o.x for NaN and Infinity, we need to follow SetProperty convention for these,
-            // which would check for read-only properties, strict mode, etc.
-            // Note that "-Infinity" does not qualify as property name, so we don't have to take care of it.
-            return JavascriptOperators::SetProperty(receiver, object, propId, value, scriptContext, flags);
-        }
-        return SetPropertyWPCache(receiver, object, propId, value, scriptContext, flags, &propertyValueInfo);
+        return JavascriptOperators::SetProperty(receiver, object, propertyRecord->GetPropertyId(), value, scriptContext, flags);
     }
 
     BOOL JavascriptOperators::OP_SetNativeIntElementI(
@@ -7203,7 +7226,7 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
             if (!propertyRecord->IsNumeric())
             {
                 PropertyValueInfo info;
-                if (propertyRecordUsageCache->TryGetPropertyFromCache<false /* OwnPropertyOnly */, true /* OutputExistence */>(instance, object, &value, scriptContext, &info, cacheOwner))
+                if (propertyRecordUsageCache->TryGetPropertyFromCache<false /* OwnPropertyOnly */, true /* OutputExistence */, false /* ReturnOperationInfo */>(instance, object, &value, scriptContext, &info, cacheOwner, nullptr))
                 {
                     Assert(JavascriptBoolean::Is(value));
                     return value;

+ 53 - 8
lib/Runtime/Language/JavascriptOperators.h

@@ -191,6 +191,26 @@ namespace Js
 
         static BOOL GetProperty(Var instance, RecyclableObject* propertyObject, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info = NULL);
         static BOOL GetPropertyObject(Var instance, ScriptContext * scriptContext, RecyclableObject** propertyObject);
+
+        static bool GetPropertyObjectForElementAccess(
+            _In_ Var instance,
+            _In_ Var index,
+            _In_ ScriptContext* scriptContext,
+            _Out_ RecyclableObject** propertyObject,
+            _In_ rtErrors error);
+
+        static bool GetPropertyObjectForSetElementI(
+            _In_ Var instance,
+            _In_ Var index,
+            _In_ ScriptContext* scriptContext,
+            _Out_ RecyclableObject** propertyObject);
+
+        static bool GetPropertyObjectForGetElementI(
+            _In_ Var instance,
+            _In_ Var index,
+            _In_ ScriptContext* scriptContext,
+            _Out_ RecyclableObject** propertyObject);
+
         static BOOL GetRootProperty(Var instance, PropertyId propertyId, Var* value, ScriptContext* requestContext, PropertyValueInfo* info = NULL);
         static Var  GetRootProperty(RecyclableObject* instance, PropertyId propertyId, ScriptContext* requestContext, PropertyValueInfo* info = NULL);
         static Var  GetPropertyReference(RecyclableObject* instance, PropertyId propertyId, ScriptContext* requestContext);
@@ -306,6 +326,13 @@ namespace Js
         static Var OP_GetRootProperty(Var instance, PropertyId propertyId, PropertyValueInfo * info, ScriptContext* scriptContext);
 
         static BOOL OP_SetProperty(Var instance, PropertyId propertyId, Var newValue, ScriptContext* scriptContext, PropertyValueInfo * info = nullptr, PropertyOperationFlags flags = PropertyOperation_None, Var thisInstance = nullptr);
+        static bool SetElementIOnTaggedNumber(
+            _In_ Var receiver,
+            _In_ RecyclableObject* object,
+            _In_ Var index,
+            _In_ Var value,
+            _In_ ScriptContext* requestContext,
+            _In_ PropertyOperationFlags propertyOperationFlags);
         static BOOL SetPropertyOnTaggedNumber(Var instance, RecyclableObject* object, PropertyId propertyId, Var newValue, ScriptContext* requestContext, PropertyOperationFlags flags);
         static BOOL SetItemOnTaggedNumber(Var instance, RecyclableObject* object, uint32 index, Var newValue, ScriptContext* requestContext, PropertyOperationFlags propertyOperationFlags);
         static BOOL OP_StFunctionExpression(Var instance, PropertyId propertyId, Var newValue);
@@ -630,6 +657,31 @@ namespace Js
         static RecyclableObject* SpeciesConstructor(_In_ RecyclableObject* object, _In_ JavascriptFunction* defaultConstructor, _In_ ScriptContext* scriptContext);
         static Var GetSpecies(RecyclableObject* constructor, ScriptContext* scriptContext);
 
+        // Get the property record usage cache from the given index variable, if it has one.
+        // Adds a PropertyString to a LiteralStringWithPropertyStringPtr on second call with that string.
+        // Also outputs the object that owns the usage cache, since PropertyRecordUsageCache is an interior pointer.
+        // Returns whether a PropertyRecordUsageCache was found.
+        _Success_(return) static bool GetPropertyRecordUsageCache(Var index, ScriptContext* scriptContext, _Outptr_ PropertyRecordUsageCache** propertyRecordUsageCache, _Outptr_ RecyclableObject** cacheOwner);
+
+        template <bool ReturnOperationInfo>
+        static bool SetElementIWithCache(
+            _In_ Var receiver,
+            _In_ RecyclableObject* object,
+            _In_ RecyclableObject* index,
+            _In_ Var value,
+            _In_ PropertyRecordUsageCache* propertyRecordUsageCache,
+            _In_ ScriptContext* scriptContext,
+            _In_ PropertyOperationFlags flags,
+            _Inout_opt_ PropertyCacheOperationInfo* operationInfo);
+
+        template <bool ReturnOperationInfo>
+        static Var GetElementIWithCache(
+            _In_ Var instance,
+            _In_ RecyclableObject* index,
+            _In_ PropertyRecordUsageCache* propertyRecordUsageCache,
+            _In_ ScriptContext* scriptContext,
+            _Inout_opt_ PropertyCacheOperationInfo* operationInfo);
+
     private:
         static BOOL RelationalComparisonHelper(Var aLeft, Var aRight, ScriptContext* scriptContext, bool leftFirst, bool undefinedAs);
 
@@ -644,6 +696,7 @@ namespace Js
         template <typename ArrayType>
         static Js::Var GetElementAtIndex(ArrayType* arrayObject, UINT index, Js::ScriptContext* scriptContext);
 
+        static Var GetElementIIntIndex(_In_ Var instance, _In_ Var index, _In_ ScriptContext* scriptContext);
 #if DBG
         static BOOL IsPropertyObject(RecyclableObject * instance);
 #endif
@@ -702,8 +755,6 @@ namespace Js
         template <typename T>
         static BOOL OP_GetElementI_ArrayFastPath(T * arr, int indexInt, Var * result, ScriptContext * scriptContext);
 
-        static Var GetElementIWithCache(Var instance, RecyclableObject* index, PropertyRecordUsageCache* propertyRecordUsageCache, ScriptContext* scriptContext);
-
         static ImplicitCallFlags  CacheAndClearImplicitBit(ScriptContext* scriptContext);
 
         static ImplicitCallFlags CheckAndUpdateFunctionBodyWithImplicitFlag(FunctionBody* functionBody);
@@ -714,12 +765,6 @@ namespace Js
         static BOOL ToPropertyDescriptorForGenericObjects(Var propertySpec, PropertyDescriptor* descriptor, ScriptContext* scriptContext);
 
         static BOOL IsRemoteArray(RecyclableObject* instance);
-
-        // Get the property record usage cache from the given index variable, if it has one.
-        // Adds a PropertyString to a LiteralStringWithPropertyStringPtr on second call with that string.
-        // Also outputs the object that owns the usage cache, since PropertyRecordUsageCache is an interior pointer.
-        // Returns whether a PropertyRecordUsageCache was found.
-        _Success_(return) static bool GetPropertyRecordUsageCache(Var index, ScriptContext* scriptContext, _Outptr_ PropertyRecordUsageCache** propertyRecordUsageCache, _Outptr_ RecyclableObject** cacheOwner);
     };
 
 } // namespace Js

+ 38 - 2
lib/Runtime/Language/ProfilingHelpers.cpp

@@ -105,7 +105,22 @@ namespace Js
             }
         } while(false);
 
-        const Var element = JavascriptOperators::OP_GetElementI(base, varIndex, functionBody->GetScriptContext());
+        ScriptContext* scriptContext = functionBody->GetScriptContext();
+        RecyclableObject* cacheOwner;
+        PropertyRecordUsageCache* propertyRecordUsageCache;
+        Var element = nullptr;
+        if (JavascriptOperators::GetPropertyRecordUsageCache(varIndex, scriptContext, &propertyRecordUsageCache, &cacheOwner))
+        {
+            PropertyCacheOperationInfo operationInfo;
+            element = JavascriptOperators::GetElementIWithCache<true /* ReturnOperationInfo */>(base, cacheOwner, propertyRecordUsageCache, scriptContext, &operationInfo);
+
+            ldElemInfo.flags = DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType);
+            ldElemInfo.flags = DynamicProfileInfo::MergeFldInfoFlags(ldElemInfo.flags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
+        }
+        else
+        {
+            element = JavascriptOperators::OP_GetElementI(base, varIndex, scriptContext);
+        }
 
         const ValueType arrayType(ldElemInfo.GetArrayType());
         if(!arrayType.IsUninitialized())
@@ -119,6 +134,7 @@ namespace Js
             }
 
             ldElemInfo.elemType = ValueType::Uninitialized.Merge(element);
+
             functionBody->GetDynamicProfileInfo()->RecordElementLoad(functionBody, profileId, ldElemInfo);
             return element;
         }
@@ -337,7 +353,26 @@ namespace Js
             }
         } while(false);
 
-        JavascriptOperators::OP_SetElementI(base, varIndex, value, scriptContext, flags);
+        RecyclableObject* cacheOwner;
+        PropertyRecordUsageCache* propertyRecordUsageCache;
+        TypeId instanceType = JavascriptOperators::GetTypeId(base);
+        bool isTypedArray = (instanceType >= TypeIds_Int8Array && instanceType <= TypeIds_Float64Array);
+        if (!isTypedArray && JavascriptOperators::GetPropertyRecordUsageCache(varIndex, scriptContext, &propertyRecordUsageCache, &cacheOwner))
+        {
+            RecyclableObject* object = nullptr;
+            bool result = JavascriptOperators::GetPropertyObjectForSetElementI(base, cacheOwner, scriptContext, &object);
+            Assert(result);
+
+            PropertyCacheOperationInfo operationInfo;
+            JavascriptOperators::SetElementIWithCache<true /* ReturnOperationInfo */>(base, object, cacheOwner, value, propertyRecordUsageCache, scriptContext, flags, &operationInfo);
+
+            stElemInfo.flags = DynamicProfileInfo::FldInfoFlagsFromCacheType(operationInfo.cacheType);
+            stElemInfo.flags = DynamicProfileInfo::MergeFldInfoFlags(stElemInfo.flags, DynamicProfileInfo::FldInfoFlagsFromSlotType(operationInfo.slotType));
+        }
+        else
+        {
+            JavascriptOperators::OP_SetElementI(base, varIndex, value, scriptContext, flags);
+        }
 
         if(!stElemInfo.GetArrayType().IsUninitialized())
         {
@@ -345,6 +380,7 @@ namespace Js
             {
                 stElemInfo.createdMissingValue &= !array->HasNoMissingValues();
             }
+
             functionBody->GetDynamicProfileInfo()->RecordElementStore(functionBody, profileId, stElemInfo);
             return;
         }

+ 14 - 0
lib/Runtime/Library/JavascriptArray.cpp

@@ -12265,6 +12265,20 @@ Case0:
         return DynamicObject::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext);
     }
 
+#if ENABLE_COPYONACCESS_ARRAY
+    PropertyQueryFlags JavascriptCopyOnAccessNativeIntArray::GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
+    {
+        this->ConvertCopyOnAccessSegment();
+        return JavascriptArray::GetPropertyQuery(originalInstance, propertyId, value, info, requestContext);
+    }
+
+    PropertyQueryFlags JavascriptCopyOnAccessNativeIntArray::GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext)
+    {
+        this->ConvertCopyOnAccessSegment();
+        return JavascriptArray::GetPropertyQuery(originalInstance, propertyNameString, value, info, requestContext);
+    }
+#endif
+
     BOOL JavascriptArray::GetPropertyBuiltIns(PropertyId propertyId, Var* value)
     {
         //

+ 2 - 0
lib/Runtime/Library/JavascriptArray.h

@@ -1137,6 +1137,8 @@ namespace Js
         uint32 GetNextIndex(uint32 index) const;
         BOOL DirectGetItemAt(uint32 index, int* outVal);
 
+        virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, PropertyId propertyId, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
+        virtual PropertyQueryFlags GetPropertyQuery(Var originalInstance, JavascriptString* propertyNameString, Var* value, PropertyValueInfo* info, ScriptContext* requestContext) override;
         static VTableValue VtableHelper()
         {
             return VTableValue::VtableCopyOnAccessNativeIntArray;

+ 0 - 55
lib/Runtime/Library/PropertyRecordUsageCache.cpp

@@ -41,61 +41,6 @@ namespace Js
         return this->hitRate > (int)CONFIG_FLAG(PropertyCacheMissThreshold);
     }
 
-    bool PropertyRecordUsageCache::TrySetPropertyFromCache(
-        _In_ RecyclableObject *const object,
-        _In_ Var propertyValue,
-        _In_ ScriptContext *const requestContext,
-        const PropertyOperationFlags propertyOperationFlags,
-        _Inout_ PropertyValueInfo *const propertyValueInfo,
-        RecyclableObject *const owner /* Object that this usage cache is part of */)
-    {
-        if (ShouldUseCache())
-        {
-            PropertyValueInfo::SetCacheInfo(propertyValueInfo, owner, this, GetStElemInlineCache(), true /* allowResizing */);
-            bool found = CacheOperators::TrySetProperty<
-                true,   // CheckLocal
-                true,   // CheckLocalTypeWithoutProperty
-                true,   // CheckAccessor
-                true,   // CheckPolymorphicInlineCache
-                true,   // CheckTypePropertyCache
-                false,  // IsInlineCacheAvailable
-                true,   // IsPolymorphicInlineCacheAvailable
-                false>  // ReturnOperationInfo
-                    (object,
-                    false, // isRoot
-                    this->propertyRecord->GetPropertyId(),
-                    propertyValue,
-                    requestContext,
-                    propertyOperationFlags,
-                    nullptr, // operationInfo
-                    propertyValueInfo);
-
-            if (found)
-            {
-#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
-                if (PHASE_TRACE1(PropertyCachePhase))
-                {
-                    Output::Print(_u("PropertyCache: SetElem cache hit for '%s': type %p\n"), GetString(), object->GetType());
-                }
-#endif
-                RegisterCacheHit();
-                return true;
-            }
-        }
-        RegisterCacheMiss();
-#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
-        if (PHASE_TRACE1(PropertyCachePhase))
-        {
-            Output::Print(_u("PropertyCache: SetElem cache miss for '%s': type %p, index %d\n"),
-                GetString(),
-                object->GetType(),
-                GetStElemInlineCache()->GetInlineCacheIndexForType(object->GetType()));
-            DumpCache(false);
-        }
-#endif
-        return false;
-    }
-
     void PropertyRecordUsageCache::RegisterCacheMiss()
     {
         this->hitRate -= (int)CONFIG_FLAG(PropertyCacheMissPenalty);

+ 77 - 13
lib/Runtime/Library/PropertyRecordUsageCache.h

@@ -34,25 +34,29 @@ namespace Js
         static uint32 GetOffsetOfStElemInlineCache() { return offsetof(PropertyRecordUsageCache, stElemInlineCache); }
         static uint32 GetOffsetOfHitRate() { return offsetof(PropertyRecordUsageCache, hitRate); }
 
-        bool TrySetPropertyFromCache(
+        template <bool ReturnOperationInfo>
+        _Success_(return) bool TrySetPropertyFromCache(
             _In_ RecyclableObject *const object,
             _In_ Var propertyValue,
             _In_ ScriptContext *const requestContext,
             const PropertyOperationFlags propertyOperationFlags,
-            _Inout_ PropertyValueInfo *const propertyValueInfo,
-            RecyclableObject *const owner /* Object that this usage cache is part of */);
+            _Out_ PropertyValueInfo *const propertyValueInfo,
+            _In_ RecyclableObject *const owner, // Object that this usage cache is part of
+            _Out_opt_ PropertyCacheOperationInfo* operationInfo);
 
 
         template <
             bool OwnPropertyOnly,
-            bool OutputExistence /*When set, propertyValue represents whether the property exists on the instance, not its actual value*/>
-        inline bool TryGetPropertyFromCache(
-            Var const instance,
-            RecyclableObject *const object,
-            Var *const propertyValue,
-            ScriptContext *const requestContext,
-            PropertyValueInfo *const propertyValueInfo,
-            RecyclableObject *const owner /* Object that this usage cache is part of */)
+            bool OutputExistence, // When set, propertyValue represents whether the property exists on the instance, not its actual value
+            bool ReturnOperationInfo>
+        _Success_(return) bool TryGetPropertyFromCache(
+            _In_ Var const instance,
+            _In_ RecyclableObject *const object,
+            _Out_ Var* const propertyValue,
+            _In_ ScriptContext* const requestContext,
+            _Out_ PropertyValueInfo* const propertyValueInfo,
+            _In_ RecyclableObject* const owner, // Object that this usage cache is part of
+            _Out_opt_ PropertyCacheOperationInfo* operationInfo)
         {
             if (ShouldUseCache())
             {
@@ -68,7 +72,7 @@ namespace Js
                     !OwnPropertyOnly,   // CheckTypePropertyCache
                     false,              // IsInlineCacheAvailable
                     true,               // IsPolymorphicInlineCacheAvailable
-                    false,              // ReturnOperationInfo
+                    ReturnOperationInfo,// ReturnOperationInfo
                     OutputExistence>    // OutputExistence
                         (instance,
                         false, // isRoot
@@ -76,7 +80,7 @@ namespace Js
                         this->propertyRecord->GetPropertyId(),
                         propertyValue,
                         requestContext,
-                        nullptr, // operationInfo
+                        operationInfo,
                         propertyValueInfo);
 
                 if (found)
@@ -128,4 +132,64 @@ namespace Js
         }
 #endif
     };
+
+
+    template <bool ReturnOperationInfo>
+    _Success_(return) inline bool PropertyRecordUsageCache::TrySetPropertyFromCache(
+        _In_ RecyclableObject *const object,
+        _In_ Var propertyValue,
+        _In_ ScriptContext *const requestContext,
+        const PropertyOperationFlags propertyOperationFlags,
+        _Out_ PropertyValueInfo *const propertyValueInfo,
+        _In_ RecyclableObject *const owner, // Object that this usage cache is part of
+        _Out_opt_ PropertyCacheOperationInfo* operationInfo)
+    {
+        if (ShouldUseCache())
+        {
+            PropertyValueInfo::SetCacheInfo(propertyValueInfo, owner, this, GetStElemInlineCache(), true /* allowResizing */);
+            bool found = CacheOperators::TrySetProperty<
+                true,   // CheckLocal
+                true,   // CheckLocalTypeWithoutProperty
+                true,   // CheckAccessor
+                true,   // CheckPolymorphicInlineCache
+                true,   // CheckTypePropertyCache
+                false,  // IsInlineCacheAvailable
+                true,   // IsPolymorphicInlineCacheAvailable
+                ReturnOperationInfo>
+                (object,
+                    false, // isRoot
+                    this->propertyRecord->GetPropertyId(),
+                    propertyValue,
+                    requestContext,
+                    propertyOperationFlags,
+                    operationInfo,
+                    propertyValueInfo);
+
+            if (found)
+            {
+#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
+                if (PHASE_TRACE1(PropertyCachePhase))
+                {
+                    Output::Print(_u("PropertyCache: SetElem cache hit for '%s': type %p\n"), GetString(), object->GetType());
+                }
+#endif
+                RegisterCacheHit();
+                return true;
+            }
+        }
+        RegisterCacheMiss();
+#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
+        if (PHASE_TRACE1(PropertyCachePhase))
+        {
+            Output::Print(_u("PropertyCache: SetElem cache miss for '%s': type %p, index %d\n"),
+                GetString(),
+                object->GetType(),
+                GetStElemInlineCache()->GetInlineCacheIndexForType(object->GetType()));
+            DumpCache(false);
+        }
+#endif
+        return false;
+    }
+
 }
+

+ 1 - 1
lib/Runtime/Library/PropertyString.cpp

@@ -67,7 +67,7 @@ namespace Js
         const PropertyOperationFlags propertyOperationFlags,
         _Inout_ PropertyValueInfo *const propertyValueInfo)
     {
-        return this->propertyRecordUsageCache.TrySetPropertyFromCache(object, propertyValue, requestContext, propertyOperationFlags, propertyValueInfo, this);
+        return this->propertyRecordUsageCache.TrySetPropertyFromCache<false /* ReturnOperationInfo */>(object, propertyValue, requestContext, propertyOperationFlags, propertyValueInfo, this, nullptr);
     }
 
     RecyclableObject * PropertyString::CloneToScriptContext(ScriptContext* requestContext)

+ 2 - 2
lib/Runtime/Library/PropertyString.h

@@ -40,14 +40,14 @@ public:
     template <
         bool OwnPropertyOnly,
         bool OutputExistence /*When set, propertyValue represents whether the property exists on the instance, not its actual value*/>
-    inline bool TryGetPropertyFromCache(
+    bool TryGetPropertyFromCache(
         Var const instance,
         RecyclableObject *const object,
         Var *const propertyValue,
         ScriptContext *const requestContext,
         PropertyValueInfo *const propertyValueInfo)
     {
-        return this->propertyRecordUsageCache.TryGetPropertyFromCache<OwnPropertyOnly, OutputExistence>(instance, object, propertyValue, requestContext, propertyValueInfo, this);
+        return this->propertyRecordUsageCache.TryGetPropertyFromCache<OwnPropertyOnly, OutputExistence, false /* ReturnOperationInfo */>(instance, object, propertyValue, requestContext, propertyValueInfo, this, nullptr);
     }
 
     static PropertyString* New(StaticType* type, const Js::PropertyRecord* propertyRecord, Recycler *recycler);