@@ -2733,3 +2733,158 @@ bool Compiler::fgLateCastExpansionForCall(BasicBlock** pBlock, Statement* stmt,
27332733
27342734 return true ;
27352735}
2736+
2737+ // ------------------------------------------------------------------------------
2738+ // fgExpandStackArrayAllocations : expand "new helpers" for stack arrays
2739+ //
2740+ // Returns:
2741+ // PhaseStatus indicating what, if anything, was changed.
2742+ //
2743+ PhaseStatus Compiler::fgExpandStackArrayAllocations ()
2744+ {
2745+ PhaseStatus result = PhaseStatus::MODIFIED_NOTHING;
2746+
2747+ if (!doesMethodHaveStackAllocatedArray ())
2748+ {
2749+ // The method being compiled doesn't have any stack allocated arrays.
2750+ return result;
2751+ }
2752+
2753+ // Find allocation sites, and transform them into initializations of the
2754+ // array method table and length, and replace the allocation call with
2755+ // the address of the local array.
2756+ //
2757+ bool modified = false ;
2758+
2759+ for (BasicBlock* const block : Blocks ())
2760+ {
2761+ for (Statement* const stmt : block->Statements ())
2762+ {
2763+ if ((stmt->GetRootNode ()->gtFlags & GTF_CALL) == 0 )
2764+ {
2765+ continue ;
2766+ }
2767+
2768+ for (GenTree* const tree : stmt->TreeList ())
2769+ {
2770+ if (!tree->IsCall ())
2771+ {
2772+ continue ;
2773+ }
2774+
2775+ if (fgExpandStackArrayAllocation (block, stmt, tree->AsCall ()))
2776+ {
2777+ // If we expand, we split the statement's tree
2778+ // so will be done with this statment.
2779+ //
2780+ modified = true ;
2781+ break ;
2782+ }
2783+ }
2784+ }
2785+ }
2786+
2787+ // we cant assert(modified) here as array allocation sites may
2788+ // have been unreachable or dead-coded.
2789+ //
2790+ return modified ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
2791+ }
2792+
2793+ // ------------------------------------------------------------------------------
2794+ // fgExpandStackArrayAllocation: expand new array helpers for stack allocated arrays
2795+ //
2796+ // Arguments:
2797+ // block - block containing the helper call to expand
2798+ // stmt - Statement containing the helper call
2799+ // call - The helper call
2800+ //
2801+ // Returns:
2802+ // true if a runtime lookup was found and expanded.
2803+ //
2804+ bool Compiler::fgExpandStackArrayAllocation (BasicBlock* block, Statement* stmt, GenTreeCall* call)
2805+ {
2806+ if (!call->IsHelperCall ())
2807+ {
2808+ return false ;
2809+ }
2810+
2811+ const CorInfoHelpFunc helper = eeGetHelperNum (call->gtCallMethHnd );
2812+ int lengthArgIndex = -1 ;
2813+
2814+ switch (helper)
2815+ {
2816+ case CORINFO_HELP_NEWARR_1_DIRECT:
2817+ case CORINFO_HELP_NEWARR_1_VC:
2818+ case CORINFO_HELP_NEWARR_1_OBJ:
2819+ case CORINFO_HELP_NEWARR_1_ALIGN8:
2820+ lengthArgIndex = 1 ;
2821+ break ;
2822+
2823+ case CORINFO_HELP_READYTORUN_NEWARR_1:
2824+ lengthArgIndex = 0 ;
2825+ break ;
2826+
2827+ default :
2828+ return false ;
2829+ }
2830+
2831+ // If this is a local array, the new helper will have an arg for the array's address
2832+ //
2833+ CallArg* const stackLocalAddressArg = call->gtArgs .FindWellKnownArg (WellKnownArg::StackArrayLocal);
2834+
2835+ if (stackLocalAddressArg == nullptr )
2836+ {
2837+ return false ;
2838+ }
2839+
2840+ JITDUMP (" Expanding new array helper for stack allocated array at [%06d] in " FMT_BB " :\n " , dspTreeID (call),
2841+ block->bbNum );
2842+ DISPTREE (call);
2843+ JITDUMP (" \n " );
2844+
2845+ Statement* newStmt = nullptr ;
2846+ GenTree** callUse = nullptr ;
2847+ bool split = gtSplitTree (block, stmt, call, &newStmt, &callUse);
2848+
2849+ if (split)
2850+ {
2851+ while ((newStmt != nullptr ) && (newStmt != stmt))
2852+ {
2853+ fgMorphStmtBlockOps (block, newStmt);
2854+ newStmt = newStmt->GetNextStmt ();
2855+ }
2856+ }
2857+
2858+ GenTree* const stackLocalAddress = stackLocalAddressArg->GetNode ();
2859+
2860+ // Initialize the array method table pointer.
2861+ //
2862+ CORINFO_CLASS_HANDLE arrayHnd = (CORINFO_CLASS_HANDLE)call->compileTimeHelperArgumentHandle ;
2863+
2864+ GenTree* const mt = gtNewIconEmbClsHndNode (arrayHnd);
2865+ GenTree* const mtStore = gtNewStoreValueNode (TYP_I_IMPL, stackLocalAddress, mt);
2866+ Statement* const mtStmt = fgNewStmtFromTree (mtStore);
2867+
2868+ fgInsertStmtBefore (block, stmt, mtStmt);
2869+
2870+ // Initialize the array length.
2871+ //
2872+ GenTree* const lengthArg = call->gtArgs .GetArgByIndex (lengthArgIndex)->GetNode ();
2873+ GenTree* const lengthArgInt = fgOptimizeCast (gtNewCastNode (TYP_INT, lengthArg, false , TYP_INT));
2874+ GenTree* const lengthAddress = gtNewOperNode (GT_ADD, TYP_I_IMPL, gtCloneExpr (stackLocalAddress),
2875+ gtNewIconNode (OFFSETOF__CORINFO_Array__length, TYP_I_IMPL));
2876+ GenTree* const lengthStore = gtNewStoreValueNode (TYP_INT, lengthAddress, lengthArgInt);
2877+ Statement* const lenStmt = fgNewStmtFromTree (lengthStore);
2878+
2879+ fgInsertStmtBefore (block, stmt, lenStmt);
2880+
2881+ // Replace call with local address
2882+ //
2883+ *callUse = gtCloneExpr (stackLocalAddress);
2884+ DEBUG_DESTROY_NODE (call);
2885+
2886+ fgMorphStmtBlockOps (block, stmt);
2887+ gtUpdateStmtSideEffects (stmt);
2888+
2889+ return true ;
2890+ }
0 commit comments