Skip to content
Open
20 changes: 20 additions & 0 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11307,6 +11307,26 @@ GenTree* Compiler::fgMorphSmpOpOptional(GenTreeOp* tree, bool* optAssertionPropD

break;

case GT_RSH:
case GT_RSZ:
// Optimise right shift of an int -> unsigned cast by a constant amount, and the shift amount is >= the
// bit width of the cast to type. This always equals zero so replace shift & cast with setting to 0.
if (opts.OptimizationEnabled() && op1->OperIs(GT_CAST) && op2->IsCnsIntOrI())
{
GenTreeCast* cast = op1->AsCast();
GenTreeIntCon* cns = op2->AsIntCon();
if (!cast->gtOverflow() && cast->CastOp()->TypeIs(TYP_INT) && varTypeIsUnsigned(cast->CastToType()))
{
ssize_t shiftAmount = cns->IconValue();
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you add a small comment around here saying what the transform does? Just similar to the one on line 11279.

Change lgtm otherwise

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a comment.

unsigned srcBits = genTypeSize(cast->CastToType()) * BITS_PER_BYTE;
if ((shiftAmount >= 0) && (static_cast<unsigned>(shiftAmount) >= srcBits))
{
return gtNewZeroConNode(tree->TypeGet());
}
}
}
break;

case GT_INIT_VAL:
// Initialization values for initBlk have special semantics - their lower
// byte is used to fill the struct. However, we allow 0 as a "bare" value,
Expand Down
349 changes: 349 additions & 0 deletions src/tests/JIT/opt/InstructionCombining/CastRightShift.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
// 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 Xunit;

namespace TestCastRightShift
{
public class Program
{
[MethodImpl(MethodImplOptions.NoInlining)]
[Fact]
public static int CheckCastRightShift()
{
bool fail = false;

if (CastASR7_byte_int(255) == 0)
{
fail = true;
}

if (CastASR8_byte_int(255) != 0)
{
fail = true;
}

if (CastLSR7_byte_uint(255) != 1)
{
fail = true;
}

if (CastLSR8_byte_uint(255) != 0)
{
fail = true;
}

if (CastASR7_byte_long(255) == 0)
{
fail = true;
}

if (CastASR8_byte_long(255) != 0)
{
fail = true;
}

if (CastLSR7_byte_ulong(255) == 0)
{
fail = true;
}

if (CastLSR8_byte_ulong(255) != 0)
{
fail = true;
}

if (CastASR7_sbyte_int(-127) != -1)
{
fail = true;
}

if (CastASR8_sbyte_int(-127) != -1)
{
fail = true;
}

if (CastASR7_sbyte_long(-127) != -1)
{
fail = true;
}

if (CastASR8_sbyte_long(-127) != -1)
{
fail = true;
}

if (CastASR15_ushort_int(65535) == 0)
{
fail = true;
}

if (CastASR16_ushort_int(65535) != 0)
{
fail = true;
}

if (CastLSR15_ushort_uint(65535) == 0)
{
fail = true;
}

if (CastLSR16_ushort_uint(65535) != 0)
{
fail = true;
}

if (CastASR15_ushort_long(65535) == 0)
{
fail = true;
}

if (CastASR16_ushort_long(65535) != 0)
{
fail = true;
}

if (CastLSR15_ushort_ulong(65535) == 0)
{
fail = true;
}

if (CastLSR16_ushort_ulong(65535) != 0)
{
fail = true;
}

if (CastASR15_short_int(-1) != -1)
{
fail = true;
}

if (CastASR16_short_int(-1) != -1)
{
fail = true;
}

if (CastASR15_short_long(-1) != -1)
{
fail = true;
}

if (CastASR16_short_long(-1) != -1)
{
fail = true;
}

if (fail)
{
return 101;
}
return 100;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR7_byte_int(int x)
{
//ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #7
return (byte)x >> 7;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR8_byte_int(int x)
{
//ARM64-FULL-LINE: mov {{w[0-9]+}}, wzr
return (byte)x >> 8;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint CastLSR7_byte_uint(int x)
{
//ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #7
return (uint)(byte)x >> 7;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint CastLSR8_byte_uint(int x)
{
//ARM64-FULL-LINE: mov {{w[0-9]+}}, wzr
return (uint)(byte)x >> 8;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR7_byte_long(int x)
{
//ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #7
//ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}}
return (byte)x >> 7;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR8_byte_long(int x)
{
//ARM64-FULL-LINE: mov {{x[0-9]+}}, xzr
return (byte)x >> 8;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static ulong CastLSR7_byte_ulong(int x)
{
//ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: lsr {{x[0-9]+}}, {{x[0-9]+}}, #7
return (ulong)(byte)x >> 7;
}

// This produces the following tree section with 2 CASTs
// We should check all the CASTs and then check that the lsr value
// is greater than or equal to smallest src bits. In this case
// we could change to mov w0, wzr.
// * RSZ long
// +--* CAST long <- ulong
// | \--* CAST int <- ubyte <- int
// | \--* LCL_VAR int
[MethodImpl(MethodImplOptions.NoInlining)]
static ulong CastLSR8_byte_ulong(int x)
{
//ARM64-FULL-LINE: uxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: lsr {{x[0-9]+}}, {{x[0-9]+}}, #8
return (ulong)(byte)x >> 8;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR7_sbyte_int(int x)
{
//ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #7
return (sbyte)x >> 7;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR8_sbyte_int(int x)
{
//ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #8
return (sbyte)x >> 8;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR7_sbyte_long(int x)
{
//ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #7
//ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}}
return (sbyte)x >> 7;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR8_sbyte_long(int x)
{
//ARM64-FULL-LINE: sxtb {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #8
//ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}}
return (sbyte)x >> 8;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR15_ushort_int(int x)
{
//ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #15
return (ushort)x >> 15;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR16_ushort_int(int x)
{
//ARM64-FULL-LINE: mov {{w[0-9]+}}, wzr
return (ushort)x >> 16;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint CastLSR15_ushort_uint(int x)
{
//ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #15
return (uint)(ushort)x >> 15;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static uint CastLSR16_ushort_uint(int x)
{
//ARM64-FULL-LINE: mov {{w[0-9]+}}, wzr
return (uint)(ushort)x >> 16;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR15_ushort_long(int x)
{
//ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #15
//ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}}
return (ushort)x >> 15;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR16_ushort_long(int x)
{
//ARM64-FULL-LINE: mov {{x[0-9]+}}, xzr
return (ushort)x >> 16;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static ulong CastLSR15_ushort_ulong(int x)
{
//ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: lsr {{x[0-9]+}}, {{x[0-9]+}}, #15
return (ulong)(ushort)x >> 15;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static ulong CastLSR16_ushort_ulong(int x)
{
//ARM64-FULL-LINE: uxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: lsr {{x[0-9]+}}, {{x[0-9]+}}, #16
return (ulong)(ushort)x >> 16;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR15_short_int(int x)
{
//ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #15
return (short)x >> 15;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static int CastASR16_short_int(int x)
{
//ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #16
return (short)x >> 16;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR15_short_long(int x)
{
//ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #15
//ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}}
return (short)x >> 15;
}

[MethodImpl(MethodImplOptions.NoInlining)]
static long CastASR16_short_long(int x)
{
//ARM64-FULL-LINE: sxth {{w[0-9]+}}, {{w[0-9]+}}
//ARM64-FULL-LINE: asr {{w[0-9]+}}, {{w[0-9]+}}, #16
//ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}}
return (short)x >> 16;
}
}
}
17 changes: 17 additions & 0 deletions src/tests/JIT/opt/InstructionCombining/CastRightShift.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Needed for CLRTestEnvironmentVariable -->
<RequiresProcessIsolation>true</RequiresProcessIsolation>
</PropertyGroup>
<PropertyGroup>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="CastRightShift.cs">
<HasDisasmCheck>true</HasDisasmCheck>
</Compile>
<CLRTestEnvironmentVariable Include="DOTNET_TieredCompilation" Value="0" />
<CLRTestEnvironmentVariable Include="DOTNET_JITMinOpts" Value="0" />
</ItemGroup>
</Project>
Loading