From cccda305c332c6ba3a5f6e0003c30aa10fd4b887 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 26 May 2023 14:03:52 -0700 Subject: [PATCH] JIT: morph blocks in RPO When optimizing, process blocks in RPO. Disallow creation of new blocks and new flow edges (the latter with certain preapproved exceptions). Morph does not yet take advantage of the RPO to enable more optimization. Contributes to #93246. --- src/coreclr/jit/block.h | 1 + src/coreclr/jit/compiler.h | 7 ++-- src/coreclr/jit/fgbasic.cpp | 1 + src/coreclr/jit/fgflow.cpp | 5 +++ src/coreclr/jit/flowgraph.cpp | 2 +- src/coreclr/jit/morph.cpp | 70 +++++++++++++++++++++++++++++++---- 6 files changed, 75 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 7d9e30971ce0ac..86413ce25c40cd 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -440,6 +440,7 @@ enum BasicBlockFlags : unsigned __int64 BBF_TAILCALL_SUCCESSOR = MAKE_BBFLAG(40), // BB has pred that has potential tail call BBF_RECURSIVE_TAILCALL = MAKE_BBFLAG(41), // Block has recursive tailcall that may turn into a loop BBF_NO_CSE_IN = MAKE_BBFLAG(42), // Block should kill off any incoming CSE + BBF_CAN_ADD_PRED = MAKE_BBFLAG(43), // Ok to add pred edge to this block, even when "safe" edge creation disabled // The following are sets of flags. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index dfafe03461d349..817e507e378cbc 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -3215,10 +3215,11 @@ class Compiler //========================================================================= // BasicBlock functions #ifdef DEBUG - // This is a debug flag we will use to assert when creating block during codegen - // as this interferes with procedure splitting. If you know what you're doing, set - // it to true before creating the block. (DEBUG only) + // When false, assert when creating a new basic block. bool fgSafeBasicBlockCreation; + + // When false, assert when creating a new flow edge + bool fgSafeFlowEdgeCreation; #endif /* diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index e3f7f4f61521b1..2a736951871e81 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -84,6 +84,7 @@ void Compiler::fgInit() #ifdef DEBUG fgSafeBasicBlockCreation = true; + fgSafeFlowEdgeCreation = true; #endif // DEBUG fgLocalVarLivenessDone = false; diff --git a/src/coreclr/jit/fgflow.cpp b/src/coreclr/jit/fgflow.cpp index 363ad7c2a17dc8..12cb0e87dc7099 100644 --- a/src/coreclr/jit/fgflow.cpp +++ b/src/coreclr/jit/fgflow.cpp @@ -162,6 +162,11 @@ FlowEdge* Compiler::fgAddRefPred(BasicBlock* block, BasicBlock* blockPred, FlowE } else { + // Create a new edge + // + // We may be disallowing edge creation, except for edges targeting special blocks. + // + assert(fgSafeFlowEdgeCreation || ((block->bbFlags & BBF_CAN_ADD_PRED) != 0)); #if MEASURE_BLOCK_SIZE genFlowNodeCnt += 1; diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 32fb8f457bf51e..1ff2d1544c862a 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -2228,7 +2228,7 @@ class MergedReturns // Add 'return' expression to the return block comp->fgNewStmtAtEnd(newReturnBB, returnExpr); // Flag that this 'return' was generated by return merging so that subsequent - // return block morhping will know to leave it alone. + // return block merging will know to leave it alone. returnExpr->gtFlags |= GTF_RET_MERGED; #ifdef DEBUG diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index e7b54cf37a826d..ff179a6865387d 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -7472,7 +7472,6 @@ void Compiler::fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCa //------------------------------------------------------------------------------ // fgAssignRecursiveCallArgToCallerParam : Assign argument to a recursive call to the corresponding caller parameter. // -// // Arguments: // arg - argument to assign // late - whether to use early or late arg @@ -13892,13 +13891,70 @@ PhaseStatus Compiler::fgMorphBlocks() // Morph all blocks. // - // Note morph can add blocks downstream from the current block, - // and alter (but not null out) the current block's bbNext; - // this iterator ensures they all get visited. - // - for (BasicBlock* block : Blocks()) + if (!optLocalAssertionProp) { - fgMorphBlock(block); + // If we aren't optimizing, we just morph in normal bbNext order. + // + // Note morph can add blocks downstream from the current block, + // and alter (but not null out) the current block's bbNext; + // this iterator ensures they all get visited. + // + for (BasicBlock* block : Blocks()) + { + fgMorphBlock(block); + } + } + else + { + // We are optimizing. Process in RPO. + // + fgRenumberBlocks(); + EnsureBasicBlockEpoch(); + fgComputeEnterBlocksSet(); + fgDfsReversePostorder(); + + // Disallow general creation of new blocks or edges as it + // would invalidate RPO. + // + // Removal of edges, or altering dup counts, is OK. + // + INDEBUG(fgSafeBasicBlockCreation = false;); + INDEBUG(fgSafeFlowEdgeCreation = false;); + + // Allow edge creation to genReturnBB (target of return merging) + // and the scratch block successor (target for tail call to loop). + // This will also disallow dataflow into these blocks. + // + if (genReturnBB != nullptr) + { + genReturnBB->bbFlags |= BBF_CAN_ADD_PRED; + } + if (fgFirstBBisScratch()) + { + fgFirstBB->Next()->bbFlags |= BBF_CAN_ADD_PRED; + } + + unsigned const bbNumMax = fgBBNumMax; + for (unsigned i = 1; i <= bbNumMax; i++) + { + BasicBlock* const block = fgBBReversePostorder[i]; + fgMorphBlock(block); + } + + // Re-enable block and edge creation, and revoke + // special treatment of genReturnBB and the "first" bb + // + INDEBUG(fgSafeBasicBlockCreation = true;); + INDEBUG(fgSafeFlowEdgeCreation = true;); + + if (genReturnBB != nullptr) + { + genReturnBB->bbFlags &= ~BBF_CAN_ADD_PRED; + } + if (fgFirstBBisScratch()) + { + fgFirstBB->Next()->bbFlags &= ~BBF_CAN_ADD_PRED; + } } // Under OSR, we no longer need to specially protect the original method entry