diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 37cca461351f49..6aa8861290be20 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -4278,31 +4278,33 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea) else { #ifdef TARGET_ARM64 - if (index->isContained()) { + // Only BFIZ/CAST nodes should be present for contained index on ARM64. + GenTreeCast* cast; if (index->OperIs(GT_BFIZ)) { - // Handle LEA with "contained" BFIZ assert(scale == 0); scale = (DWORD)index->gtGetOp2()->AsIntConCommon()->IconValue(); - index = index->gtGetOp1()->gtGetOp1(); - } - else if (index->OperIs(GT_CAST)) - { - index = index->AsCast()->gtGetOp1(); + cast = index->gtGetOp1()->AsCast(); } else { - // Only BFIZ/CAST nodes should be present for for contained index on ARM64. - // If there are more, we need to handle them here. - unreached(); + cast = index->AsCast(); } + + // The 32-bit index has to be sign/zero-extended as part of the add. + assert(genActualTypeIsInt(cast->CastOp()) && (scale <= 4)); + emit->emitIns_R_R_R_I(INS_add, size, lea->GetRegNum(), memBase->GetRegNum(), + cast->CastOp()->GetRegNum(), scale, + cast->IsUnsigned() ? INS_OPTS_UXTW : INS_OPTS_SXTW); } + else #endif - - // Then compute target reg from [base + index*scale] - genScaledAdd(size, lea->GetRegNum(), memBase->GetRegNum(), index->GetRegNum(), scale); + { + // Then compute target reg from [base + index*scale] + genScaledAdd(size, lea->GetRegNum(), memBase->GetRegNum(), index->GetRegNum(), scale); + } } } else if (lea->HasBase()) diff --git a/src/coreclr/jit/rangecheck.cpp b/src/coreclr/jit/rangecheck.cpp index 0857982031fa66..b1f71b2a41874e 100644 --- a/src/coreclr/jit/rangecheck.cpp +++ b/src/coreclr/jit/rangecheck.cpp @@ -599,6 +599,12 @@ Range RangeCheck::GetRangeFromAssertionsWorker( #endif } + if (varTypeIsFloating(vnType)) + { + // Integer ranges don't describe floating point values (e.g. int->float casts may round). + return Limit(Limit::keUnknown); + } + // // First, let's see if we can tighten the range based on VN information. // @@ -2144,9 +2150,11 @@ bool RangeCheck::DoesVarDefOverflow(BasicBlock* block, GenTreeLclVarCommon* lcl, // But only if the range from the assertion is more strict than the global // range computed; otherwise we might still have used the def's value to - // tighten the range of the global range. + // tighten the range of the global range. A dependent limit doesn't qualify: + // Widen resolves it later assuming the defs don't overflow. Range merged = RangeOps::Merge(range, assertionRange, false); - if (merged.LowerLimit().Equals(range.LowerLimit()) && merged.UpperLimit().Equals(range.UpperLimit())) + if (!range.LowerLimit().IsDependent() && !range.UpperLimit().IsDependent() && + merged.LowerLimit().Equals(range.LowerLimit()) && merged.UpperLimit().Equals(range.UpperLimit())) { return false; } diff --git a/src/coreclr/jit/rangecheck.h b/src/coreclr/jit/rangecheck.h index 64c38ac7b687b9..ffbbf09c9d4538 100644 --- a/src/coreclr/jit/rangecheck.h +++ b/src/coreclr/jit/rangecheck.h @@ -442,9 +442,13 @@ struct RangeOps // For RSZ by N >= 1, result is in [0, UINT_MAX >> N] regardless of r1's signedness. // When r1 isn't proven non-negative, the bound above is unsound (negative r1 reinterprets // as large unsigned), so override with the type-based bound. - if (logical && (r2.LowerLimit().GetConstant() >= 1) && - !(r1.LowerLimit().IsConstant() && (r1.LowerLimit().GetConstant() >= 0))) + if (logical && !(r1.LowerLimit().IsConstant() && (r1.LowerLimit().GetConstant() >= 0))) { + if (r2.LowerLimit().GetConstant() == 0) + { + // A shift by 0 may preserve a negative r1, so nothing is known. + return Limit(Limit::keUnknown); + } result.lLimit = Limit(Limit::keConstant, 0); result.uLimit = Limit(Limit::keConstant, (int)(UINT32_MAX >> r2.LowerLimit().GetConstant())); } diff --git a/src/tests/JIT/Regression_ro_2/Runtime_134451.cs b/src/tests/JIT/Regression_ro_2/Runtime_134451.cs new file mode 100644 index 00000000000000..294c3edc4c5b7c --- /dev/null +++ b/src/tests/JIT/Regression_ro_2/Runtime_134451.cs @@ -0,0 +1,33 @@ +// 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; +using Xunit; + +public class Runtime_134451 +{ + [Fact] + public static void TestEntryPoint() + { + Assert.Throws(() => Test(new int[101], true)); + } + + // "x" wraps around, so "j += x" is not monotonic and "j" becomes negative. + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static int Test(int[] a, bool flag) + { + int j = 0, x = int.MaxValue - 10, sum = 0; + if (a.Length != 101) + return 0; + while (j < 101) + { + if (flag) + sum += a[j]; + x++; + if (x >= int.MinValue + 1 && x <= 10) + j += x; + } + return sum; + } +} diff --git a/src/tests/JIT/Regression_ro_2/Runtime_134452.cs b/src/tests/JIT/Regression_ro_2/Runtime_134452.cs new file mode 100644 index 00000000000000..38bca98c6a189f --- /dev/null +++ b/src/tests/JIT/Regression_ro_2/Runtime_134452.cs @@ -0,0 +1,52 @@ +// 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; +using Xunit; + +public class Runtime_134452 +{ + [Fact] + [SkipOnMono("CoreCLR JIT regression test")] + public static void TestEntryPoint() + { + Assert.Equal(1, IntToFloatRounding(16777219)); + Assert.Equal(1, DoubleToUIntSaturation(-5)); + Assert.Throws(() => IndexThroughFloat(new byte[16777220], 16777219)); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static int IntToFloatRounding(int x) + { + if (x >= 0 && x <= 16777219) + { + // (float)16777219 rounds to 16777220 + int y = (int)(float)x; + if (y > 16777219) + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static int DoubleToUIntSaturation(int x) + { + if (x < 0) + { + // Saturates to 0 + uint u = (uint)(double)x; + if (u == 0) + return 1; + } + return 0; + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static int IndexThroughFloat(byte[] arr, int x) + { + if (arr.Length == 16777220 && x >= 0 && x <= 16777219) + return arr[(int)(float)x]; + return -1; + } +} diff --git a/src/tests/JIT/Regression_ro_2/Runtime_134455.cs b/src/tests/JIT/Regression_ro_2/Runtime_134455.cs new file mode 100644 index 00000000000000..b112d54b04f657 --- /dev/null +++ b/src/tests/JIT/Regression_ro_2/Runtime_134455.cs @@ -0,0 +1,31 @@ +// 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; +using Xunit; + +public class Runtime_134455 +{ + [Fact] + public static void TestEntryPoint() + { + Assert.Throws(() => Test(-1, 1)); + Assert.Equal(0, Test(-1, 0)); + Assert.Equal(0, Test(50, 0)); + } + + // "x >>> (y & 3)" is a large positive value for negative "x". + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + private static int Test(int x, int y) + { + int[] a = new int[51]; + if (x <= 50) + { + int v = x >>> (y & 3); + if (v >= 0) + return a[v]; + } + return 0; + } +}