Skip to content

Commit

Permalink
RyuJIT: x*2 -> x+x; x*1 -> x (fp) (#33024)
Browse files Browse the repository at this point in the history
* Optimize "x * 2" to "x + x" for floats

* Add tests

* Fix typo

* Address feedback

* Move to morph.cpp, also add "x*1" optimization

* clean up

* clean up

* update op2

* Fix incorrect "fgMakeMultiUse" usage

* update op1

* Address feedback

* Add opts.OptimizationEnabled()
  • Loading branch information
EgorBo committed May 6, 2020
1 parent 87f7afa commit 5abde01
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
24 changes: 24 additions & 0 deletions src/coreclr/src/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13095,6 +13095,30 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
op2 = tree->AsOp()->gtOp2;
}

// See if we can fold floating point operations (can regress minopts mode)
if (opts.OptimizationEnabled() && varTypeIsFloating(tree->TypeGet()) && !optValnumCSE_phase)
{
if ((oper == GT_MUL) && !op1->IsCnsFltOrDbl() && op2->IsCnsFltOrDbl())
{
if (op2->AsDblCon()->gtDconVal == 2.0)
{
// Fold "x*2.0" to "x+x"
op2 = op1->OperIsLeaf() ? gtCloneExpr(op1) : fgMakeMultiUse(&tree->AsOp()->gtOp1);
op1 = tree->AsOp()->gtOp1;
oper = GT_ADD;
tree = gtNewOperNode(oper, tree->TypeGet(), op1, op2);
INDEBUG(tree->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
}
else if (op2->AsDblCon()->gtDconVal == 1.0)
{
// Fold "x*1.0" to "x"
DEBUG_DESTROY_NODE(op2);
DEBUG_DESTROY_NODE(tree);
return op1;
}
}
}

/* See if we can fold GT_ADD nodes. */

if (oper == GT_ADD)
Expand Down
133 changes: 133 additions & 0 deletions src/coreclr/tests/src/JIT/opt/InstructionCombining/MulToAdd.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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.Linq;
using System.Runtime.CompilerServices;

// Test "X * 2" to "X + X"

public class Program
{
private static int resultCode = 100;

public static int Main(string[] args)
{
float[] testValues =
{
0, 0.01f, 1.333f, 1/3.0f, 0.5f, 1, 2, 3, 4,
MathF.PI, MathF.E,
float.MinValue, float.MaxValue,
int.MaxValue, long.MaxValue,
int.MinValue, long.MinValue,
float.NegativeInfinity,
float.PositiveInfinity,
float.NaN,
};

testValues = testValues.Concat(testValues.Select(v => -v)).ToArray();

foreach (float testValue in testValues)
{
var tf = new TestFloats();

// Case 1: argument
AssertEquals(tf.TestArg(testValue), tf.TestArg_var(testValue));

// Case 2: ref argument
float t1 = testValue, t2 = testValue;
tf.TestArgRef(ref t1);
tf.TestArgRef_var(ref t2);
AssertEquals(t1, t2);

// Case 3: out argument
tf.TestArgOut(t1, out t1);
tf.TestArgOut_var(t2, out t2);
AssertEquals(t1, t2);

// Case 4: field
tf.TestField();
tf.TestField_var();
AssertEquals(tf.field1, tf.field2);

// Case 5: call
AssertEquals(tf.TestCall(), tf.TestCall_var());
AssertEquals(tf.field1, tf.field2);
}

return resultCode;
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void AssertEquals(float expected, float actual)
{
int expectedBits = BitConverter.SingleToInt32Bits(expected);
int actualBits = BitConverter.SingleToInt32Bits(actual);
if (expectedBits != actualBits)
{
resultCode++;
Console.WriteLine($"AssertEquals: {expected} != {actual}");
}
}
}

public class TestFloats
{
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Var<T>(T t) => t;


// Case 1: argument

[MethodImpl(MethodImplOptions.NoInlining)]
public float TestArg(float x) => x * 2;

[MethodImpl(MethodImplOptions.NoInlining)]
public float TestArg_var(float x) => x * Var(2);


// Case 2: ref argument

[MethodImpl(MethodImplOptions.NoInlining)]
public void TestArgRef(ref float x) => x *= 2;

[MethodImpl(MethodImplOptions.NoInlining)]
public void TestArgRef_var(ref float x) => x *= Var(2);


// Case 3: out argument

[MethodImpl(MethodImplOptions.NoInlining)]
public void TestArgOut(float x, out float y) => y = x * 2;

[MethodImpl(MethodImplOptions.NoInlining)]
public void TestArgOut_var(float x, out float y) => y = x * Var(2);


// Case 4: field

public float field1 = 3.14f;
[MethodImpl(MethodImplOptions.NoInlining)]
public void TestField() => field1 *= 2;

public float field2 = 3.14f;
[MethodImpl(MethodImplOptions.NoInlining)]
public void TestField_var() => field2 *= Var(2);


// Case 5: Call

[MethodImpl(MethodImplOptions.NoInlining)]
public float Call1() => field1++; // with side-effect

[MethodImpl(MethodImplOptions.NoInlining)]
public float Call2() => field2++; // with side-effect


[MethodImpl(MethodImplOptions.NoInlining)]
public float TestCall() => Call1() * 2;

[MethodImpl(MethodImplOptions.NoInlining)]
public float TestCall_var() => Call2() * Var(2);
}
12 changes: 12 additions & 0 deletions src/coreclr/tests/src/JIT/opt/InstructionCombining/MulToAdd.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="MulToAdd.cs" />
</ItemGroup>
</Project>

0 comments on commit 5abde01

Please sign in to comment.