summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jit/lowerxarch.cpp120
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.cs52
-rw-r--r--tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.csproj36
3 files changed, 182 insertions, 26 deletions
diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp
index cee84033cc..7eb7edf899 100644
--- a/src/jit/lowerxarch.cpp
+++ b/src/jit/lowerxarch.cpp
@@ -2457,6 +2457,8 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, Ge
{
case HW_Category_SimpleSIMD:
{
+ // These intrinsics only expect 16 or 32-byte nodes for containment
+ assert((genTypeSize(node->TypeGet()) == 16) || (genTypeSize(node->TypeGet()) == 32));
assert(supportsSIMDScalarLoads == false);
supportsAlignedSIMDLoads =
@@ -2502,6 +2504,8 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, Ge
case NI_AVX2_ShuffleHigh:
case NI_AVX2_ShuffleLow:
{
+ // These intrinsics only expect 16 or 32-byte nodes for containment
+ assert((genTypeSize(node->TypeGet()) == 16) || (genTypeSize(node->TypeGet()) == 32));
assert(supportsSIMDScalarLoads == false);
supportsAlignedSIMDLoads = !comp->canUseVexEncoding();
@@ -2511,15 +2515,30 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, Ge
break;
}
+ case NI_SSE2_Insert:
case NI_SSE41_Insert:
case NI_SSE41_X64_Insert:
{
if (containingNode->gtSIMDBaseType == TYP_FLOAT)
{
+ assert(containingIntrinsicId == NI_SSE41_Insert);
+ assert(genTypeSize(node->TypeGet()) == 16);
+
+ // Sse41.Insert(V128<float>, V128<float>, byte) is a bit special
+ // in that it has different behavior depending on whether the
+ // second operand is coming from a register or memory. When coming
+ // from a register, all 4 elements of the vector can be used and it
+ // is effectively a regular `SimpleSIMD` operation; but when loading
+ // from memory, it only works with the lowest element and is effectively
+ // a `SIMDScalar`.
+
+ assert(supportsAlignedSIMDLoads == false);
+ assert(supportsUnalignedSIMDLoads == false);
+ assert(supportsGeneralLoads == false);
assert(supportsSIMDScalarLoads == false);
GenTree* op1 = containingNode->gtGetOp1();
- GenTree* op2 = containingNode->gtGetOp2();
+ GenTree* op2 = nullptr;
GenTree* op3 = nullptr;
assert(op1->OperIsList());
@@ -2548,41 +2567,36 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, Ge
ssize_t ival = op3->AsIntCon()->IconValue();
assert((ival >= 0) && (ival <= 255));
- if (ival <= 0x3F)
- {
- supportsAlignedSIMDLoads = !comp->canUseVexEncoding();
- supportsUnalignedSIMDLoads = !supportsAlignedSIMDLoads;
- supportsGeneralLoads = supportsUnalignedSIMDLoads;
-
- break;
- }
+ supportsSIMDScalarLoads = (ival <= 0x3F);
+ supportsGeneralLoads = supportsSIMDScalarLoads;
}
-
- assert(supportsAlignedSIMDLoads == false);
- assert(supportsUnalignedSIMDLoads == false);
- assert(supportsGeneralLoads == false);
+ break;
}
- else
- {
- assert(supportsAlignedSIMDLoads == false);
- assert(supportsUnalignedSIMDLoads == false);
- supportsSIMDScalarLoads = true;
- supportsGeneralLoads = supportsSIMDScalarLoads;
- }
+ // We should only get here for integral nodes.
+ assert(varTypeIsIntegral(node->TypeGet()));
+
+ assert(supportsAlignedSIMDLoads == false);
+ assert(supportsUnalignedSIMDLoads == false);
+ assert(supportsSIMDScalarLoads == false);
+ const unsigned expectedSize = genTypeSize(containingNode->gtSIMDBaseType);
+ const unsigned operandSize = genTypeSize(node->TypeGet());
+
+ supportsGeneralLoads = (operandSize >= expectedSize);
break;
}
- case NI_SSE2_Insert:
case NI_AVX_CompareScalar:
{
+ // These intrinsics only expect 16 or 32-byte nodes for containment
+ assert((genTypeSize(node->TypeGet()) == 16) || (genTypeSize(node->TypeGet()) == 32));
+
assert(supportsAlignedSIMDLoads == false);
assert(supportsUnalignedSIMDLoads == false);
supportsSIMDScalarLoads = true;
supportsGeneralLoads = supportsSIMDScalarLoads;
-
break;
}
@@ -2603,19 +2617,73 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* containingNode, Ge
assert(supportsAlignedSIMDLoads == false);
assert(supportsUnalignedSIMDLoads == false);
- supportsSIMDScalarLoads = true;
- supportsGeneralLoads = supportsSIMDScalarLoads;
+ switch (containingIntrinsicId)
+ {
+ case NI_Base_Vector128_CreateScalarUnsafe:
+ case NI_Base_Vector256_CreateScalarUnsafe:
+ {
+ assert(supportsSIMDScalarLoads == false);
+
+ const unsigned expectedSize = genTypeSize(genActualType(containingNode->gtSIMDBaseType));
+ const unsigned operandSize = genTypeSize(node->TypeGet());
+
+ supportsGeneralLoads = (operandSize == expectedSize);
+ break;
+ }
+
+ case NI_SSE_ConvertScalarToVector128Single:
+ case NI_SSE2_ConvertScalarToVector128Double:
+ case NI_SSE2_ConvertScalarToVector128Int32:
+ case NI_SSE2_ConvertScalarToVector128UInt32:
+ case NI_SSE_X64_ConvertScalarToVector128Single:
+ case NI_SSE2_X64_ConvertScalarToVector128Double:
+ case NI_SSE2_X64_ConvertScalarToVector128Int64:
+ case NI_SSE2_X64_ConvertScalarToVector128UInt64:
+ {
+ if (!varTypeIsIntegral(node->TypeGet()))
+ {
+ // The floating-point overload doesn't require any special semantics
+ assert(containingIntrinsicId == NI_SSE2_ConvertScalarToVector128Double);
+ supportsSIMDScalarLoads = true;
+ supportsGeneralLoads = supportsSIMDScalarLoads;
+ break;
+ }
+
+ assert(supportsSIMDScalarLoads == false);
+
+ const unsigned expectedSize = genTypeSize(genActualType(containingNode->gtSIMDBaseType));
+ const unsigned operandSize = genTypeSize(node->TypeGet());
+ supportsGeneralLoads = (operandSize == expectedSize);
+ break;
+ }
+
+ default:
+ {
+ // These intrinsics only expect 16 or 32-byte nodes for containment
+ assert((genTypeSize(node->TypeGet()) == 16) || (genTypeSize(node->TypeGet()) == 32));
+
+ supportsSIMDScalarLoads = true;
+ supportsGeneralLoads = supportsSIMDScalarLoads;
+ break;
+ }
+ }
break;
}
case HW_Category_Scalar:
{
+ // We should only get here for integral nodes.
+ assert(varTypeIsIntegral(node->TypeGet()));
+
assert(supportsAlignedSIMDLoads == false);
- assert(supportsSIMDScalarLoads == false);
assert(supportsUnalignedSIMDLoads == false);
+ assert(supportsSIMDScalarLoads == false);
+
+ const unsigned expectedSize = genTypeSize(containingNode->TypeGet());
+ const unsigned operandSize = genTypeSize(node->TypeGet());
- supportsGeneralLoads = true;
+ supportsGeneralLoads = (operandSize >= expectedSize);
break;
}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.cs b/tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.cs
new file mode 100644
index 0000000000..405d6ff5f7
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.cs
@@ -0,0 +1,52 @@
+// 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.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace GitHub_21625
+{
+ public class test
+ {
+ public static Vector128<ushort> CreateScalar(ushort value)
+ {
+ if (Sse2.IsSupported)
+ {
+ return Sse2.ConvertScalarToVector128UInt32(value).AsUInt16();
+ }
+
+ return SoftwareFallback(value);
+
+ Vector128<ushort> SoftwareFallback(ushort x)
+ {
+ var result = Vector128<ushort>.Zero;
+ Unsafe.WriteUnaligned(ref Unsafe.As<Vector128<ushort>, byte>(ref result), value);
+ return result;
+ }
+ }
+
+ static int Main()
+ {
+ ushort value = TestLibrary.Generator.GetUInt16();
+ Vector128<ushort> result = CreateScalar(value);
+
+ if (result.GetElement(0) != value)
+ {
+ return 0;
+ }
+
+ for (int i = 1; i < Vector128<ushort>.Count; i++)
+ {
+ if (result.GetElement(i) != 0)
+ {
+ return 0;
+ }
+ }
+
+ return 100;
+ }
+ }
+}
diff --git a/tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.csproj b/tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.csproj
new file mode 100644
index 0000000000..17a6d4efe0
--- /dev/null
+++ b/tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.csproj
@@ -0,0 +1,36 @@
+<?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>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{2649FAFE-07BF-4F93-8120-BA9A69285ABB}</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>
+ <PropertyGroup>
+ <DebugType>None</DebugType>
+ <Optimize>True</Optimize>
+ </PropertyGroup>
+ <ItemGroup>
+ <CodeAnalysisDependentAssemblyPaths Condition=" '$(VS100COMNTOOLS)' != '' " Include="$(VS100COMNTOOLS)..\IDE\PrivateAssemblies">
+ <Visible>False</Visible>
+ </CodeAnalysisDependentAssemblyPaths>
+ </ItemGroup>
+ <ItemGroup>
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="$(MSBuildProjectName).cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="$(SourceDir)Common/CoreCLRTestLibrary/CoreCLRTestLibrary.csproj" />
+ </ItemGroup>
+ <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+ <PropertyGroup Condition=" '$(MsBuildProjectDirOverride)' != '' "></PropertyGroup>
+</Project>