Keep OBJ on mismatched struct assignment
authorCarol Eidt <carol.eidt@microsoft.com>
Thu, 4 Jan 2018 01:45:07 +0000 (17:45 -0800)
committerCarol Eidt <carol.eidt@microsoft.com>
Thu, 4 Jan 2018 01:45:07 +0000 (17:45 -0800)
In a mismatched struct assignment, e.g. using Unsafe.As, we need to retain the OBJ(ADDR(lcl)) on the rhs of the assignment.

Fix #15237

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

index 5e785f6..f85bafb 100644 (file)
@@ -10208,7 +10208,7 @@ GenTree* Compiler::fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigne
         if (lclNode != nullptr)
         {
             LclVarDsc* varDsc = &(lvaTable[lclNode->gtLclNum]);
-            if (varTypeIsStruct(varDsc) && (varDsc->lvExactSize == blockWidth))
+            if (varTypeIsStruct(varDsc) && (varDsc->lvType == asgType))
             {
 #ifndef LEGACY_BACKEND
                 effectiveVal     = lclNode;
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_15237/GitHub_15237.cs b/tests/src/JIT/Regression/JitBlue/GitHub_15237/GitHub_15237.cs
new file mode 100644 (file)
index 0000000..2e1b7a0
--- /dev/null
@@ -0,0 +1,148 @@
+using System;
+using System.Globalization;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+namespace UnsafeTesting
+{
+    public class Program
+    {
+        static int Main(string[] args)
+        {
+            float UnsafeAs = LengthSquaredUnsafeAs();
+            Console.WriteLine($"Unsafe.As           : {UnsafeAs}");
+            float UnsafeRead = LengthSquaredUnsafeRead();
+            Console.WriteLine($"Unsafe.Read         : {UnsafeRead}");
+            float UnsafeReadUnaligned = LengthSquaredUnsafeReadUnaligned();
+            Console.WriteLine($"Unsafe.ReadUnaligned: {UnsafeReadUnaligned}");
+            float NoVectors = LengthSquaredUnsafeReadUnaligned();
+            Console.WriteLine($"No Vectors          : {NoVectors}");
+            float ManualVectors = LengthSquaredUnsafeReadUnaligned();
+            Console.WriteLine($"Manual Vectors      : {ManualVectors}");
+            if ((Math.Abs(UnsafeAs - ManualVectors) > Single.Epsilon) ||
+                (Math.Abs(UnsafeRead - ManualVectors) > Single.Epsilon) ||
+                (Math.Abs(UnsafeReadUnaligned - ManualVectors) > Single.Epsilon) ||
+                (Math.Abs(NoVectors - ManualVectors) > Single.Epsilon))
+            {
+                Console.WriteLine("FAIL");
+                return -1;
+            }
+            else
+            {
+                Console.WriteLine("PASS");
+                return 100;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static float LengthSquaredUnsafeAs()
+        {
+            QuaternionStruct start = new QuaternionStruct(8.5f, 9.4f, 1.2f, 1f);
+
+            return start.LengthSquaredUnsafeAs();
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static float LengthSquaredUnsafeRead()
+        {
+            QuaternionStruct start = new QuaternionStruct(8.5f, 9.4f, 1.2f, 1f);
+
+            return start.LengthSquaredUnsafeRead();
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static float LengthSquaredUnsafeReadUnaligned()
+        {
+            QuaternionStruct start = new QuaternionStruct(8.5f, 9.4f, 1.2f, 1f);
+
+            return start.LengthSquaredUnsafeReadUnaligned();
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static float LengthSquaredNoVectors()
+        {
+            QuaternionStruct start = new QuaternionStruct(8.5f, 9.4f, 1.2f, 1f);
+
+            return start.LengthSquaredNoVectors();
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        public static float LengthSquaredManualVectors()
+        {
+            QuaternionStruct start = new QuaternionStruct(8.5f, 9.4f, 1.2f, 1f);
+
+            return start.LengthSquaredManualVectors();
+        }
+    }
+
+    public struct QuaternionStruct : IEquatable<QuaternionStruct>
+    {
+        public float X;
+        public float Y;
+        public float Z;
+        public float W;
+
+        public QuaternionStruct(float x, float y, float z, float w)
+        {
+            this.X = x;
+            this.Y = y;
+            this.Z = z;
+            this.W = w;
+        }
+
+        public float LengthSquaredManualVectors() => ToVector4(this).LengthSquared();
+
+        public float LengthSquaredUnsafeAs()
+        {
+            Vector4 q = Unsafe.As<QuaternionStruct, Vector4>(ref this);
+
+            return q.LengthSquared();
+        }
+
+        public unsafe float LengthSquaredUnsafeRead()
+        {
+            fixed (QuaternionStruct* p = &this)
+            {
+                Vector4 q = Unsafe.Read<Vector4>(p);
+
+                return q.LengthSquared();
+            }
+        }
+
+        public float LengthSquaredUnsafeReadUnaligned()
+        {
+            Vector4 q = Unsafe.ReadUnaligned<Vector4>(ref Unsafe.As<QuaternionStruct, byte>(ref this));
+
+            return q.LengthSquared();
+        }
+
+        public float LengthSquaredNoVectors()
+        {
+            return X * X + Y * Y + Z * Z + W * W;
+        }
+
+        public bool Equals(QuaternionStruct other)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override string ToString()
+        {
+            CultureInfo ci = CultureInfo.CurrentCulture;
+
+            return $"{{X:{X.ToString(ci)} Y:{Y.ToString(ci)} Z:{Z.ToString(ci)} W:{W.ToString(ci)}}}";
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static QuaternionStruct FromVector4(Vector4 vector)
+        {
+            return new QuaternionStruct(vector.X, vector.Y, vector.Z, vector.W);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static Vector4 ToVector4(QuaternionStruct quaternionStruct)
+        {
+            return new Vector4(quaternionStruct.X, quaternionStruct.Y, quaternionStruct.Z, quaternionStruct.W);
+        }
+    }
+}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_15237/GitHub_15237.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_15237/GitHub_15237.csproj
new file mode 100644 (file)
index 0000000..1b7f8a7
--- /dev/null
@@ -0,0 +1,41 @@
+<?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>{76E69AA0-8C5A-4F76-8561-B8089FFA8D79}</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>
+    <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+  </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>
+  <PropertyGroup>
+    <ProjectAssetsFile>$(JitPackagesConfigFileDirectory)benchmark\obj\project.assets.json</ProjectAssetsFile>
+  </PropertyGroup>
+</Project>