Răsfoiți Sursa

Deleting old field hoisting code

Rajat Dua 7 ani în urmă
părinte
comite
69495788cd

+ 0 - 89
lib/Backend/BackwardPass.cpp

@@ -58,20 +58,6 @@ BackwardPass::DoByteCodeUpwardExposedUsed() const
         );
 }
 
-bool
-BackwardPass::DoFieldHoistCandidates() const
-{
-    return DoFieldHoistCandidates(this->currentBlock->loop);
-}
-
-bool
-BackwardPass::DoFieldHoistCandidates(Loop * loop) const
-{
-    // We only need to do one pass to generate this data
-    return this->tag == Js::BackwardPhase
-        && !this->IsPrePass() && loop && GlobOpt::DoFieldHoisting(loop);
-}
-
 bool
 BackwardPass::DoMarkTempNumbers() const
 {
@@ -228,7 +214,6 @@ BackwardPass::CleanupBackwardPassInfoInFlowGraph()
         block->upwardExposedUses = nullptr;
         block->upwardExposedFields = nullptr;
         block->typesNeedingKnownObjectLayout = nullptr;
-        block->fieldHoistCandidates = nullptr;
         block->slotDeadStoreCandidates = nullptr;
         block->byteCodeUpwardExposedUsed = nullptr;
 #if DBG
@@ -479,7 +464,6 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
     BVSparse<JitArenaAllocator> * upwardExposedUses = nullptr;
     BVSparse<JitArenaAllocator> * upwardExposedFields = nullptr;
     BVSparse<JitArenaAllocator> * typesNeedingKnownObjectLayout = nullptr;
-    BVSparse<JitArenaAllocator> * fieldHoistCandidates = nullptr;
     BVSparse<JitArenaAllocator> * slotDeadStoreCandidates = nullptr;
     BVSparse<JitArenaAllocator> * byteCodeUpwardExposedUsed = nullptr;
     BVSparse<JitArenaAllocator> * couldRemoveNegZeroBailoutForDef = nullptr;
@@ -522,10 +506,6 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
                 typesNeedingKnownObjectLayout = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
             }
 
-            if (this->DoFieldHoistCandidates())
-            {
-                fieldHoistCandidates = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
-            }
             if (this->DoDeadStoreSlots())
             {
                 slotDeadStoreCandidates = JitAnew(this->tempAlloc, BVSparse<JitArenaAllocator>, this->tempAlloc);
@@ -641,9 +621,6 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
             Assert((blockSucc->typesNeedingKnownObjectLayout != nullptr)
                 || (blockSucc->isLoopHeader && (this->IsPrePass() || blockSucc->loop->IsDescendentOrSelf(block->loop)))
                 || this->tag != Js::DeadStorePhase);
-            Assert((blockSucc->fieldHoistCandidates != nullptr)
-                || blockSucc->isLoopHeader
-                || !this->DoFieldHoistCandidates(blockSucc->loop));
             Assert((blockSucc->slotDeadStoreCandidates != nullptr)
                 || (blockSucc->isLoopHeader && (this->IsPrePass() || blockSucc->loop->IsDescendentOrSelf(block->loop)))
                 || !this->DoDeadStoreSlots());
@@ -690,15 +667,6 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
                 }
             }
 
-            if (fieldHoistCandidates && blockSucc->fieldHoistCandidates != nullptr)
-            {
-                fieldHoistCandidates->Or(blockSucc->fieldHoistCandidates);
-                if (deleteData)
-                {
-                    JitAdelete(this->tempAlloc, blockSucc->fieldHoistCandidates);
-                    blockSucc->fieldHoistCandidates = nullptr;
-                }
-            }
             if (blockSucc->slotDeadStoreCandidates != nullptr)
             {
                 slotDeadStoreCandidates->And(blockSucc->slotDeadStoreCandidates);
@@ -1134,7 +1102,6 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
             Assert(block->upwardExposedUses == nullptr);
             Assert(block->upwardExposedFields == nullptr);
             Assert(block->typesNeedingKnownObjectLayout == nullptr);
-            Assert(block->fieldHoistCandidates == nullptr);
             // byteCodeUpwardExposedUsed is required to populate the writeThroughSymbolsSet for the try region in the backwards pass
             Assert(block->byteCodeUpwardExposedUsed == nullptr || (this->DoByteCodeUpwardExposedUsed()));
             Assert(block->byteCodeRestoreSyms == nullptr);
@@ -1179,7 +1146,6 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
     block->upwardExposedUses = upwardExposedUses;
     block->upwardExposedFields = upwardExposedFields;
     block->typesNeedingKnownObjectLayout = typesNeedingKnownObjectLayout;
-    block->fieldHoistCandidates = fieldHoistCandidates;
     block->byteCodeUpwardExposedUsed = byteCodeUpwardExposedUsed;
 #if DBG
     block->byteCodeRestoreSyms = byteCodeRestoreSyms;
@@ -1355,11 +1321,6 @@ BackwardPass::DeleteBlockData(BasicBlock * block)
         JitAdelete(this->tempAlloc, block->typesNeedingKnownObjectLayout);
         block->typesNeedingKnownObjectLayout = nullptr;
     }
-    if (block->fieldHoistCandidates != nullptr)
-    {
-        JitAdelete(this->tempAlloc, block->fieldHoistCandidates);
-        block->fieldHoistCandidates = nullptr;
-    }
     if (block->byteCodeUpwardExposedUsed != nullptr)
     {
         JitAdelete(this->tempAlloc, block->byteCodeUpwardExposedUsed);
@@ -1589,7 +1550,6 @@ BackwardPass::ProcessLoop(BasicBlock * lastBlock)
 
         if (block->isLoopHeader && block->loop == lastBlock->loop)
         {
-            Assert(block->fieldHoistCandidates == nullptr);
             break;
         }
     }
@@ -2731,11 +2691,6 @@ BackwardPass::ProcessBlock(BasicBlock * block)
                 block->slotDeadStoreCandidates->ClearAll();
             }
 
-            if (this->DoFieldHoistCandidates())
-            {
-                this->ProcessFieldHoistKills(instr);
-            }
-
             TrackIntUsage(instr);
             TrackBitWiseOrNumberOp(instr);
 
@@ -2990,12 +2945,6 @@ BackwardPass::ProcessBlock(BasicBlock * block)
 
     EndIntOverflowDoesNotMatterRange();
 
-    if (this->DoFieldHoistCandidates() && !block->isDead && block->isLoopHeader)
-    {
-        Assert(block->loop->fieldHoistCandidates == nullptr);
-        block->loop->fieldHoistCandidates = block->fieldHoistCandidates->CopyNew(this->func->m_alloc);
-    }
-
     if (!this->IsPrePass() && !block->isDead && block->isLoopHeader)
     {
         // Copy the upward exposed use as the live on back edge regs
@@ -3300,12 +3249,6 @@ BackwardPass::DumpBlockData(BasicBlock * block)
         block->typesNeedingKnownObjectLayout->Dump();
     }
 
-    if (this->DoFieldHoistCandidates() && !block->isDead)
-    {
-        Output::Print(_u("            Exposed Field: "));
-        block->fieldHoistCandidates->Dump();
-    }
-
     if (block->byteCodeUpwardExposedUsed)
     {
         Output::Print(_u("   Byte Code Exposed Uses: "));
@@ -4336,15 +4279,6 @@ BackwardPass::ProcessSymUse(Sym * sym, bool isRegOpndUse, BOOLEAN isNonByteCodeU
             return true;
         }
 
-        Assert((block->fieldHoistCandidates != nullptr) == this->DoFieldHoistCandidates());
-
-        if (block->fieldHoistCandidates && this->currentInstr->TransfersSrcValue())
-        {
-            // If the instruction doesn't transfer the src value to dst, it will not be copyprop'd
-            // So we can't hoist those.
-            block->fieldHoistCandidates->Set(propertySym->m_id);
-        }
-
         if (this->DoDeadStoreSlots())
         {
             block->slotDeadStoreCandidates->Clear(propertySym->m_id);
@@ -6656,11 +6590,6 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
             return false;
         }
 
-        Assert((block->fieldHoistCandidates != nullptr) == this->DoFieldHoistCandidates());
-        if (block->fieldHoistCandidates)
-        {
-            block->fieldHoistCandidates->Clear(sym->m_id);
-        }
         PropertySym *propertySym = sym->AsPropertySym();
         if (this->DoDeadStoreSlots())
         {
@@ -6989,24 +6918,6 @@ BackwardPass::ClearBucketsOnFieldKill(IR::Instr *instr, HashTable<T> *table)
     }
 }
 
-void
-BackwardPass::ProcessFieldHoistKills(IR::Instr * instr)
-{
-    // The backward pass, we optimistically will not kill on a[] access
-    // So that the field hoist candidate will be more then what can be hoisted
-    // The root prepass will figure out the exact set of field that is hoisted
-    this->globOpt->ProcessFieldKills(instr, this->currentBlock->fieldHoistCandidates, false);
-
-    switch (instr->m_opcode)
-    {
-    case Js::OpCode::BrOnHasProperty:
-    case Js::OpCode::BrOnNoProperty:
-        // Should not hoist pass these instructions
-        this->currentBlock->fieldHoistCandidates->Clear(instr->GetSrc1()->AsSymOpnd()->m_sym->m_id);
-        break;
-    }
-}
-
 bool
 BackwardPass::TrackNoImplicitCallInlinees(IR::Instr *instr)
 {

+ 0 - 3
lib/Backend/BackwardPass.h

@@ -37,7 +37,6 @@ private:
     void ProcessTransfers(IR::Instr * instr);
     void ProcessFieldKills(IR::Instr * instr);
     template<typename T> void ClearBucketsOnFieldKill(IR::Instr *instr, HashTable<T> *table);
-    void ProcessFieldHoistKills(IR::Instr * instr);
     bool ProcessBailOutInfo(IR::Instr * instr);
     void ProcessBailOutInfo(IR::Instr * instr, BailOutInfo * bailOutInfo);
     void ProcessPendingPreOpBailOutInfo(IR::Instr *const currentInstr);
@@ -107,8 +106,6 @@ private:
 
     bool DoByteCodeUpwardExposedUsed() const;
     void DoSetDead(IR::Opnd * opnd, bool isDead) const;
-    bool DoFieldHoistCandidates() const;
-    bool DoFieldHoistCandidates(Loop * loop) const;
     bool DoMarkTempObjects() const;
     bool DoMarkTempNumbers() const;
     bool DoMarkTempNumbersOnTempObjects() const;

+ 0 - 13
lib/Backend/FlowGraph.cpp

@@ -3630,13 +3630,6 @@ Loop::CanDoFieldCopyProp()
     return GlobOpt::ImplicitCallFlagsAllowOpts(this);
 }
 
-bool
-Loop::CanDoFieldHoist()
-{
-    // We can do field hoist wherever we can do copy prop
-    return CanDoFieldCopyProp();
-}
-
 bool
 Loop::CanHoistInvariants() const
 {
@@ -4522,14 +4515,8 @@ BasicBlock::MergePredBlocksValueMaps(GlobOpt* globOpt)
     // Consider: We can recreate values for hoisted field so it can copy prop out of the loop
     if (blockData.symToValueMap == nullptr)
     {
-        Assert(blockData.hoistableFields == nullptr);
         blockData.InitBlockData(globOpt, globOpt->func);
     }
-    else if (blockData.hoistableFields && this->globOptData.liveFields)
-    {
-        Assert(globOpt->TrackHoistableFields());
-        blockData.hoistableFields->And(this->globOptData.liveFields);
-    }
 
     if (!globOpt->DoObjTypeSpec())
     {

+ 0 - 11
lib/Backend/FlowGraph.h

@@ -383,7 +383,6 @@ public:
     BVSparse<JitArenaAllocator> *              upwardExposedUses;
     BVSparse<JitArenaAllocator> *              upwardExposedFields;
     BVSparse<JitArenaAllocator> *              typesNeedingKnownObjectLayout;
-    BVSparse<JitArenaAllocator> *              fieldHoistCandidates;
     BVSparse<JitArenaAllocator> *              slotDeadStoreCandidates;
     TempNumberTracker *                     tempNumberTracker;
     TempObjectTracker *                     tempObjectTracker;
@@ -455,7 +454,6 @@ private:
         isBreakCompensationBlockAtSource(false),
         isBreakCompensationBlockAtSink(false),
 #endif
-        fieldHoistCandidates(nullptr),
         dataUseCount(0),
         intOverflowDoesNotMatterRange(nullptr),
         func(func),
@@ -553,7 +551,6 @@ class Loop
 {
     friend FlowGraph;
 private:
-    typedef JsUtil::BaseDictionary<SymID, StackSym *, JitArenaAllocator, PowerOf2SizePolicy> FieldHoistSymMap;
     typedef JsUtil::BaseDictionary<PropertySym *, Value *, JitArenaAllocator> InitialValueFieldMap;
 
     Js::ImplicitCallFlags implicitCallFlags;
@@ -587,11 +584,6 @@ public:
 
     BailOutInfo *       bailOutInfo;
     IR::BailOutInstr *  toPrimitiveSideEffectCheck;
-    BVSparse<JitArenaAllocator> * fieldHoistCandidates;
-    BVSparse<JitArenaAllocator> * liveInFieldHoistCandidates;
-    BVSparse<JitArenaAllocator> * fieldHoistCandidateTypes;
-    SListBase<IR::Instr *> prepassFieldHoistInstrCandidates;
-    FieldHoistSymMap fieldHoistSymMap;
     IR::Instr *         endDisableImplicitCall;
     BVSparse<JitArenaAllocator> * hoistedFields;
     BVSparse<JitArenaAllocator> * hoistedFieldCopySyms;
@@ -727,8 +719,6 @@ public:
         forceFloat64SymsOnEntry(nullptr),
         symsDefInLoop(nullptr),
         symsAssignedToInLoop(nullptr),
-        fieldHoistCandidateTypes(nullptr),
-        fieldHoistSymMap(alloc),
         needImplicitCallBailoutChecksForJsArrayCheckHoist(false),
         inductionVariables(nullptr),
         dominatingLoopCountableBlock(nullptr),
@@ -755,7 +745,6 @@ public:
     void SetLoopFlags(Js::LoopFlags val) { loopFlags = val; }
     bool                CanHoistInvariants() const;
     bool                CanDoFieldCopyProp();
-    bool                CanDoFieldHoist();
     void                SetHasCall();
     IR::LabelInstr *    GetLoopTopInstr() const;
     void                SetLoopTopInstr(IR::LabelInstr * loopTop);

+ 67 - 136
lib/Backend/GlobOpt.cpp

@@ -474,29 +474,11 @@ GlobOpt::OptBlock(BasicBlock *block)
     this->intOverflowCurrentlyMattersInRange = true;
     this->intOverflowDoesNotMatterRange = this->currentBlock->intOverflowDoesNotMatterRange;
 
-    if (loop && DoFieldHoisting(loop))
+    if (!DoFieldCopyProp() && !DoFieldRefOpts())
     {
-        if (block->isLoopHeader)
-        {
-            if (!this->IsLoopPrePass())
-            {
-                this->PrepareFieldHoisting(loop);
-            }
-            else if (loop == this->rootLoopPrePass)
-            {
-                this->PreparePrepassFieldHoisting(loop);
-            }
-        }
+        this->KillAllFields(CurrentBlockData()->liveFields);
     }
-    else
-    {
-        Assert(!TrackHoistableFields() || !HasHoistableFields(CurrentBlockData()));
-        if (!DoFieldCopyProp() && !DoFieldRefOpts())
-        {
-            this->KillAllFields(CurrentBlockData()->liveFields);
-        }
-    }
-
+    
     this->tempAlloc->Reset();
 
     if(loop && block->isLoopHeader)
@@ -3034,15 +3016,7 @@ GlobOpt::CopyPropDstUses(IR::Opnd *opnd, IR::Instr *instr, Value *src1Val)
             Value *const objectValue = CurrentBlockData()->FindValue(originalPropertySym->m_stackSym);
             symOpnd->SetPropertyOwnerValueType(objectValue ? objectValue->GetValueInfo()->Type() : ValueType::Uninitialized);
 
-            this->FieldHoistOptDst(instr, originalPropertySym, src1Val);
-            PropertySym * sym = this->CopyPropPropertySymObj(symOpnd, instr);
-            if (sym != originalPropertySym && !this->IsLoopPrePass())
-            {
-                // Consider: This doesn't detect hoistability of a property sym after object pointer copy prop
-                // on loop prepass. But if it so happened that the property sym is hoisted, we might as well do so.
-                this->FieldHoistOptDst(instr, sym, src1Val);
-            }
-
+            this->CopyPropPropertySymObj(symOpnd, instr);
         }
     }
 }
@@ -3242,67 +3216,56 @@ GlobOpt::OptSrc(IR::Opnd *opnd, IR::Instr * *pInstr, Value **indirIndexValRef, I
         opnd->AsSymOpnd()->SetPropertyOwnerValueType(
             objectValue ? objectValue->GetValueInfo()->Type() : ValueType::Uninitialized);
 
-        if (!FieldHoistOptSrc(opnd->AsSymOpnd(), instr, originalPropertySym))
-        {
-            sym = this->CopyPropPropertySymObj(opnd->AsSymOpnd(), instr);
+        
+        sym = this->CopyPropPropertySymObj(opnd->AsSymOpnd(), instr);
 
-            // Consider: This doesn't detect hoistability of a property sym after object pointer copy prop
-            // on loop prepass. But if it so happened that the property sym is hoisted, we might as well do so.
-            if (originalPropertySym == sym || this->IsLoopPrePass() ||
-                !FieldHoistOptSrc(opnd->AsSymOpnd(), instr, sym->AsPropertySym()))
+        if (!DoFieldCopyProp())
+        {
+            if (opnd->AsSymOpnd()->IsPropertySymOpnd())
             {
-                if (!DoFieldCopyProp())
-                {
-                    if (opnd->AsSymOpnd()->IsPropertySymOpnd())
-                    {
-                        this->FinishOptPropOp(instr, opnd->AsPropertySymOpnd());
-                    }
-                    return nullptr;
-                }
-                switch (instr->m_opcode)
-                {
-                    // These need the symbolic reference to the field, don't copy prop the value of the field
-                case Js::OpCode::DeleteFld:
-                case Js::OpCode::DeleteRootFld:
-                case Js::OpCode::DeleteFldStrict:
-                case Js::OpCode::DeleteRootFldStrict:
-                case Js::OpCode::ScopedDeleteFld:
-                case Js::OpCode::ScopedDeleteFldStrict:
-                case Js::OpCode::LdMethodFromFlags:
-                case Js::OpCode::BrOnNoProperty:
-                case Js::OpCode::BrOnHasProperty:
-                case Js::OpCode::LdMethodFldPolyInlineMiss:
-                case Js::OpCode::StSlotChkUndecl:
-                    return nullptr;
-                };
+                this->FinishOptPropOp(instr, opnd->AsPropertySymOpnd());
+            }
+            return nullptr;
+        }
+        switch (instr->m_opcode)
+        {
+            // These need the symbolic reference to the field, don't copy prop the value of the field
+        case Js::OpCode::DeleteFld:
+        case Js::OpCode::DeleteRootFld:
+        case Js::OpCode::DeleteFldStrict:
+        case Js::OpCode::DeleteRootFldStrict:
+        case Js::OpCode::ScopedDeleteFld:
+        case Js::OpCode::ScopedDeleteFldStrict:
+        case Js::OpCode::LdMethodFromFlags:
+        case Js::OpCode::BrOnNoProperty:
+        case Js::OpCode::BrOnHasProperty:
+        case Js::OpCode::LdMethodFldPolyInlineMiss:
+        case Js::OpCode::StSlotChkUndecl:
+            return nullptr;
+        };
 
-                if (instr->CallsGetter())
-                {
-                    return nullptr;
-                }
+        if (instr->CallsGetter())
+        {
+            return nullptr;
+        }
 
-                if (this->IsLoopPrePass() && this->DoFieldPRE(this->rootLoopPrePass))
+        if (this->IsLoopPrePass() && this->DoFieldPRE(this->rootLoopPrePass))
+        {
+            if (!this->prePassLoop->allFieldsKilled && !this->prePassLoop->fieldKilled->Test(sym->m_id))
+            {
+                this->SetLoopFieldInitialValue(this->rootLoopPrePass, instr, sym->AsPropertySym(), originalPropertySym);
+            }
+            if (this->IsPREInstrCandidateLoad(instr->m_opcode))
+            {
+                // Foreach property sym, remember the first instruction that loads it.
+                // Can this be done in one call?
+                if (!this->prePassInstrMap->ContainsKey(sym->m_id))
                 {
-                    if (!this->prePassLoop->allFieldsKilled && !this->prePassLoop->fieldKilled->Test(sym->m_id))
-                    {
-                        this->SetLoopFieldInitialValue(this->rootLoopPrePass, instr, sym->AsPropertySym(), originalPropertySym);
-                    }
-                    if (this->IsPREInstrCandidateLoad(instr->m_opcode))
-                    {
-                        // Foreach property sym, remember the first instruction that loads it.
-                        // Can this be done in one call?
-                        if (!this->prePassInstrMap->ContainsKey(sym->m_id))
-                        {
-                            this->prePassInstrMap->AddNew(sym->m_id, instr);
-                        }
-                    }
+                    this->prePassInstrMap->AddNew(sym->m_id, instr);
                 }
-                break;
             }
         }
-
-        // We field hoisted, we can continue as a reg.
-        opnd = instr->GetSrc1();
+        break; 
     }
     case IR::OpndKindReg:
         // Clear the opnd's value type up-front, so that this code cannot accidentally use the value type set from a previous
@@ -3764,10 +3727,6 @@ GlobOpt::CopyProp(IR::Opnd *opnd, IR::Instr *instr, Value *val, IR::IndirOpnd *p
         return opnd;
     }
 
-    // We should have dealt with field hoist already
-    Assert(!instr->TransfersSrcValue() || !opndSym->IsPropertySym() ||
-        !this->IsHoistedPropertySym(opndSym->AsPropertySym()));
-
     StackSym *copySym = CurrentBlockData()->GetCopyPropSym(opndSym, val);
     if (copySym != nullptr)
     {
@@ -4665,7 +4624,7 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val)
                 }
                 else if(src1ValueInfo->IsUninitialized())
                 {
-                    if(IsLoopPrePass() && (!dst->IsRegOpnd() || !dst->AsRegOpnd()->m_sym->IsSingleDef() || DoFieldHoisting()))
+                    if(IsLoopPrePass() && (!dst->IsRegOpnd() || !dst->AsRegOpnd()->m_sym->IsSingleDef()))
                     {
                         dstVal = this->NewGenericValue(profiledValueType, dst);
                     }
@@ -4688,16 +4647,8 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val)
         {
             // We cannot transfer value if the field hasn't been copy prop'd because we don't generate
             // an implicit call bailout between those values if we don't have "live fields" unless, we are hoisting the field.
-            PropertySym *propertySym = instr->GetSrc1()->AsSymOpnd()->m_sym->AsPropertySym();
-            StackSym * fieldHoistSym;
-            Loop * loop = this->FindFieldHoistStackSym(this->currentBlock->loop, propertySym->m_id, &fieldHoistSym, instr);
             ValueInfo *dstValueInfo = (dstVal ? dstVal->GetValueInfo() : nullptr);
 
-            // Update symStore for field hoisting
-            if (loop != nullptr && (dstValueInfo != nullptr))
-            {
-                this->SetSymStoreDirect(dstValueInfo, fieldHoistSym);
-            }
             // Update symStore if it isn't a stackSym
             if (dstVal && (!dstValueInfo->GetSymStore() || !dstValueInfo->GetSymStore()->IsStackSym()))
             {
@@ -4749,13 +4700,7 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val)
             Assert(sym->IsPropertySym());
             SymID symId = sym->m_id;
             Assert(instr->m_opcode == Js::OpCode::StSlot || instr->m_opcode == Js::OpCode::StSlotChkUndecl || !CurrentBlockData()->liveFields->Test(symId));
-            if (IsHoistablePropertySym(symId))
-            {
-                // We have changed the value of a hoistable field, load afterwards shouldn't get hoisted,
-                // but we will still copy prop the pre-assign sym to it if we have a live value.
-                Assert((instr->m_opcode == Js::OpCode::StSlot || instr->m_opcode == Js::OpCode::StSlotChkUndecl) && CurrentBlockData()->liveFields->Test(symId));
-                CurrentBlockData()->hoistableFields->Clear(symId);
-            }
+
             CurrentBlockData()->liveFields->Set(symId);
             if (!this->IsLoopPrePass() && dst->GetIsDead())
             {
@@ -5591,11 +5536,6 @@ GlobOpt::ValueNumberTransferDst(IR::Instr *const instr, Value * src1Val)
 bool
 GlobOpt::IsSafeToTransferInPrePass(IR::Opnd *src, Value *srcValue)
 {
-    if (this->DoFieldHoisting())
-    {
-        return false;
-    }
-
     if (src->IsRegOpnd())
     {
         StackSym *srcSym = src->AsRegOpnd()->m_sym;
@@ -5652,33 +5592,30 @@ GlobOpt::ValueNumberTransferDstInPrepass(IR::Instr *const instr, Value *const sr
         }
     }
 
-    if (!this->DoFieldHoisting())
+    if (instr->GetDst()->IsRegOpnd())
     {
-        if (instr->GetDst()->IsRegOpnd())
-        {
-            StackSym *stackSym = instr->GetDst()->AsRegOpnd()->m_sym;
+        StackSym *stackSym = instr->GetDst()->AsRegOpnd()->m_sym;
 
-            if (stackSym->IsSingleDef() || this->IsLive(stackSym, this->prePassLoop->landingPad))
+        if (stackSym->IsSingleDef() || this->IsLive(stackSym, this->prePassLoop->landingPad))
+        {
+            IntConstantBounds src1IntConstantBounds;
+            if (src1ValueInfo->TryGetIntConstantBounds(&src1IntConstantBounds) &&
+                !(
+                    src1IntConstantBounds.LowerBound() == INT32_MIN &&
+                    src1IntConstantBounds.UpperBound() == INT32_MAX
+                    ))
             {
-                IntConstantBounds src1IntConstantBounds;
-                if (src1ValueInfo->TryGetIntConstantBounds(&src1IntConstantBounds) &&
-                    !(
-                        src1IntConstantBounds.LowerBound() == INT32_MIN &&
-                        src1IntConstantBounds.UpperBound() == INT32_MAX
-                        ))
-                {
-                    const ValueType valueType(
-                        GetPrepassValueTypeForDst(src1ValueInfo->Type(), instr, src1Val, nullptr, &isValueInfoPrecise));
-                    if (isValueInfoPrecise)
-                    {
-                        return src1Val;
-                    }
-                }
-                else
+                const ValueType valueType(
+                    GetPrepassValueTypeForDst(src1ValueInfo->Type(), instr, src1Val, nullptr, &isValueInfoPrecise));
+                if (isValueInfoPrecise)
                 {
                     return src1Val;
                 }
             }
+            else
+            {
+                return src1Val;
+            }
         }
     }
 #endif
@@ -15923,13 +15860,12 @@ GlobOpt::Trace(BasicBlock * block, bool before) const
     bool globOptTrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::GlobOptPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
     bool typeSpecTrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::TypeSpecPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
     bool floatTypeSpecTrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::FloatTypeSpecPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
-    bool fieldHoistTrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
-    bool fieldCopyPropTrace = fieldHoistTrace || Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldCopyPropPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
+    bool fieldCopyPropTrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldCopyPropPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
     bool objTypeSpecTrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::ObjTypeSpecPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
     bool valueTableTrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::ValueTablePhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
     bool fieldPRETrace = Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldPREPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId());
 
-    bool anyTrace = globOptTrace || typeSpecTrace || floatTypeSpecTrace || fieldCopyPropTrace || fieldHoistTrace || objTypeSpecTrace || valueTableTrace || fieldPRETrace;
+    bool anyTrace = globOptTrace || typeSpecTrace || floatTypeSpecTrace || fieldCopyPropTrace || objTypeSpecTrace || valueTableTrace || fieldPRETrace;
 
     if (!anyTrace)
     {
@@ -15999,11 +15935,6 @@ GlobOpt::Trace(BasicBlock * block, bool before) const
         Output::Print(_u("    Live field syms: "));
         block->globOptData.liveFields->Dump();
     }
-    if ((fieldHoistTrace || objTypeSpecTrace) && this->DoFieldHoisting(block->loop) && HasHoistableFields(block))
-    {
-        Output::Print(_u("    Hoistable field sym: "));
-        block->globOptData.hoistableFields->Dump();
-    }
     if (objTypeSpecTrace || valueTableTrace)
     {
         Output::Print(_u("    Value table:\n"));

+ 1 - 24
lib/Backend/GlobOpt.h

@@ -524,7 +524,6 @@ public:
 
     // GlobOptFields.cpp
     void                    ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> * bv, bool inGlobOpt);
-    static bool             DoFieldHoisting(Loop * loop);
 
     IR::ByteCodeUsesInstr * ConvertToByteCodeUses(IR::Instr * isntr);
     bool GetIsAsmJSFunc()const{ return isAsmJSFunc; };
@@ -556,7 +555,6 @@ private:
     bool                    CheckIfPropOpEmitsTypeCheck(IR::Instr *instr, IR::PropertySymOpnd *opnd);
     IR::PropertySymOpnd *   CreateOpndForTypeCheckOnly(IR::PropertySymOpnd* opnd, Func* func);
     bool                    FinishOptPropOp(IR::Instr *instr, IR::PropertySymOpnd *opnd, BasicBlock* block = nullptr, bool updateExistingValue = false, bool* emitsTypeCheckOut = nullptr, bool* changesTypeValueOut = nullptr);
-    void                    FinishOptHoistedPropOps(Loop * loop);
     IR::Instr *             SetTypeCheckBailOut(IR::Opnd *opnd, IR::Instr *instr, BailOutInfo *bailOutInfo);
     void                    OptArguments(IR::Instr *Instr);
     void                    TrackInstrsForScopeObjectRemoval(IR::Instr * instr);
@@ -895,7 +893,6 @@ private:
     bool                    DoFieldCopyProp(Loop * loop) const;
     bool                    DoFunctionFieldCopyProp() const;
 
-    bool                    DoFieldHoisting() const;
     bool                    DoObjTypeSpec() const;
     bool                    DoObjTypeSpec(Loop * loop) const;
     bool                    DoFieldRefOpts() const { return DoObjTypeSpec(); }
@@ -904,26 +901,8 @@ private:
     bool                    DoFieldPRE() const;
     bool                    DoFieldPRE(Loop *loop) const;
 
-    bool                    FieldHoistOptSrc(IR::Opnd *opnd, IR::Instr *instr, PropertySym * propertySym);
-    void                    FieldHoistOptDst(IR::Instr * instr, PropertySym * propertySym, Value * src1Val);
-
-    bool                    TrackHoistableFields() const;
-    void                    PreparePrepassFieldHoisting(Loop * loop);
-    void                    PrepareFieldHoisting(Loop * loop);
-    void                    CheckFieldHoistCandidate(IR::Instr * instr, PropertySym * sym);
-    Loop *                  FindFieldHoistStackSym(Loop * startLoop, SymID propertySymId, StackSym ** copySym, IR::Instr * instrToHoist = nullptr) const;
-    bool                    CopyPropHoistedFields(PropertySym * sym, IR::Opnd ** ppOpnd, IR::Instr * instr);
-    void                    HoistFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, Value * oldValue, Value * newValue);
-    void                    HoistNewFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, Value * oldValue, Value * newValue);
-    void                    GenerateHoistFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, StackSym * newStackSym, Value * oldValue, Value * newValue);
-    void                    HoistFieldLoadValue(Loop * loop, Value * newValue, SymID symId, Js::OpCode opcode, IR::Opnd * srcOpnd);
-    void                    ReloadFieldHoistStackSym(IR::Instr * instr, PropertySym * propertySym);
-    void                    CopyStoreFieldHoistStackSym(IR::Instr * storeFldInstr, PropertySym * sym, Value * src1Val);
     Value *                 CreateFieldSrcValue(PropertySym * sym, PropertySym * originalSym, IR::Opnd **ppOpnd, IR::Instr * instr);
 
-    static bool             HasHoistableFields(BasicBlock const * block);
-    static bool             HasHoistableFields(GlobOptBlockData const * globOptData);
-    bool                    IsHoistablePropertySym(SymID symId) const;
     bool                    NeedBailOnImplicitCallWithFieldOpts(Loop *loop, bool hasLiveFields) const;
     IR::Instr *             EnsureDisableImplicitCallRegion(Loop * loop);
     void                    UpdateObjPtrValueType(IR::Opnd * opnd, IR::Instr * instr);
@@ -933,9 +912,7 @@ private:
 
 #if DBG
     bool                    IsPropertySymId(SymID symId) const;
-    bool                    IsHoistedPropertySym(PropertySym * sym) const;
-    bool                    IsHoistedPropertySym(SymID symId, Loop * loop) const;
-
+    
     static void             AssertCanCopyPropOrCSEFieldLoad(IR::Instr * instr);
 #endif
 

+ 1 - 2
lib/Backend/GlobOptBailOut.cpp

@@ -1085,8 +1085,7 @@ bool
 GlobOpt::MayNeedBailOut(Loop * loop) const
 {
     Assert(this->IsLoopPrePass());
-    return loop->CanHoistInvariants() ||
-        this->DoFieldCopyProp(loop) || (this->DoFieldHoisting(loop) && !loop->fieldHoistCandidates->IsEmpty());
+    return loop->CanHoistInvariants() || this->DoFieldCopyProp(loop) ;
 }
 
 bool

+ 0 - 35
lib/Backend/GlobOptBlockData.cpp

@@ -18,7 +18,6 @@ GlobOptBlockData::NullOutBlockData(GlobOpt* globOpt, Func* func)
     this->liveInt32Syms = nullptr;
     this->liveLossyInt32Syms = nullptr;
     this->liveFloat64Syms = nullptr;
-    this->hoistableFields = nullptr;
     this->argObjSyms = nullptr;
     this->maybeTempObjectSyms = nullptr;
     this->canStoreTempObjectSyms = nullptr;
@@ -61,7 +60,6 @@ GlobOptBlockData::InitBlockData(GlobOpt* globOpt, Func* func)
     this->liveInt32Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
     this->liveLossyInt32Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
     this->liveFloat64Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
-    this->hoistableFields = nullptr;
     this->argObjSyms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
     this->maybeTempObjectSyms = nullptr;
     this->canStoreTempObjectSyms = nullptr;
@@ -105,10 +103,6 @@ GlobOptBlockData::ReuseBlockData(GlobOptBlockData *fromData)
     this->liveInt32Syms = fromData->liveInt32Syms;
     this->liveLossyInt32Syms = fromData->liveLossyInt32Syms;
     this->liveFloat64Syms = fromData->liveFloat64Syms;
-    if (this->globOpt->TrackHoistableFields())
-    {
-        this->hoistableFields = fromData->hoistableFields;
-    }
 
     if (this->globOpt->TrackArgumentsObject())
     {
@@ -156,7 +150,6 @@ GlobOptBlockData::CopyBlockData(GlobOptBlockData *fromData)
     this->liveInt32Syms = fromData->liveInt32Syms;
     this->liveLossyInt32Syms = fromData->liveLossyInt32Syms;
     this->liveFloat64Syms = fromData->liveFloat64Syms;
-    this->hoistableFields = fromData->hoistableFields;
     this->argObjSyms = fromData->argObjSyms;
     this->maybeTempObjectSyms = fromData->maybeTempObjectSyms;
     this->canStoreTempObjectSyms = fromData->canStoreTempObjectSyms;
@@ -197,10 +190,6 @@ GlobOptBlockData::DeleteBlockData()
     JitAdelete(alloc, this->liveInt32Syms);
     JitAdelete(alloc, this->liveLossyInt32Syms);
     JitAdelete(alloc, this->liveFloat64Syms);
-    if (this->hoistableFields)
-    {
-        JitAdelete(alloc, this->hoistableFields);
-    }
     if (this->argObjSyms)
     {
         JitAdelete(alloc, this->argObjSyms);
@@ -290,13 +279,6 @@ void GlobOptBlockData::CloneBlockData(BasicBlock *const toBlockContext, BasicBlo
 
     this->liveFloat64Syms = JitAnew(alloc, BVSparse<JitArenaAllocator>, alloc);
     this->liveFloat64Syms->Copy(fromData->liveFloat64Syms);
-    if (this->globOpt->TrackHoistableFields())
-    {
-        if (fromData->hoistableFields)
-        {
-            this->hoistableFields = fromData->hoistableFields->CopyNew(alloc);
-        }
-    }
 
     if (this->globOpt->TrackArgumentsObject() && fromData->argObjSyms)
     {
@@ -672,18 +654,6 @@ GlobOptBlockData::MergeBlockData(
         this->liveLossyInt32Syms->And(this->liveInt32Syms);
     }
 
-    if (this->globOpt->TrackHoistableFields() && this->globOpt->HasHoistableFields(fromData))
-    {
-        if (this->hoistableFields)
-        {
-            this->hoistableFields->Or(fromData->hoistableFields);
-        }
-        else
-        {
-            this->hoistableFields = fromData->hoistableFields->CopyNew(this->globOpt->alloc);
-        }
-    }
-
     if (this->globOpt->TrackArgumentsObject())
     {
         if (!this->argObjSyms->Equal(fromData->argObjSyms))
@@ -1323,7 +1293,6 @@ GlobOptBlockData::FindPropertyValue(SymID symId)
     Assert(this->globOpt->func->m_symTable->Find(symId)->IsPropertySym());
     if (!this->liveFields->Test(symId))
     {
-        Assert(!this->globOpt->IsHoistablePropertySym(symId));
         return nullptr;
     }
     return FindValueFromMapDirect(symId);
@@ -1721,10 +1690,6 @@ GlobOptBlockData::KillStateForGeneratorYield()
     this->liveInt32Syms->ClearAll();
     this->liveLossyInt32Syms->ClearAll();
     this->liveFloat64Syms->ClearAll();
-    if (this->hoistableFields)
-    {
-        this->hoistableFields->ClearAll();
-    }
     // Keep this->liveVarSyms as is
     // Keep this->argObjSyms as is
 

+ 0 - 2
lib/Backend/GlobOptBlockData.h

@@ -118,7 +118,6 @@ public:
         liveInt32Syms(nullptr),
         liveLossyInt32Syms(nullptr),
         liveFloat64Syms(nullptr),
-        hoistableFields(nullptr),
         argObjSyms(nullptr),
         maybeTempObjectSyms(nullptr),
         canStoreTempObjectSyms(nullptr),
@@ -158,7 +157,6 @@ public:
     // Conversely, a lossless int32 sym can be reused to avoid a lossy conversion.
     BVSparse<JitArenaAllocator> *           liveLossyInt32Syms;
     BVSparse<JitArenaAllocator> *           liveFloat64Syms;
-    BVSparse<JitArenaAllocator> *           hoistableFields;
     BVSparse<JitArenaAllocator> *           argObjSyms;
     BVSparse<JitArenaAllocator> *           maybeTempObjectSyms;
     BVSparse<JitArenaAllocator> *           canStoreTempObjectSyms;

+ 63 - 1533
lib/Backend/GlobOptFields.cpp

@@ -3,102 +3,6 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 #include "Backend.h"
-/*
-Field Hoisting
---------------
-The backward pass calculates field load values that are reachable from the loop top.
-It optimistically assumes that a[] doesn't kill any fields in the hopes that glob opt
-will have more information to not kill the field.
-
-During the forward pass the root prepass will assume that the field hoist candidate
-is live (set the livefields bitvector). (GlobOpt::PreparePrepassFieldHoisting)
-The "hoistable field" bitvector is used to keep track of whether the current instruction
-may have the initial field value at the loop top. If so, they are hoistable.
-Even when the value is only visible at the loop top from one path, there is benefit in
-hoisting the field.
-
-e.g 1. We can hoist the field in this case and get benefit:
-    while {
-        if  {
-            o.x =       <== kills field value
-        }
-        = o.x           <== only has the loop top field value on the "!if" path
-    }
-After hoisting the field:
-    s1 = o.x            <== hoisted field load
-    while {
-        if {
-            o.x =
-            s1 =        <== maintain the hoisted field value
-        }
-        = s1            <== avoided a field load
-    }
-
-When we identify a field load as hoistable, we will add the instruction to a list on the loop.
-
-After the prepass, we will determine the fields that we are going to hoist from the
-field candidates. (GlobOpt::PrepareFieldHoisting)
-
-If it is not live - even if we detect a hoistable load - it is not beneficial to hoist
-the load as we will have to insert a field load to compensate for the loop back edges.
-We will just rely on field copy prop to optimize in that case.
-
-e.g 2. Hoisting this require us to add a field load back at the end of the loop with no benefit.
-    while {
-        = o.x           <== hoistable field but isn't live on back edge
-        b.x =           <== kills o.x as o and b may be aliased
-    }
-
-If it is live on back edge then it is possible to hoist the field load.
-
-e.g 3. Although the field is killed, if the value is live on back edge we can still hoist it.
-    while {
-        = o.x
-        = o.x
-        b.x =
-        = o.x
-    }
-After hoisting the field, s1 is live for the whole loop
-    s1 = o.x
-    while {
-        = s1            <== eliminated one field load
-        = s1            <== copy prop
-        b.x =
-        s1 = o.x
-    }
-
-However, since our register allocator doesn't handle long lifetimes, copy prop may do a better job.
-We would only replace one field load - instead of two.
-(Currently we hoist in this case)
-
-e.g. 4. Live time of s1 is much shorter, which works better with our current register allocator.
-    while {
-        s1 = o.x
-        = s1            <== copy prop
-        b.x =
-        s1 = o.x
-    }
-
-May want to add heuristics to determine whether to hoist by looking at the number of field
-loads we can replace. See unittest\fieldopts\fieldhoist5.js for timing with various -off/-force
-of fieldhoist/fieldcopyprop. Currently, field hoist is better or the same as field copy prop
-except for kill_singleuse in the test where we can only eliminate one field load compared to copy prop
-If we ever improve the register allocator to do better, we might lift this restriction.
-
-In GlobOpt::PrepareFieldHoisting, we go through all the hoistable field loads that are live on the back edge.
-We create a preassigned symbol for the hoisted field and add it to the fieldHoistSymMap of the loop.
-If the value is live coming into the loop (via field copy prop, we will create the instruction to
-assign the value to the preassigned sym. (GlobOpt::HoistFieldLoadValue)
-If we don't know the value yet, we will create the load field instead. (GlobOpt::HoistFieldLoad)
-
-As we are processing instructions in the non-prepass, when we see a field load, if it is live already then we have
-a value in the preassigned sym and we can just replace the load. (GlobOpt::CopyPropHoistedFields)
-If it is not live, then we don't have the value of the field, so keep the field load, but also
-assign the loaded value to the preassigned sym. (GlobOpt::ReloadFieldHoistStackSym)
-
-If the instruction is a store of a hoisted field, then create an assignment of the value to the preassigned
-symbol to maintain a live field value. (GlobOpt::CopyStoreFieldHoistStackSym)
-*/
 
 bool
 GlobOpt::DoFieldCopyProp() const
@@ -140,12 +44,6 @@ GlobOpt::DoFieldCopyProp(Loop * loop) const
         return true;
     }
 
-    if (this->DoFieldHoisting(loop))
-    {
-        // Have to do field copy prop when we are doing field hoisting
-        return true;
-    }
-
     if (PHASE_OFF(Js::FieldCopyPropPhase, this->func))
     {
         return false;
@@ -154,46 +52,6 @@ GlobOpt::DoFieldCopyProp(Loop * loop) const
     return this->DoFieldOpts(loop);
 }
 
-bool
-GlobOpt::DoFieldHoisting(Loop *loop)
-{
-    if (loop == nullptr)
-    {
-        return false;
-    }
-
-    Func * func = loop->GetHeadBlock()->GetFirstInstr()->m_func->GetTopFunc();
-    if (PHASE_OFF(Js::CopyPropPhase, func))
-    {
-        // Can't do field hoisting without copy prop
-        return false;
-    }
-
-    if (PHASE_OFF(Js::FieldHoistPhase, func))
-    {
-        return false;
-    }
-
-    if (!PHASE_OFF(Js::FieldPREPhase, func))
-    {
-        return false;
-    }
-
-    if (PHASE_FORCE(Js::FieldHoistPhase, func))
-    {
-        // Force always turns on field hoisting
-        return true;
-    }
-
-    return loop->CanDoFieldHoist();
-}
-
-bool
-GlobOpt::DoFieldHoisting() const
-{
-    return this->DoFieldHoisting(this->currentBlock->loop);
-}
-
 bool
 GlobOpt::DoObjTypeSpec() const
 {
@@ -278,12 +136,6 @@ bool GlobOpt::HasMemOp(Loop *loop)
     );
 }
 
-bool
-GlobOpt::TrackHoistableFields() const
-{
-    return this->IsLoopPrePass() && this->currentBlock->loop == this->prePassLoop;
-}
-
 void
 GlobOpt::KillLiveFields(StackSym * stackSym, BVSparse<JitArenaAllocator> * bv)
 {
@@ -597,1428 +449,106 @@ GlobOpt::ProcessFieldKills(IR::Instr * instr)
     }
 
     ProcessFieldKills(instr, this->currentBlock->globOptData.liveFields, true);
-    if (this->currentBlock->globOptData.hoistableFields)
-    {
-        Assert(this->TrackHoistableFields());
-
-        // Fields that are killed are no longer hoistable.
-        this->currentBlock->globOptData.hoistableFields->And(this->currentBlock->globOptData.liveFields);
-    }
 }
 
-void
-GlobOpt::PreparePrepassFieldHoisting(Loop * loop)
+Value *
+GlobOpt::CreateFieldSrcValue(PropertySym * sym, PropertySym * originalSym, IR::Opnd ** ppOpnd, IR::Instr * instr)
 {
-    BVSparse<JitArenaAllocator> * fieldHoistCandidates = loop->fieldHoistCandidates;
-
-#if DBG_DUMP
-    if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-    {
-        Output::Print(_u("\nFieldHoist: Start Loop: "));
-        loop->GetHeadBlock()->DumpHeader();
-        Output::Print(_u("FieldHoist: Backward candidates          : "));
-        fieldHoistCandidates->Dump();
-    }
-#endif
-#if ENABLE_DEBUG_CONFIG_OPTIONS
-    if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-    {
-        char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
-        Output::Print(_u("FieldHoist: START LOOP function %s (%s)\n"), this->func->GetJITFunctionBody()->GetDisplayName(), this->func->GetDebugNumberSet(debugStringBuffer));
-    }
-#endif
-
-    loop->fieldHoistCandidateTypes = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
-
-    if (fieldHoistCandidates->IsEmpty())
-    {
-        return;
-    }
+#if DBG
+    // If the opcode going to kill all field values immediate anyway, we shouldn't be giving it a value
+    Assert(!instr->UsesAllFields());
 
-    BasicBlock * landingPad = loop->landingPad;
+    AssertCanCopyPropOrCSEFieldLoad(instr);
 
-    // If it is live, the field doesn't need to be hoisted
-    Assert(loop->liveInFieldHoistCandidates == nullptr);
-    BVSparse<JitArenaAllocator> * liveInFieldHoistCandidates = fieldHoistCandidates->AndNew(landingPad->globOptData.liveFields);
-    loop->liveInFieldHoistCandidates = liveInFieldHoistCandidates;
+    Assert(instr->GetSrc1() == *ppOpnd);
+#endif
 
-    if (!liveInFieldHoistCandidates->IsEmpty())
+    // Only give a value to fields if we are doing field copy prop.
+    // Consider: We should always copy prop local slots, but the only use right now is LdSlot from jit loop body.
+    // This should have one onus load, and thus no need for copy prop of field itself.  We may want to support
+    // copy prop LdSlot if there are other uses of local slots
+    if (!this->DoFieldCopyProp())
     {
-        // Assume the live fields don't need to hoist for now
-        fieldHoistCandidates->Minus(liveInFieldHoistCandidates);
-
-        // If it was hoisted in an outer loop, and the value is live coming in, we don't need to hoist it again
-        Loop * currentLoop = loop->parent;
-        while (currentLoop != nullptr && this->DoFieldHoisting(currentLoop))
-        {
-            if (currentLoop->hoistedFields)
-            {
-                liveInFieldHoistCandidates->Minus(currentLoop->hoistedFields);
-            }
-            currentLoop = currentLoop->parent;
-        }
-
-        FOREACH_BITSET_IN_SPARSEBV(index, liveInFieldHoistCandidates)
-        {
-            if (landingPad->globOptData.FindValueFromMapDirect((SymID)index) == nullptr)
-            {
-                // Create initial values if we don't have one already for live fields
-                Value * newValue = this->NewGenericValue(ValueType::Uninitialized);
-                Value * oldValue = CopyValue(newValue, newValue->GetValueNumber());
-                Sym *sym = this->func->m_symTable->Find(index);
-                landingPad->globOptData.SetValue(oldValue, sym);
-                this->currentBlock->globOptData.SetValue(newValue, sym);
-            }
-        }
-        NEXT_BITSET_IN_SPARSEBV;
+        return nullptr;
     }
 
-    // Assume that the candidates are hoisted on prepass
-    landingPad->globOptData.liveFields->Or(fieldHoistCandidates);
-    this->currentBlock->globOptData.liveFields->Or(fieldHoistCandidates);
+    BOOL wasLive = this->currentBlock->globOptData.liveFields->TestAndSet(sym->m_id);
 
-    Loop * parentLoop = loop->parent;
-    FOREACH_BITSET_IN_SPARSEBV(index, fieldHoistCandidates)
+    if (sym != originalSym)
     {
-        // Create initial values
-        Value * newValue = this->NewGenericValue(ValueType::Uninitialized);
-        Value * oldValue = CopyValue(newValue, newValue->GetValueNumber());
-        Sym *sym = this->func->m_symTable->Find(index);
-        landingPad->globOptData.SetValue(oldValue, sym);
-        this->currentBlock->globOptData.SetValue(newValue, sym);
-
-        StackSym* objectSym = sym->AsPropertySym()->m_stackSym;
-        if (objectSym->HasObjectTypeSym())
-        {
-            StackSym* typeSym = objectSym->GetObjectTypeSym();
-
-            // If the type isn't live into the loop, let's keep track of it, so we can add it to
-            // live fields on pre-pass, verify if it is invariant through the loop, and if so produce it
-            // into the loop on the real pass.
-            if (!loop->landingPad->globOptData.liveFields->Test(typeSym->m_id))
-            {
-                Assert(!this->currentBlock->globOptData.liveFields->Test(typeSym->m_id));
-                loop->fieldHoistCandidateTypes->Set(typeSym->m_id);
-
-                // Set object type live on prepass so we can track if it got killed in the loop. (see FinishOptHoistedPropOps)
-                JsTypeValueInfo* typeValueInfo = JsTypeValueInfo::New(this->alloc, nullptr, nullptr);
-                typeValueInfo->SetIsShared();
-                this->SetSymStoreDirect(typeValueInfo, typeSym);
-
-                ValueNumber typeValueNumber = this->NewValueNumber();
-                Value* landingPadTypeValue = NewValue(typeValueNumber, typeValueInfo);
-                Value* headerTypeValue = NewValue(typeValueNumber, typeValueInfo);
-
-                SetObjectTypeFromTypeSym(typeSym, landingPadTypeValue, landingPad);
-                SetObjectTypeFromTypeSym(typeSym, headerTypeValue, this->currentBlock);
-            }
-        }
-
-        // If the sym holding the hoisted value is used as an instance pointer in the outer loop,
-        // its type may appear to be live in the inner loop. But the instance itself is being killed
-        // here, so make sure the type is killed as well.
-        if (parentLoop != nullptr)
-        {
-            StackSym * copySym;
-            Loop * hoistedLoop = FindFieldHoistStackSym(parentLoop, index, &copySym, nullptr);
-            if (hoistedLoop != nullptr)
-            {
-                this->KillObjectType(copySym);
-            }
-        }
+        this->currentBlock->globOptData.liveFields->TestAndSet(originalSym->m_id);
     }
-    NEXT_BITSET_IN_SPARSEBV;
-
-    Assert(this->TrackHoistableFields());
 
-    // Initialize the bit vector to keep track of whether the hoisted value will reach a field load
-    // to determine whether it should be hoisted.
-    if (this->currentBlock->globOptData.hoistableFields)
-    {
-        this->currentBlock->globOptData.hoistableFields->Copy(fieldHoistCandidates);
-    }
-    else
+    if (!wasLive)
     {
-        this->currentBlock->globOptData.hoistableFields = fieldHoistCandidates->CopyNew(this->alloc);
-        this->currentBlock->globOptData.hoistableFields = this->currentBlock->globOptData.hoistableFields;
+        // We don't clear the value when we kill the field.
+        // Clear it to make sure we don't use the old value.
+        this->currentBlock->globOptData.ClearSymValue(sym);
+        this->currentBlock->globOptData.ClearSymValue(originalSym);
     }
-    this->currentBlock->globOptData.hoistableFields->Or(liveInFieldHoistCandidates);
 
-#if DBG_DUMP
-    if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-    {
-        Output::Print(_u("FieldHoist: Prepass candidates (not live): "));
-        fieldHoistCandidates->Dump();
-        Output::Print(_u("FieldHoist: Prepass candidates (live)    : "));
-        liveInFieldHoistCandidates->Dump();
-    }
-#endif
+    Assert((*ppOpnd)->AsSymOpnd()->m_sym == sym || this->IsLoopPrePass());
+    
+    // We don't use the sym store to do copy prop on hoisted fields, but create a value
+    // in case it can be copy prop out of the loop.
+    return this->NewGenericValue(ValueType::Uninitialized, *ppOpnd);
 }
 
-void
-GlobOpt::PrepareFieldHoisting(Loop * loop)
+bool
+GlobOpt::NeedBailOnImplicitCallWithFieldOpts(Loop *loop, bool hasLiveFields) const
 {
-    Assert(!this->IsLoopPrePass());
-
-    if (loop->parent != nullptr)
-    {
-        loop->hasHoistedFields = loop->parent->hasHoistedFields;
-    }
-
-    BVSparse<JitArenaAllocator> * fieldHoistCandidates = loop->fieldHoistCandidates;
-    BVSparse<JitArenaAllocator> * liveInFieldHoistCandidates = loop->liveInFieldHoistCandidates;
-    if (fieldHoistCandidates->IsEmpty() && (!liveInFieldHoistCandidates || liveInFieldHoistCandidates->IsEmpty()))
+    if (!(((this->DoFieldRefOpts(loop) ||
+            this->DoFieldCopyProp(loop)) &&
+           hasLiveFields)))
     {
-        if (loop->hasHoistedFields)
-        {
-            loop->hoistedFieldCopySyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
-
-            AnalysisAssert(loop->parent && loop->parent->hasHoistedFields);
-            loop->hoistedFieldCopySyms->Copy(loop->parent->hoistedFieldCopySyms);
-            loop->regAlloc.liveOnBackEdgeSyms->Or(loop->hoistedFieldCopySyms);
-        }
-
-        return;
+        return false;
     }
 
-    BasicBlock * landingPad = loop->landingPad;
-    Assert(landingPad->globOptData.hoistableFields == nullptr);
-    Assert(this->currentBlock->globOptData.hoistableFields == nullptr);
-
-    BVSparse<JitArenaAllocator>* fieldHoistCandidateTypes = loop->fieldHoistCandidateTypes;
-
-    // Remove the live fields that are added during prepass
-    landingPad->globOptData.liveFields->Minus(fieldHoistCandidates);
-    landingPad->globOptData.liveFields->Minus(fieldHoistCandidateTypes);
-
-    // After prepass, if the field is not loaded on the back edge then we shouldn't hoist it
-    fieldHoistCandidates->And(this->currentBlock->globOptData.liveFields);
-    liveInFieldHoistCandidates->And(this->currentBlock->globOptData.liveFields);
-    fieldHoistCandidateTypes->And(this->currentBlock->globOptData.liveFields);
-
-    // Remove the live fields that were added during prepass
-    this->currentBlock->globOptData.liveFields->Minus(fieldHoistCandidates);
-    this->currentBlock->globOptData.liveFields->Minus(fieldHoistCandidateTypes);
-
-    loop->hoistedFields = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
-    loop->hoistedFieldCopySyms = JitAnew(this->alloc, BVSparse<JitArenaAllocator>, this->alloc);
+    return true;
+}
 
-    if (loop->parent && loop->parent->hasHoistedFields)
+IR::Instr *
+GlobOpt::EnsureDisableImplicitCallRegion(Loop * loop)
+{
+    Assert(loop->bailOutInfo != nullptr);
+    IR::Instr * endDisableImplicitCall = loop->endDisableImplicitCall;
+    if (endDisableImplicitCall)
     {
-        loop->hoistedFieldCopySyms->Copy(loop->parent->hoistedFieldCopySyms);
+        return endDisableImplicitCall;
     }
 
-    Func * loopTopFunc = loop->GetFunc();
-
-    // We built the list in reverse order, i.e., by prepending to it. Reverse it now so
-    // the hoisted instr's can be inserted in the correct order.
-    loop->prepassFieldHoistInstrCandidates.Reverse();
-
-    // Hoist the field load
-    FOREACH_SLISTBASE_ENTRY(IR::Instr *, instr, &loop->prepassFieldHoistInstrCandidates)
-    {
-        // We should have removed all fields that are hoisted in outer loops already.
-#if DBG
-        AssertCanCopyPropOrCSEFieldLoad(instr);
-#endif
-        PropertySym * propertySym = instr->GetSrc1()->AsSymOpnd()->m_sym->AsPropertySym();
-        SymID symId = propertySym->m_id;
-
-        if (loop->fieldHoistSymMap.ContainsKey(symId))
-        {
-            // The field is already hoisted
-#if DBG
-            StackSym * hoistedCopySym;
-            Assert(loop == FindFieldHoistStackSym(loop, symId, &hoistedCopySym, instr));
-#endif
-            continue;
-        }
-
-        Assert(landingPad->globOptData.IsLive(propertySym->m_stackSym));
-
-        if (fieldHoistCandidates->Test(symId))
-        {
-            // Hoist non-live field in
-            Value * oldValue = landingPad->globOptData.FindValueFromMapDirect(symId);
-            Value * newValue = this->currentBlock->globOptData.FindValueFromMapDirect(symId);
-            HoistFieldLoad(propertySym, loop, instr, oldValue, newValue);
-            continue;
-        }
-
-        if (!liveInFieldHoistCandidates->Test(symId))
-        {
-            // Not live in back edge; don't hoist field
-            Assert(!this->currentBlock->globOptData.liveFields->Test(symId));
-            continue;
-        }
-
-        Assert(landingPad->globOptData.liveFields->Test(symId));
-        Assert(this->currentBlock->globOptData.liveFields->Test(symId));
-
-        // If the value is live in, we shouldn't have a hoisted symbol already
-        Assert(!this->IsHoistedPropertySym(symId, loop->parent));
-        Value * oldValue = landingPad->globOptData.FindPropertyValue(symId);
-        AssertMsg(oldValue != nullptr, "We should have created an initial value for the field");
-        ValueInfo *oldValueInfo = oldValue->GetValueInfo();
-
-        Value * newValue = this->currentBlock->globOptData.FindPropertyValue(symId);
-
-        // The value of the loop isn't invariant, we need to create a value to hold the field through the loop
-
-        int32 oldIntConstantValue;
-        if (oldValueInfo->TryGetIntConstantValue(&oldIntConstantValue))
-        {
-            // Generate the constant load
-            IR::IntConstOpnd * intConstOpnd = IR::IntConstOpnd::New(oldIntConstantValue, TyInt32, loopTopFunc);
-            this->HoistFieldLoadValue(loop, newValue, symId, Js::OpCode::LdC_A_I4, intConstOpnd);
-        }
-        else if (oldValueInfo->IsFloatConstant())
-        {
-            // Generate the constant load
-            this->HoistFieldLoadValue(loop, newValue, symId,
-                Js::OpCode::LdC_A_R8, IR::FloatConstOpnd::New(oldValueInfo->AsFloatConstant()->FloatValue(), TyFloat64, loopTopFunc));
-        }
-        else
-        {
-            // This should be looking at the landingPad's value
-            Sym * copySym = landingPad->globOptData.GetCopyPropSym(nullptr, oldValue);
-
-            if (copySym != nullptr)
-            {
-                if (newValue && oldValue->GetValueNumber() == newValue->GetValueNumber())
-                {
-                    // The value of the field is invariant through the loop.
-                    // Copy prop can deal with this so we don't need to do anything.
-                    continue;
-                }
-
-                StackSym * copyStackSym = copySym->AsStackSym();
+    IR::Instr * bailOutTarget = EnsureBailTarget(loop);
 
-                // Transfer from an old copy prop value
-                IR::RegOpnd * srcOpnd = IR::RegOpnd::New(copyStackSym, TyVar, loopTopFunc);
-                srcOpnd->SetIsJITOptimizedReg(true);
-                this->HoistFieldLoadValue(loop, newValue, symId, Js::OpCode::Ld_A, srcOpnd);
-            }
-            else
-            {
-                // We don't have a copy sym, even though the field value is live, we can't copy prop.
-                // Generate the field load instead.
-#if DBG
-                landingPad->globOptData.liveFields->Clear(symId);
-                this->currentBlock->globOptData.liveFields->Clear(symId);
-                liveInFieldHoistCandidates->Clear(symId);
-                fieldHoistCandidates->Set(symId);
-#endif
-                HoistNewFieldLoad(propertySym, loop, instr, oldValue,  newValue);
-            }
-        }
-    }
-    NEXT_SLISTBASE_ENTRY;
+    Func * bailOutFunc = loop->GetFunc();
+    Assert(loop->bailOutInfo->bailOutFunc == bailOutFunc);
 
-    this->FinishOptHoistedPropOps(loop);
+    IR::MemRefOpnd * disableImplicitCallAddress = IR::MemRefOpnd::New(this->func->GetThreadContextInfo()->GetDisableImplicitFlagsAddr(), TyInt8, bailOutFunc);
+    IR::IntConstOpnd * disableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitCallAndExceptionFlag, TyInt8, bailOutFunc, true);
+    IR::IntConstOpnd * enableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitNoFlag, TyInt8, bailOutFunc, true);
 
-    JitAdelete(this->alloc, loop->fieldHoistCandidateTypes);
-    fieldHoistCandidateTypes = nullptr;
-    loop->fieldHoistCandidateTypes = nullptr;
+    IR::Opnd * implicitCallFlags = Lowerer::GetImplicitCallFlagsOpnd(bailOutFunc);
+    IR::IntConstOpnd * noImplicitCall = IR::IntConstOpnd::New(Js::ImplicitCall_None, TyInt8, bailOutFunc, true);
 
-    loop->regAlloc.liveOnBackEdgeSyms->Or(loop->hoistedFieldCopySyms);
+    // Consider: if we are already doing implicit call in the outer loop, we don't need to clear the implicit call bit again
+    IR::Instr * clearImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, implicitCallFlags, noImplicitCall, bailOutFunc);
+    bailOutTarget->InsertBefore(clearImplicitCall);
 
-#if DBG || DBG_DUMP
-    if (loop->hoistedFields->IsEmpty())
-    {
-        Assert(loop->fieldHoistSymMap.Count() == 0);
-        liveInFieldHoistCandidates->ClearAll();
-    }
-    else
-    {
-        // Update liveInFieldHoistCandidates for assert in FindFieldHoistStackSym
-        liveInFieldHoistCandidates->And(loop->hoistedFields);
+    IR::Instr * disableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, disableImplicitCallAndExceptionValue, bailOutFunc);
+    bailOutTarget->InsertBefore(disableImplicitCall);
 
-        if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-        {
-            Output::Print(_u("FieldHoist: All candidates: "));
-            loop->hoistedFields->Dump();
-            Output::Print(_u("FieldHoist: Live in candidates: "));
-            liveInFieldHoistCandidates->Dump();
-        }
-    }
-#else
-    JitAdelete(this->alloc, liveInFieldHoistCandidates);
-    loop->liveInFieldHoistCandidates = nullptr;
-#endif
+    endDisableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, enableImplicitCallAndExceptionValue, bailOutFunc);
+    bailOutTarget->InsertBefore(endDisableImplicitCall);
 
-    JitAdelete(this->alloc, fieldHoistCandidates);
-    loop->fieldHoistCandidates = nullptr;
-}
+    IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, IR::BailOutOnImplicitCalls, loop->bailOutInfo, loop->bailOutInfo->bailOutFunc);
+    bailOutInstr->SetSrc1(implicitCallFlags);
+    bailOutInstr->SetSrc2(noImplicitCall);
+    bailOutTarget->InsertBefore(bailOutInstr);
 
-void
-GlobOpt::CheckFieldHoistCandidate(IR::Instr * instr, PropertySym * sym)
-{
-    // See if this field load is hoistable.
-    // This load probably may have a store or kill before it.
-    // We will hoist it in another path. Just copy prop the value from the field store.
-    //
-    // For example:
-    // loop
-    // {
-    //      if ()
-    //      {
-    //          o.i =
-    //              = o.i   <= not hoistable (but can copy prop)
-    //      }
-    //      else
-    //      {
-    //          = o.i       <= hoistable
-    //      }
-    // }
-    if (this->currentBlock->globOptData.hoistableFields->TestAndClear(sym->m_id))
-    {
-        Assert(this->currentBlock->globOptData.liveFields->Test(sym->m_id));
-        // We're adding this instruction as a candidate for hoisting. If it gets hoisted, its jit-time inline
-        // cache will be used to generate the type check and bailout at the top of the loop. After we bail out,
-        // however, we may not go down the code path on which this instruction resides, and so the inline cache
-        // will not turn polymorphic. If we then re-jit, we would hoist the same instruction again, and get
-        // stuck in infinite bailout cycle. That's why we use BailOutRecord::polymorphicCacheIndex for hoisted
-        // field loads to force the profile info for the right inline cache into polymorphic state.
-        this->rootLoopPrePass->prepassFieldHoistInstrCandidates.Prepend(this->alloc, instr);
-#if DBG_DUMP
-        if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-        {
-            Output::Print(_u("FieldHoist: Prepass marked hoist load"));
-            Output::SkipToColumn(30);
-            Output::Print(_u(" : "));
-            instr->Dump();
-        }
-#endif
-    }
+    loop->endDisableImplicitCall = endDisableImplicitCall;
+    return endDisableImplicitCall;
 }
 
-void
-GlobOpt::FinishOptHoistedPropOps(Loop * loop)
-{
-    // Set up hoisted fields for object type specialization.
-    Assert(loop);
-
-    // This extra check for parent loop was added as a fix for Windows 8 Bug 480217.  The issue there might have affected
-    // the original redundant type elimination, but does not cause problems for object type spec.  With this check some
-    // operations which were candidates for object type spec in the backward pass (where we only checked the current loop),
-    // could unexpectedly not be candidates, anymore.  This led to problems in the lowerer.
-    // (Do this only if we're doing the optimization in the loop's parent, which is where we're inserting
-    // the hoisted instruction.)
-    //if (loop->parent && !DoFieldRefOpts(loop->parent))
-    //{
-    //    return;
-    //}
-
-    bool doFieldRefOpts = DoFieldRefOpts(loop);
-    bool forceFieldHoisting = PHASE_FORCE(Js::FieldHoistPhase, this->func);
-    bool doForcedTypeChecksOnly = !doFieldRefOpts && forceFieldHoisting;
-
-    if (!doFieldRefOpts && !forceFieldHoisting)
-    {
-        IR::Instr * instrEnd = loop->endDisableImplicitCall;
-        if (instrEnd == nullptr)
-        {
-            return;
-        }
-
-        FOREACH_INSTR_EDITING_IN_RANGE(instr, instrNext, loop->landingPad->GetFirstInstr(), instrEnd)
-        {
-            // LdMethodFromFlags must always have a type check and bailout.  If we hoisted it as a result of
-            // -force:fieldHoist, we will have to set the bailout here again, even if there are implicit calls
-            // in the loop (and DoFieldRefOpts returns false).  See Windows Blue Bugs 608503 and 610237.
-            if (instr->m_opcode == Js::OpCode::LdMethodFromFlags)
-            {
-                instr = SetTypeCheckBailOut(instr->GetSrc1(), instr, loop->bailOutInfo);
-            }
-        }
-        NEXT_INSTR_EDITING_IN_RANGE;
-
-        return;
-    }
-
-    // Walk the implicit-call-disabled region in the loop header, creating PropertySymOpnd's and
-    // tracking liveness of the type/slot-array syms.
-    IR::Instr * instrEnd = loop->endDisableImplicitCall;
-    if (instrEnd == nullptr)
-    {
-        return;
-    }
-    Assert(loop->bailOutInfo->bailOutInstr != nullptr);
-
-    // Consider (ObjTypeSpec): Do we really need all this extra tracking of live fields on back edges, so as to
-    // remove them from the live fields on the loop header?  We already do this in MergeBlockData called from
-    // MergePredBlocksValueMaps, which takes place just before we get here.
-
-    // Build the set of fields that are live on all back edges.
-    // Use this to limit the type symbols we make live into the loop. We made the types of the hoisted fields
-    // live in the prepass, so if they're not live on a back edge, that means some path through the loop
-    // kills them.
-    BVSparse<JitArenaAllocator> *bvBackEdge = nullptr;
-    FOREACH_PREDECESSOR_BLOCK(predBlock, loop->GetHeadBlock())
-    {
-        if (!loop->IsDescendentOrSelf(predBlock->loop))
-        {
-            // This is the edge that enters the loop - not interesting here.
-            continue;
-        }
-        if (!bvBackEdge)
-        {
-            bvBackEdge = predBlock->globOptData.liveFields;
-        }
-        else
-        {
-            bvBackEdge = bvBackEdge->AndNew(predBlock->globOptData.liveFields, this->alloc);
-        }
-    }
-    NEXT_PREDECESSOR_BLOCK;
-
-    if (!doForcedTypeChecksOnly)
-    {
-        FOREACH_INSTR_EDITING_IN_RANGE(instr, instrNext, loop->landingPad->GetFirstInstr(), instrEnd)
-        {
-            IR::Opnd *opnd = instr->GetSrc1();
-            if (opnd && opnd->IsSymOpnd() && opnd->AsSymOpnd()->IsPropertySymOpnd())
-            {
-                bool isHoistedTypeValue = false;
-                bool isTypeInvariant = false;
-                if (opnd->AsPropertySymOpnd()->HasObjectTypeSym())
-                {
-                    StackSym* typeSym = opnd->AsPropertySymOpnd()->GetObjectTypeSym();
-
-                    // We've cleared the live bits for types that are purely hoisted (not live into the loop),
-                    // so we can't use FindObjectTypeValue here.
-                    Value* landingPadValue = loop->landingPad->globOptData.FindValueFromMapDirect(typeSym->m_id);
-                    Value* headerValue = loop->GetHeadBlock()->globOptData.FindValueFromMapDirect(typeSym->m_id);
-                    isHoistedTypeValue = landingPadValue != nullptr && loop->fieldHoistCandidateTypes->Test(typeSym->m_id);
-                    isTypeInvariant = landingPadValue != nullptr && headerValue != nullptr && landingPadValue->GetValueNumber() == headerValue->GetValueNumber();
-                }
-
-                // Prepare the operand for object type specialization by creating a type sym for it, if not yet present
-                // and marking it as candidate for specialization.
-                PreparePropertySymOpndForTypeCheckSeq(opnd->AsPropertySymOpnd(), instr, loop);
-
-                // Let's update the existing type value, if possible, to retain the value number created in pre-pass.
-                bool changesTypeValue = false;
-                FinishOptPropOp(instr, opnd->AsPropertySymOpnd(), loop->landingPad, /* updateExistingValue = */ isHoistedTypeValue, nullptr, &changesTypeValue);
-                instr = SetTypeCheckBailOut(opnd, instr, loop->bailOutInfo);
-
-                // If we changed the type's value in the landing pad we want to reflect this change in the header block as well,
-                // but only if the type is invariant throughout the loop. Note that if the type was live into the loop and
-                // live on all back edges, but not invariant, it will already be live in the header, but its value will be blank,
-                // because we merge type values conservatively on loop back edges. (see MergeJsTypeValueInfo)
-
-                // Consider (ObjTypeSpec): There are corner cases where we copy prop an object pointer into the newly hoisted instruction,
-                // and that object doesn't have a type yet. We then create a type on the fly (see GenerateHoistFieldLoad and
-                // CopyPropPropertySymObj), and don't have a value for it in the landing pad. Thus we can't prove that the type is invariant
-                // throughout the loop, and so we won't produce a value for it into the loop. This could be addressed by creating
-                // a mapping of type syms from before to after object pointer copy prop.
-                if (changesTypeValue && isTypeInvariant)
-                {
-                    Assert(opnd->AsPropertySymOpnd()->HasObjectTypeSym());
-                    StackSym* typeSym = opnd->AsPropertySymOpnd()->GetObjectTypeSym();
-
-                    // If we changed the type value in the landing pad, we must have set it live there.
-                    Value* landingPadValue = loop->landingPad->globOptData.FindObjectTypeValue(typeSym);
-                    Assert(landingPadValue != nullptr && landingPadValue->GetValueInfo()->IsJsType());
-
-                    // But in the loop header we may have only a value with the live bit still cleared,
-                    // so we can't use FindObjectTypeValue here.
-                    Value* headerValue = loop->GetHeadBlock()->globOptData.FindValueFromMapDirect(typeSym->m_id);
-                    Assert(headerValue != nullptr && headerValue->GetValueInfo()->IsJsType());
-
-                    Assert(!isHoistedTypeValue || landingPadValue->GetValueNumber() == headerValue->GetValueNumber());
-                    JsTypeValueInfo* valueInfo = landingPadValue->GetValueInfo()->AsJsType();
-                    valueInfo->SetIsShared();
-                    headerValue->SetValueInfo(valueInfo);
-
-                    loop->GetHeadBlock()->globOptData.liveFields->Set(typeSym->m_id);
-                }
-
-#if DBG
-                if (opnd->AsPropertySymOpnd()->HasObjectTypeSym())
-                {
-                    StackSym* typeSym = opnd->AsPropertySymOpnd()->GetObjectTypeSym();
-                    Assert(!isHoistedTypeValue || isTypeInvariant || !loop->GetHeadBlock()->globOptData.liveFields->Test(typeSym->m_id));
-                }
-#endif
-            }
-        }
-        NEXT_INSTR_EDITING_IN_RANGE;
-    }
-    else
-    {
-        FOREACH_INSTR_EDITING_IN_RANGE(instr, instrNext, loop->landingPad->GetFirstInstr(), instrEnd)
-        {
-            // LdMethodFromFlags must always have a type check and bailout. If we hoisted it as a result of
-            // -force:fieldHoist, we will have to set the bailout here again, even if there are implicit calls
-            // in the loop.
-            if (instr->m_opcode == Js::OpCode::LdMethodFromFlags)
-            {
-                instr = SetTypeCheckBailOut(instr->GetSrc1(), instr, loop->bailOutInfo);
-            }
-        }
-        NEXT_INSTR_EDITING_IN_RANGE;
-    }
-
-    if (bvBackEdge)
-    {
-        // Take the fields not live on some back edge out of the set that's live into the loop.
-        this->currentBlock->globOptData.liveFields->And(bvBackEdge);
-    }
-}
-
-void
-GlobOpt::HoistFieldLoadValue(Loop * loop, Value * newValue, SymID symId, Js::OpCode opcode, IR::Opnd * srcOpnd)
-{
-    IR::Instr * insertInstr = this->EnsureDisableImplicitCallRegion(loop);
-
-    Assert(!this->IsLoopPrePass());
-    Assert(IsPropertySymId(symId));
-    Assert(!loop->fieldHoistCandidates->Test(symId));
-    Assert(loop->landingPad->globOptData.liveFields->Test(symId));
-    Assert(this->currentBlock->globOptData.liveFields->Test(symId));
-
-    Func * loopTopFunc = loop->GetFunc();
-
-    // Just transfer the copy prop sym to a new stack sym for the property.
-    // Consider: What happens if the outer loop already has a field hoist stack sym for this propertysym?
-    StackSym * newStackSym = StackSym::New(TyVar, loopTopFunc);
-
-    // This new stack sym may or may not be single def.
-    // Just make it not a single def so that we don't lose the value when it become non-single def.
-    newStackSym->m_isSingleDef = false;
-    IR::RegOpnd * newOpnd = IR::RegOpnd::New(newStackSym, TyVar, loopTopFunc);
-    IR::Instr * newInstr = IR::Instr::New(opcode, newOpnd, srcOpnd, loopTopFunc);
-
-    insertInstr->InsertBefore(newInstr);
-    loop->landingPad->globOptData.liveVarSyms->Set(newStackSym->m_id);
-    loop->varSymsOnEntry->Set(newStackSym->m_id);
-
-    // Update value in the current block
-    if (newValue == nullptr)
-    {
-        // Even though we don't use the symStore to copy prop the hoisted stack sym in the loop
-        // we might be able to propagate it out of the loop. Create a value just in case.
-        newValue = this->NewGenericValue(ValueType::Uninitialized, newStackSym);
-
-        // This should pass the sym directly.
-        Sym *sym = this->func->m_symTable->Find(symId);
-
-        this->currentBlock->globOptData.SetValue(newValue, sym);
-        Assert(newValue->GetValueInfo()->GetSymStore() == newStackSym);
-    }
-    else
-    {
-        this->currentBlock->globOptData.SetValue(newValue, newStackSym);
-        this->SetSymStoreDirect(newValue->GetValueInfo(), newStackSym);
-    }
-
-
-    this->currentBlock->globOptData.liveVarSyms->Set(newStackSym->m_id);
-    loop->fieldHoistSymMap.Add(symId, newStackSym);
-    loop->hoistedFieldCopySyms->Set(newStackSym->m_id);
-
-    loop->hasHoistedFields = true;
-    loop->hoistedFields->Set(symId);
-
-    if(newInstr->GetSrc1()->IsRegOpnd())
-    {
-        // Make sure the source sym is available as a var
-        const auto srcRegOpnd = newInstr->GetSrc1()->AsRegOpnd();
-        if(!loop->landingPad->globOptData.liveVarSyms->Test(srcRegOpnd->m_sym->m_id))
-        {
-            this->ToVar(newInstr, srcRegOpnd, loop->landingPad, nullptr, false);
-        }
-    }
-
-#if DBG_DUMP
-    if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-    {
-        Output::Print(_u("FieldHoist: Live value load "));
-        this->func->m_symTable->Find(symId)->Dump();
-        Output::SkipToColumn(30);
-        Output::Print(_u(" : "));
-        newInstr->Dump();
-    }
-#endif
-}
-
-bool
-GlobOpt::IsHoistablePropertySym(SymID symId) const
-{
-    return this->currentBlock->globOptData.hoistableFields && this->currentBlock->globOptData.hoistableFields->Test(symId);
-}
-
-bool
-GlobOpt::HasHoistableFields(BasicBlock const * basicBlock)
-{
-    return HasHoistableFields(&basicBlock->globOptData);
-}
-
-bool
-GlobOpt::HasHoistableFields(GlobOptBlockData const * globOptData)
-{
-    return globOptData->hoistableFields && !globOptData->hoistableFields->IsEmpty();
-}
-
-Loop *
-GlobOpt::FindFieldHoistStackSym(Loop * startLoop, SymID propertySymId, StackSym ** copySym, IR::Instr * instrToHoist) const
-{
-    Assert(IsPropertySymId(propertySymId));
-
-    if (instrToHoist && instrToHoist->m_opcode == Js::OpCode::LdMethodFromFlags)
-    {
-        return nullptr;
-    }
-
-    Loop * loop = startLoop;
-
-    while (loop && this->DoFieldHoisting(loop))
-    {
-        if (loop->fieldHoistSymMap.TryGetValue(propertySymId, copySym))
-        {
-            Assert(loop->hasHoistedFields);
-            Assert(loop->hoistedFields->Test(propertySymId));
-
-            if (this->IsLoopPrePass())
-            {
-                return loop;
-            }
-
-            BasicBlock * landingPad = loop->landingPad;
-#if DBG
-            BOOL liveInSym = FALSE;
-            liveInSym = loop->liveInFieldHoistCandidates->Test(propertySymId);
-
-            Assert(landingPad->globOptData.liveFields->Test(propertySymId));
-            Assert(landingPad->globOptData.liveVarSyms->Test((*copySym)->m_id));
-#endif
-
-            // This has been hoisted already.
-            // Verify the hoisted instruction.
-            bool found = false;
-            FOREACH_INSTR_BACKWARD_IN_BLOCK(instr, landingPad)
-            {
-                IR::Opnd * dstOpnd = instr->GetDst();
-                if (dstOpnd && dstOpnd->IsRegOpnd() && dstOpnd->AsRegOpnd()->m_sym == *copySym)
-                {
-                    found = true;
-#if DBG
-                    // We used to try to assert that the property sym on the instruction in the landing pad
-                    // matched the one on the instruction we're changing now. But we may have done object ptr
-                    // copy prop in the landing pad, so the assertion no longer holds.
-                    if (liveInSym)
-                    {
-                        Assert((instr->m_opcode == Js::OpCode::Ld_A && instr->GetSrc1()->IsRegOpnd())
-                            || (instr->m_opcode == Js::OpCode::LdC_A_I4 && instr->GetSrc1()->IsIntConstOpnd())
-                            || instr->m_opcode == Js::OpCode::LdC_A_R8 && instr->GetSrc1()->IsFloatConstOpnd());
-                    }
-                    else if (instrToHoist)
-                    {
-                        bool instrIsLdFldEquivalent = (instr->m_opcode == Js::OpCode::LdFld || instr->m_opcode == Js::OpCode::LdFldForCallApplyTarget);
-                        bool instrToHoistIsLdFldEquivalent = (instrToHoist->m_opcode == Js::OpCode::LdFld || instrToHoist->m_opcode == Js::OpCode::LdFldForCallApplyTarget);
-                        Assert(instr->m_opcode == instrToHoist->m_opcode ||
-                               instrIsLdFldEquivalent && instrToHoistIsLdFldEquivalent ||
-                               instr->m_opcode == Js::OpCode::LdMethodFld ||
-                               instr->m_opcode == Js::OpCode::LdRootMethodFld ||
-                               instr->m_opcode == Js::OpCode::ScopedLdMethodFld ||
-                               instrToHoist->m_opcode == Js::OpCode::LdMethodFld ||
-                               instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld ||
-                               instrToHoist->m_opcode == Js::OpCode::ScopedLdMethodFld ||
-                               (instrIsLdFldEquivalent && instrToHoist->m_opcode == Js::OpCode::LdRootFld) ||
-                               (instr->m_opcode == Js::OpCode::LdMethodFld && instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld) ||
-                               (instrToHoistIsLdFldEquivalent && instr->m_opcode == Js::OpCode::LdRootFld) ||
-                               (instrToHoist->m_opcode == Js::OpCode::LdMethodFld && instr->m_opcode == Js::OpCode::LdRootMethodFld));
-                    }
-#endif
-                    if (instrToHoist
-                        && (instrToHoist->m_opcode == Js::OpCode::LdMethodFld ||
-                            instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld ||
-                            instrToHoist->m_opcode == Js::OpCode::ScopedLdMethodFld)
-                        && instr->m_opcode != Js::OpCode::Ld_A
-                        && instr->m_opcode != Js::OpCode::LdC_A_I4
-                        && instr->m_opcode != Js::OpCode::LdC_A_R8)
-                    {
-                        // We may have property sym referred to by both Ld[Root]Fld and Ld[Root]MethodFld
-                        // in the loop. If this happens, make sure the hoisted instruction is Ld[Root]MethodFld
-                        // so we get the prototype inline cache fast path we want.
-                        // Other differences such as error messages and HostDispatch behavior shouldn't
-                        // matter, because we'll bail out in those cases.
-                        Assert(instr->GetSrc1()->IsSymOpnd() && instr->GetSrc1()->AsSymOpnd()->m_sym->IsPropertySym());
-                        instr->m_opcode = instrToHoist->m_opcode;
-                    }
-                    else if (instrToHoist &&
-                           ((instr->m_opcode == Js::OpCode::LdFld && instrToHoist->m_opcode == Js::OpCode::LdRootFld)
-                            || (instr->m_opcode == Js::OpCode::LdMethodFld && instrToHoist->m_opcode == Js::OpCode::LdRootMethodFld)))
-                    {
-                        instr->m_opcode = instrToHoist->m_opcode;
-                    }
-                    break;
-                }
-            }
-            NEXT_INSTR_BACKWARD_IN_BLOCK;
-            Assert(found);
-
-            return loop;
-        }
-        Assert(!loop->hoistedFields || !loop->hoistedFields->Test(propertySymId));
-        loop = loop->parent;
-    }
-    return nullptr;
-}
-
-void
-GlobOpt::HoistFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, Value * oldValue, Value * newValue)
-{
-    Loop * parentLoop = loop->parent;
-    if (parentLoop != nullptr)
-    {
-        StackSym * copySym;
-        Loop * hoistedLoop = FindFieldHoistStackSym(parentLoop, sym->m_id, &copySym, instr);
-        if (hoistedLoop != nullptr)
-        {
-            // Use an outer loop pre-assigned stack sym if it is already hoisted there
-            Assert(hoistedLoop != loop);
-            GenerateHoistFieldLoad(sym, loop, instr, copySym, oldValue, newValue);
-            return;
-        }
-    }
-
-    HoistNewFieldLoad(sym, loop, instr, oldValue, newValue);
-}
-
-void
-GlobOpt::HoistNewFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, Value * oldValue, Value * newValue)
-{
-    Assert(!this->IsHoistedPropertySym(sym->m_id, loop));
-
-    StackSym * newStackSym = StackSym::New(TyVar, this->func);
-
-    // This new stack sym may or may not be single def.
-    // Just make it not a single def so that we don't lose the value when it become non-single def.
-    newStackSym->m_isSingleDef = false;
-
-    GenerateHoistFieldLoad(sym, loop, instr, newStackSym, oldValue, newValue);
-}
-
-void
-GlobOpt::GenerateHoistFieldLoad(PropertySym * sym, Loop * loop, IR::Instr * instr, StackSym * newStackSym, Value * oldValue, Value * newValue)
-{
-    Assert(loop != nullptr);
-
-    SymID symId = sym->m_id;
-    BasicBlock * landingPad = loop->landingPad;
-
-#if DBG
-    Assert(!this->IsLoopPrePass());
-    AssertCanCopyPropOrCSEFieldLoad(instr);
-    Assert(instr->GetSrc1()->AsSymOpnd()->m_sym == sym);
-
-    Assert(loop->fieldHoistCandidates->Test(symId));
-    Assert(!landingPad->globOptData.liveFields->Test(sym->m_id));
-    Assert(!this->currentBlock->globOptData.liveFields->Test(sym->m_id));
-    Assert(!loop->fieldHoistSymMap.ContainsKey(symId));
-#endif
-
-    loop->fieldHoistSymMap.Add(symId, newStackSym);
-    loop->hoistedFieldCopySyms->Set(newStackSym->m_id);
-
-    Func * loopTopFunc = loop->GetFunc();
-
-    // Generate the hoisted field load
-    IR::RegOpnd * newDst = IR::RegOpnd::New(newStackSym, TyVar, loopTopFunc);
-    IR::SymOpnd * newSrc;
-
-    if (instr->GetSrc1() && instr->GetSrc1()->IsSymOpnd() && instr->GetSrc1()->AsSymOpnd()->IsPropertySymOpnd())
-    {
-        IR::PropertySymOpnd * srcPropertySymOpnd = instr->GetSrc1()->AsPropertySymOpnd();
-        AssertMsg(!srcPropertySymOpnd->IsTypeAvailable() && !srcPropertySymOpnd->IsTypeChecked() && !srcPropertySymOpnd->IsWriteGuardChecked(),
-            "Why are the object type spec bits set before we specialized this instruction?");
-
-        // We only set guarded properties in the dead store pass, so they shouldn't be set here yet. If they were
-        // we would need to move them from this operand to the operand which is being copy propagated.
-        Assert(srcPropertySymOpnd->GetGuardedPropOps() == nullptr);
-
-        // We're hoisting an instruction from the loop, so we're placing it in a different position in the flow. Make sure only the flow
-        // insensitive info is copied.
-        IR::PropertySymOpnd * newPropertySymOpnd = srcPropertySymOpnd->CopyWithoutFlowSensitiveInfo(loopTopFunc);
-        Assert(newPropertySymOpnd->GetObjTypeSpecFlags() == 0);
-        Value *const propertyOwnerValueInLandingPad = loop->landingPad->globOptData.FindValue(srcPropertySymOpnd->GetObjectSym());
-        if(propertyOwnerValueInLandingPad)
-        {
-            newPropertySymOpnd->SetPropertyOwnerValueType(propertyOwnerValueInLandingPad->GetValueInfo()->Type());
-        }
-        newSrc = newPropertySymOpnd;
-    }
-    else
-    {
-        newSrc = IR::SymOpnd::New(sym, TyVar, func);
-    }
-
-    IR::Instr * newInstr = nullptr;
-    ValueType profiledFieldType;
-
-    if (instr->IsProfiledInstr())
-    {
-        profiledFieldType = instr->AsProfiledInstr()->u.FldInfo().valueType;
-    }
-
-    newInstr = IR::Instr::New(instr->m_opcode, newDst, newSrc, loopTopFunc);
-
-    // Win8 910551: Kill the live field for this hoisted field load
-    KillLiveFields(newStackSym, this->currentBlock->globOptData.liveFields);
-
-    IR::Instr * insertInstr = this->EnsureDisableImplicitCallRegion(loop);
-    insertInstr->InsertBefore(newInstr);
-
-    // Track use/def of arguments object
-    this->OptArguments(newInstr);
-
-    landingPad->globOptData.liveFields->Set(symId);
-    this->currentBlock->globOptData.liveFields->Set(symId);
-
-    // If we are reusing an already hoisted stack sym, while the var version is made live, we need to make sure that specialized
-    // versions of it are not live since this is effectively a field reload.
-    this->ToVarStackSym(newStackSym, landingPad);
-    this->ToVarStackSym(newStackSym, this->currentBlock);
-    loop->varSymsOnEntry->Set(newStackSym->m_id);
-    loop->int32SymsOnEntry->Clear(newStackSym->m_id);
-    loop->lossyInt32SymsOnEntry->Clear(newStackSym->m_id);
-    loop->float64SymsOnEntry->Clear(newStackSym->m_id);
-
-    Assert(oldValue != nullptr);
-
-    // Create a value in case we can copy prop out of the loop
-    if (newValue == nullptr || newValue->GetValueInfo()->IsUninitialized())
-    {
-        const bool hoistValue = newValue && oldValue->GetValueNumber() == newValue->GetValueNumber();
-
-        if(newValue)
-        {
-            // Assuming the profile data gives more precise value types based on the path it took at runtime, we can improve the
-            // original value type.
-            newValue->GetValueInfo()->Type() = profiledFieldType;
-        }
-        else
-        {
-            newValue = NewGenericValue(profiledFieldType, newDst);
-        }
-
-        this->currentBlock->globOptData.SetValue(newValue, sym);
-        if(hoistValue)
-        {
-            // The field value is invariant through the loop. Since we're updating its value to a more precise value, hoist the
-            // new value up to the loop landing pad where the field is being hoisted.
-            Assert(loop == currentBlock->loop);
-            Assert(landingPad == loop->landingPad);
-            oldValue = CopyValue(newValue, newValue->GetValueNumber());
-            landingPad->globOptData.SetValue(oldValue, sym);
-        }
-    }
-
-    newInstr->GetDst()->SetValueType(oldValue->GetValueInfo()->Type());
-    newInstr->GetSrc1()->SetValueType(oldValue->GetValueInfo()->Type());
-    loop->landingPad->globOptData.SetValue(oldValue, newStackSym);
-
-    this->currentBlock->globOptData.SetValue(newValue, newStackSym);
-    instr->GetSrc1()->SetValueType(newValue->GetValueInfo()->Type());
-
-    loop->hasHoistedFields = true;
-    loop->hoistedFields->Set(sym->m_id);
-
-    // Try to do object pointer copy prop. Do it now because, for instance, we want the ToVar we insert below
-    // to define the right sym (Win8 906875).
-    // Consider: Restructure field hoisting to call OptBlock on the completed loop landing pad instead of
-    // doing these optimizations and bitvector updates piecemeal.
-#ifdef DBG
-    PropertySym *propertySymUseBefore = nullptr;
-    Assert(this->byteCodeUses == nullptr);
-    this->byteCodeUsesBeforeOpt->ClearAll();
-    GlobOpt::TrackByteCodeSymUsed(instr, this->byteCodeUsesBeforeOpt, &propertySymUseBefore);
-#endif
-    this->CaptureByteCodeSymUses(newInstr);
-
-    // Consider (ObjTypeSpec): If we copy prop an object sym into the hoisted instruction we lose track of the original
-    // object sym's type being invariant through the loop and so we won't produce the new type's value into the loop,
-    // and end up with unnecessary type checks in the loop. If the new type isn't live in the landing pad (that is
-    // we weren't tracking its liveness and invariance through the loop), but the old type was invariant, let's add
-    // the new type to fieldHoistCandidateTypes and produce a value for it in the landing pad and loop header. If the
-    // old type was live then its liveness and invariance are already correctly reflected and there is nothing to do.
-    this->CopyPropPropertySymObj(newSrc, newInstr);
-    if (this->byteCodeUses != nullptr)
-    {
-        sym = newSrc->m_sym->AsPropertySym();
-        this->InsertByteCodeUses(newInstr);
-    }
-
-    StackSym * propertyBase = sym->m_stackSym;
-    if (!landingPad->globOptData.liveVarSyms->Test(propertyBase->m_id))
-    {
-        IR::RegOpnd *newOpnd = IR::RegOpnd::New(propertyBase, TyVar, instr->m_func);
-        this->ToVar(newInstr, newOpnd, landingPad, this->currentBlock->globOptData.FindValue(propertyBase), false);
-    }
-
-    if (landingPad->globOptData.canStoreTempObjectSyms && landingPad->globOptData.canStoreTempObjectSyms->Test(propertyBase->m_id))
-    {
-        newSrc->SetCanStoreTemp();
-    }
-
-#if DBG_DUMP
-    if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-    {
-        Output::Print(_u("FieldHoist: Hoisted Load "));
-        Output::SkipToColumn(30);
-        Output::Print(_u(" : "));
-        newInstr->Dump();
-    }
-#endif
-#if ENABLE_DEBUG_CONFIG_OPTIONS
-    if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-    {
-        char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
-        Output::Print(_u("    FieldHoist: function %s (%s) "), this->func->GetJITFunctionBody()->GetDisplayName(), this->func->GetDebugNumberSet(debugStringBuffer));
-        newInstr->DumpTestTrace();
-    }
-#endif
-}
-
-Value *
-GlobOpt::CreateFieldSrcValue(PropertySym * sym, PropertySym * originalSym, IR::Opnd ** ppOpnd, IR::Instr * instr)
-{
-#if DBG
-    // If the opcode going to kill all field values immediate anyway, we shouldn't be giving it a value
-    Assert(!instr->UsesAllFields());
-
-    AssertCanCopyPropOrCSEFieldLoad(instr);
-
-    Assert(instr->GetSrc1() == *ppOpnd);
-#endif
-
-    // Only give a value to fields if we are doing field copy prop.
-    // Consider: We should always copy prop local slots, but the only use right now is LdSlot from jit loop body.
-    // This should have one onus load, and thus no need for copy prop of field itself.  We may want to support
-    // copy prop LdSlot if there are other uses of local slots
-    if (!this->DoFieldCopyProp())
-    {
-        return nullptr;
-    }
-
-    BOOL wasLive = this->currentBlock->globOptData.liveFields->TestAndSet(sym->m_id);
-
-    if (this->DoFieldHoisting())
-    {
-        // We don't track copy prop sym for fields on loop prepass, no point in creating an empty unknown value.
-        // If we can copy prop through the back edge, we would have hoisted the field load, in which case we will
-        // just pick the live in copy prop sym for the field or create a new sym for the stack sym of the hoist field.
-        if (this->IsLoopPrePass())
-        {
-            // We don't clear the value when we kill the field.
-            // Clear it to make sure we don't use the old value.
-            this->currentBlock->globOptData.ClearSymValue(sym);
-            return nullptr;
-        }
-    }
-    else if (sym != originalSym)
-    {
-        this->currentBlock->globOptData.liveFields->TestAndSet(originalSym->m_id);
-    }
-
-    if (!wasLive)
-    {
-        // We don't clear the value when we kill the field.
-        // Clear it to make sure we don't use the old value.
-        this->currentBlock->globOptData.ClearSymValue(sym);
-        this->currentBlock->globOptData.ClearSymValue(originalSym);
-    }
-
-    Assert((*ppOpnd)->AsSymOpnd()->m_sym == sym || this->IsLoopPrePass());
-    if (wasLive)
-    {
-        // We should have dealt with field hoist already
-        Assert(!IsHoistedPropertySym(sym) || instr->m_opcode == Js::OpCode::CheckFixedFld);
-
-        // We don't use the sym store to do copy prop on hoisted fields, but create a value
-        // in case it can be copy prop out of the loop.
-    }
-    else
-    {
-        // If it wasn't live, it should not be hoistable
-        Assert(!this->IsHoistablePropertySym(sym->m_id));
-    }
-
-    return this->NewGenericValue(ValueType::Uninitialized, *ppOpnd);
-}
-
-bool
-GlobOpt::FieldHoistOptSrc(IR::Opnd *opnd, IR::Instr *instr, PropertySym * propertySym)
-{
-    if (!DoFieldHoisting())
-    {
-        return false;
-    }
-    if (!instr->TransfersSrcValue() || instr->m_opcode == Js::OpCode::LdMethodFromFlags)
-    {
-        // Instructions like typeof don't transfer value of the field, we can't hoist those right now.
-        return false;
-    }
-    if (TrackHoistableFields() && HasHoistableFields(&this->currentBlock->globOptData))
-    {
-        Assert(this->DoFieldHoisting());
-        CheckFieldHoistCandidate(instr, propertySym);
-
-        // This may have been a hoistable field with respect to the current loop. If so, that means:
-        // - It is assumed that it will be live on the back-edge and hence currently live for the purposes of determining
-        //   whether to hoist the field.
-        // - It is not already hoisted outside a parent loop or not live coming into this loop.
-        // - It is not already marked for hoisting in this loop.
-        //
-        // If this is a hoistable field, and if the field is ultimately chosen to be hoisted outside this loop, the field will
-        // be reloaded in this loop's landing pad. However, since the field may already have been hoisted outside a parent
-        // loop with a specialized stack sym still live and a value still available (since these are killed lazily), neither of
-        // which are valid anymore due to the reload, we still need to kill the specialized stack syms and the field value. On
-        // the other hand, if this was not a hoistable field, we need to treat it as a field load anyway. So, since this is the
-        // first use of the field in this loop, fall through to reload the field.
-    }
-    else if (!this->IsLoopPrePass())
-    {
-        if (CopyPropHoistedFields(propertySym, &opnd, instr))
-        {
-            return true;
-        }
-    }
-
-    this->ReloadFieldHoistStackSym(instr, propertySym);
-    return false;
-}
-
-void
-GlobOpt::FieldHoistOptDst(IR::Instr * instr, PropertySym * propertySym, Value * src1Val)
-{
-    if(DoFieldHoisting())
-    {
-        switch (instr->m_opcode)
-        {
-        case Js::OpCode::StSlot:
-        case Js::OpCode::StSlotChkUndecl:
-        case Js::OpCode::StFld:
-        case Js::OpCode::StRootFld:
-        case Js::OpCode::StFldStrict:
-        case Js::OpCode::StRootFldStrict:
-            CopyStoreFieldHoistStackSym(instr, propertySym, src1Val);
-            break;
-        }
-    }
-}
-
-bool
-GlobOpt::CopyPropHoistedFields(PropertySym * sym, IR::Opnd ** ppOpnd, IR::Instr * instr)
-{
-    Assert(instr->TransfersSrcValue());
-    if (!this->currentBlock->globOptData.liveFields->Test(sym->m_id))
-    {
-        // Not live
-        return false;
-    }
-
-    StackSym * hoistedCopySym;
-    Loop * loop = FindFieldHoistStackSym(this->currentBlock->loop, sym->m_id, &hoistedCopySym, instr);
-    Assert(loop != nullptr || !this->IsHoistablePropertySym(sym->m_id));
-
-    if (loop)
-    {
-        // The field was live before, so we have the hoisted stack sym live value, just copy prop it
-        *ppOpnd = CopyPropReplaceOpnd(instr, *ppOpnd, hoistedCopySym);
-
 #if DBG
-        if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-        {
-            Output::Print(_u("FieldHoist: Copy prop "));
-            sym->Dump();
-            Output::SkipToColumn(30);
-            Output::Print(_u(" : "));
-            instr->Dump();
-        }
-#endif
-        return true;
-    }
-    return false;
-}
-
-void
-GlobOpt::ReloadFieldHoistStackSym(IR::Instr * instr, PropertySym * propertySym)
-{
-    Assert(instr->TransfersSrcValue());
-    StackSym * fieldHoistSym = nullptr;
-    Loop * loop = this->FindFieldHoistStackSym(this->currentBlock->loop, propertySym->m_id, &fieldHoistSym, instr);
-
-    if (loop == nullptr)
-    {
-        return;
-    }
-
-    // When a field is killed, ideally the specialized versions of the corresponding hoisted stack syms should also be killed,
-    // since the field needs to be reloaded the next time it's used (which may be earlier in the loop). However, killing the
-    // specialized stack syms when the field is killed requires discovering and walking all fields that are killed and their
-    // hoisted stack syms, which requires more computation (since many fields can be killed at once).
-    //
-    // Alternatively, we can kill the specialized stack syms for a field when the field is reloaded, which is what's happening
-    // here. Since this happens per field and lazily, it requires less work. It works because killing the specialized stack
-    // syms only matters when the field is reloaded.
-    //
-    // Furthermore, to handle the case where a field is not live on entry into the loop (field is killed in the loop and not
-    // reloaded in the same loop afterwards), the specialized stack syms for that field must also be killed on entry into the
-    // loop. Instead of checking all hoisted field stack syms on entry into a loop after the prepass merge, and killing them if
-    // their corresponding field is not live, this is also done in a lazy fashion as above, only when a field is reloaded. If a
-    // field is reloaded in a loop before it's killed, and not reloaded again after the kill, the field won't be live on entry,
-    // and hence the specialized stack syms should also not be live on entry. This is true for all parent loops up to the
-    // nearest parent loop out of which the field is hoisted.
-
-    ToVarStackSym(fieldHoistSym, currentBlock);
-    if(!this->IsLoopPrePass())
-    {
-        for(Loop *currentLoop = currentBlock->loop;
-            currentLoop != loop->parent && !currentLoop->liveFieldsOnEntry->Test(propertySym->m_id);
-            currentLoop = currentLoop->parent)
-        {
-            currentLoop->int32SymsOnEntry->Clear(fieldHoistSym->m_id);
-            currentLoop->lossyInt32SymsOnEntry->Clear(fieldHoistSym->m_id);
-            currentLoop->float64SymsOnEntry->Clear(fieldHoistSym->m_id);
-        }
-    }
-
-    // Win8 943662: Kill the live field for this hoisted field load
-    this->KillLiveFields(fieldHoistSym, this->currentBlock->globOptData.liveFields);
-
-    if (this->IsLoopPrePass())
-    {
-        // In the prepass we are conservative and always assume that the fields are going to be reloaded
-        // because we don't loop until value is unchanged and we are unable to detect dependencies.
-
-        // Clear the value of the field to kill the value of the field even if it still live now.
-        this->currentBlock->globOptData.liveFields->Clear(propertySym->m_id);
-
-        // If we have to reload, we don't know the value, kill the old value for the fieldHoistSym.
-        this->currentBlock->globOptData.ClearSymValue(fieldHoistSym);
-
-        // No IR transformations in the prepass.
-        return;
-    }
-
-    // If we are reloading, the field should be dead. CreateFieldSrc will create a value for the field.
-    Assert(!this->currentBlock->globOptData.liveFields->Test(propertySym->m_id));
-
-    // Copy the dst to the field hoist sym.
-    IR::Instr * copyInstr = IR::Instr::New(Js::OpCode::Ld_A, IR::RegOpnd::New(fieldHoistSym, TyVar, instr->m_func), instr->GetDst(), instr->m_func);
-    instr->InsertAfter(copyInstr);
-
-#if DBG_DUMP
-    if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-    {
-        Output::Print(_u("FieldHoist: Reload field sym "));
-        Output::SkipToColumn(30);
-        Output::Print(_u(" : "));
-        instr->Dump();
-    }
-#endif
-}
-
-void
-GlobOpt::CopyStoreFieldHoistStackSym(IR::Instr * storeFldInstr, PropertySym * sym, Value * src1Val)
-{
-    // In the real (not prepass) pass, do the actual IR rewrites.
-    // In the prepass, only track the impact that the rewrites will have. (See Win8 521029)
-
-    Assert(storeFldInstr->m_opcode == Js::OpCode::StSlot
-        || storeFldInstr->m_opcode == Js::OpCode::StSlotChkUndecl
-        || storeFldInstr->m_opcode == Js::OpCode::StFld
-        || storeFldInstr->m_opcode == Js::OpCode::StRootFld
-        || storeFldInstr->m_opcode == Js::OpCode::StFldStrict
-        || storeFldInstr->m_opcode == Js::OpCode::StRootFldStrict);
-    Assert(storeFldInstr->GetDst()->GetType() == TyVar);
-
-    // We may use StSlot for all sort of things other then assigning TyVars
-    Assert(storeFldInstr->GetSrc1()->GetType() == TyVar || storeFldInstr->m_opcode == Js::OpCode::StSlot || storeFldInstr->m_opcode == Js::OpCode::StSlotChkUndecl);
-    Assert(storeFldInstr->GetSrc2() == nullptr);
-
-    StackSym * copySym;
-    Loop * loop = this->FindFieldHoistStackSym(this->currentBlock->loop, sym->m_id, &copySym);
-    if (loop == nullptr)
-    {
-        return;
-    }
-    IR::Opnd * srcOpnd = storeFldInstr->GetSrc1();
-    Func * storeFldFunc = storeFldInstr->m_func;
-    IR::Instr * newInstr;
-    if (!this->IsLoopPrePass())
-    {
-        this->CaptureByteCodeSymUses(storeFldInstr);
-
-        IR::RegOpnd * dstOpnd = IR::RegOpnd::New(copySym, TyVar, storeFldFunc);
-        dstOpnd->SetIsJITOptimizedReg(true);
-        storeFldInstr->UnlinkSrc1();
-        newInstr = IR::Instr::New(Js::OpCode::Ld_A, dstOpnd, srcOpnd, storeFldFunc);
-        storeFldInstr->SetSrc1(dstOpnd);
-        storeFldInstr->InsertBefore(newInstr);
-    }
-    this->ToVarStackSym(copySym, this->currentBlock); // The field-hoisted stack sym is now unspecialized
-
-    Value * dstVal = this->CopyValue(src1Val);
-    TrackCopiedValueForKills(dstVal);
-    this->SetSymStoreDirect(dstVal->GetValueInfo(), copySym);
-    this->currentBlock->globOptData.SetValue(dstVal, copySym);
-
-    // Copy the type specialized sym as well, in case we have a use for them
-    bool neededCopySymDef = false;
-    if(srcOpnd->IsRegOpnd())
-    {
-        StackSym *const srcSym = srcOpnd->AsRegOpnd()->m_sym;
-        if (this->currentBlock->globOptData.liveInt32Syms->Test(srcSym->m_id))
-        {
-            this->currentBlock->globOptData.liveInt32Syms->Set(copySym->m_id);
-            if(this->currentBlock->globOptData.liveLossyInt32Syms->Test(srcSym->m_id))
-            {
-                this->currentBlock->globOptData.liveLossyInt32Syms->Set(copySym->m_id);
-            }
-            if (!this->IsLoopPrePass())
-            {
-                StackSym * int32CopySym = copySym->GetInt32EquivSym(storeFldFunc);
-                IR::RegOpnd * int32CopyOpnd = IR::RegOpnd::New(int32CopySym, TyInt32, storeFldFunc);
-                IR::RegOpnd * int32SrcOpnd = IR::RegOpnd::New(srcSym->GetInt32EquivSym(nullptr),
-                    TyInt32, storeFldFunc);
-                newInstr = IR::Instr::New(Js::OpCode::Ld_I4, int32CopyOpnd, int32SrcOpnd, storeFldFunc);
-                int32SrcOpnd->SetIsJITOptimizedReg(true);
-                storeFldInstr->InsertBefore(newInstr);
-            }
-            neededCopySymDef = true;
-        }
-        if (this->currentBlock->globOptData.liveFloat64Syms->Test(srcSym->m_id))
-        {
-            this->currentBlock->globOptData.liveFloat64Syms->Set(copySym->m_id);
-            if (!this->IsLoopPrePass())
-            {
-                StackSym * float64CopySym = copySym->GetFloat64EquivSym(storeFldFunc);
-                IR::RegOpnd * float64CopyOpnd = IR::RegOpnd::New(float64CopySym, TyFloat64, storeFldFunc);
-                IR::RegOpnd * float64SrcOpnd = IR::RegOpnd::New(srcSym->GetFloat64EquivSym(nullptr),
-                    TyFloat64, storeFldFunc);
-                newInstr = IR::Instr::New(Js::OpCode::Ld_A, float64CopyOpnd, float64SrcOpnd, storeFldFunc);
-                float64SrcOpnd->SetIsJITOptimizedReg(true);
-                storeFldInstr->InsertBefore(newInstr);
-            }
-            neededCopySymDef = true;
-        }
-    }
-    else if(srcOpnd->IsAddrOpnd())
-    {
-        const auto srcAddrOpnd = srcOpnd->AsAddrOpnd();
-        if(srcAddrOpnd->IsVar() && Js::TaggedInt::Is(srcAddrOpnd->m_address))
-        {
-            this->currentBlock->globOptData.liveInt32Syms->Set(copySym->m_id);
-            if (!this->IsLoopPrePass())
-            {
-                StackSym * int32CopySym = copySym->GetInt32EquivSym(storeFldFunc);
-                IR::RegOpnd * int32CopyOpnd = IR::RegOpnd::New(int32CopySym, TyInt32, storeFldFunc);
-                IR::IntConstOpnd * int32SrcOpnd =
-                    IR::IntConstOpnd::New(Js::TaggedInt::ToInt32(srcAddrOpnd->m_address), TyInt32, storeFldFunc);
-                newInstr = IR::Instr::New(Js::OpCode::Ld_I4, int32CopyOpnd, int32SrcOpnd, storeFldFunc);
-                int32SrcOpnd->SetIsJITOptimizedReg(true);
-                storeFldInstr->InsertBefore(newInstr);
-            }
-            neededCopySymDef = true;
-        }
-    }
-
-    if(IsLoopPrePass() && neededCopySymDef)
-    {
-        // Record the def that would have been added
-        rootLoopPrePass->symsDefInLoop->Set(copySym->m_id);
-    }
-
-    this->KillLiveFields(copySym, this->currentBlock->globOptData.liveFields);
-
-#if DBG_DUMP
-    if (!this->IsLoopPrePass())
-    {
-        if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::FieldHoistPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
-        {
-            Output::Print(_u("FieldHoist: Copy field store "));
-            Output::SkipToColumn(30);
-            Output::Print(_u(" : "));
-            storeFldInstr->Dump();
-        }
-    }
-#endif
-}
-
-bool
-GlobOpt::NeedBailOnImplicitCallWithFieldOpts(Loop *loop, bool hasLiveFields) const
-{
-    if (!((this->DoFieldHoisting(loop) && loop->hasHoistedFields) ||
-          ((this->DoFieldRefOpts(loop) ||
-            this->DoFieldCopyProp(loop)) &&
-           hasLiveFields)))
-    {
-        return false;
-    }
-
-    return true;
-}
-
-IR::Instr *
-GlobOpt::EnsureDisableImplicitCallRegion(Loop * loop)
-{
-    Assert(loop->bailOutInfo != nullptr);
-    IR::Instr * endDisableImplicitCall = loop->endDisableImplicitCall;
-    if (endDisableImplicitCall)
-    {
-        return endDisableImplicitCall;
-    }
-
-    IR::Instr * bailOutTarget = EnsureBailTarget(loop);
-
-    Func * bailOutFunc = loop->GetFunc();
-    Assert(loop->bailOutInfo->bailOutFunc == bailOutFunc);
-
-    IR::MemRefOpnd * disableImplicitCallAddress = IR::MemRefOpnd::New(this->func->GetThreadContextInfo()->GetDisableImplicitFlagsAddr(), TyInt8, bailOutFunc);
-    IR::IntConstOpnd * disableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitCallAndExceptionFlag, TyInt8, bailOutFunc, true);
-    IR::IntConstOpnd * enableImplicitCallAndExceptionValue = IR::IntConstOpnd::New(DisableImplicitNoFlag, TyInt8, bailOutFunc, true);
-
-    IR::Opnd * implicitCallFlags = Lowerer::GetImplicitCallFlagsOpnd(bailOutFunc);
-    IR::IntConstOpnd * noImplicitCall = IR::IntConstOpnd::New(Js::ImplicitCall_None, TyInt8, bailOutFunc, true);
-
-    // Consider: if we are already doing implicit call in the outer loop, we don't need to clear the implicit call bit again
-    IR::Instr * clearImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, implicitCallFlags, noImplicitCall, bailOutFunc);
-    bailOutTarget->InsertBefore(clearImplicitCall);
-
-    IR::Instr * disableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, disableImplicitCallAndExceptionValue, bailOutFunc);
-    bailOutTarget->InsertBefore(disableImplicitCall);
-
-    endDisableImplicitCall = IR::Instr::New(Js::OpCode::Ld_A, disableImplicitCallAddress, enableImplicitCallAndExceptionValue, bailOutFunc);
-    bailOutTarget->InsertBefore(endDisableImplicitCall);
-
-    IR::BailOutInstr * bailOutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNotEqual, IR::BailOutOnImplicitCalls, loop->bailOutInfo, loop->bailOutInfo->bailOutFunc);
-    bailOutInstr->SetSrc1(implicitCallFlags);
-    bailOutInstr->SetSrc2(noImplicitCall);
-    bailOutTarget->InsertBefore(bailOutInstr);
-
-    loop->endDisableImplicitCall = endDisableImplicitCall;
-    return endDisableImplicitCall;
-}
-
-#if DBG
-bool
-GlobOpt::IsHoistedPropertySym(PropertySym * sym) const
-{
-    return IsHoistedPropertySym(sym->m_id, this->currentBlock->loop);
-}
-
-bool
-GlobOpt::IsHoistedPropertySym(SymID symId, Loop * loop) const
-{
-    StackSym * copySym;
-    return this->FindFieldHoistStackSym(loop, symId, &copySym) != nullptr;
-}
-
 bool
 GlobOpt::IsPropertySymId(SymID symId) const
 {

+ 0 - 1
lib/Common/ConfigFlagsList.h

@@ -137,7 +137,6 @@ PHASE(All)
                 PHASE(InductionVars)
                 PHASE(Invariants)
                 PHASE(FieldCopyProp)
-                PHASE(FieldHoist)
                 PHASE(FieldPRE)
                 PHASE(HostOpt)
                 PHASE(ObjTypeSpec)