Optimize out gc polls in basic blocks with regular PInvokes (#41581)
authorJan Kotas <jkotas@microsoft.com>
Tue, 1 Sep 2020 01:18:23 +0000 (18:18 -0700)
committerGitHub <noreply@github.com>
Tue, 1 Sep 2020 01:18:23 +0000 (18:18 -0700)
Regular PInvokes poll for GC. It means that the explicit gc polls can be skipped in basic blocks that contain regular PInvoke.

src/coreclr/src/jit/flowgraph.cpp

index 48f3273..fd6ec58 100644 (file)
@@ -3563,6 +3563,49 @@ void Compiler::fgInitBlockVarSets()
     fgBBVarSetsInited = true;
 }
 
+//------------------------------------------------------------------------
+// blockNeedsGCPoll: Determine whether the block needs GC poll inserted
+//
+// Arguments:
+//   block         - the block to check
+//
+// Notes:
+//    The GC poll may not be required because of optimizations applied earlier
+//    or because of GC poll done implicitly by regular unmanaged calls.
+//
+// Returns:
+//    Whether the GC poll needs to be inserted after the block
+//
+static bool blockNeedsGCPoll(BasicBlock* block)
+{
+    bool blockMayNeedGCPoll = false;
+    for (Statement* stmt = block->FirstNonPhiDef(); stmt != nullptr; stmt = stmt->GetNextStmt())
+    {
+        if ((stmt->GetRootNode()->gtFlags & GTF_CALL) != 0)
+        {
+            for (GenTree* tree = stmt->GetTreeList(); tree != nullptr; tree = tree->gtNext)
+            {
+                if (tree->OperGet() == GT_CALL)
+                {
+                    GenTreeCall* call = tree->AsCall();
+                    if (call->IsUnmanaged())
+                    {
+                        if (!call->IsSuppressGCTransition())
+                        {
+                            // If the block contains regular unmanaged call, we can depend on it
+                            // to poll for GC. No need to scan further.
+                            return false;
+                        }
+
+                        blockMayNeedGCPoll = true;
+                    }
+                }
+            }
+        }
+    }
+    return blockMayNeedGCPoll;
+}
+
 //------------------------------------------------------------------------------
 // fgInsertGCPolls : Insert GC polls for basic blocks containing calls to methods
 //                   with SuppressGCTransitionAttribute.
@@ -3572,6 +3615,9 @@ void Compiler::fgInitBlockVarSets()
 //    find the basic blocks that require GC polls; when optimizing the tree nodes
 //    are scanned to find calls to methods with SuppressGCTransitionAttribute.
 //
+//    This must be done after any transformations that would add control flow between
+//    calls.
+//
 // Returns:
 //    PhaseStatus indicating what, if anything, was changed.
 //
@@ -3598,43 +3644,12 @@ PhaseStatus Compiler::fgInsertGCPolls()
 
     BasicBlock* block;
 
-    // Walk through the blocks and hunt for a block that has needs a GC Poll
+    // Walk through the blocks and hunt for a block that needs a GC Poll
     for (block = fgFirstBB; block; block = block->bbNext)
     {
-        bool blockNeedsGCPoll = false;
-        if (opts.OptimizationDisabled())
-        {
-            if ((block->bbFlags & BBF_HAS_SUPPRESSGC_CALL) != 0)
-            {
-                blockNeedsGCPoll = true;
-            }
-        }
-        else
-        {
-            // When optimizations are enabled, we can't rely on BBF_HAS_SUPPRESSGC_CALL flag:
-            // the call could've been moved, e.g., hoisted from a loop, CSE'd, etc.
-            for (Statement* stmt = block->FirstNonPhiDef(); !blockNeedsGCPoll && (stmt != nullptr);
-                 stmt            = stmt->GetNextStmt())
-            {
-                if ((stmt->GetRootNode()->gtFlags & GTF_CALL) != 0)
-                {
-                    for (GenTree* tree = stmt->GetTreeList(); !blockNeedsGCPoll && (tree != nullptr);
-                         tree          = tree->gtNext)
-                    {
-                        if (tree->OperGet() == GT_CALL)
-                        {
-                            GenTreeCall* call = tree->AsCall();
-                            if (call->IsUnmanaged() && call->IsSuppressGCTransition())
-                            {
-                                blockNeedsGCPoll = true;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        if (!blockNeedsGCPoll)
+        // When optimizations are enabled, we can't rely on BBF_HAS_SUPPRESSGC_CALL flag:
+        // the call could've been moved, e.g., hoisted from a loop, CSE'd, etc.
+        if (opts.OptimizationDisabled() ? ((block->bbFlags & BBF_HAS_SUPPRESSGC_CALL) == 0) : !blockNeedsGCPoll(block))
         {
             continue;
         }