diff --git a/src/coreclr/jit/instr.cpp b/src/coreclr/jit/instr.cpp index 500e94832339cc..9e7354784992bf 100644 --- a/src/coreclr/jit/instr.cpp +++ b/src/coreclr/jit/instr.cpp @@ -1975,45 +1975,19 @@ instruction CodeGen::ins_Move_Extend(var_types srcType, bool srcInReg) // if (srcInReg) { - if (varTypeIsUnsigned(srcType)) + if (!varTypeIsSmall(srcType)) { - if (varTypeIsByte(srcType)) - { - ins = INS_uxtb; - } - else if (varTypeIsShort(srcType)) - { - ins = INS_uxth; - } - else - { - // A mov Rd, Rm instruction performs the zero extend - // for the upper 32 bits when the size is EA_4BYTE - - ins = INS_mov; - } + // An int/long value already fills its register width, so a plain mov suffices; + // for int the EA_4BYTE mov also zero extends the unused upper bits. + ins = INS_mov; + } + else if (varTypeIsUnsigned(srcType)) + { + ins = varTypeIsByte(srcType) ? INS_uxtb : INS_uxth; } else { - if (varTypeIsByte(srcType)) - { - ins = INS_sxtb; - } - else if (varTypeIsShort(srcType)) - { - ins = INS_sxth; - } - else - { - if (srcType == TYP_INT) - { - ins = INS_sxtw; - } - else - { - ins = INS_mov; - } - } + ins = varTypeIsByte(srcType) ? INS_sxtb : INS_sxth; } } else diff --git a/src/tests/JIT/opt/InstructionCombining/IntMoveNoSignExtend.cs b/src/tests/JIT/opt/InstructionCombining/IntMoveNoSignExtend.cs new file mode 100644 index 00000000000000..05d2b71de536e2 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/IntMoveNoSignExtend.cs @@ -0,0 +1,78 @@ +// 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 TestIntMoveNoSignExtend +{ + public class Program + { + static int s_value = 7; + + [MethodImpl(MethodImplOptions.NoInlining)] + static int GetInt() => s_value; + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Sink(int x) => s_value ^= x; + + // A reg-to-reg move of a TYP_INT value (here the call result kept in a callee-saved register + // across the following call) must not be sign extended to 64 bits: the upper bits are never + // observed for an int, so a plain 'mov' suffices instead of an 'sxtw'. + [MethodImpl(MethodImplOptions.NoInlining)] + static int IntMove() + { + //ARM64-NOT: sxtw + int a = GetInt(); + Sink(0); + return a + GetInt(); + } + + // A genuine int -> long widening must still sign extend with 'sxtw Xd, Wn'. + [MethodImpl(MethodImplOptions.NoInlining)] + static long Widen(int x) + { + //ARM64-FULL-LINE: sxtw {{x[0-9]+}}, {{w[0-9]+}} + return x; + } + + [Fact] + public static int TestEntryPoint() + { + int result = 100; + + s_value = 7; + if (IntMove() != 14) + { + result = -1; + } + + // Negative values must round-trip correctly through the move. + s_value = -123456; + if (IntMove() != -246912) + { + result = -1; + } + + s_value = int.MinValue; + // int.MinValue + int.MinValue overflows to 0 in unchecked int arithmetic. + if (IntMove() != 0) + { + result = -1; + } + + // Genuine widening must preserve the sign. + if (Widen(-7) != -7L) + { + result = -1; + } + if (Widen(int.MinValue) != int.MinValue) + { + result = -1; + } + + return result; + } + } +} diff --git a/src/tests/JIT/opt/InstructionCombining/IntMoveNoSignExtend.csproj b/src/tests/JIT/opt/InstructionCombining/IntMoveNoSignExtend.csproj new file mode 100644 index 00000000000000..9a71cd1712d753 --- /dev/null +++ b/src/tests/JIT/opt/InstructionCombining/IntMoveNoSignExtend.csproj @@ -0,0 +1,17 @@ + + + + true + + + None + True + + + + true + + + + +