diff --git a/src/coreclr/src/jit/compiler.cpp b/src/coreclr/src/jit/compiler.cpp index 7eaf9f5cf32589..eb935c9d6c9be8 100644 --- a/src/coreclr/src/jit/compiler.cpp +++ b/src/coreclr/src/jit/compiler.cpp @@ -4270,49 +4270,57 @@ void Compiler::EndPhase(Phases phase) // void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags* compileFlags) { - if (compIsForInlining()) - { - // Notify root instance that an inline attempt is about to import IL - impInlineRoot()->m_inlineStrategy->NoteImport(); - } - - hashBv::Init(this); + // Prepare for importation + // + auto preImportPhase = [this]() { + if (compIsForInlining()) + { + // Notify root instance that an inline attempt is about to import IL + impInlineRoot()->m_inlineStrategy->NoteImport(); + } - VarSetOps::AssignAllowUninitRhs(this, compCurLife, VarSetOps::UninitVal()); + hashBv::Init(this); - // The temp holding the secret stub argument is used by fgImport() when importing the intrinsic. - if (info.compPublishStubParam) - { - assert(lvaStubArgumentVar == BAD_VAR_NUM); - lvaStubArgumentVar = lvaGrabTempWithImplicitUse(false DEBUGARG("stub argument")); - lvaTable[lvaStubArgumentVar].lvType = TYP_I_IMPL; - } + VarSetOps::AssignAllowUninitRhs(this, compCurLife, VarSetOps::UninitVal()); - EndPhase(PHASE_PRE_IMPORT); + // The temp holding the secret stub argument is used by fgImport() when importing the intrinsic. + if (info.compPublishStubParam) + { + assert(lvaStubArgumentVar == BAD_VAR_NUM); + lvaStubArgumentVar = lvaGrabTempWithImplicitUse(false DEBUGARG("stub argument")); + lvaTable[lvaStubArgumentVar].lvType = TYP_I_IMPL; + } + }; + DoPhase(this, PHASE_PRE_IMPORT, preImportPhase); compFunctionTraceStart(); - // Convert the instrs in each basic block to a tree based intermediate representation - fgImport(); + // Import: convert the instrs in each basic block to a tree based intermediate representation + // + auto importPhase = [this]() { + fgImport(); - assert(!fgComputePredsDone); - if (fgCheapPredsValid) - { - // Remove cheap predecessors before inlining and fat call transformation; - // allowing the cheap predecessor lists to be inserted causes problems - // with splitting existing blocks. - fgRemovePreds(); - } + assert(!fgComputePredsDone); + if (fgCheapPredsValid) + { + // Remove cheap predecessors before inlining and fat call transformation; + // allowing the cheap predecessor lists to be inserted causes problems + // with splitting existing blocks. + fgRemovePreds(); + } + }; + DoPhase(this, PHASE_IMPORTATION, importPhase); // Transform indirect calls that require control flow expansion. - fgTransformIndirectCalls(); + // + DoPhase(this, PHASE_INDXCALL, &Compiler::fgTransformIndirectCalls); - EndPhase(PHASE_IMPORTATION); + // PostImportPhase: cleanup inlinees + // + auto postImportPhase = [this]() { - if (compIsForInlining()) - { - // Abandon inlining if fgImport() failed for any reason - if (!compDonotInline()) + // If this is a viable inline candidate + if (compIsForInlining() && !compDonotInline()) { // Filter out unimported BBs fgRemoveEmptyBlocks(); @@ -4335,8 +4343,12 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags } } } + }; + DoPhase(this, PHASE_POST_IMPORT, postImportPhase); - EndPhase(PHASE_POST_IMPORT); + // If we're importing for inlining, we're done. + if (compIsForInlining()) + { #ifdef FEATURE_JIT_METHOD_PERF if (pCompJitTimer != nullptr) @@ -4371,7 +4383,7 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags if (compileFlags->IsSet(JitFlags::JIT_FLAG_BBINSTR)) { - fgInstrumentMethod(); + DoPhase(this, PHASE_IBCINSTR, &Compiler::fgInstrumentMethod); } // We could allow ESP frames. Just need to reserve space for @@ -4395,94 +4407,84 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags // compLocallocUsed = true; } - EndPhase(PHASE_POST_IMPORT); - - // Initialize the BlockSet epoch - NewBasicBlockEpoch(); - // Start phases that are broadly called morphing, and includes // global morph, as well as other phases that massage the trees so // that we can generate code out of them. - fgOutgoingArgTemps = nullptr; + // + auto morphInitPhase = [this]() { -#ifdef DEBUG - if (verbose) - { - printf("*************** In fgMorph()\n"); - } - if (verboseTrees) - { - fgDispBasicBlocks(true); - } -#endif // DEBUG + // Initialize the BlockSet epoch + NewBasicBlockEpoch(); - // Insert call to class constructor as the first basic block if - // we were asked to do so. - if (info.compCompHnd->initClass(nullptr /* field */, info.compMethodHnd /* method */, - impTokenLookupContextHandle /* context */) & - CORINFO_INITCLASS_USE_HELPER) - { - fgEnsureFirstBBisScratch(); - fgNewStmtAtBeg(fgFirstBB, fgInitThisClass()); - } + fgOutgoingArgTemps = nullptr; + + // Insert call to class constructor as the first basic block if + // we were asked to do so. + if (info.compCompHnd->initClass(nullptr /* field */, info.compMethodHnd /* method */, + impTokenLookupContextHandle /* context */) & + CORINFO_INITCLASS_USE_HELPER) + { + fgEnsureFirstBBisScratch(); + fgNewStmtAtBeg(fgFirstBB, fgInitThisClass()); + } #ifdef DEBUG - if (opts.compGcChecks) - { - for (unsigned i = 0; i < info.compArgsCount; i++) + if (opts.compGcChecks) { - if (lvaTable[i].TypeGet() == TYP_REF) + for (unsigned i = 0; i < info.compArgsCount; i++) { - // confirm that the argument is a GC pointer (for debugging (GC stress)) - GenTree* op = gtNewLclvNode(i, TYP_REF); - GenTreeCall::Use* args = gtNewCallArgs(op); - op = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_VOID, args); + if (lvaTable[i].TypeGet() == TYP_REF) + { + // confirm that the argument is a GC pointer (for debugging (GC stress)) + GenTree* op = gtNewLclvNode(i, TYP_REF); + GenTreeCall::Use* args = gtNewCallArgs(op); + op = gtNewHelperCallNode(CORINFO_HELP_CHECK_OBJ, TYP_VOID, args); - fgEnsureFirstBBisScratch(); - fgNewStmtAtEnd(fgFirstBB, op); + fgEnsureFirstBBisScratch(); + fgNewStmtAtEnd(fgFirstBB, op); - if (verbose) - { - printf("\ncompGcChecks tree:\n"); - gtDispTree(op); + if (verbose) + { + printf("\ncompGcChecks tree:\n"); + gtDispTree(op); + } } } } - } #endif // DEBUG #if defined(DEBUG) && defined(TARGET_XARCH) - if (opts.compStackCheckOnRet) - { - lvaReturnSpCheck = lvaGrabTempWithImplicitUse(false DEBUGARG("ReturnSpCheck")); - lvaTable[lvaReturnSpCheck].lvType = TYP_I_IMPL; - } + if (opts.compStackCheckOnRet) + { + lvaReturnSpCheck = lvaGrabTempWithImplicitUse(false DEBUGARG("ReturnSpCheck")); + lvaTable[lvaReturnSpCheck].lvType = TYP_I_IMPL; + } #endif // defined(DEBUG) && defined(TARGET_XARCH) #if defined(DEBUG) && defined(TARGET_X86) - if (opts.compStackCheckOnCall) - { - lvaCallSpCheck = lvaGrabTempWithImplicitUse(false DEBUGARG("CallSpCheck")); - lvaTable[lvaCallSpCheck].lvType = TYP_I_IMPL; - } + if (opts.compStackCheckOnCall) + { + lvaCallSpCheck = lvaGrabTempWithImplicitUse(false DEBUGARG("CallSpCheck")); + lvaTable[lvaCallSpCheck].lvType = TYP_I_IMPL; + } #endif // defined(DEBUG) && defined(TARGET_X86) - // Filter out unimported BBs - fgRemoveEmptyBlocks(); + // Filter out unimported BBs + fgRemoveEmptyBlocks(); + }; + DoPhase(this, PHASE_MORPH_INIT, morphInitPhase); #ifdef DEBUG // Inliner could add basic blocks. Check that the flowgraph data is up-to-date fgDebugCheckBBlist(false, false); #endif // DEBUG - EndPhase(PHASE_MORPH_INIT); - // Inline callee methods into this root method - fgInline(); - - RecordStateAtEndOfInlining(); // Record "start" values for post-inlining cycles and elapsed time. + // + DoPhase(this, PHASE_MORPH_INLINE, &Compiler::fgInline); - EndPhase(PHASE_MORPH_INLINE); + // Record "start" values for post-inlining cycles and elapsed time. + RecordStateAtEndOfInlining(); // Transform each GT_ALLOCOBJ node into either an allocation helper call or // local variable allocation on the stack. @@ -4496,31 +4498,28 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags objectAllocator.Run(); // Add any internal blocks/trees we may need - fgAddInternal(); - -#ifdef DEBUG - // Inliner could add basic blocks. Check that the flowgraph data is up-to-date - fgDebugCheckBBlist(false, false); - // Inliner could clone some trees. - fgDebugCheckNodesUniqueness(); -#endif // DEBUG - - fgRemoveEmptyTry(); - - EndPhase(PHASE_EMPTY_TRY); - - fgRemoveEmptyFinally(); - - EndPhase(PHASE_EMPTY_FINALLY); + // + DoPhase(this, PHASE_MORPH_ADD_INTERNAL, &Compiler::fgAddInternal); - fgMergeFinallyChains(); + // Remove empty try regions + // + DoPhase(this, PHASE_EMPTY_TRY, &Compiler::fgRemoveEmptyTry); - EndPhase(PHASE_MERGE_FINALLY_CHAINS); + // Remove empty finally regions + // + DoPhase(this, PHASE_EMPTY_FINALLY, &Compiler::fgRemoveEmptyFinally); - fgCloneFinally(); - fgUpdateFinallyTargetFlags(); + // Streamline chains of finally invocations + // + DoPhase(this, PHASE_MERGE_FINALLY_CHAINS, &Compiler::fgMergeFinallyChains); - EndPhase(PHASE_CLONE_FINALLY); + // Clone code in finallys to reduce overhead for non-exceptional paths + // + auto cloneFinallyPhase = [this]() { + fgCloneFinally(); + fgUpdateFinallyTargetFlags(); + }; + DoPhase(this, PHASE_CLONE_FINALLY, cloneFinallyPhase); #if DEBUG if (lvaEnregEHVars) @@ -4555,40 +4554,49 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags // Compute bbNum, bbRefs and bbPreds // - JITDUMP("\nRenumbering the basic blocks for fgComputePreds\n"); - fgRenumberBlocks(); - // This is the first time full (not cheap) preds will be computed // - noway_assert(!fgComputePredsDone); - fgComputePreds(); + auto computePredsPhase = [this]() { + JITDUMP("\nRenumbering the basic blocks for fgComputePred\n"); + fgRenumberBlocks(); + noway_assert(!fgComputePredsDone); + fgComputePreds(); + }; + DoPhase(this, PHASE_COMPUTE_PREDS, computePredsPhase); // Run an early flow graph simplification pass if (opts.OptimizationEnabled()) { - fgUpdateFlowGraph(); + auto earlyUpdateFlowGraphPhase = [this]() { + const bool doTailDup = false; + fgUpdateFlowGraph(doTailDup); + }; + DoPhase(this, PHASE_EARLY_UPDATE_FLOW_GRAPH, earlyUpdateFlowGraphPhase); } - EndPhase(PHASE_COMPUTE_PREDS); - // From this point on the flowgraph information such as bbNum, // bbRefs or bbPreds has to be kept updated + // + // Promote struct locals + // + auto promoteStructsPhase = [this]() { - // For x64 and ARM64 we need to mark irregular parameters - lvaRefCountState = RCS_EARLY; - fgResetImplicitByRefRefCount(); - - // Promote struct locals if necessary - fgPromoteStructs(); + // For x64 and ARM64 we need to mark irregular parameters + lvaRefCountState = RCS_EARLY; + fgResetImplicitByRefRefCount(); - // Figure out what locals are address exposed - fgMarkAddressExposedLocals(); + fgPromoteStructs(); + }; + DoPhase(this, PHASE_PROMOTE_STRUCTS, promoteStructsPhase); - EndPhase(PHASE_STR_ADRLCL); + // Figure out what locals are address-taken. + // + DoPhase(this, PHASE_STR_ADRLCL, &Compiler::fgMarkAddressExposedLocals); - // Apply type updates to implicit byref parameters; also choose (based on address-exposed + // Apply the type update to implicit byref parameters; also choose (based on address-exposed // analysis) which implicit byref promotions to keep (requires copy to initialize) or discard. - fgRetypeImplicitByRefArgs(); + // + DoPhase(this, PHASE_MORPH_IMPBYREF, &Compiler::fgRetypeImplicitByRefArgs); #ifdef DEBUG // Now that locals have address-taken and implicit byref marked, we can safely apply stress. @@ -4596,124 +4604,108 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags fgStress64RsltMul(); #endif // DEBUG - EndPhase(PHASE_MORPH_IMPBYREF); - // Morph the trees in all the blocks of the method - fgMorphBlocks(); - - // Fix any LclVar annotations on discarded struct promotion temps for implicit by-ref args - fgMarkDemotedImplicitByRefArgs(); - lvaRefCountState = RCS_INVALID; - - EndPhase(PHASE_MORPH_GLOBAL); + // + auto morphGlobalPhase = [this]() { + fgMorphBlocks(); -#if 0 - JITDUMP("trees after fgMorphBlocks\n"); - DBEXEC(VERBOSE, fgDispBasicBlocks(true)); -#endif + // Fix any LclVar annotations on discarded struct promotion temps for implicit by-ref args + fgMarkDemotedImplicitByRefArgs(); + lvaRefCountState = RCS_INVALID; #if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM) - if (fgNeedToAddFinallyTargetBits) - { - // We previously wiped out the BBF_FINALLY_TARGET bits due to some morphing; add them back. - fgAddFinallyTargetFlags(); - fgNeedToAddFinallyTargetBits = false; - } + if (fgNeedToAddFinallyTargetBits) + { + // We previously wiped out the BBF_FINALLY_TARGET bits due to some morphing; add them back. + fgAddFinallyTargetFlags(); + fgNeedToAddFinallyTargetBits = false; + } #endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM) - // Decide the kind of code we want to generate - fgSetOptions(); + // Decide the kind of code we want to generate + fgSetOptions(); - fgExpandQmarkNodes(); + fgExpandQmarkNodes(); #ifdef DEBUG - compCurBB = nullptr; + compCurBB = nullptr; #endif // DEBUG - // End of the morphing phases - // - // We can now enable all phase checking - EndPhase(PHASE_MORPH_END); - activePhaseChecks = PhaseChecks::CHECK_ALL; + // We can now enable all phase checking + activePhaseChecks = PhaseChecks::CHECK_ALL; + }; + DoPhase(this, PHASE_MORPH_GLOBAL, morphGlobalPhase); // GS security checks for unsafe buffers - if (getNeedsGSSecurityCookie()) - { -#ifdef DEBUG - if (verbose) + // + auto gsPhase = [this]() { + if (getNeedsGSSecurityCookie()) { - printf("\n*************** -GS checks for unsafe buffers \n"); - } -#endif - - gsGSChecksInitCookie(); + gsGSChecksInitCookie(); - if (compGSReorderStackLayout) - { - gsCopyShadowParams(); + if (compGSReorderStackLayout) + { + gsCopyShadowParams(); + } } - -#ifdef DEBUG - if (verbose) + else { - fgDispBasicBlocks(true); - printf("\n"); + JITDUMP("No GS security needed\n"); } -#endif - } - EndPhase(PHASE_GS_COOKIE); - - // GC Poll marking assumes block bbnums match lexical block order, - // so make sure this is the case. - fgRenumberBlocks(); + }; + DoPhase(this, PHASE_GS_COOKIE, gsPhase); // If we need to emit GC Poll calls, mark the blocks that need them now. // This is conservative and can be optimized later. - fgMarkGCPollBlocks(); - EndPhase(PHASE_MARK_GC_POLL_BLOCKS); + // + // GC Poll marking assumes block bbnums match lexical block order, + // so make sure this is the case. + // + auto gcPollPhase = [this]() { + fgRenumberBlocks(); + fgMarkGCPollBlocks(); + }; + DoPhase(this, PHASE_MARK_GC_POLL_BLOCKS, gcPollPhase); // Compute the block and edge weights - fgComputeBlockAndEdgeWeights(); - EndPhase(PHASE_COMPUTE_EDGE_WEIGHTS); + // + DoPhase(this, PHASE_COMPUTE_EDGE_WEIGHTS, &Compiler::fgComputeBlockAndEdgeWeights); #if defined(FEATURE_EH_FUNCLETS) // Create funclets from the EH handlers. - fgCreateFunclets(); - EndPhase(PHASE_CREATE_FUNCLETS); + // + DoPhase(this, PHASE_CREATE_FUNCLETS, &Compiler::fgCreateFunclets); #endif // FEATURE_EH_FUNCLETS if (opts.OptimizationEnabled()) { - fgTailMergeThrows(); - EndPhase(PHASE_MERGE_THROWS); - - optOptimizeLayout(); - EndPhase(PHASE_OPTIMIZE_LAYOUT); - + // Merge common throw blocks + // + DoPhase(this, PHASE_MERGE_THROWS, &Compiler::fgTailMergeThrows); + // Optimize block order + // + DoPhase(this, PHASE_OPTIMIZE_LAYOUT, &Compiler::optOptimizeLayout); // Compute reachability sets and dominators. - fgComputeReachability(); - EndPhase(PHASE_COMPUTE_REACHABILITY); - } + // + DoPhase(this, PHASE_COMPUTE_REACHABILITY, &Compiler::fgComputeReachability); - if (opts.OptimizationEnabled()) - { // Perform loop inversion (i.e. transform "while" loops into // "repeat" loops) and discover and classify natural loops // (e.g. mark iterative loops as such). Also marks loop blocks // and sets bbWeight to the loop nesting levels - optOptimizeLoops(); - EndPhase(PHASE_OPTIMIZE_LOOPS); + // + DoPhase(this, PHASE_OPTIMIZE_LOOPS, &Compiler::optOptimizeLoops); // Clone loops with optimization opportunities, and // choose the one based on dynamic condition evaluation. - optCloneLoops(); - EndPhase(PHASE_CLONE_LOOPS); + // + DoPhase(this, PHASE_CLONE_LOOPS, &Compiler::optCloneLoops); // Unroll loops - optUnrollLoops(); - EndPhase(PHASE_UNROLL_LOOPS); + // + DoPhase(this, PHASE_UNROLL_LOOPS, &Compiler::optUnrollLoops); } #ifdef DEBUG @@ -4721,8 +4713,8 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags #endif // Create the variable table (and compute variable ref counts) - lvaMarkLocalVars(); - EndPhase(PHASE_MARK_LOCAL_VARS); + // + DoPhase(this, PHASE_MARK_LOCAL_VARS, &Compiler::lvaMarkLocalVars); // IMPORTANT, after this point, locals are ref counted. // However, ref counts are not kept incrementally up to date. @@ -4731,37 +4723,22 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags if (opts.OptimizationEnabled()) { // Optimize boolean conditions - optOptimizeBools(); - EndPhase(PHASE_OPTIMIZE_BOOLS); + // + DoPhase(this, PHASE_OPTIMIZE_BOOLS, &Compiler::optOptimizeBools); // optOptimizeBools() might have changed the number of blocks; the dominators/reachability might be bad. } // Figure out the order in which operators are to be evaluated - fgFindOperOrder(); - EndPhase(PHASE_FIND_OPER_ORDER); + // + DoPhase(this, PHASE_FIND_OPER_ORDER, &Compiler::fgFindOperOrder); // Weave the tree lists. Anyone who modifies the tree shapes after // this point is responsible for calling fgSetStmtSeq() to keep the // nodes properly linked. // This can create GC poll calls, and create new BasicBlocks (without updating dominators/reachability). - fgSetBlockOrder(); - EndPhase(PHASE_SET_BLOCK_ORDER); - - // IMPORTANT, after this point, every place where tree topology changes must redo evaluation - // order (gtSetStmtInfo) and relink nodes (fgSetStmtSeq) if required. - CLANG_FORMAT_COMMENT_ANCHOR; - -#ifdef DEBUG - // Now we have determined the order of evaluation and the gtCosts for every node. - // If verbose, dump the full set of trees here before the optimization phases mutate them // - if (verbose) - { - fgDispBasicBlocks(true); // 'true' will call fgDumpTrees() after dumping the BasicBlocks - printf("\n"); - } -#endif + DoPhase(this, PHASE_SET_BLOCK_ORDER, &Compiler::fgSetBlockOrder); // At this point we know if we are fully interruptible or not if (opts.OptimizationEnabled()) @@ -4794,68 +4771,79 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags { if (doSsa) { - fgSsaBuild(); - EndPhase(PHASE_BUILD_SSA); + // Build up SSA form for the IR + // + DoPhase(this, PHASE_BUILD_SSA, &Compiler::fgSsaBuild); } if (doEarlyProp) { // Propagate array length and rewrite getType() method call - optEarlyProp(); - EndPhase(PHASE_EARLY_PROP); + // + DoPhase(this, PHASE_EARLY_PROP, &Compiler::optEarlyProp); } if (doValueNum) { - fgValueNumber(); - EndPhase(PHASE_VALUE_NUMBER); + // Value number the trees + // + DoPhase(this, PHASE_VALUE_NUMBER, &Compiler::fgValueNumber); } if (doLoopHoisting) { // Hoist invariant code out of loops - optHoistLoopCode(); - EndPhase(PHASE_HOIST_LOOP_CODE); + // + DoPhase(this, PHASE_HOIST_LOOP_CODE, &Compiler::optHoistLoopCode); } if (doCopyProp) { // Perform VN based copy propagation - optVnCopyProp(); - EndPhase(PHASE_VN_COPY_PROP); + // + DoPhase(this, PHASE_VN_COPY_PROP, &Compiler::optVnCopyProp); } #if FEATURE_ANYCSE // Remove common sub-expressions - optOptimizeCSEs(); + // + DoPhase(this, PHASE_OPTIMIZE_VALNUM_CSES, &Compiler::optOptimizeCSEs); #endif // FEATURE_ANYCSE #if ASSERTION_PROP if (doAssertionProp) { // Assertion propagation - optAssertionPropMain(); - EndPhase(PHASE_ASSERTION_PROP_MAIN); + // + DoPhase(this, PHASE_ASSERTION_PROP_MAIN, &Compiler::optAssertionPropMain); } if (doRangeAnalysis) { - // Optimize array index range checks - RangeCheck rc(this); - rc.OptimizeRangeChecks(); - EndPhase(PHASE_OPTIMIZE_INDEX_CHECKS); + auto rangePhase = [this]() { + RangeCheck rc(this); + rc.OptimizeRangeChecks(); + }; + + // Bounds check elimination via range analysis + // + DoPhase(this, PHASE_OPTIMIZE_INDEX_CHECKS, rangePhase); } #endif // ASSERTION_PROP - // update the flowgraph if we modified it during the optimization phase if (fgModified) { - fgUpdateFlowGraph(); - EndPhase(PHASE_UPDATE_FLOW_GRAPH); + // update the flowgraph if we modified it during the optimization phase + // + auto optUpdateFlowGraphPhase = [this]() { + const bool doTailDup = false; + fgUpdateFlowGraph(doTailDup); + }; + DoPhase(this, PHASE_OPT_UPDATE_FLOW_GRAPH, optUpdateFlowGraphPhase); // Recompute the edge weight if we have modified the flow graph - fgComputeEdgeWeights(); - EndPhase(PHASE_COMPUTE_EDGE_WEIGHTS2); + // + DoPhase(this, PHASE_COMPUTE_EDGE_WEIGHTS2, &Compiler::fgComputeEdgeWeights); } // Iterate if requested, resetting annotations first. @@ -4873,8 +4861,9 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags compQuirkForPPPflag = compQuirkForPPP(); #endif - fgDetermineFirstColdBlock(); - EndPhase(PHASE_DETERMINE_FIRST_COLD_BLOCK); + // Determine start of cold region if we are hot/cold splitting + // + DoPhase(this, PHASE_DETERMINE_FIRST_COLD_BLOCK, &Compiler::fgDetermineFirstColdBlock); #ifdef DEBUG fgDebugCheckLinks(compStressCompile(STRESS_REMORPH_TREES, 50)); @@ -4903,8 +4892,8 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags // platforms, this will be part of the more general lowering phase. For now, though, we do a separate // pass of "final lowering." We must do this before (final) liveness analysis, because this creates // range check throw blocks, in which the liveness must be correct. - fgSimpleLowering(); - EndPhase(PHASE_SIMPLE_LOWERING); + // + DoPhase(this, PHASE_SIMPLE_LOWERING, &Compiler::fgSimpleLowering); #ifdef DEBUG fgDebugCheckBBlist(); @@ -4937,18 +4926,22 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags m_pLinearScan = getLinearScanAllocator(this); // Lower + // m_pLowering = new (this, CMK_LSRA) Lowering(this, m_pLinearScan); // PHASE_LOWERING m_pLowering->Run(); - StackLevelSetter stackLevelSetter(this); // PHASE_STACK_LEVEL_SETTER + // Set stack levels + // + StackLevelSetter stackLevelSetter(this); stackLevelSetter.Run(); // We can not add any new tracked variables after this point. lvaTrackedFixed = true; // Now that lowering is completed we can proceed to perform register allocation - m_pLinearScan->doLinearScan(); - EndPhase(PHASE_LINEAR_SCAN); + // + auto linearScanPhase = [this]() { m_pLinearScan->doLinearScan(); }; + DoPhase(this, PHASE_LINEAR_SCAN, linearScanPhase); // Copied from rpPredictRegUse() SetFullPtrRegMapRequired(codeGen->GetInterruptible() || !codeGen->isFramePointerUsed()); diff --git a/src/coreclr/src/jit/compphases.h b/src/coreclr/src/jit/compphases.h index ae78751f85215f..12ef4313928237 100644 --- a/src/coreclr/src/jit/compphases.h +++ b/src/coreclr/src/jit/compphases.h @@ -26,20 +26,25 @@ // parent CompPhaseNameMacro(PHASE_PRE_IMPORT, "Pre-import", "PRE-IMP", false, -1, false) CompPhaseNameMacro(PHASE_IMPORTATION, "Importation", "IMPORT", false, -1, true) +CompPhaseNameMacro(PHASE_INDXCALL, "Indirect call transform", "INDXCALL", false, -1, true) CompPhaseNameMacro(PHASE_POST_IMPORT, "Post-import", "POST-IMP", false, -1, false) +CompPhaseNameMacro(PHASE_IBCINSTR, "IBC instrumentation", "IBCINSTR", false, -1, false) CompPhaseNameMacro(PHASE_MORPH_INIT, "Morph - Init", "MOR-INIT" ,false, -1, false) CompPhaseNameMacro(PHASE_MORPH_INLINE, "Morph - Inlining", "MOR-INL", false, -1, true) +CompPhaseNameMacro(PHASE_MORPH_ADD_INTERNAL, "Morph - Add internal blocks", "MOR-ADD", false, -1, true) CompPhaseNameMacro(PHASE_ALLOCATE_OBJECTS, "Allocate Objects", "ALLOC-OBJ", false, -1, false) CompPhaseNameMacro(PHASE_EMPTY_TRY, "Remove empty try", "EMPTYTRY", false, -1, false) CompPhaseNameMacro(PHASE_EMPTY_FINALLY, "Remove empty finally", "EMPTYFIN", false, -1, false) CompPhaseNameMacro(PHASE_MERGE_FINALLY_CHAINS, "Merge callfinally chains", "MRGCFCHN", false, -1, false) CompPhaseNameMacro(PHASE_CLONE_FINALLY, "Clone finally", "CLONEFIN", false, -1, false) +CompPhaseNameMacro(PHASE_COMPUTE_PREDS, "Compute preds", "PREDS", false, -1, false) +CompPhaseNameMacro(PHASE_EARLY_UPDATE_FLOW_GRAPH,"Update flow graph early pass", "UPD-FG-E", false, -1, false) CompPhaseNameMacro(PHASE_STR_ADRLCL, "Morph - Structs/AddrExp", "MOR-STRAL",false, -1, false) CompPhaseNameMacro(PHASE_MORPH_IMPBYREF, "Morph - ByRefs", "MOR-BYREF",false, -1, false) +CompPhaseNameMacro(PHASE_PROMOTE_STRUCTS, "Morph - Promote Structs", "PROMOTER" ,false, -1, false) CompPhaseNameMacro(PHASE_MORPH_GLOBAL, "Morph - Global", "MOR-GLOB", false, -1, false) CompPhaseNameMacro(PHASE_MORPH_END, "Morph - Finish", "MOR-END", false, -1, true) CompPhaseNameMacro(PHASE_GS_COOKIE, "GS Cookie", "GS-COOK", false, -1, false) -CompPhaseNameMacro(PHASE_COMPUTE_PREDS, "Compute preds", "PREDS", false, -1, false) CompPhaseNameMacro(PHASE_MARK_GC_POLL_BLOCKS, "Mark GC poll blocks", "GC-POLL", false, -1, false) CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS, "Compute edge weights (1, false)", "EDG-WGT", false, -1, false) #if defined(FEATURE_EH_FUNCLETS) @@ -77,7 +82,7 @@ CompPhaseNameMacro(PHASE_VN_COPY_PROP, "VN based copy prop", #if ASSERTION_PROP CompPhaseNameMacro(PHASE_ASSERTION_PROP_MAIN, "Assertion prop", "AST-PROP", false, -1, false) #endif -CompPhaseNameMacro(PHASE_UPDATE_FLOW_GRAPH, "Update flow graph", "UPD-FG", false, -1, false) +CompPhaseNameMacro(PHASE_OPT_UPDATE_FLOW_GRAPH, "Update flow graph opt pass", "UPD-FG-O", false, -1, false) CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS2, "Compute edge weights (2, false)", "EDG-WGT2", false, -1, false) CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block", "COLD-BLK", false, -1, true) CompPhaseNameMacro(PHASE_RATIONALIZE, "Rationalize IR", "RAT", false, -1, false) diff --git a/src/coreclr/src/jit/phase.cpp b/src/coreclr/src/jit/phase.cpp index 1d5b0553bcea91..ff67699cf196dc 100644 --- a/src/coreclr/src/jit/phase.cpp +++ b/src/coreclr/src/jit/phase.cpp @@ -27,8 +27,40 @@ void Phase::PrePhase() comp->BeginPhase(m_phase); #ifdef DEBUG + + // To help in the incremental conversion of jit activity to phases + // without greatly increasing dump size or checked jit time, we + // currently whitelist the phases that do pre-phase checks and + // dumps via the phase object, and not via explicit calls from + // the various methods in the phase. + // + // In the long run the aim is to get rid of all pre-phase checks + // and dumps, relying instead on post-phase checks and dumps from + // the preceeding phase. + // + // Currently the list is just the set of phases that have custom + // derivations from the Phase class. + static Phases s_whitelist[] = {PHASE_ALLOCATE_OBJECTS, PHASE_BUILD_SSA, PHASE_RATIONALIZE, PHASE_LOWERING, + PHASE_STACK_LEVEL_SETTER}; + bool doPrePhase = false; + + for (int i = 0; i < sizeof(s_whitelist) / sizeof(Phases); i++) + { + if (m_phase == s_whitelist[i]) + { + doPrePhase = true; + break; + } + } + if (VERBOSE) { + if (doPrePhase) + { + printf("Trees before %s\n", m_name); + comp->fgDispBasicBlocks(true); + } + if (comp->compIsForInlining()) { printf("\n*************** Inline @[%06u] Starting PHASE %s\n", @@ -38,17 +70,17 @@ void Phase::PrePhase() { printf("\n*************** Starting PHASE %s\n", m_name); } - - printf("Trees before %s\n", m_name); - comp->fgDispBasicBlocks(true); } - if ((comp->activePhaseChecks == PhaseChecks::CHECK_ALL) && (comp->expensiveDebugCheckLevel >= 2)) + if (doPrePhase) { - // If everyone used the Phase class, this would duplicate the PostPhase() from the previous phase. - // But, not everyone does, so go ahead and do the check here, too. - comp->fgDebugCheckBBlist(); - comp->fgDebugCheckLinks(); + if ((comp->activePhaseChecks == PhaseChecks::CHECK_ALL) && (comp->expensiveDebugCheckLevel >= 2)) + { + // If everyone used the Phase class, this would duplicate the PostPhase() from the previous phase. + // But, not everyone does, so go ahead and do the check here, too. + comp->fgDebugCheckBBlist(); + comp->fgDebugCheckLinks(); + } } #endif // DEBUG } @@ -59,6 +91,32 @@ void Phase::PrePhase() void Phase::PostPhase() { #ifdef DEBUG + + // To help in the incremental conversion of jit activity to phases + // without greatly increasing dump size or checked jit time, we + // currently whitelist the phases that do post-phase checks and + // dumps via the phase object, and not via explicit calls from + // the various methods in the phase. + // + // As we remove the explicit checks and dumps from each phase, we + // will add to thist list; once all phases are updated, we can + // remove the list entirely. + // + // Currently the list is just the set of phases that have custom + // derivations from the Phase class. + static Phases s_whitelist[] = {PHASE_ALLOCATE_OBJECTS, PHASE_BUILD_SSA, PHASE_RATIONALIZE, PHASE_LOWERING, + PHASE_STACK_LEVEL_SETTER}; + bool doPostPhase = false; + + for (int i = 0; i < sizeof(s_whitelist) / sizeof(Phases); i++) + { + if (m_phase == s_whitelist[i]) + { + doPostPhase = true; + break; + } + } + if (VERBOSE) { if (comp->compIsForInlining()) @@ -71,22 +129,27 @@ void Phase::PostPhase() printf("\n*************** Finishing PHASE %s\n", m_name); } - printf("Trees after %s\n", m_name); - comp->fgDispBasicBlocks(true); - } -#endif // DEBUG + if (doPostPhase) + { + printf("Trees after %s\n", m_name); + comp->fgDispBasicBlocks(true); + } #if DUMP_FLOWGRAPHS - comp->fgDumpFlowGraph(m_phase); + comp->fgDumpFlowGraph(m_phase); #endif // DUMP_FLOWGRAPHS + } -#ifdef DEBUG - if (comp->activePhaseChecks == PhaseChecks::CHECK_ALL) + if (doPostPhase) { - comp->fgDebugCheckBBlist(); - comp->fgDebugCheckLinks(); - comp->fgDebugCheckNodesUniqueness(); + if (comp->activePhaseChecks == PhaseChecks::CHECK_ALL) + { + comp->fgDebugCheckBBlist(); + comp->fgDebugCheckLinks(); + comp->fgDebugCheckNodesUniqueness(); + } } + #endif // DEBUG comp->EndPhase(m_phase); diff --git a/src/coreclr/src/jit/phase.h b/src/coreclr/src/jit/phase.h index 646a8a2ee8a0cc..42b952960be238 100644 --- a/src/coreclr/src/jit/phase.h +++ b/src/coreclr/src/jit/phase.h @@ -30,8 +30,6 @@ class Phase // A phase that accepts a lambda for the actions done by the phase. // -// Would prefer to use std::function via here, but seemingly can't. -// template class ActionPhase final : public Phase { @@ -64,7 +62,8 @@ void DoPhase(Compiler* _compiler, Phases _phase, A _action) class CompilerPhase final : public Phase { public: - CompilerPhase(Compiler* _compiler, Phases _phase, void (Compiler::*_action)()) : Phase(_compiler, _phase) + CompilerPhase(Compiler* _compiler, Phases _phase, void (Compiler::*_action)()) + : Phase(_compiler, _phase), action(_action) { }