Skip to content

Optimize better with maximum array length #69735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,26 @@ bool IntegralRange::Contains(int64_t value) const
// value - the symbolic value in question
//
// Return Value:
// Integer correspoding to the symbolic value.
// Integer corresponding to the symbolic value.
//
/* static */ int64_t IntegralRange::SymbolicToRealValue(SymbolicIntegerValue value)
{
static const int64_t SymbolicToRealMap[]{INT64_MIN, INT32_MIN, INT16_MIN, INT8_MIN, 0,
1, INT8_MAX, UINT8_MAX, INT16_MAX, UINT16_MAX,
INT32_MAX, UINT32_MAX, INT64_MAX};
static const int64_t SymbolicToRealMap[]{
INT64_MIN, // SymbolicIntegerValue::LongMin
INT32_MIN, // SymbolicIntegerValue::IntMin
INT16_MIN, // SymbolicIntegerValue::ShortMin
INT8_MIN, // SymbolicIntegerValue::ByteMin
0, // SymbolicIntegerValue::Zero
1, // SymbolicIntegerValue::One
INT8_MAX, // SymbolicIntegerValue::ByteMax
UINT8_MAX, // SymbolicIntegerValue::UByteMax
INT16_MAX, // SymbolicIntegerValue::ShortMax
UINT16_MAX, // SymbolicIntegerValue::UShortMax
CORINFO_Array_MaxLength, // SymbolicIntegerValue::ArrayLenMax
INT32_MAX, // SymbolicIntegerValue::IntMax
UINT32_MAX, // SymbolicIntegerValue::UIntMax
INT64_MAX // SymbolicIntegerValue::LongMax
};

assert(sizeof(SymbolicIntegerValue) == sizeof(int32_t));
assert(SymbolicToRealMap[static_cast<int32_t>(SymbolicIntegerValue::LongMin)] == INT64_MIN);
Expand Down Expand Up @@ -145,7 +158,7 @@ bool IntegralRange::Contains(int64_t value) const
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::One};

case GT_ARR_LENGTH:
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::IntMax};
return {SymbolicIntegerValue::Zero, SymbolicIntegerValue::ArrayLenMax};

case GT_CALL:
if (node->AsCall()->NormalizesSmallTypesOnReturn())
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,7 @@ enum class SymbolicIntegerValue : int32_t
UByteMax,
ShortMax,
UShortMax,
ArrayLenMax,
IntMax,
UIntMax,
LongMax,
Expand Down
24 changes: 16 additions & 8 deletions src/coreclr/jit/rangecheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1056,13 +1056,7 @@ Range RangeCheck::ComputeRangeForLocalDef(BasicBlock* block,
return range;
}

// https://msdn.microsoft.com/en-us/windows/apps/hh285054.aspx
// CLR throws IDS_EE_ARRAY_DIMENSIONS_EXCEEDED if array length is > INT_MAX.
// new byte[INT_MAX]; still throws OutOfMemoryException on my system with 32 GB RAM.
// I believe practical limits are still smaller than this number.
#define ARRLEN_MAX (0x7FFFFFFF)

// Get the limit's maximum possible value, treating array length to be ARRLEN_MAX.
// Get the limit's maximum possible value.
bool RangeCheck::GetLimitMax(Limit& limit, int* pMax)
{
int& max1 = *pMax;
Expand All @@ -1077,7 +1071,21 @@ bool RangeCheck::GetLimitMax(Limit& limit, int* pMax)
int tmp = GetArrLength(limit.vn);
if (tmp <= 0)
{
tmp = ARRLEN_MAX;
// If we can't figure out the array length, use the maximum array length,
// CORINFO_Array_MaxLength (0x7FFFFFC7). However, we get here also when
// we can't find a Span/ReadOnlySpan bounds check length, and these have
// a maximum length of INT_MAX (0x7FFFFFFF). If limit.vn refers to a
// GT_ARR_LENGTH node, then it's an array length, otherwise use the INT_MAX value.

if (m_pCompiler->vnStore->IsVNArrLen(limit.vn))
{
tmp = CORINFO_Array_MaxLength;
}
else
{
const int MaxSpanLength = 0x7FFFFFFF;
tmp = MaxSpanLength;
}
}
if (IntAddOverflows(tmp, limit.GetConstant()))
{
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/rangecheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,8 @@ class RangeCheck
// Inspect the assertions about the current ValueNum to refine pRange
void MergeEdgeAssertions(ValueNum num, ASSERT_VALARG_TP assertions, Range* pRange);

// The maximum possible value of the given "limit." If such a value could not be determined
// return "false." For example: ARRLEN_MAX for array length.
// The maximum possible value of the given "limit". If such a value could not be determined
// return "false". For example: CORINFO_Array_MaxLength for array length.
bool GetLimitMax(Limit& limit, int* pMax);

// Does the addition of the two limits overflow?
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5749,7 +5749,7 @@ bool ValueNumStore::IsVNCheckedBound(ValueNum vn)
if (m_checkedBoundVNs.TryGetValue(vn, &dummy))
{
// This VN appeared as the conservative VN of the length argument of some
// GT_ARR_BOUND node.
// GT_BOUNDS_CHECK node.
return true;
}
if (IsVNArrLen(vn))
Expand Down