Fix "cns".StartsWith bug (#72558)
authorEgor Bogatov <egorbo@gmail.com>
Thu, 21 Jul 2022 09:30:21 +0000 (11:30 +0200)
committerGitHub <noreply@github.com>
Thu, 21 Jul 2022 09:30:21 +0000 (11:30 +0200)
src/coreclr/jit/importer_vectorization.cpp
src/tests/JIT/Regression/JitBlue/Runtime_72550/Runtime_72550.cs [new file with mode: 0644]
src/tests/JIT/Regression/JitBlue/Runtime_72550/Runtime_72550.csproj [new file with mode: 0644]

index a3f2142..f394859 100644 (file)
@@ -653,15 +653,20 @@ GenTree* Compiler::impStringEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO
 
     GenTree*       varStr;
     GenTreeStrCon* cnsStr;
-    if (op1->OperIs(GT_CNS_STR))
+    if (op2->OperIs(GT_CNS_STR))
     {
-        cnsStr = op1->AsStrCon();
-        varStr = op2;
+        cnsStr = op2->AsStrCon();
+        varStr = op1;
     }
     else
     {
-        cnsStr = op2->AsStrCon();
-        varStr = op1;
+        if (startsWith)
+        {
+            // StartsWith is not commutative
+            return nullptr;
+        }
+        cnsStr = op1->AsStrCon();
+        varStr = op2;
     }
 
     bool needsNullcheck = true;
@@ -804,15 +809,20 @@ GenTree* Compiler::impSpanEqualsOrStartsWith(bool startsWith, CORINFO_SIG_INFO*
 
     GenTree*       spanObj;
     GenTreeStrCon* cnsStr;
-    if (op1Str != nullptr)
+    if (op2Str != nullptr)
     {
-        cnsStr  = op1Str;
-        spanObj = op2;
+        cnsStr  = op2Str;
+        spanObj = op1;
     }
     else
     {
-        cnsStr  = op2Str;
-        spanObj = op1;
+        if (startsWith)
+        {
+            // StartsWith is not commutative
+            return nullptr;
+        }
+        cnsStr  = op1Str;
+        spanObj = op2;
     }
 
     int      cnsLength = -1;
diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_72550/Runtime_72550.cs b/src/tests/JIT/Regression/JitBlue/Runtime_72550/Runtime_72550.cs
new file mode 100644 (file)
index 0000000..42b934d
--- /dev/null
@@ -0,0 +1,270 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.CompilerServices;
+
+public static class Runtime_72550
+{
+    private static int retCode = 100;
+
+    public static int Main()
+    {
+        IsTrue(Test1.StartsWith1(""));
+        IsTrue(Test1.StartsWith2(""));
+        IsTrue(Test1.StartsWith3(""));
+        IsTrue(Test1.StartsWith4(""));
+        IsTrue(Test1.StartsWith5(""));
+
+        IsTrue(!Test1.StartsWith1("swar"));
+        IsTrue(!Test1.StartsWith2("swar"));
+        IsTrue(!Test1.StartsWith3("swar"));
+        IsTrue(!Test1.StartsWith4("swar"));
+        IsTrue(!Test1.StartsWith5("swar"));
+
+        IsTrue(!Test1.StartsWith1("simd"));
+        IsTrue(!Test1.StartsWith2("simd"));
+        IsTrue(!Test1.StartsWith3("simd"));
+        IsTrue(!Test1.StartsWith4("simd"));
+        IsTrue(!Test1.StartsWith5("simd"));
+
+        IsTrue(!Test1.StartsWith1("swar\0"));
+        IsTrue(!Test1.StartsWith2("swar\0"));
+        IsTrue(!Test1.StartsWith3("swar\0"));
+        IsTrue(!Test1.StartsWith4("swar\0"));
+        IsTrue(!Test1.StartsWith5("swar\0"));
+
+        IsTrue(!Test1.StartsWith1(" swar"));
+        IsTrue(!Test1.StartsWith2(" swar"));
+        IsTrue(!Test1.StartsWith3(" swar"));
+        IsTrue(!Test1.StartsWith4(" swar"));
+        IsTrue(!Test1.StartsWith5(" swar"));
+
+        IsTrue(!Test1.StartsWith1("swarswar"));
+        IsTrue(!Test1.StartsWith2("swarswar"));
+        IsTrue(!Test1.StartsWith3("swarswar"));
+        IsTrue(!Test1.StartsWith4("swarswar"));
+        IsTrue(!Test1.StartsWith5("swarswar"));
+
+        IsTrue(!Test1.StartsWith1("simd"));
+        IsTrue(!Test1.StartsWith2("simd"));
+        IsTrue(!Test1.StartsWith3("simd"));
+        IsTrue(!Test1.StartsWith4("simd"));
+        IsTrue(!Test1.StartsWith5("simd"));
+
+        IsTrue(!Test1.StartsWith1("simx"));
+        IsTrue(!Test1.StartsWith2("simx"));
+        IsTrue(!Test1.StartsWith3("simx"));
+        IsTrue(!Test1.StartsWith4("simx"));
+        IsTrue(!Test1.StartsWith5("simx"));
+
+        IsTrue(!Test1.StartsWith1("simdsimdsimd"));
+        IsTrue(!Test1.StartsWith2("simdsimdsimd"));
+        IsTrue(!Test1.StartsWith3("simdsimdsimd"));
+        IsTrue(!Test1.StartsWith4("simdsimdsimd"));
+        IsTrue(!Test1.StartsWith5("simdsimdsimd"));
+
+        IsTrue(!Test1.StartsWith1("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test1.StartsWith2("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test1.StartsWith3("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test1.StartsWith4("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test1.StartsWith5("simdsimdsimdsimdsimdsimd"));
+
+
+        IsTrue(Test2.StartsWith1(""));
+        IsTrue(Test2.StartsWith2(""));
+        IsTrue(Test2.StartsWith3(""));
+        IsTrue(Test2.StartsWith4(""));
+        IsTrue(Test2.StartsWith5(""));
+
+        IsTrue(Test2.StartsWith1("swar"));
+        IsTrue(Test2.StartsWith2("swar"));
+        IsTrue(Test2.StartsWith3("swar"));
+        IsTrue(Test2.StartsWith4("swar"));
+        IsTrue(Test2.StartsWith5("swar"));
+
+        IsTrue(Test2.StartsWith1("swa"));
+        IsTrue(Test2.StartsWith2("swa"));
+        IsTrue(Test2.StartsWith3("swa"));
+        IsTrue(Test2.StartsWith4("swa"));
+        IsTrue(Test2.StartsWith5("swa"));
+
+        IsTrue(Test2.StartsWith1("s"));
+        IsTrue(Test2.StartsWith2("s"));
+        IsTrue(Test2.StartsWith3("s"));
+        IsTrue(Test2.StartsWith4("s"));
+        IsTrue(Test2.StartsWith5("s"));
+
+        IsTrue(!Test2.StartsWith1("s\0"));
+        IsTrue(!Test2.StartsWith2("s\0"));
+        IsTrue(!Test2.StartsWith3("s\0"));
+        IsTrue(!Test2.StartsWith4("s\0"));
+        IsTrue(!Test2.StartsWith5("s\0"));
+
+        IsTrue(!Test2.StartsWith1("simd"));
+        IsTrue(!Test2.StartsWith2("simd"));
+        IsTrue(!Test2.StartsWith3("simd"));
+        IsTrue(!Test2.StartsWith4("simd"));
+        IsTrue(!Test2.StartsWith5("simd"));
+
+        IsTrue(!Test2.StartsWith1("swar\0"));
+        IsTrue(!Test2.StartsWith2("swar\0"));
+        IsTrue(!Test2.StartsWith3("swar\0"));
+        IsTrue(!Test2.StartsWith4("swar\0"));
+        IsTrue(!Test2.StartsWith5("swar\0"));
+
+        IsTrue(!Test2.StartsWith1(" swar"));
+        IsTrue(!Test2.StartsWith2(" swar"));
+        IsTrue(!Test2.StartsWith3(" swar"));
+        IsTrue(!Test2.StartsWith4(" swar"));
+        IsTrue(!Test2.StartsWith5(" swar"));
+
+        IsTrue(!Test2.StartsWith1("swarswar"));
+        IsTrue(!Test2.StartsWith2("swarswar"));
+        IsTrue(!Test2.StartsWith3("swarswar"));
+        IsTrue(!Test2.StartsWith4("swarswar"));
+        IsTrue(!Test2.StartsWith5("swarswar"));
+
+        IsTrue(!Test2.StartsWith1("simd"));
+        IsTrue(!Test2.StartsWith2("simd"));
+        IsTrue(!Test2.StartsWith3("simd"));
+        IsTrue(!Test2.StartsWith4("simd"));
+        IsTrue(!Test2.StartsWith5("simd"));
+
+        IsTrue(!Test2.StartsWith1("simx"));
+        IsTrue(!Test2.StartsWith2("simx"));
+        IsTrue(!Test2.StartsWith3("simx"));
+        IsTrue(!Test2.StartsWith4("simx"));
+        IsTrue(!Test2.StartsWith5("simx"));
+
+        IsTrue(!Test2.StartsWith1("simdsimdsimd"));
+        IsTrue(!Test2.StartsWith2("simdsimdsimd"));
+        IsTrue(!Test2.StartsWith3("simdsimdsimd"));
+        IsTrue(!Test2.StartsWith4("simdsimdsimd"));
+        IsTrue(!Test2.StartsWith5("simdsimdsimd"));
+
+        IsTrue(!Test2.StartsWith1("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test2.StartsWith2("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test2.StartsWith3("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test2.StartsWith4("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test2.StartsWith5("simdsimdsimdsimdsimdsimd"));
+
+
+        IsTrue(Test3.StartsWith1(""));
+        IsTrue(Test3.StartsWith2(""));
+        IsTrue(Test3.StartsWith3(""));
+        IsTrue(Test3.StartsWith4(""));
+        IsTrue(Test3.StartsWith5(""));
+
+        IsTrue(!Test3.StartsWith1("swar"));
+        IsTrue(!Test3.StartsWith2("swar"));
+        IsTrue(!Test3.StartsWith3("swar"));
+        IsTrue(!Test3.StartsWith4("swar"));
+        IsTrue(!Test3.StartsWith5("swar"));
+
+        IsTrue(Test3.StartsWith1("simd"));
+        IsTrue(Test3.StartsWith2("simd"));
+        IsTrue(Test3.StartsWith3("simd"));
+        IsTrue(Test3.StartsWith4("simd"));
+        IsTrue(Test3.StartsWith5("simd"));
+        
+        IsTrue(!Test3.StartsWith1("swar\0"));
+        IsTrue(!Test3.StartsWith2("swar\0"));
+        IsTrue(!Test3.StartsWith3("swar\0"));
+        IsTrue(!Test3.StartsWith4("swar\0"));
+        IsTrue(!Test3.StartsWith5("swar\0"));
+
+        IsTrue(!Test3.StartsWith1(" swar"));
+        IsTrue(!Test3.StartsWith2(" swar"));
+        IsTrue(!Test3.StartsWith3(" swar"));
+        IsTrue(!Test3.StartsWith4(" swar"));
+        IsTrue(!Test3.StartsWith5(" swar"));
+
+        IsTrue(!Test3.StartsWith1("swarswar"));
+        IsTrue(!Test3.StartsWith2("swarswar"));
+        IsTrue(!Test3.StartsWith3("swarswar"));
+        IsTrue(!Test3.StartsWith4("swarswar"));
+        IsTrue(!Test3.StartsWith5("swarswar"));
+
+        IsTrue(Test3.StartsWith1("simd"));
+        IsTrue(Test3.StartsWith2("simd"));
+        IsTrue(Test3.StartsWith3("simd"));
+        IsTrue(Test3.StartsWith4("simd"));
+        IsTrue(Test3.StartsWith5("simd"));
+
+        IsTrue(!Test3.StartsWith1("simx"));
+        IsTrue(!Test3.StartsWith2("simx"));
+        IsTrue(!Test3.StartsWith3("simx"));
+        IsTrue(!Test3.StartsWith4("simx"));
+        IsTrue(!Test3.StartsWith5("simx"));
+
+        IsTrue(Test3.StartsWith1("simdsimdsimd"));
+        IsTrue(Test3.StartsWith2("simdsimdsimd"));
+        IsTrue(Test3.StartsWith3("simdsimdsimd"));
+        IsTrue(Test3.StartsWith4("simdsimdsimd"));
+        IsTrue(Test3.StartsWith5("simdsimdsimd"));
+
+        IsTrue(!Test3.StartsWith1("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test3.StartsWith2("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test3.StartsWith3("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test3.StartsWith4("simdsimdsimdsimdsimdsimd"));
+        IsTrue(!Test3.StartsWith5("simdsimdsimdsimdsimdsimd"));
+
+        return retCode;
+    }
+
+    [MethodImpl(MethodImplOptions.NoInlining)]
+    public static void IsTrue(bool condition, [CallerLineNumber] int line = 0)
+    {
+        if (!condition)
+        {
+            Console.WriteLine($"Expected: true, line: {line}");
+            retCode++;
+        }
+    }
+
+    class Test1
+    {
+        private const string Str = "";
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith1(string s) => Str.StartsWith(s, StringComparison.OrdinalIgnoreCase);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith2(string s) => Str.StartsWith(s, StringComparison.Ordinal);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith3(string s) => Str.AsSpan().StartsWith(s);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith4(string s) => Str.AsSpan().StartsWith(s, StringComparison.Ordinal);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith5(string s) => Str.AsSpan().StartsWith(s, StringComparison.OrdinalIgnoreCase);
+    }
+
+    class Test2
+    {
+        private const string Str = "swar";
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith1(string s) => Str.StartsWith(s, StringComparison.OrdinalIgnoreCase);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith2(string s) => Str.StartsWith(s, StringComparison.Ordinal);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith3(string s) => Str.AsSpan().StartsWith(s);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith4(string s) => Str.AsSpan().StartsWith(s, StringComparison.Ordinal);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith5(string s) => Str.AsSpan().StartsWith(s, StringComparison.OrdinalIgnoreCase);
+    }
+
+    class Test3
+    {
+        private const string Str = "simdsimdsimd";
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith1(string s) => Str.StartsWith(s, StringComparison.OrdinalIgnoreCase);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith2(string s) => Str.StartsWith(s, StringComparison.Ordinal);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith3(string s) => Str.AsSpan().StartsWith(s);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith4(string s) => Str.AsSpan().StartsWith(s, StringComparison.Ordinal);
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static bool StartsWith5(string s) => Str.AsSpan().StartsWith(s, StringComparison.OrdinalIgnoreCase);
+    }
+}
diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_72550/Runtime_72550.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_72550/Runtime_72550.csproj
new file mode 100644 (file)
index 0000000..f492aea
--- /dev/null
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+</Project>
\ No newline at end of file