From f6bb7d45107dda672c54c57bc8de30b7f3ec1282 Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 3 Nov 2022 19:37:44 -0700 Subject: [PATCH 01/58] Trying to optimize Add --- src/coreclr/jit/lower.cpp | 2 +- src/coreclr/jit/lower.h | 2 +- src/coreclr/jit/lowerarmarch.cpp | 4 +++- src/coreclr/jit/lowerxarch.cpp | 27 ++++++++++++++++++++++++++- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 4a20bac071333c..73370dfedc875c 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -257,7 +257,7 @@ GenTree* Lowering::LowerNode(GenTree* node) break; case GT_CAST: - LowerCast(node); + return LowerCast(node); break; #if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 30b12a38729e69..9a14ec96b79115 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -330,7 +330,7 @@ class Lowering final : public Phase bool TryLowerSwitchToBitTest( BasicBlock* jumpTable[], unsigned jumpCount, unsigned targetCount, BasicBlock* bbSwitch, GenTree* switchValue); - void LowerCast(GenTree* node); + GenTree* LowerCast(GenTree* node); #if !CPU_LOAD_STORE_ARCH bool IsRMWIndirCandidate(GenTree* operand, GenTree* storeInd); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 1401de10b4705b..b12bd83802688f 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -750,7 +750,7 @@ void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode) // don't expect to see them here. // i) GT_CAST(float/double, int type with overflow detection) // -void Lowering::LowerCast(GenTree* tree) +GenTree* Lowering::LowerCast(GenTree* tree) { assert(tree->OperGet() == GT_CAST); @@ -773,6 +773,8 @@ void Lowering::LowerCast(GenTree* tree) // Now determine if we have operands that should be contained. ContainCheckCast(tree->AsCast()); + + return tree->gtNext; } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 72d9720c5ec3dd..9219ad3214c049 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -770,7 +770,8 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) * system.windows.forms, scimark, fractals, bio mums). If we ever find evidence that * doing this optimization is a win, should consider generating in-lined code. */ -void Lowering::LowerCast(GenTree* tree) + +GenTree* Lowering::LowerCast(GenTree* tree) { assert(tree->OperGet() == GT_CAST); @@ -779,6 +780,28 @@ void Lowering::LowerCast(GenTree* tree) var_types srcType = castOp->TypeGet(); var_types tmpType = TYP_UNDEF; +#if TARGET_AMD64 + if (castOp->OperIs(GT_ADD) && castOp->gtGetOp1()->OperIs(GT_CAST) && castOp->gtGetOp2()->OperIs(GT_CAST)) + { + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); + + if (castToType == op1->CastToType() && castToType == op2->CastToType()) + { + op1->CastOp()->ClearRegOptional(); + op2->CastOp()->ClearRegOptional(); + + castOp->AsOp()->gtOp1 = op1->CastOp(); + castOp->AsOp()->gtOp2 = op2->CastOp(); + + BlockRange().Remove(op2); + BlockRange().Remove(op1); + + return castOp->gtNext; + } + } +#endif // TARGET_AMD64 + // force the srcType to unsigned if GT_UNSIGNED flag is set if (tree->gtFlags & GTF_UNSIGNED) { @@ -835,6 +858,8 @@ void Lowering::LowerCast(GenTree* tree) // Now determine if we have operands that should be contained. ContainCheckCast(tree->AsCast()); + + return tree->gtNext; } #ifdef FEATURE_HW_INTRINSICS From 81a1bcb810aec5e6f5a32e30ae78f1d7b5aef25e Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 3 Nov 2022 19:50:57 -0700 Subject: [PATCH 02/58] Trying to optimize Add --- src/coreclr/jit/lowerxarch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 9219ad3214c049..3c0aadc06eb648 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -797,7 +797,7 @@ GenTree* Lowering::LowerCast(GenTree* tree) BlockRange().Remove(op2); BlockRange().Remove(op1); - return castOp->gtNext; + return LowerNode(castOp); } } #endif // TARGET_AMD64 From 1b29b475103d63f2e66aa82ecc3c1446b894a4b9 Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 3 Nov 2022 20:03:35 -0700 Subject: [PATCH 03/58] Trying to optimize Add --- src/coreclr/jit/lowerxarch.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 3c0aadc06eb648..20a5e9a0d6b905 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -781,7 +781,8 @@ GenTree* Lowering::LowerCast(GenTree* tree) var_types tmpType = TYP_UNDEF; #if TARGET_AMD64 - if (castOp->OperIs(GT_ADD) && castOp->gtGetOp1()->OperIs(GT_CAST) && castOp->gtGetOp2()->OperIs(GT_CAST)) + if (castOp->OperIs(GT_ADD) && varTypeIsIntegral(castToType) && varTypeIsIntegral(srcType) && + castOp->gtGetOp1()->OperIs(GT_CAST) && castOp->gtGetOp2()->OperIs(GT_CAST)) { GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); From 5e3741c95f67e57a542887e32f73ecceca324ec6 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 4 Nov 2022 17:27:31 -0700 Subject: [PATCH 04/58] Added IntAdd disasm tests. Expanded cast removals to all arithmetic and neg/not. --- src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/lir.h | 6 ++ src/coreclr/jit/lower.h | 1 + src/coreclr/jit/lowerxarch.cpp | 101 +++++++++++++++--- src/tests/JIT/opt/Add/IntAdd.cs | 152 ++++++++++++++++++++++++++++ src/tests/JIT/opt/Add/IntAdd.csproj | 17 ++++ 6 files changed, 262 insertions(+), 16 deletions(-) create mode 100644 src/tests/JIT/opt/Add/IntAdd.cs create mode 100644 src/tests/JIT/opt/Add/IntAdd.csproj diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 90daf0f20dc0f4..7ff5b09c4f96ad 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1114,6 +1114,7 @@ struct GenTree inline bool IsRegOptional() const; inline void SetRegOptional(); inline void ClearRegOptional(); + inline void ClearContainedAndRegOptional(); #ifdef DEBUG void dumpLIRFlags(); #endif diff --git a/src/coreclr/jit/lir.h b/src/coreclr/jit/lir.h index e589c3e8f953b5..52073558ea1503 100644 --- a/src/coreclr/jit/lir.h +++ b/src/coreclr/jit/lir.h @@ -337,4 +337,10 @@ inline bool GenTree::IsRegOptional() const return (gtLIRFlags & LIR::Flags::RegOptional) != 0; } +inline void GenTree::ClearContainedAndRegOptional() +{ + ClearContained(); + ClearRegOptional(); +} + #endif // _LIR_H_ diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 9a14ec96b79115..890fd1d16946b5 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -320,6 +320,7 @@ class Lowering final : public Phase #ifdef TARGET_XARCH void LowerPutArgStk(GenTreePutArgStk* putArgStk); GenTree* TryLowerMulToLshSubOrLshAdd(GenTreeOp* node); + GenTree* TryLowerCastOfSimpleOp(GenTreeCast* node); #endif // TARGET_XARCH bool TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* parent); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 20a5e9a0d6b905..fb681dc0decbff 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -735,6 +735,87 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) #endif // TARGET_X86 } +//---------------------------------------------------------------------------------------------- +// Lowering::TryLowerCastOfSimpleOp: +// +// Arguments: +// node - A simple op node +// +// Return Value: +// Returns the replacement node if one is created else nullptr indicating no replacement +// +// Notes: +// Performs containment checks on the replacement node if one is created +GenTree* Lowering::TryLowerCastOfSimpleOp(GenTreeCast* node) +{ + GenTree* castOp = node->CastOp(); + var_types castToType = node->CastToType(); + var_types srcType = castOp->TypeGet(); + + assert(castOp->OperIsSimple()); + + if (castOp->OperMayOverflow() && castOp->gtOverflow()) + return nullptr; + + if (castOp->gtSetFlags()) + return nullptr; + + if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) + return nullptr; + + if (castOp->OperIsArithmetic()) + { + if (!castOp->gtGetOp1()->OperIs(GT_CAST) || !castOp->gtGetOp2()->OperIs(GT_CAST)) + return nullptr; + + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); + + if (castToType == op1->CastToType() && castToType == op2->CastToType()) + { + // This removes the casts as it prevents zero/sign-extended 'mov's before the simple op. + + op1->CastOp()->ClearContainedAndRegOptional(); + op2->CastOp()->ClearContainedAndRegOptional(); + + castOp->AsOp()->gtOp1 = op1->CastOp(); + castOp->AsOp()->gtOp2 = op2->CastOp(); + + BlockRange().Remove(op2); + BlockRange().Remove(op1); + + ContainCheckBinary(castOp->AsOp()); + ContainCheckCast(node); + + return node; + } + } + else if (castOp->OperIs(GT_NEG, GT_NOT)) + { + if (!castOp->gtGetOp1()->OperIs(GT_CAST)) + return nullptr; + + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + + if (castToType == op1->CastToType()) + { + // This removes the casts as it prevents zero/sign-extended 'mov's before the simple op. + + op1->CastOp()->ClearContainedAndRegOptional(); + + castOp->AsOp()->gtOp1 = op1->CastOp(); + + BlockRange().Remove(op1); + + ContainCheckCast(node); + + return node; + } + } + + return nullptr; +} + /* Lower GT_CAST(srcType, DstType) nodes. * * Casts from small int type to float/double are transformed as follows: @@ -781,24 +862,12 @@ GenTree* Lowering::LowerCast(GenTree* tree) var_types tmpType = TYP_UNDEF; #if TARGET_AMD64 - if (castOp->OperIs(GT_ADD) && varTypeIsIntegral(castToType) && varTypeIsIntegral(srcType) && - castOp->gtGetOp1()->OperIs(GT_CAST) && castOp->gtGetOp2()->OperIs(GT_CAST)) + if (castOp->OperIsSimple()) { - GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - - if (castToType == op1->CastToType() && castToType == op2->CastToType()) + GenTree* replacementNode = TryLowerCastOfSimpleOp(tree->AsCast()); + if (replacementNode != nullptr) { - op1->CastOp()->ClearRegOptional(); - op2->CastOp()->ClearRegOptional(); - - castOp->AsOp()->gtOp1 = op1->CastOp(); - castOp->AsOp()->gtOp2 = op2->CastOp(); - - BlockRange().Remove(op2); - BlockRange().Remove(op1); - - return LowerNode(castOp); + return replacementNode->gtNext; } } #endif // TARGET_AMD64 diff --git a/src/tests/JIT/opt/Add/IntAdd.cs b/src/tests/JIT/opt/Add/IntAdd.cs new file mode 100644 index 00000000000000..8da84b9cceb615 --- /dev/null +++ b/src/tests/JIT/opt/Add/IntAdd.cs @@ -0,0 +1,152 @@ +// 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; + + Console.Write("Succeeded"); + + 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 + + + + + + From 2a430215b58cc1590905ecf9f4b153dc871162a2 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 4 Nov 2022 22:46:06 -0700 Subject: [PATCH 05/58] Checked unsigned --- src/coreclr/jit/lowerxarch.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index fb681dc0decbff..99009a72130276 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -752,6 +752,12 @@ GenTree* Lowering::TryLowerCastOfSimpleOp(GenTreeCast* node) var_types castToType = node->CastToType(); var_types srcType = castOp->TypeGet(); + // force the srcType to unsigned if GT_UNSIGNED flag is set + if (node->gtFlags & GTF_UNSIGNED) + { + srcType = varTypeToUnsigned(srcType); + } + assert(castOp->OperIsSimple()); if (castOp->OperMayOverflow() && castOp->gtOverflow()) @@ -763,6 +769,9 @@ GenTree* Lowering::TryLowerCastOfSimpleOp(GenTreeCast* node) if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return nullptr; + if (varTypeIsUnsigned(castToType) && !varTypeIsUnsigned(srcType)) + return nullptr; + if (castOp->OperIsArithmetic()) { if (!castOp->gtGetOp1()->OperIs(GT_CAST) || !castOp->gtGetOp2()->OperIs(GT_CAST)) From 5428df9242b2f58902930aa72d2f69500681e3d1 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 7 Nov 2022 10:36:31 -0800 Subject: [PATCH 06/58] Limit arithmetic opt --- src/coreclr/jit/lowerxarch.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 99009a72130276..d13e515192aaba 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -769,10 +769,7 @@ GenTree* Lowering::TryLowerCastOfSimpleOp(GenTreeCast* node) if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return nullptr; - if (varTypeIsUnsigned(castToType) && !varTypeIsUnsigned(srcType)) - return nullptr; - - if (castOp->OperIsArithmetic()) + if (castOp->OperIs(GT_ADD, GT_SUB)) { if (!castOp->gtGetOp1()->OperIs(GT_CAST) || !castOp->gtGetOp2()->OperIs(GT_CAST)) return nullptr; From 6d85c1a5997932c1629c79887d57d7cef63e106f Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 8 Nov 2022 13:54:58 -0800 Subject: [PATCH 07/58] Moving optimization to Morph --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/lower.h | 1 - src/coreclr/jit/lowerxarch.cpp | 98 ---------------------------------- src/coreclr/jit/morph.cpp | 68 +++++++++++++++++++++++ 4 files changed, 69 insertions(+), 99 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index a5c7c3f2522b1e..7453ff7dfb7971 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5782,6 +5782,7 @@ class Compiler GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optAssertionPropDone = nullptr); void fgTryReplaceStructLocalWithField(GenTree* tree); GenTree* fgOptimizeCast(GenTreeCast* cast); + void fgOptimizeCastOfSmpOp(GenTreeCast* cast); GenTree* fgOptimizeCastOnAssignment(GenTreeOp* asg); GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp); GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp); diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index db2595369b0b54..29d714c733353e 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -322,7 +322,6 @@ class Lowering final : public Phase #ifdef TARGET_XARCH void LowerPutArgStk(GenTreePutArgStk* putArgStk); GenTree* TryLowerMulWithConstant(GenTreeOp* node); - GenTree* TryLowerCastOfSimpleOp(GenTreeCast* node); #endif // TARGET_XARCH bool TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* parent); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index c440a5b3526248..de9b97cc5510ed 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -745,93 +745,6 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) #endif // TARGET_X86 } -//---------------------------------------------------------------------------------------------- -// Lowering::TryLowerCastOfSimpleOp: -// -// Arguments: -// node - A simple op node -// -// Return Value: -// Returns the replacement node if one is created else nullptr indicating no replacement -// -// Notes: -// Performs containment checks on the replacement node if one is created -GenTree* Lowering::TryLowerCastOfSimpleOp(GenTreeCast* node) -{ - GenTree* castOp = node->CastOp(); - var_types castToType = node->CastToType(); - var_types srcType = castOp->TypeGet(); - - // force the srcType to unsigned if GT_UNSIGNED flag is set - if (node->gtFlags & GTF_UNSIGNED) - { - srcType = varTypeToUnsigned(srcType); - } - - assert(castOp->OperIsSimple()); - - if (castOp->OperMayOverflow() && castOp->gtOverflow()) - return nullptr; - - if (castOp->gtSetFlags()) - return nullptr; - - if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) - return nullptr; - - if (castOp->OperIs(GT_ADD, GT_SUB)) - { - if (!castOp->gtGetOp1()->OperIs(GT_CAST) || !castOp->gtGetOp2()->OperIs(GT_CAST)) - return nullptr; - - GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - - if (castToType == op1->CastToType() && castToType == op2->CastToType()) - { - // This removes the casts as it prevents zero/sign-extended 'mov's before the simple op. - - op1->CastOp()->ClearContainedAndRegOptional(); - op2->CastOp()->ClearContainedAndRegOptional(); - - castOp->AsOp()->gtOp1 = op1->CastOp(); - castOp->AsOp()->gtOp2 = op2->CastOp(); - - BlockRange().Remove(op2); - BlockRange().Remove(op1); - - ContainCheckBinary(castOp->AsOp()); - ContainCheckCast(node); - - return node; - } - } - else if (castOp->OperIs(GT_NEG, GT_NOT)) - { - if (!castOp->gtGetOp1()->OperIs(GT_CAST)) - return nullptr; - - GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - - if (castToType == op1->CastToType()) - { - // This removes the casts as it prevents zero/sign-extended 'mov's before the simple op. - - op1->CastOp()->ClearContainedAndRegOptional(); - - castOp->AsOp()->gtOp1 = op1->CastOp(); - - BlockRange().Remove(op1); - - ContainCheckCast(node); - - return node; - } - } - - return nullptr; -} - /* Lower GT_CAST(srcType, DstType) nodes. * * Casts from small int type to float/double are transformed as follows: @@ -877,17 +790,6 @@ GenTree* Lowering::LowerCast(GenTree* tree) var_types srcType = castOp->TypeGet(); var_types tmpType = TYP_UNDEF; -#if TARGET_AMD64 - if (castOp->OperIsSimple()) - { - GenTree* replacementNode = TryLowerCastOfSimpleOp(tree->AsCast()); - if (replacementNode != nullptr) - { - return replacementNode->gtNext; - } - } -#endif // TARGET_AMD64 - // force the srcType to unsigned if GT_UNSIGNED flag is set if (tree->gtFlags & GTF_UNSIGNED) { diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 34c15bb737b2bc..2dc46bd5467732 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11414,6 +11414,11 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } + if (src->OperIsSimple()) + { + fgOptimizeCastOfSmpOp(cast); + } + // See if we can discard the cast. if (varTypeIsIntegral(cast) && varTypeIsIntegral(src)) { @@ -11495,6 +11500,69 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } +//------------------------------------------------------------------------ +// fgOptimizeCastOfSmpOp: Optimizes the supplied GT_CAST tree of a GTK_SMPOP. +// +// Arguments: +// tree - the cast tree to optimize +// +void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) +{ + GenTree* src = cast->CastOp(); + + assert(src->OperIsSimple()); + + if (gtIsActiveCSE_Candidate(cast) || gtIsActiveCSE_Candidate(src)) + return; + + var_types castToType = cast->CastToType(); + var_types srcType = src->TypeGet(); + + if (src->OperMayOverflow() && src->gtOverflow()) + return; + + if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) + return; + + if (src->OperIs(GT_ADD, GT_SUB)) + { + if (!src->gtGetOp1()->OperIs(GT_CAST) || !src->gtGetOp2()->OperIs(GT_CAST)) + return; + + GenTreeCast* op1 = src->gtGetOp1()->AsCast(); + GenTreeCast* op2 = src->gtGetOp2()->AsCast(); + + if (gtIsActiveCSE_Candidate(op1) || gtIsActiveCSE_Candidate(op2)) + return; + + if (castToType == op1->CastToType() && castToType == op2->CastToType()) + { + src->AsOp()->gtOp1 = op1->CastOp(); + src->AsOp()->gtOp2 = op2->CastOp(); + + DEBUG_DESTROY_NODE(op1); + DEBUG_DESTROY_NODE(op2); + } + } + else if (src->OperIs(GT_NEG, GT_NOT)) + { + if (!src->gtGetOp1()->OperIs(GT_CAST)) + return; + + GenTreeCast* op1 = src->gtGetOp1()->AsCast(); + + if (gtIsActiveCSE_Candidate(op1)) + return; + + if (castToType == op1->CastToType()) + { + src->AsOp()->gtOp1 = op1->CastOp(); + + DEBUG_DESTROY_NODE(op1); + } + } +} + //------------------------------------------------------------------------ // fgOptimizeCastOnAssignment: Optimizes the supplied GT_ASG tree with a GT_CAST node. // From 02dbb22ab6237a8e65ae3e215cb1396fdc7faf87 Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 8 Nov 2022 15:05:06 -0800 Subject: [PATCH 08/58] Cleanup. Allow more opts. --- src/coreclr/jit/gentree.h | 1 - src/coreclr/jit/lir.h | 6 ---- src/coreclr/jit/lower.cpp | 2 +- src/coreclr/jit/lower.h | 2 +- src/coreclr/jit/lowerxarch.cpp | 5 +--- src/coreclr/jit/morph.cpp | 51 +++++++++++++++------------------ src/tests/JIT/opt/Add/IntAdd.cs | 2 -- 7 files changed, 26 insertions(+), 43 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 5a575e2d9b6723..aa9a9c8d04516f 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1114,7 +1114,6 @@ struct GenTree inline bool IsRegOptional() const; inline void SetRegOptional(); inline void ClearRegOptional(); - inline void ClearContainedAndRegOptional(); #ifdef DEBUG void dumpLIRFlags(); #endif diff --git a/src/coreclr/jit/lir.h b/src/coreclr/jit/lir.h index 52073558ea1503..e589c3e8f953b5 100644 --- a/src/coreclr/jit/lir.h +++ b/src/coreclr/jit/lir.h @@ -337,10 +337,4 @@ inline bool GenTree::IsRegOptional() const return (gtLIRFlags & LIR::Flags::RegOptional) != 0; } -inline void GenTree::ClearContainedAndRegOptional() -{ - ClearContained(); - ClearRegOptional(); -} - #endif // _LIR_H_ diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index d8471d1aa56242..6b1ec741211eb0 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -312,7 +312,7 @@ GenTree* Lowering::LowerNode(GenTree* node) break; case GT_CAST: - return LowerCast(node); + LowerCast(node); break; #if defined(TARGET_XARCH) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 29d714c733353e..f7a3da7771f912 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -332,7 +332,7 @@ class Lowering final : public Phase bool TryLowerSwitchToBitTest( BasicBlock* jumpTable[], unsigned jumpCount, unsigned targetCount, BasicBlock* bbSwitch, GenTree* switchValue); - GenTree* LowerCast(GenTree* node); + void LowerCast(GenTree* node); #if !CPU_LOAD_STORE_ARCH bool IsRMWIndirCandidate(GenTree* operand, GenTree* storeInd); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index de9b97cc5510ed..44d6781d081fc9 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -780,8 +780,7 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) * system.windows.forms, scimark, fractals, bio mums). If we ever find evidence that * doing this optimization is a win, should consider generating in-lined code. */ - -GenTree* Lowering::LowerCast(GenTree* tree) +void Lowering::LowerCast(GenTree* tree) { assert(tree->OperGet() == GT_CAST); @@ -846,8 +845,6 @@ GenTree* Lowering::LowerCast(GenTree* tree) // Now determine if we have operands that should be contained. ContainCheckCast(tree->AsCast()); - - return tree->gtNext; } #ifdef FEATURE_HW_INTRINSICS diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2dc46bd5467732..80c38598c97088 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11524,41 +11524,36 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return; - if (src->OperIs(GT_ADD, GT_SUB)) + if (src->OperIs(GT_ADD, GT_SUB, GT_NEG, GT_NOT)) { - if (!src->gtGetOp1()->OperIs(GT_CAST) || !src->gtGetOp2()->OperIs(GT_CAST)) - return; - - GenTreeCast* op1 = src->gtGetOp1()->AsCast(); - GenTreeCast* op2 = src->gtGetOp2()->AsCast(); - - if (gtIsActiveCSE_Candidate(op1) || gtIsActiveCSE_Candidate(op2)) - return; - - if (castToType == op1->CastToType() && castToType == op2->CastToType()) + if (src->gtGetOp1()->OperIs(GT_CAST)) { - src->AsOp()->gtOp1 = op1->CastOp(); - src->AsOp()->gtOp2 = op2->CastOp(); + GenTreeCast* op1 = src->gtGetOp1()->AsCast(); - DEBUG_DESTROY_NODE(op1); - DEBUG_DESTROY_NODE(op2); - } - } - else if (src->OperIs(GT_NEG, GT_NOT)) - { - if (!src->gtGetOp1()->OperIs(GT_CAST)) - return; - - GenTreeCast* op1 = src->gtGetOp1()->AsCast(); + if (!gtIsActiveCSE_Candidate(op1)) + { + if (castToType == op1->CastToType()) + { + src->AsOp()->gtOp1 = op1->CastOp(); - if (gtIsActiveCSE_Candidate(op1)) - return; + DEBUG_DESTROY_NODE(op1); + } + } + } - if (castToType == op1->CastToType()) + if (src->OperIsBinary() && src->gtGetOp2()->OperIs(GT_CAST)) { - src->AsOp()->gtOp1 = op1->CastOp(); + GenTreeCast* op2 = src->gtGetOp2()->AsCast(); - DEBUG_DESTROY_NODE(op1); + if (!gtIsActiveCSE_Candidate(op2)) + { + if (castToType == op2->CastToType()) + { + src->AsOp()->gtOp2 = op2->CastOp(); + + DEBUG_DESTROY_NODE(op2); + } + } } } } diff --git a/src/tests/JIT/opt/Add/IntAdd.cs b/src/tests/JIT/opt/Add/IntAdd.cs index 8da84b9cceb615..158e54a1e22904 100644 --- a/src/tests/JIT/opt/Add/IntAdd.cs +++ b/src/tests/JIT/opt/Add/IntAdd.cs @@ -144,8 +144,6 @@ static int Main() if (UInt64_Add(15, UInt64.MaxValue) != 14) return 0; - Console.Write("Succeeded"); - return 100; } } From 779dd21883560927f3e30b7c15e678f89b9ebfaf Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 8 Nov 2022 15:06:14 -0800 Subject: [PATCH 09/58] Fix build --- src/coreclr/jit/lowerarmarch.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index b12bd83802688f..1401de10b4705b 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -750,7 +750,7 @@ void Lowering::LowerPutArgStkOrSplit(GenTreePutArgStk* putArgNode) // don't expect to see them here. // i) GT_CAST(float/double, int type with overflow detection) // -GenTree* Lowering::LowerCast(GenTree* tree) +void Lowering::LowerCast(GenTree* tree) { assert(tree->OperGet() == GT_CAST); @@ -773,8 +773,6 @@ GenTree* Lowering::LowerCast(GenTree* tree) // Now determine if we have operands that should be contained. ContainCheckCast(tree->AsCast()); - - return tree->gtNext; } //------------------------------------------------------------------------ From a263faad53f064afa240adfbc9d43703c921b6c9 Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 8 Nov 2022 15:41:36 -0800 Subject: [PATCH 10/58] More opts --- src/coreclr/jit/morph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 80c38598c97088..fe0b0df011aa08 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11524,7 +11524,7 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return; - if (src->OperIs(GT_ADD, GT_SUB, GT_NEG, GT_NOT)) + if (src->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_UDIV, GT_DIV, GT_MOD, GT_UMOD, GT_NEG, GT_NOT)) { if (src->gtGetOp1()->OperIs(GT_CAST)) { From f7c8b68164fcb8692932525fd6b248588174c897 Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 8 Nov 2022 18:47:28 -0800 Subject: [PATCH 11/58] Added regression test. Allowing all arithmetic ops. Only do transformation if the op is a local --- src/coreclr/jit/morph.cpp | 34 +++-- .../Regressions/Regression1/Regression1.cs | 136 ++++++++++++++++++ .../Regression1/Regression1.csproj | 12 ++ 3 files changed, 169 insertions(+), 13 deletions(-) create mode 100644 src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs create mode 100644 src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.csproj diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index fe0b0df011aa08..3fd02ad1473bcb 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11524,20 +11524,24 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return; - if (src->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_UDIV, GT_DIV, GT_MOD, GT_UMOD, GT_NEG, GT_NOT)) + if (src->OperIsArithmetic() || src->OperIs(GT_NOT, GT_NEG)) { if (src->gtGetOp1()->OperIs(GT_CAST)) { GenTreeCast* op1 = src->gtGetOp1()->AsCast(); - if (!gtIsActiveCSE_Candidate(op1)) + GenTree* op1src = op1->CastOp(); + if (opts.OptimizationEnabled()) { - if (castToType == op1->CastToType()) - { - src->AsOp()->gtOp1 = op1->CastOp(); + op1src = op1src->gtEffectiveVal(); + } - DEBUG_DESTROY_NODE(op1); - } + if (op1src->OperIsLocal() && (genActualType(op1) == genActualType(srcType)) && + !gtIsActiveCSE_Candidate(op1) && (castToType == op1->CastToType())) + { + src->AsOp()->gtOp1 = op1->CastOp(); + + DEBUG_DESTROY_NODE(op1); } } @@ -11545,14 +11549,18 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { GenTreeCast* op2 = src->gtGetOp2()->AsCast(); - if (!gtIsActiveCSE_Candidate(op2)) + GenTree* op2src = op2->CastOp(); + if (opts.OptimizationEnabled()) { - if (castToType == op2->CastToType()) - { - src->AsOp()->gtOp2 = op2->CastOp(); + op2src = op2src->gtEffectiveVal(); + } - DEBUG_DESTROY_NODE(op2); - } + if (op2src->OperIsLocal() && (genActualType(op2) == genActualType(srcType)) && + !gtIsActiveCSE_Candidate(op2) && (castToType == op2->CastToType())) + { + src->AsOp()->gtOp2 = op2->CastOp(); + + DEBUG_DESTROY_NODE(op2); } } } diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs new file mode 100644 index 00000000000000..6b4a05f36982f5 --- /dev/null +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs @@ -0,0 +1,136 @@ +// 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()); + } + + 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(); + + 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; + + return 100; + } +} \ No newline at end of file diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.csproj b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.csproj new file mode 100644 index 00000000000000..f3e1cbd44b4041 --- /dev/null +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + From 3eba2fa81ee6dfa6aef4a1e569600a0c33560e27 Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 9 Nov 2022 02:21:12 -0800 Subject: [PATCH 12/58] Do not do optimization for unsigned ops --- src/coreclr/jit/morph.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 3fd02ad1473bcb..3498451a57c2ab 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11524,6 +11524,9 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return; + if (src->OperIs(GT_UMOD, GT_UDIV)) + return; + if (src->OperIsArithmetic() || src->OperIs(GT_NOT, GT_NEG)) { if (src->gtGetOp1()->OperIs(GT_CAST)) From ec3dcda71e5486e2e4713f0e54c633b36f665da7 Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 9 Nov 2022 10:45:42 -0800 Subject: [PATCH 13/58] Added more division test cases --- src/coreclr/jit/gentree.h | 1 + src/coreclr/jit/lir.h | 6 ++ src/coreclr/jit/lower.h | 1 + src/coreclr/jit/lowerxarch.cpp | 71 ++++++++++++++++++- src/coreclr/jit/morph.cpp | 25 ++----- .../Regressions/Regression1/Regression1.cs | 45 +++++++++++- 6 files changed, 126 insertions(+), 23 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index aa9a9c8d04516f..5a575e2d9b6723 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1114,6 +1114,7 @@ struct GenTree inline bool IsRegOptional() const; inline void SetRegOptional(); inline void ClearRegOptional(); + inline void ClearContainedAndRegOptional(); #ifdef DEBUG void dumpLIRFlags(); #endif diff --git a/src/coreclr/jit/lir.h b/src/coreclr/jit/lir.h index e589c3e8f953b5..52073558ea1503 100644 --- a/src/coreclr/jit/lir.h +++ b/src/coreclr/jit/lir.h @@ -337,4 +337,10 @@ inline bool GenTree::IsRegOptional() const return (gtLIRFlags & LIR::Flags::RegOptional) != 0; } +inline void GenTree::ClearContainedAndRegOptional() +{ + ClearContained(); + ClearRegOptional(); +} + #endif // _LIR_H_ diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index f7a3da7771f912..0c068d70447e07 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -322,6 +322,7 @@ class Lowering final : public Phase #ifdef TARGET_XARCH void LowerPutArgStk(GenTreePutArgStk* putArgStk); GenTree* TryLowerMulWithConstant(GenTreeOp* node); + void LowerCastOfSmpOp(GenTreeCast* node); #endif // TARGET_XARCH bool TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* parent); diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 44d6781d081fc9..ce2aca9ed027e3 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -745,6 +745,69 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) #endif // TARGET_X86 } +//---------------------------------------------------------------------------------------------- +// Lowering::LowerCastOfSmpOp: +// +// Arguments: +// node - A cast node that is of a simple op node +// +void Lowering::LowerCastOfSmpOp(GenTreeCast* node) +{ + GenTree* castOp = node->CastOp(); + var_types castToType = node->CastToType(); + var_types srcType = castOp->TypeGet(); + + // force the srcType to unsigned if GT_UNSIGNED flag is set + if (node->gtFlags & GTF_UNSIGNED) + { + srcType = varTypeToUnsigned(srcType); + } + + assert(castOp->OperIsSimple()); + + if (castOp->OperMayOverflow() && castOp->gtOverflow()) + return; + + if (castOp->gtSetFlags()) + return; + + if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) + return; + + if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) + { + // This removes the casts as it prevents zero/sign-extended 'mov's before the simple op. + + if (castOp->gtGetOp1()->OperIs(GT_CAST)) + { + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + + if (castToType == op1->CastToType()) + { + op1->CastOp()->ClearContainedAndRegOptional(); + + castOp->AsOp()->gtOp1 = op1->CastOp(); + + BlockRange().Remove(op1); + } + } + + if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) + { + GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); + + if (castToType == op2->CastToType()) + { + op2->CastOp()->ClearContainedAndRegOptional(); + + castOp->AsOp()->gtOp2 = op2->CastOp(); + + BlockRange().Remove(op2); + } + } + } +} + /* Lower GT_CAST(srcType, DstType) nodes. * * Casts from small int type to float/double are transformed as follows: @@ -784,7 +847,13 @@ void Lowering::LowerCast(GenTree* tree) { assert(tree->OperGet() == GT_CAST); - GenTree* castOp = tree->AsCast()->CastOp(); + GenTree* castOp = tree->AsCast()->CastOp(); + +// if (castOp->OperIsSimple()) +// { +// LowerCastOfSmpOp(tree->AsCast()); +// } + var_types castToType = tree->CastToType(); var_types srcType = castOp->TypeGet(); var_types tmpType = TYP_UNDEF; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 3498451a57c2ab..e2a68e53f528ca 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11524,23 +11524,14 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return; - if (src->OperIs(GT_UMOD, GT_UDIV)) - return; - - if (src->OperIsArithmetic() || src->OperIs(GT_NOT, GT_NEG)) + if (src->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) { if (src->gtGetOp1()->OperIs(GT_CAST)) { GenTreeCast* op1 = src->gtGetOp1()->AsCast(); - GenTree* op1src = op1->CastOp(); - if (opts.OptimizationEnabled()) - { - op1src = op1src->gtEffectiveVal(); - } - - if (op1src->OperIsLocal() && (genActualType(op1) == genActualType(srcType)) && - !gtIsActiveCSE_Candidate(op1) && (castToType == op1->CastToType())) + if ((genActualType(op1) == genActualType(srcType)) && !gtIsActiveCSE_Candidate(op1) && + (castToType == op1->CastToType())) { src->AsOp()->gtOp1 = op1->CastOp(); @@ -11552,14 +11543,8 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { GenTreeCast* op2 = src->gtGetOp2()->AsCast(); - GenTree* op2src = op2->CastOp(); - if (opts.OptimizationEnabled()) - { - op2src = op2src->gtEffectiveVal(); - } - - if (op2src->OperIsLocal() && (genActualType(op2) == genActualType(srcType)) && - !gtIsActiveCSE_Candidate(op2) && (castToType == op2->CastToType())) + if ((genActualType(op2) == genActualType(srcType)) && !gtIsActiveCSE_Candidate(op2) && + (castToType == op2->CastToType())) { src->AsOp()->gtOp2 = op2->CastOp(); diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs index 6b4a05f36982f5..f9f703a54a61bc 100644 --- a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs @@ -92,6 +92,32 @@ public static int Test9() 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); + } + public static int Main() { var result1 = Test1(24648); @@ -103,6 +129,10 @@ public static int Main() 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); if (result1 != 0) return 0; @@ -131,6 +161,17 @@ public static int Main() 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; + return 100; - } -} \ No newline at end of file + } \ No newline at end of file From 183d570ff60cfa25036e9445adc748e49e9541fc Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 9 Nov 2022 14:23:13 -0800 Subject: [PATCH 14/58] Fixed test. Checking overflows on cast. Comparing actual types properly on the CastOp. --- src/coreclr/jit/lowerxarch.cpp | 6 +- src/coreclr/jit/morph.cpp | 8 +- .../Regressions/Regression1/Regression1.cs | 84 ++++++++++++++++++- 3 files changed, 90 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index ce2aca9ed027e3..48f14fb9804f2a 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -782,7 +782,8 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) { GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - if (castToType == op1->CastToType()) + if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && + castToType == op1->CastToType()) { op1->CastOp()->ClearContainedAndRegOptional(); @@ -796,7 +797,8 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) { GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - if (castToType == op2->CastToType()) + if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && + castToType == op2->CastToType()) { op2->CastOp()->ClearContainedAndRegOptional(); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index e2a68e53f528ca..a65805c47db8e5 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11530,8 +11530,8 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { GenTreeCast* op1 = src->gtGetOp1()->AsCast(); - if ((genActualType(op1) == genActualType(srcType)) && !gtIsActiveCSE_Candidate(op1) && - (castToType == op1->CastToType())) + if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && + !gtIsActiveCSE_Candidate(op1) && (castToType == op1->CastToType())) { src->AsOp()->gtOp1 = op1->CastOp(); @@ -11543,8 +11543,8 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { GenTreeCast* op2 = src->gtGetOp2()->AsCast(); - if ((genActualType(op2) == genActualType(srcType)) && !gtIsActiveCSE_Candidate(op2) && - (castToType == op2->CastToType())) + if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && + !gtIsActiveCSE_Candidate(op2) && (castToType == op2->CastToType())) { src->AsOp()->gtOp2 = op2->CastOp(); diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs index f9f703a54a61bc..987e06ca973208 100644 --- a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs @@ -6,7 +6,7 @@ public class Program { - [MethodImpl(MethodImplOptions.NoInlining)] +[MethodImpl(MethodImplOptions.NoInlining)] public static ushort GetUShortValue() { return 24648; @@ -118,6 +118,57 @@ 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); @@ -133,6 +184,13 @@ public static int Main() 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(unchecked((short)0x10000), 2); if (result1 != 0) return 0; @@ -173,5 +231,27 @@ public static int Main() 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; - } \ No newline at end of file + } +} \ No newline at end of file From 4378c1b5a626ec627c8fabb03fd19bc2fc9f7527 Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 9 Nov 2022 22:42:55 -0800 Subject: [PATCH 15/58] Added flag --- src/coreclr/jit/gentree.h | 2 ++ src/coreclr/jit/morph.cpp | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 5a575e2d9b6723..5b478323551a3d 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -415,6 +415,8 @@ enum GenTreeFlags : unsigned int // With operators: the specified node is an unsigned operator GTF_SPILL = 0x00020000, // Needs to be spilled here + GTF_DONT_REMOVE_CAST = 0x00040000, + // The extra flag GTF_IS_IN_CSE is used to tell the consumer of the side effect flags // that we are calling in the context of performing a CSE, thus we // should allow the run-once side effects of running a class constructor. diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index a65805c47db8e5..557d4b533759ef 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11535,6 +11535,8 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { src->AsOp()->gtOp1 = op1->CastOp(); + cast->gtFlags |= GTF_DONT_REMOVE_CAST; + DEBUG_DESTROY_NODE(op1); } } @@ -11548,6 +11550,8 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { src->AsOp()->gtOp2 = op2->CastOp(); + cast->gtFlags |= GTF_DONT_REMOVE_CAST; + DEBUG_DESTROY_NODE(op2); } } @@ -11584,6 +11588,9 @@ GenTree* Compiler::fgOptimizeCastOnAssignment(GenTreeOp* asg) if (op2->gtOverflow()) return asg; + if ((op2->gtFlags & GTF_DONT_REMOVE_CAST)) + return asg; + if (gtIsActiveCSE_Candidate(op2)) return asg; From 4a0b63d697d36ae85fed72213cc13d26beb0eb6b Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 10 Nov 2022 09:44:13 -0800 Subject: [PATCH 16/58] Remove flag --- src/coreclr/jit/gentree.h | 2 -- src/coreclr/jit/morph.cpp | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 5b478323551a3d..5a575e2d9b6723 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -415,8 +415,6 @@ enum GenTreeFlags : unsigned int // With operators: the specified node is an unsigned operator GTF_SPILL = 0x00020000, // Needs to be spilled here - GTF_DONT_REMOVE_CAST = 0x00040000, - // The extra flag GTF_IS_IN_CSE is used to tell the consumer of the side effect flags // that we are calling in the context of performing a CSE, thus we // should allow the run-once side effects of running a class constructor. diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 557d4b533759ef..b51d6554ed20a6 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11535,8 +11535,6 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { src->AsOp()->gtOp1 = op1->CastOp(); - cast->gtFlags |= GTF_DONT_REMOVE_CAST; - DEBUG_DESTROY_NODE(op1); } } @@ -11550,8 +11548,6 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { src->AsOp()->gtOp2 = op2->CastOp(); - cast->gtFlags |= GTF_DONT_REMOVE_CAST; - DEBUG_DESTROY_NODE(op2); } } @@ -11588,7 +11584,7 @@ GenTree* Compiler::fgOptimizeCastOnAssignment(GenTreeOp* asg) if (op2->gtOverflow()) return asg; - if ((op2->gtFlags & GTF_DONT_REMOVE_CAST)) + if (op2->AsCast()->CastOp()->gtEffectiveVal()->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) return asg; if (gtIsActiveCSE_Candidate(op2)) From 73fbc61219d1a43316cdf50f5ce6779f715dce7b Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 10 Nov 2022 09:51:44 -0800 Subject: [PATCH 17/58] Formatting --- src/coreclr/jit/lowerxarch.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 48f14fb9804f2a..aabe88381e4f18 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -851,10 +851,10 @@ void Lowering::LowerCast(GenTree* tree) GenTree* castOp = tree->AsCast()->CastOp(); -// if (castOp->OperIsSimple()) -// { -// LowerCastOfSmpOp(tree->AsCast()); -// } + // if (castOp->OperIsSimple()) + // { + // LowerCastOfSmpOp(tree->AsCast()); + // } var_types castToType = tree->CastToType(); var_types srcType = castOp->TypeGet(); From 38ec57f6e6b1386e29d59d2c1882711334bee7c1 Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 10 Nov 2022 09:52:27 -0800 Subject: [PATCH 18/58] Revert minor change --- src/coreclr/jit/morph.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index b51d6554ed20a6..a65805c47db8e5 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11584,9 +11584,6 @@ GenTree* Compiler::fgOptimizeCastOnAssignment(GenTreeOp* asg) if (op2->gtOverflow()) return asg; - if (op2->AsCast()->CastOp()->gtEffectiveVal()->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) - return asg; - if (gtIsActiveCSE_Candidate(op2)) return asg; From 343badf2dea91e2ceb889ce406835dd19ddf669f Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 10 Nov 2022 12:49:06 -0800 Subject: [PATCH 19/58] Removed ClearRegOptionalAndContained --- src/coreclr/jit/gentree.h | 1 - src/coreclr/jit/lir.h | 6 ------ src/coreclr/jit/lowerxarch.cpp | 4 ++-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 5a575e2d9b6723..aa9a9c8d04516f 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1114,7 +1114,6 @@ struct GenTree inline bool IsRegOptional() const; inline void SetRegOptional(); inline void ClearRegOptional(); - inline void ClearContainedAndRegOptional(); #ifdef DEBUG void dumpLIRFlags(); #endif diff --git a/src/coreclr/jit/lir.h b/src/coreclr/jit/lir.h index 52073558ea1503..e589c3e8f953b5 100644 --- a/src/coreclr/jit/lir.h +++ b/src/coreclr/jit/lir.h @@ -337,10 +337,4 @@ inline bool GenTree::IsRegOptional() const return (gtLIRFlags & LIR::Flags::RegOptional) != 0; } -inline void GenTree::ClearContainedAndRegOptional() -{ - ClearContained(); - ClearRegOptional(); -} - #endif // _LIR_H_ diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index aabe88381e4f18..ee076108b63e36 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -785,7 +785,7 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && castToType == op1->CastToType()) { - op1->CastOp()->ClearContainedAndRegOptional(); + op1->CastOp()->ClearContained(); castOp->AsOp()->gtOp1 = op1->CastOp(); @@ -800,7 +800,7 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && castToType == op2->CastToType()) { - op2->CastOp()->ClearContainedAndRegOptional(); + op2->CastOp()->ClearContained(); castOp->AsOp()->gtOp2 = op2->CastOp(); From 95840b259178c8b596c63175952f71a5574e1b3b Mon Sep 17 00:00:00 2001 From: TIHan Date: Sat, 12 Nov 2022 18:06:22 -0800 Subject: [PATCH 20/58] Trying out lowering --- src/coreclr/jit/lowerxarch.cpp | 8 ++++---- src/coreclr/jit/morph.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 34f924f1d69bff..e16d01e41f7e14 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -851,10 +851,10 @@ void Lowering::LowerCast(GenTree* tree) GenTree* castOp = tree->AsCast()->CastOp(); - // if (castOp->OperIsSimple()) - // { - // LowerCastOfSmpOp(tree->AsCast()); - // } + if (castOp->OperIsSimple()) + { + LowerCastOfSmpOp(tree->AsCast()); + } var_types castToType = tree->CastToType(); var_types srcType = castOp->TypeGet(); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 3619b34dec8c82..68b1417c3ccbcc 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11006,10 +11006,10 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } - if (src->OperIsSimple()) - { - fgOptimizeCastOfSmpOp(cast); - } + //if (src->OperIsSimple()) + //{ + // fgOptimizeCastOfSmpOp(cast); + //} // See if we can discard the cast. if (varTypeIsIntegral(cast) && varTypeIsIntegral(src)) From 1c2e139f2a907d3750b228e9206ef0f123968a02 Mon Sep 17 00:00:00 2001 From: TIHan Date: Sat, 12 Nov 2022 21:38:07 -0800 Subject: [PATCH 21/58] Expanded lowering cast removal to arm --- src/coreclr/jit/lower.cpp | 68 ++++++++++++++++++ src/coreclr/jit/lower.h | 2 +- src/coreclr/jit/lowerarmarch.cpp | 5 ++ src/coreclr/jit/lowerxarch.cpp | 65 ----------------- src/coreclr/jit/morph.cpp | 10 +-- .../Regressions/Regression1/Regression1.cs | 72 +++++++++++++++++++ .../Regression1/Regression1.csproj | 17 +++++ 7 files changed, 169 insertions(+), 70 deletions(-) create mode 100644 src/tests/JIT/opt/Regressions/Regression1/Regression1.cs create mode 100644 src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 6eb3cc4ccbc95c..5ed850d7e89840 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -491,6 +491,74 @@ GenTree* Lowering::LowerNode(GenTree* node) return node->gtNext; } +//---------------------------------------------------------------------------------------------- +// Lowering::LowerCastOfSmpOp: +// +// Arguments: +// node - A cast node that is of a simple op node +// +void Lowering::LowerCastOfSmpOp(GenTreeCast* node) +{ + GenTree* castOp = node->CastOp(); + var_types castToType = node->CastToType(); + var_types srcType = castOp->TypeGet(); + + assert(castOp->OperIsSimple()); + + if (castOp->OperMayOverflow() && castOp->gtOverflow()) + return; + + if (castOp->gtSetFlags()) + return; + + if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) + return; + + if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) + { + // This removes the casts as it prevents zero/sign-extended 'mov's before the op. + + bool canRelowerCastOp = false; + + if (castOp->gtGetOp1()->OperIs(GT_CAST)) + { + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + + if (!op1->gtOverflow() && varTypeIsIntegral(op1->CastFromType())) + { + canRelowerCastOp = true; + castOp->AsOp()->gtOp1 = op1->CastOp(); + + BlockRange().Remove(op1); + } + } + + if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) + { + GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); + + if (!op2->gtOverflow() && varTypeIsIntegral(op2->CastFromType())) + { + canRelowerCastOp = true; + castOp->AsOp()->gtOp2 = op2->CastOp(); + + BlockRange().Remove(op2); + } + } + + if (canRelowerCastOp) + { + castOp->gtGetOp1()->ClearContained(); + if (castOp->OperIsBinary()) + { + castOp->gtGetOp2()->ClearContained(); + } + + LowerNode(castOp); + } + } +} + /** -- Switch Lowering -- * The main idea of switch lowering is to keep transparency of the register requirements of this node * downstream in LSRA. Given that the switch instruction is inherently a control statement which in the JIT diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index d6d6f86a7063c5..62dc24518438f2 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -322,8 +322,8 @@ class Lowering final : public Phase #ifdef TARGET_XARCH void LowerPutArgStk(GenTreePutArgStk* putArgStk); GenTree* TryLowerMulWithConstant(GenTreeOp* node); - void LowerCastOfSmpOp(GenTreeCast* node); #endif // TARGET_XARCH + void LowerCastOfSmpOp(GenTreeCast* node); bool TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* parent); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 7f988dabad669c..5159019e4ea48a 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -771,6 +771,11 @@ void Lowering::LowerCast(GenTree* tree) assert(!varTypeIsSmall(srcType)); + if (tree->AsCast()->CastOp()->OperIsSimple()) + { + LowerCastOfSmpOp(tree->AsCast()); + } + // Now determine if we have operands that should be contained. ContainCheckCast(tree->AsCast()); } diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index e16d01e41f7e14..f8112fd0cd1f84 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -745,71 +745,6 @@ void Lowering::LowerPutArgStk(GenTreePutArgStk* putArgStk) #endif // TARGET_X86 } -//---------------------------------------------------------------------------------------------- -// Lowering::LowerCastOfSmpOp: -// -// Arguments: -// node - A cast node that is of a simple op node -// -void Lowering::LowerCastOfSmpOp(GenTreeCast* node) -{ - GenTree* castOp = node->CastOp(); - var_types castToType = node->CastToType(); - var_types srcType = castOp->TypeGet(); - - // force the srcType to unsigned if GT_UNSIGNED flag is set - if (node->gtFlags & GTF_UNSIGNED) - { - srcType = varTypeToUnsigned(srcType); - } - - assert(castOp->OperIsSimple()); - - if (castOp->OperMayOverflow() && castOp->gtOverflow()) - return; - - if (castOp->gtSetFlags()) - return; - - if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) - return; - - if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) - { - // This removes the casts as it prevents zero/sign-extended 'mov's before the simple op. - - if (castOp->gtGetOp1()->OperIs(GT_CAST)) - { - GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - - if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && - castToType == op1->CastToType()) - { - op1->CastOp()->ClearContained(); - - castOp->AsOp()->gtOp1 = op1->CastOp(); - - BlockRange().Remove(op1); - } - } - - if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) - { - GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - - if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && - castToType == op2->CastToType()) - { - op2->CastOp()->ClearContained(); - - castOp->AsOp()->gtOp2 = op2->CastOp(); - - BlockRange().Remove(op2); - } - } - } -} - /* Lower GT_CAST(srcType, DstType) nodes. * * Casts from small int type to float/double are transformed as follows: diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 68b1417c3ccbcc..c46382ca1bd946 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11006,10 +11006,10 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } - //if (src->OperIsSimple()) - //{ - // fgOptimizeCastOfSmpOp(cast); - //} + if (src->OperIsSimple()) + { + fgOptimizeCastOfSmpOp(cast); + } // See if we can discard the cast. if (varTypeIsIntegral(cast) && varTypeIsIntegral(src)) @@ -11100,6 +11100,8 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) // void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) { + return; + GenTree* src = cast->CastOp(); assert(src->OperIsSimple()); diff --git a/src/tests/JIT/opt/Regressions/Regression1/Regression1.cs b/src/tests/JIT/opt/Regressions/Regression1/Regression1.cs new file mode 100644 index 00000000000000..d6961c7fd8886d --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression1/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/Regression1.csproj b/src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj new file mode 100644 index 00000000000000..42a89c8384d74e --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj @@ -0,0 +1,17 @@ + + + Exe + + + None + True + + + + true + + + + + + From 49341e66c1afd344b8ec95ade0cc89a66a03b799 Mon Sep 17 00:00:00 2001 From: TIHan Date: Sat, 12 Nov 2022 21:50:24 -0800 Subject: [PATCH 22/58] Added regression test --- .../Regressions/Regression2/Regression2.cs | 83 +++++++++++++++++++ .../Regression2/Regression2.csproj | 17 ++++ 2 files changed, 100 insertions(+) create mode 100644 src/tests/JIT/opt/Regressions/Regression2/Regression2.cs create mode 100644 src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj diff --git a/src/tests/JIT/opt/Regressions/Regression2/Regression2.cs b/src/tests/JIT/opt/Regressions/Regression2/Regression2.cs new file mode 100644 index 00000000000000..d5c8bf737cdebb --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression2/Regression2.cs @@ -0,0 +1,83 @@ +// 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() + { + for (int i = 0; i < ushort.MaxValue + 1; i++) + { + for (int j = 1; j < ushort.MaxValue + 1; 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(); + } + catch(DivideByZeroException) {} + + return 100; + } +} diff --git a/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj b/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj new file mode 100644 index 00000000000000..42a89c8384d74e --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj @@ -0,0 +1,17 @@ + + + Exe + + + None + True + + + + true + + + + + + From 60f48ba4d01ed9503f0db1f34875b88be0feeace Mon Sep 17 00:00:00 2001 From: TIHan Date: Sat, 12 Nov 2022 21:56:37 -0800 Subject: [PATCH 23/58] Do not use disasm checks for these tests --- .../JIT/opt/Regressions/Regression1/Regression1.csproj | 7 +------ .../JIT/opt/Regressions/Regression2/Regression2.csproj | 7 +------ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj b/src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj index 42a89c8384d74e..f3e1cbd44b4041 100644 --- a/src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj +++ b/src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj @@ -7,11 +7,6 @@ True - - true - - - - + diff --git a/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj b/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj index 42a89c8384d74e..f3e1cbd44b4041 100644 --- a/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj +++ b/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj @@ -7,11 +7,6 @@ True - - true - - - - + From b267aa5ebcd5be51ef2afec54515e84fa7c34764 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Sat, 12 Nov 2022 22:33:07 -0800 Subject: [PATCH 24/58] Update lower.cpp --- src/coreclr/jit/lower.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 5ed850d7e89840..134c826870132d 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -504,6 +504,9 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) var_types srcType = castOp->TypeGet(); assert(castOp->OperIsSimple()); + + if (node->gtOverflow()) + return; if (castOp->OperMayOverflow() && castOp->gtOverflow()) return; From caefb4af17eb21db2d4c7692f9096695bb7e38ee Mon Sep 17 00:00:00 2001 From: TIHan Date: Sun, 13 Nov 2022 00:52:32 -0800 Subject: [PATCH 25/58] Fixing --- src/coreclr/jit/lower.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 134c826870132d..429c927f434380 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -527,7 +527,8 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) { GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - if (!op1->gtOverflow() && varTypeIsIntegral(op1->CastFromType())) + if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && + castToType == op1->CastToType()) { canRelowerCastOp = true; castOp->AsOp()->gtOp1 = op1->CastOp(); @@ -540,7 +541,8 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) { GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - if (!op2->gtOverflow() && varTypeIsIntegral(op2->CastFromType())) + if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && + castToType == op2->CastToType()) { canRelowerCastOp = true; castOp->AsOp()->gtOp2 = op2->CastOp(); From e226f30ade89541726e3cc2cbadbf20da69f1cab Mon Sep 17 00:00:00 2001 From: TIHan Date: Sun, 13 Nov 2022 04:12:28 -0800 Subject: [PATCH 26/58] For JCC, do not change castOp type if it is a bool --- src/coreclr/jit/lower.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 429c927f434380..0a7556cbc0b587 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3000,7 +3000,12 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) op2->SetIconValue(0xff); op2->gtType = castOp->gtType; #else - castOp->gtType = castToType; + // Do not change the castOp type for OR, XOR, AND if the cast type is BOOL + // as it will not matter. + if (!(castOp->OperIs(GT_OR, GT_XOR, GT_AND) && (castToType == TYP_BOOL))) + { + castOp->gtType = castToType; + } op2->gtType = castToType; #endif // If we have any contained memory ops on castOp, they must now not be contained. @@ -3008,16 +3013,25 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) if (castOp->OperIs(GT_OR, GT_XOR, GT_AND)) { + bool canRecontainCastOp = false; + GenTree* op1 = castOp->gtGetOp1(); if ((op1 != nullptr) && !op1->IsCnsIntOrI()) { op1->ClearContained(); + canRecontainCastOp = true; } GenTree* op2 = castOp->gtGetOp2(); if ((op2 != nullptr) && !op2->IsCnsIntOrI()) { op2->ClearContained(); + canRecontainCastOp = true; + } + + if (canRecontainCastOp) + { + ContainCheckBinary(castOp->AsOp()); } } From 84ae5a74d2742a152a59a52dc84f3cac238f30af Mon Sep 17 00:00:00 2001 From: TIHan Date: Sun, 13 Nov 2022 13:50:00 -0800 Subject: [PATCH 27/58] Added more regression tests made from Fuzzlyn. Reverted minor cast change. Changing type for GT_LCL_VAR if it is appropriate for OptimizeConstCompare. --- src/coreclr/jit/lower.cpp | 28 +++----- .../Regressions/Regression3/Regression3.cs | 71 +++++++++++++++++++ .../Regression3/Regression3.csproj | 12 ++++ .../Regressions/Regression4/Regression4.cs | 71 +++++++++++++++++++ .../Regression4/Regression4.csproj | 12 ++++ .../Regressions/Regression5/Regression5.cs | 57 +++++++++++++++ .../Regression5/Regression5.csproj | 12 ++++ 7 files changed, 245 insertions(+), 18 deletions(-) create mode 100644 src/tests/JIT/opt/Regressions/Regression3/Regression3.cs create mode 100644 src/tests/JIT/opt/Regressions/Regression3/Regression3.csproj create mode 100644 src/tests/JIT/opt/Regressions/Regression4/Regression4.cs create mode 100644 src/tests/JIT/opt/Regressions/Regression4/Regression4.csproj create mode 100644 src/tests/JIT/opt/Regressions/Regression5/Regression5.cs create mode 100644 src/tests/JIT/opt/Regressions/Regression5/Regression5.csproj diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 0a7556cbc0b587..e3334f0618bb19 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3000,12 +3000,7 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) op2->SetIconValue(0xff); op2->gtType = castOp->gtType; #else - // Do not change the castOp type for OR, XOR, AND if the cast type is BOOL - // as it will not matter. - if (!(castOp->OperIs(GT_OR, GT_XOR, GT_AND) && (castToType == TYP_BOOL))) - { - castOp->gtType = castToType; - } + castOp->gtType = castToType; op2->gtType = castToType; #endif // If we have any contained memory ops on castOp, they must now not be contained. @@ -3013,26 +3008,23 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) if (castOp->OperIs(GT_OR, GT_XOR, GT_AND)) { - bool canRecontainCastOp = false; - GenTree* op1 = castOp->gtGetOp1(); - if ((op1 != nullptr) && !op1->IsCnsIntOrI()) + op1->ClearContained(); + + if (op1->OperIs(GT_LCL_VAR) && !op2->OperIs(GT_LCL_VAR)) { - op1->ClearContained(); - canRecontainCastOp = true; + op1->ChangeType(castToType); } GenTree* op2 = castOp->gtGetOp2(); - if ((op2 != nullptr) && !op2->IsCnsIntOrI()) - { - op2->ClearContained(); - canRecontainCastOp = true; - } + op2->ClearContained(); - if (canRecontainCastOp) + if (op2->OperIs(GT_LCL_VAR) && !op1->OperIs(GT_LCL_VAR)) { - ContainCheckBinary(castOp->AsOp()); + op2->ChangeType(castToType); } + + ContainCheckBinary(castOp->AsOp()); } cmp->AsOp()->gtOp1 = castOp; diff --git a/src/tests/JIT/opt/Regressions/Regression3/Regression3.cs b/src/tests/JIT/opt/Regressions/Regression3/Regression3.cs new file mode 100644 index 00000000000000..78b6910c0c8db5 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression3/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/Regression3.csproj b/src/tests/JIT/opt/Regressions/Regression3/Regression3.csproj new file mode 100644 index 00000000000000..f3e1cbd44b4041 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression3/Regression3.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + diff --git a/src/tests/JIT/opt/Regressions/Regression4/Regression4.cs b/src/tests/JIT/opt/Regressions/Regression4/Regression4.cs new file mode 100644 index 00000000000000..b39ff25e82933d --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression4/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/Regression4.csproj b/src/tests/JIT/opt/Regressions/Regression4/Regression4.csproj new file mode 100644 index 00000000000000..f3e1cbd44b4041 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression4/Regression4.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + diff --git a/src/tests/JIT/opt/Regressions/Regression5/Regression5.cs b/src/tests/JIT/opt/Regressions/Regression5/Regression5.cs new file mode 100644 index 00000000000000..346b2503ef34e2 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression5/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/Regression5.csproj b/src/tests/JIT/opt/Regressions/Regression5/Regression5.csproj new file mode 100644 index 00000000000000..f3e1cbd44b4041 --- /dev/null +++ b/src/tests/JIT/opt/Regressions/Regression5/Regression5.csproj @@ -0,0 +1,12 @@ + + + Exe + + + None + True + + + + + From 937596b0d8a05779c4281dadb9e748aefffb7d42 Mon Sep 17 00:00:00 2001 From: TIHan Date: Sun, 13 Nov 2022 14:17:33 -0800 Subject: [PATCH 28/58] Remove Morph impl as we are not favoring Lowering. Do not change var type on arm. --- src/coreclr/jit/compiler.h | 1 - src/coreclr/jit/lower.cpp | 8 +++-- src/coreclr/jit/morph.cpp | 61 -------------------------------------- 3 files changed, 5 insertions(+), 65 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f7559f10a600bf..34a0b475d09abd 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5789,7 +5789,6 @@ class Compiler GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optAssertionPropDone = nullptr); void fgTryReplaceStructLocalWithField(GenTree* tree); GenTree* fgOptimizeCast(GenTreeCast* cast); - void fgOptimizeCastOfSmpOp(GenTreeCast* cast); GenTree* fgOptimizeCastOnAssignment(GenTreeOp* asg); GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp); GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp); diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index e3334f0618bb19..b30911b9f9e774 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3009,20 +3009,22 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) if (castOp->OperIs(GT_OR, GT_XOR, GT_AND)) { GenTree* op1 = castOp->gtGetOp1(); + GenTree* op2 = castOp->gtGetOp2(); + op1->ClearContained(); + op2->ClearContained(); +#ifdef TARGET_XARCH if (op1->OperIs(GT_LCL_VAR) && !op2->OperIs(GT_LCL_VAR)) { op1->ChangeType(castToType); } - GenTree* op2 = castOp->gtGetOp2(); - op2->ClearContained(); - if (op2->OperIs(GT_LCL_VAR) && !op1->OperIs(GT_LCL_VAR)) { op2->ChangeType(castToType); } +#endif // TARGET_XARCH ContainCheckBinary(castOp->AsOp()); } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index c46382ca1bd946..2ea9f1225e3412 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11006,11 +11006,6 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } - if (src->OperIsSimple()) - { - fgOptimizeCastOfSmpOp(cast); - } - // See if we can discard the cast. if (varTypeIsIntegral(cast) && varTypeIsIntegral(src)) { @@ -11092,62 +11087,6 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } -//------------------------------------------------------------------------ -// fgOptimizeCastOfSmpOp: Optimizes the supplied GT_CAST tree of a GTK_SMPOP. -// -// Arguments: -// tree - the cast tree to optimize -// -void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) -{ - return; - - GenTree* src = cast->CastOp(); - - assert(src->OperIsSimple()); - - if (gtIsActiveCSE_Candidate(cast) || gtIsActiveCSE_Candidate(src)) - return; - - var_types castToType = cast->CastToType(); - var_types srcType = src->TypeGet(); - - if (src->OperMayOverflow() && src->gtOverflow()) - return; - - if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) - return; - - if (src->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) - { - if (src->gtGetOp1()->OperIs(GT_CAST)) - { - GenTreeCast* op1 = src->gtGetOp1()->AsCast(); - - if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && - !gtIsActiveCSE_Candidate(op1) && (castToType == op1->CastToType())) - { - src->AsOp()->gtOp1 = op1->CastOp(); - - DEBUG_DESTROY_NODE(op1); - } - } - - if (src->OperIsBinary() && src->gtGetOp2()->OperIs(GT_CAST)) - { - GenTreeCast* op2 = src->gtGetOp2()->AsCast(); - - if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && - !gtIsActiveCSE_Candidate(op2) && (castToType == op2->CastToType())) - { - src->AsOp()->gtOp2 = op2->CastOp(); - - DEBUG_DESTROY_NODE(op2); - } - } - } -} - //------------------------------------------------------------------------ // fgOptimizeCastOnAssignment: Optimizes the supplied GT_ASG tree with a GT_CAST node. // From 6cc95ca50c134b2d19cd64214916ba089cb35c80 Mon Sep 17 00:00:00 2001 From: TIHan Date: Sun, 13 Nov 2022 14:56:04 -0800 Subject: [PATCH 29/58] Formatting --- src/coreclr/jit/lower.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index b30911b9f9e774..f922295653df51 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -504,7 +504,7 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) var_types srcType = castOp->TypeGet(); assert(castOp->OperIsSimple()); - + if (node->gtOverflow()) return; From e5c9b93d81aa1134a4a9e0e32189b28e6102f1f9 Mon Sep 17 00:00:00 2001 From: TIHan Date: Sun, 13 Nov 2022 17:08:00 -0800 Subject: [PATCH 30/58] Re-arranging regression tests. Some comments. Some And and Or disasm tests. --- src/coreclr/jit/lower.cpp | 5 +- src/tests/JIT/opt/And/IntAnd.cs | 173 ++++++++++++++++++ src/tests/JIT/opt/And/IntAnd.csproj | 17 ++ .../{Regression1 => }/Regression1.cs | 0 .../{Regression1 => }/Regression1.csproj | 0 src/tests/JIT/opt/Or/IntOr.cs | 112 ++++++++++++ src/tests/JIT/opt/Or/IntOr.csproj | 17 ++ .../{Regression1 => }/Regression1.cs | 0 .../{Regression1 => }/Regression1.csproj | 0 .../{Regression2 => }/Regression2.cs | 0 .../{Regression2 => }/Regression2.csproj | 0 .../{Regression3 => }/Regression3.cs | 0 .../{Regression3 => }/Regression3.csproj | 0 .../{Regression4 => }/Regression4.cs | 0 .../{Regression4 => }/Regression4.csproj | 0 .../{Regression5 => }/Regression5.cs | 0 .../{Regression5 => }/Regression5.csproj | 0 17 files changed, 321 insertions(+), 3 deletions(-) create mode 100644 src/tests/JIT/opt/And/IntAnd.cs create mode 100644 src/tests/JIT/opt/And/IntAnd.csproj rename src/tests/JIT/opt/Divide/Regressions/{Regression1 => }/Regression1.cs (100%) rename src/tests/JIT/opt/Divide/Regressions/{Regression1 => }/Regression1.csproj (100%) create mode 100644 src/tests/JIT/opt/Or/IntOr.cs create mode 100644 src/tests/JIT/opt/Or/IntOr.csproj rename src/tests/JIT/opt/Regressions/{Regression1 => }/Regression1.cs (100%) rename src/tests/JIT/opt/Regressions/{Regression1 => }/Regression1.csproj (100%) rename src/tests/JIT/opt/Regressions/{Regression2 => }/Regression2.cs (100%) rename src/tests/JIT/opt/Regressions/{Regression2 => }/Regression2.csproj (100%) rename src/tests/JIT/opt/Regressions/{Regression3 => }/Regression3.cs (100%) rename src/tests/JIT/opt/Regressions/{Regression3 => }/Regression3.csproj (100%) rename src/tests/JIT/opt/Regressions/{Regression4 => }/Regression4.cs (100%) rename src/tests/JIT/opt/Regressions/{Regression4 => }/Regression4.csproj (100%) rename src/tests/JIT/opt/Regressions/{Regression5 => }/Regression5.cs (100%) rename src/tests/JIT/opt/Regressions/{Regression5 => }/Regression5.csproj (100%) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index f922295653df51..4b589c3aa75590 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -511,9 +511,6 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) if (castOp->OperMayOverflow() && castOp->gtOverflow()) return; - if (castOp->gtSetFlags()) - return; - if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) return; @@ -3015,6 +3012,8 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) op2->ClearContained(); #ifdef TARGET_XARCH + // We change the type here so we can take advantage of containment for a memory op. + if (op1->OperIs(GT_LCL_VAR) && !op2->OperIs(GT_LCL_VAR)) { op1->ChangeType(castToType); diff --git a/src/tests/JIT/opt/And/IntAnd.cs b/src/tests/JIT/opt/And/IntAnd.cs new file mode 100644 index 00000000000000..af52e052529603 --- /dev/null +++ b/src/tests/JIT/opt/And/IntAnd.cs @@ -0,0 +1,173 @@ +// 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 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 r8, r8'. + // X64: and [[REG0:[^x]+]], [[REG1:[^x]+]] + + 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 r8, r8'. + // X64: and [[REG0:[^x]+]], [[REG1:[^x]+]] + + 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 r8, r8'. + // X64: and [[REG0:[^x]+]], [[REG1:[^x]+]] + + if ((byte)((byte)x & (byte)y) == 0) + { + SideEffect(); + return true; + } + return false; + } + + static int Main() + { + // No CastByte + if (!Test_UInt32_UInt32_And(0b10000000000000000000000000000000, 0b00000000000000000000000000000001)) + return 0; + + if (!Test_UInt32_UInt32_And(0b00000000000000000000000000000001, 0b10000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_And(0b10000000000000000000000000000000, 0b10000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_And(0b10000000000000000000000000000000, 0b00000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_And(0b00000000000000000000000000000000, 0b10000000000000000000000000000000)) + return 0; + + if (Test_UInt32_UInt32_And(0b00000000000000000000000000000001, 0b00000000000000000000000000000001)) + return 0; + + if (!Test_UInt32_UInt32_And(0b00000000000000000000000000000010, 0b00000000000000000000000000000001)) + return 0; + + if (Test_UInt32_UInt32_And(0b00000000000000000000000000000010, 0b00000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_And(0b10000000000000000000000000000010, 0b10000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_And(0b00100000000000000000000000000010, 0b10000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_And(0b10000000000000000000000000000010, 0b00100000000000000000000000000010)) + return 0; + + // CastByte + if (!Test_UInt32_UInt32_CastByte_And(0b10000000000000000000000000000000, 0b00000000000000000000000000000001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b00000000000000000000000000000001, 0b10000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b10000000000000000000000000000000, 0b10000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b10000000000000000000000000000000, 0b00000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b00000000000000000000000000000000, 0b10000000000000000000000000000000)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b00000000000000000000000000000001, 0b00000000000000000000000000000001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_And(0b00000000000000000000000000000010, 0b00000000000000000000000000000001)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b00000000000000000000000000000010, 0b00000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b10000000000000000000000000000010, 0b10000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b00100000000000000000000000000010, 0b10000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_And(0b10000000000000000000000000000010, 0b00100000000000000000000000000010)) + return 0; + + // CastByte_CastByte + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b10000000000000000000000000000000, 0b00000000000000000000000000000001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b00000000000000000000000000000001, 0b10000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b10000000000000000000000000000000, 0b10000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b10000000000000000000000000000000, 0b00000000000000000000000000000000)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b00000000000000000000000000000000, 0b10000000000000000000000000000000)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b00000000000000000000000000000001, 0b00000000000000000000000000000001)) + return 0; + + if (!Test_UInt32_UInt32_CastByte_CastByte_And(0b00000000000000000000000000000010, 0b00000000000000000000000000000001)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b00000000000000000000000000000010, 0b00000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b10000000000000000000000000000010, 0b10000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b00100000000000000000000000000010, 0b10000000000000000000000000000010)) + return 0; + + if (Test_UInt32_UInt32_CastByte_CastByte_And(0b10000000000000000000000000000010, 0b00100000000000000000000000000010)) + return 0; + + Console.Write("Succeeded"); + + return 100; + } + } +} \ No newline at end of file diff --git a/src/tests/JIT/opt/And/IntAnd.csproj b/src/tests/JIT/opt/And/IntAnd.csproj new file mode 100644 index 00000000000000..42a89c8384d74e --- /dev/null +++ b/src/tests/JIT/opt/And/IntAnd.csproj @@ -0,0 +1,17 @@ + + + Exe + + + None + True + + + + true + + + + + + diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs b/src/tests/JIT/opt/Divide/Regressions/Regression1.cs similarity index 100% rename from src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.cs rename to src/tests/JIT/opt/Divide/Regressions/Regression1.cs diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.csproj b/src/tests/JIT/opt/Divide/Regressions/Regression1.csproj similarity index 100% rename from src/tests/JIT/opt/Divide/Regressions/Regression1/Regression1.csproj rename to src/tests/JIT/opt/Divide/Regressions/Regression1.csproj diff --git a/src/tests/JIT/opt/Or/IntOr.cs b/src/tests/JIT/opt/Or/IntOr.cs new file mode 100644 index 00000000000000..bcc2673badb508 --- /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 r8, r8'. + // X64: or [[REG0:[^x]+]], [[REG1:[^x]+]] + + if ((byte)((byte)x | y) == 0) + { + SideEffect(); + return true; + } + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public static unsafe bool Test_UInt32_ByRef_CastByte_CastByte_Or(uint x, ref uint y) + { + // X64-NOT: movzx + + // We expect 'or r8, m8'. + // X64: or [[REG0:[^x]+]], 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; + } + } +} \ No newline at end of file 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/Regression1.cs b/src/tests/JIT/opt/Regressions/Regression1.cs similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression1/Regression1.cs rename to src/tests/JIT/opt/Regressions/Regression1.cs diff --git a/src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj b/src/tests/JIT/opt/Regressions/Regression1.csproj similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression1/Regression1.csproj rename to src/tests/JIT/opt/Regressions/Regression1.csproj diff --git a/src/tests/JIT/opt/Regressions/Regression2/Regression2.cs b/src/tests/JIT/opt/Regressions/Regression2.cs similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression2/Regression2.cs rename to src/tests/JIT/opt/Regressions/Regression2.cs diff --git a/src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj b/src/tests/JIT/opt/Regressions/Regression2.csproj similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression2/Regression2.csproj rename to src/tests/JIT/opt/Regressions/Regression2.csproj diff --git a/src/tests/JIT/opt/Regressions/Regression3/Regression3.cs b/src/tests/JIT/opt/Regressions/Regression3.cs similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression3/Regression3.cs rename to src/tests/JIT/opt/Regressions/Regression3.cs diff --git a/src/tests/JIT/opt/Regressions/Regression3/Regression3.csproj b/src/tests/JIT/opt/Regressions/Regression3.csproj similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression3/Regression3.csproj rename to src/tests/JIT/opt/Regressions/Regression3.csproj diff --git a/src/tests/JIT/opt/Regressions/Regression4/Regression4.cs b/src/tests/JIT/opt/Regressions/Regression4.cs similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression4/Regression4.cs rename to src/tests/JIT/opt/Regressions/Regression4.cs diff --git a/src/tests/JIT/opt/Regressions/Regression4/Regression4.csproj b/src/tests/JIT/opt/Regressions/Regression4.csproj similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression4/Regression4.csproj rename to src/tests/JIT/opt/Regressions/Regression4.csproj diff --git a/src/tests/JIT/opt/Regressions/Regression5/Regression5.cs b/src/tests/JIT/opt/Regressions/Regression5.cs similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression5/Regression5.cs rename to src/tests/JIT/opt/Regressions/Regression5.cs diff --git a/src/tests/JIT/opt/Regressions/Regression5/Regression5.csproj b/src/tests/JIT/opt/Regressions/Regression5.csproj similarity index 100% rename from src/tests/JIT/opt/Regressions/Regression5/Regression5.csproj rename to src/tests/JIT/opt/Regressions/Regression5.csproj From 966df08bbaa4e34739cb3aeaa16fa103a4171bde Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 14 Nov 2022 01:24:04 -0800 Subject: [PATCH 31/58] Fixed a few minor regressions --- src/coreclr/jit/lower.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 4b589c3aa75590..22596a4da2c744 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -511,7 +511,11 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) if (castOp->OperMayOverflow() && castOp->gtOverflow()) return; - if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType)) + if (castOp->gtSetFlags()) + return; + + // Only optimize if the castToType is a small integer type. + if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType) || !varTypeIsSmall(castToType)) return; if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) @@ -525,7 +529,7 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && - castToType == op1->CastToType()) + (castToType == op1->CastToType())) { canRelowerCastOp = true; castOp->AsOp()->gtOp1 = op1->CastOp(); @@ -539,7 +543,7 @@ void Lowering::LowerCastOfSmpOp(GenTreeCast* node) GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && - castToType == op2->CastToType()) + (castToType == op2->CastToType())) { canRelowerCastOp = true; castOp->AsOp()->gtOp2 = op2->CastOp(); @@ -3008,23 +3012,22 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) GenTree* op1 = castOp->gtGetOp1(); GenTree* op2 = castOp->gtGetOp2(); - op1->ClearContained(); - op2->ClearContained(); - #ifdef TARGET_XARCH - // We change the type here so we can take advantage of containment for a memory op. - - if (op1->OperIs(GT_LCL_VAR) && !op2->OperIs(GT_LCL_VAR)) + // If one of the ops is not already contained and one of the ops is a GT_LCL_VAR, + // then change its type to the castToType. We do this to take advantage of + // containment for a memory op. + if (op1->OperIs(GT_LCL_VAR) && !op2->isContained()) { op1->ChangeType(castToType); } - - if (op2->OperIs(GT_LCL_VAR) && !op1->OperIs(GT_LCL_VAR)) + else if (op2->OperIs(GT_LCL_VAR) && !op1->isContained()) { op2->ChangeType(castToType); } #endif // TARGET_XARCH + op1->ClearContained(); + op2->ClearContained(); ContainCheckBinary(castOp->AsOp()); } From 94411b5ba5dcd193b5b90f13f3a7109a13bd54b2 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 14 Nov 2022 02:19:26 -0800 Subject: [PATCH 32/58] minor change --- src/coreclr/jit/lower.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 22596a4da2c744..4f9242c2f53f8a 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -3013,14 +3013,13 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) GenTree* op2 = castOp->gtGetOp2(); #ifdef TARGET_XARCH - // If one of the ops is not already contained and one of the ops is a GT_LCL_VAR, - // then change its type to the castToType. We do this to take advantage of - // containment for a memory op. - if (op1->OperIs(GT_LCL_VAR) && !op2->isContained()) + // If one of the ops is a GT_LCL_VAR, then change its type to the castToType. + // We do this to take advantage of containment for a memory op. + if (op1->OperIs(GT_LCL_VAR) && !op2->IsCnsIntOrI()) { op1->ChangeType(castToType); } - else if (op2->OperIs(GT_LCL_VAR) && !op1->isContained()) + else if (op2->OperIs(GT_LCL_VAR)) { op2->ChangeType(castToType); } From 3ec0f0d24502099b439fe2a10321c966d7a0f615 Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 14 Nov 2022 10:59:53 -0800 Subject: [PATCH 33/58] Moving optimization to simple lowering --- src/coreclr/jit/flowgraph.cpp | 61 ++++++++++++++++++++++++++ src/coreclr/jit/lower.cpp | 74 -------------------------------- src/coreclr/jit/lower.h | 1 - src/coreclr/jit/lowerarmarch.cpp | 5 --- src/coreclr/jit/lowerxarch.cpp | 8 +--- 5 files changed, 62 insertions(+), 87 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 4fc2b9b0d9d9e5..371573a8b9a39d 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3068,6 +3068,67 @@ PhaseStatus Compiler::fgSimpleLowering() } #endif // FEATURE_FIXED_OUT_ARGS + case GT_CAST: + { + // Simple cast removal optimization. + // TODO: This could be done in morph, but the following occurs when done in morph: + // - Possible bug in value numbering/CSE that causes a situation + // for a variable to not be normalized on-load correctly. + // - Several regressions that need to be investigated. + // - TP is higher in morph. + + GenTreeCast* cast = tree->AsCast(); + GenTree* castOp = cast->CastOp(); + var_types castToType = cast->CastToType(); + var_types srcType = castOp->TypeGet(); + + if (!castOp->OperIsSimple()) + break; + + if (cast->gtOverflow()) + break; + + if (castOp->OperMayOverflow() && castOp->gtOverflow()) + break; + + if (castOp->gtSetFlags()) + break; + + // Only optimize if the castToType is a small integer type. + if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType) || !varTypeIsSmall(castToType)) + break; + + if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) + { + // This removes the casts as it prevents zero/sign-extended 'mov's before the op. + + if (castOp->gtGetOp1()->OperIs(GT_CAST)) + { + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + + if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && + (castToType == op1->CastToType())) + { + castOp->AsOp()->gtOp1 = op1->CastOp(); + range.Remove(op1); + } + } + + if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) + { + GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); + + if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && + (castToType == op2->CastToType())) + { + castOp->AsOp()->gtOp2 = op2->CastOp(); + range.Remove(op2); + } + } + } + } + break; + default: { // No other operators need processing. diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 4f9242c2f53f8a..7c197155eaccbc 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -491,80 +491,6 @@ GenTree* Lowering::LowerNode(GenTree* node) return node->gtNext; } -//---------------------------------------------------------------------------------------------- -// Lowering::LowerCastOfSmpOp: -// -// Arguments: -// node - A cast node that is of a simple op node -// -void Lowering::LowerCastOfSmpOp(GenTreeCast* node) -{ - GenTree* castOp = node->CastOp(); - var_types castToType = node->CastToType(); - var_types srcType = castOp->TypeGet(); - - assert(castOp->OperIsSimple()); - - if (node->gtOverflow()) - return; - - if (castOp->OperMayOverflow() && castOp->gtOverflow()) - return; - - if (castOp->gtSetFlags()) - return; - - // Only optimize if the castToType is a small integer type. - if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType) || !varTypeIsSmall(castToType)) - return; - - if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) - { - // This removes the casts as it prevents zero/sign-extended 'mov's before the op. - - bool canRelowerCastOp = false; - - if (castOp->gtGetOp1()->OperIs(GT_CAST)) - { - GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - - if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && - (castToType == op1->CastToType())) - { - canRelowerCastOp = true; - castOp->AsOp()->gtOp1 = op1->CastOp(); - - BlockRange().Remove(op1); - } - } - - if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) - { - GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - - if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && - (castToType == op2->CastToType())) - { - canRelowerCastOp = true; - castOp->AsOp()->gtOp2 = op2->CastOp(); - - BlockRange().Remove(op2); - } - } - - if (canRelowerCastOp) - { - castOp->gtGetOp1()->ClearContained(); - if (castOp->OperIsBinary()) - { - castOp->gtGetOp2()->ClearContained(); - } - - LowerNode(castOp); - } - } -} - /** -- Switch Lowering -- * The main idea of switch lowering is to keep transparency of the register requirements of this node * downstream in LSRA. Given that the switch instruction is inherently a control statement which in the JIT diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 62dc24518438f2..803dd890a8e84d 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -323,7 +323,6 @@ class Lowering final : public Phase void LowerPutArgStk(GenTreePutArgStk* putArgStk); GenTree* TryLowerMulWithConstant(GenTreeOp* node); #endif // TARGET_XARCH - void LowerCastOfSmpOp(GenTreeCast* node); bool TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* parent); diff --git a/src/coreclr/jit/lowerarmarch.cpp b/src/coreclr/jit/lowerarmarch.cpp index 5159019e4ea48a..7f988dabad669c 100644 --- a/src/coreclr/jit/lowerarmarch.cpp +++ b/src/coreclr/jit/lowerarmarch.cpp @@ -771,11 +771,6 @@ void Lowering::LowerCast(GenTree* tree) assert(!varTypeIsSmall(srcType)); - if (tree->AsCast()->CastOp()->OperIsSimple()) - { - LowerCastOfSmpOp(tree->AsCast()); - } - // Now determine if we have operands that should be contained. ContainCheckCast(tree->AsCast()); } diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index f8112fd0cd1f84..40ccaebec0f938 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -784,13 +784,7 @@ void Lowering::LowerCast(GenTree* tree) { assert(tree->OperGet() == GT_CAST); - GenTree* castOp = tree->AsCast()->CastOp(); - - if (castOp->OperIsSimple()) - { - LowerCastOfSmpOp(tree->AsCast()); - } - + GenTree* castOp = tree->AsCast()->CastOp(); var_types castToType = tree->CastToType(); var_types srcType = castOp->TypeGet(); var_types tmpType = TYP_UNDEF; From cb1983cc373579d12841c47412e76097c07fd4ea Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 15 Nov 2022 11:29:05 -0800 Subject: [PATCH 34/58] Moving back to morph --- src/coreclr/jit/compiler.h | 1 + src/coreclr/jit/flowgraph.cpp | 61 --------------------------------- src/coreclr/jit/morph.cpp | 64 +++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 61 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 1f47f5df419d6d..fbb9c6ea04d408 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5785,6 +5785,7 @@ class Compiler GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optAssertionPropDone = nullptr); void fgTryReplaceStructLocalWithField(GenTree* tree); GenTree* fgOptimizeCast(GenTreeCast* cast); + void fgOptimizeCastOfSmpOp(GenTreeCast* cast); GenTree* fgOptimizeCastOnAssignment(GenTreeOp* asg); GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp); GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 371573a8b9a39d..4fc2b9b0d9d9e5 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3068,67 +3068,6 @@ PhaseStatus Compiler::fgSimpleLowering() } #endif // FEATURE_FIXED_OUT_ARGS - case GT_CAST: - { - // Simple cast removal optimization. - // TODO: This could be done in morph, but the following occurs when done in morph: - // - Possible bug in value numbering/CSE that causes a situation - // for a variable to not be normalized on-load correctly. - // - Several regressions that need to be investigated. - // - TP is higher in morph. - - GenTreeCast* cast = tree->AsCast(); - GenTree* castOp = cast->CastOp(); - var_types castToType = cast->CastToType(); - var_types srcType = castOp->TypeGet(); - - if (!castOp->OperIsSimple()) - break; - - if (cast->gtOverflow()) - break; - - if (castOp->OperMayOverflow() && castOp->gtOverflow()) - break; - - if (castOp->gtSetFlags()) - break; - - // Only optimize if the castToType is a small integer type. - if (!varTypeIsIntegral(castToType) || !varTypeIsIntegral(srcType) || !varTypeIsSmall(castToType)) - break; - - if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) - { - // This removes the casts as it prevents zero/sign-extended 'mov's before the op. - - if (castOp->gtGetOp1()->OperIs(GT_CAST)) - { - GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - - if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && - (castToType == op1->CastToType())) - { - castOp->AsOp()->gtOp1 = op1->CastOp(); - range.Remove(op1); - } - } - - if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) - { - GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - - if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && - (castToType == op2->CastToType())) - { - castOp->AsOp()->gtOp2 = op2->CastOp(); - range.Remove(op2); - } - } - } - } - break; - default: { // No other operators need processing. diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2ea9f1225e3412..365e649e6962a9 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11084,9 +11084,73 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) } } + if (cast->CastOp()->OperIsSimple()) + { + fgOptimizeCastOfSmpOp(cast); + } + return cast; } +//------------------------------------------------------------------------ +// fgOptimizeCastOfSmpOp: Optimizes the supplied GT_CAST of GTK_SMPOP tree. +// +// Arguments: +// cast - the cast tree to optimize +// +void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) +{ + GenTree* castOp = cast->CastOp(); + var_types castToType = cast->CastToType(); + var_types srcType = castOp->TypeGet(); + + assert(castOp->OperIsSimple()); + + if (gtIsActiveCSE_Candidate(cast) || gtIsActiveCSE_Candidate(castOp)) + return; + + if (cast->gtOverflow()) + return; + + if (castOp->OperMayOverflow() && castOp->gtOverflow()) + return; + + // Only optimize if the castToType is a small integer type. + if (!varTypeIsSmall(castToType) || !varTypeIsIntegral(srcType)) + return; + + if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) + { + // This removes the casts as it prevents zero/sign-extended 'mov's before the op. + + if (castOp->gtGetOp1()->OperIs(GT_CAST)) + { + GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); + + if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && + (castToType == op1->CastToType()) && !gtIsActiveCSE_Candidate(op1)) + { + castOp->AsOp()->gtOp1 = op1->CastOp(); + + DEBUG_DESTROY_NODE(op1); + } + } + + if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) + { + GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); + + if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && + (castToType == op2->CastToType()) && !gtIsActiveCSE_Candidate(op2)) + { + castOp->AsOp()->gtOp2 = op2->CastOp(); + + DEBUG_DESTROY_NODE(op2); + } + } + } +} + //------------------------------------------------------------------------ // fgOptimizeCastOnAssignment: Optimizes the supplied GT_ASG tree with a GT_CAST node. // From 6f71cff07aa1a5e52c4f1ea37c18bc85777595f4 Mon Sep 17 00:00:00 2001 From: TIHan Date: Tue, 15 Nov 2022 19:48:59 -0800 Subject: [PATCH 35/58] Add print for implicit cast --- src/coreclr/jit/valuenum.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 03fa15cccfd749..ccc0c6a80d440d 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8355,6 +8355,20 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree) // This means that there is an implicit cast on the rhs value // We will add a cast function to reflect the possible narrowing of the rhs value rhsVNPair = vnStore->VNPairForCast(rhsVNPair, lhs->TypeGet(), rhs->TypeGet()); + +#ifdef DEBUG + if (verbose) + { + printf("N%03u ", rhs->gtSeqNum); + printTreeID(rhs); + printf(" "); + gtDispNodeName(rhs); + printf(" (implicit cast) => "); + vnpPrint(rhsVNPair, 1); + printf("\n"); + } +#endif // DEBUG + } } From ca600555826cc798fd86b2f5aa22cd246ea926b4 Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 16 Nov 2022 09:49:57 -0800 Subject: [PATCH 36/58] Do it a little earlier --- src/coreclr/jit/morph.cpp | 14 +++++++++----- src/tests/JIT/opt/Regressions/Regression1.csproj | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 365e649e6962a9..2af66070fc697d 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11006,6 +11006,11 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } + if (cast->CastOp()->OperIsSimple()) + { + fgOptimizeCastOfSmpOp(cast); + } + // See if we can discard the cast. if (varTypeIsIntegral(cast) && varTypeIsIntegral(src)) { @@ -11084,11 +11089,6 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) } } - if (cast->CastOp()->OperIsSimple()) - { - fgOptimizeCastOfSmpOp(cast); - } - return cast; } @@ -11133,6 +11133,8 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) castOp->AsOp()->gtOp1 = op1->CastOp(); DEBUG_DESTROY_NODE(op1); + + //castOp->SetDoNotCSE(); } } @@ -11146,6 +11148,8 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) castOp->AsOp()->gtOp2 = op2->CastOp(); DEBUG_DESTROY_NODE(op2); + + //castOp->SetDoNotCSE(); } } } diff --git a/src/tests/JIT/opt/Regressions/Regression1.csproj b/src/tests/JIT/opt/Regressions/Regression1.csproj index f3e1cbd44b4041..c053e59c003e01 100644 --- a/src/tests/JIT/opt/Regressions/Regression1.csproj +++ b/src/tests/JIT/opt/Regressions/Regression1.csproj @@ -8,5 +8,8 @@ + + + From e44509232e43c464a9dc5b44dca12b2d95888ab1 Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 16 Nov 2022 10:02:27 -0800 Subject: [PATCH 37/58] Mark as do-not-cse to prevent the CAST from being removed on assignment --- src/coreclr/jit/morph.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 2af66070fc697d..d8da5b7716389f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11134,7 +11134,7 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) DEBUG_DESTROY_NODE(op1); - //castOp->SetDoNotCSE(); + cast->SetDoNotCSE(); } } @@ -11149,7 +11149,7 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) DEBUG_DESTROY_NODE(op2); - //castOp->SetDoNotCSE(); + cast->SetDoNotCSE(); } } } @@ -11212,6 +11212,11 @@ GenTree* Compiler::fgOptimizeCastOnAssignment(GenTreeOp* asg) if (genActualType(castFromType) == genActualType(castToType)) { + // If the cast is marked as do-not-cse, then + // we should not remove it to imply an implicit cast. + if (cast->gtFlags & GTF_DONT_CSE) + return asg; + // Removes the cast. asg->gtOp2 = cast->CastOp(); } From 4b9d34472467612666a73730a4c1f3411c2299d8 Mon Sep 17 00:00:00 2001 From: TIHan Date: Wed, 16 Nov 2022 18:40:47 -0800 Subject: [PATCH 38/58] Fix test --- src/tests/JIT/opt/Or/IntOr.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/JIT/opt/Or/IntOr.cs b/src/tests/JIT/opt/Or/IntOr.cs index bcc2673badb508..785481ce71e072 100644 --- a/src/tests/JIT/opt/Or/IntOr.cs +++ b/src/tests/JIT/opt/Or/IntOr.cs @@ -30,7 +30,7 @@ public static bool Test_UInt32_UInt32_CastByte_Or(uint x, uint y) } [MethodImpl(MethodImplOptions.NoInlining)] - public static unsafe bool Test_UInt32_ByRef_CastByte_CastByte_Or(uint x, ref uint y) + public static bool Test_UInt32_ByRef_CastByte_CastByte_Or(uint x, ref uint y) { // X64-NOT: movzx From 7022968ae94be2c794cdc38fa4d06ab39153b209 Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 17 Nov 2022 12:03:31 -0800 Subject: [PATCH 39/58] Remove do-not-set cse --- src/coreclr/jit/morph.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index d8da5b7716389f..0d576c71eebe27 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11133,8 +11133,6 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) castOp->AsOp()->gtOp1 = op1->CastOp(); DEBUG_DESTROY_NODE(op1); - - cast->SetDoNotCSE(); } } @@ -11148,8 +11146,6 @@ void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) castOp->AsOp()->gtOp2 = op2->CastOp(); DEBUG_DESTROY_NODE(op2); - - cast->SetDoNotCSE(); } } } @@ -11212,11 +11208,6 @@ GenTree* Compiler::fgOptimizeCastOnAssignment(GenTreeOp* asg) if (genActualType(castFromType) == genActualType(castToType)) { - // If the cast is marked as do-not-cse, then - // we should not remove it to imply an implicit cast. - if (cast->gtFlags & GTF_DONT_CSE) - return asg; - // Removes the cast. asg->gtOp2 = cast->CastOp(); } From ec5088f8c2511ff533e8d9e24d0868f2e38d565f Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 18 Nov 2022 11:47:16 -0800 Subject: [PATCH 40/58] Moving optimization to simple lowering again for correctness --- src/coreclr/jit/compiler.h | 3 +- src/coreclr/jit/flowgraph.cpp | 91 +++++++++++++++++++++++++++++++++++ src/coreclr/jit/morph.cpp | 64 ------------------------ 3 files changed, 93 insertions(+), 65 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index fbb9c6ea04d408..80d02420046afd 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -4664,6 +4664,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 @@ -5785,7 +5787,6 @@ class Compiler GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optAssertionPropDone = nullptr); void fgTryReplaceStructLocalWithField(GenTree* tree); GenTree* fgOptimizeCast(GenTreeCast* cast); - void fgOptimizeCastOfSmpOp(GenTreeCast* cast); GenTree* fgOptimizeCastOnAssignment(GenTreeOp* asg); GenTree* fgOptimizeEqualityComparisonWithConst(GenTreeOp* cmp); GenTree* fgOptimizeRelationalComparisonWithConst(GenTreeOp* cmp); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 4fc2b9b0d9d9e5..ae8423430b91e5 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3068,6 +3068,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. @@ -3126,6 +3135,88 @@ 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 (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; + } + } + +#if 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/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 0d576c71eebe27..2ea9f1225e3412 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -11006,11 +11006,6 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } - if (cast->CastOp()->OperIsSimple()) - { - fgOptimizeCastOfSmpOp(cast); - } - // See if we can discard the cast. if (varTypeIsIntegral(cast) && varTypeIsIntegral(src)) { @@ -11092,65 +11087,6 @@ GenTree* Compiler::fgOptimizeCast(GenTreeCast* cast) return cast; } -//------------------------------------------------------------------------ -// fgOptimizeCastOfSmpOp: Optimizes the supplied GT_CAST of GTK_SMPOP tree. -// -// Arguments: -// cast - the cast tree to optimize -// -void Compiler::fgOptimizeCastOfSmpOp(GenTreeCast* cast) -{ - GenTree* castOp = cast->CastOp(); - var_types castToType = cast->CastToType(); - var_types srcType = castOp->TypeGet(); - - assert(castOp->OperIsSimple()); - - if (gtIsActiveCSE_Candidate(cast) || gtIsActiveCSE_Candidate(castOp)) - return; - - if (cast->gtOverflow()) - return; - - if (castOp->OperMayOverflow() && castOp->gtOverflow()) - return; - - // Only optimize if the castToType is a small integer type. - if (!varTypeIsSmall(castToType) || !varTypeIsIntegral(srcType)) - return; - - if (castOp->OperIs(GT_ADD, GT_SUB, GT_MUL, GT_AND, GT_XOR, GT_OR, GT_NOT, GT_NEG)) - { - // This removes the casts as it prevents zero/sign-extended 'mov's before the op. - - if (castOp->gtGetOp1()->OperIs(GT_CAST)) - { - GenTreeCast* op1 = castOp->gtGetOp1()->AsCast(); - - if (!op1->gtOverflow() && (genActualType(op1->CastOp()) == genActualType(srcType)) && - (castToType == op1->CastToType()) && !gtIsActiveCSE_Candidate(op1)) - { - castOp->AsOp()->gtOp1 = op1->CastOp(); - - DEBUG_DESTROY_NODE(op1); - } - } - - if (castOp->OperIsBinary() && castOp->gtGetOp2()->OperIs(GT_CAST)) - { - GenTreeCast* op2 = castOp->gtGetOp2()->AsCast(); - - if (!op2->gtOverflow() && (genActualType(op2->CastOp()) == genActualType(srcType)) && - (castToType == op2->CastToType()) && !gtIsActiveCSE_Candidate(op2)) - { - castOp->AsOp()->gtOp2 = op2->CastOp(); - - DEBUG_DESTROY_NODE(op2); - } - } - } -} - //------------------------------------------------------------------------ // fgOptimizeCastOnAssignment: Optimizes the supplied GT_ASG tree with a GT_CAST node. // From 62938baeee56b6dbab149cfb20627c523912d365 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 18 Nov 2022 12:04:37 -0800 Subject: [PATCH 41/58] Disable some of the regression tests for mono --- src/tests/JIT/opt/Regressions/Regression2.csproj | 1 + src/tests/JIT/opt/Regressions/Regression3.csproj | 1 + src/tests/JIT/opt/Regressions/Regression4.csproj | 1 + 3 files changed, 3 insertions(+) diff --git a/src/tests/JIT/opt/Regressions/Regression2.csproj b/src/tests/JIT/opt/Regressions/Regression2.csproj index f3e1cbd44b4041..675d36d50e8db7 100644 --- a/src/tests/JIT/opt/Regressions/Regression2.csproj +++ b/src/tests/JIT/opt/Regressions/Regression2.csproj @@ -1,6 +1,7 @@ Exe + true None diff --git a/src/tests/JIT/opt/Regressions/Regression3.csproj b/src/tests/JIT/opt/Regressions/Regression3.csproj index f3e1cbd44b4041..675d36d50e8db7 100644 --- a/src/tests/JIT/opt/Regressions/Regression3.csproj +++ b/src/tests/JIT/opt/Regressions/Regression3.csproj @@ -1,6 +1,7 @@ Exe + true None diff --git a/src/tests/JIT/opt/Regressions/Regression4.csproj b/src/tests/JIT/opt/Regressions/Regression4.csproj index f3e1cbd44b4041..675d36d50e8db7 100644 --- a/src/tests/JIT/opt/Regressions/Regression4.csproj +++ b/src/tests/JIT/opt/Regressions/Regression4.csproj @@ -1,6 +1,7 @@ Exe + true None From e90ce8848cd1e9adb9e86fe1273ace81ab9d91d6 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 18 Nov 2022 12:34:48 -0800 Subject: [PATCH 42/58] Fixing IntOr disasm test --- src/tests/JIT/opt/Or/IntOr.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/JIT/opt/Or/IntOr.cs b/src/tests/JIT/opt/Or/IntOr.cs index 785481ce71e072..0a9f947f5e980f 100644 --- a/src/tests/JIT/opt/Or/IntOr.cs +++ b/src/tests/JIT/opt/Or/IntOr.cs @@ -19,7 +19,7 @@ public static bool Test_UInt32_UInt32_CastByte_Or(uint x, uint y) // X64-NOT: movzx // We expect 'or r8, r8'. - // X64: or [[REG0:[^x]+]], [[REG1:[^x]+]] + // X64: or [[REG0:[l|b]]], [[REG1:[l|b]]] if ((byte)((byte)x | y) == 0) { @@ -35,7 +35,7 @@ public static bool Test_UInt32_ByRef_CastByte_CastByte_Or(uint x, ref uint y) // X64-NOT: movzx // We expect 'or r8, m8'. - // X64: or [[REG0:[^x]+]], ptr + // X64: or [[REG0:[l|b]]], ptr if ((byte)((byte)x | (byte)y) == 0) { From 769d1c143b9697e21c310cef47a3e1b449c19d92 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 18 Nov 2022 13:01:07 -0800 Subject: [PATCH 43/58] Formatting, fixing test again --- src/coreclr/jit/flowgraph.cpp | 2 +- src/coreclr/jit/lower.cpp | 20 ++------------------ src/coreclr/jit/valuenum.cpp | 1 - src/tests/JIT/opt/Or/IntOr.cs | 4 ++-- 4 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index ae8423430b91e5..1e6fd3aeb9490d 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3203,7 +3203,7 @@ bool Compiler::fgSimpleLowerCastOfSmpOp(LIR::Range& range, GenTreeCast* cast) } } -#if DEBUG +#ifdef DEBUG if (madeChanges) { JITDUMP("Lower - Cast of Simple Op %s:\n", GenTree::OpName(cast->OperGet())); diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 7c197155eaccbc..198e02be5e780e 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -2935,24 +2935,8 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) if (castOp->OperIs(GT_OR, GT_XOR, GT_AND)) { - GenTree* op1 = castOp->gtGetOp1(); - GenTree* op2 = castOp->gtGetOp2(); - -#ifdef TARGET_XARCH - // If one of the ops is a GT_LCL_VAR, then change its type to the castToType. - // We do this to take advantage of containment for a memory op. - if (op1->OperIs(GT_LCL_VAR) && !op2->IsCnsIntOrI()) - { - op1->ChangeType(castToType); - } - else if (op2->OperIs(GT_LCL_VAR)) - { - op2->ChangeType(castToType); - } -#endif // TARGET_XARCH - - op1->ClearContained(); - op2->ClearContained(); + castOp->gtGetOp1()->ClearContained(); + castOp->gtGetOp2()->ClearContained(); ContainCheckBinary(castOp->AsOp()); } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index ccc0c6a80d440d..034f55b443027f 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8368,7 +8368,6 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree) printf("\n"); } #endif // DEBUG - } } diff --git a/src/tests/JIT/opt/Or/IntOr.cs b/src/tests/JIT/opt/Or/IntOr.cs index 0a9f947f5e980f..47e45392013b02 100644 --- a/src/tests/JIT/opt/Or/IntOr.cs +++ b/src/tests/JIT/opt/Or/IntOr.cs @@ -19,7 +19,7 @@ public static bool Test_UInt32_UInt32_CastByte_Or(uint x, uint y) // X64-NOT: movzx // We expect 'or r8, r8'. - // X64: or [[REG0:[l|b]]], [[REG1:[l|b]]] + // X64: or [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] if ((byte)((byte)x | y) == 0) { @@ -35,7 +35,7 @@ public static bool Test_UInt32_ByRef_CastByte_CastByte_Or(uint x, ref uint y) // X64-NOT: movzx // We expect 'or r8, m8'. - // X64: or [[REG0:[l|b]]], ptr + // X64: or [[REG0:[a-z]+[l|b]]], byte ptr if ((byte)((byte)x | (byte)y) == 0) { From 6903869677acf79c640ff4d9d1e812e550d1a9c3 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 18 Nov 2022 14:51:51 -0800 Subject: [PATCH 44/58] Smaller size operators can contain/reg-optional operands that are larger --- src/coreclr/jit/lower.h | 11 ++++++++--- src/coreclr/jit/lowerxarch.cpp | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 803dd890a8e84d..96430f3508be0c 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -283,9 +283,14 @@ class Lowering final : public Phase const unsigned operatorSize = genTypeSize(tree->TypeGet()); - const bool op1Legal = - isSafeToMarkOp1 && tree->OperIsCommutative() && (operatorSize == genTypeSize(op1->TypeGet())); - const bool op2Legal = isSafeToMarkOp2 && (operatorSize == genTypeSize(op2->TypeGet())); + // The operatorSize can be smaller than the operands as it will use a lower register/mem. + + const bool op1Legal = isSafeToMarkOp1 && tree->OperIsCommutative() && + ((operatorSize == genTypeSize(op1->TypeGet())) || + (varTypeIsIntegral(tree) && operatorSize < genTypeSize(op1->TypeGet()))); + const bool op2Legal = + isSafeToMarkOp2 && ((operatorSize == genTypeSize(op2->TypeGet())) || + (varTypeIsIntegral(tree) && operatorSize < genTypeSize(op2->TypeGet()))); GenTree* regOptionalOperand = nullptr; diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 40ccaebec0f938..d5aeb87bfc5b11 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -5772,7 +5772,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node) if (!binOpInRMW) { const unsigned operatorSize = genTypeSize(node->TypeGet()); - if ((genTypeSize(op2->TypeGet()) == operatorSize) && IsContainableMemoryOp(op2)) + if ((genTypeSize(op2->TypeGet()) >= operatorSize) && IsContainableMemoryOp(op2)) { isSafeToContainOp2 = IsSafeToContainMem(node, op2); if (isSafeToContainOp2) @@ -5791,7 +5791,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node) directlyEncodable = true; operand = op1; } - else if ((genTypeSize(op1->TypeGet()) == operatorSize) && IsContainableMemoryOp(op1)) + else if ((genTypeSize(op1->TypeGet()) >= operatorSize) && IsContainableMemoryOp(op1)) { isSafeToContainOp1 = IsSafeToContainMem(node, op1); if (isSafeToContainOp1) From de34d34c10a6f9739eef62c301f77a1cefef1154 Mon Sep 17 00:00:00 2001 From: TIHan Date: Fri, 18 Nov 2022 15:16:52 -0800 Subject: [PATCH 45/58] Formatting --- src/coreclr/jit/lower.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 96430f3508be0c..87ed1119c9f698 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -284,7 +284,7 @@ class Lowering final : public Phase const unsigned operatorSize = genTypeSize(tree->TypeGet()); // The operatorSize can be smaller than the operands as it will use a lower register/mem. - + const bool op1Legal = isSafeToMarkOp1 && tree->OperIsCommutative() && ((operatorSize == genTypeSize(op1->TypeGet())) || (varTypeIsIntegral(tree) && operatorSize < genTypeSize(op1->TypeGet()))); From f1ef6f3528571cf4778da700295e117a5f5a3b8c Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 28 Nov 2022 12:48:12 -0800 Subject: [PATCH 46/58] Added subtract tests --- src/tests/JIT/opt/Subtract/IntSubtract.cs | 154 ++++++++++++++++++ src/tests/JIT/opt/Subtract/IntSubtract.csproj | 17 ++ 2 files changed, 171 insertions(+) create mode 100644 src/tests/JIT/opt/Subtract/IntSubtract.cs create mode 100644 src/tests/JIT/opt/Subtract/IntSubtract.csproj diff --git a/src/tests/JIT/opt/Subtract/IntSubtract.cs b/src/tests/JIT/opt/Subtract/IntSubtract.cs new file mode 100644 index 00000000000000..4b4d865a94d850 --- /dev/null +++ b/src/tests/JIT/opt/Subtract/IntSubtract.cs @@ -0,0 +1,154 @@ +// 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: mov + // X64: sub + + return x - y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static uint UInt32_Subtract(uint x, uint y) + { + // X64: mov + // X64: sub + + return x - y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static long Int64_Subtract(long x, long y) + { + // X64: mov + // X64: sub + + return x - y; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ulong UInt64_Subtract(ulong x, ulong y) + { + // X64: mov + // X64: sub + + 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; + } + } +} \ No newline at end of file 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 + + + + + + From 38386d57243ed79bad0f09d7265edd0477c28b7a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 29 Nov 2022 10:47:46 -0800 Subject: [PATCH 47/58] Update IntAnd.cs --- src/tests/JIT/opt/And/IntAnd.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tests/JIT/opt/And/IntAnd.cs b/src/tests/JIT/opt/And/IntAnd.cs index af52e052529603..cb9b66d02c86e7 100644 --- a/src/tests/JIT/opt/And/IntAnd.cs +++ b/src/tests/JIT/opt/And/IntAnd.cs @@ -19,7 +19,7 @@ static bool Test_UInt32_UInt32_And(uint x, uint y) // X64-NOT: movzx // We expect 'and r8, r8'. - // X64: and [[REG0:[^x]+]], [[REG1:[^x]+]] + // X64: and [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] if ((byte)(x & y) == 0) { @@ -35,7 +35,7 @@ static bool Test_UInt32_UInt32_CastByte_And(uint x, uint y) // X64-NOT: movzx // We expect 'and r8, r8'. - // X64: and [[REG0:[^x]+]], [[REG1:[^x]+]] + // X64: and [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] if ((byte)((byte)x & y) == 0) { @@ -51,7 +51,7 @@ static bool Test_UInt32_UInt32_CastByte_CastByte_And(uint x, uint y) // X64-NOT: movzx // We expect 'and r8, r8'. - // X64: and [[REG0:[^x]+]], [[REG1:[^x]+]] + // X64: and [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] if ((byte)((byte)x & (byte)y) == 0) { @@ -170,4 +170,4 @@ static int Main() return 100; } } -} \ No newline at end of file +} From f34f9434d903608e4a769f246cd4230eddce6023 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 29 Nov 2022 14:20:35 -0800 Subject: [PATCH 48/58] Update IntAnd.cs --- src/tests/JIT/opt/And/IntAnd.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/tests/JIT/opt/And/IntAnd.cs b/src/tests/JIT/opt/And/IntAnd.cs index cb9b66d02c86e7..390c769a77db32 100644 --- a/src/tests/JIT/opt/And/IntAnd.cs +++ b/src/tests/JIT/opt/And/IntAnd.cs @@ -18,8 +18,8 @@ static bool Test_UInt32_UInt32_And(uint x, uint y) { // X64-NOT: movzx - // We expect 'and r8, r8'. - // X64: and [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] + // We expect 'and reg8, reg8'. + // X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} if ((byte)(x & y) == 0) { @@ -34,8 +34,8 @@ static bool Test_UInt32_UInt32_CastByte_And(uint x, uint y) { // X64-NOT: movzx - // We expect 'and r8, r8'. - // X64: and [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] + // We expect 'and reg8, reg8'. + // X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} if ((byte)((byte)x & y) == 0) { @@ -50,8 +50,8 @@ static bool Test_UInt32_UInt32_CastByte_CastByte_And(uint x, uint y) { // X64-NOT: movzx - // We expect 'and r8, r8'. - // X64: and [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] + // We expect 'and reg8, reg8'. + // X64: and {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} if ((byte)((byte)x & (byte)y) == 0) { @@ -165,8 +165,6 @@ static int Main() if (Test_UInt32_UInt32_CastByte_CastByte_And(0b10000000000000000000000000000010, 0b00100000000000000000000000000010)) return 0; - Console.Write("Succeeded"); - return 100; } } From 232fe855d5d749670e820a14a045aa6d87bd46a0 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 29 Nov 2022 14:21:36 -0800 Subject: [PATCH 49/58] Update IntOr.cs --- src/tests/JIT/opt/Or/IntOr.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tests/JIT/opt/Or/IntOr.cs b/src/tests/JIT/opt/Or/IntOr.cs index 47e45392013b02..7ea7d89b76f2b6 100644 --- a/src/tests/JIT/opt/Or/IntOr.cs +++ b/src/tests/JIT/opt/Or/IntOr.cs @@ -18,8 +18,8 @@ public static bool Test_UInt32_UInt32_CastByte_Or(uint x, uint y) { // X64-NOT: movzx - // We expect 'or r8, r8'. - // X64: or [[REG0:[a-z]+[l|b]]], [[REG1:[a-z]+[l|b]]] + // We expect 'or reg8, reg8'. + // X64: or {{[a-z]+[l|b]}}, {{[a-z]+[l|b]}} if ((byte)((byte)x | y) == 0) { @@ -34,8 +34,8 @@ public static bool Test_UInt32_ByRef_CastByte_CastByte_Or(uint x, ref uint y) { // X64-NOT: movzx - // We expect 'or r8, m8'. - // X64: or [[REG0:[a-z]+[l|b]]], byte ptr + // We expect 'or reg8, mem8'. + // X64: or {{[a-z]+[l|b]}}, byte ptr if ((byte)((byte)x | (byte)y) == 0) { @@ -109,4 +109,4 @@ static int Main() return 100; } } -} \ No newline at end of file +} From 476143eae1825da9658970d95c86944ee7bc8699 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Tue, 29 Nov 2022 18:00:56 -0800 Subject: [PATCH 50/58] Update IntSubtract.cs --- src/tests/JIT/opt/Subtract/IntSubtract.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tests/JIT/opt/Subtract/IntSubtract.cs b/src/tests/JIT/opt/Subtract/IntSubtract.cs index 4b4d865a94d850..fd9e8042beda15 100644 --- a/src/tests/JIT/opt/Subtract/IntSubtract.cs +++ b/src/tests/JIT/opt/Subtract/IntSubtract.cs @@ -57,8 +57,11 @@ static ushort UInt16_Subtract(ushort x, ushort y) [MethodImpl(MethodImplOptions.NoInlining)] static int Int32_Subtract(int x, int y) { - // X64: mov + // X64-NOT: movsx + // X64: sub + + // X64-NOT: movsx return x - y; } @@ -66,8 +69,11 @@ static int Int32_Subtract(int x, int y) [MethodImpl(MethodImplOptions.NoInlining)] static uint UInt32_Subtract(uint x, uint y) { - // X64: mov + // X64-NOT: movzx + // X64: sub + + // X64-NOT: movzx return x - y; } @@ -75,8 +81,11 @@ static uint UInt32_Subtract(uint x, uint y) [MethodImpl(MethodImplOptions.NoInlining)] static long Int64_Subtract(long x, long y) { - // X64: mov + // X64-NOT: movsx + // X64: sub + + // X64-NOT: movsx return x - y; } @@ -84,8 +93,11 @@ static long Int64_Subtract(long x, long y) [MethodImpl(MethodImplOptions.NoInlining)] static ulong UInt64_Subtract(ulong x, ulong y) { - // X64: mov + // X64-NOT: movzx + // X64: sub + + // X64-NOT: movzx return x - y; } @@ -151,4 +163,4 @@ static int Main() return 100; } } -} \ No newline at end of file +} From e0d49b319eb7ed4ff2875f0e6ecda90d20e1d0fc Mon Sep 17 00:00:00 2001 From: TIHan Date: Thu, 1 Dec 2022 16:54:52 -0800 Subject: [PATCH 51/58] Added IsContainableMemoryOpSize --- src/coreclr/jit/lower.cpp | 14 +++----------- src/coreclr/jit/lower.h | 31 ++++++++++++++++++++++++++----- src/coreclr/jit/lowerxarch.cpp | 5 ++--- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 6eb3cc4ccbc95c..198e02be5e780e 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -2935,17 +2935,9 @@ GenTree* Lowering::OptimizeConstCompare(GenTree* cmp) if (castOp->OperIs(GT_OR, GT_XOR, GT_AND)) { - GenTree* op1 = castOp->gtGetOp1(); - if ((op1 != nullptr) && !op1->IsCnsIntOrI()) - { - op1->ClearContained(); - } - - GenTree* op2 = castOp->gtGetOp2(); - if ((op2 != nullptr) && !op2->IsCnsIntOrI()) - { - op2->ClearContained(); - } + castOp->gtGetOp1()->ClearContained(); + castOp->gtGetOp2()->ClearContained(); + ContainCheckBinary(castOp->AsOp()); } cmp->AsOp()->gtOp1 = castOp; diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 803dd890a8e84d..0b085da8f696f8 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -281,11 +281,8 @@ class Lowering final : public Phase GenTree* const op1 = tree->gtGetOp1(); GenTree* const op2 = tree->gtGetOp2(); - const unsigned operatorSize = genTypeSize(tree->TypeGet()); - - const bool op1Legal = - isSafeToMarkOp1 && tree->OperIsCommutative() && (operatorSize == genTypeSize(op1->TypeGet())); - const bool op2Legal = isSafeToMarkOp2 && (operatorSize == genTypeSize(op2->TypeGet())); + const bool op1Legal = isSafeToMarkOp1 && tree->OperIsCommutative() && IsContainableMemoryOpSize(tree, op1); + const bool op2Legal = isSafeToMarkOp2 && IsContainableMemoryOpSize(tree, op2); GenTree* regOptionalOperand = nullptr; @@ -447,6 +444,30 @@ class Lowering final : public Phase return m_lsra->isContainableMemoryOp(node); } + // Return true if 'childNode' is a containable memory op by its size relative to the 'parentNode'. + // Currently very conservative. + bool IsContainableMemoryOpSize(GenTree* parentNode, GenTree* childNode) const + { + if (parentNode->OperIsBinary()) + { + const unsigned operatorSize = genTypeSize(parentNode->TypeGet()); + +#ifdef TARGET_XARCH + + // Conservative - only do this for AND, OR, XOR. + if (parentNode->OperIs(GT_AND, GT_OR, GT_XOR)) + { + return genTypeSize(childNode->TypeGet()) >= operatorSize; + } + +#endif // TARGET_XARCH + + return genTypeSize(childNode->TypeGet()) == operatorSize; + } + + return false; + } + #ifdef TARGET_ARM64 bool IsContainableBinaryOp(GenTree* parentNode, GenTree* childNode) const; #endif // TARGET_ARM64 diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 40ccaebec0f938..c385b5c93e0ccb 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -5771,8 +5771,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node) binOpInRMW = IsBinOpInRMWStoreInd(node); if (!binOpInRMW) { - const unsigned operatorSize = genTypeSize(node->TypeGet()); - if ((genTypeSize(op2->TypeGet()) == operatorSize) && IsContainableMemoryOp(op2)) + if (IsContainableMemoryOpSize(node, op2) && IsContainableMemoryOp(op2)) { isSafeToContainOp2 = IsSafeToContainMem(node, op2); if (isSafeToContainOp2) @@ -5791,7 +5790,7 @@ void Lowering::ContainCheckBinary(GenTreeOp* node) directlyEncodable = true; operand = op1; } - else if ((genTypeSize(op1->TypeGet()) == operatorSize) && IsContainableMemoryOp(op1)) + else if (IsContainableMemoryOpSize(node, op1) && IsContainableMemoryOp(op1)) { isSafeToContainOp1 = IsSafeToContainMem(node, op1); if (isSafeToContainOp1) From 23427f67b467b1df045b311136f488892273e1a4 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 13 Jan 2023 14:29:57 -0800 Subject: [PATCH 52/58] Removed unnecessary code. --- src/coreclr/jit/valuenum.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 90016fdce5bc54..8e2c86b2d73cdd 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8393,19 +8393,6 @@ void Compiler::fgValueNumberAssignment(GenTreeOp* tree) // This means that there is an implicit cast on the rhs value // We will add a cast function to reflect the possible narrowing of the rhs value rhsVNPair = vnStore->VNPairForCast(rhsVNPair, lhs->TypeGet(), rhs->TypeGet()); - -#ifdef DEBUG - if (verbose) - { - printf("N%03u ", rhs->gtSeqNum); - printTreeID(rhs); - printf(" "); - gtDispNodeName(rhs); - printf(" (implicit cast) => "); - vnpPrint(rhsVNPair, 1); - printf("\n"); - } -#endif // DEBUG } } From 520be33a5a3241d6f4e8020b73273cb74c6cdbff Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 13 Jan 2023 14:52:06 -0800 Subject: [PATCH 53/58] Update IntAnd.cs --- src/tests/JIT/opt/And/IntAnd.cs | 66 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/tests/JIT/opt/And/IntAnd.cs b/src/tests/JIT/opt/And/IntAnd.cs index 390c769a77db32..034cc51169393e 100644 --- a/src/tests/JIT/opt/And/IntAnd.cs +++ b/src/tests/JIT/opt/And/IntAnd.cs @@ -64,105 +64,105 @@ static bool Test_UInt32_UInt32_CastByte_CastByte_And(uint x, uint y) static int Main() { // No CastByte - if (!Test_UInt32_UInt32_And(0b10000000000000000000000000000000, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000001, 0b10000000000000000000000000000000)) + 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(0b10000000000000000000000000000000, 0b10000000000000000000000000000000)) + 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(0b10000000000000000000000000000000, 0b00000000000000000000000000000000)) + 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(0b00000000000000000000000000000000, 0b10000000000000000000000000000000)) + 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(0b00000000000000000000000000000001, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000010, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000010, 0b00000000000000000000000000000010)) + 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(0b10000000000000000000000000000010, 0b10000000000000000000000000000010)) + 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(0b00100000000000000000000000000010, 0b10000000000000000000000000000010)) + 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(0b10000000000000000000000000000010, 0b00100000000000000000000000000010)) + 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(0b10000000000000000000000000000000, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000001, 0b10000000000000000000000000000000)) + 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(0b10000000000000000000000000000000, 0b10000000000000000000000000000000)) + 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(0b10000000000000000000000000000000, 0b00000000000000000000000000000000)) + 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(0b00000000000000000000000000000000, 0b10000000000000000000000000000000)) + 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(0b00000000000000000000000000000001, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000010, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000010, 0b00000000000000000000000000000010)) + 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(0b10000000000000000000000000000010, 0b10000000000000000000000000000010)) + 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(0b00100000000000000000000000000010, 0b10000000000000000000000000000010)) + 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(0b10000000000000000000000000000010, 0b00100000000000000000000000000010)) + 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(0b10000000000000000000000000000000, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000001, 0b10000000000000000000000000000000)) + 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(0b10000000000000000000000000000000, 0b10000000000000000000000000000000)) + 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(0b10000000000000000000000000000000, 0b00000000000000000000000000000000)) + 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(0b00000000000000000000000000000000, 0b10000000000000000000000000000000)) + 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(0b00000000000000000000000000000001, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000010, 0b00000000000000000000000000000001)) + 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(0b00000000000000000000000000000010, 0b00000000000000000000000000000010)) + 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(0b10000000000000000000000000000010, 0b10000000000000000000000000000010)) + 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(0b00100000000000000000000000000010, 0b10000000000000000000000000000010)) + 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(0b10000000000000000000000000000010, 0b00100000000000000000000000000010)) + 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; return 100; From 7cac5e24486d71f7a21bcac417c00d762507f421 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 13 Jan 2023 14:59:22 -0800 Subject: [PATCH 54/58] Update Regression1.cs --- src/tests/JIT/opt/Divide/Regressions/Regression1.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1.cs b/src/tests/JIT/opt/Divide/Regressions/Regression1.cs index 987e06ca973208..def6bbb9b62141 100644 --- a/src/tests/JIT/opt/Divide/Regressions/Regression1.cs +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1.cs @@ -190,7 +190,7 @@ public static int Main() var result17 = Test17(); var result18 = Test18(0x10000); var result19 = Test19(0x10000, 2); - var result20 = Test20(unchecked((short)0x10000), 2); + var result20 = Test20(0, 2); if (result1 != 0) return 0; @@ -254,4 +254,4 @@ public static int Main() return 100; } -} \ No newline at end of file +} From c6a31fe14ca5340bd46aaaf4999331d64a2d0a01 Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 13 Jan 2023 14:59:57 -0800 Subject: [PATCH 55/58] Update Regression1.cs --- src/tests/JIT/opt/Divide/Regressions/Regression1.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/JIT/opt/Divide/Regressions/Regression1.cs b/src/tests/JIT/opt/Divide/Regressions/Regression1.cs index def6bbb9b62141..0e6b0bf7313721 100644 --- a/src/tests/JIT/opt/Divide/Regressions/Regression1.cs +++ b/src/tests/JIT/opt/Divide/Regressions/Regression1.cs @@ -6,7 +6,7 @@ public class Program { -[MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.NoInlining)] public static ushort GetUShortValue() { return 24648; From 2fbe82937b8aea474758a2a35cb831e6c53fcb7a Mon Sep 17 00:00:00 2001 From: Will Smith Date: Fri, 13 Jan 2023 15:07:19 -0800 Subject: [PATCH 56/58] Update Regression2.cs --- src/tests/JIT/opt/Regressions/Regression2.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/tests/JIT/opt/Regressions/Regression2.cs b/src/tests/JIT/opt/Regressions/Regression2.cs index d5c8bf737cdebb..83448de695e3e0 100644 --- a/src/tests/JIT/opt/Regressions/Regression2.cs +++ b/src/tests/JIT/opt/Regressions/Regression2.cs @@ -54,14 +54,11 @@ static void Mul_SmallType_Correctness() [MethodImpl(MethodImplOptions.NoInlining)] static void Div_SmallType_Correctness() { - for (int i = 0; i < ushort.MaxValue + 1; i++) + for (int i = 1; i < ushort.MaxValue + 1; i++) { - for (int j = 1; j < ushort.MaxValue + 1; j++) + if ((byte)(0 / i) != (byte)((byte)0 / (byte)i)) { - if ((byte)(i / j) != (byte)((byte)i / (byte)j)) - { - throw new Exception(); - } + throw new Exception(); } } } From 3384759e5379e8ad8dc18ed4686532ba7cf2786b Mon Sep 17 00:00:00 2001 From: Will Smith Date: Sat, 14 Jan 2023 10:32:23 -0800 Subject: [PATCH 57/58] Update Regression2.cs --- src/tests/JIT/opt/Regressions/Regression2.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/tests/JIT/opt/Regressions/Regression2.cs b/src/tests/JIT/opt/Regressions/Regression2.cs index 83448de695e3e0..5e8f36a4730ab8 100644 --- a/src/tests/JIT/opt/Regressions/Regression2.cs +++ b/src/tests/JIT/opt/Regressions/Regression2.cs @@ -52,14 +52,11 @@ static void Mul_SmallType_Correctness() } [MethodImpl(MethodImplOptions.NoInlining)] - static void Div_SmallType_Correctness() + static void Div_SmallType_Correctness(int i, int j) { - for (int i = 1; i < ushort.MaxValue + 1; i++) + if ((byte)(i / j) != (byte)((byte)i / (byte)j)) { - if ((byte)(0 / i) != (byte)((byte)0 / (byte)i)) - { - throw new Exception(); - } + throw new Exception(); } } @@ -71,7 +68,7 @@ static int Main() try { - Div_SmallType_Correctness(); + Div_SmallType_Correctness(2, 256); } catch(DivideByZeroException) {} From 40b59c2b6799ef7382f990e063cbf039847dfefe Mon Sep 17 00:00:00 2001 From: TIHan Date: Mon, 30 Jan 2023 10:12:31 -0800 Subject: [PATCH 58/58] Disable if optimizations are disabled --- src/coreclr/jit/flowgraph.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 8c25e26f8e49a4..40c72bfccf5a88 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3080,6 +3080,9 @@ bool Compiler::fgSimpleLowerCastOfSmpOp(LIR::Range& range, GenTreeCast* cast) assert(castOp->OperIsSimple()); + if (opts.OptimizationDisabled()) + return false; + if (cast->gtOverflow()) return false;