Browse Source

[MERGE #2954 @meg-gupta] Enable globopt for functions with try finally

Merge pull request #2954 from meg-gupta:tryfinallypr

This change enables globopt on functions with try finally.
We transform the flowgraph such that we have 2 flow edges -
try to a excepting finally region and try to non excepting finally region.
This enables us to optimize the function on the non exception path.
We bailout on the exception path.

Special handling is needed when there are early exits (break, continue, return) within the tryfinally.
We need to execute the finally block on early exit, currently we bailout on early exit.
We transform the flow graph from (eh region -> early exit) to have an edge from  (eh region -> finally) and (finally -> early exit)
This transformation can be done only after the regions are assigned in FlowGraph.

So the flowgraph builder now has the following order of phase:

- build flow graph -> add the excepting and non excepting finallys alongside
- remove unreachable blocks
- assign regions
- identify early exits and add edges (does region info update when required)
- break blocks removal (does region info update when required)
Meghana Gupta 8 years ago
parent
commit
357fe4c954
41 changed files with 2406 additions and 272 deletions
  1. 16 15
      lib/Backend/BackwardPass.cpp
  2. 2 2
      lib/Backend/BailOut.cpp
  3. 1 0
      lib/Backend/BailOutKind.h
  4. 696 76
      lib/Backend/FlowGraph.cpp
  5. 18 3
      lib/Backend/FlowGraph.h
  6. 10 4
      lib/Backend/Func.h
  7. 110 24
      lib/Backend/GlobOpt.cpp
  8. 2 1
      lib/Backend/GlobOpt.h
  9. 7 1
      lib/Backend/GlobOptBailOut.cpp
  10. 35 16
      lib/Backend/IRBuilder.cpp
  11. 5 2
      lib/Backend/IRBuilder.h
  12. 4 0
      lib/Backend/JnHelperMethod.cpp
  13. 1 0
      lib/Backend/JnHelperMethodList.h
  14. 12 13
      lib/Backend/LinearScan.cpp
  15. 77 8
      lib/Backend/Lower.cpp
  16. 1 0
      lib/Backend/Lower.h
  17. 1 2
      lib/Backend/LowerMDShared.cpp
  18. 47 2
      lib/Backend/Region.cpp
  19. 27 4
      lib/Backend/Region.h
  20. 3 3
      lib/Backend/SccLiveness.cpp
  21. 1 1
      lib/Backend/amd64/LowererMDArch.cpp
  22. 3 3
      lib/Backend/arm/LowerMD.cpp
  23. 1 1
      lib/Backend/i386/LowererMDArch.cpp
  24. 1 0
      lib/Common/ConfigFlagsList.h
  25. 11 0
      lib/Runtime/Base/ThreadContext.h
  26. 2 0
      lib/Runtime/ByteCode/ByteCodeEmitter.cpp
  27. 3 1
      lib/Runtime/ByteCode/OpCodes.h
  28. 12 2
      lib/Runtime/Language/EHBailoutData.h
  29. 171 41
      lib/Runtime/Language/InterpreterStackFrame.cpp
  30. 1 1
      lib/Runtime/Language/InterpreterStackFrame.h
  31. 244 8
      lib/Runtime/Language/JavascriptExceptionOperators.cpp
  32. 6 3
      lib/Runtime/Language/JavascriptExceptionOperators.h
  33. 9 9
      lib/Runtime/Library/InJavascript/Intl.js.bc.32b.h
  34. 9 9
      lib/Runtime/Library/InJavascript/Intl.js.bc.64b.h
  35. 8 8
      lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.32b.h
  36. 9 9
      lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.64b.h
  37. 261 0
      test/EH/early1.baseline
  38. 315 0
      test/EH/early1.js
  39. 111 0
      test/EH/early2.baseline
  40. 141 0
      test/EH/early2.js
  41. 12 0
      test/EH/rlexe.xml

+ 16 - 15
lib/Backend/BackwardPass.cpp

@@ -48,7 +48,7 @@ bool
 BackwardPass::DoByteCodeUpwardExposedUsed() const
 {
     return (this->tag == Js::DeadStorePhase && this->func->hasBailout) ||
-        (this->func->HasTry() && this->func->DoOptimizeTryCatch() && this->tag == Js::BackwardPhase);
+        (this->func->HasTry() && this->func->DoOptimizeTry() && this->tag == Js::BackwardPhase);
 }
 
 bool
@@ -114,7 +114,7 @@ BackwardPass::DoDeadStore(Func* func)
 {
     return
         !PHASE_OFF(Js::DeadStorePhase, func) &&
-        (!func->HasTry() || func->DoOptimizeTryCatch());
+        (!func->HasTry() || func->DoOptimizeTry());
 }
 
 bool
@@ -1111,7 +1111,7 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
             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->tag == Js::BackwardPhase && this->func->HasTry() && this->func->DoOptimizeTryCatch()));
+            Assert(block->byteCodeUpwardExposedUsed == nullptr || (this->tag == Js::BackwardPhase && this->func->HasTry() && this->func->DoOptimizeTry()));
             Assert(block->byteCodeRestoreSyms == nullptr);
             Assert(block->stackSymToFinalType == nullptr);
             Assert(block->stackSymToGuardedProperties == nullptr);
@@ -1847,7 +1847,7 @@ BackwardPass::ProcessBailOutInfo(IR::Instr * instr)
     {
         // We don't need to fill in the bailout instruction in backward pass
         Assert(this->func->hasBailout || !instr->HasBailOutInfo());
-        Assert(!instr->HasBailOutInfo() || instr->GetBailOutInfo()->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTryCatch()));
+        Assert(!instr->HasBailOutInfo() || instr->GetBailOutInfo()->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTry()));
 
         if (instr->IsByteCodeUsesInstr())
         {
@@ -2298,7 +2298,7 @@ BackwardPass::ProcessBailOutInfo(IR::Instr * instr, BailOutInfo * bailOutInfo)
     // The byteCodeUpwardExposedUsed should only be assigned once. The only case which would break this
     // assumption is when we are optimizing a function having try-catch. In that case, we need the
     // byteCodeUpwardExposedUsed analysis in the initial backward pass too.
-    Assert(bailOutInfo->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTryCatch()));
+    Assert(bailOutInfo->byteCodeUpwardExposedUsed == nullptr || (this->func->HasTry() && this->func->DoOptimizeTry()));
 
     // Make a copy of the byteCodeUpwardExposedUsed so we can remove the constants
     if (!this->IsPrePass())
@@ -2765,7 +2765,7 @@ BackwardPass::ProcessBlock(BasicBlock * block)
                 }
                 case Js::OpCode::Catch:
                 {
-                    if (this->func->DoOptimizeTryCatch() && !this->IsPrePass())
+                    if (this->func->DoOptimizeTry() && !this->IsPrePass())
                     {
                         // Execute the "Catch" in the JIT'ed code, and bailout to the next instruction. This way, the bailout will restore the exception object automatically.
                         IR::BailOutInstr* bailOnException = IR::BailOutInstr::New(Js::OpCode::BailOnException, IR::BailOutOnException, instr->m_next, instr->m_func);
@@ -2792,15 +2792,15 @@ BackwardPass::ProcessBlock(BasicBlock * block)
             this->ProcessInlineeEnd(instr);
         }
 
-        if (instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Catch)
+        if ((instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Catch) || (instr->IsLabelInstr() && instr->m_next->m_opcode == Js::OpCode::Finally))
         {
             if (!this->currentRegion)
             {
-                Assert(!this->func->DoOptimizeTryCatch() && !(this->func->IsSimpleJit() && this->func->hasBailout));
+                Assert(!this->func->DoOptimizeTry() && !(this->func->IsSimpleJit() && this->func->hasBailout));
             }
             else
             {
-                Assert(this->currentRegion->GetType() == RegionTypeCatch);
+                Assert(this->currentRegion->GetType() == RegionTypeCatch || this->currentRegion->GetType() == RegionTypeFinally);
                 Region * matchingTryRegion = this->currentRegion->GetMatchingTryRegion();
                 Assert(matchingTryRegion);
 
@@ -2827,10 +2827,11 @@ BackwardPass::ProcessBlock(BasicBlock * block)
 #if DBG
         if (instr->m_opcode == Js::OpCode::TryCatch)
         {
-            if (!this->IsPrePass() && (this->func->DoOptimizeTryCatch() || (this->func->IsSimpleJit() && this->func->hasBailout)))
+            if (!this->IsPrePass() && (this->func->DoOptimizeTry() || (this->func->IsSimpleJit() && this->func->hasBailout)))
             {
                 Assert(instr->m_next->IsLabelInstr() && (instr->m_next->AsLabelInstr()->GetRegion() != nullptr));
                 Region * tryRegion = instr->m_next->AsLabelInstr()->GetRegion();
+                Assert(tryRegion && tryRegion->GetType() == RegionType::RegionTypeTry && tryRegion->GetMatchingCatchRegion() != nullptr);
                 Assert(tryRegion->writeThroughSymbolsSet);
             }
         }
@@ -7496,16 +7497,16 @@ BackwardPass::FoldCmBool(IR::Instr *instr)
 }
 
 void
-BackwardPass::SetWriteThroughSymbolsSetForRegion(BasicBlock * catchBlock, Region * tryRegion)
+BackwardPass::SetWriteThroughSymbolsSetForRegion(BasicBlock * catchOrFinallyBlock, Region * tryRegion)
 {
     tryRegion->writeThroughSymbolsSet = JitAnew(this->func->m_alloc, BVSparse<JitArenaAllocator>, this->func->m_alloc);
 
     if (this->DoByteCodeUpwardExposedUsed())
     {
-        Assert(catchBlock->byteCodeUpwardExposedUsed);
-        if (!catchBlock->byteCodeUpwardExposedUsed->IsEmpty())
+        Assert(catchOrFinallyBlock->byteCodeUpwardExposedUsed);
+        if (!catchOrFinallyBlock->byteCodeUpwardExposedUsed->IsEmpty())
         {
-            FOREACH_BITSET_IN_SPARSEBV(id, catchBlock->byteCodeUpwardExposedUsed)
+            FOREACH_BITSET_IN_SPARSEBV(id, catchOrFinallyBlock->byteCodeUpwardExposedUsed)
             {
                 tryRegion->writeThroughSymbolsSet->Set(id);
             }
@@ -7550,7 +7551,7 @@ BackwardPass::SetWriteThroughSymbolsSetForRegion(BasicBlock * catchBlock, Region
 bool
 BackwardPass::CheckWriteThroughSymInRegion(Region* region, StackSym* sym)
 {
-    if (region->GetType() == RegionTypeRoot || region->GetType() == RegionTypeFinally)
+    if (region->GetType() == RegionTypeRoot)
     {
         return false;
     }

+ 2 - 2
lib/Backend/BailOut.cpp

@@ -1050,7 +1050,7 @@ Js::Var BailOutRecord::BailOut(BailOutRecord const * bailOutRecord)
     void * argoutRestoreAddr = nullptr;
 #ifdef _M_IX86
     void * addressOfRetAddress = _AddressOfReturnAddress();
-    if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0))
+    if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0 || bailOutRecord->ehBailoutData->finallyOffset != 0 || bailOutRecord->ehBailoutData->ht == Js::HandlerType::HT_Finally))
     {
         // For a bailout in argument evaluation from an EH region, the esp is offset by the TryCatch helper's frame. So, the argouts are not at the offsets
         // stored in the bailout record, which are relative to ebp. Need to restore the argouts from the actual value of esp before calling the Bailout helper
@@ -2708,7 +2708,7 @@ Js::Var BranchBailOutRecord::BailOut(BranchBailOutRecord const * bailOutRecord,
     void * argoutRestoreAddr = nullptr;
 #ifdef _M_IX86
     void * addressOfRetAddress = _AddressOfReturnAddress();
-    if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0))
+    if (bailOutRecord->ehBailoutData && (bailOutRecord->ehBailoutData->catchOffset != 0 || bailOutRecord->ehBailoutData->finallyOffset != 0 || bailOutRecord->ehBailoutData->ht == Js::HandlerType::HT_Finally))
     {
         argoutRestoreAddr = (void *)((char*)addressOfRetAddress + ((2 + 1) * MachPtr)); // Account for the parameters and return address of this function
     }

+ 1 - 0
lib/Backend/BailOutKind.h

@@ -46,6 +46,7 @@ BAIL_OUT_KIND(LazyBailOut,                          0)
 BAIL_OUT_KIND(BailOutOnFailedHoistedLoopCountBasedBoundCheck, 0)
 BAIL_OUT_KIND(BailOutForGeneratorYield,             0)
 BAIL_OUT_KIND(BailOutOnException,                   0)
+BAIL_OUT_KIND(BailOutOnEarlyExit,                   0)
 
 // SIMD_JS
 BAIL_OUT_KIND(BailOutSimd128F4Only,                 0)

File diff suppressed because it is too large
+ 696 - 76
lib/Backend/FlowGraph.cpp


+ 18 - 3
lib/Backend/FlowGraph.h

@@ -139,6 +139,9 @@ public:
         tailBlock(nullptr),
         loopList(nullptr),
         catchLabelStack(nullptr),
+        finallyLabelStack(nullptr),
+        leaveNullLabelStack(nullptr),
+        regToFinallyEndMap(nullptr),
         hasBackwardPassInfo(false),
         hasLoop(false),
         implicitCallFlags(Js::ImplicitCall_HasNoInfo)
@@ -149,7 +152,7 @@ public:
     void Destroy(void);
 
     void         RunPeeps();
-    BasicBlock * AddBlock(IR::Instr * firstInstr, IR::Instr * lastInstr, BasicBlock * nextBlock);
+    BasicBlock * AddBlock(IR::Instr * firstInstr, IR::Instr * lastInstr, BasicBlock * nextBlock, BasicBlock *prevBlock = nullptr);
     FlowEdge *   AddEdge(BasicBlock * predBlock, BasicBlock * succBlock);
     BasicBlock * InsertCompensationCodeForBlockMove(FlowEdge * edge, // Edge where compensation code needs to be inserted
                                                     bool insertCompensationBlockToLoopList = false,
@@ -166,7 +169,12 @@ public:
     static void  SafeRemoveInstr(IR::Instr *instr);
     void         SortLoopLists();
     FlowEdge *   FindEdge(BasicBlock *predBlock, BasicBlock *succBlock);
-
+    IR::LabelInstr * DeleteLeaveChainBlocks(IR::BranchInstr *leaveInstr, IR::Instr * &instrPrev);
+    bool         IsEarlyExitFromFinally(IR::BranchInstr *leaveInstr, Region *currentRegion, Region *branchTargetRegion, IR::Instr *&instrPrev, IR::LabelInstr *&exitLabel);
+    bool         Dominates(Region *finallyRegion, Region *exitLabelRegion);
+    bool         DoesExitLabelDominate(IR::BranchInstr *leaveInstr);
+    void         InsertEdgeFromFinallyToEarlyExit(BasicBlock * finallyEndBlock, IR::LabelInstr * exitLabel);
+    BasicBlock * FindInfiniteLoop(BasicBlock * finallyBlock);
 #if DBG_DUMP
     void         Dump();
     void         Dump(bool verbose, const char16 *form);
@@ -177,6 +185,10 @@ public:
     BasicBlock *              tailBlock;
     Loop *                    loopList;
     SList<IR::LabelInstr*> *  catchLabelStack;
+    SList<IR::LabelInstr*> *  finallyLabelStack;
+    SList<IR::LabelInstr*> *  leaveNullLabelStack;
+    typedef JsUtil::BaseDictionary<Region *, BasicBlock *, JitArenaAllocator> RegionToFinallyEndMapType;
+    RegionToFinallyEndMapType * regToFinallyEndMap;
     bool                      hasBackwardPassInfo;
     bool                      hasLoop;
     Js::ImplicitCallFlags     implicitCallFlags;
@@ -186,7 +198,10 @@ private:
     void        BuildLoop(BasicBlock *headBlock, BasicBlock *tailBlock, Loop *parentLoop = nullptr);
     void        WalkLoopBlocks(BasicBlock *block, Loop *loop, JitArenaAllocator *tempAlloc);
     void        AddBlockToLoop(BasicBlock *block, Loop *loop);
-    void        UpdateRegionForBlock(BasicBlock *block, Region **blockToRegion);
+    bool        IsEHTransitionInstr(IR::Instr *instr);
+    BasicBlock * GetPredecessorForRegionPropagation(BasicBlock *block);
+    void        UpdateRegionForBlock(BasicBlock *block);
+    void        UpdateRegionForBlockFromEHPred(BasicBlock *block, bool reassign = false);
     Region *    PropagateRegionFromPred(BasicBlock *block, BasicBlock *predBlock, Region *predRegion, IR::Instr * &tryInstr);
     IR::Instr * PeepCm(IR::Instr *instr);
     IR::Instr * PeepTypedCm(IR::Instr *instr);

+ 10 - 4
lib/Backend/Func.h

@@ -196,7 +196,8 @@ public:
     {
         return
             !PHASE_OFF(Js::GlobOptPhase, this) && !IsSimpleJit() &&
-            (!GetTopFunc()->HasTry() || GetTopFunc()->CanOptimizeTryCatch());
+            (!GetTopFunc()->HasTry() || GetTopFunc()->CanOptimizeTryCatch()) &&
+            (!GetTopFunc()->HasFinally() || GetTopFunc()->CanOptimizeTryFinally());
     }
 
     bool DoInline() const
@@ -204,15 +205,20 @@ public:
         return DoGlobOpt() && !GetTopFunc()->HasTry();
     }
 
-    bool DoOptimizeTryCatch() const
+    bool DoOptimizeTry() const
     {
         Assert(IsTopFunc());
         return DoGlobOpt();
     }
 
+    bool CanOptimizeTryFinally() const
+    {
+        return !this->m_workItem->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryFinallyPhase, this);
+    }
+
     bool CanOptimizeTryCatch() const
     {
-        return !this->HasFinally() && !this->m_workItem->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, this);
+        return !this->m_workItem->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, this);
     }
 
     bool DoSimpleJitDynamicProfile() const;
@@ -834,7 +840,7 @@ public:
     bool                HasArrayInfo()
     {
         const auto top = this->GetTopFunc();
-        return this->HasProfileInfo() && this->GetWeakFuncRef() && !(top->HasTry() && !top->DoOptimizeTryCatch()) &&
+        return this->HasProfileInfo() && this->GetWeakFuncRef() && !(top->HasTry() && !top->DoOptimizeTry()) &&
             top->DoGlobOpt() && !PHASE_OFF(Js::LoopFastPathPhase, top);
     }
 

+ 110 - 24
lib/Backend/GlobOpt.cpp

@@ -870,7 +870,7 @@ GlobOpt::TryTailDup(IR::BranchInstr *tailBranch)
     // If we've duplicated everywhere, tail block is dead and should be removed.
     if (dupCount == origPredCount)
     {
-        AssertMsg(mergeLabel->IsUnreferenced(), "Should not remove block with referenced label.");
+        AssertMsg(mergeLabel->labelRefs.Empty(), "Should not remove block with referenced label.");
         func->m_fg->RemoveBlock(labelBlock, nullptr, true);
     }
 
@@ -2423,7 +2423,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
     IR::Instr *instrPrev = instr->m_prev;
     IR::Instr *instrNext = instr->m_next;
 
-    if (instr->IsLabelInstr() && this->func->HasTry() && this->func->DoOptimizeTryCatch())
+    if (instr->IsLabelInstr() && this->func->HasTry() && this->func->DoOptimizeTry())
     {
         this->currentRegion = instr->AsLabelInstr()->GetRegion();
         Assert(this->currentRegion);
@@ -2536,27 +2536,45 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
         src1Val = src2Val;
         src2Val = nullptr;
     }
-    else if (instr->m_opcode == Js::OpCode::TryCatch && this->func->DoOptimizeTryCatch())
+    else if ((instr->m_opcode == Js::OpCode::TryCatch && this->func->DoOptimizeTry()) || (instr->m_opcode == Js::OpCode::TryFinally && this->func->DoOptimizeTry()))
     {
-        ProcessTryCatch(instr);
+        ProcessTryHandler(instr);
     }
     else if (instr->m_opcode == Js::OpCode::BrOnException)
     {
-        // BrOnException was added to model flow from try region to the catch region to assist
-        // the backward pass in propagating bytecode upward exposed info from the catch block
-        // to the try, and to handle break blocks. Removing it here as it has served its purpose
-        // and keeping it around might also have unintended effects while merging block data for
-        // the catch block's predecessors.
-        // Note that the Deadstore pass will still be able to propagate bytecode upward exposed info
-        // because it doesn't skip dead blocks for that.
-        this->RemoveFlowEdgeToCatchBlock(instr);
-        *isInstrRemoved = true;
-        this->currentBlock->RemoveInstr(instr);
-        return instrNext;
+        if (instr->AsBranchInstr()->GetTarget()->GetRegion()->GetType() != RegionType::RegionTypeFinally)
+        {
+            // BrOnException was added to model flow from try region to the catch region to assist
+            // the backward pass in propagating bytecode upward exposed info from the catch block
+            // to the try, and to handle break blocks. Removing it here as it has served its purpose
+            // and keeping it around might also have unintended effects while merging block data for
+            // the catch block's predecessors.
+            // Note that the Deadstore pass will still be able to propagate bytecode upward exposed info
+            // because it doesn't skip dead blocks for that.
+            this->RemoveFlowEdgeToCatchBlock(instr);
+            *isInstrRemoved = true;
+            this->currentBlock->RemoveInstr(instr);
+            return instrNext;
+        }
+        else
+        {
+            // We add BrOnException from a finally region to early exit
+            this->RemoveFlowEdgeToFinallyOnExceptionBlock(instr);
+            *isInstrRemoved = true;
+            this->currentBlock->RemoveInstr(instr);
+            return instrNext;
+        }
     }
     else if (instr->m_opcode == Js::OpCode::BrOnNoException)
     {
-        this->RemoveFlowEdgeToCatchBlock(instr);
+        if (instr->AsBranchInstr()->GetTarget()->GetRegion()->GetType() == RegionType::RegionTypeCatch)
+        {
+            this->RemoveFlowEdgeToCatchBlock(instr);
+        }
+        else
+        {
+            this->RemoveFlowEdgeToFinallyOnExceptionBlock(instr);
+        }
     }
 
     bool isAlreadyTypeSpecialized = false;
@@ -2647,7 +2665,7 @@ GlobOpt::OptInstr(IR::Instr *&instr, bool* isInstrRemoved)
     instrNext = instr->m_next;
     if (dst)
     {
-        if (this->func->HasTry() && this->func->DoOptimizeTryCatch())
+        if (this->func->HasTry() && this->func->DoOptimizeTry())
         {
             this->InsertToVarAtDefInTryRegion(instr, dst);
         }
@@ -17387,11 +17405,25 @@ GlobOpt::PreOptPeep(IR::Instr *instr)
             }
             case Js::OpCode::BailOnException:
             {
-                Assert(this->func->HasTry() && this->func->DoOptimizeTryCatch() &&
-                    instr->m_prev->m_opcode == Js::OpCode::Catch &&
-                    instr->m_prev->m_prev->IsLabelInstr() &&
-                    instr->m_prev->m_prev->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeCatch); // Should also handle RegionTypeFinally
-
+                Assert(
+                    (
+                        this->func->HasTry() && this->func->DoOptimizeTry() &&
+                        instr->m_prev->m_opcode == Js::OpCode::Catch &&
+                        instr->m_prev->m_prev->IsLabelInstr() &&
+                        instr->m_prev->m_prev->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeCatch
+                    )
+                    ||
+                    (
+                        this->func->HasFinally() && this->func->DoOptimizeTry() &&
+                        instr->m_prev->AsLabelInstr() &&
+                        instr->m_prev->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeFinally
+                    )
+                );
+                break;
+            }
+            case Js::OpCode::BailOnEarlyExit:
+            {
+                Assert(this->func->HasFinally() && this->func->DoOptimizeTry());
                 break;
             }
             default:
@@ -17445,7 +17477,7 @@ GlobOpt::RemoveCodeAfterNoFallthroughInstr(IR::Instr *instr)
 }
 
 void
-GlobOpt::ProcessTryCatch(IR::Instr* instr)
+GlobOpt::ProcessTryHandler(IR::Instr* instr)
 {
     Assert(instr->m_next->IsLabelInstr() && instr->m_next->AsLabelInstr()->GetRegion()->GetType() == RegionType::RegionTypeTry);
 
@@ -17487,8 +17519,9 @@ GlobOpt::RemoveFlowEdgeToCatchBlock(IR::Instr * instr)
         catchBlock = instr->AsBranchInstr()->GetTarget()->GetBasicBlock();
         predBlock = this->currentBlock;
     }
-    else if (instr->m_opcode == Js::OpCode::BrOnNoException)
+    else
     {
+        Assert(instr->m_opcode == Js::OpCode::BrOnNoException);
         IR::Instr * nextInstr = instr->GetNextRealInstrOrLabel();
         Assert(nextInstr->IsLabelInstr());
         IR::LabelInstr * nextLabel = nextInstr->AsLabelInstr();
@@ -17522,6 +17555,59 @@ GlobOpt::RemoveFlowEdgeToCatchBlock(IR::Instr * instr)
     }
 }
 
+void
+GlobOpt::RemoveFlowEdgeToFinallyOnExceptionBlock(IR::Instr * instr)
+{
+    Assert(instr->IsBranchInstr());
+
+    BasicBlock * finallyBlock = nullptr;
+    BasicBlock * predBlock = nullptr;
+    if (instr->m_opcode == Js::OpCode::BrOnException)
+    {
+        finallyBlock = instr->AsBranchInstr()->GetTarget()->GetBasicBlock();
+        predBlock = this->currentBlock;
+        Assert(finallyBlock && predBlock);
+    }
+    else
+    {
+        Assert(instr->m_opcode == Js::OpCode::BrOnNoException);
+        IR::Instr * nextInstr = instr->GetNextRealInstrOrLabel();
+        Assert(nextInstr->IsLabelInstr());
+        IR::LabelInstr * nextLabel = nextInstr->AsLabelInstr();
+
+        if (nextLabel->GetRegion() && nextLabel->GetRegion()->GetType() == RegionTypeFinally)
+        {
+            finallyBlock = nextLabel->GetBasicBlock();
+            predBlock = this->currentBlock;
+        }
+        else
+        {
+            if (!(nextLabel->m_next->IsBranchInstr() && nextLabel->m_next->AsBranchInstr()->IsUnconditional()))
+            {
+                // Already processed in loop prepass
+                return;
+            }
+            BasicBlock * nextBlock = nextLabel->GetBasicBlock();
+            IR::BranchInstr * branchTofinallyBlockOrEarlyExit = nextLabel->m_next->AsBranchInstr();
+            IR::LabelInstr * finallyBlockLabelOrEarlyExitLabel = branchTofinallyBlockOrEarlyExit->GetTarget();
+            finallyBlock = finallyBlockLabelOrEarlyExitLabel->GetBasicBlock();
+            predBlock = nextBlock;
+        }
+    }
+
+    if (finallyBlock && predBlock)
+    {
+        if (this->func->m_fg->FindEdge(predBlock, finallyBlock))
+        {
+            predBlock->RemoveDeadSucc(finallyBlock, this->func->m_fg);
+            if (predBlock == this->currentBlock)
+            {
+                predBlock->DecrementDataUseCount();
+            }
+        }
+    }
+}
+
 IR::Instr *
 GlobOpt::OptPeep(IR::Instr *instr, Value *src1Val, Value *src2Val)
 {

+ 2 - 1
lib/Backend/GlobOpt.h

@@ -918,9 +918,10 @@ private:
     IR::Instr *             OptPeep(IR::Instr *instr, Value *src1Val, Value *src2Val);
     void                    OptimizeIndirUses(IR::IndirOpnd *indir, IR::Instr * *pInstr, Value **indirIndexValRef);
     void                    RemoveCodeAfterNoFallthroughInstr(IR::Instr *instr);
-    void                    ProcessTryCatch(IR::Instr* instr);
+    void                    ProcessTryHandler(IR::Instr* instr);
     void                    InsertToVarAtDefInTryRegion(IR::Instr * instr, IR::Opnd * dstOpnd);
     void                    RemoveFlowEdgeToCatchBlock(IR::Instr * instr);
+    void                    RemoveFlowEdgeToFinallyOnExceptionBlock(IR::Instr * instr);
 
     void                    CSEAddInstr(BasicBlock *block, IR::Instr *instr, Value *dstVal, Value *src1Val, Value *src2Val, Value *dstIndirIndexVal, Value *src1IndirIndexVal);
     void                    OptimizeChecks(IR::Instr * const instr);

+ 7 - 1
lib/Backend/GlobOptBailOut.cpp

@@ -984,7 +984,7 @@ GlobOpt::FillBailOutInfo(BasicBlock *block, BailOutInfo * bailOutInfo)
 
                 bailOutInfo->startCallFunc[startCallNumber] = sym->m_instrDef->m_func;
 #ifdef _M_IX86
-                if (this->currentRegion && this->currentRegion->GetType() == RegionTypeTry)
+                if (this->currentRegion && (this->currentRegion->GetType() == RegionTypeTry || this->currentRegion->GetType() == RegionTypeFinally))
                 {
                     // For a bailout in argument evaluation from an EH region, the esp is offset by the TryCatch helper�s frame. So, the argouts are not actually pushed at the
                     // offsets stored in the bailout record, which are relative to ebp. Need to restore the argouts from the actual value of esp before calling the Bailout helper.
@@ -1361,6 +1361,12 @@ GlobOpt::GenerateBailAfterOperation(IR::Instr * *const pInstr, IR::BailOutKind k
     {
         nextInstr = nextInstr->GetNextRealInstrOrLabel();
     }
+    // This can happen due to break block removal
+    while (nextInstr->GetByteCodeOffset() == Js::Constants::NoByteCodeOffset ||
+        nextInstr->GetByteCodeOffset() < currentOffset)
+    {
+        nextInstr = nextInstr->GetNextRealInstrOrLabel();
+    }
     IR::Instr * bailOutInstr = instr->ConvertToBailOutInstr(nextInstr, kind);
     if (this->currentBlock->GetLastInstr() == instr)
     {

+ 35 - 16
lib/Backend/IRBuilder.cpp

@@ -390,10 +390,10 @@ IRBuilder::Build()
     this->branchRelocList = JitAnew(m_tempAlloc, SList<BranchReloc *>, m_tempAlloc);
     Func * topFunc = this->m_func->GetTopFunc();
     if (topFunc->HasTry() &&
-        ((!topFunc->HasFinally() && !topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, topFunc)) ||
+        ((!topFunc->IsLoopBody() && !PHASE_OFF(Js::OptimizeTryCatchPhase, topFunc)) ||
         (topFunc->IsSimpleJit() && topFunc->GetJITFunctionBody()->DoJITLoopBody()))) // should be relaxed as more bailouts are added in Simple Jit
     {
-        this->catchOffsetStack = JitAnew(m_tempAlloc, SList<uint>, m_tempAlloc);
+        this->handlerOffsetStack = JitAnew(m_tempAlloc, SList<handlerStackElementType>, m_tempAlloc);
     }
 
     this->firstTemp = m_func->GetJITFunctionBody()->GetFirstTmpReg();
@@ -876,7 +876,7 @@ IRBuilder::Build()
 
     InsertLabels();
 
-    Assert(!this->catchOffsetStack || this->catchOffsetStack->Empty());
+    Assert(!this->handlerOffsetStack || this->handlerOffsetStack->Empty());
 
     // Insert bailout for ignore exception for labels, after all labels were finalized.
     ignoreExBranchInstrToOffsetMap.Map([this](IR::Instr* instr, int byteCodeOffset) {
@@ -1677,8 +1677,8 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
     case Js::OpCode::Throw:
         {
             srcOpnd = this->BuildSrcOpnd(srcRegOpnd);
-
-            if (this->catchOffsetStack && !this->catchOffsetStack->Empty())
+            if ((this->handlerOffsetStack && !this->handlerOffsetStack->Empty()) ||
+                finallyBlockLevel > 0)
             {
                 newOpcode = Js::OpCode::EHThrow;
             }
@@ -1791,9 +1791,10 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
         return;
 
     case Js::OpCode::Catch:
-        if (this->catchOffsetStack)
+        if (this->handlerOffsetStack)
         {
-            this->catchOffsetStack->Pop();
+            Assert(this->handlerOffsetStack->Top().Second() == true);
+            this->handlerOffsetStack->Pop();
         }
         dstIsCatchObject = true;
         break;
@@ -2076,7 +2077,7 @@ IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot ds
             ValueType arrayType(ldElemInfo->GetArrayType());
             if(arrayType.IsLikelyNativeArray() &&
                 (
-                    (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) ||
+                    (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) ||
                     m_func->IsJitInDebugMode()
                 ))
             {
@@ -2090,7 +2091,7 @@ IRBuilder::BuildProfiledReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot ds
             }
             src1Opnd->SetValueType(arrayType);
 
-            if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch())
+            if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry())
             {
                 isProfiled = false;
             }
@@ -5369,7 +5370,7 @@ IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRe
     {
         if(arrayType.IsLikelyNativeArray() &&
             (
-                (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) ||
+                (!(m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry()) && m_func->GetWeakFuncRef() && !m_func->HasArrayInfo()) ||
                 m_func->IsJitInDebugMode()
             ))
         {
@@ -5392,7 +5393,7 @@ IRBuilder::BuildElementI(Js::OpCode newOpcode, uint32 offset, Js::RegSlot baseRe
         }
         indirOpnd->GetBaseOpnd()->SetValueType(arrayType);
 
-        if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTryCatch())
+        if (m_func->GetTopFunc()->HasTry() && !m_func->GetTopFunc()->DoOptimizeTry())
         {
             isProfiledLoad = false;
             isProfiledStore = false;
@@ -6764,7 +6765,7 @@ IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset)
         IR::BranchInstr * branchInstr;
         IR::LabelInstr * labelInstr;
 
-        if (this->catchOffsetStack && !this->catchOffsetStack->Empty())
+        if (this->handlerOffsetStack && !this->handlerOffsetStack->Empty() && this->handlerOffsetStack->Top().Second())
         {
             // If the try region has a break block, we don't want the Flowgraph to move all of that code out of the loop
             // because an exception will bring the control back into the loop. The branch out of the loop (which is the
@@ -6773,17 +6774,31 @@ IRBuilder::BuildEmpty(Js::OpCode newOpcode, uint32 offset)
             // "BrOnException $catch" is inserted before Leave's in the try region to instrument flow from the try region
             // to the catch region (which is in the loop).
             IR::BranchInstr * brOnException = IR::BranchInstr::New(Js::OpCode::BrOnException, nullptr, this->m_func);
-            this->AddBranchInstr(brOnException, offset, this->catchOffsetStack->Top());
+            this->AddBranchInstr(brOnException, offset, this->handlerOffsetStack->Top().First());
         }
 
         labelInstr = IR::LabelInstr::New(Js::OpCode::Label, this->m_func);
         branchInstr = IR::BranchInstr::New(newOpcode, labelInstr, this->m_func);
         this->AddInstr(branchInstr, offset);
         this->AddInstr(labelInstr, Js::Constants::NoByteCodeOffset);
-
         break;
     }
 
+    case Js::OpCode::LeaveNull:
+        finallyBlockLevel--;
+        this->AddInstr(instr, offset);
+        break;
+
+    case Js::OpCode::Finally:
+        if (this->handlerOffsetStack)
+        {
+            Assert(this->handlerOffsetStack->Top().Second() == false);
+            this->handlerOffsetStack->Pop();
+        }
+        finallyBlockLevel++;
+        this->AddInstr(IR::Instr::New(Js::OpCode::Finally, this->m_func), offset);
+        break;
+
     case Js::OpCode::Break:
         if (m_func->IsJitInDebugMode())
         {
@@ -7058,9 +7073,13 @@ IRBuilder::BuildBr(Js::OpCode newOpcode, uint32 offset)
     }
 #endif
 
-    if ((newOpcode == Js::OpCode::TryCatch) && this->catchOffsetStack)
+    if ((newOpcode == Js::OpCode::TryCatch) && this->handlerOffsetStack)
+    {
+        this->handlerOffsetStack->Push(Pair<uint, bool>(targetOffset, true));
+    }
+    else if ((newOpcode == Js::OpCode::TryFinally) && this->handlerOffsetStack)
     {
-        this->catchOffsetStack->Push(targetOffset);
+        this->handlerOffsetStack->Push(Pair<uint, bool>(targetOffset, false));
     }
     branchInstr = IR::BranchInstr::New(newOpcode, nullptr, m_func);
     this->AddBranchInstr(branchInstr, offset, targetOffset);

+ 5 - 2
lib/Backend/IRBuilder.h

@@ -64,8 +64,9 @@ public:
         , m_ldSlots(nullptr)
         , m_loopCounterSym(nullptr)
         , callTreeHasSomeProfileInfo(false)
+        , finallyBlockLevel(0)
         , m_saveLoopImplicitCallFlags(nullptr)
-        , catchOffsetStack(nullptr)
+        , handlerOffsetStack(nullptr)
         , m_switchAdapter(this)
         , m_switchBuilder(&m_switchAdapter)
         , m_stackFuncPtrSym(nullptr)
@@ -323,7 +324,8 @@ private:
     Js::StatementReader<Js::FunctionBody::ArenaStatementMapList> m_statementReader;
     SList<IR::Instr *> *m_argStack;
     SList<BranchReloc*> *branchRelocList;
-    SList<uint>         *catchOffsetStack;
+    typedef Pair<uint, bool> handlerStackElementType;
+    SList<handlerStackElementType>         *handlerOffsetStack;
     SymID *             tempMap;
     BVFixed *           fbvTempUsed;
     Js::RegSlot         firstTemp;
@@ -339,6 +341,7 @@ private:
     StackSym*           m_loopCounterSym;
     StackSym *          m_stackFuncPtrSym;
     bool                callTreeHasSomeProfileInfo;
+    uint                finallyBlockLevel;
 
     // Keep track of how many args we have on the stack whenever
     // we make a call so that the max stack used over all calls can be

+ 4 - 0
lib/Backend/JnHelperMethod.cpp

@@ -295,6 +295,10 @@ DECLSPEC_GUARDIGNORE  _NOINLINE intptr_t GetNonTableMethodAddress(ThreadContextI
     case HelperOp_TryFinally:
         return SHIFT_ADDR(context, Js::JavascriptExceptionOperators::OP_TryFinally);
 
+
+    case HelperOp_TryFinallySimpleJit:
+        return SHIFT_ADDR(context, Js::JavascriptExceptionOperators::OP_TryFinallySimpleJit);
+
     //
     // Methods that we don't want to get marked as CFG targets as they dump all registers to a controlled address
     //

+ 1 - 0
lib/Backend/JnHelperMethodList.h

@@ -353,6 +353,7 @@ HELPERCALL(AllocUninitializedSimdI4, Js::JavascriptSIMDInt32x4::AllocUninitializ
 
 HELPERCALL(Op_TryCatch, nullptr, 0)
 HELPERCALL(Op_TryFinally, nullptr, AttrCanThrow)
+HELPERCALL(Op_TryFinallySimpleJit, nullptr, AttrCanThrow)
 #if _M_X64
 HELPERCALL(Op_ReturnFromCallWithFakeFrame, amd64_ReturnFromCallWithFakeFrame, 0)
 #endif

+ 12 - 13
lib/Backend/LinearScan.cpp

@@ -187,7 +187,7 @@ LinearScan::RegAlloc()
         }
         else if (instr->IsBranchInstr())
         {
-            if (this->func->HasTry() && this->func->DoOptimizeTryCatch())
+            if (this->func->HasTry() && this->func->DoOptimizeTry())
             {
                 this->ProcessEHRegionBoundary(instr);
             }
@@ -206,8 +206,7 @@ LinearScan::RegAlloc()
             if (this->currentRegion)
             {
                 RegionType curRegType = this->currentRegion->GetType();
-                Assert(curRegType != RegionTypeFinally); //Finally regions are not optimized yet
-                if (curRegType == RegionTypeTry || curRegType == RegionTypeCatch)
+                if (curRegType == RegionTypeTry || curRegType == RegionTypeCatch || curRegType == RegionTypeFinally)
                 {
                     this->func->hasBailoutInEHRegion = true;
                 }
@@ -1005,7 +1004,7 @@ LinearScan::NeedsWriteThrough(StackSym * sym)
 bool
 LinearScan::NeedsWriteThroughForEH(StackSym * sym)
 {
-    if (!this->func->HasTry() || !this->func->DoOptimizeTryCatch() || !sym->HasByteCodeRegSlot())
+    if (!this->func->HasTry() || !this->func->DoOptimizeTry() || !sym->HasByteCodeRegSlot())
     {
         return false;
     }
@@ -1351,7 +1350,7 @@ LinearScan::FillBailOutRecord(IR::Instr * instr)
     if (this->func->HasTry())
     {
         RegionType currentRegionType = this->currentRegion->GetType();
-        if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch)
+        if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch || currentRegionType == RegionTypeFinally)
         {
             bailOutInfo->bailOutRecord->ehBailoutData = this->currentRegion->ehBailoutData;
         }
@@ -2239,7 +2238,7 @@ LinearScan::RecordUse(Lifetime * lifetime, IR::Instr * instr, IR::RegOpnd * regO
     // have real accurate flow info for the later.
     if ((regOpnd && regOpnd->m_sym->IsConst())
           || (
-                 (this->func->HasTry() && !this->func->DoOptimizeTryCatch()) &&
+                 (this->func->HasTry() && !this->func->DoOptimizeTry()) &&
                  this->IsInLoop() &&
                  lifetime->lastUseLabel != this->lastLabel &&
                  this->liveOnBackEdgeSyms->Test(lifetime->sym->m_id) &&
@@ -2279,7 +2278,7 @@ void LinearScan::RecordLoopUse(Lifetime *lifetime, RegNum reg)
         return;
     }
 
-    if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
+    if (this->func->HasTry() && !this->func->DoOptimizeTry())
     {
         return;
     }
@@ -3122,8 +3121,8 @@ void
 LinearScan::ProcessEHRegionBoundary(IR::Instr * instr)
 {
     Assert(instr->IsBranchInstr());
-    Assert(instr->m_opcode != Js::OpCode::TryFinally); // finallys are not supported for optimization yet.
-    if (instr->m_opcode != Js::OpCode::TryCatch && instr->m_opcode != Js::OpCode::Leave)
+
+    if (instr->m_opcode != Js::OpCode::TryCatch && instr->m_opcode != Js::OpCode::TryFinally && instr->m_opcode != Js::OpCode::Leave)
     {
         return;
     }
@@ -3275,7 +3274,7 @@ LinearScan::InsertStores(Lifetime *lifetime, RegNum reg, IR::Instr *insertionIns
     uint localStoreCost = LinearScan::GetUseSpillCost(this->loopNest, (this->currentOpHelperBlock != nullptr));
 
     // Is it cheaper to spill all the defs we've seen so far or just insert a store at the current point?
-    if ((this->func->HasTry() && !this->func->DoOptimizeTryCatch()) || localStoreCost >= lifetime->allDefsCost)
+    if ((this->func->HasTry() && !this->func->DoOptimizeTry()) || localStoreCost >= lifetime->allDefsCost)
     {
         // Insert a store for each def point we've seen so far
         FOREACH_SLIST_ENTRY(IR::Instr *, instr, &(lifetime->defList))
@@ -3856,7 +3855,7 @@ LinearScan::GetUseSpillCost(uint loopNest, BOOL isInHelperBlock)
 void
 LinearScan::ProcessSecondChanceBoundary(IR::BranchInstr *branchInstr)
 {
-    if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
+    if (this->func->HasTry() && !this->func->DoOptimizeTry())
     {
         return;
     }
@@ -3946,7 +3945,7 @@ LinearScan::ProcessSecondChanceBoundaryHelper(IR::BranchInstr *branchInstr, IR::
 void
 LinearScan::ProcessSecondChanceBoundary(IR::LabelInstr *labelInstr)
 {
-    if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
+    if (this->func->HasTry() && !this->func->DoOptimizeTry())
     {
         return;
     }
@@ -4718,7 +4717,7 @@ IR::Instr * LinearScan::TryHoistLoad(IR::Instr *instr, Lifetime *lifetime)
         return insertInstr;
     }
 
-    if ((this->func->HasTry() && !this->func->DoOptimizeTryCatch()) || (this->currentRegion && this->currentRegion->GetType() != RegionTypeRoot))
+    if ((this->func->HasTry() && !this->func->DoOptimizeTry()) || (this->currentRegion && this->currentRegion->GetType() != RegionTypeRoot))
     {
         return insertInstr;
     }

+ 77 - 8
lib/Backend/Lower.cpp

@@ -2561,12 +2561,23 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
             instrPrev = m_lowererMD.LowerCatch(instr);
             break;
 
+        case Js::OpCode::Finally:
+            instr->Remove();
+            break;
+
         case Js::OpCode::LeaveNull:
-            instrPrev = m_lowererMD.LowerLeaveNull(instr);
+            if (this->m_func->IsSimpleJit() || !this->m_func->DoOptimizeTry())
+            {
+                instrPrev = m_lowererMD.LowerLeaveNull(instr);
+            }
+            else
+            {
+                instr->Remove();
+            }
             break;
 
         case Js::OpCode::Leave:
-            if (this->m_func->HasTry() && this->m_func->DoOptimizeTryCatch())
+            if (this->m_func->HasTry() && this->m_func->DoOptimizeTry())
             {
                 // Required in Register Allocator to mark region boundaries
                 break;
@@ -2578,6 +2589,10 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
             instrPrev = this->LowerBailOnException(instr);
             break;
 
+        case Js::OpCode::BailOnEarlyExit:
+            instrPrev = this->LowerBailOnEarlyExit(instr);
+            break;
+
         case Js::OpCode::RuntimeTypeError:
         case Js::OpCode::InlineRuntimeTypeError:
             this->LowerUnaryHelperMem(instr, IR::HelperOp_RuntimeTypeError);
@@ -12078,7 +12093,17 @@ Lowerer::LowerBailOnException(IR::Instr * instr)
 {
     Assert(instr->HasBailOutInfo());
     IR::Instr * instrPrev = instr->m_prev;
-    Assert(instrPrev->m_opcode == Js::OpCode::Catch);
+
+    this->GenerateBailOut(instr, nullptr, nullptr);
+
+    return instrPrev;
+}
+
+IR::Instr*
+Lowerer::LowerBailOnEarlyExit(IR::Instr * instr)
+{
+    Assert(instr->HasBailOutInfo());
+    IR::Instr * instrPrev = instr->m_prev;
 
     this->GenerateBailOut(instr, nullptr, nullptr);
 
@@ -19972,8 +19997,21 @@ Lowerer::EHBailoutPatchUp()
         if (this->currentRegion)
         {
             RegionType currentRegionType = this->currentRegion->GetType();
-            if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch)
+            if (currentRegionType == RegionTypeTry || currentRegionType == RegionTypeCatch || currentRegionType == RegionTypeFinally)
             {
+                if (this->currentRegion->IsNonExceptingFinally())
+                {
+                    Region * parent = this->currentRegion->GetParent();
+
+                    while (parent->IsNonExceptingFinally())
+                    {
+                        parent = parent->GetParent();
+                    }
+                    if (parent->GetType() == RegionTypeRoot)
+                    {
+                        continue;
+                    }
+                }
                 this->InsertReturnThunkForRegion(this->currentRegion, restoreReturnValueFromBailoutLabel);
                 if (instr->HasBailOutInfo())
                 {
@@ -23578,7 +23616,7 @@ Lowerer::ValidOpcodeAfterLower(IR::Instr* instr, Func * func)
 
     case Js::OpCode::Leave:
         Assert(!func->IsLoopBodyInTry());
-        Assert(func->HasTry() && func->DoOptimizeTryCatch());
+        Assert(func->HasTry() && func->DoOptimizeTry());
         return func && !func->isPostFinalLower; //Lowered in FinalLower phase
     };
 
@@ -24580,7 +24618,8 @@ Lowerer::LowerTry(IR::Instr* instr, bool tryCatch)
     instr->InsertBefore(setInstr);
     LowererMD::Legalize(setInstr);
 
-    return m_lowererMD.LowerTry(instr, tryCatch ? IR::HelperOp_TryCatch : IR::HelperOp_TryFinally);
+    return m_lowererMD.LowerTry(instr, tryCatch ? IR::HelperOp_TryCatch : ((this->m_func->IsSimpleJit() && !this->m_func->hasBailout) || !this->m_func->DoOptimizeTry()) ?
+        IR::HelperOp_TryFinallySimpleJit : IR::HelperOp_TryFinally);
 }
 
 void
@@ -24607,7 +24646,7 @@ void
 Lowerer::InsertReturnThunkForRegion(Region* region, IR::LabelInstr* restoreLabel)
 {
     Assert(this->m_func->isPostLayout);
-    Assert(region->GetType() == RegionTypeTry || region->GetType() == RegionTypeCatch);
+    Assert(region->GetType() == RegionTypeTry || region->GetType() == RegionTypeCatch || region->GetType() == RegionTypeFinally);
 
     if (!region->returnThunkEmitted)
     {
@@ -24625,7 +24664,37 @@ Lowerer::InsertReturnThunkForRegion(Region* region, IR::LabelInstr* restoreLabel
         }
 
         IR::LabelOpnd * continuationAddr;
-        if (region->GetParent()->GetType() != RegionTypeRoot)
+        // We insert return thunk to the region's parent return thunk label
+        // For non exception finallys, we do not need a return thunk
+        // Because, we are not calling none xception finallys from within amd64_callWithFakeFrame
+        // But a non exception finally maybe within other eh regions that need a return thunk
+        if (region->IsNonExceptingFinally())
+        {
+            Assert(region->GetParent()->GetType() != RegionTypeRoot);
+            Region *ancestor = region->GetFirstAncestorOfNonExceptingFinallyParent();
+            Assert(ancestor && !ancestor->IsNonExceptingFinally());
+            if (ancestor->GetType() != RegionTypeRoot)
+            {
+                continuationAddr = IR::LabelOpnd::New(ancestor->GetBailoutReturnThunkLabel(), this->m_func);
+            }
+            else
+            {
+                continuationAddr = IR::LabelOpnd::New(restoreLabel, this->m_func);
+            }
+        }
+        else if (region->GetParent()->IsNonExceptingFinally())
+        {
+            Region *ancestor = region->GetFirstAncestorOfNonExceptingFinally();
+            if (ancestor && ancestor->GetType() != RegionTypeRoot)
+            {
+                continuationAddr = IR::LabelOpnd::New(ancestor->GetBailoutReturnThunkLabel(), this->m_func);
+            }
+            else
+            {
+                continuationAddr = IR::LabelOpnd::New(restoreLabel, this->m_func);
+            }
+        }
+        else if (region->GetParent()->GetType() != RegionTypeRoot)
         {
             continuationAddr = IR::LabelOpnd::New(region->GetParent()->GetBailoutReturnThunkLabel(), this->m_func);
         }

+ 1 - 0
lib/Backend/Lower.h

@@ -474,6 +474,7 @@ private:
     IR::Instr *     LowerBailForDebugger(IR::Instr* instr, bool isInsideHelper = false);
     IR::Instr *     LowerBailOnException(IR::Instr* instr);
     void            LowerReinterpretPrimitive(IR::Instr* instr);
+    IR::Instr *     LowerBailOnEarlyExit(IR::Instr* instr);
 
     void            LowerOneBailOutKind(IR::Instr *const instr, const IR::BailOutKind bailOutKindToLower, const bool isInHelperBlock, const bool preserveBailOutKindInInstr = false);
 

+ 1 - 2
lib/Backend/LowerMDShared.cpp

@@ -370,13 +370,12 @@ LowererMD::LowerTry(IR::Instr *tryInstr, IR::JnHelperMethod helperMethod)
     // Arg 5: ScriptContext
     this->m_lowerer->LoadScriptContext(tryAddr);
 
-    if (tryInstr->m_opcode == Js::OpCode::TryCatch)
+    if (tryInstr->m_opcode == Js::OpCode::TryCatch || this->m_func->DoOptimizeTry())
     {
         // Arg 4 : hasBailedOutOffset
         IR::Opnd * hasBailedOutOffset = IR::IntConstOpnd::New(this->m_func->m_hasBailedOutSym->m_offset, TyInt32, this->m_func);
         this->LoadHelperArgument(tryAddr, hasBailedOutOffset);
     }
-
 #ifdef _M_X64
     // Arg: args size
     IR::RegOpnd *argsSizeOpnd = IR::RegOpnd::New(TyMachReg, m_func);

+ 47 - 2
lib/Backend/Region.cpp

@@ -24,11 +24,11 @@ Region::AllocateEHBailoutData(Func * func, IR::Instr * tryInstr)
 {
     if (this->GetType() == RegionTypeRoot)
     {
-        this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, -1 /*nestingDepth*/, 0 /*catchOffset*/, nullptr /*parent*/);
+        this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, -1 /*nestingDepth*/, 0 /*catchOffset*/, 0 /*finallyOffset*/, Js::HandlerType::HT_None, nullptr /*parent*/);
     }
     else
     {
-        this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, this->GetParent()->ehBailoutData->nestingDepth + 1, 0, this->GetParent()->ehBailoutData);
+        this->ehBailoutData = NativeCodeDataNew(func->GetNativeCodeDataAllocator(), Js::EHBailoutData, this->GetParent()->ehBailoutData->nestingDepth + 1, 0, 0, Js::HandlerType::HT_None, this->GetParent()->ehBailoutData);
         if (this->GetType() == RegionTypeTry)
         {
             Assert(tryInstr);
@@ -36,6 +36,18 @@ Region::AllocateEHBailoutData(Func * func, IR::Instr * tryInstr)
             {
                 this->ehBailoutData->catchOffset = tryInstr->AsBranchInstr()->GetTarget()->GetByteCodeOffset(); // ByteCode offset of the Catch
             }
+            else if (tryInstr->m_opcode == Js::OpCode::TryFinally)
+            {
+                this->ehBailoutData->finallyOffset = tryInstr->AsBranchInstr()->GetTarget()->GetByteCodeOffset(); // ByteCode offset of the Finally
+            }
+        }
+        else if (this->GetType() == RegionTypeCatch)
+        {
+            this->ehBailoutData->ht = Js::HandlerType::HT_Catch;
+        }
+        else
+        {
+            this->ehBailoutData->ht = Js::HandlerType::HT_Finally;
         }
     }
 }
@@ -54,3 +66,36 @@ Region::GetSelfOrFirstTryAncestor()
     }
     return this->selfOrFirstTryAncestor;
 }
+
+// Return the first ancestor of the region's parent which is not a non exception finally
+Region *
+Region::GetFirstAncestorOfNonExceptingFinallyParent()
+{
+    Region * ancestor = this->GetParent();
+    while (ancestor && ancestor->IsNonExceptingFinally())
+    {
+        ancestor = ancestor->GetParent();
+    }
+    // ancestor is the first ancestor which is not a non exception finally
+    Assert(ancestor && !ancestor->IsNonExceptingFinally());
+    // If the ancestor's parent is a non exception finally, recurse
+    if (ancestor && ancestor->GetType() != RegionTypeRoot && ancestor->GetParent()->IsNonExceptingFinally())
+    {
+        return ancestor->GetParent()->GetFirstAncestorOfNonExceptingFinallyParent();
+    }
+
+    return ancestor ? (ancestor->GetType() == RegionTypeRoot ? ancestor : ancestor->GetParent()) : nullptr;
+}
+
+// Return first ancestor which is not a non exception finally
+Region *
+Region::GetFirstAncestorOfNonExceptingFinally()
+{
+    Region *ancestor = this->GetParent();
+    while (ancestor->IsNonExceptingFinally())
+    {
+        ancestor = ancestor->GetParent();
+    }
+    return ancestor;
+}
+

+ 27 - 4
lib/Backend/Region.h

@@ -17,7 +17,7 @@ class Region
 {
 public:
     Region() : type(RegionTypeInvalid),
-               parent(NULL), matchingTryRegion(nullptr), matchingCatchRegion(nullptr), matchingFinallyRegion(nullptr), selfOrFirstTryAncestor(nullptr),
+               parent(NULL), matchingTryRegion(nullptr), matchingCatchRegion(nullptr), matchingFinallyOnExceptRegion(nullptr), matchingFinallyOnNoExceptRegion(nullptr), selfOrFirstTryAncestor(nullptr),
                start(NULL), end(NULL),
                writeThroughSymbolsSet(nullptr),
                ehBailoutData(nullptr), bailoutReturnThunkLabel(nullptr), returnThunkEmitted(false),
@@ -37,8 +37,25 @@ public:
     inline Region * GetMatchingCatchRegion() const      { return this->matchingCatchRegion; }
     inline void SetMatchingCatchRegion(Region* catchRegion) { this->matchingCatchRegion = catchRegion; }
 
-    inline Region * GetMatchingFinallyRegion() const    { return this->matchingFinallyRegion; }
-    inline void SetMatchingFinallyRegion(Region* finallyRegion) { this->matchingFinallyRegion = finallyRegion; }
+    inline Region * GetMatchingFinallyRegion(bool isExcept) const
+    {
+        return isExcept ? this->matchingFinallyOnExceptRegion : this->matchingFinallyOnNoExceptRegion;
+    }
+    inline void SetMatchingFinallyRegion(Region* finallyRegion, bool isExcept)
+    {
+        if (isExcept)
+        {
+            this->matchingFinallyOnExceptRegion = finallyRegion;
+        }
+        else
+        {
+            this->matchingFinallyOnNoExceptRegion = finallyRegion;
+        }
+    }
+    bool IsNonExceptingFinally()
+    {
+        return (this->GetType() == RegionTypeFinally && this->GetMatchingTryRegion()->GetMatchingFinallyRegion(false) == this);
+    }
 
     inline IR::Instr * GetStart() const                 { return this->start; }
     inline void SetStart(IR::Instr * instr)             { this->start = instr; }
@@ -49,13 +66,19 @@ public:
     inline void SetExceptionObjectSym(StackSym * sym)   { this->exceptionObjectSym = sym; }
     void   AllocateEHBailoutData(Func * func, IR::Instr * tryInstr);
     Region * GetSelfOrFirstTryAncestor();
+    Region * GetFirstAncestorOfNonExceptingFinallyParent();
+    Region * GetFirstAncestorOfNonExceptingFinally();
 
 private:
     RegionType                      type;
     Region *                        parent;
     Region *                        matchingTryRegion;
     Region *                        matchingCatchRegion;
-    Region *                        matchingFinallyRegion;
+    Region *                        matchingFinallyOnExceptRegion;
+    Region *                        matchingFinallyOnNoExceptRegion;
+    // We need to mark a non-expecting finally region we execute in the JIT, as in EH region.
+    // We can bailout from the non excepting EH region, in that case we need ehBailoutData to reconstruct eh frames in the interpreter
+
     Region *                        selfOrFirstTryAncestor; // = self, if try region, otherwise
                                                             // = first try ancestor
     IR::Instr *                     start;

+ 3 - 3
lib/Backend/SccLiveness.cpp

@@ -309,7 +309,7 @@ SCCLiveness::Build()
 
         // Check for lifetimes that have been extended such that they now span multiple regions.
         this->curRegion->SetEnd(this->func->m_exitInstr);
-        if (this->func->HasTry() && !this->func->DoOptimizeTryCatch())
+        if (this->func->HasTry() && !this->func->DoOptimizeTry())
         {
             FOREACH_SLIST_ENTRY(Lifetime *, lifetime, &this->lifetimeList)
             {
@@ -527,7 +527,7 @@ SCCLiveness::ProcessStackSymUse(StackSym * stackSym, IR::Instr * instr, int usag
     }
     else
     {
-        if (lifetime->region != this->curRegion && !this->func->DoOptimizeTryCatch())
+        if (lifetime->region != this->curRegion && !this->func->DoOptimizeTry())
         {
             lifetime->dontAllocate = true;
         }
@@ -628,7 +628,7 @@ SCCLiveness::ProcessRegDef(IR::RegOpnd *regDef, IR::Instr *instr)
 
         ExtendLifetime(lifetime, instr);
 
-        if (lifetime->region != this->curRegion && !this->func->DoOptimizeTryCatch())
+        if (lifetime->region != this->curRegion && !this->func->DoOptimizeTry())
         {
             lifetime->dontAllocate = true;
         }

+ 1 - 1
lib/Backend/amd64/LowererMDArch.cpp

@@ -3148,7 +3148,7 @@ LowererMDArch::FinalLower()
             break;
 
         case Js::OpCode::Leave:
-            Assert(this->m_func->DoOptimizeTryCatch() && !this->m_func->IsLoopBodyInTry());
+            Assert(this->m_func->DoOptimizeTry() && !this->m_func->IsLoopBodyInTry());
             instrPrev = this->lowererMD->LowerLeave(instr, instr->AsBranchInstr()->GetTarget(), true /*fromFinalLower*/);
             break;
 

+ 3 - 3
lib/Backend/arm/LowerMD.cpp

@@ -1524,7 +1524,7 @@ LowererMD::LowerExitInstr(IR::ExitInstr * exitInstr)
     int32 stackAdjust;
     if (hasTry)
     {
-        if (this->m_func->DoOptimizeTryCatch())
+        if (this->m_func->DoOptimizeTry())
         {
             this->EnsureEpilogLabel();
         }
@@ -1725,7 +1725,7 @@ LowererMD::LowerTry(IR::Instr * tryInstr, IR::JnHelperMethod helperMethod)
     // Arg 7: ScriptContext
     this->m_lowerer->LoadScriptContext(tryAddr);
 
-    if (tryInstr->m_opcode == Js::OpCode::TryCatch)
+    if (tryInstr->m_opcode == Js::OpCode::TryCatch || this->m_func->DoOptimizeTry())
     {
         // Arg 6 : hasBailedOutOffset
         IR::Opnd * hasBailedOutOffset = IR::IntConstOpnd::New(this->m_func->m_hasBailedOutSym->m_offset, TyInt32, this->m_func);
@@ -8688,7 +8688,7 @@ LowererMD::FinalLower()
             switch (instr->m_opcode)
             {
             case Js::OpCode::Leave:
-                Assert(this->m_func->DoOptimizeTryCatch() && !this->m_func->IsLoopBodyInTry());
+                Assert(this->m_func->DoOptimizeTry() && !this->m_func->IsLoopBodyInTry());
                 instrPrev = this->LowerLeave(instr, instr->AsBranchInstr()->GetTarget(), true /*fromFinalLower*/);
                 break;
             }

+ 1 - 1
lib/Backend/i386/LowererMDArch.cpp

@@ -3982,7 +3982,7 @@ LowererMDArch::FinalLower()
         switch (instr->m_opcode)
         {
         case Js::OpCode::Leave:
-            Assert(this->m_func->DoOptimizeTryCatch() && !this->m_func->IsLoopBodyInTry());
+            Assert(this->m_func->DoOptimizeTry() && !this->m_func->IsLoopBodyInTry());
             this->lowererMD->LowerLeave(instr, instr->AsBranchInstr()->GetTarget(), true /*fromFinalLower*/);
             break;
 

+ 1 - 0
lib/Common/ConfigFlagsList.h

@@ -88,6 +88,7 @@ PHASE(All)
             PHASE(InlinerConstFold)
     PHASE(ExecBOIFastPath)
         PHASE(FGBuild)
+            PHASE(OptimizeTryFinally)
             PHASE(RemoveBreakBlock)
             PHASE(TailDup)
         PHASE(FGPeeps)

+ 11 - 0
lib/Runtime/Base/ThreadContext.h

@@ -448,6 +448,7 @@ public:
 #endif
 
 private:
+    Js::JavascriptExceptionObject * pendingFinallyException;
     bool noScriptScope;
 
     Js::DebugManager * debugManager;
@@ -1261,6 +1262,16 @@ public:
     void SetNoScriptScope(bool noScriptScope) { this->noScriptScope = noScriptScope; }
     bool IsNoScriptScope() { return this->noScriptScope; }
 
+    void SetPendingFinallyException(Js::JavascriptExceptionObject * exceptionObj)
+    {
+        pendingFinallyException = exceptionObj;
+    }
+
+    Js::JavascriptExceptionObject * GetPendingFinallyException()
+    {
+        return pendingFinallyException;
+    }
+
     Js::EntryPointInfo ** RegisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo * entryPoint);
     void UnregisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo ** entryPoint);
     void RegisterProtoInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId);

+ 2 - 0
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -6741,6 +6741,7 @@ void EmitTopLevelFinally(Js::ByteCodeLabel finallyLabel,
 
     byteCodeGenerator->Writer()->Br(afterFinallyBlockLabel);
     byteCodeGenerator->Writer()->MarkLabel(finallyLabel);
+    byteCodeGenerator->Writer()->Empty(Js::OpCode::Finally);
 
     ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, yieldExceptionLocation, yieldOffsetLocation);
     if (isCoroutine)
@@ -11873,6 +11874,7 @@ void Emit(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator, FuncInfo *func
 
         byteCodeGenerator->Writer()->Br(pnode->sxStmt.breakLabel);
         byteCodeGenerator->Writer()->MarkLabel(finallyLabel);
+        byteCodeGenerator->Writer()->Empty(Js::OpCode::Finally);
 
         ByteCodeGenerator::TryScopeRecord tryRecForFinally(Js::OpCode::ResumeFinally, finallyLabel, regException, regOffset);
         if (funcInfo->byteCodeFunction->IsCoroutine())

+ 3 - 1
lib/Runtime/ByteCode/OpCodes.h

@@ -556,9 +556,10 @@ MACRO(                  TryCatch,           Br,             OpSideEffect)
 MACRO(                  TryFinally,         Br,             OpSideEffect|OpPostOpDbgBailOut)
 MACRO_EXTEND_WMS(       TryFinallyWithYield, BrReg2,         OpSideEffect|OpPostOpDbgBailOut)
 MACRO_WMS(              Catch,              Reg1,           OpSideEffect)
+MACRO_EXTEND(           Finally,            Empty,          OpSideEffect)
 MACRO_EXTEND(           ResumeCatch,        Empty,          OpSideEffect)
 MACRO_EXTEND_WMS(       ResumeFinally,      BrReg2,         OpSideEffect)
-MACRO(                  LeaveNull,          Empty,          OpSideEffect|OpNoFallThrough)
+MACRO(                  LeaveNull,          Empty,          OpSideEffect)
 MACRO(                  Leave,              Empty,          OpSideEffect|OpNoFallThrough)
 
 MACRO_BACKEND_ONLY(     InlineRuntimeTypeError,        W1,             OpSideEffect|OpPostOpDbgBailOut)     // Throws TypeError at runtime.
@@ -641,6 +642,7 @@ MACRO_BACKEND_ONLY(     BailOnNotArray,              Empty,          OpBailOutRe
 MACRO_BACKEND_ONLY(     BailForDebugger,             Empty,          OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpSideEffect)    // Bail out so that we can continue the function under debugger. Disable optimizations for this instr so that it's not moved.
 MACRO_BACKEND_ONLY(     BailOnNotBuiltIn,            Empty,          OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpCanCSE)
 MACRO_BACKEND_ONLY(     BailOnException,             Empty,          OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpDeadFallThrough)
+MACRO_BACKEND_ONLY(     BailOnEarlyExit,             Empty,          OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpDeadFallThrough)
 MACRO_BACKEND_ONLY(     BailOnTaggedValue,           Empty,          OpBailOutRec|OpTempNumberSources|OpTempObjectSources|OpCanCSE)
 MACRO_BACKEND_ONLY(     BytecodeArgOutCapture,       Empty,          OpTempNumberTransfer|OpTempObjectTransfer|OpNonIntTransfer) // Represents snapshotting of bytecode ArgOut_A in backend for purpose of bailout
 MACRO_BACKEND_ONLY(     BytecodeArgOutUse,           Empty,          OpTempNumberSources | OpTempObjectSources) // Represents bytecode ArgOut_A use in the backend to keep args alive for the globopt

+ 12 - 2
lib/Runtime/Language/EHBailoutData.h

@@ -6,20 +6,30 @@
 
 namespace Js
 {
+    enum HandlerType
+    {
+        HT_None,
+        HT_Catch,
+        HT_Finally
+    };
     class EHBailoutData
     {
     public:
         int32 nestingDepth;
         int32 catchOffset;
+        int32 finallyOffset;
+        HandlerType ht;
         EHBailoutData * parent;
         EHBailoutData * child;
 
     public:
-        EHBailoutData() : nestingDepth(-1), catchOffset(0), parent(nullptr), child(nullptr) {}
-        EHBailoutData(int32 nestingDepth, int32 catchOffset, EHBailoutData * parent)
+        EHBailoutData() : nestingDepth(-1), catchOffset(0), finallyOffset(0), parent(nullptr), child(nullptr),  ht(HT_None) {}
+        EHBailoutData(int32 nestingDepth, int32 catchOffset, int32 finallyOffset, HandlerType ht, EHBailoutData * parent)
         {
             this->nestingDepth = nestingDepth;
             this->catchOffset = catchOffset;
+            this->finallyOffset = finallyOffset;
+            this->ht = ht;
             this->parent = parent;
             this->child = nullptr;
         }

+ 171 - 41
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -3374,7 +3374,7 @@ namespace Js
                     topLevelEHBailoutData->parent->child = topLevelEHBailoutData;
                     topLevelEHBailoutData = topLevelEHBailoutData->parent;
                 }
-                ProcessTryCatchBailout(topLevelEHBailoutData, this->ehBailoutData->nestingDepth);
+                ProcessTryHandlerBailout(topLevelEHBailoutData, this->ehBailoutData->nestingDepth);
                 m_flags &= ~Js::InterpreterStackFrameFlags_ProcessingBailOutFromEHCode;
                 this->ehBailoutData = nullptr;
             }
@@ -6640,8 +6640,6 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
     int InterpreterStackFrame::ProcessFinally()
     {
         this->nestedFinallyDepth++;
-        // mark the stackFrame as 'in finally block'
-        this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock;
 
         int newOffset = 0;
         if (this->IsInDebugMode())
@@ -6653,20 +6651,16 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
             newOffset = ::Math::PointerCastToIntegral<int>(this->Process());
         }
 
-        if (--this->nestedFinallyDepth == -1)
-        {
-            // unmark the stackFrame as 'in finally block'
-            this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock;
-        }
         return newOffset;
     }
 
-    void InterpreterStackFrame::ProcessTryCatchBailout(EHBailoutData * ehBailoutData, uint32 tryNestingDepth)
+    void InterpreterStackFrame::ProcessTryHandlerBailout(EHBailoutData * ehBailoutData, uint32 tryNestingDepth)
     {
         int catchOffset = ehBailoutData->catchOffset;
+        int finallyOffset = ehBailoutData->finallyOffset;
         Js::JavascriptExceptionObject* exception = NULL;
 
-        if (catchOffset != 0)
+        if (catchOffset != 0 || finallyOffset != 0)
         {
             try
             {
@@ -6676,7 +6670,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
 
                 if (tryNestingDepth != 0)
                 {
-                    this->ProcessTryCatchBailout(ehBailoutData->child, --tryNestingDepth);
+                    this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth);
                 }
 
                 Js::JavascriptExceptionOperators::AutoCatchHandlerExists autoCatchHandlerExists(scriptContext);
@@ -6711,7 +6705,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
                 exception = err.GetAndClear();
             }
         }
-        else
+        else if (ehBailoutData->ht == HandlerType::HT_Catch)
         {
             this->nestedCatchDepth++;
             // mark the stackFrame as 'in catch block'
@@ -6719,7 +6713,7 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
 
             if (tryNestingDepth != 0)
             {
-                this->ProcessTryCatchBailout(ehBailoutData->child, --tryNestingDepth);
+                this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth);
             }
             this->ProcessCatch();
 
@@ -6730,6 +6724,40 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
             }
             return;
         }
+        else
+        {
+            Assert(ehBailoutData->ht == HandlerType::HT_Finally);
+            this->nestedFinallyDepth++;
+            // mark the stackFrame as 'in finally block'
+            this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock;
+
+            if (tryNestingDepth != 0)
+            {
+                this->ProcessTryHandlerBailout(ehBailoutData->child, --tryNestingDepth);
+            }
+
+            int finallyEndOffset = this->ProcessFinally();
+
+            if (--this->nestedFinallyDepth == -1)
+            {
+                // unmark the stackFrame as 'in finally block'
+                this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock;
+            }
+
+            volatile Js::JavascriptExceptionObject * exceptionObj = this->scriptContext->GetThreadContext()->GetPendingFinallyException();
+            this->scriptContext->GetThreadContext()->SetPendingFinallyException(nullptr);
+            // Finally exited with LeaveNull, We don't throw for early returns
+            if (finallyEndOffset == 0 && exceptionObj)
+            {
+                JavascriptExceptionOperators::DoThrow(const_cast<Js::JavascriptExceptionObject *>(exceptionObj), scriptContext);
+            }
+            if (finallyEndOffset != 0)
+            {
+                m_reader.SetCurrentOffset(finallyEndOffset);
+            }
+
+            return;
+        }
 
         if (--this->nestedTryDepth == -1)
         {
@@ -6746,41 +6774,124 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
                 JavascriptExceptionOperators::DoThrow(exception, scriptContext);
             }
 
-            exception = exception->CloneIfStaticExceptionObject(scriptContext);
-            // We've got a JS exception. Grab the exception object and assign it to the
-            // catch object's location, then call the handler (i.e., we consume the Catch op here).
-            Var catchObject = exception->GetThrownObject(scriptContext);
+            if (catchOffset != 0)
+            {
+                exception = exception->CloneIfStaticExceptionObject(scriptContext);
+                // We've got a JS exception. Grab the exception object and assign it to the
+                // catch object's location, then call the handler (i.e., we consume the Catch op here).
+                Var catchObject = exception->GetThrownObject(scriptContext);
 
-            m_reader.SetCurrentOffset(catchOffset);
+                m_reader.SetCurrentOffset(catchOffset);
 
-            LayoutSize layoutSize;
-            OpCode catchOp = m_reader.ReadOp(layoutSize);
+                LayoutSize layoutSize;
+                OpCode catchOp = m_reader.ReadOp(layoutSize);
 #ifdef BYTECODE_BRANCH_ISLAND
-            if (catchOp == Js::OpCode::BrLong)
-            {
-                Assert(layoutSize == SmallLayout);
-                auto playoutBrLong = m_reader.BrLong();
-                m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
-                catchOp = m_reader.ReadOp(layoutSize);
-            }
+                if (catchOp == Js::OpCode::BrLong)
+                {
+                    Assert(layoutSize == SmallLayout);
+                    auto playoutBrLong = m_reader.BrLong();
+                    m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
+                    catchOp = m_reader.ReadOp(layoutSize);
+                }
 #endif
-            AssertMsg(catchOp == OpCode::Catch, "Catch op not found at catch offset");
-            RegSlot reg = layoutSize == SmallLayout ? m_reader.Reg1_Small()->R0 :
-                layoutSize == MediumLayout ? m_reader.Reg1_Medium()->R0 : m_reader.Reg1_Large()->R0;
-            SetReg(reg, catchObject);
+                AssertMsg(catchOp == OpCode::Catch, "Catch op not found at catch offset");
+                RegSlot reg = layoutSize == SmallLayout ? m_reader.Reg1_Small()->R0 :
+                    layoutSize == MediumLayout ? m_reader.Reg1_Medium()->R0 : m_reader.Reg1_Large()->R0;
+                SetReg(reg, catchObject);
 
-            ResetOut();
+                ResetOut();
 
-            this->nestedCatchDepth++;
-            // mark the stackFrame as 'in catch block'
-            this->m_flags |= InterpreterStackFrameFlags_WithinCatchBlock;
+                this->nestedCatchDepth++;
+                // mark the stackFrame as 'in catch block'
+                this->m_flags |= InterpreterStackFrameFlags_WithinCatchBlock;
 
-            this->ProcessCatch();
+                this->ProcessCatch();
 
-            if (--this->nestedCatchDepth == -1)
+                if (--this->nestedCatchDepth == -1)
+                {
+                    // unmark the stackFrame as 'in catch block'
+                    this->m_flags &= ~InterpreterStackFrameFlags_WithinCatchBlock;
+                }
+            }
+            else
             {
-                // unmark the stackFrame as 'in catch block'
-                this->m_flags &= ~InterpreterStackFrameFlags_WithinCatchBlock;
+                Assert(finallyOffset != 0);
+                exception = exception->CloneIfStaticExceptionObject(scriptContext);
+
+                m_reader.SetCurrentOffset(finallyOffset);
+
+                ResetOut();
+
+                this->nestedFinallyDepth++;
+                // mark the stackFrame as 'in finally block'
+                this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock;
+
+                LayoutSize layoutSize;
+                OpCode finallyOp = m_reader.ReadOp(layoutSize);
+#ifdef BYTECODE_BRANCH_ISLAND
+                if (finallyOp == Js::OpCode::BrLong)
+                {
+                    Assert(layoutSize == SmallLayout);
+                    auto playoutBrLong = m_reader.BrLong();
+                    m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
+                    finallyOp = m_reader.ReadOp(layoutSize);
+                }
+#endif
+                Assert(finallyOp == Js::OpCode::Finally);
+
+                int finallyEndOffset = this->ProcessFinally();
+
+                if (--this->nestedFinallyDepth == -1)
+                {
+                    // unmark the stackFrame as 'in finally block'
+                    this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock;
+                }
+                if (finallyEndOffset == 0)
+                {
+                    JavascriptExceptionOperators::DoThrow(exception, scriptContext);
+                }
+                m_reader.SetCurrentOffset(finallyEndOffset);
+            }
+        }
+        else
+        {
+            if (finallyOffset != 0)
+            {
+                int currOffset = m_reader.GetCurrentOffset();
+
+                m_reader.SetCurrentOffset(finallyOffset);
+
+                ResetOut();
+
+                this->nestedFinallyDepth++;
+
+                // mark the stackFrame as 'in finally block'
+                this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock;
+
+                LayoutSize layoutSize;
+                OpCode finallyOp = m_reader.ReadOp(layoutSize);
+#ifdef BYTECODE_BRANCH_ISLAND
+                if (finallyOp == Js::OpCode::BrLong)
+                {
+                    Assert(layoutSize == SmallLayout);
+                    auto playoutBrLong = m_reader.BrLong();
+                    m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
+                    finallyOp = m_reader.ReadOp(layoutSize);
+                }
+#endif
+                Assert(finallyOp == Js::OpCode::Finally);
+
+                int finallyEndOffset = this->ProcessFinally();
+
+                if (--this->nestedFinallyDepth == -1)
+                {
+                    // unmark the stackFrame as 'in finally block'
+                    this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock;
+                }
+                if (finallyEndOffset == 0)
+                {
+                    m_reader.SetCurrentOffset(currOffset);
+                }
             }
         }
     }
@@ -6940,9 +7051,30 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
         m_reader.SetCurrentRelativeOffset(ip, jumpOffset);
 
         RestoreSp();
+        // mark the stackFrame as 'in finally block'
+        this->m_flags |= InterpreterStackFrameFlags_WithinFinallyBlock;
+
+        LayoutSize layoutSize;
+        OpCode finallyOp = m_reader.ReadOp(layoutSize);
+#ifdef BYTECODE_BRANCH_ISLAND
+        if (finallyOp == Js::OpCode::BrLong)
+        {
+            Assert(layoutSize == SmallLayout);
+            auto playoutBrLong = m_reader.BrLong();
+            m_reader.SetCurrentRelativeOffset((const byte *)(playoutBrLong + 1), playoutBrLong->RelativeJumpOffset);
+            finallyOp = m_reader.ReadOp(layoutSize);
+        }
+#endif
+        AssertMsg(finallyOp == OpCode::Finally, "Finally op not found at catch offset");
 
         newOffset = this->ProcessFinally();
 
+        if (--this->nestedFinallyDepth == -1)
+        {
+            // unmark the stackFrame as 'in finally block'
+            this->m_flags &= ~InterpreterStackFrameFlags_WithinFinallyBlock;
+        }
+
         bool endOfFinallyBlock = newOffset == 0;
         if (endOfFinallyBlock)
         {
@@ -6954,7 +7086,6 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
             // Finally seized the flow with a jump out of its scope. Resume at the jump target and
             // force the runtime to return to this frame without executing the catch.
             m_reader.SetCurrentOffset(newOffset);
-
             return;
         }
 
@@ -7002,7 +7133,6 @@ const byte * InterpreterStackFrame::OP_ProfiledLoopBodyStart(const byte * ip)
             // Finally seized the flow with a jump out of its scope. Resume at the jump target and
             // force the runtime to return to this frame without executing the catch.
             m_reader.SetCurrentOffset(newOffset);
-
             return;
         }
 

+ 1 - 1
lib/Runtime/Language/InterpreterStackFrame.h

@@ -706,7 +706,7 @@ namespace Js
         void OP_TryCatch(const unaligned OpLayoutBr* playout);
         void ProcessCatch();
         int ProcessFinally();
-        void ProcessTryCatchBailout(EHBailoutData * innermostEHBailoutData, uint32 tryNestingDepth);
+        void ProcessTryHandlerBailout(EHBailoutData * innermostEHBailoutData, uint32 tryNestingDepth);
         void OP_TryFinally(const unaligned OpLayoutBr* playout);
         void OP_TryFinallyWithYield(const byte* ip, Js::JumpOffset jumpOffset, Js::RegSlot regException, Js::RegSlot regOffset);
         void OP_ResumeCatch();

+ 244 - 8
lib/Runtime/Language/JavascriptExceptionOperators.cpp

@@ -118,10 +118,10 @@ namespace Js
                                                       void          *frame,
                                                       size_t         spillSize,
                                                       size_t         argsSize,
+                                                      int            hasBailedOutOffset,
                                                       ScriptContext *scriptContext)
     {
         void                      *tryContinuation     = nullptr;
-        void                      *finallyContinuation = nullptr;
         JavascriptExceptionObject *exception           = nullptr;
 
         PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize);
@@ -141,6 +141,47 @@ namespace Js
             exception = exception->CloneIfStaticExceptionObject(scriptContext);
         }
 
+        if (exception)
+        {
+            bool hasBailedOut = *(bool*)((char*)frame + hasBailedOutOffset); // stack offsets are negative
+            if (hasBailedOut)
+            {
+                // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
+                // it so happens that this catch was on the stack and caught the exception.
+                // Re-throw!
+                JavascriptExceptionOperators::DoThrow(exception, scriptContext);
+            }
+            // MGTODO : We need to set the exception object, so that we can access in the interpreter, better way out ?
+            scriptContext->GetThreadContext()->SetPendingFinallyException(exception);
+            void *continuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize, exception);
+            return continuation;
+        }
+
+        return tryContinuation;
+    }
+
+    void * JavascriptExceptionOperators::OP_TryFinallySimpleJit(void * tryAddr, void * finallyAddr, void * frame, size_t spillSize, size_t argsSize, ScriptContext * scriptContext)
+    {
+        void                      *tryContinuation = nullptr;
+        void                      *finallyContinuation = nullptr;
+        JavascriptExceptionObject *exception           = nullptr;
+
+        PROBE_STACK(scriptContext, Constants::MinStackDefault + spillSize + argsSize);
+        try
+        {
+            tryContinuation = amd64_CallWithFakeFrame(tryAddr, frame, spillSize, argsSize);
+        }
+        catch (const Js::JavascriptException& err)
+        {
+            exception = err.GetAndClear();
+        }
+
+        if (exception)
+        {
+            // Clone static exception object early in case finally block overwrites it
+            exception = exception->CloneIfStaticExceptionObject(scriptContext);
+        }
+
         finallyContinuation = amd64_CallWithFakeFrame(finallyAddr, frame, spillSize, argsSize);
         if (finallyContinuation)
         {
@@ -154,6 +195,7 @@ namespace Js
 
         return tryContinuation;
     }
+
 #elif defined(_M_ARM32_OR_ARM64)
 
     void *JavascriptExceptionOperators::OP_TryCatch(
@@ -213,10 +255,10 @@ namespace Js
         void *framePtr,
         void *localsPtr,
         size_t argsSize,
+        int hasBailedOutOffset,
         ScriptContext *scriptContext)
     {
         void                      *tryContinuation     = nullptr;
-        void                      *finallyContinuation = nullptr;
         JavascriptExceptionObject *exception           = nullptr;
 
         PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize);
@@ -234,6 +276,57 @@ namespace Js
             exception = err.GetAndClear();
         }
 
+        if (exception)
+        {
+            // Clone static exception object early in case finally block overwrites it
+            exception = exception->CloneIfStaticExceptionObject(scriptContext);
+            bool hasBailedOut = *(bool*)((char*)localsPtr + hasBailedOutOffset); // stack offsets are sp relative
+            if (hasBailedOut)
+            {
+                // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
+                // it so happens that this catch was on the stack and caught the exception.
+                // Re-throw!
+                JavascriptExceptionOperators::DoThrow(exception, scriptContext);
+            }
+            scriptContext->GetThreadContext()->SetPendingFinallyException(exception);
+#if defined(_M_ARM)
+            void * finallyContinuation = arm_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
+#elif defined(_M_ARM64)
+            void * finallyContinuation = arm64_CallEhFrame(finallyAddr, framePtr, localsPtr, argsSize);
+#endif
+            return finallyContinuation;
+        }
+
+        return tryContinuation;
+    }
+
+    void *JavascriptExceptionOperators::OP_TryFinallySimpleJit(
+        void *tryAddr,
+        void *finallyAddr,
+        void *framePtr,
+        void *localsPtr,
+        size_t argsSize,
+        ScriptContext *scriptContext)
+    {
+        void                      *tryContinuation = nullptr;
+        void                      *finallyContinuation = nullptr;
+        JavascriptExceptionObject *exception = nullptr;
+
+        PROBE_STACK(scriptContext, Constants::MinStackDefault + argsSize);
+
+        try
+        {
+#if defined(_M_ARM)
+            tryContinuation = arm_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
+#elif defined(_M_ARM64)
+            tryContinuation = arm64_CallEhFrame(tryAddr, framePtr, localsPtr, argsSize);
+#endif
+        }
+        catch (const Js::JavascriptException& err)
+        {
+            exception = err.GetAndClear();
+        }
+
         if (exception)
         {
             // Clone static exception object early in case finally block overwrites it
@@ -402,7 +495,7 @@ namespace Js
         return continuationAddr;
     }
 
-    void* JavascriptExceptionOperators::OP_TryFinally(void* tryAddr, void* handlerAddr, void* framePtr, ScriptContext *scriptContext)
+    void* JavascriptExceptionOperators::OP_TryFinally(void* tryAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext *scriptContext)
     {
         Js::JavascriptExceptionObject* pExceptionObject = NULL;
         void* continuationAddr = NULL;
@@ -475,6 +568,149 @@ namespace Js
             pExceptionObject = err.GetAndClear();
         }
 
+        if (pExceptionObject)
+        {
+            // Clone static exception object early in case finally block overwrites it
+            pExceptionObject = pExceptionObject->CloneIfStaticExceptionObject(scriptContext);
+            bool hasBailedOut = *(bool*)((char*)framePtr + hasBailedOutOffset); // stack offsets are negative
+            if (hasBailedOut)
+            {
+                // If we have bailed out, this exception is coming from the interpreter. It should not have been caught;
+                // it so happens that this catch was on the stack and caught the exception.
+                // Re-throw!
+                JavascriptExceptionOperators::DoThrow(pExceptionObject, scriptContext);
+            }
+            scriptContext->GetThreadContext()->SetPendingFinallyException(pExceptionObject);
+
+            void* newContinuationAddr = NULL;
+#ifdef _M_IX86
+            void *savedEsp;
+
+            __asm
+            {
+                // Save and restore the callee-saved registers around the call.
+                // TODO: track register kills by region and generate per-region prologs and epilogs
+                push esi
+                push edi
+                push ebx
+
+                // 8-byte align frame to improve floating point perf of our JIT'd code.
+                // Save ESP
+                mov ecx, esp
+                mov savedEsp, ecx
+                and esp, -8
+
+                // Set up the call target
+                mov eax, handlerAddr
+
+#if 0 && defined(_CONTROL_FLOW_GUARD)
+                // verify that the call target is valid
+                mov  ebx, eax; save call target
+                mov  ecx, eax
+                call[__guard_check_icall_fptr]
+                mov  eax, ebx; restore call target
+#endif
+
+                // save the current frame ptr, and adjust the frame to access
+                // locals in native code.
+                push ebp
+                mov ebp, framePtr
+                call eax
+                pop ebp
+
+                // The native code gives us the address where execution should continue on exit
+                // from the finally, but only if flow leaves the finally before it completes.
+                mov newContinuationAddr, eax
+
+                // Restore ESP
+                mov ecx, savedEsp
+                mov esp, ecx
+
+                pop ebx
+                pop edi
+                pop esi
+            }
+#else
+        AssertMsg(FALSE, "Unsupported native try-finally handler");
+#endif
+            return newContinuationAddr;
+        }
+        return continuationAddr;
+    }
+
+    void* JavascriptExceptionOperators::OP_TryFinallySimpleJit(void* tryAddr, void* handlerAddr, void* framePtr, ScriptContext *scriptContext)
+    {
+        Js::JavascriptExceptionObject* pExceptionObject = NULL;
+        void* continuationAddr = NULL;
+
+        PROBE_STACK(scriptContext, Constants::MinStackDefault);
+
+        try
+        {
+            // Bug in compiler optimizer: try-catch can be optimized away if the try block contains __asm calls into function
+            // that may throw. The current workaround is to add the following dummy throw to prevent this optimization.
+            // It seems like compiler got smart and still optimizes if the exception is not JavascriptExceptionObject (see catch handler below).
+            // In order to circumvent that we are throwing OutOfMemory.
+            if (!tryAddr)
+            {
+                Assert(false);
+                ThrowOutOfMemory(scriptContext);
+            }
+
+#ifdef _M_IX86
+            void *savedEsp;
+            __asm
+            {
+                // Save and restore the callee-saved registers around the call.
+                // TODO: track register kills by region and generate per-region prologs and epilogs
+                push esi
+                push edi
+                push ebx
+
+                // 8-byte align frame to improve floating point perf of our JIT'd code.
+                // Save ESP
+                mov ecx, esp
+                mov savedEsp, ecx
+                and esp, -8
+
+                // Set up the call target, save the current frame ptr, and adjust the frame to access
+                // locals in native code.
+                mov eax, tryAddr
+
+#if 0 && defined(_CONTROL_FLOW_GUARD)
+                // verify that the call target is valid
+                mov  ebx, eax; save call target
+                mov  ecx, eax
+                call[__guard_check_icall_fptr]
+                mov  eax, ebx; restore call target
+#endif
+
+                push ebp
+                mov ebp, framePtr
+                call eax
+                pop ebp
+
+                // The native code gives us the address where execution should continue on exit
+                // from the region.
+                mov continuationAddr, eax
+
+                // Restore ESP
+                mov ecx, savedEsp
+                mov esp, ecx
+
+                pop ebx
+                pop edi
+                pop esi
+            }
+#else
+            AssertMsg(FALSE, "Unsupported native try-finally handler");
+#endif
+        }
+        catch (const Js::JavascriptException& err)
+        {
+            pExceptionObject = err.GetAndClear();
+        }
+
         if (pExceptionObject)
         {
             // Clone static exception object early in case finally block overwrites it
@@ -503,11 +739,11 @@ namespace Js
             mov eax, handlerAddr
 
 #if 0 && defined(_CONTROL_FLOW_GUARD)
-                // verify that the call target is valid
-                mov  ebx, eax     ; save call target
-                mov  ecx, eax
-                call [__guard_check_icall_fptr]
-                mov  eax, ebx     ; restore call target
+            // verify that the call target is valid
+            mov  ebx, eax; save call target
+            mov  ecx, eax
+            call[__guard_check_icall_fptr]
+            mov  eax, ebx; restore call target
 #endif
 
             // save the current frame ptr, and adjust the frame to access

+ 6 - 3
lib/Runtime/Language/JavascriptExceptionOperators.h

@@ -53,13 +53,16 @@ namespace Js
 
 #ifdef _M_X64
         static void *OP_TryCatch(void *try_, void *catch_, void *frame, size_t spillSize, size_t argsSize, int hasBailedOutOffset, ScriptContext *scriptContext);
-        static void *OP_TryFinally(void *try_, void *finally_, void *frame, size_t spillSize, size_t argsSize, ScriptContext *scriptContext);
+        static void *OP_TryFinally(void *try_, void *finally_, void *frame, size_t spillSize, size_t argsSize, int hasBailedOutOffset, ScriptContext *scriptContext);
+        static void *OP_TryFinallySimpleJit(void *try_, void *finally_, void *frame, size_t spillSize, size_t argsSize, ScriptContext *scriptContext);
 #elif defined(_M_ARM32_OR_ARM64)
         static void* OP_TryCatch(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, int hasBailedOutOffset, ScriptContext* scriptContext);
-        static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, ScriptContext* scriptContext);
+        static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, int hasBailedOutOffset, ScriptContext* scriptContext);
+        static void* OP_TryFinallySimpleJit(void* continuationAddr, void* handlerAddr, void* framePtr, void *localsPtr, size_t argsSize, ScriptContext* scriptContext);
 #else
         static void* OP_TryCatch(void* continuationAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext* scriptContext);
-        static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, ScriptContext* scriptContext);
+        static void* OP_TryFinally(void* continuationAddr, void* handlerAddr, void* framePtr, int hasBailedOutOffset, ScriptContext* scriptContext);
+        static void* OP_TryFinallySimpleJit(void* continuationAddr, void* handlerAddr, void* framePtr, ScriptContext* scriptContext);
 #endif
 #if defined(DBG) && defined(_M_IX86)
         static void DbgCheckEHChain();

+ 9 - 9
lib/Runtime/Library/InJavascript/Intl.js.bc.32b.h

@@ -3116,7 +3116,7 @@ namespace Js
 /* 00006940 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x03, 0x00, 0x62, 0x3E, 0x3E, 0x0F, 0x5C, 0x01,
 /* 00006950 */ 0x3E, 0x5D, 0x02, 0x2D, 0x0E, 0x00, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E,
 /* 00006960 */ 0x00, 0x00, 0x00, 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01,
-/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7B,
+/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7B,
 /* 00006980 */ 0x29, 0x3E, 0x0C, 0x7B, 0x25, 0x3E, 0x0D, 0x7B, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0xEE, 0x04,
 /* 00006990 */ 0xFF, 0x3D, 0x0E, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x05, 0x00,
 /* 000069A0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E,
@@ -4009,7 +4009,7 @@ namespace Js
 /* 0000A110 */ 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x62, 0x2B,
 /* 0000A120 */ 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5D, 0x02, 0x1E, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03,
 /* 0000A130 */ 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x2D,
-/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x65,
+/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x66,
 /* 0000A150 */ 0x01, 0x2C, 0x2B, 0x7B, 0x0C, 0x2B, 0x02, 0x7B, 0x1B, 0x2B, 0x04, 0x7B, 0x0C, 0x2B, 0x03, 0x5C,
 /* 0000A160 */ 0x03, 0x2B, 0xEE, 0x04, 0xFF, 0x2A, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00,
 /* 0000A170 */ 0x00, 0x2A, 0x05, 0x00, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02,
@@ -4400,7 +4400,7 @@ namespace Js
 /* 0000B980 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x62, 0x1E, 0x1E, 0x05, 0x5C, 0x01,
 /* 0000B990 */ 0x1E, 0x5D, 0x02, 0x13, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E,
 /* 0000B9A0 */ 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50, 0x01,
-/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E, 0x7B,
+/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E, 0x7B,
 /* 0000B9C0 */ 0x0C, 0x1E, 0x02, 0x7B, 0x10, 0x1E, 0x04, 0x7B, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0xEE, 0x04,
 /* 0000B9D0 */ 0xFF, 0x1D, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x05, 0x00,
 /* 0000B9E0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E,
@@ -5095,12 +5095,12 @@ namespace Js
 /* 0000E4F0 */ 0x03, 0x02, 0x01, 0xFE, 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04, 0x8E, 0x8F, 0x01,
 /* 0000E500 */ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x07, 0x03, 0x00, 0x5C, 0x00, 0x09,
 /* 0000E510 */ 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00,
-/* 0000E520 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00,
-/* 0000E530 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00,
-/* 0000E540 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D,
-/* 0000E550 */ 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7B,
-/* 0000E560 */ 0x0D, 0x0C, 0x04, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D,
-/* 0000E570 */ 0x0C, 0x05, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08, 0x00, 0x00, 0xEE,
+/* 0000E520 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00,
+/* 0000E530 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00,
+/* 0000E540 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D,
+/* 0000E550 */ 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7B,
+/* 0000E560 */ 0x0D, 0x0C, 0x04, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D,
+/* 0000E570 */ 0x0C, 0x05, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08, 0x00, 0x00, 0xEE,
 /* 0000E580 */ 0x03, 0x00, 0x0B, 0x00, 0x00, 0x09, 0x02, 0x00, 0xA8, 0x00, 0x24, 0x00, 0x01, 0x20, 0x00, 0x00,
 /* 0000E590 */ 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x03, 0x00, 0x00, 0x5D,
 /* 0000E5A0 */ 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, 0x00, 0x61, 0x02, 0x00, 0x00, 0x42,

+ 9 - 9
lib/Runtime/Library/InJavascript/Intl.js.bc.64b.h

@@ -3116,7 +3116,7 @@ namespace Js
 /* 00006940 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x03, 0x00, 0x62, 0x3E, 0x3E, 0x0F, 0x5C, 0x01,
 /* 00006950 */ 0x3E, 0x5D, 0x02, 0x2D, 0x0E, 0x00, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E,
 /* 00006960 */ 0x00, 0x00, 0x00, 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01,
-/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7B,
+/* 00006970 */ 0x0D, 0x00, 0x00, 0x00, 0x3F, 0x40, 0x7B, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7B,
 /* 00006980 */ 0x29, 0x3E, 0x0C, 0x7B, 0x25, 0x3E, 0x0D, 0x7B, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0xEE, 0x04,
 /* 00006990 */ 0xFF, 0x3D, 0x0E, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x05, 0x00,
 /* 000069A0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E,
@@ -4009,7 +4009,7 @@ namespace Js
 /* 0000A110 */ 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x62, 0x2B,
 /* 0000A120 */ 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5D, 0x02, 0x1E, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03,
 /* 0000A130 */ 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x2D,
-/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x65,
+/* 0000A140 */ 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7B, 0x2C, 0x2B, 0x01, 0x01, 0x66,
 /* 0000A150 */ 0x01, 0x2C, 0x2B, 0x7B, 0x0C, 0x2B, 0x02, 0x7B, 0x1B, 0x2B, 0x04, 0x7B, 0x0C, 0x2B, 0x03, 0x5C,
 /* 0000A160 */ 0x03, 0x2B, 0xEE, 0x04, 0xFF, 0x2A, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00,
 /* 0000A170 */ 0x00, 0x2A, 0x05, 0x00, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02,
@@ -4400,7 +4400,7 @@ namespace Js
 /* 0000B980 */ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x62, 0x1E, 0x1E, 0x05, 0x5C, 0x01,
 /* 0000B990 */ 0x1E, 0x5D, 0x02, 0x13, 0x09, 0x00, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x1E,
 /* 0000B9A0 */ 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50, 0x01,
-/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E, 0x7B,
+/* 0000B9B0 */ 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7B, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E, 0x7B,
 /* 0000B9C0 */ 0x0C, 0x1E, 0x02, 0x7B, 0x10, 0x1E, 0x04, 0x7B, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0xEE, 0x04,
 /* 0000B9D0 */ 0xFF, 0x1D, 0x09, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x05, 0x00,
 /* 0000B9E0 */ 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x91, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E,
@@ -5095,12 +5095,12 @@ namespace Js
 /* 0000E4F0 */ 0x01, 0xFE, 0x40, 0x03, 0x02, 0x01, 0xFE, 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04,
 /* 0000E500 */ 0x8E, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x07, 0x03, 0x00,
 /* 0000E510 */ 0x5C, 0x00, 0x09, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
-/* 0000E520 */ 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4,
-/* 0000E530 */ 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02,
-/* 0000E540 */ 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00,
-/* 0000E550 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00,
-/* 0000E560 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x04, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00,
-/* 0000E570 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x05, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08,
+/* 0000E520 */ 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4,
+/* 0000E530 */ 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x01, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02,
+/* 0000E540 */ 0x00, 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x02, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00,
+/* 0000E550 */ 0x00, 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x03, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00,
+/* 0000E560 */ 0x00, 0x0D, 0x7B, 0x0D, 0x0C, 0x04, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00,
+/* 0000E570 */ 0x0D, 0x7B, 0x0D, 0x0C, 0x05, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5D, 0x02, 0x08,
 /* 0000E580 */ 0x00, 0x00, 0xEE, 0x03, 0x00, 0x0B, 0x00, 0x00, 0x09, 0x02, 0x00, 0xA8, 0x00, 0x24, 0x00, 0x01,
 /* 0000E590 */ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x03,
 /* 0000E5A0 */ 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, 0x00, 0x61, 0x02,

+ 8 - 8
lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.32b.h

@@ -3091,7 +3091,7 @@ namespace Js
 /* 000067B0 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x61, 0x3E, 0x3E, 0x0F, 0x5C, 0x01, 0x3E,
 /* 000067C0 */ 0x5C, 0x02, 0x2D, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
 /* 000067D0 */ 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01, 0x0D, 0x00, 0x00,
-/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C,
+/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C,
 /* 000067F0 */ 0x7A, 0x25, 0x3E, 0x0D, 0x7A, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0x1F, 0x04, 0xFF, 0x3D, 0x8E,
 /* 00006800 */ 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x90,
 /* 00006810 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x5C, 0x01, 0x3E, 0x5C, 0x02, 0x2E, 0xCC,
@@ -3907,7 +3907,7 @@ namespace Js
 /* 00009AB0 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1E, 0xCC,
 /* 00009AC0 */ 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7,
 /* 00009AD0 */ 0x01, 0x00, 0x00, 0x00, 0x2D, 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7A,
-/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x65, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04,
+/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x66, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04,
 /* 00009AF0 */ 0x7A, 0x0C, 0x2B, 0x03, 0x5C, 0x03, 0x2B, 0x1F, 0x04, 0xFF, 0x2A, 0x8E, 0x01, 0x00, 0x00, 0x00,
 /* 00009B00 */ 0x19, 0x00, 0x00, 0x00, 0x2A, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00,
 /* 00009B10 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1F, 0xCC,
@@ -4264,7 +4264,7 @@ namespace Js
 /* 0000B100 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E,
 /* 0000B110 */ 0x05, 0x5C, 0x01, 0x1E, 0x5C, 0x02, 0x13, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
 /* 0000B120 */ 0x1E, 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50,
-/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E,
+/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E,
 /* 0000B140 */ 0x7A, 0x0C, 0x1E, 0x02, 0x7A, 0x10, 0x1E, 0x04, 0x7A, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0x1F,
 /* 0000B150 */ 0x04, 0xFF, 0x1D, 0x8E, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x07, 0x04, 0x00,
 /* 0000B160 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E,
@@ -4904,11 +4904,11 @@ namespace Js
 /* 0000D900 */ 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04, 0x88, 0x8E, 0x01, 0x00, 0x00, 0x00, 0x08,
 /* 0000D910 */ 0x00, 0x00, 0x00, 0x0B, 0x07, 0x03, 0x00, 0x5C, 0x00, 0x09, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00,
 /* 0000D920 */ 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C,
-/* 0000D930 */ 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x01,
-/* 0000D940 */ 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x02, 0x01,
-/* 0000D950 */ 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x03, 0x01, 0x65,
-/* 0000D960 */ 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04, 0x01, 0x65, 0x01,
-/* 0000D970 */ 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01, 0x65, 0x01, 0x0D,
+/* 0000D930 */ 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x01,
+/* 0000D940 */ 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x02, 0x01,
+/* 0000D950 */ 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x03, 0x01, 0x66,
+/* 0000D960 */ 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04, 0x01, 0x66, 0x01,
+/* 0000D970 */ 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01, 0x66, 0x01, 0x0D,
 /* 0000D980 */ 0x0C, 0x5C, 0x01, 0x0C, 0x5C, 0x02, 0x08, 0x1F, 0x03, 0x00, 0x0B, 0x09, 0x02, 0x00, 0xA8, 0x00,
 /* 0000D990 */ 0x24, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
 /* 0000D9A0 */ 0x00, 0x3D, 0x03, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00,

+ 9 - 9
lib/Runtime/Library/InJavascript/Intl.js.nojit.bc.64b.h

@@ -3091,7 +3091,7 @@ namespace Js
 /* 000067B0 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x61, 0x3E, 0x3E, 0x0F, 0x5C, 0x01, 0x3E,
 /* 000067C0 */ 0x5C, 0x02, 0x2D, 0xCC, 0x70, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00,
 /* 000067D0 */ 0xB8, 0x40, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x40, 0x40, 0x01, 0x50, 0x01, 0x0D, 0x00, 0x00,
-/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x65, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C,
+/* 000067E0 */ 0x00, 0x3F, 0x40, 0x7A, 0x3F, 0x3E, 0x0B, 0x01, 0x66, 0x01, 0x3F, 0x3E, 0x7A, 0x29, 0x3E, 0x0C,
 /* 000067F0 */ 0x7A, 0x25, 0x3E, 0x0D, 0x7A, 0x29, 0x3E, 0x0E, 0x5C, 0x03, 0x3E, 0x1F, 0x04, 0xFF, 0x3D, 0x8E,
 /* 00006800 */ 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x18, 0x90,
 /* 00006810 */ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x3E, 0x5C, 0x01, 0x3E, 0x5C, 0x02, 0x2E, 0xCC,
@@ -3907,7 +3907,7 @@ namespace Js
 /* 00009AB0 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1E, 0xCC,
 /* 00009AC0 */ 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xB8, 0x2D, 0x00, 0xB7,
 /* 00009AD0 */ 0x01, 0x00, 0x00, 0x00, 0x2D, 0x2D, 0x01, 0x50, 0x01, 0x04, 0x00, 0x00, 0x00, 0x2C, 0x2D, 0x7A,
-/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x65, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04,
+/* 00009AE0 */ 0x2C, 0x2B, 0x01, 0x01, 0x66, 0x01, 0x2C, 0x2B, 0x7A, 0x0C, 0x2B, 0x02, 0x7A, 0x1B, 0x2B, 0x04,
 /* 00009AF0 */ 0x7A, 0x0C, 0x2B, 0x03, 0x5C, 0x03, 0x2B, 0x1F, 0x04, 0xFF, 0x2A, 0x8E, 0x01, 0x00, 0x00, 0x00,
 /* 00009B00 */ 0x19, 0x00, 0x00, 0x00, 0x2A, 0x07, 0x04, 0x00, 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00,
 /* 00009B10 */ 0x02, 0x00, 0x00, 0x00, 0x2B, 0x61, 0x2B, 0x2B, 0x05, 0x5C, 0x01, 0x2B, 0x5C, 0x02, 0x1F, 0xCC,
@@ -4264,7 +4264,7 @@ namespace Js
 /* 0000B100 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E,
 /* 0000B110 */ 0x05, 0x5C, 0x01, 0x1E, 0x5C, 0x02, 0x13, 0xCC, 0x44, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
 /* 0000B120 */ 0x1E, 0x00, 0x00, 0x00, 0xB8, 0x20, 0x00, 0xB7, 0x01, 0x00, 0x00, 0x00, 0x20, 0x20, 0x01, 0x50,
-/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x65, 0x01, 0x1F, 0x1E,
+/* 0000B130 */ 0x01, 0x04, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x7A, 0x1F, 0x1E, 0x01, 0x01, 0x66, 0x01, 0x1F, 0x1E,
 /* 0000B140 */ 0x7A, 0x0C, 0x1E, 0x02, 0x7A, 0x10, 0x1E, 0x04, 0x7A, 0x0C, 0x1E, 0x03, 0x5C, 0x03, 0x1E, 0x1F,
 /* 0000B150 */ 0x04, 0xFF, 0x1D, 0x8E, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1D, 0x07, 0x04, 0x00,
 /* 0000B160 */ 0x5C, 0x00, 0x05, 0x90, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x1E, 0x61, 0x1E, 0x1E,
@@ -4904,12 +4904,12 @@ namespace Js
 /* 0000D900 */ 0x02, 0x01, 0xFE, 0x41, 0x03, 0x02, 0x01, 0xFE, 0x42, 0x03, 0x03, 0x04, 0x88, 0x8E, 0x01, 0x00,
 /* 0000D910 */ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x07, 0x03, 0x00, 0x5C, 0x00, 0x09, 0xCC, 0x00, 0x00,
 /* 0000D920 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x0D,
-/* 0000D930 */ 0x7A, 0x0D, 0x0C, 0x00, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A,
-/* 0000D940 */ 0x0D, 0x0C, 0x01, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D,
-/* 0000D950 */ 0x0C, 0x02, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C,
-/* 0000D960 */ 0x03, 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04,
-/* 0000D970 */ 0x01, 0x65, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01,
-/* 0000D980 */ 0x65, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5C, 0x02, 0x08, 0x1F, 0x03, 0x00, 0x0B, 0x09, 0x02,
+/* 0000D930 */ 0x7A, 0x0D, 0x0C, 0x00, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x01, 0x00, 0x00, 0x00, 0x0D, 0x7A,
+/* 0000D940 */ 0x0D, 0x0C, 0x01, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D,
+/* 0000D950 */ 0x0C, 0x02, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C,
+/* 0000D960 */ 0x03, 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x04, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x04,
+/* 0000D970 */ 0x01, 0x66, 0x01, 0x0D, 0x0C, 0xD4, 0x05, 0x00, 0x00, 0x00, 0x0D, 0x7A, 0x0D, 0x0C, 0x05, 0x01,
+/* 0000D980 */ 0x66, 0x01, 0x0D, 0x0C, 0x5C, 0x01, 0x0C, 0x5C, 0x02, 0x08, 0x1F, 0x03, 0x00, 0x0B, 0x09, 0x02,
 /* 0000D990 */ 0x00, 0xA8, 0x00, 0x24, 0x00, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00,
 /* 0000D9A0 */ 0x00, 0x00, 0x00, 0x00, 0x3D, 0x03, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00,
 /* 0000D9B0 */ 0x5C, 0x02, 0x00, 0x00, 0x61, 0x02, 0x00, 0x00, 0x42, 0x03, 0x00, 0x00, 0x00, 0xFE, 0x3D, 0x03,

+ 261 - 0
test/EH/early1.baseline

@@ -0,0 +1,261 @@
+catch 0
+catch 1
+catch 2
+catch 3
+catch 4
+catch 5
+catch 6
+done
+Done earlyReturn
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+done
+Done earlyBreak
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+done
+Done earlyContinue
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+Done earlyReturnFromFinally
+catch 0
+Done earlyReturnFromCatch
+inner finally 0
+outer finally 0
+Done earlyReturnFromNestedFinally
+outer finally 0
+Done earlyReturnFromNestedTFTC
+outer finally 0
+done
+Done earlyBreakFromNestedTFTC
+outer finally 0
+outer finally 1
+outer finally 2
+outer finally 3
+outer finally 4
+outer finally 5
+outer finally 6
+done
+Done earlyContinueFromNestedTFTC
+outer finally 0
+done
+Done earlyReturnFromNestedTFTC
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+done
+Done earlyReturnFromNestedTFTC
+catch 0
+finally 0
+earlyReturnFromCatchInTryFinally
+catch 0
+finally 0
+earlyReturnFromCatchInTryCatchTryFinally
+try
+inner finally 0
+outer finally 0
+Done earlyReturnFromFinallyInTryFinally
+catch 0
+catch 1
+catch 2
+catch 3
+catch 4
+catch 5
+catch 6
+done
+infinite loop catch
+infinite loop finally
+earlyReturnFromCatchInInfiniteLoop
+catch 0
+catch 1
+catch 2
+catch 3
+catch 4
+catch 5
+catch 6
+done
+Done earlyReturn
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+done
+Done earlyBreak
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+done
+Done earlyContinue
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+Done earlyReturnFromFinally
+catch 0
+Done earlyReturnFromCatch
+inner finally 0
+outer finally 0
+Done earlyReturnFromNestedFinally
+outer finally 0
+Done earlyReturnFromNestedTFTC
+outer finally 0
+done
+Done earlyBreakFromNestedTFTC
+outer finally 0
+outer finally 1
+outer finally 2
+outer finally 3
+outer finally 4
+outer finally 5
+outer finally 6
+done
+Done earlyContinueFromNestedTFTC
+outer finally 0
+done
+Done earlyReturnFromNestedTFTC
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+done
+Done earlyReturnFromNestedTFTC
+catch 0
+finally 0
+earlyReturnFromCatchInTryFinally
+catch 0
+finally 0
+earlyReturnFromCatchInTryCatchTryFinally
+try
+inner finally 0
+outer finally 0
+Done earlyReturnFromFinallyInTryFinally
+catch 0
+catch 1
+catch 2
+catch 3
+catch 4
+catch 5
+catch 6
+done
+infinite loop catch
+infinite loop finally
+earlyReturnFromCatchInInfiniteLoop
+catch 0
+catch 1
+catch 2
+catch 3
+catch 4
+catch 5
+catch 6
+done
+Done earlyReturn
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+done
+Done earlyBreak
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+done
+Done earlyContinue
+finally 0
+finally 1
+finally 2
+finally 3
+finally 4
+finally 5
+finally 6
+Done earlyReturnFromFinally
+catch 0
+Done earlyReturnFromCatch
+inner finally 0
+outer finally 0
+Done earlyReturnFromNestedFinally
+outer finally 0
+Done earlyReturnFromNestedTFTC
+outer finally 0
+done
+Done earlyBreakFromNestedTFTC
+outer finally 0
+outer finally 1
+outer finally 2
+outer finally 3
+outer finally 4
+outer finally 5
+outer finally 6
+done
+Done earlyContinueFromNestedTFTC
+outer finally 0
+done
+Done earlyReturnFromNestedTFTC
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+outer finally 0
+done
+Done earlyReturnFromNestedTFTC
+catch 0
+finally 0
+earlyReturnFromCatchInTryFinally
+catch 0
+finally 0
+earlyReturnFromCatchInTryCatchTryFinally
+try
+inner finally 0
+outer finally 0
+Done earlyReturnFromFinallyInTryFinally
+catch 0
+catch 1
+catch 2
+catch 3
+catch 4
+catch 5
+catch 6
+done
+infinite loop catch
+infinite loop finally
+earlyReturnFromCatchInInfiniteLoop

+ 315 - 0
test/EH/early1.js

@@ -0,0 +1,315 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function oos() {
+  oos();
+}
+
+function earlyReturn(num) {
+for (var i = 0; i < num; i++) {
+  try {
+    if (num > 5) {
+      oos();
+      return;
+    }
+  }
+  catch(e) {
+    WScript.Echo("catch " + i);
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyBreak(num) {
+for (var i = 0; i < num; i++) {
+  try {
+    if (i > 5) {
+     break;
+    }
+  }
+  finally {
+    WScript.Echo("finally " + i);
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyContinue(num) {
+for (var i = 0; i < num; i++) {
+  try {
+    if (i > 5) {
+     continue;
+    }
+  }
+  finally {
+    WScript.Echo("finally " + i);
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromFinally(num)
+{
+
+for (var i = 0; i < num; i++) {
+  try {
+  }
+  finally {
+    WScript.Echo("finally " + i);
+    if (i > 5) {
+      return;
+    }
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromCatch(num)
+{
+for (var i = 0; i < num; i++) {
+  try {
+    oos();
+  }
+  catch(e) {
+    WScript.Echo("catch " + i);
+    if (num > 5) {
+      return;
+    }
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromNestedFinally(num)
+{
+for (var i = 0; i < num; i++) {
+  try {
+    try {
+      if (num > 5) return;
+    }
+    finally{
+        WScript.Echo("inner finally " + i);
+    }
+  }
+  finally {
+    WScript.Echo("outer finally " + i);
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromNestedTFTC(num)
+{
+for (var i = 0; i < num; i++) {
+  try {
+    try {
+      if (num > 5) return;
+    }
+    catch (e){
+        WScript.Echo("inner catch " + i);
+    }
+  }
+  finally {
+    WScript.Echo("outer finally " + i);
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyBreakFromNestedTFTC(num)
+{
+for (var i = 0; i < num; i++) {
+  try {
+    try {
+      if (num > 5) break;
+    }
+    catch (e){
+        WScript.Echo("inner catch " + i);
+    }
+  }
+  finally {
+    WScript.Echo("outer finally " + i);
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyContinueFromNestedTFTC(num)
+{
+for (var i = 0; i < num; i++) {
+  try {
+    try {
+      if (num > 5) continue;
+    }
+    catch (e){
+        WScript.Echo("inner catch " + i);
+    }
+  }
+  finally {
+    WScript.Echo("outer finally " + i);
+  }
+}
+WScript.Echo("done");
+}
+
+function earlyBreakLabelFromNestedTFTC(num)
+{
+outer:for (var x = 0; x < num; x++) {
+for (var i = 0; i < num; i++) {
+  try {
+    try {
+      if (num > 5) break outer;
+    }
+    catch (e){
+        WScript.Echo("inner catch " + i);
+    }
+  }
+  finally {
+    WScript.Echo("outer finally " + i);
+  }
+}
+}
+WScript.Echo("done");
+}
+
+function earlyContinueLabelFromNestedTFTC(num)
+{
+outer:for (var x = 0; x < num; x++) {
+for (var i = 0; i < num; i++) {
+  try {
+    try {
+      if (num > 5) continue outer;
+    }
+    catch (e){
+        WScript.Echo("inner catch " + i);
+    }
+  }
+  finally {
+    WScript.Echo("outer finally " + i);
+  }
+}
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromCatchInTryFinally(num)
+{
+for (var i = 0; i < num; i++) {
+try {
+  try {
+    throw "Err";
+  }
+  catch(e) {
+    WScript.Echo("catch " + i);
+    if (num > 5) {
+      return;
+    }
+  }
+}
+finally {
+WScript.Echo("finally " + i);
+}
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromCatchInTryCatchTryFinally(num)
+{
+for (var i = 0; i < num; i++) {
+try {
+  try {
+    throw "Err";
+  }
+  catch(e) {
+    WScript.Echo("catch " + i);
+    if (num > 5) {
+      return;
+    }
+  }
+}
+finally {
+WScript.Echo("finally " + i);
+}
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromFinallyInTryFinally(num)
+{
+for (var i = 0; i < num; i++) {
+try {
+  try {
+    WScript.Echo("try");
+  }
+  finally {
+    WScript.Echo("inner finally " + i);
+    return;
+  }
+}
+finally {
+WScript.Echo("outer finally " + i);
+}
+}
+WScript.Echo("done");
+}
+
+function earlyReturnFromCatchInInfiniteLoop(num)
+{
+while (true) {
+try {
+  try {
+    throw "Err";
+  }
+  catch(e) {
+    WScript.Echo("infinite loop catch");
+    if (num > 5) {
+      return;
+    }
+  }
+}
+finally {
+WScript.Echo("infinite loop finally");
+}
+}
+WScript.Echo("done");
+}
+
+function test0() {
+  earlyReturn(7);
+  WScript.Echo("Done earlyReturn");
+  earlyBreak(7);
+  WScript.Echo("Done earlyBreak");
+  earlyContinue(7);
+  WScript.Echo("Done earlyContinue");
+  earlyReturnFromFinally(7);
+  WScript.Echo("Done earlyReturnFromFinally");
+  earlyReturnFromCatch(7);
+  WScript.Echo("Done earlyReturnFromCatch");
+  earlyReturnFromNestedFinally(7);
+  WScript.Echo("Done earlyReturnFromNestedFinally");
+  earlyReturnFromNestedTFTC(7);
+  WScript.Echo("Done earlyReturnFromNestedTFTC");
+  earlyBreakFromNestedTFTC(7);
+  WScript.Echo("Done earlyBreakFromNestedTFTC");
+  earlyContinueFromNestedTFTC(7);
+  WScript.Echo("Done earlyContinueFromNestedTFTC");
+  earlyBreakLabelFromNestedTFTC(7);
+  WScript.Echo("Done earlyReturnFromNestedTFTC");
+  earlyContinueLabelFromNestedTFTC(7);
+  WScript.Echo("Done earlyReturnFromNestedTFTC");
+  earlyReturnFromCatchInTryFinally(7);
+  WScript.Echo("earlyReturnFromCatchInTryFinally");
+  earlyReturnFromCatchInTryCatchTryFinally(7);
+  WScript.Echo("earlyReturnFromCatchInTryCatchTryFinally");
+  earlyReturnFromFinallyInTryFinally(7);
+  WScript.Echo("Done earlyReturnFromFinallyInTryFinally");
+  earlyReturn(7);
+  earlyReturnFromCatchInInfiniteLoop(7);
+  WScript.Echo("earlyReturnFromCatchInInfiniteLoop");
+}
+
+test0();
+test0();
+test0();

+ 111 - 0
test/EH/early2.baseline

@@ -0,0 +1,111 @@
+return outer finally
+break outer finally
+continue outer finally 0
+continue outer finally 1
+continue outer finally 2
+continue outer finally 3
+continue outer finally 4
+continue outer finally 5
+continue outer finally 6
+outer finally
+inner finally
+outer finally
+inner finally
+outer finally
+inner finally
+continue outer finally 0
+inner finally
+continue outer finally 1
+inner finally
+continue outer finally 2
+inner finally
+continue outer finally 3
+inner finally
+continue outer finally 4
+inner finally
+continue outer finally 5
+inner finally
+continue outer finally 6
+outer finally
+break outer finally
+continue outer finally 0
+continue outer finally 1
+continue outer finally 2
+continue outer finally 3
+continue outer finally 4
+continue outer finally 5
+continue outer finally 6
+return outer finally
+break outer finally
+continue outer finally 0
+continue outer finally 1
+continue outer finally 2
+continue outer finally 3
+continue outer finally 4
+continue outer finally 5
+continue outer finally 6
+outer finally
+inner finally
+outer finally
+inner finally
+outer finally
+inner finally
+continue outer finally 0
+inner finally
+continue outer finally 1
+inner finally
+continue outer finally 2
+inner finally
+continue outer finally 3
+inner finally
+continue outer finally 4
+inner finally
+continue outer finally 5
+inner finally
+continue outer finally 6
+outer finally
+break outer finally
+continue outer finally 0
+continue outer finally 1
+continue outer finally 2
+continue outer finally 3
+continue outer finally 4
+continue outer finally 5
+continue outer finally 6
+return outer finally
+break outer finally
+continue outer finally 0
+continue outer finally 1
+continue outer finally 2
+continue outer finally 3
+continue outer finally 4
+continue outer finally 5
+continue outer finally 6
+outer finally
+inner finally
+outer finally
+inner finally
+outer finally
+inner finally
+continue outer finally 0
+inner finally
+continue outer finally 1
+inner finally
+continue outer finally 2
+inner finally
+continue outer finally 3
+inner finally
+continue outer finally 4
+inner finally
+continue outer finally 5
+inner finally
+continue outer finally 6
+outer finally
+break outer finally
+continue outer finally 0
+continue outer finally 1
+continue outer finally 2
+continue outer finally 3
+continue outer finally 4
+continue outer finally 5
+continue outer finally 6

+ 141 - 0
test/EH/early2.js

@@ -0,0 +1,141 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+function earlyReturnTF(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      if (num > 5) return;
+    }finally {
+        WScript.Echo("return outer finally");
+    }
+  }
+}
+
+function earlyBreakTF(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      if (num > 5) break;
+    }finally {
+        WScript.Echo("break outer finally");
+    }
+  }
+}
+
+function earlyContinueTF(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      if (i < 3) continue;
+    }finally {
+      WScript.Echo("continue outer finally " + i);
+    }
+  }
+}
+
+function earlyReturnNestedTFTC(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      try {
+        if (num > 5) return;
+      }
+      catch(e) {
+        WScript.Echo("inner catch");
+      }
+    }finally {
+        WScript.Echo("outer finally");
+    }
+  }
+}
+
+function earlyReturnNestedTFTF(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      try {
+        if (num > 5) return;
+      }
+      finally {
+        WScript.Echo("inner finally");
+      }
+    }finally {
+        WScript.Echo("outer finally");
+    }
+  }
+}
+
+function earlyBreakNestedTFTF(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      try {
+        if (num > 5) break;
+      }
+      finally {
+        WScript.Echo("inner finally");
+      }
+    }finally {
+        WScript.Echo("outer finally");
+    }
+  }
+}
+
+function earlyContinueNestedTFTF(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      try {
+        if (i > 3) continue;
+      }
+      finally {
+        WScript.Echo("inner finally");
+      }
+    }finally {
+        WScript.Echo("continue outer finally " + i);
+    }
+  }
+}
+
+function earlyBreakNestedTFTC(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      try {
+        if (num > 5) break;
+      }
+      catch(e) {
+        WScript.Echo("inner catch");
+      }
+    }finally {
+        WScript.Echo("break outer finally");
+    }
+  }
+}
+
+function earlyContinueNestedTFTC(num) {
+  for (var i = 0; i < num; i++) {
+    try {
+      try {
+        if (num > 5) continue;
+      }
+      catch(e) {
+        WScript.Echo("inner catch");
+      }
+    }finally {
+        WScript.Echo("continue outer finally " + i);
+    }
+  }
+}
+
+function test0() {
+  earlyReturnTF(7);
+  earlyBreakTF(7);
+  earlyContinueTF(7);
+  earlyReturnNestedTFTC(7);
+  earlyReturnNestedTFTF(7);
+  earlyBreakNestedTFTF(7);
+  earlyContinueNestedTFTF(7);
+  earlyReturnNestedTFTC(7);
+  earlyBreakNestedTFTC(7);
+  earlyContinueNestedTFTC(7);
+}
+
+test0();
+test0();
+test0();

+ 12 - 0
test/EH/rlexe.xml

@@ -96,4 +96,16 @@
       <baseline>101832.baseline</baseline>
     </default>
   </test>
+  <test>
+    <default>
+      <files>early1.js</files>
+      <baseline>early1.baseline</baseline>
+    </default>
+  </test>
+  <test>
+    <default>
+      <files>early2.js</files>
+      <baseline>early2.baseline</baseline>
+    </default>
+  </test>
 </regress-exe>

Some files were not shown because too many files changed in this diff