Explorar el Código

Avoid using TryFinallyWithYield OpCode when there is no Yield.
Implement partial tracking of Yield within ByteCodeGenerator to enable this.

The OpCode TryFinallyWithYield is not supported by the Jit. It was being used unnecessarily in various code patterns that should be safe to Jit, such patterns were hitting an Abort in the Jit because of the unimplemented OpCode.

As the OpCode is unnecessary when there is no Yield (the cases we currently try to jit) fix logic so we do not use it in those cases.

rhuanjl hace 4 meses
padre
commit
1c242f74e3

+ 3 - 0
lib/Backend/Lower.cpp

@@ -2605,6 +2605,9 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
             instrPrev = this->LowerTry(instr, true /*try-catch*/);
             instrPrev = this->LowerTry(instr, true /*try-catch*/);
             break;
             break;
 
 
+        case Js::OpCode::TryFinallyWithYield:
+            // TODO Implement TryFinallyWithYield  before enabling Jit on full async functions
+            AssertOrFailFastMsg(false, "TryFinallyWithYield not implemented in Jit, should not be trying to Jit this function.");
         case Js::OpCode::TryFinally:
         case Js::OpCode::TryFinally:
             instrPrev = this->LowerTry(instr, false /*try-finally*/);
             instrPrev = this->LowerTry(instr, false /*try-finally*/);
             break;
             break;

+ 20 - 14
lib/Runtime/ByteCode/ByteCodeEmitter.cpp

@@ -6777,9 +6777,9 @@ void EmitIteratorTopLevelFinally(
     Js::RegSlot yieldOffsetLocation,
     Js::RegSlot yieldOffsetLocation,
     ByteCodeGenerator* byteCodeGenerator,
     ByteCodeGenerator* byteCodeGenerator,
     FuncInfo* funcInfo,
     FuncInfo* funcInfo,
+    bool hasYield,
     bool isAsync)
     bool isAsync)
 {
 {
-    bool isCoroutine = funcInfo->byteCodeFunction->IsCoroutine();
 
 
     Js::ByteCodeLabel afterFinallyBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
     Js::ByteCodeLabel afterFinallyBlockLabel = byteCodeGenerator->Writer()->DefineLabel();
     byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
     byteCodeGenerator->Writer()->Empty(Js::OpCode::Leave);
@@ -6805,8 +6805,9 @@ void EmitIteratorTopLevelFinally(
     byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
     byteCodeGenerator->Writer()->MarkLabel(skipCallCloseLabel);
 
 
     byteCodeGenerator->PopJumpCleanup();
     byteCodeGenerator->PopJumpCleanup();
-    if (isCoroutine)
+    if (hasYield)
     {
     {
+        Assert(funcInfo->byteCodeFunction->IsCoroutine());
         funcInfo->ReleaseTmpRegister(yieldOffsetLocation);
         funcInfo->ReleaseTmpRegister(yieldOffsetLocation);
         funcInfo->ReleaseTmpRegister(yieldExceptionLocation);
         funcInfo->ReleaseTmpRegister(yieldExceptionLocation);
     }
     }
@@ -6826,6 +6827,7 @@ void EmitIteratorCatchAndFinally(
     Js::RegSlot yieldOffsetLocation,
     Js::RegSlot yieldOffsetLocation,
     ByteCodeGenerator* byteCodeGenerator,
     ByteCodeGenerator* byteCodeGenerator,
     FuncInfo* funcInfo,
     FuncInfo* funcInfo,
+    bool hasYield,
     bool isAsync = false)
     bool isAsync = false)
 {
 {
     byteCodeGenerator->PopJumpCleanup();
     byteCodeGenerator->PopJumpCleanup();
@@ -6849,6 +6851,7 @@ void EmitIteratorCatchAndFinally(
         yieldOffsetLocation,
         yieldOffsetLocation,
         byteCodeGenerator,
         byteCodeGenerator,
         funcInfo,
         funcInfo,
+        hasYield,
         isAsync);
         isAsync);
 
 
     funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocationFinally);
     funcInfo->ReleaseTmpRegister(shouldCallReturnFunctionLocationFinally);
@@ -6901,10 +6904,11 @@ void EmitDestructuredArray(
 
 
     Js::RegSlot regException = Js::Constants::NoRegister;
     Js::RegSlot regException = Js::Constants::NoRegister;
     Js::RegSlot regOffset = Js::Constants::NoRegister;
     Js::RegSlot regOffset = Js::Constants::NoRegister;
-    bool isCoroutine = funcInfo->byteCodeFunction->IsCoroutine();
+    bool hasYield = byteCodeGenerator->GetHasYield(lhs);
 
 
-    if (isCoroutine)
+    if (hasYield)
     {
     {
+        Assert(funcInfo->byteCodeFunction->IsCoroutine());
         regException = funcInfo->AcquireTmpRegister();
         regException = funcInfo->AcquireTmpRegister();
         regOffset = funcInfo->AcquireTmpRegister();
         regOffset = funcInfo->AcquireTmpRegister();
     }
     }
@@ -6914,7 +6918,7 @@ void EmitDestructuredArray(
     Js::ByteCodeLabel catchLabel = byteCodeGenerator->Writer()->DefineLabel();
     Js::ByteCodeLabel catchLabel = byteCodeGenerator->Writer()->DefineLabel();
     byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
     byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
 
 
-    if (isCoroutine)
+    if (hasYield)
     {
     {
         byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
         byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
         byteCodeGenerator->PushJumpCleanupForTry(
         byteCodeGenerator->PushJumpCleanupForTry(
@@ -6950,7 +6954,8 @@ void EmitDestructuredArray(
         regException,
         regException,
         regOffset,
         regOffset,
         byteCodeGenerator,
         byteCodeGenerator,
-        funcInfo);
+        funcInfo,
+        hasYield);
 
 
     funcInfo->ReleaseTmpRegister(nextMethodReg);
     funcInfo->ReleaseTmpRegister(nextMethodReg);
     funcInfo->ReleaseTmpRegister(iteratorLocation);
     funcInfo->ReleaseTmpRegister(iteratorLocation);
@@ -9836,6 +9841,7 @@ void EmitForInOrForOf(ParseNodeForInOrForOf *loopNode, ByteCodeGenerator *byteCo
 {
 {
     bool isForIn = (loopNode->nop == knopForIn);
     bool isForIn = (loopNode->nop == knopForIn);
     bool isForAwaitOf = (loopNode->nop == knopForAwaitOf);
     bool isForAwaitOf = (loopNode->nop == knopForAwaitOf);
+    bool hasYield = isForAwaitOf || byteCodeGenerator->GetHasYield(loopNode);
     Assert(isForAwaitOf || isForIn || loopNode->nop == knopForOf);
     Assert(isForAwaitOf || isForIn || loopNode->nop == knopForOf);
 
 
     BeginEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
     BeginEmitBlock(loopNode->pnodeBlock, byteCodeGenerator, funcInfo);
@@ -9899,10 +9905,9 @@ void EmitForInOrForOf(ParseNodeForInOrForOf *loopNode, ByteCodeGenerator *byteCo
     Js::RegSlot shouldCallReturnFunctionLocation = loopNode->shouldCallReturnFunctionLocation;
     Js::RegSlot shouldCallReturnFunctionLocation = loopNode->shouldCallReturnFunctionLocation;
     Js::RegSlot shouldCallReturnFunctionLocationFinally = loopNode->shouldCallReturnFunctionLocationFinally;
     Js::RegSlot shouldCallReturnFunctionLocationFinally = loopNode->shouldCallReturnFunctionLocationFinally;
 
 
-    bool isCoroutine = funcInfo->byteCodeFunction->IsCoroutine();
-
-    if (isCoroutine)
+    if (hasYield)
     {
     {
+        Assert(funcInfo->byteCodeFunction->IsCoroutine());
         regException = funcInfo->AcquireTmpRegister();
         regException = funcInfo->AcquireTmpRegister();
         regOffset = funcInfo->AcquireTmpRegister();
         regOffset = funcInfo->AcquireTmpRegister();
     }
     }
@@ -9946,7 +9951,7 @@ void EmitForInOrForOf(ParseNodeForInOrForOf *loopNode, ByteCodeGenerator *byteCo
     byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
     byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocation);
     byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
     byteCodeGenerator->Writer()->Reg1(Js::OpCode::LdFalse, shouldCallReturnFunctionLocationFinally);
 
 
-    if (isCoroutine)
+    if (hasYield)
     {
     {
         byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
         byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
         byteCodeGenerator->PushJumpCleanupForTry(
         byteCodeGenerator->PushJumpCleanupForTry(
@@ -10027,6 +10032,7 @@ void EmitForInOrForOf(ParseNodeForInOrForOf *loopNode, ByteCodeGenerator *byteCo
         regOffset,
         regOffset,
         byteCodeGenerator,
         byteCodeGenerator,
         funcInfo,
         funcInfo,
+        hasYield,
         isForAwaitOf);
         isForAwaitOf);
 }
 }
 
 
@@ -12545,11 +12551,11 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func
 
 
         finallyLabel = byteCodeGenerator->Writer()->DefineLabel();
         finallyLabel = byteCodeGenerator->Writer()->DefineLabel();
         byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
         byteCodeGenerator->Writer()->RecordCrossFrameEntryExitRecord(true);
+        bool hasYield = byteCodeGenerator->GetHasYield(pnodeTryFinally);
 
 
-        // [CONSIDER][aneeshd] Ideally the TryFinallyWithYield opcode needs to be used only if there is a yield expression.
-        // For now, if the function is generator we are using the TryFinallyWithYield.
-        if (funcInfo->byteCodeFunction->IsCoroutine())
+        if (hasYield)
         {
         {
+            Assert(funcInfo->byteCodeFunction->IsCoroutine());
             regException = funcInfo->AcquireTmpRegister();
             regException = funcInfo->AcquireTmpRegister();
             regOffset = funcInfo->AcquireTmpRegister();
             regOffset = funcInfo->AcquireTmpRegister();
             byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
             byteCodeGenerator->Writer()->BrReg2(Js::OpCode::TryFinallyWithYield, finallyLabel, regException, regOffset);
@@ -12593,7 +12599,7 @@ void Emit(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* func
         Emit(pnodeFinally->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
         Emit(pnodeFinally->pnodeBody, byteCodeGenerator, funcInfo, fReturnValue);
         funcInfo->ReleaseLoc(pnodeFinally->pnodeBody);
         funcInfo->ReleaseLoc(pnodeFinally->pnodeBody);
 
 
-        if (funcInfo->byteCodeFunction->IsCoroutine())
+        if (hasYield)
         {
         {
             funcInfo->ReleaseTmpRegister(regOffset);
             funcInfo->ReleaseTmpRegister(regOffset);
             funcInfo->ReleaseTmpRegister(regException);
             funcInfo->ReleaseTmpRegister(regException);

+ 62 - 0
lib/Runtime/ByteCode/ByteCodeGenerator.cpp

@@ -219,6 +219,12 @@ void Visit(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn pref
     prefix(pnode, byteCodeGenerator);
     prefix(pnode, byteCodeGenerator);
     switch (pnode->nop)
     switch (pnode->nop)
     {
     {
+    case knopYield:
+    case knopYieldLeaf:
+    case knopYieldStar:
+    case knopAwait:
+        byteCodeGenerator->SetHasYield();
+    // fall through to default
     default:
     default:
     {
     {
         uint flags = ParseNode::Grfnop(pnode->nop);
         uint flags = ParseNode::Grfnop(pnode->nop);
@@ -245,6 +251,7 @@ void Visit(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn pref
         break;
         break;
 
 
     case knopArrayPattern:
     case knopArrayPattern:
+        byteCodeGenerator->PushTrackForYield(pnode);
         if (!byteCodeGenerator->InDestructuredPattern())
         if (!byteCodeGenerator->InDestructuredPattern())
         {
         {
             byteCodeGenerator->SetInDestructuredPattern(true);
             byteCodeGenerator->SetInDestructuredPattern(true);
@@ -255,6 +262,7 @@ void Visit(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn pref
         {
         {
             Visit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, prefix, postfix);
             Visit(pnode->AsParseNodeUni()->pnode1, byteCodeGenerator, prefix, postfix);
         }
         }
+        byteCodeGenerator->PopTrackForYield(pnode);
         break;
         break;
 
 
     case knopCall:
     case knopCall:
@@ -379,11 +387,13 @@ void Visit(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn pref
     case knopForOf:
     case knopForOf:
     case knopForAwaitOf:
     case knopForAwaitOf:
         BeginVisitBlock(pnode->AsParseNodeForInOrForOf()->pnodeBlock, byteCodeGenerator);
         BeginVisitBlock(pnode->AsParseNodeForInOrForOf()->pnodeBlock, byteCodeGenerator);
+        byteCodeGenerator->PushTrackForYield(pnode);
         Visit(pnode->AsParseNodeForInOrForOf()->pnodeLval, byteCodeGenerator, prefix, postfix);
         Visit(pnode->AsParseNodeForInOrForOf()->pnodeLval, byteCodeGenerator, prefix, postfix);
         Visit(pnode->AsParseNodeForInOrForOf()->pnodeObj, byteCodeGenerator, prefix, postfix);
         Visit(pnode->AsParseNodeForInOrForOf()->pnodeObj, byteCodeGenerator, prefix, postfix);
         byteCodeGenerator->EnterLoop();
         byteCodeGenerator->EnterLoop();
         Visit(pnode->AsParseNodeForInOrForOf()->pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
         Visit(pnode->AsParseNodeForInOrForOf()->pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
         byteCodeGenerator->ExitLoop();
         byteCodeGenerator->ExitLoop();
+        byteCodeGenerator->PopTrackForYield(pnode);
         EndVisitBlock(pnode->AsParseNodeForInOrForOf()->pnodeBlock, byteCodeGenerator);
         EndVisitBlock(pnode->AsParseNodeForInOrForOf()->pnodeBlock, byteCodeGenerator);
         break;
         break;
     // PTNODE(knopReturn     , "return"    ,None    ,Uni  ,fnopNone)
     // PTNODE(knopReturn     , "return"    ,None    ,Uni  ,fnopNone)
@@ -441,8 +451,10 @@ void Visit(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn pref
         break;
         break;
     // PTNODE(knopTryCatchFinally,"try-catch-finally",None,TryCatchFinally,fnopCleanup)
     // PTNODE(knopTryCatchFinally,"try-catch-finally",None,TryCatchFinally,fnopCleanup)
     case knopTryFinally:
     case knopTryFinally:
+        byteCodeGenerator->PushTrackForYield(pnode);
         Visit(pnode->AsParseNodeTryFinally()->pnodeTry, byteCodeGenerator, prefix, postfix, pnode);
         Visit(pnode->AsParseNodeTryFinally()->pnodeTry, byteCodeGenerator, prefix, postfix, pnode);
         Visit(pnode->AsParseNodeTryFinally()->pnodeFinally, byteCodeGenerator, prefix, postfix, pnode);
         Visit(pnode->AsParseNodeTryFinally()->pnodeFinally, byteCodeGenerator, prefix, postfix, pnode);
+        byteCodeGenerator->PopTrackForYield(pnode);
         break;
         break;
     // PTNODE(knopTryCatch      , "try-catch" ,None    ,TryCatch  ,fnopCleanup)
     // PTNODE(knopTryCatch      , "try-catch" ,None    ,TryCatch  ,fnopCleanup)
     case knopTryCatch:
     case knopTryCatch:
@@ -728,6 +740,8 @@ ByteCodeGenerator::ByteCodeGenerator(Js::ScriptContext* scriptContext, Js::Scope
     flags(0),
     flags(0),
     funcInfoStack(nullptr),
     funcInfoStack(nullptr),
     jumpCleanupList(nullptr),
     jumpCleanupList(nullptr),
+    nodesToTrackForYield(nullptr),
+    nodesWithYield(nullptr),
     pRootFunc(nullptr),
     pRootFunc(nullptr),
     pCurrentFunction(nullptr),
     pCurrentFunction(nullptr),
     globalScope(nullptr),
     globalScope(nullptr),
@@ -769,6 +783,52 @@ void ByteCodeGenerator::AddFuncInfoToFinalizationSet(FuncInfo * funcInfo)
     this->funcInfosToFinalize->Prepend(funcInfo);
     this->funcInfosToFinalize->Prepend(funcInfo);
 }
 }
 
 
+void ByteCodeGenerator::PushTrackForYield(ParseNode* node)
+{
+    if (this->nodesToTrackForYield == nullptr)
+    {
+        this->nodesToTrackForYield = Anew(alloc, SList<ParseNode*>, alloc);
+    }
+    this->nodesToTrackForYield->Push(node);
+}
+
+void ByteCodeGenerator::PopTrackForYield(ParseNode* node)
+{
+    Assert (this->nodesToTrackForYield != nullptr);
+    Assert (this->nodesToTrackForYield->Top() == node);
+    this->nodesToTrackForYield->Pop();
+    if (this->nodesToTrackForYield->Empty())
+    {
+        this->nodesToTrackForYield = nullptr;
+    }
+    else if (this->GetHasYield(node))
+    {
+        this->SetHasYield();
+    }
+}
+
+void ByteCodeGenerator::SetHasYield()
+{
+    if (this->nodesToTrackForYield != nullptr)
+    {
+        Assert(!this->nodesToTrackForYield->Empty());
+        if (this->nodesWithYield == nullptr)
+        {
+            this->nodesWithYield = Anew(alloc, SList<ParseNode*>, alloc);
+        }
+        this->nodesWithYield->Push(this->nodesToTrackForYield->Top());
+    }
+}
+
+bool ByteCodeGenerator::GetHasYield(ParseNode* pnode)
+{
+    if (this->nodesWithYield != nullptr && this->nodesWithYield->Has(pnode))
+    {
+        return true;
+    }
+    return false;
+}
+
 /* static */
 /* static */
 bool ByteCodeGenerator::IsFalse(ParseNode* node)
 bool ByteCodeGenerator::IsFalse(ParseNode* node)
 {
 {
@@ -2203,6 +2263,8 @@ void ByteCodeGenerator::Begin(
     this->envDepth = 0;
     this->envDepth = 0;
     this->trackEnvDepth = false;
     this->trackEnvDepth = false;
     this->funcInfosToFinalize = nullptr;
     this->funcInfosToFinalize = nullptr;
+    this->nodesToTrackForYield = nullptr;
+    this->nodesWithYield = nullptr;
 
 
     this->funcInfoStack = Anew(alloc, SList<FuncInfo*>, alloc);
     this->funcInfoStack = Anew(alloc, SList<FuncInfo*>, alloc);
     this->jumpCleanupList = Anew(alloc, JumpCleanupList, alloc);
     this->jumpCleanupList = Anew(alloc, JumpCleanupList, alloc);

+ 7 - 0
lib/Runtime/ByteCode/ByteCodeGenerator.h

@@ -62,6 +62,8 @@ private:
     Js::ParseableFunctionInfo * pRootFunc;
     Js::ParseableFunctionInfo * pRootFunc;
 
 
     SList<FuncInfo*> * funcInfosToFinalize;
     SList<FuncInfo*> * funcInfosToFinalize;
+    SList<ParseNode*> * nodesToTrackForYield;
+    SList<ParseNode*> * nodesWithYield;
 
 
     using JumpCleanupList = DList<JumpCleanupInfo, ArenaAllocator>;
     using JumpCleanupList = DList<JumpCleanupInfo, ArenaAllocator>;
     JumpCleanupList* jumpCleanupList;
     JumpCleanupList* jumpCleanupList;
@@ -485,6 +487,11 @@ public:
     bool HasJumpCleanup() { return !this->jumpCleanupList->Empty(); }
     bool HasJumpCleanup() { return !this->jumpCleanupList->Empty(); }
     void EmitJumpCleanup(ParseNode* target, FuncInfo* funcInfo);
     void EmitJumpCleanup(ParseNode* target, FuncInfo* funcInfo);
 
 
+    void ByteCodeGenerator::SetHasYield();
+    bool ByteCodeGenerator::GetHasYield(ParseNode* node);
+    void ByteCodeGenerator::PopTrackForYield(ParseNode* node);
+    void ByteCodeGenerator::PushTrackForYield(ParseNode* node);
+
 private:
 private:
     bool NeedCheckBlockVar(Symbol* sym, Scope* scope, FuncInfo* funcInfo) const;
     bool NeedCheckBlockVar(Symbol* sym, Scope* scope, FuncInfo* funcInfo) const;
 
 

+ 78 - 4
test/es6GeneratorJit/async-jit-bugs.js

@@ -4,7 +4,7 @@
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
 //-------------------------------------------------------------------------------------------------------
 //-------------------------------------------------------------------------------------------------------
 
 
-function main() {
+function testOne() {
     const v2 = [13.37,13.37,13.37,13.37,13.37];
     const v2 = [13.37,13.37,13.37,13.37,13.37];
     async function v4(v5,v6,v7,v8) {
     async function v4(v5,v6,v7,v8) {
         const v10 = 0;
         const v10 = 0;
@@ -26,8 +26,82 @@ function main() {
             const v38 = --v33;
             const v38 = --v33;
         }
         }
         const v39 = 128;
         const v39 = 128;
-        print("pass")
     }
     }
-v4("vEBD7ei78q");
+    return v4("vEBD7ei78q");
 }
 }
-main();
+
+// BugIssue #7034
+function testTwo() {
+    let finallyCount = 0;
+    let throwCount = 0;
+    async function asyncFinally() {
+        for (let i = 0; i < 1000; ++i){
+            try {
+                if (i > 170) {
+                    ++throwCount;
+                    throw 1;
+                }
+            }
+            finally {
+                ++finallyCount;
+            }
+        }
+    }
+    return asyncFinally ().catch((e) => {
+        if (throwCount != 1) {
+            throw new Error ("Wrong number of throws within async function expected 1 but received " + throwCount);
+        }
+        if (e != 1) {
+            throw new Error ("Wrong value thrown from async function expected 1 but received " + e);
+        }
+        if (finallyCount != 172) {
+            throw new Error ("Wrong number of finally calls from async function expected 172 but received " + finallyCount);
+        }
+    });
+}
+
+function testThree() {
+    let finallyCount = 0;
+    let throwCount = 0;
+    async function asyncFinallyAwait() {
+        for (let i = 0; i < 1000; ++i){
+            try {
+                if (i > 170) {
+                    ++throwCount;
+                    throw 1;
+                }
+            }
+            finally {
+                await 5;
+                ++finallyCount;
+            }
+        }
+    }
+    return asyncFinallyAwait().catch((e) => {
+        if (throwCount != 1) {
+            throw new Error ("Wrong number of throws within async function expected 1 but received " + throwCount);
+        }
+        if (e != 1) {
+            throw new Error ("Wrong value thrown from async function expected 1 but received " + e);
+        }
+        if (finallyCount != 172) {
+            throw new Error ("Wrong number of finally calls from async function expected 172 but received " + finallyCount);
+        }
+    });
+}
+
+// BugIssue #7016
+function testFour()
+{
+    async function test() {
+        var i8 = new Int8Array(256);
+        var IntArr0 = [];
+        for (var _strvar1 of i8) {
+            for (var _strvar1 of IntArr0) {}
+        }
+    }
+    return test();
+}
+
+
+Promise.all([testOne(), testTwo(), testThree(), testFour()]).then(()=>{print("pass")}, (e)=>{print (e)});