JIT: finally cloning -- handle fall through out of try (dotnet/coreclr#18416)
authorAndy Ayers <andya@microsoft.com>
Tue, 12 Jun 2018 16:49:36 +0000 (09:49 -0700)
committerGitHub <noreply@github.com>
Tue, 12 Jun 2018 16:49:36 +0000 (09:49 -0700)
When searching for a handler to clone, handle the case where
control can fall out of the try into a callfinally pair.

Closes dotnet/coreclr#18353.

Commit migrated from https://github.com/dotnet/coreclr/commit/065c9b33eba2894452624245689c194cd7879eb3

src/coreclr/src/jit/flowgraph.cpp

index 55601f6..df05fa4 100644 (file)
@@ -23731,6 +23731,11 @@ void Compiler::fgRemoveEmptyFinally()
                 BasicBlock* const leaveBlock          = currentBlock->bbNext;
                 BasicBlock* const postTryFinallyBlock = leaveBlock->bbJumpDest;
 
+                JITDUMP("Modifying callfinally BB%02u leave BB%02u finally BB%02u continuation BB%02u\n",
+                        currentBlock->bbNum, leaveBlock->bbNum, firstBlock->bbNum, postTryFinallyBlock->bbNum);
+                JITDUMP("so that BB%02u jumps to BB%02u; then remove BB%02u\n", currentBlock->bbNum,
+                        postTryFinallyBlock->bbNum, leaveBlock->bbNum);
+
                 noway_assert(leaveBlock->bbJumpKind == BBJ_ALWAYS);
 
                 currentBlock->bbJumpDest = postTryFinallyBlock;
@@ -23758,6 +23763,8 @@ void Compiler::fgRemoveEmptyFinally()
             currentBlock = nextBlock;
         }
 
+        JITDUMP("Remove now-unreachable handler BB%02u\n", firstBlock->bbNum);
+
         // Handler block should now be unreferenced, since the only
         // explicit references to it were in call finallys.
         firstBlock->bbRefs = 0;
@@ -24392,15 +24399,27 @@ void Compiler::fgCloneFinally()
         for (BasicBlock* block = lastTryBlock; block != beforeTryBlock; block = block->bbPrev)
         {
 #if FEATURE_EH_CALLFINALLY_THUNKS
-            // Look for blocks that are always jumps to a call finally
-            // pair that targets our finally.
-            if (block->bbJumpKind != BBJ_ALWAYS)
+            // Blocks that transfer control to callfinallies are usually
+            // BBJ_ALWAYS blocks, but the last block of a try may fall
+            // through to a callfinally.
+            BasicBlock* jumpDest = nullptr;
+
+            if ((block->bbJumpKind == BBJ_NONE) && (block == lastTryBlock))
             {
-                continue;
+                jumpDest = block->bbNext;
+            }
+            else if (block->bbJumpKind == BBJ_ALWAYS)
+            {
+                jumpDest = block->bbJumpDest;
             }
 
-            BasicBlock* const jumpDest = block->bbJumpDest;
+            if (jumpDest == nullptr)
+            {
+                continue;
+            }
 
+            // The jumpDest must be a callfinally that in turn invokes the
+            // finally of interest.
             if (!jumpDest->isBBCallAlwaysPair() || (jumpDest->bbJumpDest != firstBlock))
             {
                 continue;