diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 220059c38dd42e..e49916d0f21b55 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4783,6 +4783,8 @@ class Compiler // lowering that is distributed between fgMorph and the lowering phase of LSRA. PhaseStatus fgSimpleLowering(); + bool fgSimpleLowerCastOfSmpOp(LIR::Range& range, GenTreeCast* cast); + #if FEATURE_LOOP_ALIGN PhaseStatus placeLoopAlignInstructions(); #endif diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 0bad2bc849a220..40c72bfccf5a88 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -2989,6 +2989,15 @@ PhaseStatus Compiler::fgSimpleLowering() } #endif // FEATURE_FIXED_OUT_ARGS + case GT_CAST: + { + if (tree->AsCast()->CastOp()->OperIsSimple() && fgSimpleLowerCastOfSmpOp(range, tree->AsCast())) + { + madeChanges = true; + } + break; + } + default: { // No other operators need processing. @@ -3047,6 +3056,91 @@ PhaseStatus Compiler::fgSimpleLowering() return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } +//------------------------------------------------------------------------ +// fgSimpleLowerCastOfSmpOp: Optimization to remove CAST nodes from operands of some simple ops that are safe to do so +// since the upper bits do not affect the lower bits, and result of the simple op is zero/sign-extended via a CAST. +// Example: +// CAST(ADD(CAST(x), CAST(y))) transforms to CAST(ADD(x, y)) +// +// Returns: +// True or false, representing changes were made. +// +// Notes: +// This optimization could be done in morph, but it cannot because there are correctness +// problems with NOLs (normalized-on-load locals) and how they are handled in VN. +// Simple put, you cannot remove a CAST from CAST(LCL_VAR{nol}) in HIR. +// +// Because the optimization happens after rationalization, turning into LIR, it is safe to remove the CAST. +// +bool Compiler::fgSimpleLowerCastOfSmpOp(LIR::Range& range, GenTreeCast* cast) +{ + GenTree* castOp = cast->CastOp(); + var_types castToType = cast->CastToType(); + var_types srcType = castOp->TypeGet(); + + assert(castOp->OperIsSimple()); + + if (opts.OptimizationDisabled()) + return false; + + if (cast->gtOverflow()) + return false; + + if (castOp->OperMayOverflow() && castOp->gtOverflow()) + return false; + + // Only optimize if the castToType is a small integer type. + // Only optimize if the srcType is an integer type. + if (!varTypeIsSmall(castToType) || !varTypeIsIntegral(srcType)) + return false; + + // These are the only safe ops where the CAST is not necessary for the inputs. + if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) + { + bool madeChanges = false; + + if (castOp->gtGetOp1()->OperIs(GT_CAST)) + { + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + + if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && + (castToType == op1->CastToType())) + { + // Removes the cast. + castOp->AsOp()->gtOp1 = op1->CastOp(); + range.Remove(op1); + madeChanges = true; + } + } + + if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) + { + GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); + + if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && + (castToType == op2->CastToType())) + { + // Removes the cast. + castOp->AsOp()->gtOp2 = op2->CastOp(); + range.Remove(op2); + madeChanges = true; + } + } + +#ifdef DEBUG + if (madeChanges) + { + JITDUMP("Lower - Cast of Simple Op %s:\n", GenTree::OpName(cast->OperGet())); + DISPTREE(cast); + } +#endif // DEBUG + + return madeChanges; + } + + return false; +} + /***************************************************************************************************** * * Function to return the last basic block in the main part of the function. With funclets, it is diff --git a/src/tests/JIT/opt/Add/IntAdd.cs b/src/tests/JIT/opt/Add/IntAdd.cs new file mode 100644 index 00000000000000..158e54a1e22904 --- /dev/null +++ b/src/tests/JIT/opt/Add/IntAdd.cs @@ -0,0 +1,150 @@ +// 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; + +namespace CodeGenTests +{ + static class IntAdd + { + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte Int8_Add(sbyte x, sbyte y) + { + // X64-NOT: movsx + + // X64: add + // X64-NEXT: movsx + + return (sbyte)(x + y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte UInt8_Add(byte x, byte y) + { + // X64-NOT: movzx + + // X64: add + // X64-NEXT: movzx + + return (byte)(x + y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static short Int16_Add(short x, short y) + { + // X64-NOT: movsx + + // X64: add + // X64-NEXT: movsx + + // X64-NOT: cwde + + return (short)(x + y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort UInt16_Add(ushort x, ushort y) + { + // X64-NOT: movzx + + // X64: add + // X64-NEXT: movzx + + return (ushort)(x + y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Int32_Add(int x, int y) + { + // X64: lea + + return x + y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint UInt32_Add(uint x, uint y) + { + // X64: lea + + return x + y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long Int64_Add(long x, long y) + { + // X64: lea + + return x + y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong UInt64_Add(ulong x, ulong y) + { + // X64: lea + + return x + y; + } + + static int Main() + { + // Int8 + if (Int8_Add(SByte.MaxValue, 15) != -114) + return 0; + + if (Int8_Add(15, SByte.MaxValue) != -114) + return 0; + + // UInt8 + if (UInt8_Add(Byte.MaxValue, 15) != 14) + return 0; + + if (UInt8_Add(15, Byte.MaxValue) != 14) + return 0; + + // Int16 + if (Int16_Add(Int16.MaxValue, 15) != -32754) + return 0; + + if (Int16_Add(15, Int16.MaxValue) != -32754) + return 0; + + // UInt16 + if (UInt16_Add(UInt16.MaxValue, 15) != 14) + return 0; + + if (UInt16_Add(15, UInt16.MaxValue) != 14) + return 0; + + // Int32 + if (Int32_Add(Int32.MaxValue, 15) != -2147483634) + return 0; + + if (Int32_Add(15, Int32.MaxValue) != -2147483634) + return 0; + + // UInt32 + if (UInt32_Add(UInt32.MaxValue, 15) != 14) + return 0; + + if (UInt32_Add(15, UInt32.MaxValue) != 14) + return 0; + + // Int64 + if (Int64_Add(Int64.MaxValue, 15) != -9223372036854775794) + return 0; + + if (Int64_Add(15, Int64.MaxValue) != -9223372036854775794) + return 0; + + // UInt64 + if (UInt64_Add(UInt64.MaxValue, 15) != 14) + return 0; + + if (UInt64_Add(15, UInt64.MaxValue) != 14) + return 0; + + return 100; + } + } +} \ No newline at end of file diff --git a/src/tests/JIT/opt/Add/IntAdd.csproj b/src/tests/JIT/opt/Add/IntAdd.csproj new file mode 100644 index 00000000000000..42a89c8384d74e --- /dev/null +++ b/src/tests/JIT/opt/Add/IntAdd.csproj @@ -0,0 +1,17 @@ + + + Exe + + + None + True + + + + true + + + + + + diff --git a/src/tests/JIT/opt/And/IntAnd.cs b/src/tests/JIT/opt/And/IntAnd.cs index 80fb37b4e32b19..1f2e2275c5780e 100644 --- a/src/tests/JIT/opt/And/IntAnd.cs +++ b/src/tests/JIT/opt/And/IntAnd.cs @@ -8,6 +8,59 @@ namespace CodeGenTests { class IntAnd { + [MethodImpl(MethodImplOptions.NoInlining)] + static void SideEffect() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Test_UInt32_UInt32_And(uint x, uint y) + { + // X64-NOT: movzx + + // We expect 'and reg8, reg8'. + // X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} + + if ((byte)(x & y) == 0) + { + SideEffect(); + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Test_UInt32_UInt32_CastByte_And(uint x, uint y) + { + // X64-NOT: movzx + + // We expect 'and reg8, reg8'. + // X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} + + if ((byte)((byte)x & y) == 0) + { + SideEffect(); + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool Test_UInt32_UInt32_CastByte_CastByte_And(uint x, uint y) + { + // X64-NOT: movzx + + // We expect 'and reg8, reg8'. + // X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} + + if ((byte)((byte)x & (byte)y) == 0) + { + SideEffect(); + return true; + } + return false; + } + [MethodImpl(MethodImplOptions.NoInlining)] static uint Test_And_UInt32_MaxValue(uint i) { @@ -19,6 +72,108 @@ static uint Test_And_UInt32_MaxValue(uint i) static int Main() { + // No CastByte + if (!Test_UInt32_UInt32_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (!Test_UInt32_UInt32_And(0b0000_0000_0000_0000_0000_0000_0000_0001, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_And(0b0000_0000_0000_0000_0000_0000_0000_0000, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (Test_UInt32_UInt32_And(0b0000_0000_0000_0000_0000_0000_0000_0001, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (!Test_UInt32_UInt32_And(0b0000_0000_0000_0000_0000_0000_0000_0010, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (Test_UInt32_UInt32_And(0b0000_0000_0000_0000_0000_0000_0000_0010, 0b0000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_And(0b1000_0000_0000_0000_0000_0000_0000_0010, 0b1000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_And(0b0010_0000_0000_0000_0000_0000_0000_0010, 0b1000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_And(0b1000_0000_0000_0000_0000_0000_0000_0010, 0b0010_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + // CastByte + if (!Test_UInt32_UInt32_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0001, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0000, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0001, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0010, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0010, 0b0000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0010, 0b1000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b0010_0000_0000_0000_0000_0000_0000_0010, 0b1000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0010, 0b0010_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + // CastByte_CastByte + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0001, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0000, 0b0000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0000, 0b1000_0000_0000_0000_0000_0000_0000_0000)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0001, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0010, 0b0000_0000_0000_0000_0000_0000_0000_0001)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b0000_0000_0000_0000_0000_0000_0000_0010, 0b0000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0010, 0b1000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b0010_0000_0000_0000_0000_0000_0000_0010, 0b1000_0000_0000_0000_0000_0000_0000_0010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b1000_0000_0000_0000_0000_0000_0000_0010, 0b0010_0000_0000_0000_0000_0000_0000_0010)) + return 0; + if (Test_And_UInt32_MaxValue(1234) != 1234) return 0; diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1.cs b/src/tests/JIT/opt/Divide/Regressions/Regression1.cs new file mode 100644 index 00000000000000..0e6b0bf7313721 --- /dev/null +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1.cs @@ -0,0 +1,257 @@ +// 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; + +public class Program +{ + [MethodImpl(MethodImplOptions.NoInlining)] + public static ushort GetUShortValue() + { + return 24648; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int GetByteMaxValue() + { + return byte.MaxValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int GetSByteMaxValue() + { + return sbyte.MaxValue; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Test1(ushort vr6) + { + ushort vr3 = 1; + ushort vr4 = (ushort)~vr3; + return (byte)((byte)vr6 / (byte)((byte)vr4 | 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Test2() + { + ushort vr3 = 1; + ushort vr4 = (ushort)~vr3; + ushort vr6 = 24648; + return (byte)((byte)vr6 / (byte)((byte)vr4 | 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ushort Test3() + { + ushort vr3 = 1; + ushort vr4 = (ushort)~vr3; + return (byte)((byte)vr4 | 1); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Test4() + { + ushort vr1 = 24648; + return (byte)((byte)vr1 / GetByteMaxValue()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Test5() + { + ushort vr3 = 1; + ushort vr4 = (ushort)~vr3; + ushort vr6 = 24648; + return (sbyte)((sbyte)vr6 / (sbyte)((sbyte)vr4 | 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Test6() + { + return (byte)((byte)GetUShortValue() / GetByteMaxValue()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Test7() + { + ushort vr1 = 24648; + return (byte)((byte)vr1 / GetByteMaxValue()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static uint Test8() + { + ushort vr1 = GetUShortValue(); + return (byte)((byte)vr1 / GetByteMaxValue()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Test9() + { + ushort vr1 = 24648; + return (sbyte)((sbyte)vr1 / GetSByteMaxValue()); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static int Test10(ushort vr6) + { + ushort vr3 = 1; + ushort vr4 = (ushort)~vr3; + return (sbyte)((sbyte)vr6 / (sbyte)((sbyte)vr4 | 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ushort Test11(int v) + { + return (ushort)((ushort)1 / (ushort)v); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short Test12(int v) + { + return (short)((short)1 / (short)v); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ushort Test13(int v) + { + return (ushort)((ushort)v / 2); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static ushort Test14(int v1, int v2) + { + return (ushort)((ushort)v1 / (ushort)v2); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short Test15() + { + short y = short.MinValue; + return unchecked((short)(y / -1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short Test16(short y) + { + return unchecked((short)(y / -1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short Test17() + { + try + { + short y = short.MinValue; + return checked((short)(y / -1)); + } + catch (ArithmeticException) + { + return 456; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short Test18(int v) + { + return (short)((short)v / 2); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short Test19(int x, int y) + { + return (short)((short)x / (short)y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short Test20(short x, short y) + { + return (short)(x / y); + } + + public static int Main() + { + var result1 = Test1(24648); + var result2 = Test2(); + var result3 = Test3(); + var result4 = Test4(); + var result5 = Test5(); + var result6 = Test6(); + var result7 = Test7(); + var result8 = Test8(); + var result9 = Test9(); + var result10 = Test10(24648); + var result11 = Test11(0x10001); + var result12 = Test12(0x10001); + var result13 = Test13(0x10000); + var result14 = Test14(1, 0x10001); + var result15 = Test15(); + var result16 = Test16(short.MinValue); + var result17 = Test17(); + var result18 = Test18(0x10000); + var result19 = Test19(0x10000, 2); + var result20 = Test20(0, 2); + + if (result1 != 0) + return 0; + + if (result2 != 0) + return 0; + + if (result3 != 255) + return 0; + + if (result4 != 0) + return 0; + + if (result5 != -72) + return 0; + + if (result6 != 0) + return 0; + + if (result7 != 0) + return 0; + + if (result8 != 0) + return 0; + + if (result9 != 0) + return 0; + + if (result10 != -72) + return 0; + + if (result11 != 1) + return 0; + + if (result12 != 1) + return 0; + + if (result13 != 0) + return 0; + + if (result14 != 1) + return 0; + + if (result15 != -32768) + return 0; + + if (result16 != -32768) + return 0; + + if (result17 != 456) + return 0; + + if (result18 != 0) + return 0; + + if (result19 != 0) + return 0; + + if (result20 != 0) + return 0; + + return 100; + } +} diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1.csproj b/src/tests/JIT/opt/Divide/Regressions/Regression1.csproj new file mode 100644 index 00000000000000..f3e1cbd44b4041 --- /dev/null +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + diff --git a/src/tests/JIT/opt/Or/IntOr.cs b/src/tests/JIT/opt/Or/IntOr.cs new file mode 100644 index 00000000000000..7ea7d89b76f2b6 --- /dev/null +++ b/src/tests/JIT/opt/Or/IntOr.cs @@ -0,0 +1,112 @@ +// 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; + +namespace CodeGenTests +{ + class IntOr + { + [MethodImpl(MethodImplOptions.NoInlining)] + static void SideEffect() + { + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Test_UInt32_UInt32_CastByte_Or(uint x, uint y) + { + // X64-NOT: movzx + + // We expect 'or reg8, reg8'. + // X64: or {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} + + if ((byte)((byte)x | y) == 0) + { + SideEffect(); + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static bool Test_UInt32_ByRef_CastByte_CastByte_Or(uint x, ref uint y) + { + // X64-NOT: movzx + + // We expect 'or reg8, mem8'. + // X64: or {{[a-z]+[l|b]}}, byte ptr + + if ((byte)((byte)x | (byte)y) == 0) + { + SideEffect(); + return true; + } + return false; + } + + static int Main() + { + uint leftMostBit = 0b10000000000000000000000000000000; + uint rightMostBit = 0b00000000000000000000000000000001; + uint noBits = 0b00000000000000000000000000000000; + + if (!Test_UInt32_UInt32_CastByte_Or(leftMostBit, leftMostBit)) + return 0; + + if (Test_UInt32_UInt32_CastByte_Or(leftMostBit, rightMostBit)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_Or(leftMostBit, noBits)) + return 0; + + if (Test_UInt32_UInt32_CastByte_Or(rightMostBit, leftMostBit)) + return 0; + + if (Test_UInt32_UInt32_CastByte_Or(rightMostBit, rightMostBit)) + return 0; + + if (Test_UInt32_UInt32_CastByte_Or(rightMostBit, noBits)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_Or(noBits, leftMostBit)) + return 0; + + if (Test_UInt32_UInt32_CastByte_Or(noBits, rightMostBit)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_Or(noBits, noBits)) + return 0; + + // ByRef + if (!Test_UInt32_ByRef_CastByte_CastByte_Or(leftMostBit, ref leftMostBit)) + return 0; + + if (Test_UInt32_ByRef_CastByte_CastByte_Or(leftMostBit, ref rightMostBit)) + return 0; + + if (!Test_UInt32_ByRef_CastByte_CastByte_Or(leftMostBit, ref noBits)) + return 0; + + if (Test_UInt32_ByRef_CastByte_CastByte_Or(rightMostBit, ref leftMostBit)) + return 0; + + if (Test_UInt32_ByRef_CastByte_CastByte_Or(rightMostBit, ref rightMostBit)) + return 0; + + if (Test_UInt32_ByRef_CastByte_CastByte_Or(rightMostBit, ref noBits)) + return 0; + + if (!Test_UInt32_ByRef_CastByte_CastByte_Or(noBits, ref leftMostBit)) + return 0; + + if (Test_UInt32_ByRef_CastByte_CastByte_Or(noBits, ref rightMostBit)) + return 0; + + if (!Test_UInt32_ByRef_CastByte_CastByte_Or(noBits, ref noBits)) + return 0; + + return 100; + } + } +} diff --git a/src/tests/JIT/opt/Or/IntOr.csproj b/src/tests/JIT/opt/Or/IntOr.csproj new file mode 100644 index 00000000000000..42a89c8384d74e --- /dev/null +++ b/src/tests/JIT/opt/Or/IntOr.csproj @@ -0,0 +1,17 @@ + + + Exe + + + None + True + + + + true + + + + + + diff --git a/src/tests/JIT/opt/Regressions/Regression1.cs b/src/tests/JIT/opt/Regressions/Regression1.cs new file mode 100644 index 00000000000000..d6961c7fd8886d --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression1.cs @@ -0,0 +1,72 @@ +// 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; + +public class C0 +{ + public short F0; + public bool F1; + public sbyte F2; + public short F3; + public C0(sbyte f2, short f3) + { + F2 = f2; + F3 = f3; + } +} + +public class Program +{ + public static IRuntime s_rt; + public static int[][] s_13 = new int[][] { new int[] { 0 } }; + + public static int Main() + { + s_rt = new Runtime(); + var result = M74(0); + + if (result != -1) + return 0; + + return 100; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static short M74(short arg1) + { + try + { + M75(); + } + finally + { + C0 var5 = new C0(0, 1); + int var6 = s_13[0][0]; + arg1 = var5.F3; + s_rt.WriteLine(var5.F0); + s_rt.WriteLine(var5.F1); + s_rt.WriteLine(var5.F2); + } + + arg1 = (short)~arg1; + arg1++; + return arg1; + } + + public static sbyte[] M75() + { + return default(sbyte[]); + } +} + +public interface IRuntime +{ + void WriteLine(T value); +} + +public class Runtime : IRuntime +{ + public void WriteLine(T value) { } +} diff --git a/src/tests/JIT/opt/Regressions/Regression1.csproj b/src/tests/JIT/opt/Regressions/Regression1.csproj new file mode 100644 index 00000000000000..c053e59c003e01 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression1.csproj @@ -0,0 +1,15 @@ + + + Exe + + + None + True + + + + + + + + diff --git a/src/tests/JIT/opt/Regressions/Regression2.cs b/src/tests/JIT/opt/Regressions/Regression2.cs new file mode 100644 index 00000000000000..5e8f36a4730ab8 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression2.cs @@ -0,0 +1,77 @@ +// 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; + +public class Program +{ + [MethodImpl(MethodImplOptions.NoInlining)] + static void Add_SmallType_Correctness() + { + for (int i = 0; i < ushort.MaxValue + 1; i++) + { + for (int j = 0; j < ushort.MaxValue + 1; j++) + { + if ((byte)(i + j) != (byte)((byte)i + (byte)j)) + { + throw new Exception(); + } + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Sub_SmallType_Correctness() + { + for (int i = 0; i < ushort.MaxValue + 1; i++) + { + for (int j = 0; j < ushort.MaxValue + 1; j++) + { + if ((byte)(i - j) != (byte)((byte)i - (byte)j)) + { + throw new Exception(); + } + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Mul_SmallType_Correctness() + { + for (int i = 0; i < ushort.MaxValue + 1; i++) + { + for (int j = 0; j < ushort.MaxValue + 1; j++) + { + if ((byte)(i * j) != (byte)((byte)i * (byte)j)) + { + throw new Exception(); + } + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Div_SmallType_Correctness(int i, int j) + { + if ((byte)(i / j) != (byte)((byte)i / (byte)j)) + { + throw new Exception(); + } + } + + static int Main() + { + Add_SmallType_Correctness(); + Sub_SmallType_Correctness(); + Mul_SmallType_Correctness(); + + try + { + Div_SmallType_Correctness(2, 256); + } + catch(DivideByZeroException) {} + + return 100; + } +} diff --git a/src/tests/JIT/opt/Regressions/Regression2.csproj b/src/tests/JIT/opt/Regressions/Regression2.csproj new file mode 100644 index 00000000000000..675d36d50e8db7 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression2.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + None + True + + + + + diff --git a/src/tests/JIT/opt/Regressions/Regression3.cs b/src/tests/JIT/opt/Regressions/Regression3.cs new file mode 100644 index 00000000000000..78b6910c0c8db5 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression3.cs @@ -0,0 +1,71 @@ +// 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; + +public class C1 +{ + public sbyte F1; + public byte F2; +} + +public struct S0 +{ + public ulong F0; +} + +public class Program +{ + public static IRuntime s_rt; + public static bool s_25; + public static short[] s_42; + public static S0[][] s_43 = new S0[][]{new S0[]{new S0()}}; + public static int Main() + { + CollectibleALC alc = new CollectibleALC(); + System.Reflection.Assembly asm = alc.LoadFromAssemblyPath(System.Reflection.Assembly.GetExecutingAssembly().Location); + System.Reflection.MethodInfo mi = asm.GetType(typeof(Program).FullName).GetMethod(nameof(MainInner)); + System.Type runtimeTy = asm.GetType(typeof(Runtime).FullName); + mi.Invoke(null, new object[]{System.Activator.CreateInstance(runtimeTy)}); + return 100; + } + + public static void MainInner(IRuntime rt) + { + s_rt = rt; + var vr3 = new C1(); + C1 vr8 = new C1(); + bool vr10 = vr8.F2 == vr8.F2; + M6(vr3, vr10); + } + + public static void M60(ref sbyte arg1, ref short[] arg2) + { + } + + public static void M6(C1 argThis, bool arg0) + { + arg0 = s_25; + M60(ref argThis.F1, ref s_42); + if (arg0 && arg0) + { + throw new Exception(); + } + } +} + +public interface IRuntime +{ +} + +public class Runtime : IRuntime +{ +} + +public class CollectibleALC : System.Runtime.Loader.AssemblyLoadContext +{ + public CollectibleALC(): base(true) + { + } +} diff --git a/src/tests/JIT/opt/Regressions/Regression3.csproj b/src/tests/JIT/opt/Regressions/Regression3.csproj new file mode 100644 index 00000000000000..675d36d50e8db7 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression3.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + None + True + + + + + diff --git a/src/tests/JIT/opt/Regressions/Regression4.cs b/src/tests/JIT/opt/Regressions/Regression4.cs new file mode 100644 index 00000000000000..b39ff25e82933d --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression4.cs @@ -0,0 +1,71 @@ +// 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; + +public class Program +{ + public static IRuntime s_rt; + public static int s_58; + public static uint s_66; + public static byte[] s_126 = new byte[] { 0 }; + public static int Main() + { + CollectibleALC alc = new CollectibleALC(); + System.Reflection.Assembly asm = alc.LoadFromAssemblyPath(System.Reflection.Assembly.GetExecutingAssembly().Location); + System.Reflection.MethodInfo mi = asm.GetType(typeof(Program).FullName).GetMethod(nameof(MainInner)); + System.Type runtimeTy = asm.GetType(typeof(Runtime).FullName); + int count = (int)mi.Invoke(null, new object[] { System.Activator.CreateInstance(runtimeTy) }); + + if (count != 2) + return 0; + + return 100; + } + + public static int MainInner(IRuntime rt) + { + s_rt = rt; + var vr3 = s_126[0]; + M59(vr3, false, ref s_66); + + return s_rt.Count; + } + + public static void M59(byte arg2, bool arg3, ref uint arg4) + { + for (int var0 = 0; var0 < 2; var0++) + { + byte var3 = arg2; + arg3 = 1 <= s_58; + s_rt.WriteLine("c_480", var3); + if ((arg3 || arg3)) + { + short vr12 = default(short); + arg4 = (uint)vr12; + int vr13 = default(int); + s_rt.WriteLine("c_439", vr13); + } + } + } +} + +public interface IRuntime +{ + int Count { get; } + void WriteLine(string site, T value); +} + +public class Runtime : IRuntime +{ + public int Count { get; set; } + public void WriteLine(string site, T value) => Count++; +} + +public class CollectibleALC : System.Runtime.Loader.AssemblyLoadContext +{ + public CollectibleALC() : base(true) + { + } +} diff --git a/src/tests/JIT/opt/Regressions/Regression4.csproj b/src/tests/JIT/opt/Regressions/Regression4.csproj new file mode 100644 index 00000000000000..675d36d50e8db7 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression4.csproj @@ -0,0 +1,13 @@ + + + Exe + true + + + None + True + + + + + diff --git a/src/tests/JIT/opt/Regressions/Regression5.cs b/src/tests/JIT/opt/Regressions/Regression5.cs new file mode 100644 index 00000000000000..346b2503ef34e2 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression5.cs @@ -0,0 +1,57 @@ +// 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; + +public class Program +{ + public static IRuntime s_rt; + public static bool[,] s_32 = new bool[,] { { false } }; + public static sbyte[] s_53 = new sbyte[] { 0 }; + public static ushort s_62; + public static int Main() + { + s_rt = new Runtime(); + var vr9 = s_32[0, 0]; + var vr10 = M76(ref s_62, vr9); + + if (s_53[0] != 0) + return 0; + + return 100; + } + + public static ushort M76(ref ushort arg0, bool arg1) + { + byte var17 = default(byte); + if (!arg1) + { + s_rt.WriteLine(0); + bool[] var14 = new bool[] { false }; + arg1 = var14[0]; + s_rt.WriteLine(var14[0]); + s_rt.WriteLine(var17); + if (arg1 && arg1) + { + s_53[0] += 1; + } + + bool[][] var18 = new bool[][] { new bool[] { true }, new bool[] { true }, new bool[] { true } }; + } + + return arg0; + } +} + +public interface IRuntime +{ + void WriteLine(T value); +} + +public class Runtime : IRuntime +{ + public void WriteLine(T value) + { + } +} diff --git a/src/tests/JIT/opt/Regressions/Regression5.csproj b/src/tests/JIT/opt/Regressions/Regression5.csproj new file mode 100644 index 00000000000000..f3e1cbd44b4041 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression5.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + diff --git a/src/tests/JIT/opt/Subtract/IntSubtract.cs b/src/tests/JIT/opt/Subtract/IntSubtract.cs new file mode 100644 index 00000000000000..fd9e8042beda15 --- /dev/null +++ b/src/tests/JIT/opt/Subtract/IntSubtract.cs @@ -0,0 +1,166 @@ +// 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; + +namespace CodeGenTests +{ + static class IntSubtract + { + [MethodImpl(MethodImplOptions.NoInlining)] + static sbyte Int8_Subtract(sbyte x, sbyte y) + { + // X64-NOT: movsx + + // X64: sub + // X64-NEXT: movsx + + return (sbyte)(x - y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte UInt8_Subtract(byte x, byte y) + { + // X64-NOT: movzx + + // X64: sub + // X64-NEXT: movzx + + return (byte)(x - y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static short Int16_Subtract(short x, short y) + { + // X64-NOT: movsx + + // X64: sub + // X64-NEXT: movsx + + // X64-NOT: cwde + + return (short)(x - y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort UInt16_Subtract(ushort x, ushort y) + { + // X64-NOT: movzx + + // X64: sub + // X64-NEXT: movzx + + return (ushort)(x - y); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static int Int32_Subtract(int x, int y) + { + // X64-NOT: movsx + + // X64: sub + + // X64-NOT: movsx + + return x - y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint UInt32_Subtract(uint x, uint y) + { + // X64-NOT: movzx + + // X64: sub + + // X64-NOT: movzx + + return x - y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long Int64_Subtract(long x, long y) + { + // X64-NOT: movsx + + // X64: sub + + // X64-NOT: movsx + + return x - y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong UInt64_Subtract(ulong x, ulong y) + { + // X64-NOT: movzx + + // X64: sub + + // X64-NOT: movzx + + return x - y; + } + + static int Main() + { + // Int8 + if (Int8_Subtract(SByte.MinValue, 15) != 113) + return 0; + + if (Int8_Subtract(15, SByte.MaxValue) != -112) + return 0; + + // UInt8 + if (UInt8_Subtract(Byte.MinValue, 15) != 241) + return 0; + + if (UInt8_Subtract(15, Byte.MaxValue) != 16) + return 0; + + // Int16 + if (Int16_Subtract(Int16.MinValue, 15) != 32753) + return 0; + + if (Int16_Subtract(15, Int16.MaxValue) != -32752) + return 0; + + // UInt16 + if (UInt16_Subtract(UInt16.MinValue, 15) != 65521) + return 0; + + if (UInt16_Subtract(15, UInt16.MaxValue) != 16) + return 0; + + // Int32 + if (Int32_Subtract(Int32.MinValue, 15) != 2147483633) + return 0; + + if (Int32_Subtract(15, Int32.MaxValue) != -2147483632) + return 0; + + // UInt32 + if (UInt32_Subtract(UInt32.MinValue, 15) != 4294967281) + return 0; + + if (UInt32_Subtract(15, UInt32.MaxValue) != 16) + return 0; + + // Int64 + if (Int64_Subtract(Int64.MinValue, 15) != 9223372036854775793) + return 0; + + if (Int64_Subtract(15, Int64.MaxValue) != -9223372036854775792) + return 0; + + // UInt64 + if (UInt64_Subtract(UInt64.MinValue, 15) != 18446744073709551601) + return 0; + + if (UInt64_Subtract(15, UInt64.MaxValue) != 16) + return 0; + + return 100; + } + } +} diff --git a/src/tests/JIT/opt/Subtract/IntSubtract.csproj b/src/tests/JIT/opt/Subtract/IntSubtract.csproj new file mode 100644 index 00000000000000..42a89c8384d74e --- /dev/null +++ b/src/tests/JIT/opt/Subtract/IntSubtract.csproj @@ -0,0 +1,17 @@ + + + Exe + + + None + True + + + + true + + + + + +