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