diff --git a/src/coreclr/jit/emit.h b/src/coreclr/jit/emit.h index 413b25eb37e0d..3d01433f90e21 100644 --- a/src/coreclr/jit/emit.h +++ b/src/coreclr/jit/emit.h @@ -2079,6 +2079,10 @@ class emitter void emitHandleMemOp(GenTreeIndir* indir, instrDesc* id, insFormat fmt, instruction ins); void spillIntArgRegsToShadowSlots(); +#ifdef TARGET_XARCH + bool emitIsInstrWritingToReg(instrDesc* id, regNumber reg); +#endif // TARGET_XARCH + /************************************************************************/ /* The logic that creates and keeps track of instruction groups */ /************************************************************************/ diff --git a/src/coreclr/jit/emitxarch.cpp b/src/coreclr/jit/emitxarch.cpp index b08aa2fdab39d..c357de5da79ae 100644 --- a/src/coreclr/jit/emitxarch.cpp +++ b/src/coreclr/jit/emitxarch.cpp @@ -505,125 +505,37 @@ bool emitter::AreUpper32BitsZero(regNumber reg) bool result = false; emitPeepholeIterateLastInstrs([&](instrDesc* id) { - switch ((ID_OPS)emitFmtToOps[id->idInsFmt()]) + if (emitIsInstrWritingToReg(id, reg)) { - // This is conservative. - case ID_OP_CALL: - return PEEPHOLE_ABORT; - - default: - break; - } - - // This is a special case for idiv, div, imul, and mul. - // They always write to RAX and RDX. - if (instrHasImplicitRegPairDest(id->idIns())) - { - if (reg == REG_RAX || reg == REG_RDX) - { - result = (id->idOpSize() == EA_4BYTE); - return PEEPHOLE_ABORT; - } - } - - switch (id->idInsFmt()) - { - case IF_RWR: - case IF_RRW: - - case IF_RWR_CNS: - case IF_RRW_CNS: - case IF_RRW_SHF: - - case IF_RWR_RRD: - case IF_RRW_RRD: - case IF_RRW_RRW: - case IF_RRW_RRW_CNS: - - case IF_RWR_RRD_RRD: - case IF_RWR_RRD_RRD_CNS: - - case IF_RWR_RRD_RRD_RRD: - - case IF_RWR_MRD: - case IF_RRW_MRD: - case IF_RRW_MRD_CNS: - - case IF_RWR_RRD_MRD: - case IF_RWR_MRD_CNS: - case IF_RWR_RRD_MRD_CNS: - case IF_RWR_RRD_MRD_RRD: - case IF_RWR_MRD_OFF: - - case IF_RWR_SRD: - case IF_RRW_SRD: - case IF_RRW_SRD_CNS: - - case IF_RWR_RRD_SRD: - case IF_RWR_SRD_CNS: - case IF_RWR_RRD_SRD_CNS: - case IF_RWR_RRD_SRD_RRD: - - case IF_RWR_ARD: - case IF_RRW_ARD: - case IF_RRW_ARD_CNS: - - case IF_RWR_RRD_ARD: - case IF_RWR_ARD_CNS: - case IF_RWR_ARD_RRD: - case IF_RWR_RRD_ARD_CNS: - case IF_RWR_RRD_ARD_RRD: + switch (id->idIns()) { - if (id->idReg1() != reg) - { - switch (id->idInsFmt()) - { - // Handles instructions who write to two registers. - case IF_RRW_RRW: - case IF_RRW_RRW_CNS: - { - if (id->idReg2() == reg) - { - result = (id->idOpSize() == EA_4BYTE); - return PEEPHOLE_ABORT; - } - break; - } - - default: - break; - } - - return PEEPHOLE_CONTINUE; - } - - // movsx always sign extends to 8 bytes. - if (id->idIns() == INS_movsx) - { + // Conservative. + case INS_call: return PEEPHOLE_ABORT; - } - if (id->idIns() == INS_movsxd) - { + // These instructions sign-extend. + case INS_cwde: + case INS_cdq: + case INS_movsx: + case INS_movsxd: return PEEPHOLE_ABORT; - } // movzx always zeroes the upper 32 bits. - if (id->idIns() == INS_movzx) - { + case INS_movzx: result = true; return PEEPHOLE_ABORT; - } - // otherwise rely on operation size. - result = (id->idOpSize() == EA_4BYTE); - return PEEPHOLE_ABORT; + default: + break; } - default: - { - return PEEPHOLE_CONTINUE; - } + // otherwise rely on operation size. + result = (id->idOpSize() == EA_4BYTE); + return PEEPHOLE_ABORT; + } + else + { + return PEEPHOLE_CONTINUE; } }); @@ -677,6 +589,205 @@ bool emitter::AreUpper32BitsSignExtended(regNumber reg) } #endif // TARGET_64BIT +//------------------------------------------------------------------------ +// emitIsInstrWritingToReg: checks if the given register is being written to +// +// Arguments: +// id - instruction of interest +// reg - register of interest +// +// Return Value: +// true if the instruction writes to the given register. +// false if it did not. +// +// Note: This only handles integer registers. Also, an INS_call will always return true. +// +bool emitter::emitIsInstrWritingToReg(instrDesc* id, regNumber reg) +{ + // This only handles integer registers for now. + assert(genIsValidIntReg(reg)); + + instruction ins = id->idIns(); + + // These are special cases since they modify one or more register(s) implicitly. + switch (ins) + { + // This is conservative. We assume a call will write to all registers even if it does not. + case INS_call: + return true; + + // These always write to RAX and RDX. + case INS_idiv: + case INS_div: + case INS_imulEAX: + case INS_mulEAX: + if ((reg == REG_RAX) || (reg == REG_RDX)) + { + return true; + } + break; + + // Always writes to RAX. + case INS_cmpxchg: + if (reg == REG_RAX) + { + return true; + } + break; + + case INS_movsb: + case INS_movsd: +#ifdef TARGET_AMD64 + case INS_movsq: +#endif // TARGET_AMD64 + if ((reg == REG_RDI) || (reg == REG_RSI)) + { + return true; + } + break; + + case INS_stosb: + case INS_stosd: +#ifdef TARGET_AMD64 + case INS_stosq: +#endif // TARGET_AMD64 + if (reg == REG_RDI) + { + return true; + } + break; + + case INS_r_movsb: + case INS_r_movsd: +#ifdef TARGET_AMD64 + case INS_r_movsq: +#endif // TARGET_AMD64 + if ((reg == REG_RDI) || (reg == REG_RSI) || (reg == REG_RCX)) + { + return true; + } + break; + + case INS_r_stosb: + case INS_r_stosd: +#ifdef TARGET_AMD64 + case INS_r_stosq: +#endif // TARGET_AMD64 + if ((reg == REG_RDI) || (reg == REG_RCX)) + { + return true; + } + break; + + default: + break; + } + +#ifdef TARGET_64BIT + // This is a special case for cdq/cwde. + switch (ins) + { + case INS_cwde: + if (reg == REG_RAX) + { + return true; + } + break; + + case INS_cdq: + if (reg == REG_RDX) + { + return true; + } + break; + + default: + break; + } +#endif // TARGET_64BIT + + switch (id->idInsFmt()) + { + case IF_RWR: + case IF_RRW: + + case IF_RWR_CNS: + case IF_RRW_CNS: + case IF_RRW_SHF: + + case IF_RWR_RRD: + case IF_RRW_RRD: + case IF_RRW_RRW: + case IF_RRW_RRW_CNS: + + case IF_RWR_RRD_RRD: + case IF_RWR_RRD_RRD_CNS: + + case IF_RWR_RRD_RRD_RRD: + + case IF_RWR_MRD: + case IF_RRW_MRD: + case IF_RRW_MRD_CNS: + + case IF_RWR_RRD_MRD: + case IF_RWR_MRD_CNS: + case IF_RWR_RRD_MRD_CNS: + case IF_RWR_RRD_MRD_RRD: + case IF_RWR_MRD_OFF: + + case IF_RWR_SRD: + case IF_RRW_SRD: + case IF_RRW_SRD_CNS: + + case IF_RWR_RRD_SRD: + case IF_RWR_SRD_CNS: + case IF_RWR_RRD_SRD_CNS: + case IF_RWR_RRD_SRD_RRD: + + case IF_RWR_ARD: + case IF_RRW_ARD: + case IF_RRW_ARD_CNS: + + case IF_RWR_RRD_ARD: + case IF_RWR_ARD_CNS: + case IF_RWR_ARD_RRD: + case IF_RWR_RRD_ARD_CNS: + case IF_RWR_RRD_ARD_RRD: + { + if (id->idReg1() != reg) + { + switch (id->idInsFmt()) + { + // Handles instructions who write to two registers. + case IF_RRW_RRW: + case IF_RRW_RRW_CNS: + { + if (id->idReg2() == reg) + { + return true; + } + break; + } + + default: + break; + } + + return false; + } + + return true; + } + + default: + { + return false; + } + } + + return false; +} + //------------------------------------------------------------------------ // AreFlagsSetToZeroCmp: Checks if the previous instruction set the SZ, and optionally OC, flags to // the same values as if there were a compare to 0 diff --git a/src/tests/JIT/opt/Regressions/Regression6.cs b/src/tests/JIT/opt/Regressions/Regression6.cs new file mode 100644 index 0000000000000..f19dd733cc3a0 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression6.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Generated by Fuzzlyn v1.5 on 2023-02-26 16:54:59 +// Run on X64 Linux +// Seed: 15812590312404596729 +// Note: This test is to make sure we do not remove a 'mov eax, eax' instruction if the trailing instructions like 'cwde' modifies 'eax'. +public class C0 +{ + public ulong F2; + public C0(ulong f2) + { + F2 = f2; + } +} + +public class Program +{ + public static ushort s_2; + public static C0 s_7; + public static int Main() + { + ulong result = 1234; + for (int vr0 = 0; vr0 < 2; vr0++) + { + s_7 = M5(); + ulong vr1 = s_7.F2; + result = vr1; + } + + if (result != 18446744069414584320) + return 0; + + return 100; + } + + public static C0 M5() + { + return new C0(~(ulong)(4294967295U & (-(1 % (s_2-- | 1))))); + } +} \ No newline at end of file diff --git a/src/tests/JIT/opt/Regressions/Regression6.csproj b/src/tests/JIT/opt/Regressions/Regression6.csproj new file mode 100644 index 0000000000000..f3e1cbd44b404 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression6.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + +