Don't re-scan previously visited blocks in helperexpansion.cpp (#85201)
authorEgor Bogatov <egorbo@gmail.com>
Wed, 26 Apr 2023 05:34:01 +0000 (07:34 +0200)
committerGitHub <noreply@github.com>
Wed, 26 Apr 2023 05:34:01 +0000 (07:34 +0200)
src/coreclr/jit/compiler.h
src/coreclr/jit/helperexpansion.cpp

index 718967d..d05f9d1 100644 (file)
@@ -5305,20 +5305,20 @@ public:
     void SplitTreesRandomly();
     void SplitTreesRemoveCommas();
 
-    template <bool (Compiler::*ExpansionFunction)(BasicBlock*, Statement*, GenTreeCall*)>
+    template <bool (Compiler::*ExpansionFunction)(BasicBlock**, Statement*, GenTreeCall*)>
     PhaseStatus fgExpandHelper(bool skipRarelyRunBlocks);
 
-    template <bool (Compiler::*ExpansionFunction)(BasicBlock*, Statement*, GenTreeCall*)>
-    bool fgExpandHelperForBlock(BasicBlock* block);
+    template <bool (Compiler::*ExpansionFunction)(BasicBlock**, Statement*, GenTreeCall*)>
+    bool fgExpandHelperForBlock(BasicBlock** pBlock);
 
     PhaseStatus fgExpandRuntimeLookups();
-    bool fgExpandRuntimeLookupsForCall(BasicBlock* block, Statement* stmt, GenTreeCall* call);
+    bool fgExpandRuntimeLookupsForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call);
 
     PhaseStatus fgExpandThreadLocalAccess();
-    bool fgExpandThreadLocalAccessForCall(BasicBlock* block, Statement* stmt, GenTreeCall* call);
+    bool fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call);
 
     PhaseStatus fgExpandStaticInit();
-    bool fgExpandStaticInitForCall(BasicBlock* block, Statement* stmt, GenTreeCall* call);
+    bool fgExpandStaticInitForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call);
 
     PhaseStatus fgInsertGCPolls();
     BasicBlock* fgCreateGCPoll(GCPollType pollType, BasicBlock* block);
index 2ce9482..e614e78 100644 (file)
@@ -93,8 +93,28 @@ PhaseStatus Compiler::fgExpandRuntimeLookups()
     return fgExpandHelper<&Compiler::fgExpandRuntimeLookupsForCall>(false);
 }
 
-bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock* block, Statement* stmt, GenTreeCall* call)
+//------------------------------------------------------------------------------
+// fgExpandRuntimeLookupsForCall : partially expand runtime lookups helper calls
+//    to add a nullcheck [+ size check] and a fast path
+//
+// Arguments:
+//    pBlock - Block containing the helper call to expand. If expansion is performed,
+//             this is updated to the new block that was an outcome of block splitting.
+//    stmt   - Statement containing the helper call
+//    call   - The helper call
+//
+// Returns:
+//    true if a runtime lookup was found and expanded.
+//
+// Notes:
+//    The runtime lookup itself is needed to access a handle in code shared between
+//    generic instantiations. The lookup depends on the typeContext which is only available at
+//    runtime, and not at compile - time. See ASCII block diagrams in comments below for
+//    better understanding how this phase expands runtime lookups.
+//
+bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call)
 {
+    BasicBlock* block = *pBlock;
     assert(call->IsHelperCall());
 
     if (!call->IsExpRuntimeLookup())
@@ -150,6 +170,7 @@ bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock* block, Statement* stmt,
     GenTree**   callUse      = nullptr;
     Statement*  newFirstStmt = nullptr;
     block                    = fgSplitBlockBeforeTree(block, stmt, call, &newFirstStmt, &callUse);
+    *pBlock                  = block;
     assert(prevBb != nullptr && block != nullptr);
 
     // Block ops inserted by the split need to be morphed here since we are after morph.
@@ -433,9 +454,10 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess()
 //                             that access fields marked with [ThreadLocal].
 //
 // Arguments:
-//    block - Block containing the helper call to expand
-//    stmt  - Statement containing the helper call
-//    call  - The helper call
+//    pBlock - Block containing the helper call to expand. If expansion is performed,
+//             this is updated to the new block that was an outcome of block splitting.
+//    stmt   - Statement containing the helper call
+//    call   - The helper call
 //
 //
 // Returns:
@@ -450,8 +472,9 @@ PhaseStatus Compiler::fgExpandThreadLocalAccess()
 //    If the entry is not present, the helper is called, which would make an entry of current static block
 //    in the cache.
 //
-bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock* block, Statement* stmt, GenTreeCall* call)
+bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call)
 {
+    BasicBlock* block = *pBlock;
     assert(call->IsHelperCall());
     if (!call->IsExpTLSFieldAccess())
     {
@@ -489,6 +512,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock* block, Statement* st
     Statement*  newFirstStmt = nullptr;
     DebugInfo   debugInfo    = stmt->GetDebugInfo();
     block                    = fgSplitBlockBeforeTree(block, stmt, call, &newFirstStmt, &callUse);
+    *pBlock                  = block;
     assert(prevBb != nullptr && block != nullptr);
 
     // Block ops inserted by the split need to be morphed here since we are after morph.
@@ -682,7 +706,7 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock* block, Statement* st
 // Returns:
 //    true if there was any helper that was expanded.
 //
-template <bool (Compiler::*ExpansionFunction)(BasicBlock*, Statement*, GenTreeCall*)>
+template <bool (Compiler::*ExpansionFunction)(BasicBlock**, Statement*, GenTreeCall*)>
 PhaseStatus Compiler::fgExpandHelper(bool skipRarelyRunBlocks)
 {
     PhaseStatus result = PhaseStatus::MODIFIED_NOTHING;
@@ -695,9 +719,14 @@ PhaseStatus Compiler::fgExpandHelper(bool skipRarelyRunBlocks)
         }
 
         // Expand and visit the last block again to find more candidates
-        while (fgExpandHelperForBlock<ExpansionFunction>(block))
+        INDEBUG(BasicBlock* origBlock = block);
+        while (fgExpandHelperForBlock<ExpansionFunction>(&block))
         {
             result = PhaseStatus::MODIFIED_EVERYTHING;
+#ifdef DEBUG
+            assert(origBlock != block);
+            origBlock = block;
+#endif
         }
     }
 
@@ -715,16 +744,17 @@ PhaseStatus Compiler::fgExpandHelper(bool skipRarelyRunBlocks)
 //    invoke `fgExpand` if any of the tree node was a helper call.
 //
 // Arguments:
-//    block    - block to scan for static initializations
+//    pBlock   - Block containing the helper call to expand. If expansion is performed,
+//               this is updated to the new block that was an outcome of block splitting.
 //    fgExpand - function that expands the helper call
 //
 // Returns:
 //    true if a helper was expanded
 //
-template <bool (Compiler::*ExpansionFunction)(BasicBlock*, Statement*, GenTreeCall*)>
-bool Compiler::fgExpandHelperForBlock(BasicBlock* block)
+template <bool (Compiler::*ExpansionFunction)(BasicBlock**, Statement*, GenTreeCall*)>
+bool Compiler::fgExpandHelperForBlock(BasicBlock** pBlock)
 {
-    for (Statement* const stmt : block->NonPhiStatements())
+    for (Statement* const stmt : (*pBlock)->NonPhiStatements())
     {
         if ((stmt->GetRootNode()->gtFlags & GTF_CALL) == 0)
         {
@@ -739,7 +769,7 @@ bool Compiler::fgExpandHelperForBlock(BasicBlock* block)
                 continue;
             }
 
-            if ((this->*ExpansionFunction)(block, stmt, tree->AsCall()))
+            if ((this->*ExpansionFunction)(pBlock, stmt, tree->AsCall()))
             {
                 return true;
             }
@@ -796,15 +826,17 @@ PhaseStatus Compiler::fgExpandStaticInit()
 //    Also, see fgExpandStaticInit's comments.
 //
 // Arguments:
-//    block  - call's block
-//    stmt   - call's statement
-//    call   - call that represents a static initialization
+//    pBlock - Block containing the helper call to expand. If expansion is performed,
+//             this is updated to the new block that was an outcome of block splitting.
+//    stmt   - Statement containing the helper call
+//    call   - The helper call
 //
 // Returns:
 //    true if a static initialization was expanded
 //
-bool Compiler::fgExpandStaticInitForCall(BasicBlock* block, Statement* stmt, GenTreeCall* call)
+bool Compiler::fgExpandStaticInitForCall(BasicBlock** pBlock, Statement* stmt, GenTreeCall* call)
 {
+    BasicBlock* block = *pBlock;
     assert(call->IsHelperCall());
 
     bool                    isGc       = false;
@@ -848,6 +880,7 @@ bool Compiler::fgExpandStaticInitForCall(BasicBlock* block, Statement* stmt, Gen
     GenTree**   callUse      = nullptr;
     Statement*  newFirstStmt = nullptr;
     block                    = fgSplitBlockBeforeTree(block, stmt, call, &newFirstStmt, &callUse);
+    *pBlock                  = block;
     assert(prevBb != nullptr && block != nullptr);
 
     // Block ops inserted by the split need to be morphed here since we are after morph.