Clean up impSpanEqualsOrStartsWith (#86129)
authorEgor Bogatov <egorbo@gmail.com>
Mon, 15 May 2023 22:13:15 +0000 (00:13 +0200)
committerGitHub <noreply@github.com>
Mon, 15 May 2023 22:13:15 +0000 (00:13 +0200)
Co-authored-by: SingleAccretion <62474226+SingleAccretion@users.noreply.github.com>
src/coreclr/inc/corinfo.h
src/coreclr/jit/compiler.h
src/coreclr/jit/compiler.hpp
src/coreclr/jit/importercalls.cpp
src/coreclr/jit/importervectorization.cpp

index 3208abe..04a8f2f 100644 (file)
@@ -1955,6 +1955,10 @@ struct CORINFO_VarArgInfo
 
 #define OFFSETOF__CORINFO_NullableOfT__hasValue           0
 
+#define OFFSETOF__CORINFO_Span__reference                 0
+#define OFFSETOF__CORINFO_Span__length                    TARGET_POINTER_SIZE
+
+
 /* data to optimize delegate construction */
 struct DelegateCtorArgs
 {
index 9dd385f..8b2b06c 100644 (file)
@@ -3480,7 +3480,7 @@ public:
     unsigned lvaLclSize(unsigned varNum);
     unsigned lvaLclExactSize(unsigned varNum);
 
-    bool lvaHaveManyLocals() const;
+    bool lvaHaveManyLocals(float percent = 1.0f) const;
 
     unsigned lvaGrabTemp(bool shortLifetime DEBUGARG(const char* reason));
     unsigned lvaGrabTemps(unsigned cnt DEBUGARG(const char* reason));
@@ -3907,7 +3907,7 @@ protected:
     };
     GenTree* impStringEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* sig, unsigned methodFlags);
     GenTree* impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO* sig, unsigned methodFlags);
-    GenTree* impExpandHalfConstEquals(GenTreeLclVar*   data,
+    GenTree* impExpandHalfConstEquals(GenTreeLclVarCommon*   data,
                                       GenTree*         lengthFld,
                                       bool             checkForNull,
                                       bool             startsWith,
@@ -3915,16 +3915,16 @@ protected:
                                       int              len,
                                       int              dataOffset,
                                       StringComparison cmpMode);
-    GenTree* impCreateCompareInd(GenTreeLclVar*        obj,
+    GenTree* impCreateCompareInd(GenTreeLclVarCommon*        obj,
                                  var_types             type,
                                  ssize_t               offset,
                                  ssize_t               value,
                                  StringComparison      ignoreCase,
                                  StringComparisonJoint joint = Eq);
     GenTree* impExpandHalfConstEqualsSWAR(
-        GenTreeLclVar* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode);
+        GenTreeLclVarCommon* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode);
     GenTree* impExpandHalfConstEqualsSIMD(
-        GenTreeLclVar* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode);
+        GenTreeLclVarCommon* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode);
     GenTreeStrCon* impGetStrConFromSpan(GenTree* span);
 
     GenTree* impIntrinsic(GenTree*                newobjThis,
index 83f90d4..f8f1715 100644 (file)
@@ -1526,9 +1526,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 */
 
-inline bool Compiler::lvaHaveManyLocals() const
+inline bool Compiler::lvaHaveManyLocals(float percent) const
 {
-    return (lvaCount >= (unsigned)JitConfig.JitMaxLocalsToTrack());
+    assert((percent >= 0.0) && (percent <= 1.0));
+    return (lvaCount >= (unsigned)JitConfig.JitMaxLocalsToTrack() * percent);
 }
 
 /*****************************************************************************
index 1b15353..3a625b7 100644 (file)
@@ -2221,10 +2221,10 @@ GenTree* Compiler::impCreateSpanIntrinsic(CORINFO_SIG_INFO* sig)
     unsigned             spanTempNum = lvaGrabTemp(true DEBUGARG("ReadOnlySpan<T> for CreateSpan<T>"));
     lvaSetStruct(spanTempNum, spanHnd, false);
 
-    GenTreeLclFld* pointerField    = gtNewLclFldNode(spanTempNum, TYP_BYREF, 0);
+    GenTreeLclFld* pointerField    = gtNewLclFldNode(spanTempNum, TYP_BYREF, OFFSETOF__CORINFO_Span__reference);
     GenTree*       pointerFieldAsg = gtNewAssignNode(pointerField, pointerValue);
 
-    GenTreeLclFld* lengthField    = gtNewLclFldNode(spanTempNum, TYP_INT, TARGET_POINTER_SIZE);
+    GenTreeLclFld* lengthField    = gtNewLclFldNode(spanTempNum, TYP_INT, OFFSETOF__CORINFO_Span__length);
     GenTree*       lengthFieldAsg = gtNewAssignNode(lengthField, lengthValue);
 
     // Now append a few statements the initialize the span
index c9592f8..67f4c58 100644 (file)
@@ -104,7 +104,7 @@ static bool ConvertToLowerCase(WCHAR* input, WCHAR* mask, int length)
 //    for impExpandHalfConstEquals
 //
 GenTree* Compiler::impExpandHalfConstEqualsSIMD(
-    GenTreeLclVar* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode)
+    GenTreeLclVarCommon* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode)
 {
     assert(len >= 8 && len <= MaxPossibleUnrollSize);
 
@@ -253,7 +253,7 @@ GenTree* Compiler::impExpandHalfConstEqualsSIMD(
 //    A tree with indirect load and comparison
 //    nullptr in case of 'ignoreCase' mode and non-ASCII value
 //
-GenTree* Compiler::impCreateCompareInd(GenTreeLclVar*        obj,
+GenTree* Compiler::impCreateCompareInd(GenTreeLclVarCommon*  obj,
                                        var_types             type,
                                        ssize_t               offset,
                                        ssize_t               value,
@@ -308,7 +308,7 @@ GenTree* Compiler::impCreateCompareInd(GenTreeLclVar*        obj,
 //    for impExpandHalfConstEquals
 //
 GenTree* Compiler::impExpandHalfConstEqualsSWAR(
-    GenTreeLclVar* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode)
+    GenTreeLclVarCommon* data, WCHAR* cns, int len, int dataOffset, StringComparison cmpMode)
 {
     assert(len >= 1 && len <= 8);
 
@@ -342,11 +342,11 @@ GenTree* Compiler::impExpandHalfConstEqualsSWAR(
         //
         // where offset for value2 is 2 bytes (1 char)
         //
-        UINT32   value1     = MAKEINT32(cns[0], cns[1]);
-        UINT32   value2     = MAKEINT32(cns[1], cns[2]);
-        GenTree* firstIndir = impCreateCompareInd(data, TYP_INT, dataOffset, value1, cmpMode, Xor);
-        GenTree* secondIndir =
-            impCreateCompareInd(gtClone(data)->AsLclVar(), TYP_INT, dataOffset + sizeof(USHORT), value2, cmpMode, Xor);
+        UINT32   value1      = MAKEINT32(cns[0], cns[1]);
+        UINT32   value2      = MAKEINT32(cns[1], cns[2]);
+        GenTree* firstIndir  = impCreateCompareInd(data, TYP_INT, dataOffset, value1, cmpMode, Xor);
+        GenTree* secondIndir = impCreateCompareInd(gtClone(data)->AsLclVarCommon(), TYP_INT,
+                                                   dataOffset + sizeof(USHORT), value2, cmpMode, Xor);
 
         if ((firstIndir == nullptr) || (secondIndir == nullptr))
         {
@@ -380,8 +380,9 @@ GenTree* Compiler::impExpandHalfConstEqualsSWAR(
         UINT32   value2     = MAKEINT32(cns[len - 2], cns[len - 1]);
         GenTree* firstIndir = impCreateCompareInd(data, TYP_LONG, dataOffset, value1, cmpMode, Xor);
 
-        ssize_t  offset      = dataOffset + len * sizeof(WCHAR) - sizeof(UINT32);
-        GenTree* secondIndir = impCreateCompareInd(gtClone(data)->AsLclVar(), TYP_INT, offset, value2, cmpMode, Xor);
+        ssize_t  offset = dataOffset + len * sizeof(WCHAR) - sizeof(UINT32);
+        GenTree* secondIndir =
+            impCreateCompareInd(gtClone(data)->AsLclVarCommon(), TYP_INT, offset, value2, cmpMode, Xor);
 
         if ((firstIndir == nullptr) || (secondIndir == nullptr))
         {
@@ -400,7 +401,7 @@ GenTree* Compiler::impExpandHalfConstEqualsSWAR(
     GenTree* firstIndir = impCreateCompareInd(data, TYP_LONG, dataOffset, value1, cmpMode, Xor);
 
     ssize_t  offset      = dataOffset + len * sizeof(WCHAR) - sizeof(UINT64);
-    GenTree* secondIndir = impCreateCompareInd(gtClone(data)->AsLclVar(), TYP_LONG, offset, value2, cmpMode, Xor);
+    GenTree* secondIndir = impCreateCompareInd(gtClone(data)->AsLclVarCommon(), TYP_LONG, offset, value2, cmpMode, Xor);
 
     if ((firstIndir == nullptr) || (secondIndir == nullptr))
     {
@@ -435,14 +436,14 @@ GenTree* Compiler::impExpandHalfConstEqualsSWAR(
 //    A pointer to the newly created SWAR/SIMD node or nullptr if unrolling is not
 //    possible, not profitable or constant data contains non-ASCII char(s) in 'ignoreCase' mode
 //
-GenTree* Compiler::impExpandHalfConstEquals(GenTreeLclVar*   data,
-                                            GenTree*         lengthFld,
-                                            bool             checkForNull,
-                                            bool             startsWith,
-                                            WCHAR*           cnsData,
-                                            int              len,
-                                            int              dataOffset,
-                                            StringComparison cmpMode)
+GenTree* Compiler::impExpandHalfConstEquals(GenTreeLclVarCommon* data,
+                                            GenTree*             lengthFld,
+                                            bool                 checkForNull,
+                                            bool                 startsWith,
+                                            WCHAR*               cnsData,
+                                            int                  len,
+                                            int                  dataOffset,
+                                            StringComparison     cmpMode)
 {
     assert(len >= 0);
 
@@ -471,12 +472,12 @@ GenTree* Compiler::impExpandHalfConstEquals(GenTreeLclVar*   data,
         GenTree* indirCmp = nullptr;
         if (len < 8) // SWAR impl supports len == 8 but we'd better give it to SIMD
         {
-            indirCmp = impExpandHalfConstEqualsSWAR(gtClone(data)->AsLclVar(), cnsData, len, dataOffset, cmpMode);
+            indirCmp = impExpandHalfConstEqualsSWAR(gtClone(data)->AsLclVarCommon(), cnsData, len, dataOffset, cmpMode);
         }
 #if defined(FEATURE_HW_INTRINSICS)
         else if (IsBaselineSimdIsaSupported())
         {
-            indirCmp = impExpandHalfConstEqualsSIMD(gtClone(data)->AsLclVar(), cnsData, len, dataOffset, cmpMode);
+            indirCmp = impExpandHalfConstEqualsSIMD(gtClone(data)->AsLclVarCommon(), cnsData, len, dataOffset, cmpMode);
         }
 #endif
 
@@ -585,6 +586,13 @@ GenTree* Compiler::impStringEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO
     const bool isStatic  = methodFlags & CORINFO_FLG_STATIC;
     const int  argsCount = sig->numArgs + (isStatic ? 0 : 1);
 
+    // This optimization spawns several temps so make sure we have a room
+    if (lvaHaveManyLocals(0.75))
+    {
+        JITDUMP("impSpanEqualsOrStartsWith: Method has too many locals - bail out.\n")
+        return nullptr;
+    }
+
     StringComparison cmpMode = Ordinal;
     GenTree*         op1;
     GenTree*         op2;
@@ -724,6 +732,13 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO*
     const bool isStatic  = methodFlags & CORINFO_FLG_STATIC;
     const int  argsCount = sig->numArgs + (isStatic ? 0 : 1);
 
+    // This optimization spawns several temps so make sure we have a room
+    if (lvaHaveManyLocals(0.75))
+    {
+        JITDUMP("impSpanEqualsOrStartsWith: Method has too many locals - bail out.\n")
+        return nullptr;
+    }
+
     StringComparison cmpMode = Ordinal;
     GenTree*         op1;
     GenTree*         op2;
@@ -763,9 +778,8 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO*
     GenTreeStrCon* op1Str = impGetStrConFromSpan(op1);
     GenTreeStrCon* op2Str = impGetStrConFromSpan(op2);
 
-    if (!((op1Str != nullptr) ^ (op2Str != nullptr)))
+    if ((op1Str == nullptr) && (op2Str == nullptr))
     {
-        // either op1 or op2 has to be '(ReadOnlySpan)"cns"'
         return nullptr;
     }
 
@@ -806,34 +820,33 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO*
         JITDUMP("Trying to unroll MemoryExtensions.Equals|SequenceEqual|StartsWith(op1, \"%ws\")...\n", str)
     }
 
-    CORINFO_CLASS_HANDLE spanCls;
-    info.compCompHnd->getArgType(sig, sig->args, &spanCls);
-    CORINFO_FIELD_HANDLE pointerHnd   = info.compCompHnd->getFieldInClass(spanCls, 0);
-    CORINFO_FIELD_HANDLE lengthHnd    = info.compCompHnd->getFieldInClass(spanCls, 1);
-    const unsigned       lengthOffset = info.compCompHnd->getFieldOffset(lengthHnd);
-
-    // Create a placeholder for Span object - we're not going to Append it to statements
-    // in advance to avoid redundant spills in case if we fail to vectorize
-    unsigned spanObjRef          = lvaGrabTemp(true DEBUGARG("spanObj tmp"));
-    unsigned spanDataTmp         = lvaGrabTemp(true DEBUGARG("spanData tmp"));
-    lvaTable[spanObjRef].lvType  = TYP_BYREF;
-    lvaTable[spanDataTmp].lvType = TYP_BYREF;
-
-    GenTreeLclVar* spanObjRefLcl  = gtNewLclvNode(spanObjRef, TYP_BYREF);
-    GenTreeLclVar* spanDataTmpLcl = gtNewLclvNode(spanDataTmp, TYP_BYREF);
+    unsigned spanLclNum;
+    if (spanObj->OperIs(GT_LCL_VAR))
+    {
+        // Argument is already a local
+        spanLclNum = spanObj->AsLclVarCommon()->GetLclNum();
+    }
+    else
+    {
+        // Access a local that will be set if we successfully unroll it
+        spanLclNum = lvaGrabTemp(true DEBUGARG("spilling spanObj"));
+        CORINFO_CLASS_HANDLE spanCls;
+        info.compCompHnd->getArgType(sig, sig->args, &spanCls);
+        lvaSetStruct(spanLclNum, spanCls, false);
+    }
 
-    GenTreeFieldAddr* spanLengthAddr = gtNewFieldAddrNode(lengthHnd, gtClone(spanObjRefLcl), lengthOffset);
-    GenTree*          spanLength     = gtNewIndir(TYP_INT, spanLengthAddr);
-    GenTreeFieldAddr* spanDataAddr   = gtNewFieldAddrNode(pointerHnd, spanObjRefLcl, 0);
-    GenTree*          spanData       = gtNewIndir(TYP_BYREF, spanDataAddr);
+    GenTreeLclFld* spanReferenceFld = gtNewLclFldNode(spanLclNum, TYP_BYREF, OFFSETOF__CORINFO_Span__reference);
+    GenTreeLclFld* spanLengthFld    = gtNewLclFldNode(spanLclNum, TYP_INT, OFFSETOF__CORINFO_Span__length);
+    GenTree*       unrolled = impExpandHalfConstEquals(spanReferenceFld, spanLengthFld, false, startsWith, (WCHAR*)str,
+                                                 cnsLength, 0, cmpMode);
 
-    GenTree* unrolled =
-        impExpandHalfConstEquals(spanDataTmpLcl, spanLength, false, startsWith, (WCHAR*)str, cnsLength, 0, cmpMode);
     if (unrolled != nullptr)
     {
-        // We succeeded, fill the placeholders:
-        impAssignTempGen(spanObjRef, impGetStructAddr(spanObj, CHECK_SPILL_NONE, true), CHECK_SPILL_NONE);
-        impAssignTempGen(spanDataTmp, spanData, CHECK_SPILL_NONE);
+        if (!spanObj->OperIs(GT_LCL_VAR))
+        {
+            impAssignTempGen(spanLclNum, spanObj, CHECK_SPILL_NONE);
+        }
+
         if (unrolled->OperIs(GT_QMARK))
         {
             // QMARK can't be a root node, spill it to a temp