镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
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
21 changes: 21 additions & 0 deletions src/coreclr/src/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11028,6 +11028,17 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
op2->AsDblCon()->gtDconVal = 1.0 / divisor;
}
}

// array.Length is always positive so GT_DIV can be changed to GT_UDIV
// if op2 is a positive cns
if (!optValnumCSE_phase && op1->OperIs(GT_ARR_LENGTH) && op2->IsIntegralConst() &&
op2->AsIntCon()->IconValue() >= 2) // for 0 and 1 it doesn't matter if it's UDIV or DIV
{
assert(tree->OperIs(GT_DIV));
tree->ChangeOper(GT_UDIV);
return fgMorphSmpOp(tree, mac);
}

#ifndef TARGET_64BIT
if (typ == TYP_LONG)
{
Expand Down Expand Up @@ -11092,6 +11103,16 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac)
goto USE_HELPER_FOR_ARITH;
}

// array.Length is always positive so GT_DIV can be changed to GT_UDIV
// if op2 is a positive cns
if (!optValnumCSE_phase && op1->OperIs(GT_ARR_LENGTH) && op2->IsIntegralConst() &&
op2->AsIntCon()->IconValue() >= 2) // for 0 and 1 it doesn't matter if it's UMOD or MOD
{
assert(tree->OperIs(GT_MOD));
tree->ChangeOper(GT_UMOD);
return fgMorphSmpOp(tree, mac);
}

// Do not use optimizations (unlike UMOD's idiv optimizing during codegen) for signed mod.
// A similar optimization for signed mod will not work for a negative perfectly divisible
// HI-word. To make it correct, we would need to divide without the sign and then flip the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// 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;
Comment thread
EgorBo marked this conversation as resolved.
Resolved
using System.Runtime.CompilerServices;

public class ArrayLengthArithmeticTests
{
private static int returnCode = 100;

public static int Main(string[] args)
{
for (int arrayLength = 0; arrayLength < 100; arrayLength++)
{
var array = new int[arrayLength];

if (arrayLength == 0)
{
Expect<DivideByZeroException>(() => ArrayLengthDiv_cns0(array));
Expect<DivideByZeroException>(() => ArrayLengthDiv_var0(array));
Expect<DivideByZeroException>(() => ArrayLengthMod_cns0(array));
Expect<DivideByZeroException>(() => ArrayLengthMod_var0(array));
}

// Array.Length / cns == Array.Length / ToVar(cns)
CompareResults(ArrayLengthDiv_cns1(array), ArrayLengthDiv_var1(array));
CompareResults(ArrayLengthDiv_cns2(array), ArrayLengthDiv_var2(array));
CompareResults(ArrayLengthDiv_cns3(array), ArrayLengthDiv_var3(array));
CompareResults(ArrayLengthDiv_cns4(array), ArrayLengthDiv_var4(array));
CompareResults(ArrayLengthDiv_cns5(array), ArrayLengthDiv_var5(array));
CompareResults(ArrayLengthDiv_cns8(array), ArrayLengthDiv_var8(array));
CompareResults(ArrayLengthDiv_cns10(array), ArrayLengthDiv_var10(array));
CompareResults(ArrayLengthDiv_cnsMaxValuen1(array), ArrayLengthDiv_varMaxValuen1(array));
CompareResults(ArrayLengthDiv_cnsMaxValue(array), ArrayLengthDiv_varMaxValue(array));
CompareResults(ArrayLengthDiv_cnsn1(array), ArrayLengthDiv_varn1(array));
CompareResults(ArrayLengthDiv_cnsn2(array), ArrayLengthDiv_varn2(array));
CompareResults(ArrayLengthDiv_cnsMinValue(array), ArrayLengthDiv_varMinValue(array));

// Array.Length % cns == Array.Length % ToVar(cns)
CompareResults(ArrayLengthMod_cns1(array), ArrayLengthMod_var1(array));
CompareResults(ArrayLengthMod_cns2(array), ArrayLengthMod_var2(array));
CompareResults(ArrayLengthMod_cns3(array), ArrayLengthMod_var3(array));
CompareResults(ArrayLengthMod_cns4(array), ArrayLengthMod_var4(array));
CompareResults(ArrayLengthMod_cns5(array), ArrayLengthMod_var5(array));
CompareResults(ArrayLengthMod_cns8(array), ArrayLengthMod_var8(array));
CompareResults(ArrayLengthMod_cns10(array), ArrayLengthMod_var10(array));
CompareResults(ArrayLengthMod_cnsMaxValuen1(array), ArrayLengthMod_varMaxValuen1(array));
CompareResults(ArrayLengthMod_cnsMaxValue(array), ArrayLengthMod_varMaxValue(array));
CompareResults(ArrayLengthMod_cnsn1(array), ArrayLengthMod_varn1(array));
CompareResults(ArrayLengthMod_cnsn2(array), ArrayLengthMod_varn2(array));
CompareResults(ArrayLengthMod_cnsMinValue(array), ArrayLengthMod_varMinValue(array));
}

return returnCode;
}

// Array.Length / cns
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns0(int[] array) => array.Length / 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ArrayLengthDiv_cns0 [](start = 66, length = 19)

should we add cases where the operation could become CSE candidate?

 .. = array.Length / 3;
...
.. = array.Length / 3;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I do not see any potential issues here, because we don't change tree->gtType, so I can't construct a test where this transformation can lead to add new CSE candidates or reject an existing.
It would be nice to improve the test even more, but I would not block the PR because of that.

[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns1(int[] array) => array.Length / 1;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns2(int[] array) => array.Length / 2;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns3(int[] array) => array.Length / 3;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns4(int[] array) => array.Length / 4;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns5(int[] array) => array.Length / 5;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns8(int[] array) => array.Length / 8;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cns10(int[] array) => array.Length / 10;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cnsMaxValuen1(int[] array) => array.Length / (int.MaxValue - 1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cnsMaxValue(int[] array) => array.Length / int.MaxValue;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cnsn1(int[] array) => array.Length / -1;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cnsn2(int[] array) => array.Length / -2;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_cnsMinValue(int[] array) => array.Length / int.MinValue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

consider adding few cases for long.


// Array.Length / variable
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var0(int[] array) => array.Length / ToVar(0);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var1(int[] array) => array.Length / ToVar(1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var2(int[] array) => array.Length / ToVar(2);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var3(int[] array) => array.Length / ToVar(3);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var4(int[] array) => array.Length / ToVar(4);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var5(int[] array) => array.Length / ToVar(5);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var8(int[] array) => array.Length / ToVar(8);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_var10(int[] array) => array.Length / ToVar(10);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_varMaxValuen1(int[] array) => array.Length / ToVar(int.MaxValue - 1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_varMaxValue(int[] array) => array.Length / ToVar(int.MaxValue);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_varn1(int[] array) => array.Length / ToVar(-1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_varn2(int[] array) => array.Length / ToVar(-2);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthDiv_varMinValue(int[] array) => array.Length / ToVar(int.MinValue);

// Array.Length % cns
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns0(int[] array) => array.Length % 0;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns1(int[] array) => array.Length % 1;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns2(int[] array) => array.Length % 2;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns3(int[] array) => array.Length % 3;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns4(int[] array) => array.Length % 4;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns5(int[] array) => array.Length % 5;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns8(int[] array) => array.Length % 8;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cns10(int[] array) => array.Length % 10;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cnsMaxValuen1(int[] array) => array.Length % (int.MaxValue - 1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cnsMaxValue(int[] array) => array.Length % int.MaxValue;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cnsn1(int[] array) => array.Length % -1;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cnsn2(int[] array) => array.Length % -2;
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_cnsMinValue(int[] array) => array.Length % int.MinValue;

// Array.Length % variable
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var0(int[] array) => array.Length % ToVar(0);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var1(int[] array) => array.Length % ToVar(1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var2(int[] array) => array.Length % ToVar(2);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var3(int[] array) => array.Length % ToVar(3);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var4(int[] array) => array.Length % ToVar(4);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var5(int[] array) => array.Length % ToVar(5);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var8(int[] array) => array.Length % ToVar(8);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_var10(int[] array) => array.Length % ToVar(10);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_varMaxValuen1(int[] array) => array.Length % ToVar(int.MaxValue - 1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_varMaxValue(int[] array) => array.Length % ToVar(int.MaxValue);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_varn1(int[] array) => array.Length % ToVar(-1);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_varn2(int[] array) => array.Length % ToVar(-2);
[MethodImpl(MethodImplOptions.NoInlining)] private static int ArrayLengthMod_varMinValue(int[] array) => array.Length % ToVar(int.MinValue);

private static void Expect<T>(Action action, [CallerLineNumber] int line = 0) where T : Exception
{
try
{
action();
}
catch (T)
{
return;
}
Console.WriteLine($"{typeof(T).Name} was expected, L{line}");
returnCode++;
}

private static void CompareResults(int a, int b, [CallerLineNumber] int line = 0)
{
if (a != b)
{
Console.WriteLine($"{a} != {b}, L{line}");
returnCode++;
}
}

// cns to var
[MethodImpl(MethodImplOptions.NoInlining)] private static T ToVar<T>(T t) => t;
}
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="ArrayLengthArithmetic.cs" />
</ItemGroup>
</Project>