Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/coreclr/jit/hwintrinsiccodegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2762,7 +2762,7 @@ void CodeGen::genX86BaseIntrinsic(GenTreeHWIntrinsic* node, insOpts instOptions)
GenTree* op1 = node->Op(1);
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType, m_compiler);

if (!varTypeIsSIMD(op1->TypeGet()))
if (node->OperIsMemoryLoad())
{
// Until we improve the handling of addressing modes in the emitter, we'll create a
// temporary GT_IND to generate code with.
Expand Down Expand Up @@ -2965,7 +2965,7 @@ void CodeGen::genAvxFamilyIntrinsic(GenTreeHWIntrinsic* node, insOpts instOption
{
instruction ins = HWIntrinsicInfo::lookupIns(intrinsicId, baseType, m_compiler);

if (!varTypeIsSIMD(op1->gtType))
if (node->OperIsMemoryLoad())
{
// Until we improve the handling of addressing modes in the emitter, we'll create a
// temporary GT_IND to generate code with.
Expand Down
36 changes: 31 additions & 5 deletions src/coreclr/jit/lowerxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2662,16 +2662,42 @@ GenTree* Lowering::LowerHWIntrinsic(GenTreeHWIntrinsic* node)
// be read from an undersized contained memory operand. Any other consumer (a store,
// return, or call argument) materializes a value of the node's own type and size via the
// ABI, so removing the node there would corrupt the copy size; keep the node for those.
//
// The one exception is an AVX2 gather index: the VSIB encoding (xmm vs ymm) is selected
// from the index operand's own width rather than the gather's size, so a width-changing
// reinterpret feeding it is load-bearing and must be kept.

LIR::Use use;
if (BlockRange().TryGetUse(node, &use) && use.User()->OperIsHWIntrinsic())
{
GenTree* op1 = node->Op(1);
GenTree* next = node->gtNext;
GenTreeHWIntrinsic* user = use.User()->AsHWIntrinsic();
GenTree* gatherIndex = nullptr;

use.ReplaceWith(op1);
BlockRange().Remove(node);
return next;
switch (user->GetHWIntrinsicId())
{
case NI_AVX2_GatherVector128:
case NI_AVX2_GatherVector256:
gatherIndex = user->Op(2);
break;

case NI_AVX2_GatherMaskVector128:
case NI_AVX2_GatherMaskVector256:
gatherIndex = user->Op(3);
break;

default:
break;
}

if (gatherIndex != node)
{
GenTree* op1 = node->Op(1);
GenTree* next = node->gtNext;

use.ReplaceWith(op1);
BlockRange().Remove(node);
return next;
}
}
break;
}
Expand Down
64 changes: 64 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_131137/Runtime_131137.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// Lowering elides transparent scalar/vector reinterprets (CreateScalarUnsafe, GetLower, ...).
// Codegen for the dual-overload x64 intrinsics below must therefore distinguish the vector and
// pointer/index overloads using stable node metadata rather than the post-lowering operand type:
// * ConvertTo*Int* picked the pointer (memory-load) overload from the operand type, so an elided
// CreateScalarUnsafe made it load from the scalar value as if it were an address.
// * An AVX2 gather selects its VSIB width (xmm vs ymm index) from the index operand's width, so an
// elided GetLower on the index widened it and gathered too many elements.

namespace Runtime_131137;

using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using Xunit;

public static class Runtime_131137
{
[MethodImpl(MethodImplOptions.NoInlining)]
static Vector128<int> ConvertI32(int packed)
=> Sse41.ConvertToVector128Int32(Vector128.CreateScalarUnsafe(packed).AsByte());

[MethodImpl(MethodImplOptions.NoInlining)]
static Vector128<short> ConvertI16(int packed)
=> Sse41.ConvertToVector128Int16(Vector128.CreateScalarUnsafe(packed).AsSByte());

[MethodImpl(MethodImplOptions.NoInlining)]
static Vector128<long> ConvertI64(int packed)
=> Sse41.ConvertToVector128Int64(Vector128.CreateScalarUnsafe(packed).AsByte());

[ConditionalFact(typeof(Sse41), nameof(Sse41.IsSupported))]
public static void ConvertToVectorFromScalar()
{
Assert.Equal(Vector128.Create(1, 2, 3, 4), ConvertI32(0x04030201));
Assert.Equal(Vector128.Create((short)1, 2, 3, 4, 0, 0, 0, 0), ConvertI16(0x04030201));
Assert.Equal(Vector128.Create(1L, 2), ConvertI64(0x00000201));
}

[MethodImpl(MethodImplOptions.NoInlining)]
static Vector256<int> ConvertI32x8(long packed)
=> Avx2.ConvertToVector256Int32(Vector128.CreateScalarUnsafe(packed).AsByte());

[ConditionalFact(typeof(Avx2), nameof(Avx2.IsSupported))]
public static void ConvertToVector256FromScalar()
{
Assert.Equal(Vector256.Create(1, 2, 3, 4, 5, 6, 7, 8), ConvertI32x8(0x0807060504030201L));
}

[MethodImpl(MethodImplOptions.NoInlining)]
static unsafe Vector128<int> GatherQD(int* baseAddr, Vector256<long> index)
=> Avx2.GatherVector128(baseAddr, index.GetLower(), 4);

[ConditionalFact(typeof(Avx2), nameof(Avx2.IsSupported))]
public static unsafe void GatherWithNarrowedIndex()
{
int* buf = stackalloc int[4] { 20, 21, 22, 23 };
Vector256<long> index = Vector256.Create(0L, 2, 1, 3);

// Only the low 128 bits of the index ({0, 2}) are in play, so lanes 2 and 3 stay zero.
Assert.Equal(Vector128.Create(20, 22, 0, 0), GatherQD(buf, index));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
Comment thread
tannergooding marked this conversation as resolved.
<PropertyGroup>
<OutputType>Exe</OutputType>
<RequiresProcessIsolation>true</RequiresProcessIsolation>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(TestLibraryProjectPath)" />
</ItemGroup>
</Project>
Loading