Explorar o código

Cleanup: Refactor and move JIT specific code to the BackEnd Directory

Curtis Man %!s(int64=8) %!d(string=hai) anos
pai
achega
861fb1b741

+ 1 - 0
lib/Backend/Backend.h

@@ -118,6 +118,7 @@ enum IRDumpFlags
 #include "JITTimeConstructorCache.h"
 #include "JITTypeHandler.h"
 #include "JITType.h"
+#include "EquivalentTypeSet.h"
 #include "ObjTypeSpecFldInfo.h"
 #include "FunctionCodeGenJitTimeData.h"
 #include "ServerScriptContext.h"

+ 2 - 0
lib/Backend/CMakeLists.txt

@@ -15,6 +15,7 @@ add_library (Chakra.Backend OBJECT
     EhFrame.cpp
     EmitBuffer.cpp
     Encoder.cpp
+    EquivalentTypeSet.cpp
     FixedFieldInfo.cpp
     FlowGraph.cpp
     Func.cpp
@@ -40,6 +41,7 @@ add_library (Chakra.Backend OBJECT
     InliningHeuristics.cpp
     IntBounds.cpp
     InterpreterThunkEmitter.cpp
+    JavascriptNativeOperators.cpp
     JITThunkEmitter.cpp
     JITOutput.cpp
     JITTimeConstructorCache.cpp

+ 6 - 1
lib/Backend/Chakra.Backend.vcxproj

@@ -151,6 +151,7 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)CodeGenAllocators.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)CodeGenNumberAllocator.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)DbCheckPostLower.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)EquivalentTypeSet.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)GlobOptBailOut.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)GlobOptExpr.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)GlobOptSimd128.cpp" />
@@ -177,6 +178,7 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)IRBuilder.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)IRViewer.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)IRType.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)JavascriptNativeOperators.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)JnHelperMethod.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)LinearScan.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)Lower.cpp" />
@@ -256,6 +258,7 @@
       <ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
     </ClInclude>
     <ClInclude Include="AsmJsJITInfo.h" />
+    <ClInclude Include="EquivalentTypeSet.h" />
     <ClInclude Include="FunctionCodeGenJitTimeData.h" />
     <ClInclude Include="FunctionJITRuntimeInfo.h" />
     <ClInclude Include="FunctionJITTimeInfo.h" />
@@ -392,6 +395,7 @@
     <ClInclude Include="IRViewer.h" />
     <ClInclude Include="IRType.h" />
     <ClInclude Include="IRTypeList.h" />
+    <ClInclude Include="JavascriptNativeOperators.h" />
     <ClInclude Include="JITThunkEmitter.h" />
     <ClInclude Include="ObjTypeSpecFldInfo.h" />
     <ClInclude Include="JITOutput.h" />
@@ -421,6 +425,7 @@
     <ClInclude Include="NativeCodeGenerator.h" />
     <ClInclude Include="Opnd.h" />
     <ClInclude Include="Peeps.h" />
+    <ClInclude Include="PropertyGuard.h" />
     <ClInclude Include="QueuedFullJitWorkItem.h" />
     <ClInclude Include="Region.h" />
     <ClInclude Include="SccLiveness.h" />
@@ -476,7 +481,7 @@
     <ClInclude Include="CRC.h">
       <FileType>CppCode</FileType>
     </ClInclude>
-  </ItemGroup>
+  </ItemGroup>  
   <Import Project="$(BuildConfigPropsPath)Chakra.Build.targets" Condition="exists('$(BuildConfigPropsPath)Chakra.Build.targets')" />
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

+ 6 - 1
lib/Backend/Chakra.Backend.vcxproj.filters

@@ -130,6 +130,8 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)GlobOptBlockData.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)ValueInfo.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)JITThunkEmitter.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)JavascriptNativeOperators.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)EquivalentTypeSet.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="AgenPeeps.h" />
@@ -345,6 +347,9 @@
     <ClInclude Include="GlobOptBlockData.h" />
     <ClInclude Include="ValueInfo.h" />
     <ClInclude Include="JITThunkEmitter.h" />
+    <ClInclude Include="PropertyGuard.h" />
+    <ClInclude Include="JavascriptNativeOperators.h" />
+    <ClInclude Include="EquivalentTypeSet.h" />
   </ItemGroup>
   <ItemGroup>
     <MASM Include="$(MSBuildThisFileDirectory)amd64\LinearScanMdA.asm">
@@ -390,4 +395,4 @@
       <Filter>arm</Filter>
     </ARMASM>
   </ItemGroup>
-</Project>
+</Project>

+ 159 - 0
lib/Backend/EquivalentTypeSet.cpp

@@ -0,0 +1,159 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+#include "Backend.h"
+
+#if ENABLE_NATIVE_CODEGEN
+namespace Js
+{
+
+EquivalentTypeSet::EquivalentTypeSet(RecyclerJITTypeHolder * types, uint16 count)
+    : types(types), count(count), sortedAndDuplicatesRemoved(false)
+{
+}
+
+JITTypeHolder EquivalentTypeSet::GetType(uint16 index) const
+{
+    Assert(this->types != nullptr && this->count > 0 && index < this->count);
+    return this->types[index];
+}
+
+JITTypeHolder EquivalentTypeSet::GetFirstType() const
+{
+    return GetType(0);
+}
+
+bool EquivalentTypeSet::Contains(const JITTypeHolder type, uint16* pIndex)
+{
+    if (!this->GetSortedAndDuplicatesRemoved())
+    {
+        this->SortAndRemoveDuplicates();
+    }
+    for (uint16 ti = 0; ti < this->count; ti++)
+    {
+        if (this->GetType(ti) == type)
+        {
+            if (pIndex)
+            {
+                *pIndex = ti;
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+bool EquivalentTypeSet::AreIdentical(EquivalentTypeSet * left, EquivalentTypeSet * right)
+{
+    if (!left->GetSortedAndDuplicatesRemoved())
+    {
+        left->SortAndRemoveDuplicates();
+    }
+    if (!right->GetSortedAndDuplicatesRemoved())
+    {
+        right->SortAndRemoveDuplicates();
+    }
+
+    Assert(left->GetSortedAndDuplicatesRemoved() && right->GetSortedAndDuplicatesRemoved());
+
+    if (left->count != right->count)
+    {
+        return false;
+    }
+
+    // TODO: OOP JIT, optimize this (previously we had memcmp)
+    for (uint i = 0; i < left->count; ++i)
+    {
+        if (left->types[i] != right->types[i])
+        {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool EquivalentTypeSet::IsSubsetOf(EquivalentTypeSet * left, EquivalentTypeSet * right)
+{
+    if (!left->GetSortedAndDuplicatesRemoved())
+    {
+        left->SortAndRemoveDuplicates();
+    }
+    if (!right->GetSortedAndDuplicatesRemoved())
+    {
+        right->SortAndRemoveDuplicates();
+    }
+
+    if (left->count > right->count)
+    {
+        return false;
+    }
+
+    // Try to find each left type in the right set.
+    int j = 0;
+    for (int i = 0; i < left->count; i++)
+    {
+        bool found = false;
+        for (; j < right->count; j++)
+        {
+            if (left->types[i] < right->types[j])
+            {
+                // Didn't find the left type. Fail.
+                return false;
+            }
+            if (left->types[i] == right->types[j])
+            {
+                // Found the left type. Continue to the next left/right pair.
+                found = true;
+                j++;
+                break;
+            }
+        }
+        Assert(j <= right->count);
+        if (j == right->count && !found)
+        {
+            // Exhausted the right set without finding the current left type.
+            return false;
+        }
+    }
+    return true;
+}
+
+void EquivalentTypeSet::SortAndRemoveDuplicates()
+{
+    uint16 oldCount = this->count;
+    uint16 i;
+
+    // sorting
+    for (i = 1; i < oldCount; i++)
+    {
+        uint16 j = i;
+        while (j > 0 && (this->types[j - 1] > this->types[j]))
+        {
+            JITTypeHolder tmp = this->types[j];
+            this->types[j] = this->types[j - 1];
+            this->types[j - 1] = tmp;
+            j--;
+        }
+    }
+
+    // removing duplicate types from the sorted set
+    i = 0;
+    for (uint16 j = 1; j < oldCount; j++)
+    {
+        if (this->types[i] != this->types[j])
+        {
+            this->types[++i] = this->types[j];
+        }
+    }
+    this->count = ++i;
+    for (i; i < oldCount; i++)
+    {
+        this->types[i] = JITTypeHolder(nullptr);
+    }
+
+    this->sortedAndDuplicatesRemoved = true;
+}
+}
+#endif

+ 37 - 0
lib/Backend/EquivalentTypeSet.h

@@ -0,0 +1,37 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#pragma once
+namespace Js
+{
+    class EquivalentTypeSet
+    {
+    private:
+        Field(bool) sortedAndDuplicatesRemoved;
+        Field(uint16) count;
+        Field(RecyclerJITTypeHolder *) types;
+
+    public:
+        EquivalentTypeSet(RecyclerJITTypeHolder * types, uint16 count);
+
+        uint16 GetCount() const
+        {
+            return this->count;
+        }
+
+        JITTypeHolder GetFirstType() const;
+
+        JITTypeHolder GetType(uint16 index) const;
+
+        bool GetSortedAndDuplicatesRemoved() const
+        {
+            return this->sortedAndDuplicatesRemoved;
+        }
+        bool Contains(const JITTypeHolder type, uint16 * pIndex = nullptr);
+
+        static bool AreIdentical(EquivalentTypeSet * left, EquivalentTypeSet * right);
+        static bool IsSubsetOf(EquivalentTypeSet * left, EquivalentTypeSet * right);
+        void SortAndRemoveDuplicates();
+    };
+};

+ 1 - 1
lib/Backend/IR.h

@@ -4,7 +4,7 @@
 //-------------------------------------------------------------------------------------------------------
 #pragma once
 
-#include "Language/JavascriptNativeOperators.h"
+#include "JavascriptNativeOperators.h"
 
 class Func;
 class BasicBlock;

+ 380 - 0
lib/Backend/JavascriptNativeOperators.cpp

@@ -0,0 +1,380 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#include "Backend.h"
+
+namespace Js
+{
+#if ENABLE_NATIVE_CODEGEN
+    void * JavascriptNativeOperators::Op_SwitchStringLookUp(JavascriptString* str, Js::BranchDictionaryWrapper<JavascriptString*>* branchTargets, uintptr_t funcStart, uintptr_t funcEnd)
+    {
+        void* defaultTarget = branchTargets->defaultTarget;
+        Js::BranchDictionaryWrapper<JavascriptString*>::BranchDictionary& stringDictionary = branchTargets->dictionary;
+        void* target = stringDictionary.Lookup(str, defaultTarget);
+        uintptr_t utarget = (uintptr_t)target;
+
+        if ((utarget - funcStart) > (funcEnd - funcStart))
+        {
+            AssertMsg(false, "Switch string dictionary jump target outside of function");
+            Throw::FatalInternalError();
+        }
+        return target;
+    }
+
+#if ENABLE_DEBUG_CONFIG_OPTIONS
+    void JavascriptNativeOperators::TracePropertyEquivalenceCheck(const JitEquivalentTypeGuard* guard, const Type* type, const Type* refType, bool isEquivalent, uint failedPropertyIndex)
+    {
+        if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
+        {
+            uint propertyCount = guard->GetCache()->record.propertyCount;
+
+            Output::Print(_u("EquivObjTypeSpec: checking %u properties on operation %u, (type = 0x%p, ref type = 0x%p):\n"),
+                propertyCount, guard->GetObjTypeSpecFldId(), type, refType);
+
+            const Js::TypeEquivalenceRecord& record = guard->GetCache()->record;
+            ScriptContext* scriptContext = type->GetScriptContext();
+            if (isEquivalent)
+            {
+                if (Js::Configuration::Global.flags.Verbose)
+                {
+                    Output::Print(_u("    <start>, "));
+                    for (uint pi = 0; pi < propertyCount; pi++)
+                    {
+                        const EquivalentPropertyEntry* refInfo = &record.properties[pi];
+                        const PropertyRecord* propertyRecord = scriptContext->GetPropertyName(refInfo->propertyId);
+                        Output::Print(_u("%s(#%d)@%ua%dw%d, "), propertyRecord->GetBuffer(), propertyRecord->GetPropertyId(), refInfo->slotIndex, refInfo->isAuxSlot, refInfo->mustBeWritable);
+                    }
+                    Output::Print(_u("<end>\n"));
+                }
+            }
+            else
+            {
+                const EquivalentPropertyEntry* refInfo = &record.properties[failedPropertyIndex];
+                Js::PropertyEquivalenceInfo info(Constants::NoSlot, false, false);
+                const PropertyRecord* propertyRecord = scriptContext->GetPropertyName(refInfo->propertyId);
+                if (DynamicType::Is(type->GetTypeId()))
+                {
+                    Js::DynamicTypeHandler* typeHandler = (static_cast<const DynamicType*>(type))->GetTypeHandler();
+                    typeHandler->GetPropertyEquivalenceInfo(propertyRecord, info);
+                }
+
+                Output::Print(_u("EquivObjTypeSpec: check failed for %s (#%d) on operation %u:\n"),
+                    propertyRecord->GetBuffer(), propertyRecord->GetPropertyId(), guard->GetObjTypeSpecFldId());
+                Output::Print(_u("    type = 0x%p, ref type = 0x%p, slot = 0x%u (%d), ref slot = 0x%u (%d), is writable = %d, required writable = %d\n"),
+                    type, refType, info.slotIndex, refInfo->slotIndex, info.isAuxSlot, refInfo->isAuxSlot, info.isWritable, refInfo->mustBeWritable);
+            }
+
+            Output::Flush();
+        }
+    }
+#endif
+
+    bool JavascriptNativeOperators::IsStaticTypeObjTypeSpecEquivalent(const TypeEquivalenceRecord& equivalenceRecord, uint& failedIndex)
+    {
+        uint propertyCount = equivalenceRecord.propertyCount;
+        Js::EquivalentPropertyEntry* properties = equivalenceRecord.properties;
+        for (uint pi = 0; pi < propertyCount; pi++)
+        {
+            const EquivalentPropertyEntry* refInfo = &properties[pi];
+            if (!IsStaticTypeObjTypeSpecEquivalent(refInfo))
+            {
+                failedIndex = pi;
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool JavascriptNativeOperators::IsStaticTypeObjTypeSpecEquivalent(const EquivalentPropertyEntry *entry)
+    {
+        // Objects of static types have no local properties, but they may load fields from their prototypes.
+        return entry->slotIndex == Constants::NoSlot && !entry->mustBeWritable;
+    }
+
+    bool JavascriptNativeOperators::CheckIfTypeIsEquivalentForFixedField(Type* type, JitEquivalentTypeGuard* guard)
+    {
+        if (guard->GetValue() == PropertyGuard::GuardValue::Invalidated_DuringSweep)
+        {
+            return false;
+        }
+        return CheckIfTypeIsEquivalent(type, guard);
+    }
+
+    bool JavascriptNativeOperators::CheckIfTypeIsEquivalent(Type* type, JitEquivalentTypeGuard* guard)
+    {
+        if (guard->GetValue() == PropertyGuard::GuardValue::Invalidated)
+        {
+            return false;
+        }
+
+        AssertMsg(type && type->GetScriptContext(), "type and it's ScriptContext should be valid.");
+
+        if (!guard->IsInvalidatedDuringSweep() && ((Js::Type*)guard->GetTypeAddr())->GetScriptContext() != type->GetScriptContext())
+        {
+            // For valid guard value, can't cache cross-context objects
+            return false;
+        }
+
+        // CONSIDER : Add stats on how often the cache hits, and simply force bailout if
+        // the efficacy is too low.
+
+        EquivalentTypeCache* cache = guard->GetCache();
+        // CONSIDER : Consider emitting o.type == equivTypes[hash(o.type)] in machine code before calling
+        // this helper, particularly if we want to handle polymorphism with frequently changing types.
+        Assert(EQUIVALENT_TYPE_CACHE_SIZE == 8);
+        Type** equivTypes = cache->types;
+
+        Type* refType = equivTypes[0];
+        if (refType == nullptr || refType->GetScriptContext() != type->GetScriptContext())
+        {
+            // We could have guard that was invalidated while sweeping and now we have type coming from
+            // different scriptContext. Make sure that it matches the scriptContext in cachedTypes.
+            // If not, return false because as mentioned above, we don't cache cross-context objects.
+#if DBG
+            if (refType == nullptr)
+            {
+                for (int i = 1; i < EQUIVALENT_TYPE_CACHE_SIZE; i++)
+                {
+                    AssertMsg(equivTypes[i] == nullptr, "In equiv typed caches, if first element is nullptr, all others should be nullptr");
+                }
+            }
+#endif
+            return false;
+        }
+
+        if (type == equivTypes[0] || type == equivTypes[1] || type == equivTypes[2] || type == equivTypes[3] ||
+            type == equivTypes[4] || type == equivTypes[5] || type == equivTypes[6] || type == equivTypes[7])
+        {
+#if DBG
+            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
+            {
+                if (guard->WasReincarnated())
+                {
+                    Output::Print(_u("EquivObjTypeSpec: Guard 0x%p was reincarnated and working now \n"), guard);
+                    Output::Flush();
+                }
+            }
+#endif
+            guard->SetTypeAddr((intptr_t)type);
+            return true;
+        }
+
+        // If we didn't find the type in the cache, let's check if it's equivalent the slow way, by comparing
+        // each of its relevant property slots to its equivalent in one of the cached types.
+        // We are making a few assumption that simplify the process:
+        // 1. If two types have the same prototype, any properties loaded from a prototype must come from the same slot.
+        //    If any of the prototypes in the chain was altered such that this is no longer true, the corresponding
+        //    property guard would have been invalidated and we would bail out at the guard check (either on this
+        //    type check or downstream, but before the property load is attempted).
+        // 2. For polymorphic field loads fixed fields are only supported on prototypes.  Hence, if two types have the
+        //    same prototype, any of the equivalent fixed properties will match. If any has been overwritten, the
+        //    corresponding guard would have been invalidated and we would bail out (as above).
+
+        if (cache->IsLoadedFromProto() && type->GetPrototype() != refType->GetPrototype())
+        {
+            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
+            {
+                Output::Print(_u("EquivObjTypeSpec: failed check on operation %u (type = 0x%x, ref type = 0x%x, proto = 0x%x, ref proto = 0x%x) \n"),
+                    guard->GetObjTypeSpecFldId(), type, refType, type->GetPrototype(), refType->GetPrototype());
+                Output::Flush();
+            }
+
+            return false;
+        }
+
+#pragma prefast(suppress:6011) // If type is nullptr, we would AV at the beginning of this method
+        if (type->GetTypeId() != refType->GetTypeId())
+        {
+            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
+            {
+                Output::Print(_u("EquivObjTypeSpec: failed check on operation %u (type = 0x%x, ref type = 0x%x, proto = 0x%x, ref proto = 0x%x) \n"),
+                    guard->GetObjTypeSpecFldId(), type, refType, type->GetPrototype(), refType->GetPrototype());
+                Output::Flush();
+            }
+
+            return false;
+        }
+
+        // Review : This is quite slow.  We could make it somewhat faster, by keeping slot indexes instead
+        // of property IDs, but that would mean we would need to look up property IDs from slot indexes when installing
+        // property guards, or maintain a whole separate list of equivalent slot indexes.
+        Assert(cache->record.propertyCount > 0);
+
+        // Before checking for equivalence, track existing cached non-shared types
+        DynamicType * dynamicType = (type && DynamicType::Is(type->GetTypeId())) ? static_cast<DynamicType*>(type) : nullptr;
+        bool isEquivTypesCacheFull = equivTypes[EQUIVALENT_TYPE_CACHE_SIZE - 1] != nullptr;
+        int emptySlotIndex = -1;
+        int nonSharedTypeSlotIndex = -1;
+        for (int i = 0; i < EQUIVALENT_TYPE_CACHE_SIZE; i++)
+        {
+            // Track presence of cached non-shared type if cache is full
+            if (isEquivTypesCacheFull)
+            {
+                if (DynamicType::Is(equivTypes[i]->GetTypeId()) &&
+                    nonSharedTypeSlotIndex == -1 &&
+                    !(static_cast<DynamicType*>(equivTypes[i]))->GetIsShared())
+                {
+                    nonSharedTypeSlotIndex = i;
+                }
+            }
+            // Otherwise get the next available empty index
+            else if (equivTypes[i] == nullptr)
+            {
+                emptySlotIndex = i;
+                break;
+            };
+        }
+
+        // If we get non-shared type while cache is full and we don't have any non-shared type to evict
+        // consider this type as non-equivalent
+        if (dynamicType != nullptr &&
+            isEquivTypesCacheFull &&
+            !dynamicType->GetIsShared() &&
+            nonSharedTypeSlotIndex == -1)
+        {
+            return false;
+        }
+
+        // CONSIDER (EquivObjTypeSpec): Impose a limit on the number of properties guarded by an equivalent type check.
+        // The trick is where in the glob opt to make the cut off. Perhaps in the forward pass we could track the number of
+        // field operations protected by a type check (keep a counter on the type's value info), and if that counter exceeds
+        // some threshold, simply stop optimizing any further instructions.
+
+        bool isEquivalent;
+        uint failedPropertyIndex;
+        if (dynamicType != nullptr)
+        {
+            Js::DynamicTypeHandler* typeHandler = dynamicType->GetTypeHandler();
+            isEquivalent = typeHandler->IsObjTypeSpecEquivalent(type, cache->record, failedPropertyIndex);
+        }
+        else
+        {
+            Assert(StaticType::Is(type->GetTypeId()));
+            isEquivalent = IsStaticTypeObjTypeSpecEquivalent(cache->record, failedPropertyIndex);
+        }
+
+#if ENABLE_DEBUG_CONFIG_OPTIONS
+        TracePropertyEquivalenceCheck(guard, type, refType, isEquivalent, failedPropertyIndex);
+#endif
+
+        if (!isEquivalent)
+        {
+            return false;
+        }
+
+        AssertMsg(!isEquivTypesCacheFull || !dynamicType || dynamicType->GetIsShared() || nonSharedTypeSlotIndex > -1, "If equiv cache is full, then this should be sharedType or we will evict non-shared type.");
+
+        // If cache is full, then this is definitely a sharedType, so evict non-shared type.
+        // Else evict next empty slot (only applicable for DynamicTypes)
+        emptySlotIndex = (isEquivTypesCacheFull && dynamicType) ? nonSharedTypeSlotIndex : emptySlotIndex;
+
+        // We have some empty slots, let us use those first
+        if (emptySlotIndex != -1)
+        {
+            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
+            {
+                Output::Print(_u("EquivObjTypeSpec: Saving type in unused slot of equiv types cache. \n"));
+                Output::Flush();
+            }
+            equivTypes[emptySlotIndex] = type;
+        }
+        else
+        {
+            // CONSIDER (EquivObjTypeSpec): Invent some form of least recently used eviction scheme.
+            uintptr_t index = (reinterpret_cast<uintptr_t>(type) >> 4) & (EQUIVALENT_TYPE_CACHE_SIZE - 1);
+
+            if (cache->nextEvictionVictim == EQUIVALENT_TYPE_CACHE_SIZE)
+            {
+                __analysis_assume(index < EQUIVALENT_TYPE_CACHE_SIZE);
+                // If nextEvictionVictim was never set, set it to next element after index
+                cache->nextEvictionVictim = (index + 1) & (EQUIVALENT_TYPE_CACHE_SIZE - 1);
+            }
+            else
+            {
+                Assert(cache->nextEvictionVictim < EQUIVALENT_TYPE_CACHE_SIZE);
+                __analysis_assume(cache->nextEvictionVictim < EQUIVALENT_TYPE_CACHE_SIZE);
+                equivTypes[cache->nextEvictionVictim] = equivTypes[index];
+                // Else, set it to next element after current nextEvictionVictim index
+                cache->nextEvictionVictim = (cache->nextEvictionVictim + 1) & (EQUIVALENT_TYPE_CACHE_SIZE - 1);
+            }
+
+            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
+            {
+                Output::Print(_u("EquivObjTypeSpec: Saving type in used slot of equiv types cache at index = %d. NextEvictionVictim = %d. \n"), index, cache->nextEvictionVictim);
+                Output::Flush();
+            }
+            Assert(index < EQUIVALENT_TYPE_CACHE_SIZE);
+            __analysis_assume(index < EQUIVALENT_TYPE_CACHE_SIZE);
+            equivTypes[index] = type;
+        }
+
+        // Fixed field checks allow us to assume a specific type ID, but the assumption is only
+        // valid if we lock the type. Otherwise, the type ID may change out from under us without
+        // evolving the type.
+        // We also need to lock the type in case of, for instance, adding a property to a dictionary type handler.
+        if (dynamicType != nullptr)
+        {
+            if (!dynamicType->GetIsLocked())
+            {
+                // We only need to lock the type to prevent against the type evolving after it has been cached. If the type becomes shared
+                // in the future, any further changes to the type will result in creating a new type handler.
+                dynamicType->LockTypeOnly();
+            }
+        }
+
+        type->SetHasBeenCached();
+        guard->SetTypeAddr((intptr_t)type);
+        return true;
+    }
+#endif
+
+    Var JavascriptNativeOperators::OP_GetElementI_JIT_ExpectingNativeFloatArray(Var instance, Var index, ScriptContext *scriptContext)
+    {
+        Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
+
+        JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(instance, true, false);
+        return JavascriptOperators::OP_GetElementI_JIT(instance, index, scriptContext);
+    }
+
+    Var JavascriptNativeOperators::OP_GetElementI_JIT_ExpectingVarArray(Var instance, Var index, ScriptContext *scriptContext)
+    {
+        Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
+
+        JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(instance, false, true);
+        return JavascriptOperators::OP_GetElementI_JIT(instance, index, scriptContext);
+    }
+
+    Var JavascriptNativeOperators::OP_GetElementI_UInt32_ExpectingNativeFloatArray(Var instance, uint32 index, ScriptContext* scriptContext)
+    {
+#if ENABLE_PROFILE_INFO
+        JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(instance, true, false);
+#endif
+        return JavascriptOperators::OP_GetElementI_UInt32(instance, index, scriptContext);
+    }
+
+    Var JavascriptNativeOperators::OP_GetElementI_UInt32_ExpectingVarArray(Var instance, uint32 index, ScriptContext* scriptContext)
+    {
+#if ENABLE_PROFILE_INFO
+        JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(instance, false, true);
+#endif
+        return JavascriptOperators::OP_GetElementI_UInt32(instance, index, scriptContext);
+    }
+
+    Var JavascriptNativeOperators::OP_GetElementI_Int32_ExpectingNativeFloatArray(Var instance, int32 index, ScriptContext* scriptContext)
+    {
+#if ENABLE_PROFILE_INFO
+        JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(instance, true, false);
+#endif
+        return JavascriptOperators::OP_GetElementI_Int32(instance, index, scriptContext);
+    }
+
+    Var JavascriptNativeOperators::OP_GetElementI_Int32_ExpectingVarArray(Var instance, int32 index, ScriptContext* scriptContext)
+    {
+#if ENABLE_PROFILE_INFO
+        JavascriptOperators::UpdateNativeArrayProfileInfoToCreateVarArray(instance, false, true);
+#endif
+        return JavascriptOperators::OP_GetElementI_Int32(instance, index, scriptContext);
+    }
+
+};

+ 20 - 0
lib/Runtime/Language/JavascriptNativeOperators.h → lib/Backend/JavascriptNativeOperators.h

@@ -2,6 +2,8 @@
 // Copyright (C) Microsoft. All rights reserved.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
+#pragma once
+
 namespace Js
 {
 #if ENABLE_NATIVE_CODEGEN
@@ -125,6 +127,24 @@ namespace Js
     {
     public:
         static void * Op_SwitchStringLookUp(JavascriptString* str, Js::BranchDictionaryWrapper<Js::JavascriptString*>* stringDictionary, uintptr_t funcStart, uintptr_t funcEnd);
+
+#if ENABLE_DEBUG_CONFIG_OPTIONS
+        static void TracePropertyEquivalenceCheck(const JitEquivalentTypeGuard* guard, const Type* type, const Type* refType, bool isEquivalent, uint failedPropertyIndex);
+#endif        
+        static bool CheckIfTypeIsEquivalent(Type* type, JitEquivalentTypeGuard* guard);
+        static bool CheckIfTypeIsEquivalentForFixedField(Type* type, JitEquivalentTypeGuard* guard);
+
+        static Var OP_GetElementI_JIT_ExpectingNativeFloatArray(Var instance, Var index, ScriptContext *scriptContext);
+        static Var OP_GetElementI_JIT_ExpectingVarArray(Var instance, Var index, ScriptContext *scriptContext);
+
+        static Var OP_GetElementI_UInt32_ExpectingNativeFloatArray(Var instance, uint32 aElementIndex, ScriptContext* scriptContext);
+        static Var OP_GetElementI_UInt32_ExpectingVarArray(Var instance, uint32 aElementIndex, ScriptContext* scriptContext);
+
+        static Var OP_GetElementI_Int32_ExpectingNativeFloatArray(Var instance, int32 aElementIndex, ScriptContext* scriptContext);
+        static Var OP_GetElementI_Int32_ExpectingVarArray(Var instance, int32 aElementIndex, ScriptContext* scriptContext);
+    private:
+        static bool IsStaticTypeObjTypeSpecEquivalent(const TypeEquivalenceRecord& equivalenceRecord, uint& failedIndex);
+        static bool IsStaticTypeObjTypeSpecEquivalent(const EquivalentPropertyEntry *entry);
     };
 #endif
 };

+ 2 - 0
lib/Backend/JnHelperMethod.cpp

@@ -9,7 +9,9 @@
 
 #include "Library/RegexHelper.h"
 
+#ifdef ENABLE_SCRIPT_DEBUGGING
 #include "Debug/DiagHelperMethodWrapper.h"
+#endif
 #include "Math/CrtSSE2Math.h"
 #include "Library/JavascriptGeneratorFunction.h"
 #include "RuntimeMathPch.h"

+ 8 - 8
lib/Backend/JnHelperMethodList.h

@@ -172,14 +172,14 @@ HELPERCALL(Op_DeleteProperty, Js::JavascriptOperators::OP_DeleteProperty, AttrCa
 HELPERCALL(Op_DeleteRootProperty, Js::JavascriptOperators::OP_DeleteRootProperty, AttrCanThrow)
 HELPERCALL(Op_DeletePropertyScoped, Js::JavascriptOperators::OP_DeletePropertyScoped, AttrCanThrow)
 HELPERCALL(Op_GetElementI, Js::JavascriptOperators::OP_GetElementI_JIT, AttrCanThrow)
-HELPERCALL(Op_GetElementI_ExpectingNativeFloatArray, Js::JavascriptOperators::OP_GetElementI_JIT_ExpectingNativeFloatArray, AttrCanThrow)
-HELPERCALL(Op_GetElementI_ExpectingVarArray, Js::JavascriptOperators::OP_GetElementI_JIT_ExpectingVarArray, AttrCanThrow)
+HELPERCALL(Op_GetElementI_ExpectingNativeFloatArray, Js::JavascriptNativeOperators::OP_GetElementI_JIT_ExpectingNativeFloatArray, AttrCanThrow)
+HELPERCALL(Op_GetElementI_ExpectingVarArray, Js::JavascriptNativeOperators::OP_GetElementI_JIT_ExpectingVarArray, AttrCanThrow)
 HELPERCALL(Op_GetElementI_UInt32, Js::JavascriptOperators::OP_GetElementI_UInt32, AttrCanThrow)
-HELPERCALL(Op_GetElementI_UInt32_ExpectingNativeFloatArray, Js::JavascriptOperators::OP_GetElementI_UInt32_ExpectingNativeFloatArray, AttrCanThrow)
-HELPERCALL(Op_GetElementI_UInt32_ExpectingVarArray, Js::JavascriptOperators::OP_GetElementI_UInt32_ExpectingVarArray, AttrCanThrow)
+HELPERCALL(Op_GetElementI_UInt32_ExpectingNativeFloatArray, Js::JavascriptNativeOperators::OP_GetElementI_UInt32_ExpectingNativeFloatArray, AttrCanThrow)
+HELPERCALL(Op_GetElementI_UInt32_ExpectingVarArray, Js::JavascriptNativeOperators::OP_GetElementI_UInt32_ExpectingVarArray, AttrCanThrow)
 HELPERCALL(Op_GetElementI_Int32, Js::JavascriptOperators::OP_GetElementI_Int32, AttrCanThrow)
-HELPERCALL(Op_GetElementI_Int32_ExpectingNativeFloatArray, Js::JavascriptOperators::OP_GetElementI_Int32_ExpectingNativeFloatArray, AttrCanThrow)
-HELPERCALL(Op_GetElementI_Int32_ExpectingVarArray, Js::JavascriptOperators::OP_GetElementI_Int32_ExpectingVarArray, AttrCanThrow)
+HELPERCALL(Op_GetElementI_Int32_ExpectingNativeFloatArray, Js::JavascriptNativeOperators::OP_GetElementI_Int32_ExpectingNativeFloatArray, AttrCanThrow)
+HELPERCALL(Op_GetElementI_Int32_ExpectingVarArray, Js::JavascriptNativeOperators::OP_GetElementI_Int32_ExpectingVarArray, AttrCanThrow)
 HELPERCALL(Op_GetNativeIntElementI, Js::JavascriptOperators::OP_GetNativeIntElementI, AttrCanThrow)
 HELPERCALL(Op_GetNativeFloatElementI, Js::JavascriptOperators::OP_GetNativeFloatElementI, AttrCanThrow)
 HELPERCALL(Op_GetNativeIntElementI_Int32, Js::JavascriptOperators::OP_GetNativeIntElementI_Int32, AttrCanThrow)
@@ -250,8 +250,8 @@ HELPERCALL(Op_PatchGetRootMethodPolymorphic, ((Js::Var (*)(Js::FunctionBody *con
 HELPERCALL(Op_ScopedGetMethod, ((Js::Var (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, bool))Js::JavascriptOperators::PatchScopedGetMethod<true, Js::InlineCache>), AttrCanThrow)
 HELPERCALL(Op_ScopedGetMethodPolymorphic, ((Js::Var (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, bool))Js::JavascriptOperators::PatchScopedGetMethod<true, Js::PolymorphicInlineCache>), AttrCanThrow)
 
-HELPERCALL(CheckIfTypeIsEquivalent, Js::JavascriptOperators::CheckIfTypeIsEquivalent, 0)
-HELPERCALL(CheckIfTypeIsEquivalentForFixedField, Js::JavascriptOperators::CheckIfTypeIsEquivalentForFixedField, 0)
+HELPERCALL(CheckIfTypeIsEquivalent, Js::JavascriptNativeOperators::CheckIfTypeIsEquivalent, 0)
+HELPERCALL(CheckIfTypeIsEquivalentForFixedField, Js::JavascriptNativeOperators::CheckIfTypeIsEquivalentForFixedField, 0)
 
 HELPERCALL(Op_Delete, Js::JavascriptOperators::Delete, AttrCanThrow)
 HELPERCALL(OP_InitSetter, Js::JavascriptOperators::OP_InitSetter, AttrCanThrow)

+ 232 - 0
lib/Backend/PropertyGuard.h

@@ -0,0 +1,232 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#pragma once
+
+#if ENABLE_NATIVE_CODEGEN
+namespace Js
+{
+
+class PropertyGuard
+{
+    friend class PropertyGuardValidator;
+
+private:
+    Field(intptr_t) value; // value is address of Js::Type
+#if DBG
+    Field(bool) wasReincarnated = false;
+#endif
+public:
+    static PropertyGuard* New(Recycler* recycler) { return RecyclerNewLeaf(recycler, Js::PropertyGuard); }
+    PropertyGuard() : value(GuardValue::Uninitialized) {}
+    PropertyGuard(intptr_t value) : value(value)
+    {
+        // GuardValue::Invalidated and GuardValue::Invalidated_DuringSweeping can only be set using
+        // Invalidate() and InvalidatedDuringSweep() methods respectively.
+        Assert(this->value != GuardValue::Invalidated && this->value != GuardValue::Invalidated_DuringSweep);
+    }
+
+    inline static size_t const GetSizeOfValue() { return sizeof(((PropertyGuard*)0)->value); }
+    inline static size_t const GetOffsetOfValue() { return offsetof(PropertyGuard, value); }
+
+    intptr_t GetValue() const { return this->value; }
+    bool IsValid()
+    {
+        return this->value != GuardValue::Invalidated && this->value != GuardValue::Invalidated_DuringSweep;
+    }
+    bool IsInvalidatedDuringSweep() { return this->value == GuardValue::Invalidated_DuringSweep; }
+    void SetValue(intptr_t value)
+    {
+        // GuardValue::Invalidated and GuardValue::Invalidated_DuringSweeping can only be set using
+        // Invalidate() and InvalidatedDuringSweep() methods respectively.
+        Assert(value != GuardValue::Invalidated && value != GuardValue::Invalidated_DuringSweep);
+        this->value = value;
+    }
+    intptr_t const* GetAddressOfValue() { return &this->value; }
+    void Invalidate() { this->value = GuardValue::Invalidated; }
+    void InvalidateDuringSweep()
+    {
+#if DBG
+        wasReincarnated = true;
+#endif
+        this->value = GuardValue::Invalidated_DuringSweep;
+    }
+#if DBG
+    bool WasReincarnated() { return this->wasReincarnated; }
+#endif
+    enum GuardValue : intptr_t
+    {
+        Invalidated = 0,
+        Uninitialized = 1,
+        Invalidated_DuringSweep = 2
+    };
+};
+
+class PropertyGuardValidator
+{
+    // Required by EquivalentTypeGuard::SetType.
+    CompileAssert(offsetof(PropertyGuard, value) == 0);
+    // CompileAssert(offsetof(ConstructorCache, guard.value) == offsetof(PropertyGuard, value));
+};
+
+class JitIndexedPropertyGuard : public Js::PropertyGuard
+{
+private:
+    int index;
+
+public:
+    JitIndexedPropertyGuard(intptr_t value, int index) :
+        Js::PropertyGuard(value), index(index) {}
+
+    int GetIndex() const { return this->index; }
+};
+
+class JitTypePropertyGuard : public Js::JitIndexedPropertyGuard
+{
+public:
+    JitTypePropertyGuard(intptr_t typeAddr, int index) :
+        JitIndexedPropertyGuard(typeAddr, index) {}
+
+    intptr_t GetTypeAddr() const { return this->GetValue(); }
+
+};
+
+struct TypeGuardTransferEntry
+{
+    PropertyId propertyId;
+    JitIndexedPropertyGuard* guards[0];
+
+    TypeGuardTransferEntry() : propertyId(Js::Constants::NoProperty) {}
+};
+
+class FakePropertyGuardWeakReference : public RecyclerWeakReference<Js::PropertyGuard>
+{
+public:
+    static FakePropertyGuardWeakReference* New(Recycler* recycler, Js::PropertyGuard* guard)
+    {
+        Assert(guard != nullptr);
+        return RecyclerNewLeaf(recycler, Js::FakePropertyGuardWeakReference, guard);
+    }
+    FakePropertyGuardWeakReference(const Js::PropertyGuard* guard)
+    {
+        this->strongRef = (char*)guard;
+        this->strongRefHeapBlock = &CollectedRecyclerWeakRefHeapBlock::Instance;
+    }
+
+    void Zero()
+    {
+        Assert(this->strongRef != nullptr);
+        this->strongRef = nullptr;
+    }
+};
+
+struct CtorCacheGuardTransferEntry
+{
+    PropertyId propertyId;
+    intptr_t caches[0];
+
+    CtorCacheGuardTransferEntry() : propertyId(Js::Constants::NoProperty) {}
+};
+
+struct PropertyEquivalenceInfo
+{
+    PropertyIndex slotIndex;
+    bool isAuxSlot;
+    bool isWritable;
+
+    PropertyEquivalenceInfo() :
+        slotIndex(Constants::NoSlot), isAuxSlot(false), isWritable(false) {}
+    PropertyEquivalenceInfo(PropertyIndex slotIndex, bool isAuxSlot, bool isWritable) :
+        slotIndex(slotIndex), isAuxSlot(isAuxSlot), isWritable(isWritable) {}
+};
+
+struct EquivalentPropertyEntry
+{
+    Js::PropertyId propertyId;
+    Js::PropertyIndex slotIndex;
+    bool isAuxSlot;
+    bool mustBeWritable;
+};
+
+struct TypeEquivalenceRecord
+{
+    uint propertyCount;
+    EquivalentPropertyEntry* properties;
+};
+
+struct EquivalentTypeCache
+{
+    Js::Type* types[EQUIVALENT_TYPE_CACHE_SIZE];
+    PropertyGuard *guard;
+    TypeEquivalenceRecord record;
+    uint nextEvictionVictim;
+    bool isLoadedFromProto;
+    bool hasFixedValue;
+
+    EquivalentTypeCache() : nextEvictionVictim(EQUIVALENT_TYPE_CACHE_SIZE) {}
+    bool ClearUnusedTypes(Recycler *recycler);
+    void SetGuard(PropertyGuard *theGuard) { this->guard = theGuard; }
+    void SetIsLoadedFromProto() { this->isLoadedFromProto = true; }
+    bool IsLoadedFromProto() const { return this->isLoadedFromProto; }
+    void SetHasFixedValue() { this->hasFixedValue = true; }
+    bool HasFixedValue() const { return this->hasFixedValue; }
+};
+
+class JitEquivalentTypeGuard : public JitIndexedPropertyGuard
+{
+    // This pointer is allocated from background thread first, and then transferred to recycler,
+    // so as to keep the cached types alive.
+    EquivalentTypeCache* cache;
+    uint32 objTypeSpecFldId;
+    // TODO: OOP JIT, reenable these asserts
+#if DBG && 0
+    // Intentionally have as intptr_t so this guard doesn't hold scriptContext
+    intptr_t originalScriptContextValue = 0;
+#endif
+
+public:
+    JitEquivalentTypeGuard(intptr_t typeAddr, int index, uint32 objTypeSpecFldId) :
+        JitIndexedPropertyGuard(typeAddr, index), cache(nullptr), objTypeSpecFldId(objTypeSpecFldId)
+    {
+#if DBG && 0
+        originalScriptContextValue = reinterpret_cast<intptr_t>(type->GetScriptContext());
+#endif
+    }
+
+    intptr_t GetTypeAddr() const { return this->GetValue(); }
+
+    void SetTypeAddr(const intptr_t typeAddr)
+    {
+#if DBG && 0
+        if (originalScriptContextValue == 0)
+        {
+            originalScriptContextValue = reinterpret_cast<intptr_t>(type->GetScriptContext());
+        }
+        else
+        {
+            AssertMsg(originalScriptContextValue == reinterpret_cast<intptr_t>(type->GetScriptContext()), "Trying to set guard type from different script context.");
+        }
+#endif
+        this->SetValue(typeAddr);
+    }
+
+    uint32 GetObjTypeSpecFldId() const
+    {
+        return this->objTypeSpecFldId;
+    }
+
+    Js::EquivalentTypeCache* GetCache() const
+    {
+        return this->cache;
+    }
+
+    void SetCache(Js::EquivalentTypeCache* cache)
+    {
+        this->cache = cache;
+    }
+};
+
+
+};
+#endif // ENABLE_NATIVE_CODEGEN

+ 1 - 0
lib/Common/BackendApi.h

@@ -51,6 +51,7 @@ typedef double  FloatConstType;
 #include "IRType.h"
 #include "InlineeFrameInfo.h"
 #include "CodeGenAllocators.h"
+#include "PropertyGuard.h"
 
 NativeCodeGenerator * NewNativeCodeGenerator(Js::ScriptContext * nativeCodeGen);
 void DeleteNativeCodeGenerator(NativeCodeGenerator * nativeCodeGen);

+ 0 - 195
lib/Runtime/Base/FunctionBody.h

@@ -80,201 +80,6 @@ namespace Js
         void * data;
     };
 
-#if ENABLE_NATIVE_CODEGEN
-    class PropertyGuard
-    {
-        friend class PropertyGuardValidator;
-
-    private:
-        Field(intptr_t) value; // value is address of Js::Type
-#if DBG
-        Field(bool) wasReincarnated = false;
-#endif
-    public:
-        static PropertyGuard* New(Recycler* recycler) { return RecyclerNewLeaf(recycler, Js::PropertyGuard); }
-        PropertyGuard() : value(GuardValue::Uninitialized) {}
-        PropertyGuard(intptr_t value) : value(value)
-        {
-            // GuardValue::Invalidated and GuardValue::Invalidated_DuringSweeping can only be set using
-            // Invalidate() and InvalidatedDuringSweep() methods respectively.
-            Assert(this->value != GuardValue::Invalidated && this->value != GuardValue::Invalidated_DuringSweep);
-        }
-
-        inline static size_t const GetSizeOfValue() { return sizeof(((PropertyGuard*)0)->value); }
-        inline static size_t const GetOffsetOfValue() { return offsetof(PropertyGuard, value); }
-
-        intptr_t GetValue() const { return this->value; }
-        bool IsValid()
-        {
-            return this->value != GuardValue::Invalidated && this->value != GuardValue::Invalidated_DuringSweep;
-        }
-        bool IsInvalidatedDuringSweep() { return this->value == GuardValue::Invalidated_DuringSweep; }
-        void SetValue(intptr_t value)
-        {
-            // GuardValue::Invalidated and GuardValue::Invalidated_DuringSweeping can only be set using
-            // Invalidate() and InvalidatedDuringSweep() methods respectively.
-            Assert(value != GuardValue::Invalidated && value != GuardValue::Invalidated_DuringSweep);
-            this->value = value;
-        }
-        intptr_t const* GetAddressOfValue() { return &this->value; }
-        void Invalidate() { this->value = GuardValue::Invalidated; }
-        void InvalidateDuringSweep()
-        {
-#if DBG
-            wasReincarnated = true;
-#endif
-            this->value = GuardValue::Invalidated_DuringSweep;
-        }
-#if DBG
-        bool WasReincarnated() { return this->wasReincarnated; }
-#endif
-        enum GuardValue : intptr_t
-        {
-            Invalidated = 0,
-            Uninitialized = 1,
-            Invalidated_DuringSweep = 2
-        };
-    };
-
-    class PropertyGuardValidator
-    {
-        // Required by EquivalentTypeGuard::SetType.
-        CompileAssert(offsetof(PropertyGuard, value) == 0);
-        CompileAssert(offsetof(ConstructorCache, guard.value) == offsetof(PropertyGuard, value));
-    };
-
-    class JitIndexedPropertyGuard : public Js::PropertyGuard
-    {
-    private:
-        int index;
-
-    public:
-        JitIndexedPropertyGuard(intptr_t value, int index):
-            Js::PropertyGuard(value), index(index) {}
-
-        int GetIndex() const { return this->index; }
-    };
-
-    class JitTypePropertyGuard : public Js::JitIndexedPropertyGuard
-    {
-    public:
-        JitTypePropertyGuard(intptr_t typeAddr, int index):
-            JitIndexedPropertyGuard(typeAddr, index) {}
-
-        intptr_t GetTypeAddr() const { return this->GetValue(); }
-
-    };
-
-    struct TypeGuardTransferEntry
-    {
-        PropertyId propertyId;
-        JitIndexedPropertyGuard* guards[0];
-
-        TypeGuardTransferEntry(): propertyId(Js::Constants::NoProperty) {}
-    };
-
-    class FakePropertyGuardWeakReference: public RecyclerWeakReference<Js::PropertyGuard>
-    {
-    public:
-        static FakePropertyGuardWeakReference* New(Recycler* recycler, Js::PropertyGuard* guard)
-        {
-            Assert(guard != nullptr);
-            return RecyclerNewLeaf(recycler, Js::FakePropertyGuardWeakReference, guard);
-        }
-        FakePropertyGuardWeakReference(const Js::PropertyGuard* guard)
-        {
-            this->strongRef = (char*)guard;
-            this->strongRefHeapBlock = &CollectedRecyclerWeakRefHeapBlock::Instance;
-        }
-
-        void Zero()
-        {
-            Assert(this->strongRef != nullptr);
-            this->strongRef = nullptr;
-        }
-    };
-
-    struct CtorCacheGuardTransferEntry
-    {
-        PropertyId propertyId;
-        intptr_t caches[0];
-
-        CtorCacheGuardTransferEntry(): propertyId(Js::Constants::NoProperty) {}
-    };
-
-    struct EquivalentTypeCache
-    {
-        Js::Type* types[EQUIVALENT_TYPE_CACHE_SIZE];
-        PropertyGuard *guard;
-        TypeEquivalenceRecord record;
-        uint nextEvictionVictim;
-        bool isLoadedFromProto;
-        bool hasFixedValue;
-
-        EquivalentTypeCache(): nextEvictionVictim(EQUIVALENT_TYPE_CACHE_SIZE) {}
-        bool ClearUnusedTypes(Recycler *recycler);
-        void SetGuard(PropertyGuard *theGuard) { this->guard = theGuard; }
-        void SetIsLoadedFromProto() { this->isLoadedFromProto = true; }
-        bool IsLoadedFromProto() const { return this->isLoadedFromProto; }
-        void SetHasFixedValue() { this->hasFixedValue = true; }
-        bool HasFixedValue() const { return this->hasFixedValue; }
-    };
-
-    class JitEquivalentTypeGuard : public JitIndexedPropertyGuard
-    {
-        // This pointer is allocated from background thread first, and then transferred to recycler,
-        // so as to keep the cached types alive.
-        EquivalentTypeCache* cache;
-        uint32 objTypeSpecFldId;
-        // TODO: OOP JIT, reenable these asserts
-#if DBG && 0
-        // Intentionally have as intptr_t so this guard doesn't hold scriptContext
-        intptr_t originalScriptContextValue = 0;
-#endif
-
-    public:
-        JitEquivalentTypeGuard(intptr_t typeAddr, int index, uint32 objTypeSpecFldId):
-            JitIndexedPropertyGuard(typeAddr, index), cache(nullptr), objTypeSpecFldId(objTypeSpecFldId)
-        {
-#if DBG && 0
-            originalScriptContextValue = reinterpret_cast<intptr_t>(type->GetScriptContext());
-#endif
-        }
-
-        intptr_t GetTypeAddr() const { return this->GetValue(); }
-
-        void SetTypeAddr(const intptr_t typeAddr)
-        {
-#if DBG && 0
-            if (originalScriptContextValue == 0)
-            {
-                originalScriptContextValue = reinterpret_cast<intptr_t>(type->GetScriptContext());
-            }
-            else
-            {
-                AssertMsg(originalScriptContextValue == reinterpret_cast<intptr_t>(type->GetScriptContext()), "Trying to set guard type from different script context.");
-            }
-#endif
-            this->SetValue(typeAddr);
-        }
-
-        uint32 GetObjTypeSpecFldId() const
-        {
-            return this->objTypeSpecFldId;
-        }
-
-        Js::EquivalentTypeCache* GetCache() const
-        {
-            return this->cache;
-        }
-
-        void SetCache(Js::EquivalentTypeCache* cache)
-        {
-            this->cache = cache;
-        }
-    };
-#endif // ENABLE_NATIVE_CODEGEN
-
 #pragma region Inline Cache Info class declarations
     class PolymorphicCacheUtilizationArray
     {

+ 2 - 1
lib/Runtime/Base/ScriptContext.cpp

@@ -11,8 +11,9 @@
 
 #include "ByteCode/ByteCodeApi.h"
 #include "Library/ProfileString.h"
+#ifdef ENABLE_SCRIPT_DEBUGGING
 #include "Debug/DiagHelperMethodWrapper.h"
-#include "BackendApi.h"
+#endif
 #if PROFILE_DICTIONARY
 #include "DictionaryStats.h"
 #endif

+ 0 - 1
lib/Runtime/Base/ThreadContext.cpp

@@ -4,7 +4,6 @@
 //-------------------------------------------------------------------------------------------------------
 
 #include "RuntimeBasePch.h"
-#include "BackendApi.h"
 #include "ThreadServiceWrapper.h"
 #include "Types/TypePropertyCache.h"
 #ifdef ENABLE_SCRIPT_DEBUGGING

+ 1 - 1
lib/Runtime/Language/CMakeLists.txt

@@ -14,6 +14,7 @@ set(CRL_SOURCE_FILES ${CRL_SOURCE_FILES}
     WAsmjsUtils.cpp
     WebAssemblySource.cpp
     CacheOperators.cpp
+    ConstructorCache.cpp
     CodeGenRecyclableData.cpp
     DynamicProfileInfo.cpp
     DynamicProfileMutator.cpp
@@ -26,7 +27,6 @@ set(CRL_SOURCE_FILES ${CRL_SOURCE_FILES}
     JavascriptExceptionObject.cpp
     JavascriptExceptionOperators.cpp
     JavascriptMathOperators.cpp
-    JavascriptNativeOperators.cpp
     JavascriptOperators.cpp
     JavascriptStackWalker.cpp
     ModuleNamespace.cpp

+ 2 - 2
lib/Runtime/Language/Chakra.Runtime.Language.vcxproj

@@ -173,12 +173,12 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)RuntimeLanguagePch.cpp">
       <PrecompiledHeader>Create</PrecompiledHeader>
     </ClCompile>
-    <ClCompile Include="$(MSBuildThisFileDirectory)JavascriptNativeOperators.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)ModuleNamespace.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)SourceTextModuleRecord.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)WAsmjsUtils.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)ModuleNamespaceEnumerator.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblySource.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)ConstructorCache.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="amd64\StackFrame.h">
@@ -202,6 +202,7 @@
     <ClInclude Include="AsmJsTypes.h" />
     <ClInclude Include="AsmJsUtils.h" />
     <ClInclude Include="CacheOperators.h" />
+    <ClInclude Include="ConstructorCache.h" />
     <ClInclude Include="i386\AsmJsInstructionTemplate.h">
       <ExcludedFromBuild Condition="'$(Platform)'!='Win32'">true</ExcludedFromBuild>
     </ClInclude>
@@ -221,7 +222,6 @@
     <ClInclude Include="InlineCachePointerArray.h" />
     <ClInclude Include="JavascriptExceptionOperators.h" />
     <ClInclude Include="JavascriptMathOperators.h" />
-    <ClInclude Include="JavascriptNativeOperators.h" />
     <ClInclude Include="ModuleNamespace.h" />
     <ClInclude Include="ModuleNamespaceEnumerator.h" />
     <ClInclude Include="ProfilingHelpers.h" />

+ 3 - 3
lib/Runtime/Language/Chakra.Runtime.Language.vcxproj.filters

@@ -57,7 +57,6 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)SimdUtils.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)TaggedInt.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)RuntimeLanguagePch.cpp" />
-    <ClCompile Include="$(MSBuildThisFileDirectory)JavascriptNativeOperators.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)SIMDInt16x8Operation.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)SIMDInt16x8OperationX86X64.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)SIMDUInt32x4Operation.cpp" />
@@ -77,6 +76,7 @@
     <ClCompile Include="$(MSBuildThisFileDirectory)SourceTextModuleRecord.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)ModuleNamespaceEnumerator.cpp" />
     <ClCompile Include="$(MSBuildThisFileDirectory)WebAssemblySource.cpp" />
+    <ClCompile Include="$(MSBuildThisFileDirectory)ConstructorCache.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="AsmJs.h" />
@@ -125,7 +125,6 @@
     </ClInclude>
     <ClInclude Include="AsmJsTypes.h" />
     <ClInclude Include="AsmJsBuiltInNames.h" />
-    <ClInclude Include="JavascriptNativeOperators.h" />
     <ClInclude Include="JavascriptExceptionContext.h" />
     <ClInclude Include="JavascriptExceptionObject.h" />
     <ClInclude Include="SimdFloat32x4Operation.h" />
@@ -149,6 +148,7 @@
     <ClInclude Include="ModuleNamespace.h" />
     <ClInclude Include="ModuleNamespaceEnumerator.h" />
     <ClInclude Include="WebAssemblySource.h" />
+    <ClInclude Include="ConstructorCache.h" />
   </ItemGroup>
   <ItemGroup>
     <MASM Include="$(MSBuildThisFileDirectory)amd64\amd64_Thunks.asm">
@@ -215,4 +215,4 @@
       <Filter>arm64</Filter>
     </ARMASM>
   </ItemGroup>
-</Project>
+</Project>

+ 212 - 0
lib/Runtime/Language/ConstructorCache.cpp

@@ -0,0 +1,212 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+#include "RuntimeLanguagePch.h"
+
+
+namespace Js
+{
+    ConstructorCache ConstructorCache::DefaultInstance;
+
+    ConstructorCache::ConstructorCache()
+    {
+        this->content.type = nullptr;
+        this->content.scriptContext = nullptr;
+        this->content.slotCount = 0;
+        this->content.inlineSlotCount = 0;
+        this->content.updateAfterCtor = false;
+        this->content.ctorHasNoExplicitReturnValue = false;
+        this->content.skipDefaultNewObject = false;
+        this->content.isPopulated = false;
+        this->content.isPolymorphic = false;
+        this->content.typeUpdatePending = false;
+        this->content.typeIsFinal = false;
+        this->content.hasPrototypeChanged = false;
+        this->content.callCount = 0;
+        Assert(IsConsistent());
+    }
+
+    ConstructorCache::ConstructorCache(ConstructorCache const * other)
+    {
+        Assert(other != nullptr);
+        this->content.type = other->content.type;
+        this->content.scriptContext = other->content.scriptContext;
+        this->content.slotCount = other->content.slotCount;
+        this->content.inlineSlotCount = other->content.inlineSlotCount;
+        this->content.updateAfterCtor = other->content.updateAfterCtor;
+        this->content.ctorHasNoExplicitReturnValue = other->content.ctorHasNoExplicitReturnValue;
+        this->content.skipDefaultNewObject = other->content.skipDefaultNewObject;
+        this->content.isPopulated = other->content.isPopulated;
+        this->content.isPolymorphic = other->content.isPolymorphic;
+        this->content.typeUpdatePending = other->content.typeUpdatePending;
+        this->content.typeIsFinal = other->content.typeIsFinal;
+        this->content.hasPrototypeChanged = other->content.hasPrototypeChanged;
+        this->content.callCount = other->content.callCount;
+        Assert(IsConsistent());
+    }
+
+    void ConstructorCache::Populate(DynamicType* type, ScriptContext* scriptContext, bool ctorHasNoExplicitReturnValue, bool updateAfterCtor)
+    {
+        Assert(scriptContext == type->GetScriptContext());
+        Assert(type->GetIsShared());
+        Assert(IsConsistent());
+        Assert(!this->content.isPopulated || this->content.isPolymorphic);
+        Assert(type->GetTypeHandler()->GetSlotCapacity() <= MaxCachedSlotCount);
+        this->content.isPopulated = true;
+        this->content.type = type;
+        this->content.scriptContext = scriptContext;
+        this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
+        this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
+        this->content.ctorHasNoExplicitReturnValue = ctorHasNoExplicitReturnValue;
+        this->content.updateAfterCtor = updateAfterCtor;
+        Assert(IsConsistent());
+    }
+
+    void ConstructorCache::PopulateForSkipDefaultNewObject(ScriptContext* scriptContext)
+    {
+        Assert(IsConsistent());
+        Assert(!this->content.isPopulated);
+        this->content.isPopulated = true;
+        this->guard.value = CtorCacheGuardValues::Special;
+        this->content.scriptContext = scriptContext;
+        this->content.skipDefaultNewObject = true;
+        Assert(IsConsistent());
+    }
+
+    bool ConstructorCache::TryUpdateAfterConstructor(DynamicType* type, ScriptContext* scriptContext)
+    {
+        Assert(scriptContext == type->GetScriptContext());
+        Assert(type->GetTypeHandler()->GetMayBecomeShared());
+        Assert(IsConsistent());
+        Assert(this->content.isPopulated);
+        Assert(this->content.scriptContext == scriptContext);
+        Assert(!this->content.typeUpdatePending);
+        Assert(this->content.ctorHasNoExplicitReturnValue);
+
+        if (type->GetTypeHandler()->GetSlotCapacity() > MaxCachedSlotCount)
+        {
+            return false;
+        }
+
+        if (type->GetIsShared())
+        {
+            this->content.type = type;
+            this->content.typeIsFinal = true;
+            this->content.pendingType = nullptr;
+        }
+        else
+        {
+            AssertMsg(false, "No one calls this part of the code?");
+            this->guard.value = CtorCacheGuardValues::Special;
+            this->content.pendingType = type;
+            this->content.typeUpdatePending = true;
+        }
+        this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
+        this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
+        Assert(IsConsistent());
+        return true;
+    }
+
+    void ConstructorCache::UpdateInlineSlotCount()
+    {
+        Assert(IsConsistent());
+        Assert(this->content.isPopulated);
+        Assert(IsEnabled() || NeedsTypeUpdate());
+        DynamicType* type = this->content.typeUpdatePending ? this->content.pendingType : this->content.type;
+        DynamicTypeHandler* typeHandler = type->GetTypeHandler();
+        // Inline slot capacity should never grow as a result of shrinking.
+        Assert(typeHandler->GetInlineSlotCapacity() <= this->content.inlineSlotCount);
+        // Slot capacity should never grow as a result of shrinking.
+        Assert(typeHandler->GetSlotCapacity() <= this->content.slotCount);
+        this->content.slotCount = typeHandler->GetSlotCapacity();
+        this->content.inlineSlotCount = typeHandler->GetInlineSlotCapacity();
+        Assert(IsConsistent());
+    }
+
+    void ConstructorCache::EnableAfterTypeUpdate()
+    {
+        Assert(IsConsistent());
+        Assert(this->content.isPopulated);
+        Assert(!IsEnabled());
+        Assert(this->guard.value == CtorCacheGuardValues::Special);
+        Assert(this->content.typeUpdatePending);
+        Assert(this->content.slotCount == this->content.pendingType->GetTypeHandler()->GetSlotCapacity());
+        Assert(this->content.inlineSlotCount == this->content.pendingType->GetTypeHandler()->GetInlineSlotCapacity());
+        Assert(this->content.pendingType->GetIsShared());
+        this->content.type = this->content.pendingType;
+        this->content.typeIsFinal = true;
+        this->content.pendingType = nullptr;
+        this->content.typeUpdatePending = false;
+        Assert(IsConsistent());
+    }
+
+    ConstructorCache* ConstructorCache::EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext)
+    {
+        Assert(currentCache != nullptr);
+
+        ConstructorCache* newCache = currentCache;
+
+        // If the old cache has been invalidated, we need to create a new one to avoid incorrectly re-validating
+        // caches that may have been hard-coded in the JIT-ed code with different prototype and type.  However, if
+        // the cache is already polymorphic, it will not be hard-coded, and hence we don't need to allocate a new
+        // one - in case the prototype property changes frequently.
+        if (ConstructorCache::IsDefault(currentCache) || (currentCache->IsInvalidated() && !currentCache->IsPolymorphic()))
+        {
+            // Review (jedmiad): I don't think we need to zero the struct, since we initialize each field.
+            newCache = RecyclerNew(scriptContext->GetRecycler(), ConstructorCache);
+            // TODO: Consider marking the cache as polymorphic only if the prototype and type actually changed.  In fact,
+            // if they didn't change we could reuse the same cache and simply mark it as valid.  Not really true.  The cache
+            // might have been invalidated due to a property becoming read-only.  In that case we can't re-validate an old
+            // monomorphic cache.  We must allocate a new one.
+            newCache->content.isPolymorphic = currentCache->content.isPopulated && currentCache->content.hasPrototypeChanged;
+        }
+
+        // If we kept the old invalidated cache, it better be marked as polymorphic.
+        Assert(!newCache->IsInvalidated() || newCache->IsPolymorphic());
+
+        // If the cache was polymorphic, we shouldn't have allocated a new one.
+        Assert(!currentCache->IsPolymorphic() || newCache == currentCache);
+
+        return newCache;
+    }
+
+    void ConstructorCache::InvalidateOnPrototypeChange()
+    {
+        if (IsDefault(this))
+        {
+            Assert(this->guard.value == CtorCacheGuardValues::Invalid);
+            Assert(!this->content.isPopulated);
+        }
+        else if (this->guard.value == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject)
+        {
+            // Do nothing.  If we skip the default object, changes to the prototype property don't affect
+            // what we'll do during object allocation.
+
+            // Can't assert the following because we set the prototype property during library initialization.
+            // AssertMsg(false, "Overriding a prototype on a built-in constructor should be illegal.");
+        }
+        else
+        {
+            this->guard.value = CtorCacheGuardValues::Invalid;
+            this->content.hasPrototypeChanged = true;
+            // Make sure we don't leak the old type.
+            Assert(this->content.type == nullptr);
+            this->content.pendingType = nullptr;
+            Assert(this->content.pendingType == nullptr);
+            Assert(IsInvalidated());
+        }
+        Assert(IsConsistent());
+    }
+
+#if DBG_DUMP
+    void ConstructorCache::Dump() const
+    {
+        Output::Print(_u("guard value or type = 0x%p, script context = 0x%p, pending type = 0x%p, slots = %d, inline slots = %d, populated = %d, polymorphic = %d, update cache = %d, update type = %d, skip default = %d, no return = %d"),
+            this->GetRawGuardValue(), this->GetScriptContext(), this->GetPendingType(), this->GetSlotCount(), this->GetInlineSlotCount(),
+            this->IsPopulated(), this->IsPolymorphic(), this->GetUpdateCacheAfterCtor(), this->GetTypeUpdatePending(),
+            this->GetSkipDefaultNewObject(), this->GetCtorHasNoExplicitReturnValue());
+    }
+#endif
+}

+ 271 - 0
lib/Runtime/Language/ConstructorCache.h

@@ -0,0 +1,271 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+#pragma once
+
+namespace Js
+{
+    enum class CtorCacheGuardValues : intptr_t
+    {
+        TagFlag = 0x01,
+
+        Invalid = 0x00,
+        Special = TagFlag
+    };
+    ENUM_CLASS_HELPERS(CtorCacheGuardValues, intptr_t);
+
+    #define MaxCachedSlotCount 65535
+
+    struct ConstructorCache
+    {
+        friend class JavascriptFunction;
+
+        struct GuardStruct
+        {
+            Field(CtorCacheGuardValues) value;
+        };
+
+        struct ContentStruct
+        {
+            Field(DynamicType*) type;
+            Field(ScriptContext*) scriptContext;
+            // In a pinch we could eliminate this and store type pending sharing in the type field as long
+            // as the guard value flags fit below the object alignment boundary.  However, this wouldn't
+            // keep the type alive, so it would only work if we zeroed constructor caches before GC.
+            Field(DynamicType*) pendingType;
+
+            // We cache only types whose slotCount < 64K to ensure the slotCount field doesn't look like a pointer to the recycler.
+            Field(int) slotCount;
+
+            // This layout (i.e. one-byte bit fields first, then the one-byte updateAfterCtor, and then the two byte inlineSlotCount) is
+            // chosen intentionally to make sure the whole four bytes never look like a pointer and create a false reference pinning something
+            // in recycler heap.  The isPopulated bit is always set when the cache holds any data - even if it got invalidated.
+            Field(bool) isPopulated : 1;
+            Field(bool) isPolymorphic : 1;
+            Field(bool) typeUpdatePending : 1;
+            Field(bool) ctorHasNoExplicitReturnValue : 1;
+            Field(bool) skipDefaultNewObject : 1;
+            // This field indicates that the type stored in this cache is the final type after constructor.
+            Field(bool) typeIsFinal : 1;
+            // This field indicates that the constructor cache has been invalidated due to a constructor's prototype property change.
+            // We use this flag to determine if we should mark the cache as polymorphic and not attempt subsequent optimizations.
+            // The cache may also be invalidated due to a guard invalidation resulting from some property change (e.g. in proto chain),
+            // in which case we won't deem the cache polymorphic.
+            Field(bool) hasPrototypeChanged : 1;
+
+            Field(uint8) callCount;
+
+            // Separate from the bit field below for convenient compare from the JIT-ed code. Doesn't currently increase the size.
+            // If size becomes an issue, we could merge back into the bit field and use a TEST instead of CMP.
+            Field(bool) updateAfterCtor;
+
+            Field(int16) inlineSlotCount;
+        };
+
+        union
+        {
+            Field(GuardStruct) guard;
+            Field(ContentStruct) content;
+        };
+
+        CompileAssert(offsetof(GuardStruct, value) == offsetof(ContentStruct, type));
+        CompileAssert(sizeof(((GuardStruct*)nullptr)->value) == sizeof(((ContentStruct*)nullptr)->type));
+        CompileAssert(static_cast<intptr_t>(CtorCacheGuardValues::Invalid) == static_cast<intptr_t>(NULL));
+
+        static ConstructorCache DefaultInstance;
+
+    public:
+        ConstructorCache();
+        ConstructorCache(ConstructorCache const * other);
+
+        static size_t const GetOffsetOfGuardValue() { return offsetof(Js::ConstructorCache, guard.value); }
+        static size_t const GetSizeOfGuardValue() { return sizeof(((Js::ConstructorCache*)nullptr)->guard.value); }
+
+        void Populate(DynamicType* type, ScriptContext* scriptContext, bool ctorHasNoExplicitReturnValue, bool updateAfterCtor);
+        void PopulateForSkipDefaultNewObject(ScriptContext* scriptContext);
+        bool TryUpdateAfterConstructor(DynamicType* type, ScriptContext* scriptContext);
+        void UpdateInlineSlotCount();
+        void EnableAfterTypeUpdate();
+
+        intptr_t GetRawGuardValue() const
+        {
+            return static_cast<intptr_t>(this->guard.value);
+        }
+
+        DynamicType* GetGuardValueAsType() const
+        {
+            return reinterpret_cast<DynamicType*>(this->guard.value & ~CtorCacheGuardValues::TagFlag);
+        }
+
+        DynamicType* GetType() const
+        {
+            Assert(static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0);
+            return this->content.type;
+        }
+
+        DynamicType* GetPendingType() const
+        {
+            return this->content.pendingType;
+        }
+
+        ScriptContext* GetScriptContext() const
+        {
+            return this->content.scriptContext;
+        }
+
+        int GetSlotCount() const
+        {
+            return this->content.slotCount;
+        }
+
+        int16 GetInlineSlotCount() const
+        {
+            return this->content.inlineSlotCount;
+        }
+
+        static bool IsDefault(const ConstructorCache* constructorCache)
+        {
+            return constructorCache == &ConstructorCache::DefaultInstance;
+        }
+
+        bool IsDefault() const
+        {
+            return IsDefault(this);
+        }
+
+        bool IsPopulated() const
+        {
+            Assert(IsConsistent());
+            return this->content.isPopulated;
+        }
+
+        bool IsEmpty() const
+        {
+            Assert(IsConsistent());
+            return !this->content.isPopulated;
+        }
+
+        bool IsPolymorphic() const
+        {
+            Assert(IsConsistent());
+            return this->content.isPolymorphic;
+        }
+
+        bool GetSkipDefaultNewObject() const
+        {
+            return this->content.skipDefaultNewObject;
+        }
+
+        bool GetCtorHasNoExplicitReturnValue() const
+        {
+            return this->content.ctorHasNoExplicitReturnValue;
+        }
+
+        bool GetUpdateCacheAfterCtor() const
+        {
+            return this->content.updateAfterCtor;
+        }
+
+        bool GetTypeUpdatePending() const
+        {
+            return this->content.typeUpdatePending;
+        }
+
+        bool IsEnabled() const
+        {
+            return GetGuardValueAsType() != nullptr;
+        }
+
+        bool IsInvalidated() const
+        {
+            return this->guard.value == CtorCacheGuardValues::Invalid && this->content.isPopulated;
+        }
+
+        bool NeedsTypeUpdate() const
+        {
+            return this->guard.value == CtorCacheGuardValues::Special && this->content.typeUpdatePending;
+        }
+
+        uint8 CallCount() const
+        {
+            return content.callCount;
+        }
+
+        void IncCallCount()
+        {
+            ++content.callCount;
+            Assert(content.callCount != 0);
+        }
+
+        bool NeedsUpdateAfterCtor() const
+        {
+            return this->content.updateAfterCtor;
+        }
+
+        bool IsNormal() const
+        {
+            return this->guard.value != CtorCacheGuardValues::Invalid && static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0;
+        }
+
+        bool SkipDefaultNewObject() const
+        {
+            return this->guard.value == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject;
+        }
+
+        bool IsSetUpForJit() const
+        {
+            return GetRawGuardValue() != NULL && !IsPolymorphic() && !NeedsUpdateAfterCtor() && (IsNormal() || SkipDefaultNewObject());
+        }
+
+        void ClearUpdateAfterCtor()
+        {
+            Assert(IsConsistent());
+            Assert(this->content.isPopulated);
+            Assert(this->content.updateAfterCtor);
+            this->content.updateAfterCtor = false;
+            Assert(IsConsistent());
+        }
+
+        static ConstructorCache* EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext);
+
+        const void* GetAddressOfGuardValue() const
+        {
+            return reinterpret_cast<const void*>(&this->guard.value);
+        }
+
+        static uint32 GetOffsetOfUpdateAfterCtor()
+        {
+            return offsetof(ConstructorCache, content.updateAfterCtor);
+        }
+
+        void InvalidateAsGuard()
+        {
+            Assert(!IsDefault(this));
+            this->guard.value = CtorCacheGuardValues::Invalid;
+            // Make sure we don't leak the types.
+            Assert(this->content.type == nullptr);
+            Assert(this->content.pendingType == nullptr);
+            Assert(IsInvalidated());
+            Assert(IsConsistent());
+        }
+
+    #if DBG
+        bool IsConsistent() const
+        {
+            return this->guard.value == CtorCacheGuardValues::Invalid ||
+                (this->content.isPopulated && (
+                (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.slotCount == 0 && this->content.inlineSlotCount == 0 && this->content.pendingType == nullptr) ||
+                    (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.typeUpdatePending && !this->content.skipDefaultNewObject && this->content.pendingType != nullptr) ||
+                    ((this->guard.value & CtorCacheGuardValues::TagFlag) == CtorCacheGuardValues::Invalid && !this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.pendingType == nullptr)));
+        }
+    #endif
+
+    #if DBG_DUMP
+        void Dump() const;
+    #endif
+
+    private:
+        void InvalidateOnPrototypeChange();
+    };
+}

+ 0 - 220
lib/Runtime/Language/InlineCache.cpp

@@ -1129,226 +1129,6 @@ namespace Js
     }
 #endif
 
-#if ENABLE_NATIVE_CODEGEN
-
-    EquivalentTypeSet::EquivalentTypeSet(RecyclerJITTypeHolder * types, uint16 count)
-        : types(types), count(count), sortedAndDuplicatesRemoved(false)
-    {
-    }
-
-    JITTypeHolder EquivalentTypeSet::GetType(uint16 index) const
-    {
-        Assert(this->types != nullptr && this->count > 0 && index < this->count);
-        return this->types[index];
-    }
-
-    JITTypeHolder EquivalentTypeSet::GetFirstType() const
-    {
-        return GetType(0);
-    }
-
-    bool EquivalentTypeSet::Contains(const JITTypeHolder type, uint16* pIndex)
-    {
-        if (!this->GetSortedAndDuplicatesRemoved())
-        {
-            this->SortAndRemoveDuplicates();
-        }
-        for (uint16 ti = 0; ti < this->count; ti++)
-        {
-            if (this->GetType(ti) == type)
-            {
-                if (pIndex)
-                {
-                    *pIndex = ti;
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    bool EquivalentTypeSet::AreIdentical(EquivalentTypeSet * left, EquivalentTypeSet * right)
-    {
-        if (!left->GetSortedAndDuplicatesRemoved())
-        {
-            left->SortAndRemoveDuplicates();
-        }
-        if (!right->GetSortedAndDuplicatesRemoved())
-        {
-            right->SortAndRemoveDuplicates();
-        }
-
-        Assert(left->GetSortedAndDuplicatesRemoved() && right->GetSortedAndDuplicatesRemoved());
-
-        if (left->count != right->count)
-        {
-            return false;
-        }
-
-        // TODO: OOP JIT, optimize this (previously we had memcmp)
-        for (uint i = 0; i < left->count; ++i)
-        {
-            if (left->types[i] != right->types[i])
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    bool EquivalentTypeSet::IsSubsetOf(EquivalentTypeSet * left, EquivalentTypeSet * right)
-    {
-        if (!left->GetSortedAndDuplicatesRemoved())
-        {
-            left->SortAndRemoveDuplicates();
-        }
-        if (!right->GetSortedAndDuplicatesRemoved())
-        {
-            right->SortAndRemoveDuplicates();
-        }
-
-        if (left->count > right->count)
-        {
-            return false;
-        }
-
-        // Try to find each left type in the right set.
-        int j = 0;
-        for (int i = 0; i < left->count; i++)
-        {
-            bool found = false;
-            for (; j < right->count; j++)
-            {
-                if (left->types[i] < right->types[j])
-                {
-                    // Didn't find the left type. Fail.
-                    return false;
-                }
-                if (left->types[i] == right->types[j])
-                {
-                    // Found the left type. Continue to the next left/right pair.
-                    found = true;
-                    j++;
-                    break;
-                }
-            }
-            Assert(j <= right->count);
-            if (j == right->count && !found)
-            {
-                // Exhausted the right set without finding the current left type.
-                return false;
-            }
-        }
-        return true;
-    }
-
-    void EquivalentTypeSet::SortAndRemoveDuplicates()
-    {
-        uint16 oldCount = this->count;
-        uint16 i;
-
-        // sorting
-        for (i = 1; i < oldCount; i++)
-        {
-            uint16 j = i;
-            while (j > 0 && (this->types[j - 1] > this->types[j]))
-            {
-                JITTypeHolder tmp = this->types[j];
-                this->types[j] = this->types[j - 1];
-                this->types[j - 1] = tmp;
-                j--;
-            }
-        }
-
-        // removing duplicate types from the sorted set
-        i = 0;
-        for (uint16 j = 1; j < oldCount; j++)
-        {
-            if (this->types[i] != this->types[j])
-            {
-                this->types[++i] = this->types[j];
-            }
-        }
-        this->count = ++i;
-        for (i; i < oldCount; i++)
-        {
-            this->types[i] = JITTypeHolder(nullptr);
-        }
-
-        this->sortedAndDuplicatesRemoved = true;
-    }
-#endif
-
-    ConstructorCache ConstructorCache::DefaultInstance;
-
-    ConstructorCache* ConstructorCache::EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext)
-    {
-        Assert(currentCache != nullptr);
-
-        ConstructorCache* newCache = currentCache;
-
-        // If the old cache has been invalidated, we need to create a new one to avoid incorrectly re-validating
-        // caches that may have been hard-coded in the JIT-ed code with different prototype and type.  However, if
-        // the cache is already polymorphic, it will not be hard-coded, and hence we don't need to allocate a new
-        // one - in case the prototype property changes frequently.
-        if (ConstructorCache::IsDefault(currentCache) || (currentCache->IsInvalidated() && !currentCache->IsPolymorphic()))
-        {
-            // Review (jedmiad): I don't think we need to zero the struct, since we initialize each field.
-            newCache = RecyclerNew(scriptContext->GetRecycler(), ConstructorCache);
-            // TODO: Consider marking the cache as polymorphic only if the prototype and type actually changed.  In fact,
-            // if they didn't change we could reuse the same cache and simply mark it as valid.  Not really true.  The cache
-            // might have been invalidated due to a property becoming read-only.  In that case we can't re-validate an old
-            // monomorphic cache.  We must allocate a new one.
-            newCache->content.isPolymorphic = currentCache->content.isPopulated && currentCache->content.hasPrototypeChanged;
-        }
-
-        // If we kept the old invalidated cache, it better be marked as polymorphic.
-        Assert(!newCache->IsInvalidated() || newCache->IsPolymorphic());
-
-        // If the cache was polymorphic, we shouldn't have allocated a new one.
-        Assert(!currentCache->IsPolymorphic() || newCache == currentCache);
-
-        return newCache;
-    }
-
-    void ConstructorCache::InvalidateOnPrototypeChange()
-    {
-        if (IsDefault(this))
-        {
-            Assert(this->guard.value == CtorCacheGuardValues::Invalid);
-            Assert(!this->content.isPopulated);
-        }
-        else if (this->guard.value == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject)
-        {
-            // Do nothing.  If we skip the default object, changes to the prototype property don't affect
-            // what we'll do during object allocation.
-
-            // Can't assert the following because we set the prototype property during library initialization.
-            // AssertMsg(false, "Overriding a prototype on a built-in constructor should be illegal.");
-        }
-        else
-        {
-            this->guard.value = CtorCacheGuardValues::Invalid;
-            this->content.hasPrototypeChanged = true;
-            // Make sure we don't leak the old type.
-            Assert(this->content.type == nullptr);
-            this->content.pendingType = nullptr;
-            Assert(this->content.pendingType == nullptr);
-            Assert(IsInvalidated());
-        }
-        Assert(IsConsistent());
-    }
-
-#if DBG_DUMP
-    void ConstructorCache::Dump() const
-    {
-        Output::Print(_u("guard value or type = 0x%p, script context = 0x%p, pending type = 0x%p, slots = %d, inline slots = %d, populated = %d, polymorphic = %d, update cache = %d, update type = %d, skip default = %d, no return = %d"),
-            this->GetRawGuardValue(), this->GetScriptContext(), this->GetPendingType(), this->GetSlotCount(), this->GetInlineSlotCount(),
-            this->IsPopulated(), this->IsPolymorphic(), this->GetUpdateCacheAfterCtor(), this->GetTypeUpdatePending(),
-            this->GetSkipDefaultNewObject(), this->GetCtorHasNoExplicitReturnValue());
-    }
-#endif
-
     void IsInstInlineCache::Set(Type * instanceType, JavascriptFunction * function, JavascriptBoolean * result)
     {
         this->type = instanceType;

+ 0 - 417
lib/Runtime/Language/InlineCache.h

@@ -601,423 +601,6 @@ namespace Js
         virtual void Finalize(bool isShutdown) override;
     };
 
-#if ENABLE_NATIVE_CODEGEN
-    class EquivalentTypeSet
-    {
-    private:
-        Field(bool) sortedAndDuplicatesRemoved;
-        Field(uint16) count;
-        Field(RecyclerJITTypeHolder *) types;
-
-    public:
-        EquivalentTypeSet(RecyclerJITTypeHolder * types, uint16 count);
-
-        uint16 GetCount() const
-        {
-            return this->count;
-        }
-
-        JITTypeHolder GetFirstType() const;
-
-        JITTypeHolder GetType(uint16 index) const;
-
-        bool GetSortedAndDuplicatesRemoved() const
-        {
-            return this->sortedAndDuplicatesRemoved;
-        }
-        bool Contains(const JITTypeHolder type, uint16 * pIndex = nullptr);
-
-        static bool AreIdentical(EquivalentTypeSet * left, EquivalentTypeSet * right);
-        static bool IsSubsetOf(EquivalentTypeSet * left, EquivalentTypeSet * right);
-        void SortAndRemoveDuplicates();
-    };
-#endif
-    enum class CtorCacheGuardValues : intptr_t
-    {
-        TagFlag = 0x01,
-
-        Invalid = 0x00,
-        Special = TagFlag
-    };
-    ENUM_CLASS_HELPERS(CtorCacheGuardValues, intptr_t);
-
-#define MaxCachedSlotCount 65535
-
-    struct ConstructorCache
-    {
-        friend class JavascriptFunction;
-
-        struct GuardStruct
-        {
-            Field(CtorCacheGuardValues) value;
-        };
-
-        struct ContentStruct
-        {
-            Field(DynamicType*) type;
-            Field(ScriptContext*) scriptContext;
-            // In a pinch we could eliminate this and store type pending sharing in the type field as long
-            // as the guard value flags fit below the object alignment boundary.  However, this wouldn't
-            // keep the type alive, so it would only work if we zeroed constructor caches before GC.
-            Field(DynamicType*) pendingType;
-
-            // We cache only types whose slotCount < 64K to ensure the slotCount field doesn't look like a pointer to the recycler.
-            Field(int) slotCount;
-
-            // This layout (i.e. one-byte bit fields first, then the one-byte updateAfterCtor, and then the two byte inlineSlotCount) is
-            // chosen intentionally to make sure the whole four bytes never look like a pointer and create a false reference pinning something
-            // in recycler heap.  The isPopulated bit is always set when the cache holds any data - even if it got invalidated.
-            Field(bool) isPopulated : 1;
-            Field(bool) isPolymorphic : 1;
-            Field(bool) typeUpdatePending : 1;
-            Field(bool) ctorHasNoExplicitReturnValue : 1;
-            Field(bool) skipDefaultNewObject : 1;
-            // This field indicates that the type stored in this cache is the final type after constructor.
-            Field(bool) typeIsFinal : 1;
-            // This field indicates that the constructor cache has been invalidated due to a constructor's prototype property change.
-            // We use this flag to determine if we should mark the cache as polymorphic and not attempt subsequent optimizations.
-            // The cache may also be invalidated due to a guard invalidation resulting from some property change (e.g. in proto chain),
-            // in which case we won't deem the cache polymorphic.
-            Field(bool) hasPrototypeChanged : 1;
-
-            Field(uint8) callCount;
-
-            // Separate from the bit field below for convenient compare from the JIT-ed code. Doesn't currently increase the size.
-            // If size becomes an issue, we could merge back into the bit field and use a TEST instead of CMP.
-            Field(bool) updateAfterCtor;
-
-            Field(int16) inlineSlotCount;
-        };
-
-        union
-        {
-            Field(GuardStruct) guard;
-            Field(ContentStruct) content;
-        };
-
-        CompileAssert(offsetof(GuardStruct, value) == offsetof(ContentStruct, type));
-        CompileAssert(sizeof(((GuardStruct*)nullptr)->value) == sizeof(((ContentStruct*)nullptr)->type));
-        CompileAssert(static_cast<intptr_t>(CtorCacheGuardValues::Invalid) == static_cast<intptr_t>(NULL));
-
-        static ConstructorCache DefaultInstance;
-
-    public:
-        ConstructorCache()
-        {
-            this->content.type = nullptr;
-            this->content.scriptContext = nullptr;
-            this->content.slotCount = 0;
-            this->content.inlineSlotCount = 0;
-            this->content.updateAfterCtor = false;
-            this->content.ctorHasNoExplicitReturnValue = false;
-            this->content.skipDefaultNewObject = false;
-            this->content.isPopulated = false;
-            this->content.isPolymorphic = false;
-            this->content.typeUpdatePending = false;
-            this->content.typeIsFinal = false;
-            this->content.hasPrototypeChanged = false;
-            this->content.callCount = 0;
-            Assert(IsConsistent());
-        }
-
-        ConstructorCache(ConstructorCache const * other)
-        {
-            Assert(other != nullptr);
-            this->content.type = other->content.type;
-            this->content.scriptContext = other->content.scriptContext;
-            this->content.slotCount = other->content.slotCount;
-            this->content.inlineSlotCount = other->content.inlineSlotCount;
-            this->content.updateAfterCtor = other->content.updateAfterCtor;
-            this->content.ctorHasNoExplicitReturnValue = other->content.ctorHasNoExplicitReturnValue;
-            this->content.skipDefaultNewObject = other->content.skipDefaultNewObject;
-            this->content.isPopulated = other->content.isPopulated;
-            this->content.isPolymorphic = other->content.isPolymorphic;
-            this->content.typeUpdatePending = other->content.typeUpdatePending;
-            this->content.typeIsFinal = other->content.typeIsFinal;
-            this->content.hasPrototypeChanged = other->content.hasPrototypeChanged;
-            this->content.callCount = other->content.callCount;
-            Assert(IsConsistent());
-        }
-
-        static size_t const GetOffsetOfGuardValue() { return offsetof(Js::ConstructorCache, guard.value); }
-        static size_t const GetSizeOfGuardValue() { return sizeof(((Js::ConstructorCache*)nullptr)->guard.value); }
-
-        void Populate(DynamicType* type, ScriptContext* scriptContext, bool ctorHasNoExplicitReturnValue, bool updateAfterCtor)
-        {
-            Assert(scriptContext == type->GetScriptContext());
-            Assert(type->GetIsShared());
-            Assert(IsConsistent());
-            Assert(!this->content.isPopulated || this->content.isPolymorphic);
-            Assert(type->GetTypeHandler()->GetSlotCapacity() <= MaxCachedSlotCount);
-            this->content.isPopulated = true;
-            this->content.type = type;
-            this->content.scriptContext = scriptContext;
-            this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
-            this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
-            this->content.ctorHasNoExplicitReturnValue = ctorHasNoExplicitReturnValue;
-            this->content.updateAfterCtor = updateAfterCtor;
-            Assert(IsConsistent());
-        }
-
-        void PopulateForSkipDefaultNewObject(ScriptContext* scriptContext)
-        {
-            Assert(IsConsistent());
-            Assert(!this->content.isPopulated);
-            this->content.isPopulated = true;
-            this->guard.value = CtorCacheGuardValues::Special;
-            this->content.scriptContext = scriptContext;
-            this->content.skipDefaultNewObject = true;
-            Assert(IsConsistent());
-        }
-
-        bool TryUpdateAfterConstructor(DynamicType* type, ScriptContext* scriptContext)
-        {
-            Assert(scriptContext == type->GetScriptContext());
-            Assert(type->GetTypeHandler()->GetMayBecomeShared());
-            Assert(IsConsistent());
-            Assert(this->content.isPopulated);
-            Assert(this->content.scriptContext == scriptContext);
-            Assert(!this->content.typeUpdatePending);
-            Assert(this->content.ctorHasNoExplicitReturnValue);
-
-            if (type->GetTypeHandler()->GetSlotCapacity() > MaxCachedSlotCount)
-            {
-                return false;
-            }
-
-            if (type->GetIsShared())
-            {
-                this->content.type = type;
-                this->content.typeIsFinal = true;
-                this->content.pendingType = nullptr;
-            }
-            else
-            {
-                AssertMsg(false, "No one calls this part of the code?");
-                this->guard.value = CtorCacheGuardValues::Special;
-                this->content.pendingType = type;
-                this->content.typeUpdatePending = true;
-            }
-            this->content.slotCount = type->GetTypeHandler()->GetSlotCapacity();
-            this->content.inlineSlotCount = type->GetTypeHandler()->GetInlineSlotCapacity();
-            Assert(IsConsistent());
-            return true;
-        }
-
-        void UpdateInlineSlotCount()
-        {
-            Assert(IsConsistent());
-            Assert(this->content.isPopulated);
-            Assert(IsEnabled() || NeedsTypeUpdate());
-            DynamicType* type = this->content.typeUpdatePending ? this->content.pendingType : this->content.type;
-            DynamicTypeHandler* typeHandler = type->GetTypeHandler();
-            // Inline slot capacity should never grow as a result of shrinking.
-            Assert(typeHandler->GetInlineSlotCapacity() <= this->content.inlineSlotCount);
-            // Slot capacity should never grow as a result of shrinking.
-            Assert(typeHandler->GetSlotCapacity() <= this->content.slotCount);
-            this->content.slotCount = typeHandler->GetSlotCapacity();
-            this->content.inlineSlotCount = typeHandler->GetInlineSlotCapacity();
-            Assert(IsConsistent());
-        }
-
-        void EnableAfterTypeUpdate()
-        {
-            Assert(IsConsistent());
-            Assert(this->content.isPopulated);
-            Assert(!IsEnabled());
-            Assert(this->guard.value == CtorCacheGuardValues::Special);
-            Assert(this->content.typeUpdatePending);
-            Assert(this->content.slotCount == this->content.pendingType->GetTypeHandler()->GetSlotCapacity());
-            Assert(this->content.inlineSlotCount == this->content.pendingType->GetTypeHandler()->GetInlineSlotCapacity());
-            Assert(this->content.pendingType->GetIsShared());
-            this->content.type = this->content.pendingType;
-            this->content.typeIsFinal = true;
-            this->content.pendingType = nullptr;
-            this->content.typeUpdatePending = false;
-            Assert(IsConsistent());
-        }
-
-        intptr_t GetRawGuardValue() const
-        {
-            return static_cast<intptr_t>(this->guard.value);
-        }
-
-        DynamicType* GetGuardValueAsType() const
-        {
-            return reinterpret_cast<DynamicType*>(this->guard.value & ~CtorCacheGuardValues::TagFlag);
-        }
-
-        DynamicType* GetType() const
-        {
-            Assert(static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0);
-            return this->content.type;
-        }
-
-        DynamicType* GetPendingType() const
-        {
-            return this->content.pendingType;
-        }
-
-        ScriptContext* GetScriptContext() const
-        {
-            return this->content.scriptContext;
-        }
-
-        int GetSlotCount() const
-        {
-            return this->content.slotCount;
-        }
-
-        int16 GetInlineSlotCount() const
-        {
-            return this->content.inlineSlotCount;
-        }
-
-        static bool IsDefault(const ConstructorCache* constructorCache)
-        {
-            return constructorCache == &ConstructorCache::DefaultInstance;
-        }
-
-        bool IsDefault() const
-        {
-            return IsDefault(this);
-        }
-
-        bool IsPopulated() const
-        {
-            Assert(IsConsistent());
-            return this->content.isPopulated;
-        }
-
-        bool IsEmpty() const
-        {
-            Assert(IsConsistent());
-            return !this->content.isPopulated;
-        }
-
-        bool IsPolymorphic() const
-        {
-            Assert(IsConsistent());
-            return this->content.isPolymorphic;
-        }
-
-        bool GetSkipDefaultNewObject() const
-        {
-            return this->content.skipDefaultNewObject;
-        }
-
-        bool GetCtorHasNoExplicitReturnValue() const
-        {
-            return this->content.ctorHasNoExplicitReturnValue;
-        }
-
-        bool GetUpdateCacheAfterCtor() const
-        {
-            return this->content.updateAfterCtor;
-        }
-
-        bool GetTypeUpdatePending() const
-        {
-            return this->content.typeUpdatePending;
-        }
-
-        bool IsEnabled() const
-        {
-            return GetGuardValueAsType() != nullptr;
-        }
-
-        bool IsInvalidated() const
-        {
-            return this->guard.value == CtorCacheGuardValues::Invalid && this->content.isPopulated;
-        }
-
-        bool NeedsTypeUpdate() const
-        {
-            return this->guard.value == CtorCacheGuardValues::Special && this->content.typeUpdatePending;
-        }
-
-        uint8 CallCount() const
-        {
-            return content.callCount;
-        }
-
-        void IncCallCount()
-        {
-            ++content.callCount;
-            Assert(content.callCount != 0);
-        }
-
-        bool NeedsUpdateAfterCtor() const
-        {
-            return this->content.updateAfterCtor;
-        }
-
-        bool IsNormal() const
-        {
-            return this->guard.value != CtorCacheGuardValues::Invalid && static_cast<intptr_t>(this->guard.value & CtorCacheGuardValues::TagFlag) == 0;
-        }
-
-        bool SkipDefaultNewObject() const
-        {
-            return this->guard.value == CtorCacheGuardValues::Special && this->content.skipDefaultNewObject;
-        }
-
-        bool IsSetUpForJit() const
-        {
-            return GetRawGuardValue() != NULL && !IsPolymorphic() && !NeedsUpdateAfterCtor() && (IsNormal() || SkipDefaultNewObject());
-        }
-
-        void ClearUpdateAfterCtor()
-        {
-            Assert(IsConsistent());
-            Assert(this->content.isPopulated);
-            Assert(this->content.updateAfterCtor);
-            this->content.updateAfterCtor = false;
-            Assert(IsConsistent());
-        }
-
-        static ConstructorCache* EnsureValidInstance(ConstructorCache* currentCache, ScriptContext* scriptContext);
-
-        const void* GetAddressOfGuardValue() const
-        {
-            return reinterpret_cast<const void*>(&this->guard.value);
-        }
-
-        static uint32 GetOffsetOfUpdateAfterCtor()
-        {
-            return offsetof(ConstructorCache, content.updateAfterCtor);
-        }
-
-        void InvalidateAsGuard()
-        {
-            Assert(!IsDefault(this));
-            this->guard.value = CtorCacheGuardValues::Invalid;
-            // Make sure we don't leak the types.
-            Assert(this->content.type == nullptr);
-            Assert(this->content.pendingType == nullptr);
-            Assert(IsInvalidated());
-            Assert(IsConsistent());
-        }
-
-#if DBG
-        bool IsConsistent() const
-        {
-            return this->guard.value == CtorCacheGuardValues::Invalid ||
-                (this->content.isPopulated && (
-                    (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.slotCount == 0 && this->content.inlineSlotCount == 0 && this->content.pendingType == nullptr) ||
-                    (this->guard.value == CtorCacheGuardValues::Special && !this->content.updateAfterCtor && this->content.typeUpdatePending && !this->content.skipDefaultNewObject && this->content.pendingType != nullptr) ||
-                    ((this->guard.value & CtorCacheGuardValues::TagFlag) == CtorCacheGuardValues::Invalid && !this->content.skipDefaultNewObject && !this->content.typeUpdatePending && this->content.pendingType == nullptr)));
-        }
-#endif
-
-#if DBG_DUMP
-        void Dump() const;
-#endif
-
-    private:
-        void InvalidateOnPrototypeChange();
-    };
-
     // Caches the result of an instanceof operator over a type and a function
     struct IsInstInlineCache
     {

+ 0 - 26
lib/Runtime/Language/JavascriptNativeOperators.cpp

@@ -1,26 +0,0 @@
-//-------------------------------------------------------------------------------------------------------
-// Copyright (C) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
-//-------------------------------------------------------------------------------------------------------
-#include "RuntimeLanguagePch.h"
-#include "JavascriptNativeOperators.h"
-
-namespace Js
-{
-#if ENABLE_NATIVE_CODEGEN
-    void * JavascriptNativeOperators::Op_SwitchStringLookUp(JavascriptString* str, Js::BranchDictionaryWrapper<JavascriptString*>* branchTargets, uintptr_t funcStart, uintptr_t funcEnd)
-    {
-        void* defaultTarget = branchTargets->defaultTarget;
-        Js::BranchDictionaryWrapper<JavascriptString*>::BranchDictionary& stringDictionary = branchTargets->dictionary;
-        void* target = stringDictionary.Lookup(str, defaultTarget);
-        uintptr_t utarget = (uintptr_t)target;
-
-        if ((utarget - funcStart) > (funcEnd - funcStart))
-        {
-            AssertMsg(false, "Switch string dictionary jump target outside of function");
-            Throw::FatalInternalError();
-        }
-        return target;
-    }
-#endif
-};

+ 2 - 367
lib/Runtime/Language/JavascriptOperators.cpp

@@ -3309,38 +3309,13 @@ CommonNumber:
         return RecyclableObject::FromVar(callee);
     }
 
-#if ENABLE_NATIVE_CODEGEN
     Var JavascriptOperators::OP_GetElementI_JIT(Var instance, Var index, ScriptContext *scriptContext)
     {
-        Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
-
-        return OP_GetElementI(instance, index, scriptContext);
-    }
-#else
-    Var JavascriptOperators::OP_GetElementI_JIT(Var instance, Var index, ScriptContext *scriptContext)
-    {
-        return OP_GetElementI(instance, index, scriptContext);
-    }
-#endif
-
 #if ENABLE_NATIVE_CODEGEN
-    Var JavascriptOperators::OP_GetElementI_JIT_ExpectingNativeFloatArray(Var instance, Var index, ScriptContext *scriptContext)
-    {
         Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
-
-        UpdateNativeArrayProfileInfoToCreateVarArray(instance, true, false);
-        return OP_GetElementI_JIT(instance, index, scriptContext);
-    }
-
-    Var JavascriptOperators::OP_GetElementI_JIT_ExpectingVarArray(Var instance, Var index, ScriptContext *scriptContext)
-    {
-        Assert(Js::JavascriptStackWalker::ValidateTopJitFrame(scriptContext));
-
-
-        UpdateNativeArrayProfileInfoToCreateVarArray(instance, false, true);
-        return OP_GetElementI_JIT(instance, index, scriptContext);
-    }
 #endif
+        return OP_GetElementI(instance, index, scriptContext);
+    }
 
     Var JavascriptOperators::OP_GetElementI_UInt32(Var instance, uint32 index, ScriptContext* scriptContext)
     {
@@ -3353,22 +3328,6 @@ CommonNumber:
 #endif
     }
 
-    Var JavascriptOperators::OP_GetElementI_UInt32_ExpectingNativeFloatArray(Var instance, uint32 index, ScriptContext* scriptContext)
-    {
-#if ENABLE_PROFILE_INFO
-        UpdateNativeArrayProfileInfoToCreateVarArray(instance, true, false);
-#endif
-        return OP_GetElementI_UInt32(instance, index, scriptContext);
-    }
-
-    Var JavascriptOperators::OP_GetElementI_UInt32_ExpectingVarArray(Var instance, uint32 index, ScriptContext* scriptContext)
-    {
-#if ENABLE_PROFILE_INFO
-        UpdateNativeArrayProfileInfoToCreateVarArray(instance, false, true);
-#endif
-        return OP_GetElementI_UInt32(instance, index, scriptContext);
-    }
-
     Var JavascriptOperators::OP_GetElementI_Int32(Var instance, int32 index, ScriptContext* scriptContext)
     {
 #if FLOATVAR
@@ -3380,22 +3339,6 @@ CommonNumber:
 #endif
     }
 
-    Var JavascriptOperators::OP_GetElementI_Int32_ExpectingNativeFloatArray(Var instance, int32 index, ScriptContext* scriptContext)
-    {
-#if ENABLE_PROFILE_INFO
-        UpdateNativeArrayProfileInfoToCreateVarArray(instance, true, false);
-#endif
-        return OP_GetElementI_Int32(instance, index, scriptContext);
-    }
-
-    Var JavascriptOperators::OP_GetElementI_Int32_ExpectingVarArray(Var instance, int32 index, ScriptContext* scriptContext)
-    {
-#if ENABLE_PROFILE_INFO
-        UpdateNativeArrayProfileInfoToCreateVarArray(instance, false, true);
-#endif
-        return OP_GetElementI_Int32(instance, index, scriptContext);
-    }
-
     BOOL JavascriptOperators::GetItemFromArrayPrototype(JavascriptArray * arr, int32 indexInt, Var * result, ScriptContext * scriptContext)
     {
         // try get from Array prototype
@@ -8138,314 +8081,6 @@ CommonNumber:
         }
     }
 
-#if ENABLE_NATIVE_CODEGEN
-#if ENABLE_DEBUG_CONFIG_OPTIONS
-    void JavascriptOperators::TracePropertyEquivalenceCheck(const JitEquivalentTypeGuard* guard, const Type* type, const Type* refType, bool isEquivalent, uint failedPropertyIndex)
-    {
-        if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
-        {
-            uint propertyCount = guard->GetCache()->record.propertyCount;
-
-            Output::Print(_u("EquivObjTypeSpec: checking %u properties on operation %u, (type = 0x%p, ref type = 0x%p):\n"),
-                propertyCount, guard->GetObjTypeSpecFldId(), type, refType);
-
-            const Js::TypeEquivalenceRecord& record = guard->GetCache()->record;
-            ScriptContext* scriptContext = type->GetScriptContext();
-            if (isEquivalent)
-            {
-                if (Js::Configuration::Global.flags.Verbose)
-                {
-                    Output::Print(_u("    <start>, "));
-                    for (uint pi = 0; pi < propertyCount; pi++)
-                    {
-                        const EquivalentPropertyEntry* refInfo = &record.properties[pi];
-                        const PropertyRecord* propertyRecord = scriptContext->GetPropertyName(refInfo->propertyId);
-                        Output::Print(_u("%s(#%d)@%ua%dw%d, "), propertyRecord->GetBuffer(), propertyRecord->GetPropertyId(), refInfo->slotIndex, refInfo->isAuxSlot, refInfo->mustBeWritable);
-                    }
-                    Output::Print(_u("<end>\n"));
-                }
-            }
-            else
-            {
-                const EquivalentPropertyEntry* refInfo = &record.properties[failedPropertyIndex];
-                Js::PropertyEquivalenceInfo info(Constants::NoSlot, false, false);
-                const PropertyRecord* propertyRecord = scriptContext->GetPropertyName(refInfo->propertyId);
-                if (DynamicType::Is(type->GetTypeId()))
-                {
-                    Js::DynamicTypeHandler* typeHandler = (static_cast<const DynamicType*>(type))->GetTypeHandler();
-                    typeHandler->GetPropertyEquivalenceInfo(propertyRecord, info);
-                }
-
-                Output::Print(_u("EquivObjTypeSpec: check failed for %s (#%d) on operation %u:\n"),
-                    propertyRecord->GetBuffer(), propertyRecord->GetPropertyId(), guard->GetObjTypeSpecFldId());
-                Output::Print(_u("    type = 0x%p, ref type = 0x%p, slot = 0x%u (%d), ref slot = 0x%u (%d), is writable = %d, required writable = %d\n"),
-                    type, refType, info.slotIndex, refInfo->slotIndex, info.isAuxSlot, refInfo->isAuxSlot, info.isWritable, refInfo->mustBeWritable);
-            }
-
-            Output::Flush();
-        }
-    }
-#endif
-
-    bool JavascriptOperators::IsStaticTypeObjTypeSpecEquivalent(const TypeEquivalenceRecord& equivalenceRecord, uint& failedIndex)
-    {
-        uint propertyCount = equivalenceRecord.propertyCount;
-        Js::EquivalentPropertyEntry* properties = equivalenceRecord.properties;
-        for (uint pi = 0; pi < propertyCount; pi++)
-        {
-            const EquivalentPropertyEntry* refInfo = &properties[pi];
-            if (!IsStaticTypeObjTypeSpecEquivalent(refInfo))
-            {
-                failedIndex = pi;
-                return false;
-            }
-        }
-        return true;
-    }
-
-    bool JavascriptOperators::IsStaticTypeObjTypeSpecEquivalent(const EquivalentPropertyEntry *entry)
-    {
-        // Objects of static types have no local properties, but they may load fields from their prototypes.
-        return entry->slotIndex == Constants::NoSlot && !entry->mustBeWritable;
-    }
-
-    bool JavascriptOperators::CheckIfTypeIsEquivalentForFixedField(Type* type, JitEquivalentTypeGuard* guard)
-    {
-        if (guard->GetValue() == PropertyGuard::GuardValue::Invalidated_DuringSweep)
-        {
-            return false;
-        }
-        return CheckIfTypeIsEquivalent(type, guard);
-    }
-
-    bool JavascriptOperators::CheckIfTypeIsEquivalent(Type* type, JitEquivalentTypeGuard* guard)
-    {
-        if (guard->GetValue() == PropertyGuard::GuardValue::Invalidated)
-        {
-            return false;
-        }
-
-        AssertMsg(type && type->GetScriptContext(), "type and it's ScriptContext should be valid.");
-
-        if (!guard->IsInvalidatedDuringSweep() && ((Js::Type*)guard->GetTypeAddr())->GetScriptContext() != type->GetScriptContext())
-        {
-            // For valid guard value, can't cache cross-context objects
-            return false;
-        }
-
-        // CONSIDER : Add stats on how often the cache hits, and simply force bailout if
-        // the efficacy is too low.
-
-        EquivalentTypeCache* cache = guard->GetCache();
-        // CONSIDER : Consider emitting o.type == equivTypes[hash(o.type)] in machine code before calling
-        // this helper, particularly if we want to handle polymorphism with frequently changing types.
-        Assert(EQUIVALENT_TYPE_CACHE_SIZE == 8);
-        Type** equivTypes = cache->types;
-
-        Type* refType = equivTypes[0];
-        if (refType == nullptr || refType->GetScriptContext() != type->GetScriptContext())
-        {
-            // We could have guard that was invalidated while sweeping and now we have type coming from
-            // different scriptContext. Make sure that it matches the scriptContext in cachedTypes.
-            // If not, return false because as mentioned above, we don't cache cross-context objects.
-#if DBG
-            if (refType == nullptr)
-            {
-                for (int i = 1;i < EQUIVALENT_TYPE_CACHE_SIZE;i++)
-                {
-                    AssertMsg(equivTypes[i] == nullptr, "In equiv typed caches, if first element is nullptr, all others should be nullptr");
-                }
-            }
-#endif
-            return false;
-        }
-
-        if (type == equivTypes[0] || type == equivTypes[1] || type == equivTypes[2] || type == equivTypes[3] ||
-            type == equivTypes[4] || type == equivTypes[5] || type == equivTypes[6] || type == equivTypes[7])
-        {
-#if DBG
-            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
-            {
-                if (guard->WasReincarnated())
-                {
-                    Output::Print(_u("EquivObjTypeSpec: Guard 0x%p was reincarnated and working now \n"), guard);
-                    Output::Flush();
-                }
-            }
-#endif
-            guard->SetTypeAddr((intptr_t)type);
-            return true;
-        }
-
-        // If we didn't find the type in the cache, let's check if it's equivalent the slow way, by comparing
-        // each of its relevant property slots to its equivalent in one of the cached types.
-        // We are making a few assumption that simplify the process:
-        // 1. If two types have the same prototype, any properties loaded from a prototype must come from the same slot.
-        //    If any of the prototypes in the chain was altered such that this is no longer true, the corresponding
-        //    property guard would have been invalidated and we would bail out at the guard check (either on this
-        //    type check or downstream, but before the property load is attempted).
-        // 2. For polymorphic field loads fixed fields are only supported on prototypes.  Hence, if two types have the
-        //    same prototype, any of the equivalent fixed properties will match. If any has been overwritten, the
-        //    corresponding guard would have been invalidated and we would bail out (as above).
-
-        if (cache->IsLoadedFromProto() && type->GetPrototype() != refType->GetPrototype())
-        {
-            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
-            {
-                Output::Print(_u("EquivObjTypeSpec: failed check on operation %u (type = 0x%x, ref type = 0x%x, proto = 0x%x, ref proto = 0x%x) \n"),
-                    guard->GetObjTypeSpecFldId(), type, refType, type->GetPrototype(), refType->GetPrototype());
-                Output::Flush();
-            }
-
-            return false;
-        }
-
-#pragma prefast(suppress:6011) // If type is nullptr, we would AV at the beginning of this method
-        if (type->GetTypeId() != refType->GetTypeId())
-        {
-            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
-            {
-                Output::Print(_u("EquivObjTypeSpec: failed check on operation %u (type = 0x%x, ref type = 0x%x, proto = 0x%x, ref proto = 0x%x) \n"),
-                    guard->GetObjTypeSpecFldId(), type, refType, type->GetPrototype(), refType->GetPrototype());
-                Output::Flush();
-            }
-
-            return false;
-        }
-
-        // Review : This is quite slow.  We could make it somewhat faster, by keeping slot indexes instead
-        // of property IDs, but that would mean we would need to look up property IDs from slot indexes when installing
-        // property guards, or maintain a whole separate list of equivalent slot indexes.
-        Assert(cache->record.propertyCount > 0);
-
-        // Before checking for equivalence, track existing cached non-shared types
-        DynamicType * dynamicType = (type && DynamicType::Is(type->GetTypeId())) ? static_cast<DynamicType*>(type) : nullptr;
-        bool isEquivTypesCacheFull = equivTypes[EQUIVALENT_TYPE_CACHE_SIZE - 1] != nullptr;
-        int emptySlotIndex = -1;
-        int nonSharedTypeSlotIndex = -1;
-        for (int i = 0;i < EQUIVALENT_TYPE_CACHE_SIZE;i++)
-        {
-            // Track presence of cached non-shared type if cache is full
-            if (isEquivTypesCacheFull)
-            {
-                if (DynamicType::Is(equivTypes[i]->GetTypeId()) &&
-                    nonSharedTypeSlotIndex == -1 &&
-                    !(static_cast<DynamicType*>(equivTypes[i]))->GetIsShared())
-                {
-                    nonSharedTypeSlotIndex = i;
-                }
-            }
-            // Otherwise get the next available empty index
-            else if (equivTypes[i] == nullptr)
-            {
-                emptySlotIndex = i;
-                break;
-            };
-        }
-
-        // If we get non-shared type while cache is full and we don't have any non-shared type to evict
-        // consider this type as non-equivalent
-        if (dynamicType != nullptr &&
-            isEquivTypesCacheFull &&
-            !dynamicType->GetIsShared() &&
-            nonSharedTypeSlotIndex == -1)
-        {
-            return false;
-        }
-
-        // CONSIDER (EquivObjTypeSpec): Impose a limit on the number of properties guarded by an equivalent type check.
-        // The trick is where in the glob opt to make the cut off. Perhaps in the forward pass we could track the number of
-        // field operations protected by a type check (keep a counter on the type's value info), and if that counter exceeds
-        // some threshold, simply stop optimizing any further instructions.
-
-        bool isEquivalent;
-        uint failedPropertyIndex;
-        if (dynamicType != nullptr)
-        {
-            Js::DynamicTypeHandler* typeHandler = dynamicType->GetTypeHandler();
-            isEquivalent = typeHandler->IsObjTypeSpecEquivalent(type, cache->record, failedPropertyIndex);
-        }
-        else
-        {
-            Assert(StaticType::Is(type->GetTypeId()));
-            isEquivalent = IsStaticTypeObjTypeSpecEquivalent(cache->record, failedPropertyIndex);
-        }
-
-#if ENABLE_DEBUG_CONFIG_OPTIONS
-        TracePropertyEquivalenceCheck(guard, type, refType, isEquivalent, failedPropertyIndex);
-#endif
-
-        if (!isEquivalent)
-        {
-            return false;
-        }
-
-        AssertMsg(!isEquivTypesCacheFull || !dynamicType || dynamicType->GetIsShared() || nonSharedTypeSlotIndex > -1, "If equiv cache is full, then this should be sharedType or we will evict non-shared type.");
-
-        // If cache is full, then this is definitely a sharedType, so evict non-shared type.
-        // Else evict next empty slot (only applicable for DynamicTypes)
-        emptySlotIndex = (isEquivTypesCacheFull && dynamicType) ? nonSharedTypeSlotIndex : emptySlotIndex;
-
-        // We have some empty slots, let us use those first
-        if (emptySlotIndex != -1)
-        {
-            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
-            {
-                Output::Print(_u("EquivObjTypeSpec: Saving type in unused slot of equiv types cache. \n"));
-                Output::Flush();
-            }
-            equivTypes[emptySlotIndex] = type;
-        }
-        else
-        {
-            // CONSIDER (EquivObjTypeSpec): Invent some form of least recently used eviction scheme.
-            uintptr_t index = (reinterpret_cast<uintptr_t>(type) >> 4) & (EQUIVALENT_TYPE_CACHE_SIZE - 1);
-
-            if (cache->nextEvictionVictim == EQUIVALENT_TYPE_CACHE_SIZE)
-            {
-                __analysis_assume(index < EQUIVALENT_TYPE_CACHE_SIZE);
-                // If nextEvictionVictim was never set, set it to next element after index
-                cache->nextEvictionVictim = (index + 1) & (EQUIVALENT_TYPE_CACHE_SIZE - 1);
-            }
-            else
-            {
-                Assert(cache->nextEvictionVictim < EQUIVALENT_TYPE_CACHE_SIZE);
-                __analysis_assume(cache->nextEvictionVictim < EQUIVALENT_TYPE_CACHE_SIZE);
-                equivTypes[cache->nextEvictionVictim] = equivTypes[index];
-                // Else, set it to next element after current nextEvictionVictim index
-                cache->nextEvictionVictim = (cache->nextEvictionVictim + 1) & (EQUIVALENT_TYPE_CACHE_SIZE - 1);
-            }
-
-            if (PHASE_TRACE1(Js::EquivObjTypeSpecPhase))
-            {
-                Output::Print(_u("EquivObjTypeSpec: Saving type in used slot of equiv types cache at index = %d. NextEvictionVictim = %d. \n"), index, cache->nextEvictionVictim);
-                Output::Flush();
-            }
-            Assert(index < EQUIVALENT_TYPE_CACHE_SIZE);
-            __analysis_assume(index < EQUIVALENT_TYPE_CACHE_SIZE);
-            equivTypes[index] = type;
-        }
-
-        // Fixed field checks allow us to assume a specific type ID, but the assumption is only
-        // valid if we lock the type. Otherwise, the type ID may change out from under us without
-        // evolving the type.
-        // We also need to lock the type in case of, for instance, adding a property to a dictionary type handler.
-        if (dynamicType != nullptr)
-        {
-            if (!dynamicType->GetIsLocked())
-            {
-                // We only need to lock the type to prevent against the type evolving after it has been cached. If the type becomes shared
-                // in the future, any further changes to the type will result in creating a new type handler.
-                dynamicType->LockTypeOnly();
-            }
-        }
-
-        type->SetHasBeenCached();
-        guard->SetTypeAddr((intptr_t)type);
-        return true;
-    }
-#endif
-
     void JavascriptOperators::GetPropertyIdForInt(uint64 value, ScriptContext* scriptContext, PropertyRecord const ** propertyRecord)
     {
         char16 buffer[20];

+ 2 - 20
lib/Runtime/Language/JavascriptOperators.h

@@ -308,18 +308,10 @@ namespace Js
 
         static BOOL OP_HasItem(Var instance, Var aElementIndex, ScriptContext* scriptContext);
         static Var OP_GetElementI(Var instance, Var aElementIndex, ScriptContext* scriptContext);
-        static Var OP_GetElementI_JIT(Var instance, Var index, ScriptContext *scriptContext);
-#if ENABLE_NATIVE_CODEGEN
-        static Var OP_GetElementI_JIT_ExpectingNativeFloatArray(Var instance, Var index, ScriptContext *scriptContext);
-        static Var OP_GetElementI_JIT_ExpectingVarArray(Var instance, Var index, ScriptContext *scriptContext);
-#endif
-
         static Var OP_GetElementI_UInt32(Var instance, uint32 aElementIndex, ScriptContext* scriptContext);
-        static Var OP_GetElementI_UInt32_ExpectingNativeFloatArray(Var instance, uint32 aElementIndex, ScriptContext* scriptContext);
-        static Var OP_GetElementI_UInt32_ExpectingVarArray(Var instance, uint32 aElementIndex, ScriptContext* scriptContext);
         static Var OP_GetElementI_Int32(Var instance, int32 aElementIndex, ScriptContext* scriptContext);
-        static Var OP_GetElementI_Int32_ExpectingNativeFloatArray(Var instance, int32 aElementIndex, ScriptContext* scriptContext);
-        static Var OP_GetElementI_Int32_ExpectingVarArray(Var instance, int32 aElementIndex, ScriptContext* scriptContext);
+        static Var OP_GetElementI_JIT(Var instance, Var index, ScriptContext *scriptContext);
+
         static Var GetElementIHelper(Var instance, Var index, Var receiver, ScriptContext* scriptContext);
         static int32 OP_GetNativeIntElementI(Var instance, Var index);
         static int32 OP_GetNativeIntElementI_Int32(Var instance, int32 index, ScriptContext *scriptContext);
@@ -517,16 +509,6 @@ namespace Js
 
         static Var PatchGetMethodFromObject(Var instance, RecyclableObject * propertyObject, PropertyId propertyId, PropertyValueInfo * info, ScriptContext * scriptContext, bool isRootLd);
 
-#if ENABLE_NATIVE_CODEGEN
-#if ENABLE_DEBUG_CONFIG_OPTIONS
-        static void TracePropertyEquivalenceCheck(const JitEquivalentTypeGuard* guard, const Type* type, const Type* refType, bool isEquivalent, uint failedPropertyIndex);
-#endif
-        static bool IsStaticTypeObjTypeSpecEquivalent(const TypeEquivalenceRecord& equivalenceRecord, uint& failedIndex);
-        static bool IsStaticTypeObjTypeSpecEquivalent(const EquivalentPropertyEntry *entry);
-        static bool CheckIfTypeIsEquivalent(Type* type, JitEquivalentTypeGuard* guard);
-        static bool CheckIfTypeIsEquivalentForFixedField(Type* type, JitEquivalentTypeGuard* guard);
-#endif
-
         static void GetPropertyIdForInt(uint64 value, ScriptContext* scriptContext, PropertyRecord const ** propertyRecord);
         static void GetPropertyIdForInt(uint32 value, ScriptContext* scriptContext, PropertyRecord const ** propertyRecord);
         static BOOL TryConvertToUInt32(const char16* str, int length, uint32* value);

+ 0 - 1
lib/Runtime/Library/JavascriptFunction.cpp

@@ -3,7 +3,6 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "RuntimeLibraryPch.h"
-#include "BackendApi.h"
 #include "Library/StackScriptFunction.h"
 #include "Types/SpreadArgument.h"
 

+ 5 - 3
lib/Runtime/Runtime.h

@@ -261,6 +261,8 @@ namespace Js
     class PolymorphicInlineCacheInfo;
     class PropertyGuard;
 
+    class DetachedStateBase;
+
     // asm.js
     namespace ArrayBufferView
     {
@@ -390,10 +392,9 @@ enum tagDEBUG_EVENT_INFO_TYPE
 #include "Language/ExecutionMode.h"
 #include "Types/TypeId.h"
 
-#include "BackendApi.h"
-#include "DetachedStateBase.h"
-
 #include "Base/Constants.h"
+#include "Language/ConstructorCache.h"
+#include "BackendApi.h"
 #include "ByteCode/OpLayoutsCommon.h"
 #include "ByteCode/OpLayouts.h"
 #include "ByteCode/OpLayoutsAsmJs.h"
@@ -519,6 +520,7 @@ enum tagDEBUG_EVENT_INFO_TYPE
 #include "Library/JavascriptArray.h"
 
 #include "Library/AtomicsObject.h"
+#include "DetachedStateBase.h"
 #include "Library/ArrayBuffer.h"
 #include "Library/SharedArrayBuffer.h"
 #include "Library/TypedArray.h"

+ 2 - 26
lib/Runtime/Types/TypeHandler.h

@@ -14,38 +14,14 @@ namespace Js
         DeferredInitializeMode_SetAccessors
     };
 
+#if ENABLE_FIXED_FIELDS
     enum FixedPropertyKind : CHAR
     {
         FixedDataProperty = 1 << 0,
         FixedMethodProperty = 1 << 1,
         FixedAccessorProperty = 1 << 2,
     };
-
-    struct PropertyEquivalenceInfo
-    {
-        PropertyIndex slotIndex;
-        bool isAuxSlot;
-        bool isWritable;
-
-        PropertyEquivalenceInfo():
-            slotIndex(Constants::NoSlot), isAuxSlot(false), isWritable(false) {}
-        PropertyEquivalenceInfo(PropertyIndex slotIndex, bool isAuxSlot, bool isWritable):
-            slotIndex(slotIndex), isAuxSlot(isAuxSlot), isWritable(isWritable) {}
-    };
-
-    struct EquivalentPropertyEntry
-    {
-        Js::PropertyId propertyId;
-        Js::PropertyIndex slotIndex;
-        bool isAuxSlot;
-        bool mustBeWritable;
-    };
-
-    struct TypeEquivalenceRecord
-    {
-        uint propertyCount;
-        EquivalentPropertyEntry* properties;
-    };
+#endif
 
     typedef bool (__cdecl *DeferredTypeInitializer)(DynamicObject* instance, DeferredTypeHandlerBase * typeHandler, DeferredInitializeMode mode);