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
+
+
+
+
+
+