Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
38 changes: 36 additions & 2 deletions src/coreclr/jit/valuenum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7793,10 +7793,44 @@ ValueNum ValueNumStore::EvalHWIntrinsicFunBinary(var_types type,
}

#ifdef TARGET_ARM64
case NI_AdvSimd_Multiply:
case NI_AdvSimd_MultiplyByScalar:
case NI_AdvSimd_Arm64_Multiply:
case NI_AdvSimd_Arm64_MultiplyByScalar:
{
if (!varTypeIsFloating(baseType))
{
// Handle `x * 0 == 0` and `0 * x == 0`
// Not safe for floating-point when x == -0.0, NaN, +Inf, -Inf
ValueNum zeroVN = VNZeroForType(TypeOfVN(cnsVN));

if (cnsVN == zeroVN)
{
return VNZeroForType(type);
}
}

// Handle x * 1 => x, but only if the scalar RHS is 1.
ValueNum oneVN;
if (varTypeIsSIMD(TypeOfVN(arg1VN)))
{
oneVN = VNOneForSimdType(TypeOfVN(arg1VN), baseType);
}
else
{
oneVN = VNOneForType(baseType);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this actually be hit given that we expect TYP_SIMD8 for the second operand?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question... let me add some strong asserts and simplify this a bit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the same path down in the regular multiply handling is also "dead". It was likely intended to handle the vector * scalar case, but we don't actually ever produce a GT_HWINTRINSIC(TYP_SIMD, TYP_SCALAR) node anymore, afaict.

We appear to always normalize to Broadcast on x86/x64. We similarly normalize to Broadcast on Arm64 except for when a MultiplyByScalar variant exists, in which case we always insert CreateScalarUnsafe.

I then don't see any overloads of the instructions that would take a proper scalar register, so I don't think we'd ever hit it in practice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense... probably something to clean up separately at some point in the future.


if (arg1VN == oneVN)
{
assert(TypeOfVN(arg0VN) == type);
return arg0VN;
}
break;
}
#endif

#ifdef TARGET_ARM64
case NI_AdvSimd_Multiply:
case NI_AdvSimd_Arm64_Multiply:
#else
case NI_SSE_Multiply:
case NI_SSE2_Multiply:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<PropertyGroup>
<Optimize>True</Optimize>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<!-- Needed for CLRTestEnvironmentVariable -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
Expand Down
25 changes: 25 additions & 0 deletions src/tests/JIT/Regression/JitBlue/Runtime_93876/Runtime_93876.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

public static class Runtime_93876
{
[Fact]
public static void Problem()
{
Vector4 v = Mul(0, 1);
Assert.Equal(Vector4.One, v);
Vector64<float> v64 = Mul64(0, 1);
Assert.Equal(Vector64<float>.One, v64);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static Vector4 Mul(float a, float b) => Vector4.Multiply(a + b, Vector4.One);
Copy link
Member

@tannergooding tannergooding Nov 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the type of a + b here that causes the issue? Does it become TYP_DOUBLE or something or is the issue that it's TYP_FLOAT and the other operand is TYP_SIMD16?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gtNewSimdBinOpNode creates the following IR on ARM64:

               [000005] -----------                           HWINTRINSIC simd16 float MultiplyByScalar
               [000003] -----------                         ├──▌  CNS_VEC   simd16<0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000>
               [000004] -----------                         └──▌  HWINTRINSIC simd8  float CreateScalarUnsafe
               [000002] -----------                            └──▌  ADD       float 
               [000000] -----------                               ├──▌  LCL_VAR   float  V00 arg0         
               [000001] -----------                               └──▌  LCL_VAR   float  V01 arg1         

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think MultiplyByScalar shouldn't be handled by this path. E.g. Vector64.Multiply(Vector64<float>.One, x) will produce <x, 0> instead of <x, x> because of that. So this needs a bit more work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a change that handles MultiplyByScalar separately, can you take another look?


[MethodImpl(MethodImplOptions.NoInlining)]
private static Vector64<float> Mul64(float a, float b) => Vector64.Multiply(a + b, Vector64<float>.One);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
</Project>