@@ -2367,8 +2367,13 @@ class MergedReturns
2367
2367
assert (searchLimit < maxReturns);
2368
2368
mergedReturnBlock = CreateReturnBB (searchLimit);
2369
2369
comp->genReturnBB = mergedReturnBlock;
2370
- // Downstream code expects the `genReturnBB` to always remain
2371
- // once created, so that it can redirect flow edges to it.
2370
+
2371
+ // In principle we should be able to remove the proctection
2372
+ // we add here, and remove the genReturnBB if it becomes unreachable,
2373
+ // since we now introduce all the flow to it during this phase.
2374
+ //
2375
+ // But we'll keep this as is for now.
2376
+ //
2372
2377
mergedReturnBlock->bbFlags |= BBF_DONT_REMOVE;
2373
2378
}
2374
2379
}
@@ -2471,10 +2476,11 @@ class MergedReturns
2471
2476
// Notes:
2472
2477
// * rewrites shared generic catches in to filters
2473
2478
// * adds code to handle modifiable this
2474
- // * determines number of epilogs and merges returns
2479
+ // * determines number of epilogs and merges "same constant" returns
2475
2480
// * does special setup for pinvoke/reverse pinvoke methods
2476
2481
// * adds callouts and EH for synchronized methods
2477
2482
// * adds just my code callback
2483
+ // * merges returns to common return block if necessary
2478
2484
//
2479
2485
// Returns:
2480
2486
// Suitable phase status.
@@ -2752,6 +2758,20 @@ PhaseStatus Compiler::fgAddInternal()
2752
2758
madeChanges = true ;
2753
2759
}
2754
2760
2761
+ if (genReturnBB != nullptr )
2762
+ {
2763
+ for (BasicBlock* const block : Blocks ())
2764
+ {
2765
+ // We can see BBF_HAS_JMP from CEE_JMP importation. Exclude such here
2766
+ //
2767
+ if ((block != genReturnBB) && block->KindIs (BBJ_RETURN) && ((block->bbFlags & BBF_HAS_JMP) == 0 ))
2768
+ {
2769
+ fgMergeBlockReturn (block);
2770
+ madeChanges = true ;
2771
+ }
2772
+ }
2773
+ }
2774
+
2755
2775
#ifdef DEBUG
2756
2776
if (verbose)
2757
2777
{
@@ -2764,6 +2784,122 @@ PhaseStatus Compiler::fgAddInternal()
2764
2784
return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
2765
2785
}
2766
2786
2787
+ // ------------------------------------------------------------------------
2788
+ // fgMergeBlockReturn: assign the block return value (if any) into the single return temp
2789
+ // and branch to the single return block.
2790
+ //
2791
+ // Arguments:
2792
+ // block - the block to process.
2793
+ //
2794
+ // Returns:
2795
+ // true if block was modified to branch to the single return block
2796
+ //
2797
+ // Notes:
2798
+ // A block is not guaranteed to have a last stmt if its jump kind is BBJ_RETURN.
2799
+ // For example a method returning void could have an empty block with jump kind BBJ_RETURN.
2800
+ // Such blocks do materialize as part of in-lining.
2801
+ //
2802
+ // A block with jump kind BBJ_RETURN does not necessarily need to end with GT_RETURN.
2803
+ // It could end with a tail call or rejected tail call or monitor.exit or a GT_INTRINSIC.
2804
+ // For now it is safe to explicitly check whether last stmt is GT_RETURN if genReturnLocal
2805
+ // is BAD_VAR_NUM.
2806
+ //
2807
+ bool Compiler::fgMergeBlockReturn (BasicBlock* block)
2808
+ {
2809
+ assert (block->KindIs (BBJ_RETURN) && ((block->bbFlags & BBF_HAS_JMP) == 0 ));
2810
+ assert ((genReturnBB != nullptr ) && (genReturnBB != block));
2811
+
2812
+ // TODO: Need to characterize the last top level stmt of a block ending with BBJ_RETURN.
2813
+
2814
+ Statement* lastStmt = block->lastStmt ();
2815
+ GenTree* ret = (lastStmt != nullptr ) ? lastStmt->GetRootNode () : nullptr ;
2816
+
2817
+ if ((ret != nullptr ) && (ret->OperGet () == GT_RETURN) && ((ret->gtFlags & GTF_RET_MERGED) != 0 ))
2818
+ {
2819
+ // This return was generated during epilog merging, so leave it alone
2820
+ return false ;
2821
+ }
2822
+
2823
+ #if !defined(TARGET_X86)
2824
+ if (info.compFlags & CORINFO_FLG_SYNCH)
2825
+ {
2826
+ fgConvertSyncReturnToLeave (block);
2827
+ return true ;
2828
+ }
2829
+ #endif // !TARGET_X86
2830
+
2831
+ // We'll jump to the genReturnBB.
2832
+ //
2833
+ block->SetJumpKindAndTarget (BBJ_ALWAYS, genReturnBB DEBUG_ARG (this ));
2834
+ fgAddRefPred (genReturnBB, block);
2835
+ fgReturnCount--;
2836
+
2837
+ if (genReturnLocal != BAD_VAR_NUM)
2838
+ {
2839
+ // replace the GT_RETURN node to be a STORE_LCL_VAR that stores the return value into genReturnLocal.
2840
+
2841
+ // Method must be returning a value other than TYP_VOID.
2842
+ noway_assert (compMethodHasRetVal ());
2843
+
2844
+ // This block must be ending with a GT_RETURN
2845
+ noway_assert (lastStmt != nullptr );
2846
+ noway_assert (lastStmt->GetNextStmt () == nullptr );
2847
+ noway_assert (ret != nullptr );
2848
+
2849
+ // GT_RETURN must have non-null operand as the method is returning the value assigned to
2850
+ // genReturnLocal
2851
+ noway_assert (ret->OperGet () == GT_RETURN);
2852
+ noway_assert (ret->gtGetOp1 () != nullptr );
2853
+
2854
+ Statement* pAfterStatement = lastStmt;
2855
+ const DebugInfo& di = lastStmt->GetDebugInfo ();
2856
+ GenTree* tree = gtNewTempStore (genReturnLocal, ret->gtGetOp1 (), CHECK_SPILL_NONE, &pAfterStatement, di, block);
2857
+
2858
+ if (pAfterStatement == lastStmt)
2859
+ {
2860
+ lastStmt->SetRootNode (tree);
2861
+ }
2862
+ else
2863
+ {
2864
+ // gtNewTempStore inserted additional statements after last
2865
+ fgRemoveStmt (block, lastStmt);
2866
+ Statement* newStmt = gtNewStmt (tree, di);
2867
+ fgInsertStmtAfter (block, pAfterStatement, newStmt);
2868
+ lastStmt = newStmt;
2869
+ }
2870
+ }
2871
+ else if (ret != nullptr && ret->OperGet () == GT_RETURN)
2872
+ {
2873
+ // This block ends with a GT_RETURN
2874
+ noway_assert (lastStmt != nullptr );
2875
+ noway_assert (lastStmt->GetNextStmt () == nullptr );
2876
+
2877
+ // Must be a void GT_RETURN with null operand; delete it as this block branches to oneReturn
2878
+ // block
2879
+ noway_assert (ret->TypeGet () == TYP_VOID);
2880
+ noway_assert (ret->gtGetOp1 () == nullptr );
2881
+
2882
+ fgRemoveStmt (block, lastStmt);
2883
+ }
2884
+
2885
+ JITDUMP (" \n Update " FMT_BB " to jump to common return block.\n " , block->bbNum );
2886
+ DISPBLOCK (block);
2887
+
2888
+ if (block->hasProfileWeight ())
2889
+ {
2890
+ weight_t const oldWeight = genReturnBB->hasProfileWeight () ? genReturnBB->bbWeight : BB_ZERO_WEIGHT;
2891
+ weight_t const newWeight = oldWeight + block->bbWeight ;
2892
+
2893
+ JITDUMP (" merging profile weight " FMT_WT " from " FMT_BB " to common return " FMT_BB " \n " , block->bbWeight ,
2894
+ block->bbNum , genReturnBB->bbNum );
2895
+
2896
+ genReturnBB->setBBProfileWeight (newWeight);
2897
+ DISPBLOCK (genReturnBB);
2898
+ }
2899
+
2900
+ return true ;
2901
+ }
2902
+
2767
2903
/* ****************************************************************************/
2768
2904
/* ****************************************************************************/
2769
2905
0 commit comments