@@ -1015,6 +1015,78 @@ bool GenTreeCall::IsPure(Compiler* compiler) const
10151015 compiler->s_helperCallProperties.IsPure(compiler->eeGetHelperNum(gtCallMethHnd));
10161016}
10171017
1018+ //------------------------------------------------------------------------------
1019+ // getArrayLengthFromAllocation: Return the array length for an array allocation
1020+ // helper call.
1021+ //
1022+ // Arguments:
1023+ // tree - The array allocation helper call.
1024+ // block - tree's basic block.
1025+ //
1026+ // Return Value:
1027+ // Return the array length node.
1028+
1029+ GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block))
1030+ {
1031+ assert(tree != nullptr);
1032+
1033+ GenTree* arrayLength = nullptr;
1034+
1035+ if (tree->OperGet() == GT_CALL)
1036+ {
1037+ GenTreeCall* call = tree->AsCall();
1038+ if (call->fgArgInfo == nullptr)
1039+ {
1040+ // Currently this function is only profitable during the late stages
1041+ // so we avoid complicating below code to access the early args.
1042+ return nullptr;
1043+ }
1044+
1045+ if (call->gtCallType == CT_HELPER)
1046+ {
1047+ switch (eeGetHelperNum(call->gtCallMethHnd))
1048+ {
1049+ case CORINFO_HELP_NEWARR_1_DIRECT:
1050+ case CORINFO_HELP_NEWARR_1_OBJ:
1051+ case CORINFO_HELP_NEWARR_1_VC:
1052+ case CORINFO_HELP_NEWARR_1_ALIGN8:
1053+ {
1054+ // This is an array allocation site. Grab the array length node.
1055+ arrayLength = gtArgEntryByArgNum(call, 1)->GetNode();
1056+ break;
1057+ }
1058+
1059+ case CORINFO_HELP_READYTORUN_NEWARR_1:
1060+ {
1061+ // On arm when compiling on certain platforms for ready to run, a handle will be
1062+ // inserted before the length. To handle this case, we will grab the last argument
1063+ // as that's always the length. See fgInitArgInfo for where the handle is inserted.
1064+ int arrLenArgNum = call->fgArgInfo->ArgCount() - 1;
1065+ arrayLength = gtArgEntryByArgNum(call, arrLenArgNum)->GetNode();
1066+ break;
1067+ }
1068+
1069+ default:
1070+ break;
1071+ }
1072+ #ifdef DEBUG
1073+ if ((arrayLength != nullptr) && (block != nullptr))
1074+ {
1075+ optCheckFlagsAreSet(OMF_HAS_NEWARRAY, "OMF_HAS_NEWARRAY", BBF_HAS_NEWARRAY, "BBF_HAS_NEWARRAY", tree,
1076+ block);
1077+ }
1078+ #endif
1079+ }
1080+ }
1081+
1082+ if (arrayLength != nullptr)
1083+ {
1084+ arrayLength = arrayLength->OperIsPutArg() ? arrayLength->gtGetOp1() : arrayLength;
1085+ }
1086+
1087+ return arrayLength;
1088+ }
1089+
10181090//-------------------------------------------------------------------------
10191091// HasSideEffects:
10201092// Returns true if this call has any side effects. All non-helpers are considered to have side-effects. Only helpers
@@ -1052,6 +1124,21 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool
10521124 return true;
10531125 }
10541126
1127+ // Consider array allocators side-effect free for constant length (if it's not negative and fits into i32)
1128+ if (helperProperties.IsAllocator(helper))
1129+ {
1130+ GenTree* arrLen = compiler->getArrayLengthFromAllocation((GenTree*)this DEBUGARG(nullptr));
1131+ // if arrLen is nullptr it means it wasn't an array allocator
1132+ if ((arrLen != nullptr) && arrLen->IsIntCnsFitsInI32())
1133+ {
1134+ ssize_t cns = arrLen->AsIntConCommon()->IconValue();
1135+ if ((cns >= 0) && (cns <= CORINFO_Array_MaxLength))
1136+ {
1137+ return false;
1138+ }
1139+ }
1140+ }
1141+
10551142 // If we also care about exceptions then check if the helper can throw
10561143 if (!ignoreExceptions && !helperProperties.NoThrow(helper))
10571144 {
0 commit comments