Skip to content

Commit

Permalink
Cover more methods, add ignore case support.
Browse files Browse the repository at this point in the history
  • Loading branch information
EgorBo committed Dec 25, 2020
1 parent 0561cac commit 1f25caf
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 12 deletions.
88 changes: 76 additions & 12 deletions src/coreclr/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4318,9 +4318,14 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
break;
}

case NI_System_MemoryExtensions_Equals:
case NI_System_MemoryExtensions_EqualsOrdinal:
case NI_System_MemoryExtensions_EqualsOrdinalIgnoreCase:
case NI_System_MemoryExtensions_SequenceEqual:
case NI_System_MemoryExtensions_StartsWith:
{
assert((sig->numArgs == 2) || (sig->numArgs == 3));

// We're looking for:
//
// bool x1 = arg0.StartsWith(String.op_Implicit("cstr"));
Expand All @@ -4331,11 +4336,49 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
// bool x1 = arg0.Length >= 4 && *(arg0._pointer) == ToHexConst("cstr");
// bool x2 = arg0.Length == 4 && *(arg0._pointer) == ToHexConst("cstr");
//
// TODO: Do the same for OrdinalIgnoreCase/InvariantIngoreCase
//
GenTree* arg0 = impStackTop(1).val;
GenTree* arg1 = impStackTop(0).val;
if (arg1->OperIs(GT_RET_EXPR) && (sig->numArgs == 2))
GenTree* arg0 = impStackTop(sig->numArgs - 1).val;
GenTree* arg1 = impStackTop(sig->numArgs - 2).val;
bool ignoreCase = (ni == NI_System_MemoryExtensions_EqualsOrdinalIgnoreCase);

if ((sig->numArgs == 3) && impStackTop(0).val->IsCnsIntOrI())
{
assert((ni == NI_System_MemoryExtensions_Equals) ||
(ni == NI_System_MemoryExtensions_StartsWith));

// See StringComparison.cs
const int CurrentCulture = 0;
const int CurrentCultureIgnoreCase = 1;
const int InvariantCulture = 2;
const int InvariantCultureIgnoreCase = 3;
const int Ordinal = 4;
const int OrdinalIgnoreCase = 5;

int mode = (int)impStackTop(0).val->AsIntCon()->IconValue();
if ((mode == InvariantCulture) || (mode == Ordinal))
{
ignoreCase = false;
}
else if ((mode == InvariantCultureIgnoreCase) || (mode == OrdinalIgnoreCase))
{
ignoreCase = true;
}
else
{
assert((mode == CurrentCulture) || (mode == CurrentCultureIgnoreCase));
return nullptr;
}
}
else if (sig->numArgs == 3)
{
// Comparison mode is not a constant.
return nullptr;
}
else
{
assert(sig->numArgs == 2);
}

if (arg1->OperIs(GT_RET_EXPR))
{
GenTreeCall* strToSpanCall = arg1->AsRetExpr()->gtInlineCandidate->AsCall();
if (!(strToSpanCall->gtFlags & CORINFO_FLG_JIT_INTRINSIC) ||
Expand All @@ -4352,13 +4395,15 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,

GenTreeStrCon* strCon = strToSpanCall->gtCallArgs->GetNode()->AsStrCon();
bool startsWith = (ni == NI_System_MemoryExtensions_StartsWith);
GenTree* newNode = impUnrollSpanComparisonWithStrCon(arg0, strCon, false, startsWith);
GenTree* newNode = impUnrollSpanComparisonWithStrCon(arg0, strCon, ignoreCase, startsWith);
if (newNode != nullptr)
{
retNode = newNode;
strToSpanCall->ReplaceWith(gtNewNothingNode(), this);
impPopStack();
impPopStack();
for (unsigned i = 0; i < sig->numArgs; i++)
{
impPopStack();
}
break;
}
}
Expand Down Expand Up @@ -4635,6 +4680,7 @@ GenTree* Compiler::impUnrollSpanComparisonWithStrCon(GenTree* span,
{
if (startsWith)
{
// Any span starts with "", return true
return gtNewIconNode(1);
}
else
Expand All @@ -4643,7 +4689,7 @@ GenTree* Compiler::impUnrollSpanComparisonWithStrCon(GenTree* span,
return nullptr;
}
}
if (strLen == 1)
else if (strLen == 1)
{
cmpType = TYP_SHORT;
}
Expand Down Expand Up @@ -4690,8 +4736,11 @@ GenTree* Compiler::impUnrollSpanComparisonWithStrCon(GenTree* span,
{
if (!canBeLowercased)
{
// For this case we can't just do "x | 0x0020002000200020UL"
// TODO: Still can be implemented, see UInt64OrdinalIgnoreCaseAscii
// TODO: Implement logic from UInt64OrdinalIgnoreCaseAscii
//
// bool x = (((IND ^ strAsUlong)<<2) &
// (((strAsUlong+0x0005000500050005ul)|0x00A000A000A000A0ul)+0x001A001A001A001Aul)|0xFF7FFF7FFF7FFF7Ful)==0;
//
return nullptr;
}
strAsUlong |= 0x0020002000200020UL;
Expand Down Expand Up @@ -4739,7 +4788,10 @@ GenTree* Compiler::impUnrollSpanComparisonWithStrCon(GenTree* span,
spanDataIndir = gtNewOperNode(GT_OR, cmpType, spanDataIndir, gtNewIconNode(0x0020002000200020UL, cmpType));
}

// TODO: for length == 3 (not supported yet) we need to do two indir cmp ops
// TODO: for length == 3 (not supported yet) we need to do two indir cmp ops:
//
// bool x = (*(Int64*)span._pointer ^ 0xXXX) | (*((Int32*)span._pointer + 2) ^ 0xYYY) != 0
//
GenTree* indirCmp = gtNewOperNode(GT_EQ, TYP_INT, spanDataIndir, constStrAsIntCon);
GenTree* spanLenField = gtNewFieldRef(TYP_INT, lengthHnd, spanRef, lengthOffset);
GenTreeColon* colon = new (this, GT_COLON) GenTreeColon(TYP_INT, indirCmp, gtNewIconNode(0));
Expand Down Expand Up @@ -5026,6 +5078,18 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
{
result = NI_System_MemoryExtensions_SequenceEqual;
}
else if (strcmp(methodName, "Equals") == 0)
{
result = NI_System_MemoryExtensions_Equals;
}
else if (strcmp(methodName, "EqualsOrdinal") == 0)
{
result = NI_System_MemoryExtensions_EqualsOrdinal;
}
else if (strcmp(methodName, "EqualsOrdinalIgnoreCase") == 0)
{
result = NI_System_MemoryExtensions_EqualsOrdinalIgnoreCase;
}
}
else if (strcmp(className, "String") == 0)
{
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/namedintrinsiclist.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ enum NamedIntrinsic : unsigned short
NI_System_Type_IsAssignableFrom,
NI_System_Type_IsAssignableTo,
NI_System_Array_Clone,
NI_System_MemoryExtensions_Equals,
NI_System_MemoryExtensions_EqualsOrdinal,
NI_System_MemoryExtensions_EqualsOrdinalIgnoreCase,
NI_System_MemoryExtensions_StartsWith,
NI_System_MemoryExtensions_SequenceEqual,
NI_System_String_op_Implicit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public static bool Contains(this ReadOnlySpan<char> span, ReadOnlySpan<char> val
/// <param name="other">The value to compare with the source span.</param>
/// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="other"/> are compared.</param>
/// </summary>
[Intrinsic]
public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> other, StringComparison comparisonType)
{
string.CheckStringComparison(comparisonType);
Expand All @@ -66,6 +67,7 @@ public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> other
}
}

[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool EqualsOrdinal(this ReadOnlySpan<char> span, ReadOnlySpan<char> value)
{
Expand All @@ -76,6 +78,7 @@ internal static bool EqualsOrdinal(this ReadOnlySpan<char> span, ReadOnlySpan<ch
return span.SequenceEqual(value);
}

[Intrinsic]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool EqualsOrdinalIgnoreCase(this ReadOnlySpan<char> span, ReadOnlySpan<char> value)
{
Expand Down Expand Up @@ -331,6 +334,7 @@ ref MemoryMarshal.GetReference(value),
/// <param name="span">The source span.</param>
/// <param name="value">The sequence to compare to the beginning of the source span.</param>
/// <param name="comparisonType">One of the enumeration values that determines how the <paramref name="span"/> and <paramref name="value"/> are compared.</param>
[Intrinsic]
public static bool StartsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
{
string.CheckStringComparison(comparisonType);
Expand Down

0 comments on commit 1f25caf

Please sign in to comment.