Ankur Garg пре 5 година
родитељ
комит
82d3c4556a

+ 43 - 0
lib/Backend/BackwardPass.cpp

@@ -935,6 +935,7 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
                         blockSucc->couldRemoveNegZeroBailoutForDef = nullptr;
                     }
                 }
+                this->CombineTypeIDsWithFinalType(block, blockSucc);
             }
 
             if (blockSucc->noImplicitCallUses != nullptr)
@@ -4733,6 +4734,7 @@ BackwardPass::ProcessNewScObject(IR::Instr* instr)
 #else
                     block->stackSymToFinalType->Clear(objSym->m_id);
 #endif
+                    this->ClearTypeIDWithFinalType(objSym->m_id, block);
                 }
             }
 
@@ -5208,6 +5210,10 @@ BackwardPass::MayPropertyBeWrittenTo(Js::PropertyId propertyId)
 void
 BackwardPass::ProcessPropertySymOpndUse(IR::PropertySymOpnd * opnd)
 {
+    if (opnd == this->currentInstr->GetDst() && this->HasTypeIDWithFinalType(this->currentBlock))
+    {
+        opnd->SetCantChangeType(true);
+    }
 
     // If this operand doesn't participate in the type check sequence it's a pass-through.
     // We will not set any bits on the operand and we will ignore them when lowering.
@@ -5440,6 +5446,7 @@ BackwardPass::TrackObjTypeSpecProperties(IR::PropertySymOpnd *opnd, BasicBlock *
                         this->currentInstr->ChangeEquivalentToMonoTypeCheckBailOut();
                     }
                     bucket->SetMonoGuardType(nullptr);
+                    this->ClearTypeIDWithFinalType(objSym->m_id, block);
                 }
 
                 if (!opnd->IsTypeAvailable())
@@ -5641,6 +5648,7 @@ BackwardPass::TrackAddPropertyTypes(IR::PropertySymOpnd *opnd, BasicBlock *block
     }
 
     pBucket->SetInitialType(typeWithoutProperty);
+    this->SetTypeIDWithFinalType(propertySym->m_stackSym->m_id, block);
 
     if (!PHASE_OFF(Js::ObjTypeSpecStorePhase, this->func))
     {
@@ -5728,6 +5736,7 @@ BackwardPass::TrackAddPropertyTypes(IR::PropertySymOpnd *opnd, BasicBlock *block
 #else
         block->stackSymToFinalType->Clear(propertySym->m_stackSym->m_id);
 #endif
+        this->ClearTypeIDWithFinalType(propertySym->m_stackSym->m_id, block);
     }
 }
 
@@ -5933,6 +5942,40 @@ BackwardPass::ForEachAddPropertyCacheBucket(Fn fn)
     NEXT_HASHTABLE_ENTRY;
 }
 
+void
+BackwardPass::SetTypeIDWithFinalType(int symID, BasicBlock *block)
+{
+    BVSparse<JitArenaAllocator> *bv = block->EnsureTypeIDsWithFinalType(this->tempAlloc);
+    bv->Set(symID);
+}
+
+void
+BackwardPass::ClearTypeIDWithFinalType(int symID, BasicBlock *block)
+{
+    BVSparse<JitArenaAllocator> *bv = block->typeIDsWithFinalType;
+    if (bv != nullptr)
+    {
+        bv->Clear(symID);
+    }
+}
+
+bool
+BackwardPass::HasTypeIDWithFinalType(BasicBlock *block) const
+{
+    return block->typeIDsWithFinalType != nullptr && !block->typeIDsWithFinalType->IsEmpty();
+}
+
+void
+BackwardPass::CombineTypeIDsWithFinalType(BasicBlock *block, BasicBlock *blockSucc)
+{
+    BVSparse<JitArenaAllocator> *bvSucc = blockSucc->typeIDsWithFinalType;
+    if (bvSucc != nullptr && !bvSucc->IsEmpty())
+    {
+        BVSparse<JitArenaAllocator> *bv = block->EnsureTypeIDsWithFinalType(this->tempAlloc);
+        bv->Or(bvSucc);
+    }
+}
+
 bool
 BackwardPass::TransitionUndoesObjectHeaderInlining(AddPropertyCacheBucket *data) const
 {

+ 5 - 0
lib/Backend/BackwardPass.h

@@ -149,6 +149,11 @@ private:
     void InsertTypeTransitionsAtPotentialKills();
     bool TransitionUndoesObjectHeaderInlining(AddPropertyCacheBucket *data) const;
 
+    void SetTypeIDWithFinalType(int symId, BasicBlock *block);
+    void ClearTypeIDWithFinalType(int symId, BasicBlock *block);
+    bool HasTypeIDWithFinalType(BasicBlock *block) const;
+    void CombineTypeIDsWithFinalType(BasicBlock *block, BasicBlock *blockSucc);
+
     template<class Fn> void ForEachAddPropertyCacheBucket(Fn fn);
     static ObjTypeGuardBucket MergeGuardedProperties(ObjTypeGuardBucket bucket1, ObjTypeGuardBucket bucket2);
     static ObjWriteGuardBucket MergeWriteGuards(ObjWriteGuardBucket bucket1, ObjWriteGuardBucket bucket2);

+ 10 - 0
lib/Backend/FlowGraph.cpp

@@ -3399,6 +3399,16 @@ BasicBlock::CreateLoopTopBailOutInfo(GlobOpt * globOpt)
     return bailOutInfo;
 }
 
+BVSparse<JitArenaAllocator> *
+BasicBlock::EnsureTypeIDsWithFinalType(JitArenaAllocator *alloc)
+{
+    if (typeIDsWithFinalType == nullptr)
+    {
+         typeIDsWithFinalType = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
+    }
+    return typeIDsWithFinalType;
+}
+
 IR::Instr *
 FlowGraph::RemoveInstr(IR::Instr *instr, GlobOpt * globOpt)
 {

+ 4 - 0
lib/Backend/FlowGraph.h

@@ -349,6 +349,8 @@ public:
     bool IsLandingPad();
     BailOutInfo * CreateLoopTopBailOutInfo(GlobOpt * globOpt);
 
+    BVSparse<JitArenaAllocator> *EnsureTypeIDsWithFinalType(JitArenaAllocator *alloc);
+
     // GlobOpt Stuff
 public:
     bool         PathDepBranchFolding(GlobOpt* globOptState);
@@ -400,6 +402,7 @@ public:
     HashTable<AddPropertyCacheBucket> *     stackSymToFinalType;
     HashTable<ObjTypeGuardBucket> *         stackSymToGuardedProperties; // Dead store pass only
     HashTable<ObjWriteGuardBucket> *        stackSymToWriteGuardsMap; // Backward pass only
+    BVSparse<JitArenaAllocator> *           typeIDsWithFinalType;
     BVSparse<JitArenaAllocator> *           noImplicitCallUses;
     BVSparse<JitArenaAllocator> *           noImplicitCallNoMissingValuesUses;
     BVSparse<JitArenaAllocator> *           noImplicitCallNativeArrayUses;
@@ -443,6 +446,7 @@ private:
         stackSymToFinalType(nullptr),
         stackSymToGuardedProperties(nullptr),
         stackSymToWriteGuardsMap(nullptr),
+        typeIDsWithFinalType(nullptr),
         noImplicitCallUses(nullptr),
         noImplicitCallNoMissingValuesUses(nullptr),
         noImplicitCallNativeArrayUses(nullptr),

+ 1 - 0
lib/Backend/GlobOptFields.cpp

@@ -392,6 +392,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
 
     case Js::OpCode::InitSetFld:
     case Js::OpCode::InitGetFld:
+    case Js::OpCode::InitClassMember:
     case Js::OpCode::InitClassMemberGet:
     case Js::OpCode::InitClassMemberSet:
         sym = instr->GetDst()->AsSymOpnd()->m_sym;

+ 11 - 0
lib/Backend/JnHelperMethodList.h

@@ -254,6 +254,17 @@ HELPERCALLCHK(Op_PatchPutValueWithThisPtrNoLocalFastPathPolymorphic, ((void (*)(
 HELPERCALLCHK(Op_PatchPutRootValueNoLocalFastPath, ((void (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutRootValueNoLocalFastPath<true, Js::InlineCache>), AttrCanThrow)
 HELPERCALLCHK(Op_PatchPutRootValueNoLocalFastPathPolymorphic, ((void (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutRootValueNoLocalFastPath<true, Js::PolymorphicInlineCache>), AttrCanThrow)
 
+HELPERCALLCHK(Op_PatchInitValueCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::RecyclableObject*, Js::PropertyId, Js::Var))Js::JavascriptOperators::PatchInitValueCantChangeType<Js::InlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchInitValuePolymorphicCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::RecyclableObject*, Js::PropertyId, Js::Var))Js::JavascriptOperators::PatchInitValueCantChangeType<Js::PolymorphicInlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValueCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueCantChangeType<Js::InlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValueWithThisPtrCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueWithThisPtrCantChangeType<Js::InlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValuePolymorphicCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueCantChangeType<Js::PolymorphicInlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValueWithThisPtrPolymorphicCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueWithThisPtrCantChangeType<Js::PolymorphicInlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValueNoLocalFastPathCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueNoLocalFastPathCantChangeType<Js::InlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValueWithThisPtrNoLocalFastPathCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPathCantChangeType<Js::InlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValueNoLocalFastPathPolymorphicCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueNoLocalFastPathCantChangeType<Js::PolymorphicInlineCache>), AttrCanThrow)
+HELPERCALLCHK(Op_PatchPutValueWithThisPtrNoLocalFastPathPolymorphicCantChangeType, ((bool (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPathCantChangeType<Js::PolymorphicInlineCache>), AttrCanThrow)
+
 HELPERCALLCHK(Op_PatchInitValueCheckLayout, ((bool (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::RecyclableObject*, Js::PropertyId, Js::Var))Js::JavascriptOperators::PatchInitValueCheckLayout<Js::InlineCache>), AttrCanThrow)
 HELPERCALLCHK(Op_PatchInitValuePolymorphicCheckLayout, ((bool (*)(Js::FunctionBody *const, Js::PolymorphicInlineCache *const, const Js::InlineCacheIndex, Js::RecyclableObject*, Js::PropertyId, Js::Var))Js::JavascriptOperators::PatchInitValueCheckLayout<Js::PolymorphicInlineCache>), AttrCanThrow)
 HELPERCALLCHK(Op_PatchPutValueCheckLayout, ((bool (*)(Js::FunctionBody *const, Js::InlineCache *const, const Js::InlineCacheIndex, Js::Var, Js::PropertyId, Js::Var, Js::PropertyOperationFlags))Js::JavascriptOperators::PatchPutValueCheckLayout<Js::InlineCache>), AttrCanThrow)

+ 112 - 37
lib/Backend/Lower.cpp

@@ -7112,48 +7112,14 @@ Lowerer::LowerStFld(
     if (dst->AsSymOpnd()->IsPropertySymOpnd())
     {
         propertySymOpnd = dst->AsPropertySymOpnd();
-        if (stFldInstr->HasBailOutInfo() && !propertySymOpnd->IsTypeCheckSeqCandidate() && propertySymOpnd->TypeCheckRequired())
+        if (stFldInstr->HasBailOutInfo() && !propertySymOpnd->IsTypeCheckSeqCandidate() &&
+                (propertySymOpnd->CantChangeType() || propertySymOpnd->TypeCheckRequired()))
         {
             IR::Instr * instrBailTarget = stFldInstr->ShareBailOut();
             LowerBailTarget(instrBailTarget);
             doCheckLayout = true;
             bailOutInfo = stFldInstr->GetBailOutInfo();
-            switch (helperMethod)
-            {
-                case IR::HelperOp_PatchPutValue:
-                    helperMethod = IR::HelperOp_PatchPutValueCheckLayout;
-                    break;
-                case IR::HelperOp_PatchPutValuePolymorphic:
-                    helperMethod = IR::HelperOp_PatchPutValuePolymorphicCheckLayout;
-                    break;
-                case IR::HelperOp_PatchPutValueNoLocalFastPath:
-                    helperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathCheckLayout;
-                    break;
-                case IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphic:
-                    helperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphicCheckLayout;
-                    break;
-                case IR::HelperOp_PatchPutValueWithThisPtr:
-                    helperMethod = IR::HelperOp_PatchPutValueWithThisPtrCheckLayout;
-                    break;
-                case IR::HelperOp_PatchPutValueWithThisPtrPolymorphic:
-                    helperMethod = IR::HelperOp_PatchPutValueWithThisPtrPolymorphicCheckLayout;
-                    break;
-                case IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPath:
-                    helperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathCheckLayout;
-                    break;
-                case IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphic:
-                    helperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphicCheckLayout;
-                    break;
-                case IR::HelperOp_PatchInitValue:
-                    helperMethod = IR::HelperOp_PatchInitValueCheckLayout;
-                    break;
-                case IR::HelperOp_PatchInitValuePolymorphic:
-                    helperMethod = IR::HelperOp_PatchInitValuePolymorphicCheckLayout;
-                    break;
-                default:
-                    AssertOrFailFast(false);
-                    break;
-            }
+            MapStFldHelper(propertySymOpnd, helperMethod, polymorphicHelperMethod);
         }
     }
 
@@ -7221,6 +7187,115 @@ Lowerer::LowerStFld(
     return instrPrev;
 }
 
+void
+Lowerer::MapStFldHelper(IR::PropertySymOpnd * propertySymOpnd, IR::JnHelperMethod &helperMethod, IR::JnHelperMethod &polymorphicHelperMethod)
+{
+    Assert(propertySymOpnd->CantChangeType() || propertySymOpnd->TypeCheckRequired());
+
+    if (propertySymOpnd->CantChangeType())
+    {
+        switch (helperMethod)
+        {
+            case IR::HelperOp_PatchPutValue:
+                helperMethod = IR::HelperOp_PatchPutValueCantChangeType;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValuePolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutValuePolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValuePolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutValueNoLocalFastPath:
+                helperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathCantChangeType;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtr:
+                helperMethod = IR::HelperOp_PatchPutValueWithThisPtrCantChangeType;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrPolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtrPolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrPolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPath:
+                helperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathCantChangeType;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchInitValue:
+                helperMethod = IR::HelperOp_PatchInitValueCantChangeType;
+                polymorphicHelperMethod = IR::HelperOp_PatchInitValuePolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchInitValuePolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchInitValuePolymorphicCantChangeType;
+                break;
+            case IR::HelperOp_PatchPutRootValue:
+            case IR::HelperOp_PatchPutRootValuePolymorphic:
+            case IR::HelperOp_PatchPutRootValueNoLocalFastPath:
+            case IR::HelperOp_PatchPutRootValueNoLocalFastPathPolymorphic:
+                // No helper method change is needed here, because the global object doesn't participate in final type opt, so it can't alias
+                // an object that does.
+                break;
+            default:
+                AssertOrFailFast(false);
+                break;
+        }
+    }
+    else
+    {
+        switch (helperMethod)
+        {
+            case IR::HelperOp_PatchPutValue:
+                helperMethod = IR::HelperOp_PatchPutValueCheckLayout;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValuePolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutValuePolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValuePolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutValueNoLocalFastPath:
+                helperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathCheckLayout;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValueNoLocalFastPathPolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtr:
+                helperMethod = IR::HelperOp_PatchPutValueWithThisPtrCheckLayout;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrPolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtrPolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrPolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPath:
+                helperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathCheckLayout;
+                polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchPutValueWithThisPtrNoLocalFastPathPolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchInitValue:
+                helperMethod = IR::HelperOp_PatchInitValueCheckLayout;
+                polymorphicHelperMethod = IR::HelperOp_PatchInitValuePolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchInitValuePolymorphic:
+                helperMethod = polymorphicHelperMethod = IR::HelperOp_PatchInitValuePolymorphicCheckLayout;
+                break;
+            case IR::HelperOp_PatchPutRootValue:
+            case IR::HelperOp_PatchPutRootValuePolymorphic:
+            case IR::HelperOp_PatchPutRootValueNoLocalFastPath:
+            case IR::HelperOp_PatchPutRootValueNoLocalFastPathPolymorphic:
+                // No helper method change is needed here, because the global object doesn't participate in final type opt, so it can't alias
+                // an object that does.
+                break;
+            default:
+                AssertOrFailFast(false);
+                break;
+        }
+    }
+}
+
 IR::Instr* Lowerer::GenerateCompleteStFld(IR::Instr* instr, bool emitFastPath, IR::JnHelperMethod monoHelperAfterFastPath, IR::JnHelperMethod polyHelperAfterFastPath,
     IR::JnHelperMethod monoHelperWithoutFastPath, IR::JnHelperMethod polyHelperWithoutFastPath, bool withPutFlags, Js::PropertyOperationFlags flags)
 {

+ 1 - 0
lib/Backend/Lower.h

@@ -204,6 +204,7 @@ private:
     void            GenerateStackScriptFunctionInit(IR::RegOpnd * regOpnd, Js::FunctionInfoPtrPtr nestedInfo, IR::Opnd * envOpnd, IR::Instr * insertBeforeInstr);
     IR::Instr *     LowerProfiledStFld(IR::JitProfilingInstr * instr, Js::PropertyOperationFlags flags);
     IR::Instr *     LowerStFld(IR::Instr * stFldInstr, IR::JnHelperMethod helperMethod, IR::JnHelperMethod polymorphicHelperMethod, bool withInlineCache, IR::LabelInstr *ppBailOutLabel = nullptr, bool isHelper = false, bool withPutFlags = false, Js::PropertyOperationFlags flags = Js::PropertyOperation_None);
+    void            MapStFldHelper(IR::PropertySymOpnd * propertySymOpnd, IR::JnHelperMethod &helperMethod, IR::JnHelperMethod &polymorphicHelperMethod);
     IR::Instr *     LowerScopedStFld(IR::Instr * stFldInstr, IR::JnHelperMethod helperMethod, bool withInlineCache,
                                 bool withPropertyOperationFlags = false, Js::PropertyOperationFlags flags = Js::PropertyOperation_None);
     void            LowerProfiledLdElemI(IR::JitProfilingInstr *const instr);

+ 11 - 0
lib/Backend/Opnd.h

@@ -636,6 +636,7 @@ public:
             // Note that even usesFixedValue cannot live on ObjTypeSpecFldInfo, because we may share a cache between
             // e.g. Object.prototype and new Object(), and only the latter actually uses the fixed value, even though both have it.
             bool usesFixedValue: 1;
+            bool cantChangeType: 1;
 
             union
             {
@@ -1035,6 +1036,16 @@ public:
         this->typeCheckRequired = value;
     }
 
+    bool CantChangeType() const
+    {
+        return this->cantChangeType;
+    }
+
+    void SetCantChangeType(bool value)
+    {
+        this->cantChangeType = value;
+    }
+
     uint16 GetObjTypeSpecFlags() const
     {
         return this->objTypeSpecFlags;

+ 2 - 2
lib/Runtime/ByteCode/OpCodes.h

@@ -513,8 +513,8 @@ MACRO_WMS(              StArrSegItem_CI4,       ElementUnsigned1,      OpSideEff
 MACRO(                  StArrSegItem_A,         Auxiliary,      OpSideEffect)
 MACRO_WMS(              DeleteElemI_A,          ElementI,       OpSideEffect|OpHasImplicitCall|OpPostOpDbgBailOut)                  // Remove from instance's indirect element / field, checked
 MACRO_WMS(              DeleteElemIStrict_A,    ElementI,       OpSideEffect|OpHasImplicitCall|OpPostOpDbgBailOut)                  // Remove from instance's indirect element / field, checked
-MACRO_EXTEND_WMS(       InitSetFld,             ElementC,       OpSideEffect|OpOpndHasImplicitCall|OpFastFldInstr|OpPostOpDbgBailOut)   // Set in Object Literal Syntax {set prop(args){}};
-MACRO_EXTEND_WMS(       InitGetFld,             ElementC,       OpSideEffect|OpOpndHasImplicitCall|OpFastFldInstr|OpPostOpDbgBailOut)   // Get in Object Literal Syntax {get prop(){}};
+MACRO_EXTEND_WMS(       InitSetFld,             ElementC,       OpSideEffect|OpOpndHasImplicitCall|OpPostOpDbgBailOut)                  // Set in Object Literal Syntax {set prop(args){}};
+MACRO_EXTEND_WMS(       InitGetFld,             ElementC,       OpSideEffect|OpOpndHasImplicitCall|OpPostOpDbgBailOut)                  // Get in Object Literal Syntax {get prop(){}};
 MACRO_EXTEND_WMS(       InitSetElemI,           ElementI,       OpSideEffect|OpOpndHasImplicitCall|OpPostOpDbgBailOut)                  // Set in Object Literal Syntax {set [expr](args){}};
 MACRO_EXTEND_WMS(       InitGetElemI,           ElementI,       OpSideEffect|OpOpndHasImplicitCall|OpPostOpDbgBailOut)                  // Get in Object Literal Syntax {get [expr](args){}};
 MACRO_EXTEND_WMS(       InitComputedProperty,   ElementI,       OpSideEffect|OpOpndHasImplicitCall|OpPostOpDbgBailOut)                  // Data property in Object Literal Syntax { [expr] : expr};

+ 80 - 0
lib/Runtime/Language/JavascriptOperators.cpp

@@ -8687,6 +8687,86 @@ SetElementIHelper_INDEX_TYPE_IS_NUMBER:
         }
     }
 
+    template <class TInlineCache>
+    inline bool JavascriptOperators::PatchPutValueCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
+    {
+        JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValueCantChangeType);
+        JIT_HELPER_SAME_ATTRIBUTES(Op_PatchPutValueCantChangeType, Op_PatchPutValue);
+
+        Type * oldType = DynamicObject::Is(instance) ? DynamicObject::FromVar(instance)->GetType() : nullptr;
+        PatchPutValueWithThisPtr<true, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
+        return (oldType != nullptr && oldType != DynamicObject::FromVar(instance)->GetType());
+
+        JIT_HELPER_END(Op_PatchPutValueCantChangeType);
+    }
+    JIT_HELPER_TEMPLATE(Op_PatchPutValueCantChangeType, Op_PatchPutValuePolymorphicCantChangeType);
+    template bool JavascriptOperators::PatchPutValueCantChangeType<InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
+    template bool JavascriptOperators::PatchPutValueCantChangeType<PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
+
+    template <class TInlineCache>
+    inline bool JavascriptOperators::PatchPutValueWithThisPtrCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags)
+    {
+        JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValueWithThisPtrCantChangeType);
+        JIT_HELPER_SAME_ATTRIBUTES(Op_PatchPutValueWithThisPtrCantChangeType, Op_PatchPutValueWithThisPtr);
+
+        Type * oldType = DynamicObject::Is(instance) ? DynamicObject::FromVar(instance)->GetType() : nullptr;
+        PatchPutValueWithThisPtr<true, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, thisInstance, flags);
+        return (oldType != nullptr && oldType != DynamicObject::FromVar(instance)->GetType());
+
+        JIT_HELPER_END(Op_PatchPutValueWithThisPtrCantChangeType);
+    }
+    JIT_HELPER_TEMPLATE(Op_PatchPutValueWithThisPtrCantChangeType, Op_PatchPutValueWithThisPtrPolymorphicCantChangeType);
+    template bool JavascriptOperators::PatchPutValueWithThisPtrCantChangeType<InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
+    template bool JavascriptOperators::PatchPutValueWithThisPtrCantChangeType<PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
+
+    template <class TInlineCache>
+    inline bool JavascriptOperators::PatchPutValueNoLocalFastPathCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
+    {
+        JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValueNoLocalFastPathCantChangeType);
+        JIT_HELPER_SAME_ATTRIBUTES(Op_PatchPutValueNoLocalFastPathCantChangeType, Op_PatchPutValueNoLocalFastPath);
+
+        Type * oldType = DynamicObject::Is(instance) ? DynamicObject::FromVar(instance)->GetType() : nullptr;
+        PatchPutValueWithThisPtrNoLocalFastPath<true, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, instance, flags);
+        return (oldType != nullptr && oldType != DynamicObject::FromVar(instance)->GetType());
+
+        JIT_HELPER_END(Op_PatchPutValueNoLocalFastPathCantChangeType);
+    }
+    JIT_HELPER_TEMPLATE(Op_PatchPutValueNoLocalFastPathCantChangeType, Op_PatchPutValueNoLocalFastPathPolymorphicCantChangeType);
+    template bool JavascriptOperators::PatchPutValueNoLocalFastPathCantChangeType<InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
+    template bool JavascriptOperators::PatchPutValueNoLocalFastPathCantChangeType<PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags);
+
+    template <class TInlineCache>
+    inline bool JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPathCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags)
+    {
+        JIT_HELPER_REENTRANT_HEADER(Op_PatchPutValueWithThisPtrNoLocalFastPathCantChangeType);
+        JIT_HELPER_SAME_ATTRIBUTES(Op_PatchPutValueWithThisPtrNoLocalFastPathCantChangeType, Op_PatchPutValueWithThisPtrNoLocalFastPath);
+
+        Type * oldType = DynamicObject::Is(instance) ? DynamicObject::FromVar(instance)->GetType() : nullptr;
+        PatchPutValueWithThisPtrNoLocalFastPath<true, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, instance, propertyId, newValue, thisInstance, flags);
+        return (oldType != nullptr && oldType != DynamicObject::FromVar(instance)->GetType());
+
+        JIT_HELPER_END(Op_PatchPutValueWithThisPtrNoLocalFastPathCantChangeType);
+    }
+    JIT_HELPER_TEMPLATE(Op_PatchPutValueWithThisPtrNoLocalFastPathCantChangeType, Op_PatchPutValueWithThisPtrNoLocalFastPathPolymorphicCantChangeType);
+    template bool JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPathCantChangeType<InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
+    template bool JavascriptOperators::PatchPutValueWithThisPtrNoLocalFastPathCantChangeType<PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags);
+
+    template <class TInlineCache>
+    inline bool JavascriptOperators::PatchInitValueCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue)
+    {
+        JIT_HELPER_REENTRANT_HEADER(Op_PatchInitValueCantChangeType);
+        JIT_HELPER_SAME_ATTRIBUTES(Op_PatchInitValueCantChangeType, Op_PatchInitValue);
+
+        Type * oldType = DynamicObject::Is(object) ? DynamicObject::FromVar(object)->GetType() : nullptr;
+        PatchInitValue<true, TInlineCache>(functionBody, inlineCache, inlineCacheIndex, object, propertyId, newValue);
+        return (oldType != nullptr && oldType != DynamicObject::FromVar(object)->GetType());
+
+        JIT_HELPER_END(Op_PatchInitValueCantChangeType);
+    }
+    JIT_HELPER_TEMPLATE(Op_PatchInitValueCantChangeType, Op_PatchInitValuePolymorphicCantChangeType);
+    template bool JavascriptOperators::PatchInitValueCantChangeType<InlineCache>(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
+    template bool JavascriptOperators::PatchInitValueCantChangeType<PolymorphicInlineCache>(FunctionBody *const functionBody, PolymorphicInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
+
     template <class TInlineCache>
     inline bool JavascriptOperators::PatchPutValueCheckLayout(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags)
     {

+ 6 - 0
lib/Runtime/Language/JavascriptOperators.h

@@ -575,6 +575,12 @@ namespace Js
         template <bool IsFromFullJit, class TInlineCache> static void PatchInitValue(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
         static void PatchInitValueNoFastPath(FunctionBody *const functionBody, InlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
 
+        template <class TInlineCache> static bool PatchPutValueCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var obj, PropertyId propertyId, Var newValue, PropertyOperationFlags flags = PropertyOperation_None);
+        template <class TInlineCache> static bool PatchPutValueWithThisPtrCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var obj, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags = PropertyOperation_None);
+        template <class TInlineCache> static bool PatchPutValueNoLocalFastPathCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags = PropertyOperation_None);
+        template <class TInlineCache> static bool PatchPutValueWithThisPtrNoLocalFastPathCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags = PropertyOperation_None);
+        template <class TInlineCache> static bool PatchInitValueCantChangeType(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, RecyclableObject* object, PropertyId propertyId, Var newValue);
+
         template <class TInlineCache> static bool PatchPutValueCheckLayout(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var obj, PropertyId propertyId, Var newValue, PropertyOperationFlags flags = PropertyOperation_None);
         template <class TInlineCache> static bool PatchPutValueWithThisPtrCheckLayout(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var obj, PropertyId propertyId, Var newValue, Var thisInstance, PropertyOperationFlags flags = PropertyOperation_None);
         template <class TInlineCache> static bool PatchPutValueNoLocalFastPathCheckLayout(FunctionBody *const functionBody, TInlineCache *const inlineCache, const InlineCacheIndex inlineCacheIndex, Var instance, PropertyId propertyId, Var newValue, PropertyOperationFlags flags = PropertyOperation_None);