From ccaf35e1e0dc6bbfb86e4fe6b8c24fe8fe346e55 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 7 Jan 2019 16:57:34 -0800 Subject: [PATCH] Fixing ContainCheckHWIntrinsic to ensure that scalar integer operands are the appropriate size (#21641) * Fixing ContainCheckHWIntrinsic to ensure that scalar integer operands are the appropriate size * Adding a regression test for issue 21625 * Fixing IsContainableHWIntrinsicOp to use the containing node type (rather than the simd base type) for Scalar intrinsics * Fixing the containment check for `Sse41.Insert(V128, V128, byte)` * Cleaning up the isContainableHWIntrinsicOp logic in lowerxarch.cpp * Restrict containment to genActualType(baseType) * Formatting * Removing some comments and simplifying the supportsContainment checks for various HWIntrinsics that take a scalar operand * Applying formatting patch --- src/jit/lowerxarch.cpp | 120 ++++++++++++++++----- .../JitBlue/GitHub_21625/GitHub_21625.cs | 52 +++++++++ .../JitBlue/GitHub_21625/GitHub_21625.csproj | 36 +++++++ 3 files changed, 182 insertions(+), 26 deletions(-) create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.cs create mode 100644 tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.csproj diff --git a/src/jit/lowerxarch.cpp b/src/jit/lowerxarch.cpp index cee8403..7eb7edf 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, V128, 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 0000000..405d6ff --- /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 CreateScalar(ushort value) + { + if (Sse2.IsSupported) + { + return Sse2.ConvertScalarToVector128UInt32(value).AsUInt16(); + } + + return SoftwareFallback(value); + + Vector128 SoftwareFallback(ushort x) + { + var result = Vector128.Zero; + Unsafe.WriteUnaligned(ref Unsafe.As, byte>(ref result), value); + return result; + } + } + + static int Main() + { + ushort value = TestLibrary.Generator.GetUInt16(); + Vector128 result = CreateScalar(value); + + if (result.GetElement(0) != value) + { + return 0; + } + + for (int i = 1; i < Vector128.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 0000000..17a6d4e --- /dev/null +++ b/tests/src/JIT/Regression/JitBlue/GitHub_21625/GitHub_21625.csproj @@ -0,0 +1,36 @@ + + + + + Debug + AnyCPU + 2.0 + {2649FAFE-07BF-4F93-8120-BA9A69285ABB} + Exe + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + ..\..\ + + + + + + None + True + + + + False + + + + + + + + + + + + + + -- 2.7.4