@@ -2215,15 +2215,20 @@ BackwardPass::IsLazyBailOutCurrentlyNeeeded(IR::Instr * instr) const
22152215        " liveFixedField is null, MergeSuccBlocksInfo might have not initialized it?" 
22162216    );
22172217
2218-     if  (instr->IsStFldVariant ())
2218+     //  StFld LazyBailOut tag removal optimization. Given that this instr is a StFld variant and
2219+     //  that there already is an BailOutOnImplicitCall tag on this instr, we can remove the
2220+     //  LazyBailOut tag on this instr if the StFld is writing to a live fixed field. We cannot
2221+     //  perform this optimization if a BailOutOnImplicitCall tag is abscent because writing to
2222+     //  a property can result in an implicit call that then can result in a lazy bailout.
2223+     if  (instr->IsStFldVariant () && BailOutInfo::IsBailOutOnImplicitCalls (instr->GetBailOutKind ()))
22192224    {
22202225        Assert (instr->GetDst ());
2221-         Js::PropertyId id = instr->GetDst ()->GetSym ()->AsPropertySym ()->m_propertyId ;
2222- 
2223-         //  We only need to protect against SetFld if it is setting to one of the live fixed fields
2224-         return  this ->currentBlock ->liveFixedFields ->Test (id);
2226+         //  We only need to protect against StFld if it is setting to one of the live fixed fields.
2227+         return  currentBlock->liveFixedFields ->Test (instr->GetDst ()->GetSym ()->AsPropertySym ()->m_propertyId );
22252228    }
22262229
2230+     //  If no more fixed fields exist at this point in the block it is safe to assume that any field marked as
2231+     //  a fixed field has been verified to have not been modified and thus a LazyBailOut tag is not necessary.
22272232    return  !this ->currentBlock ->liveFixedFields ->IsEmpty ();
22282233}
22292234
@@ -2333,7 +2338,7 @@ BackwardPass::DeadStoreTypeCheckBailOut(IR::Instr * instr)
23332338        return ;
23342339    }
23352340
2336-     //  If bailOutKind is equivTypeCheck then leave alone  the bailout
2341+     //  If bailOutKind is equivTypeCheck then leave the bailout alone. 
23372342    if  (bailOutKind == IR::BailOutFailedEquivalentTypeCheck ||
23382343        bailOutKind == IR::BailOutFailedEquivalentFixedFieldTypeCheck)
23392344    {
@@ -2357,9 +2362,9 @@ BackwardPass::DeadStoreTypeCheckBailOut(IR::Instr * instr)
23572362}
23582363
23592364void 
2360- BackwardPass::DeadStoreLazyBailOut (IR::Instr * instr,  bool  needsLazyBailOut )
2365+ BackwardPass::DeadStoreLazyBailOut (IR::Instr * instr)
23612366{
2362-     if  (!this ->IsPrePass () && !needsLazyBailOut &&  instr->HasLazyBailOut ())
2367+     if  (!this ->IsPrePass () && instr->HasLazyBailOut ())
23632368    {
23642369        instr->ClearLazyBailOut ();
23652370        if  (!instr->HasBailOutInfo ())
@@ -2441,12 +2446,13 @@ BackwardPass::DeadStoreImplicitCallBailOut(IR::Instr * instr, bool hasLiveFields
24412446    //  We have an implicit call bailout in the code, and we want to make sure that it's required.
24422447    //  Do this now, because only in the dead store pass do we have complete forward and backward liveness info.
24432448    bool  needsBailOutOnImplicitCall = this ->IsImplicitCallBailOutCurrentlyNeeded (instr, mayNeedBailOnImplicitCall, needsLazyBailOut, hasLiveFields);
2449+ 
24442450    if (!UpdateImplicitCallBailOutKind (instr, needsBailOutOnImplicitCall, needsLazyBailOut))
24452451    {
24462452        instr->ClearBailOutInfo ();
2447-         if  (preOpBailOutInstrToProcess == instr)
2453+         if  (this -> preOpBailOutInstrToProcess  == instr)
24482454        {
2449-             preOpBailOutInstrToProcess = nullptr ;
2455+             this -> preOpBailOutInstrToProcess  = nullptr ;
24502456        }
24512457#if  DBG
24522458        if  (this ->DoMarkTempObjectVerify ())
@@ -2476,36 +2482,41 @@ BackwardPass::UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBa
24762482
24772483    const  bool  hasMarkTempObject = bailOutKindWithBits & IR::BailOutMarkTempObject;
24782484
2479-     //  Firstly,  we remove the mark temp object bit,  as it is not needed after the dead store pass. 
2480-     //  We will later skip removing BailOutOnImplicitCalls when there is a mark temp object bit regardless 
2481-     //  of ` needsBailOutOnImplicitCall` .
2485+     //  First  we remove the mark temp object bit as it is not needed after the dead
2486+     //  store pass.  We will later skip removing BailOutOnImplicitCalls when there
2487+     //  is a mark temp object bit regardless  of needsBailOutOnImplicitCall.
24822488    if  (hasMarkTempObject)
24832489    {
24842490        instr->SetBailOutKind (bailOutKindWithBits & ~IR::BailOutMarkTempObject);
24852491    }
24862492
24872493    if  (needsBailOutOnImplicitCall)
24882494    {
2489-         //  We decided that BailOutOnImplicitCall is needed. So lazy bailout is unnecessary
2490-         //  because we are already protected from potential side effects unless the operation
2491-         //  itself can change fields' values (StFld/StElem).
2495+         //  We decided that BailOutOnImplicitCall is needed; LazyBailOut is unnecessary because
2496+         //  the modification of a property would trigger an implicit call bailout before a LazyBailOut
2497+         //  would trigger. An edge case is when the act of checking the type of the object with the
2498+         //  property, which occurs before the implicit call check, results in a property guard invalidation.
2499+         //  In this case a LazyBailOut is necessary.
24922500        if  (needsLazyBailOut && !instr->CanChangeFieldValueWithoutImplicitCall ())
24932501        {
24942502            instr->ClearLazyBailOut ();
24952503        }
24962504
24972505        return  true ;
24982506    }
2499-     else 
2507+ 
2508+     //  needsBailOutOnImplicitCall also captures our intention to keep BailOutOnImplicitCalls
2509+     //  because we want to do fixed field lazy bailout optimization. So if we don't need them,
2510+     //  just remove our lazy bailout unless this instr can cause a PropertyGuard invalidation
2511+     //  during the type check.
2512+     if  (!instr->CanChangeFieldValueWithoutImplicitCall ())
25002513    {
2501-         //  `needsBailOutOnImplicitCall` also captures our intention to keep BailOutOnImplicitCalls
2502-         //  because we want to do fixed field lazy bailout optimization. So if we don't need them,
2503-         //  just remove our lazy bailout.
25042514        instr->ClearLazyBailOut ();
2505-         if  (!instr->HasBailOutInfo ())
2506-         {
2507-             return  true ;
2508-         }
2515+     }
2516+ 
2517+     if  (!instr->HasBailOutInfo ())
2518+     {
2519+         return  true ;
25092520    }
25102521
25112522    const  IR::BailOutKind bailOutKindWithoutBits = instr->GetBailOutKindNoBits ();
@@ -2517,8 +2528,8 @@ BackwardPass::UpdateImplicitCallBailOutKind(IR::Instr *const instr, bool needsBa
25172528        return  true ;
25182529    }
25192530
2520-     //  At this point,  we don't need the bail on implicit calls. 
2521-     //  Simply  use the bailout kind bits as our new bailout kind.
2531+     //  At this point we don't need the bail on implicit calls, 
2532+     //  use the bailout kind bits as our new bailout kind.
25222533    IR::BailOutKind newBailOutKind = bailOutKindWithBits - bailOutKindWithoutBits;
25232534
25242535    if  (newBailOutKind == IR::BailOutInvalid)
@@ -3721,7 +3732,10 @@ BackwardPass::ProcessBlock(BasicBlock * block)
37213732            );
37223733
37233734            DeadStoreTypeCheckBailOut (instr);
3724-             DeadStoreLazyBailOut (instr, needsLazyBailOut);
3735+             if  (!needsLazyBailOut)
3736+             {
3737+                 DeadStoreLazyBailOut (instr);
3738+             }
37253739            DeadStoreImplicitCallBailOut (instr, hasLiveFields, needsLazyBailOut);
37263740
37273741            AssertMsg (
@@ -5714,8 +5728,13 @@ BackwardPass::TrackAddPropertyTypes(IR::PropertySymOpnd *opnd, BasicBlock *block
57145728        typeWithProperty == typeWithoutProperty ||
57155729        (opnd->IsTypeChecked () && !opnd->IsInitialTypeChecked ()))
57165730    {
5717-         if  (!this ->IsPrePass () && block->stackSymToFinalType  != nullptr  && !this ->currentInstr ->HasBailOutInfo ())
5731+         if  (
5732+             !this ->IsPrePass () &&
5733+             block->stackSymToFinalType  != nullptr  &&
5734+             (!this ->currentInstr ->HasBailOutInfo () || currentInstr->OnlyHasLazyBailOut ())
5735+         )
57185736        {
5737+ 
57195738            PropertySym *propertySym = opnd->m_sym ->AsPropertySym ();
57205739            AddPropertyCacheBucket *pBucket =
57215740                block->stackSymToFinalType ->Get (propertySym->m_stackSym ->m_id );
@@ -6009,12 +6028,15 @@ BackwardPass::InsertTypeTransitionsAtPotentialKills()
60096028    //  Final types can't be pushed up past certain instructions.
60106029    IR::Instr *instr = this ->currentInstr ;
60116030
6012-     if  (instr->HasBailOutInfo () || instr->m_opcode  == Js::OpCode::UpdateNewScObjectCache)
6031+     //  Final types can't be pushed up past a BailOut point. Insert any transitions called
6032+     //  for by the current state of add-property buckets. Also do this for ctor cache updates
6033+     //  to avoid putting a type in the ctor cache that extends past the end of the ctor that
6034+     //  the cache covers.
6035+     //  TODO: explain why LBO gets exempted from this rule.
6036+     if  (instr->m_opcode  == Js::OpCode::UpdateNewScObjectCache ||
6037+         (instr->HasBailOutInfo () && !instr->OnlyHasLazyBailOut ())
6038+     )
60136039    {
6014-         //  Final types can't be pushed up past a bailout point.
6015-         //  Insert any transitions called for by the current state of add-property buckets.
6016-         //  Also do this for ctor cache updates, to avoid putting a type in the ctor cache that extends past
6017-         //  the end of the ctor that the cache covers.
60186040        this ->ForEachAddPropertyCacheBucket ([&](int  symId, AddPropertyCacheBucket *data)->bool  {
60196041            this ->InsertTypeTransitionAfterInstr (instr, symId, data, this ->currentBlock ->upwardExposedUses );
60206042            return  false ;
0 commit comments