diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 6e8b26bcf21584..e2b079a04c4616 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -3389,14 +3389,27 @@ bool Compiler::optNarrowTree(GenTree* tree, var_types srct, var_types dstt, Valu } #endif - if ((tree->CastToType() != srct) || tree->gtOverflow()) + // We are called recursively to push a narrowing cast down through `tree`. + // In this GT_CAST case, `tree` is an inner cast whose result feeds the outer + // narrowing operation; `srct` is that result type (the inner cast's TypeGet) + // and `dstt` is the type we are narrowing to. + // + // We recognize the pattern (simplified): + // CAST( CAST(op1) ) e.g. (int)(long)x + // and below rewrite the inner cast to int<->int so the chain folds away. + // + // CastToType carries signedness independently of tree->TypeGet() (e.g. + // CAST_LONG <- ULONG <- uint has CastToType=ULONG while tree->TypeGet()==LONG). + // Use genActualType on both sides of the compare so an unsigned-widening inner + // cast still matches a LONG `srct`. + if ((genActualType(tree->CastToType()) != genActualType(srct)) || tree->gtOverflow()) { return false; } if (varTypeIsInt(op1) && varTypeIsInt(dstt) && tree->TypeIs(TYP_LONG)) { - // We have a CAST that converts into to long while dstt is int. + // We have a CAST that converts int to long while dstt is int. // so we can just convert the cast to int -> int and someone will clean it up. if (doit) { diff --git a/src/tests/JIT/opt/InstructionCombining/Casts.cs b/src/tests/JIT/opt/InstructionCombining/Casts.cs index 438b4869b3ccf5..b9586fecd6c249 100644 --- a/src/tests/JIT/opt/InstructionCombining/Casts.cs +++ b/src/tests/JIT/opt/InstructionCombining/Casts.cs @@ -484,7 +484,7 @@ static ulong CastUIntUShortULong(uint x) [MethodImpl(MethodImplOptions.NoInlining)] static uint CastUIntULongUInt(uint x) { - //ARM64-FULL-LINE: mov {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-NOT: mov {{w[0-9]+}}, {{w[0-9]+}} return (uint)(ulong)x; } diff --git a/src/tests/JIT/opt/InstructionCombining/Cmn.cs b/src/tests/JIT/opt/InstructionCombining/Cmn.cs index f8e05ed9125a0d..a45e90c30a01c4 100644 --- a/src/tests/JIT/opt/InstructionCombining/Cmn.cs +++ b/src/tests/JIT/opt/InstructionCombining/Cmn.cs @@ -93,8 +93,7 @@ static bool CmnLSLSwap(int a, int b) [MethodImpl(MethodImplOptions.NoInlining)] static bool CmnLSR(uint a, uint b) { - //ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #2 - //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: cmn {{w[0-9]+}}, {{w[0-9]+}}, LSR #2 if (a == (uint)-(b>>2)) { return true; diff --git a/src/tests/JIT/opt/InstructionCombining/Mvn.cs b/src/tests/JIT/opt/InstructionCombining/Mvn.cs index 74957a091e63af..028fb556f7ce40 100644 --- a/src/tests/JIT/opt/InstructionCombining/Mvn.cs +++ b/src/tests/JIT/opt/InstructionCombining/Mvn.cs @@ -45,6 +45,11 @@ public static int CheckMvn() fail = true; } + if (MvnCastChainLSR(0x10) != 0xFFFFFFFD) + { + fail = true; + } + if (fail) { return 101; @@ -93,5 +98,12 @@ static ulong MvnLargeShift64Bit(ulong a) //ARM64-FULL-LINE: mvn {{x[0-9]+}}, {{x[0-9]+}}, LSR #32 return ~(a>>160); } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint MvnCastChainLSR(uint a) + { + //ARM64-FULL-LINE: mvn {{w[0-9]+}}, {{w[0-9]+}}, LSR #3 + return (uint)~(ulong)(a>>3); + } } } diff --git a/src/tests/JIT/opt/InstructionCombining/Neg.cs b/src/tests/JIT/opt/InstructionCombining/Neg.cs index e270c013c2ccf8..58bcbb355e6b3c 100644 --- a/src/tests/JIT/opt/InstructionCombining/Neg.cs +++ b/src/tests/JIT/opt/InstructionCombining/Neg.cs @@ -573,8 +573,7 @@ static int NegLSL(int a) [MethodImpl(MethodImplOptions.NoInlining)] static uint NegLSR(uint a) { - //ARM64-FULL-LINE: lsr {{w[0-9]+}}, {{w[0-9]+}}, #20 - //ARM64-FULL-LINE: neg {{w[0-9]+}}, {{w[0-9]+}} + //ARM64-FULL-LINE: neg {{w[0-9]+}}, {{w[0-9]+}}, LSR #20 return (uint)-(a >> 20); }