diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 13c79a059ebe6..05a13331dd9ea 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -13589,6 +13589,30 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) return icon; }; + auto NewZeroExtendNode = [&](var_types type, GenTree* op1, var_types castToType) -> GenTree* { + assert(varTypeIsIntegral(type)); + assert(!varTypeIsSmall(type)); + assert(!varTypeIsUnsigned(type)); + assert(varTypeIsUnsigned(castToType)); + + GenTreeCast* cast = gtNewCastNode(TYP_INT, op1, false, castToType); + if (fgGlobalMorph) + { + fgMorphTreeDone(cast); + } + + if (type == TYP_LONG) + { + cast = gtNewCastNode(TYP_LONG, cast, true, TYP_LONG); + if (fgGlobalMorph) + { + fgMorphTreeDone(cast); + } + } + + return cast; + }; + // Here `op` is the non-constant operand, `cons` is the constant operand // and `val` is the constant value. @@ -13745,6 +13769,21 @@ GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) goto DONE_FOLD; } } + else if (val == 0xFF) + { + op = NewZeroExtendNode(tree->TypeGet(), op, TYP_UBYTE); + goto DONE_FOLD; + } + else if (val == 0xFFFF) + { + op = NewZeroExtendNode(tree->TypeGet(), op, TYP_USHORT); + goto DONE_FOLD; + } + else if ((val == 0xFFFFFFFF) && varTypeIsLong(tree)) + { + op = NewZeroExtendNode(tree->TypeGet(), op, TYP_UINT); + goto DONE_FOLD; + } else { /* The GTF_BOOLEAN flag is set for nodes that are part diff --git a/src/tests/JIT/opt/Remainder/IntRemainder.cs b/src/tests/JIT/opt/Remainder/IntRemainder.cs index 0783fae3067f8..9112decb62a5c 100644 --- a/src/tests/JIT/opt/Remainder/IntRemainder.cs +++ b/src/tests/JIT/opt/Remainder/IntRemainder.cs @@ -9,6 +9,7 @@ namespace CodeGenTests static class IntRemainder { static int _fieldValue = 123; + static uint _fieldValueUnsigned = 123; [MethodImpl(MethodImplOptions.NoInlining)] static int Int32_RemainderByOne() @@ -32,6 +33,56 @@ static int Int32_RemainderByOneWithValue(int value) return value % 1; } + [MethodImpl(MethodImplOptions.NoInlining)] + static byte Byte_RemainderByMaxValuePlusOne(uint value) + { + // X64-NOT: and {{[a-z]+}} + + // X64: movzx {{[a-z]+}}, {{[a-z]+}} + + return (byte)(value % (Byte.MaxValue + 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ushort UInt16_RemainderByMaxValuePlusOne(uint value) + { + // X64-NOT: and {{[a-z]+}} + + // X64: movzx {{[a-z]+}}, {{[a-z]+}} + + return (ushort)(value % (UInt16.MaxValue + 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint Byte_RemainderByMaxValuePlusOne_Return_UInt32(uint value) + { + // X64-NOT: and {{[a-z]+}} + + // X64: movzx {{[a-z]+}}, {{[a-z]+}} + + return (value % (Byte.MaxValue + 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint UInt16_RemainderByMaxValuePlusOne_Return_UInt32(uint value) + { + // X64-NOT: and {{[a-z]+}} + + // X64: movzx {{[a-z]+}}, {{[a-z]+}} + + return (value % (UInt16.MaxValue + 1)); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static byte Byte_RemainderByMaxValuePlusOne_WithField() + { + // X64-NOT: and {{[a-z]+}} + + // X64: movzx {{[a-z]+}}, {{[a-z]+}} + + return (byte)(_fieldValueUnsigned % (Byte.MaxValue + 1)); + } + static int Main() { if (Int32_RemainderByOne() != 0) @@ -40,6 +91,21 @@ static int Main() if (Int32_RemainderByOneWithValue(-123) != 0) return 0; + if (Byte_RemainderByMaxValuePlusOne(68000) != 160) + return 0; + + if (UInt16_RemainderByMaxValuePlusOne(68000) != 2464) + return 0; + + if (Byte_RemainderByMaxValuePlusOne_Return_UInt32(68000) != 160) + return 0; + + if (UInt16_RemainderByMaxValuePlusOne_Return_UInt32(68000) != 2464) + return 0; + + if (Byte_RemainderByMaxValuePlusOne_WithField() != 123) + return 0; + return 100; } }