From 63b9dbe8d02aa1e3d9a289cf75cfe956aad3dd6e Mon Sep 17 00:00:00 2001 From: EgorBo Date: Sun, 17 Nov 2024 20:03:41 +0100 Subject: [PATCH 1/3] JIT: (x u>> cns) -> [0..(x's max value >> cns)] --- src/coreclr/jit/rangecheck.cpp | 33 ++++++++++++++++--- .../Formats/Cbor/HalfHelpers.netstandard.cs | 9 ++--- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 1fb068068e845..4b1f67142da52 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1014,13 +1014,13 @@ void RangeCheck::MergeAssertion(BasicBlock* block, GenTree* op, Range* pRange DE // Compute the range for a binary operation. Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool monIncreasing DEBUGARG(int indent)) { - assert(binop->OperIs(GT_ADD, GT_AND, GT_RSH, GT_LSH, GT_UMOD, GT_MUL)); + assert(binop->OperIs(GT_ADD, GT_AND, GT_RSH, GT_RSZ, GT_LSH, GT_UMOD, GT_MUL)); GenTree* op1 = binop->gtGetOp1(); GenTree* op2 = binop->gtGetOp2(); // Special cases for binops where op2 is a constant - if (binop->OperIs(GT_AND, GT_RSH, GT_LSH, GT_UMOD)) + if (binop->OperIs(GT_AND, GT_RSH, GT_RSZ, GT_LSH, GT_UMOD)) { if (!op2->IsIntCnsFitsInI32()) { @@ -1049,6 +1049,29 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool icon = binop->OperIs(GT_RSH) ? (icon1 >> icon2) : (icon1 << icon2); } } + else if (binop->OperIs(GT_RSZ)) + { + // (x u>> cns) -> [0..(x's max value >> cns)] + int shiftBy = static_cast(op2->AsIntCon()->IconValue()); + if (shiftBy < 0) + { + return Range(Limit::keUnknown); + } + + int op1Width = (int)(genTypeSize(op1) * BITS_PER_BYTE); + if (shiftBy >= op1Width) + { + return Range(Limit(Limit::keConstant, 0)); + } + + // Calculate max possible value of op1, e.g. UINT_MAX for TYP_INT/TYP_UINT + uint64_t maxValue = 0; + for (int bit = 0; bit < op1Width; bit++) + { + maxValue |= 1ULL << bit; + } + icon = (int)(maxValue >> static_cast(op2->AsIntCon()->IconValue())); + } if (icon >= 0) { @@ -1064,7 +1087,7 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool } // other operators are expected to be handled above. - assert(binop->OperIs(GT_ADD, GT_MUL, GT_LSH, GT_RSH)); + assert(binop->OperIs(GT_ADD, GT_MUL, GT_LSH, GT_RSH, GT_RSZ)); Range* op1RangeCached = nullptr; Range op1Range = Limit(Limit::keUndef); @@ -1429,7 +1452,7 @@ bool RangeCheck::ComputeDoesOverflow(BasicBlock* block, GenTree* expr, const Ran } // These operators don't overflow. // Actually, GT_LSH can overflow so it depends on the analysis done in ComputeRangeForBinOp - else if (expr->OperIs(GT_AND, GT_RSH, GT_LSH, GT_UMOD, GT_NEG)) + else if (expr->OperIs(GT_AND, GT_RSH, GT_RSZ, GT_LSH, GT_UMOD, GT_NEG)) { overflows = false; } @@ -1523,7 +1546,7 @@ Range RangeCheck::ComputeRange(BasicBlock* block, GenTree* expr, bool monIncreas MergeAssertion(block, expr, &range DEBUGARG(indent + 1)); } // compute the range for binary operation - else if (expr->OperIs(GT_ADD, GT_AND, GT_RSH, GT_LSH, GT_UMOD, GT_MUL)) + else if (expr->OperIs(GT_ADD, GT_AND, GT_RSH, GT_RSZ, GT_LSH, GT_UMOD, GT_MUL)) { range = ComputeRangeForBinOp(block, expr->AsOp(), monIncreasing DEBUGARG(indent + 1)); } diff --git a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netstandard.cs b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netstandard.cs index 67d3c6afd31e6..d28b9b6f52728 100644 --- a/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netstandard.cs +++ b/src/libraries/System.Formats.Cbor/src/System/Formats/Cbor/HalfHelpers.netstandard.cs @@ -107,12 +107,9 @@ private static int Log2SoftwareFallback(uint value) value |= value >> 08; value |= value >> 16; - // uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check - return Unsafe.AddByteOffset( - // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u - ref MemoryMarshal.GetReference(Log2DeBruijn), - // uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here - (IntPtr)(int)((value * 0x07C4ACDDu) >> 27)); + // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u + // uint.MaxValue >> 27 is always in range [0 - 31] so no bounds check + return Log2DeBruijn[(int)((value * 0x07C4ACDDu) >> 27)]; } private static float CreateSingleNaN(bool sign, ulong significand) From 51f0fa854d87a19af46d79ce745988ce5dac750d Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 17 Nov 2024 20:29:21 +0100 Subject: [PATCH 2/3] Update src/coreclr/jit/rangecheck.cpp Co-authored-by: Theodore Tsirpanis --- src/coreclr/jit/rangecheck.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 4b1f67142da52..8709ba5bdd526 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1065,11 +1065,7 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool } // Calculate max possible value of op1, e.g. UINT_MAX for TYP_INT/TYP_UINT - uint64_t maxValue = 0; - for (int bit = 0; bit < op1Width; bit++) - { - maxValue |= 1ULL << bit; - } + uint64_t maxValue = (1ULL << op1Width) - 1; icon = (int)(maxValue >> static_cast(op2->AsIntCon()->IconValue())); } From c1194d0b52e90ccecbcdc0bc08cc445c69a7f167 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 17 Nov 2024 20:38:21 +0100 Subject: [PATCH 3/3] Update rangecheck.cpp --- src/coreclr/jit/rangecheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 8709ba5bdd526..abc22d335fa6e 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -1066,7 +1066,7 @@ Range RangeCheck::ComputeRangeForBinOp(BasicBlock* block, GenTreeOp* binop, bool // Calculate max possible value of op1, e.g. UINT_MAX for TYP_INT/TYP_UINT uint64_t maxValue = (1ULL << op1Width) - 1; - icon = (int)(maxValue >> static_cast(op2->AsIntCon()->IconValue())); + icon = (int)(maxValue >> static_cast(op2->AsIntCon()->IconValue())); } if (icon >= 0)