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
6 changes: 4 additions & 2 deletions src/coreclr/jit/lowerarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1395,12 +1395,13 @@ bool Lowering::TryLowerAddForPossibleContainment(GenTreeOp* node, GenTree** next
void Lowering::LowerHWIntrinsicFusedMultiplyAddScalar(GenTreeHWIntrinsic* node)
{
assert(node->GetHWIntrinsicId() == NI_AdvSimd_FusedMultiplyAddScalar);
assert(varTypeIsFloating(node->GetSimdBaseType()));

GenTree* op1 = node->Op(1);
GenTree* op2 = node->Op(2);
GenTree* op3 = node->Op(3);

auto lowerOperand = [this](GenTree* op) {
auto lowerOperand = [this, node](GenTree* op) {
bool wasNegated = false;

if (op->OperIsHWIntrinsic())
Expand All @@ -1414,7 +1415,8 @@ void Lowering::LowerHWIntrinsicFusedMultiplyAddScalar(GenTreeHWIntrinsic* node)
{
GenTree* valueOp = opIntrinsic->Op(1);

if (valueOp->OperIs(GT_NEG))
// Reinterprets can make the scalar's negation differ from negating an FMA element.
if (valueOp->OperIs(GT_NEG) && valueOp->TypeIs(node->GetSimdBaseType()))
{
opIntrinsic->Op(1) = valueOp->gtGetOp1();
BlockRange().Remove(valueOp);
Expand Down
7 changes: 4 additions & 3 deletions src/coreclr/jit/lowerxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,7 @@ void Lowering::LowerHWIntrinsicCC(GenTreeHWIntrinsic* node, NamedIntrinsic newIn
void Lowering::LowerFusedMultiplyOp(GenTreeHWIntrinsic* node)
{
assert(node->GetOperandCount() == 3);
assert(varTypeIsFloating(node->GetSimdBaseType()));

bool negated = false;
bool subtract = false;
Expand Down Expand Up @@ -1297,12 +1298,12 @@ void Lowering::LowerFusedMultiplyOp(GenTreeHWIntrinsic* node)
{
GenTree* arg = node->Op(i);

if (isScalar && arg->OperIs(GT_NEG))
if (isScalar && arg->OperIs(GT_NEG) && arg->TypeIs(node->GetSimdBaseType()))
{
// For scalar FMA the CreateScalarUnsafe wrapper around each argument has already been
// removed during lowering (floating-point CreateScalarUnsafe is a no-op), so a negated
// scalar argument now appears as a bare GT_NEG. Fold that negation into the FMA variant
// and drop the GT_NEG node.
// scalar argument now appears as a bare GT_NEG. Only fold it if its type matches the FMA
// element type: a reinterpret can otherwise change which bit represents the sign.

GenTree* negOp = arg->gtGetOp1();
BlockRange().Remove(arg);
Expand Down
54 changes: 54 additions & 0 deletions src/tests/JIT/Regression_ro_2/Runtime_134053.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

public class Runtime_134053
{
[ConditionalTheory(typeof(AdvSimd.Arm64), nameof(AdvSimd.Arm64.IsSupported))]
[InlineData(0x4008000000000000L)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ArmIntegerNegation(long value)
{
// Integer negation changes the encoding of 3.0 to -1.5, not -3.0.
Vector64<double> one = Vector64.Create(1.0);
Assert.Equal(-0.5, AdvSimd.FusedMultiplyAddScalar(Vector64.Create(-value).AsDouble(), one, one).ToScalar());
}

[ConditionalTheory(typeof(AdvSimd.Arm64), nameof(AdvSimd.Arm64.IsSupported))]
[InlineData(0x4008000040000000L)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ArmReinterpretedDoubleNegation(long bits)
{
// Double negation leaves the low 32 bits (2.0f) unchanged.
double value = BitConverter.Int64BitsToDouble(bits);
Vector64<float> one = Vector64.Create(1.0f);
Assert.Equal(3.0f, AdvSimd.FusedMultiplyAddScalar(one, Vector64.CreateScalarUnsafe(-value).AsSingle(), one).ToScalar());
}

[ConditionalTheory(typeof(Fma), nameof(Fma.IsSupported))]
[InlineData(0x4008000040000000L)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void XarchReinterpretedDoubleNegation(long bits)
{
double value = BitConverter.Int64BitsToDouble(bits);
Vector128<float> one = Vector128.Create(1.0f);
Assert.Equal(3.0f, Fma.MultiplyAddScalar(one, Vector128.CreateScalarUnsafe(-value).AsSingle(), one).ToScalar());
}

[Theory]
[InlineData(2.0, 3.0, 4.0, 0x4000000000000000L)]
[InlineData(0.0, -1.0, 0.0, long.MinValue)]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void SameTypeNegation(double x, double y, double z, long expectedBits)
{
double expected = BitConverter.Int64BitsToDouble(expectedBits);
Assert.Equal(expectedBits, BitConverter.DoubleToInt64Bits(Math.FusedMultiplyAdd(-x, -y, -z)));
Assert.Equal(BitConverter.SingleToInt32Bits((float)expected), BitConverter.SingleToInt32Bits(MathF.FusedMultiplyAdd(-(float)x, -(float)y, -(float)z)));
}
}
Loading