Skip to content

Commit c830e33

Browse files
EgorBojkotasjakobbotsch
authored
JIT: Optimize unused array allocations (#67205)
Co-authored-by: Jan Kotas <[email protected]> Co-authored-by: Jakob Botsch Nielsen <[email protected]>
1 parent 7281406 commit c830e33

File tree

5 files changed

+95
-53
lines changed

5 files changed

+95
-53
lines changed

src/coreclr/inc/corinfo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,8 @@ struct CORINFO_VarArgInfo
19191919

19201920
#define SIZEOF__CORINFO_Object TARGET_POINTER_SIZE /* methTable */
19211921

1922+
#define CORINFO_Array_MaxLength 0x7FFFFFC7
1923+
19221924
#define OFFSETOF__CORINFO_Array__length SIZEOF__CORINFO_Object
19231925
#ifdef TARGET_64BIT
19241926
#define OFFSETOF__CORINFO_Array__data (OFFSETOF__CORINFO_Array__length + sizeof(uint32_t) /* length */ + sizeof(uint32_t) /* alignpad */)

src/coreclr/jit/earlyprop.cpp

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -29,58 +29,6 @@ bool Compiler::optDoEarlyPropForBlock(BasicBlock* block)
2929
return bbHasArrayRef || bbHasNullCheck;
3030
}
3131

32-
//------------------------------------------------------------------------------
33-
// getArrayLengthFromAllocation: Return the array length for an array allocation
34-
// helper call.
35-
//
36-
// Arguments:
37-
// tree - The array allocation helper call.
38-
// block - tree's basic block.
39-
//
40-
// Return Value:
41-
// Return the array length node.
42-
43-
GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block))
44-
{
45-
assert(tree != nullptr);
46-
47-
GenTree* arrayLength = nullptr;
48-
49-
if (tree->OperGet() == GT_CALL)
50-
{
51-
GenTreeCall* call = tree->AsCall();
52-
53-
if (call->gtCallType == CT_HELPER)
54-
{
55-
if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) ||
56-
call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ) ||
57-
call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_VC) ||
58-
call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_NEWARR_1_ALIGN8))
59-
{
60-
// This is an array allocation site. Grab the array length node.
61-
arrayLength = gtArgEntryByArgNum(call, 1)->GetNode();
62-
}
63-
else if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_READYTORUN_NEWARR_1))
64-
{
65-
// On arm when compiling on certain platforms for ready to run, a handle will be
66-
// inserted before the length. To handle this case, we will grab the last argument
67-
// as that's always the length. See fgInitArgInfo for where the handle is inserted.
68-
int arrLenArgNum = call->fgArgInfo->ArgCount() - 1;
69-
arrayLength = gtArgEntryByArgNum(call, arrLenArgNum)->GetNode();
70-
}
71-
#ifdef DEBUG
72-
if (arrayLength != nullptr)
73-
{
74-
optCheckFlagsAreSet(OMF_HAS_NEWARRAY, "OMF_HAS_NEWARRAY", BBF_HAS_NEWARRAY, "BBF_HAS_NEWARRAY", tree,
75-
block);
76-
}
77-
#endif
78-
}
79-
}
80-
81-
return arrayLength;
82-
}
83-
8432
#ifdef DEBUG
8533
//-----------------------------------------------------------------------------
8634
// optCheckFlagsAreSet: Check that the method flag and the basic block flag are set.

src/coreclr/jit/gentree.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
{

src/tests/GC/API/GC/GetGCMemoryInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ static void MakeTemporarySOHAllocations()
7171
int byteArraySize = 1000;
7272
for (int i = 0; i < (totalTempAllocBytes / byteArraySize); i++)
7373
{
74-
byte[] byteArray = new byte[byteArraySize];
74+
GC.KeepAlive(new byte[byteArraySize]);
7575
}
7676
}
7777

src/tests/profiler/gc/gcallocate.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ public static int RunTest(String[] args)
1414
{
1515
int[] large = new int[100000];
1616
int[] pinned = GC.AllocateArray<int>(32, true);
17+
18+
// don't let the jit to optimize these allocations
19+
GC.KeepAlive(large);
20+
GC.KeepAlive(pinned);
21+
1722
Console.WriteLine("Test Passed");
1823
return 100;
1924
}

0 commit comments

Comments
 (0)