Correctly handle check for busy double reg
authorCarol Eidt <carol.eidt@microsoft.com>
Tue, 13 Feb 2018 22:35:34 +0000 (14:35 -0800)
committerCarol Eidt <carol.eidt@microsoft.com>
Fri, 16 Feb 2018 17:37:31 +0000 (09:37 -0800)
Fixes the second case in DevDev bug 543057

src/jit/lsra.cpp
tests/src/JIT/Regression/JitBlue/DevDiv_543057a/DevDiv_543057a.cs [new file with mode: 0644]
tests/src/JIT/Regression/JitBlue/DevDiv_543057a/DevDiv_543057a.csproj [new file with mode: 0644]

index 129b857..276f3a4 100644 (file)
@@ -3358,8 +3358,10 @@ bool LinearScan::isRefPositionActive(RefPosition* refPosition, LsraLocation refL
 //    False - otherwise
 //
 // Notes:
-//    This helper is designed to be used only from allocateBusyReg().
-//    The caller must have already checked for the case where 'refPosition' is a fixed ref.
+//    This helper is designed to be used only from allocateBusyReg(), where:
+//    - This register was *not* found when looking for a free register, and
+//    - The caller must have already checked for the case where 'refPosition' is a fixed ref
+//      (asserted at the beginning of this method).
 //
 bool LinearScan::isRegInUse(RegRecord* regRec, RefPosition* refPosition)
 {
@@ -3370,27 +3372,43 @@ bool LinearScan::isRegInUse(RegRecord* regRec, RefPosition* refPosition)
     {
         if (!assignedInterval->isActive)
         {
-            // This can only happen if we have a recentRefPosition active at this location that hasn't yet been freed
-            // (Or, in the case of ARM, the other half of a double is either active or has an active recentRefPosition).
+            // This can only happen if we have a recentRefPosition active at this location that hasn't yet been freed.
             CLANG_FORMAT_COMMENT_ANCHOR;
 
-#ifdef _TARGET_ARM_
-            if (refPosition->getInterval()->registerType == TYP_DOUBLE)
+            if (isRefPositionActive(assignedInterval->recentRefPosition, refPosition->nodeLocation))
             {
-                if (!isRefPositionActive(assignedInterval->recentRefPosition, refPosition->nodeLocation))
+                return true;
+            }
+            else
+            {
+#ifdef _TARGET_ARM_
+                // In the case of TYP_DOUBLE, we may have the case where 'assignedInterval' is inactive,
+                // but the other half register is active. If so, it must be have an active recentRefPosition,
+                // as above.
+                if (refPosition->getInterval()->registerType == TYP_DOUBLE)
                 {
                     RegRecord* otherHalfRegRec = findAnotherHalfRegRec(regRec);
-                    assert(otherHalfRegRec->assignedInterval->isActive ||
-                           isRefPositionActive(otherHalfRegRec->assignedInterval->recentRefPosition,
-                                               refPosition->nodeLocation));
+                    if (!otherHalfRegRec->assignedInterval->isActive)
+                    {
+                        if (isRefPositionActive(otherHalfRegRec->assignedInterval->recentRefPosition,
+                                                refPosition->nodeLocation))
+                        {
+                            return true;
+                        }
+                        else
+                        {
+                            assert(!"Unexpected inactive assigned interval in isRegInUse");
+                            return true;
+                        }
+                    }
                 }
-            }
-            else
+                else
 #endif
-            {
-                assert(isRefPositionActive(assignedInterval->recentRefPosition, refPosition->nodeLocation));
+                {
+                    assert(!"Unexpected inactive assigned interval in isRegInUse");
+                    return true;
+                }
             }
-            return true;
         }
         RefPosition* nextAssignedRef = assignedInterval->getNextRefPosition();
 
diff --git a/tests/src/JIT/Regression/JitBlue/DevDiv_543057a/DevDiv_543057a.cs b/tests/src/JIT/Regression/JitBlue/DevDiv_543057a/DevDiv_543057a.cs
new file mode 100644 (file)
index 0000000..cfa907a
--- /dev/null
@@ -0,0 +1,74 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//
+
+// The bug captured by this test was a case where:
+// - We have COMPlus_JitStressRegs=3, so we're limiting the available registers.
+// - We have a DIV with two double operands that are casts from int lclVars, and it is passed to a call.
+// - We have 4 float lclVars in registers:
+//   - One is active in a caller-save register (that will be x in our case)
+//   - One is active in a callee-save register (y)
+//   - One is inactive in a caller-save register (z)
+//   - One is inactive in a callee-save register (w)
+// - When we allocate the def (target) register for the second cast, we spill the first one.
+// - When we try to reload it, we were incorrectly returning false from 'isRegInUse()'
+//   for the inactive interval in the second half.
+
+using System;
+using System.Runtime.CompilerServices;
+
+public class DevDiv_543057
+
+{
+    public const int Pass = 100;
+    public const int Fail = -1;
+
+    [MethodImplAttribute(MethodImplOptions.NoInlining)]
+    public static float GetFloat(int i)
+    {
+        return (float)i;
+    }
+    [MethodImplAttribute(MethodImplOptions.NoInlining)]
+    public static int test(int i, int j)
+    {
+        // x and y will be preferenced to callee-save
+        //   they need to have enough references to qualify for callee-save
+        // z and w will not
+        // x and z will be inactive at the call to Math.Ceiling
+        // y and w will be active
+        float x = GetFloat(1);
+        float y = GetFloat(2);
+        float z = GetFloat(3);
+        // We want w in f1. At this point z is likely to be in f0.
+        // So define 'result' first.
+        float result = x - y;
+        float w = x + y + z;
+        if (i != j)
+        {
+            // Here all our floats are in registers. z is going to be redefined, so it is inactive,
+            // and w is not used except in the else clause so it is also inactive.
+            z = (float) Math.Ceiling(((double)i) / ((double)j));
+            // Now we use x and y so that they are live across the call to Math.Ceiling.
+            result = z + y + w;
+        }
+        else
+        {
+            // Here we need to use all of our float arguments so that they are all live.
+            y = x * y * z * w;
+            x = y * 2;
+            // Now x and y are going to be live across this call, to encourage them to get a callee-save reg.
+            Console.WriteLine("FAIL");
+            // And use x a couple more times.
+            x *= y;
+            result += x + y;
+        }
+        Console.WriteLine("Result: " + result);
+        return Pass;
+    }
+    public static int Main()
+    {
+        return test(5, 6);
+    }
+}
+
diff --git a/tests/src/JIT/Regression/JitBlue/DevDiv_543057a/DevDiv_543057a.csproj b/tests/src/JIT/Regression/JitBlue/DevDiv_543057a/DevDiv_543057a.csproj
new file mode 100644 (file)
index 0000000..6d58ab0
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+  </PropertyGroup>
+  <!-- Default configurations to help VS understand the configurations -->
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+  </PropertyGroup>
+  <ItemGroup>
+    <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+      <Visible>False</Visible>
+    </CodeAnalysisDependentAssemblyPaths>
+  </ItemGroup>
+  <PropertyGroup>
+    <DebugType></DebugType>
+    <Optimize>True</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="$(MSBuildProjectName).cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+  <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' ">
+  </PropertyGroup> 
+</Project>