// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/compiler/backend/range_analysis.h" #include "vm/compiler/backend/il_test_helper.h" #include "vm/unit_test.h" namespace dart { TEST_CASE(RangeTests) { Range* zero = new Range(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(0)); Range* positive = new Range(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(100)); Range* negative = new Range(RangeBoundary::FromConstant(-1), RangeBoundary::FromConstant(-100)); Range* range_x = new Range(RangeBoundary::FromConstant(-15), RangeBoundary::FromConstant(100)); EXPECT(positive->IsPositive()); EXPECT(zero->Overlaps(0, 0)); EXPECT(positive->Overlaps(0, 0)); EXPECT(!negative->Overlaps(0, 0)); EXPECT(range_x->Overlaps(0, 0)); EXPECT(range_x->IsWithin(-15, 100)); EXPECT(!range_x->IsWithin(-15, 99)); EXPECT(!range_x->IsWithin(-14, 100)); #define TEST_RANGE_OP_(Op, l_min, l_max, r_min, r_max, Clamp, res_min, \ res_max) \ { \ RangeBoundary min, max; \ Range* left_range = new Range(RangeBoundary::FromConstant(l_min), \ RangeBoundary::FromConstant(l_max)); \ Range* shift_range = new Range(RangeBoundary::FromConstant(r_min), \ RangeBoundary::FromConstant(r_max)); \ Op(left_range, shift_range, &min, &max); \ min = Clamp(min); \ max = Clamp(max); \ EXPECT(min.Equals(res_min)); \ if (FLAG_support_il_printer && !min.Equals(res_min)) { \ OS::PrintErr("%s\n", min.ToCString()); \ } \ EXPECT(max.Equals(res_max)); \ if (FLAG_support_il_printer && !max.Equals(res_max)) { \ OS::PrintErr("%s\n", max.ToCString()); \ } \ } #define NO_CLAMP(b) (b) #define TEST_RANGE_OP(Op, l_min, l_max, r_min, r_max, result_min, result_max) \ TEST_RANGE_OP_(Op, l_min, l_max, r_min, r_max, NO_CLAMP, result_min, \ result_max) #define CLAMP_TO_SMI(b) (b.Clamp(Range::Smi())) #define TEST_RANGE_OP_SMI(Op, l_min, l_max, r_min, r_max, res_min, res_max) \ TEST_RANGE_OP_(Op, l_min, l_max, r_min, r_max, CLAMP_TO_SMI, res_min, res_max) TEST_RANGE_OP(Range::Shl, -15, 100, 0, 2, RangeBoundary(-60), RangeBoundary(400)); TEST_RANGE_OP(Range::Shl, -15, 100, -2, 2, RangeBoundary(-60), RangeBoundary(400)); TEST_RANGE_OP(Range::Shl, -15, -10, 1, 2, RangeBoundary(-60), RangeBoundary(-20)); TEST_RANGE_OP(Range::Shl, 5, 10, -2, 2, RangeBoundary(5), RangeBoundary(40)); TEST_RANGE_OP(Range::Shl, -15, 100, 0, 64, RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); TEST_RANGE_OP(Range::Shl, -1, 1, 63, 63, RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); if (compiler::target::kSmiBits == 62) { TEST_RANGE_OP_SMI(Range::Shl, -1, 1, 62, 62, RangeBoundary(compiler::target::kSmiMin), RangeBoundary(compiler::target::kSmiMax)); TEST_RANGE_OP_SMI(Range::Shl, -1, 1, 30, 30, RangeBoundary(-(1 << 30)), RangeBoundary(1 << 30)); } else { ASSERT(compiler::target::kSmiBits == 30); TEST_RANGE_OP_SMI(Range::Shl, -1, 1, 30, 30, RangeBoundary(compiler::target::kSmiMin), RangeBoundary(compiler::target::kSmiMax)); TEST_RANGE_OP_SMI(Range::Shl, -1, 1, 62, 62, RangeBoundary(compiler::target::kSmiMin), RangeBoundary(compiler::target::kSmiMax)); } TEST_RANGE_OP(Range::Shl, 0, 100, 0, 64, RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); TEST_RANGE_OP(Range::Shl, -100, 0, 0, 64, RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); TEST_RANGE_OP(Range::Shr, -8, 8, 1, 2, RangeBoundary(-4), RangeBoundary(4)); TEST_RANGE_OP(Range::Shr, 1, 8, 1, 2, RangeBoundary::FromConstant(0), RangeBoundary(4)); TEST_RANGE_OP(Range::Shr, -16, -8, 1, 2, RangeBoundary(-8), RangeBoundary(-2)); TEST_RANGE_OP(Range::Shr, 2, 4, -1, 1, RangeBoundary(1), RangeBoundary(4)); TEST_RANGE_OP(Range::Shr, kMaxInt64, kMaxInt64, 0, 1, RangeBoundary(kMaxInt64 >> 1), RangeBoundary(kMaxInt64)); TEST_RANGE_OP(Range::Shr, kMinInt64, kMinInt64, 0, 1, RangeBoundary(kMinInt64), RangeBoundary(kMinInt64 >> 1)); #undef TEST_RANGE_OP } TEST_CASE(RangeTestsInt64Range) { const Range fullInt64Range = Range::Int64(); Range* all = new Range(RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); EXPECT(all->Equals(&fullInt64Range)); EXPECT(all->Overlaps(0, 0)); EXPECT(all->Overlaps(-1, 1)); EXPECT(!all->IsWithin(0, 100)); Range* positive = new Range(RangeBoundary::FromConstant(0), RangeBoundary(kMaxInt64)); EXPECT(positive->Overlaps(0, 1)); EXPECT(positive->Overlaps(1, 100)); EXPECT(positive->Overlaps(-1, 0)); Range* negative = new Range(RangeBoundary(kMinInt64), RangeBoundary::FromConstant(-1)); EXPECT(negative->Overlaps(-1, 0)); EXPECT(negative->Overlaps(-2, -1)); Range* negpos = new Range(RangeBoundary(kMinInt64), RangeBoundary::FromConstant(0)); EXPECT(!negpos->IsPositive()); Range* c = new Range(RangeBoundary(kMinInt64), RangeBoundary::FromConstant(32)); EXPECT(!c->OnlyLessThanOrEqualTo(31)); } TEST_CASE(RangeUtils) { // Use MinInt64/MaxInt64 bounds as any wrap-around range is // converted to the full int64 range. const RangeBoundary negativeInfinity = RangeBoundary::MinInt64(); const RangeBoundary positiveInfinity = RangeBoundary::MaxInt64(); // [-inf, +inf]. const Range& range_0 = *(new Range(negativeInfinity, positiveInfinity)); // [-inf, -1]. const Range& range_a = *(new Range(negativeInfinity, RangeBoundary::FromConstant(-1))); // [-inf, 0]. const Range& range_b = *(new Range(negativeInfinity, RangeBoundary::FromConstant(0))); // [-inf, 1]. const Range& range_c = *(new Range(negativeInfinity, RangeBoundary::FromConstant(1))); // [-1, +inf] const Range& range_d = *(new Range(RangeBoundary::FromConstant(-1), positiveInfinity)); // [0, +inf] const Range& range_e = *(new Range(RangeBoundary::FromConstant(0), positiveInfinity)); // [1, +inf]. const Range& range_f = *(new Range(RangeBoundary::FromConstant(1), positiveInfinity)); // [1, 2]. const Range& range_g = *(new Range(RangeBoundary::FromConstant(1), RangeBoundary::FromConstant(2))); // [-1, -2]. const Range& range_h = *(new Range(RangeBoundary::FromConstant(-1), RangeBoundary::FromConstant(-2))); // [-1, 1]. const Range& range_i = *(new Range(RangeBoundary::FromConstant(-1), RangeBoundary::FromConstant(1))); // OnlyPositiveOrZero. EXPECT(!Range::OnlyPositiveOrZero(range_a, range_b)); EXPECT(!Range::OnlyPositiveOrZero(range_b, range_c)); EXPECT(!Range::OnlyPositiveOrZero(range_c, range_d)); EXPECT(!Range::OnlyPositiveOrZero(range_d, range_e)); EXPECT(Range::OnlyPositiveOrZero(range_e, range_f)); EXPECT(!Range::OnlyPositiveOrZero(range_d, range_d)); EXPECT(Range::OnlyPositiveOrZero(range_e, range_e)); EXPECT(Range::OnlyPositiveOrZero(range_f, range_g)); EXPECT(!Range::OnlyPositiveOrZero(range_g, range_h)); EXPECT(!Range::OnlyPositiveOrZero(range_i, range_i)); // OnlyNegativeOrZero. EXPECT(Range::OnlyNegativeOrZero(range_a, range_b)); EXPECT(!Range::OnlyNegativeOrZero(range_b, range_c)); EXPECT(Range::OnlyNegativeOrZero(range_b, range_b)); EXPECT(!Range::OnlyNegativeOrZero(range_c, range_c)); EXPECT(!Range::OnlyNegativeOrZero(range_c, range_d)); EXPECT(!Range::OnlyNegativeOrZero(range_d, range_e)); EXPECT(!Range::OnlyNegativeOrZero(range_e, range_f)); EXPECT(!Range::OnlyNegativeOrZero(range_f, range_g)); EXPECT(!Range::OnlyNegativeOrZero(range_g, range_h)); EXPECT(Range::OnlyNegativeOrZero(range_h, range_h)); EXPECT(!Range::OnlyNegativeOrZero(range_i, range_i)); // [-inf, +inf]. EXPECT(!Range::OnlyNegativeOrZero(range_0, range_0)); EXPECT(!Range::OnlyPositiveOrZero(range_0, range_0)); EXPECT(Range::ConstantAbsMax(&range_0) == kMaxInt64); EXPECT(Range::ConstantAbsMax(&range_h) == 2); EXPECT(Range::ConstantAbsMax(&range_i) == 1); // RangeBoundary.Equals. EXPECT(RangeBoundary::FromConstant(1).Equals(RangeBoundary::FromConstant(1))); EXPECT( !RangeBoundary::FromConstant(2).Equals(RangeBoundary::FromConstant(1))); } TEST_CASE(RangeBinaryOp) { Range* range_a = new Range(RangeBoundary::FromConstant(-1), RangeBoundary::MaxInt64()); range_a->Clamp(Range::Full(kUnboxedInt32)); EXPECT(range_a->min().ConstantValue() == -1); EXPECT(range_a->max().ConstantValue() == kMaxInt32); range_a->set_max(RangeBoundary::MaxInt64()); Range* range_b = new Range(RangeBoundary::MinInt64(), RangeBoundary::FromConstant(1)); range_b->Clamp(Range::Full(kUnboxedInt32)); EXPECT(range_b->min().ConstantValue() == kMinInt32); EXPECT(range_b->max().ConstantValue() == 1); range_b->set_min(RangeBoundary::MinInt64()); { Range result; Range::BinaryOp(Token::kADD, range_a, range_b, nullptr, &result); ASSERT(!Range::IsUnknown(&result)); EXPECT(result.min().Equals(RangeBoundary::MinInt64())); EXPECT(result.max().Equals(RangeBoundary::MaxInt64())); } // Test that [5, 10] + [0, 5] = [5, 15]. Range* range_c = new Range(RangeBoundary::FromConstant(5), RangeBoundary::FromConstant(10)); Range* range_d = new Range(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(5)); { Range result; Range::BinaryOp(Token::kADD, range_c, range_d, nullptr, &result); ASSERT(!Range::IsUnknown(&result)); EXPECT(result.min().ConstantValue() == 5); EXPECT(result.max().ConstantValue() == 15); } // Test that [0xff, 0xfff] & [0xf, 0xf] = [0x0, 0xf]. Range* range_e = new Range(RangeBoundary::FromConstant(0xff), RangeBoundary::FromConstant(0xfff)); Range* range_f = new Range(RangeBoundary::FromConstant(0xf), RangeBoundary::FromConstant(0xf)); { Range result; Range::BinaryOp(Token::kBIT_AND, range_e, range_f, nullptr, &result); ASSERT(!Range::IsUnknown(&result)); EXPECT(result.min().ConstantValue() == 0x0); EXPECT(result.max().ConstantValue() == 0xf); } } TEST_CASE(RangeAdd) { #define TEST_RANGE_ADD(l_min, l_max, r_min, r_max, result_min, result_max) \ { \ RangeBoundary min, max; \ Range* left_range = new Range(RangeBoundary::FromConstant(l_min), \ RangeBoundary::FromConstant(l_max)); \ Range* right_range = new Range(RangeBoundary::FromConstant(r_min), \ RangeBoundary::FromConstant(r_max)); \ EXPECT(left_range->min().ConstantValue() == l_min); \ EXPECT(left_range->max().ConstantValue() == l_max); \ EXPECT(right_range->min().ConstantValue() == r_min); \ EXPECT(right_range->max().ConstantValue() == r_max); \ Range::Add(left_range, right_range, &min, &max, nullptr); \ EXPECT(min.Equals(result_min)); \ if (FLAG_support_il_printer && !min.Equals(result_min)) { \ OS::PrintErr("%s != %s\n", min.ToCString(), result_min.ToCString()); \ } \ EXPECT(max.Equals(result_max)); \ if (FLAG_support_il_printer && !max.Equals(result_max)) { \ OS::PrintErr("%s != %s\n", max.ToCString(), result_max.ToCString()); \ } \ } // [kMaxInt32, kMaxInt32 + 15] + [10, 20] = [kMaxInt32 + 10, kMaxInt32 + 35]. TEST_RANGE_ADD(static_cast(kMaxInt32), static_cast(kMaxInt32) + 15, static_cast(10), static_cast(20), RangeBoundary(static_cast(kMaxInt32) + 10), RangeBoundary(static_cast(kMaxInt32) + 35)); // [kMaxInt32 - 15, kMaxInt32 + 15] + [15, -15] = [kMaxInt32, kMaxInt32]. TEST_RANGE_ADD(static_cast(kMaxInt32) - 15, static_cast(kMaxInt32) + 15, static_cast(15), static_cast(-15), RangeBoundary(static_cast(kMaxInt32)), RangeBoundary(static_cast(kMaxInt32))); // [kMaxInt32, kMaxInt32 + 15] + [10, kMaxInt64] = [kMinInt64, kMaxInt64]. TEST_RANGE_ADD(static_cast(kMaxInt32), static_cast(kMaxInt32) + 15, static_cast(10), static_cast(kMaxInt64), RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); // [kMinInt64, kMaxInt32 + 15] + [10, 20] = [kMinInt64 + 10, kMaxInt32 + 35]. TEST_RANGE_ADD(static_cast(kMinInt64), static_cast(kMaxInt32) + 15, static_cast(10), static_cast(20), RangeBoundary(static_cast(kMinInt64) + 10), RangeBoundary(static_cast(kMaxInt32) + 35)); // [0, 0] + [kMinInt64, kMaxInt64] = [kMinInt64, kMaxInt64]. TEST_RANGE_ADD(static_cast(0), static_cast(0), static_cast(kMinInt64), static_cast(kMaxInt64), RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); // Overflows. // [-1, 1] + [kMinInt64, kMaxInt64] = [kMinInt64, kMaxInt64]. TEST_RANGE_ADD(static_cast(-1), static_cast(1), static_cast(kMinInt64), static_cast(kMaxInt64), RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); // [kMaxInt64, kMaxInt64] + [kMaxInt64, kMaxInt64] = [kMinInt64, kMaxInt64]. TEST_RANGE_ADD( static_cast(kMaxInt64), static_cast(kMaxInt64), static_cast(kMaxInt64), static_cast(kMaxInt64), RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); // [kMaxInt64, kMaxInt64] + [1, 1] = [kMinInt64, kMaxInt64]. TEST_RANGE_ADD(static_cast(kMaxInt64), static_cast(kMaxInt64), static_cast(1), static_cast(1), RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); #undef TEST_RANGE_ADD } TEST_CASE(RangeSub) { #define TEST_RANGE_SUB(l_min, l_max, r_min, r_max, result_min, result_max) \ { \ RangeBoundary min, max; \ Range* left_range = new Range(RangeBoundary::FromConstant(l_min), \ RangeBoundary::FromConstant(l_max)); \ Range* right_range = new Range(RangeBoundary::FromConstant(r_min), \ RangeBoundary::FromConstant(r_max)); \ EXPECT(left_range->min().ConstantValue() == l_min); \ EXPECT(left_range->max().ConstantValue() == l_max); \ EXPECT(right_range->min().ConstantValue() == r_min); \ EXPECT(right_range->max().ConstantValue() == r_max); \ Range::Sub(left_range, right_range, &min, &max, nullptr); \ EXPECT(min.Equals(result_min)); \ if (FLAG_support_il_printer && !min.Equals(result_min)) { \ OS::PrintErr("%s != %s\n", min.ToCString(), result_min.ToCString()); \ } \ EXPECT(max.Equals(result_max)); \ if (FLAG_support_il_printer && !max.Equals(result_max)) { \ OS::PrintErr("%s != %s\n", max.ToCString(), result_max.ToCString()); \ } \ } // [kMaxInt32, kMaxInt32 + 15] - [10, 20] = [kMaxInt32 - 20, kMaxInt32 + 5]. TEST_RANGE_SUB(static_cast(kMaxInt32), static_cast(kMaxInt32) + 15, static_cast(10), static_cast(20), RangeBoundary(static_cast(kMaxInt32) - 20), RangeBoundary(static_cast(kMaxInt32) + 5)); // [kMintInt64, kMintInt64] - [1, 1] = [kMinInt64, kMaxInt64]. TEST_RANGE_SUB(static_cast(kMinInt64), static_cast(kMinInt64), static_cast(1), static_cast(1), RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); // [1, 1] - [kMintInt64, kMintInt64] = [kMinInt64, kMaxInt64]. TEST_RANGE_SUB(static_cast(1), static_cast(1), static_cast(kMinInt64), static_cast(kMinInt64), RangeBoundary(kMinInt64), RangeBoundary(kMaxInt64)); // [kMaxInt32 + 10, kMaxInt32 + 20] - [-20, -20] = // [kMaxInt32 + 30, kMaxInt32 + 40]. TEST_RANGE_SUB(static_cast(kMaxInt32) + 10, static_cast(kMaxInt32) + 20, static_cast(-20), static_cast(-20), RangeBoundary(static_cast(kMaxInt32) + 30), RangeBoundary(static_cast(kMaxInt32) + 40)); #undef TEST_RANGE_SUB } TEST_CASE(RangeAnd) { #define TEST_RANGE_AND(l_min, l_max, r_min, r_max, result_min, result_max) \ { \ RangeBoundary min, max; \ Range* left_range = new Range(RangeBoundary::FromConstant(l_min), \ RangeBoundary::FromConstant(l_max)); \ Range* right_range = new Range(RangeBoundary::FromConstant(r_min), \ RangeBoundary::FromConstant(r_max)); \ EXPECT(left_range->min().ConstantValue() == l_min); \ EXPECT(left_range->max().ConstantValue() == l_max); \ EXPECT(right_range->min().ConstantValue() == r_min); \ EXPECT(right_range->max().ConstantValue() == r_max); \ Range::And(left_range, right_range, &min, &max); \ EXPECT(min.Equals(result_min)); \ if (FLAG_support_il_printer && !min.Equals(result_min)) { \ OS::PrintErr("%s != %s\n", min.ToCString(), result_min.ToCString()); \ } \ EXPECT(max.Equals(result_max)); \ if (FLAG_support_il_printer && !max.Equals(result_max)) { \ OS::PrintErr("%s != %s\n", max.ToCString(), result_max.ToCString()); \ } \ } // [0xff, 0xfff] & [0xf, 0xf] = [0x0, 0xf]. TEST_RANGE_AND(static_cast(0xff), static_cast(0xfff), static_cast(0xf), static_cast(0xf), RangeBoundary::FromConstant(0), RangeBoundary(0xf)); // [0xffffffff, 0xffffffff] & [0xfffffffff, 0xfffffffff] = [0x0, 0xfffffffff]. TEST_RANGE_AND( static_cast(0xffffffff), static_cast(0xffffffff), static_cast(0xfffffffff), static_cast(0xfffffffff), RangeBoundary::FromConstant(0), RangeBoundary(static_cast(0xfffffffff))); // [0xffffffff, 0xffffffff] & [-20, 20] = [0x0, 0xffffffff]. TEST_RANGE_AND(static_cast(0xffffffff), static_cast(0xffffffff), static_cast(-20), static_cast(20), RangeBoundary::FromConstant(0), RangeBoundary(static_cast(0xffffffff))); // [-20, 20] & [0xffffffff, 0xffffffff] = [0x0, 0xffffffff]. TEST_RANGE_AND(static_cast(-20), static_cast(20), static_cast(0xffffffff), static_cast(0xffffffff), RangeBoundary::FromConstant(0), RangeBoundary(static_cast(0xffffffff))); // Test that [-20, 20] & [-20, 20] = [-32, 31]. TEST_RANGE_AND(static_cast(-20), static_cast(20), static_cast(-20), static_cast(20), RangeBoundary(-32), RangeBoundary(31)); #undef TEST_RANGE_AND } TEST_CASE(RangeIntersectionMinMax) { // Test IntersectionMin and IntersectionMax methods which for constants are // simply defined as Max/Min respectively. // Constants. // MIN(0, 1) == 0 EXPECT(RangeBoundary::IntersectionMax(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(1)) .ConstantValue() == 0); // MIN(0, -1) == -1 EXPECT(RangeBoundary::IntersectionMax(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(-1)) .ConstantValue() == -1); // MIN(1, 0) == 0 EXPECT(RangeBoundary::IntersectionMax(RangeBoundary::FromConstant(1), RangeBoundary::FromConstant(0)) .ConstantValue() == 0); // MIN(-1, 0) == -1 EXPECT(RangeBoundary::IntersectionMax(RangeBoundary::FromConstant(-1), RangeBoundary::FromConstant(0)) .ConstantValue() == -1); // MAX(0, 1) == 1 EXPECT(RangeBoundary::IntersectionMin(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(1)) .ConstantValue() == 1); // MAX(0, -1) == 0 EXPECT(RangeBoundary::IntersectionMin(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(-1)) .ConstantValue() == 0); // MAX(1, 0) == 1 EXPECT(RangeBoundary::IntersectionMin(RangeBoundary::FromConstant(1), RangeBoundary::FromConstant(0)) .ConstantValue() == 1); // MAX(-1, 0) == 0 EXPECT(RangeBoundary::IntersectionMin(RangeBoundary::FromConstant(-1), RangeBoundary::FromConstant(0)) .ConstantValue() == 0); // Constants vs. kMinInt64 / kMaxInt64. EXPECT(RangeBoundary::IntersectionMin(RangeBoundary(kMinInt64), RangeBoundary::FromConstant(-1)) .ConstantValue() == -1); EXPECT(RangeBoundary::IntersectionMin(RangeBoundary::FromConstant(-1), RangeBoundary(kMinInt64)) .ConstantValue() == -1); EXPECT(RangeBoundary::IntersectionMin(RangeBoundary::FromConstant(1), RangeBoundary(kMinInt64)) .ConstantValue() == 1); EXPECT(RangeBoundary::IntersectionMin(RangeBoundary(kMinInt64), RangeBoundary::FromConstant(1)) .ConstantValue() == 1); EXPECT(RangeBoundary::IntersectionMax(RangeBoundary(kMaxInt64), RangeBoundary::FromConstant(-1)) .ConstantValue() == -1); EXPECT(RangeBoundary::IntersectionMax(RangeBoundary::FromConstant(-1), RangeBoundary(kMaxInt64)) .ConstantValue() == -1); EXPECT(RangeBoundary::IntersectionMax(RangeBoundary::FromConstant(1), RangeBoundary(kMaxInt64)) .ConstantValue() == 1); EXPECT(RangeBoundary::IntersectionMax(RangeBoundary(kMaxInt64), RangeBoundary::FromConstant(1)) .ConstantValue() == 1); } TEST_CASE(RangeJoinMinMax) { // Test IntersectionMin and IntersectionMax methods which for constants are // simply defined as Min/Max respectively. const Range full_range = Range::Smi(); // Constants. EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(1), full_range) .ConstantValue() == 1); EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(-1), full_range) .ConstantValue() == 0); EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(1), RangeBoundary::FromConstant(0), full_range) .ConstantValue() == 1); EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(-1), RangeBoundary::FromConstant(0), full_range) .ConstantValue() == 0); EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(1), full_range) .ConstantValue() == 0); EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(0), RangeBoundary::FromConstant(-1), full_range) .ConstantValue() == -1); EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(1), RangeBoundary::FromConstant(0), full_range) .ConstantValue() == 0); EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(-1), RangeBoundary::FromConstant(0), full_range) .ConstantValue() == -1); // Constants vs. kMinInt64 / kMaxInt64. EXPECT(RangeBoundary::JoinMin(RangeBoundary(kMinInt64), RangeBoundary::FromConstant(-1), full_range) .IsLessOrEqual(full_range.min())); EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(-1), RangeBoundary(kMinInt64), full_range) .IsLessOrEqual(full_range.min())); EXPECT(RangeBoundary::JoinMin(RangeBoundary::FromConstant(1), RangeBoundary(kMinInt64), full_range) .IsLessOrEqual(full_range.min())); EXPECT(RangeBoundary::JoinMin(RangeBoundary(kMinInt64), RangeBoundary::FromConstant(1), full_range) .IsLessOrEqual(full_range.min())); EXPECT(RangeBoundary::JoinMax(RangeBoundary(kMaxInt64), RangeBoundary::FromConstant(-1), full_range) .IsGreaterOrEqual(full_range.max())); EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(-1), RangeBoundary(kMaxInt64), full_range) .IsGreaterOrEqual(full_range.max())); EXPECT(RangeBoundary::JoinMax(RangeBoundary::FromConstant(1), RangeBoundary(kMaxInt64), full_range) .IsGreaterOrEqual(full_range.max())); EXPECT(RangeBoundary::JoinMax(RangeBoundary(kMaxInt64), RangeBoundary::FromConstant(1), full_range) .IsGreaterOrEqual(full_range.max())); } #if defined(DART_PRECOMPILER) && defined(TARGET_ARCH_IS_64_BIT) // Regression test for https://github.com/dart-lang/sdk/issues/48153. ISOLATE_UNIT_TEST_CASE(RangeAnalysis_ShiftUint32Op) { const char* kScript = R"( @pragma('vm:never-inline') int foo(int hash) { return 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); } void main() { foo(42); } )"; const auto& root_library = Library::Handle(LoadTestScript(kScript)); Invoke(root_library, "main"); const auto& function = Function::Handle(GetFunction(root_library, "foo")); TestPipeline pipeline(function, CompilerPass::kAOT); FlowGraph* flow_graph = pipeline.RunPasses({}); auto entry = flow_graph->graph_entry()->normal_entry(); EXPECT(entry != nullptr); ILMatcher cursor(flow_graph, entry, /*trace=*/true, ParallelMovesHandling::kSkip); BinaryUint32OpInstr* shift = nullptr; RELEASE_ASSERT(cursor.TryMatch({ kMoveGlob, kMatchAndMoveBinaryUint32Op, kMoveGlob, {kMatchAndMoveBinaryUint32Op, &shift}, kMoveGlob, kMatchAndMoveBinaryUint32Op, kMoveGlob, kMatchAndMoveBinaryUint32Op, kMoveGlob, kMatchDartReturn, })); EXPECT(shift->op_kind() == Token::kSHL); EXPECT(shift->right()->BindsToConstant()); EXPECT(shift->right()->BoundConstant().IsInteger()); EXPECT(Integer::Cast(shift->right()->BoundConstant()).Value() == 10); } #endif // defined(DART_PRECOMPILER) && defined(TARGET_ARCH_IS_64_BIT) ISOLATE_UNIT_TEST_CASE(RangeAnalysis_LoadClassId) { const char* kScript = R"( @pragma('vm:never-inline') bool foo(num x) { return x is int; } void main() { foo(42); } )"; const auto& root_library = Library::Handle(LoadTestScript(kScript)); Invoke(root_library, "main"); const auto& function = Function::Handle(GetFunction(root_library, "foo")); TestPipeline pipeline(function, CompilerPass::kAOT); FlowGraph* flow_graph = pipeline.RunPasses({}); auto entry = flow_graph->graph_entry()->normal_entry(); EXPECT(entry != nullptr); ILMatcher cursor(flow_graph, entry, /*trace=*/true, ParallelMovesHandling::kSkip); LoadClassIdInstr* load_cid = nullptr; RELEASE_ASSERT(cursor.TryMatch({ kMoveGlob, {kMatchAndMoveLoadClassId, &load_cid}, kMoveGlob, kMatchAndMoveTestRange, kMoveGlob, kMatchDartReturn, })); EXPECT(load_cid->range() != nullptr); EXPECT(kSmiCid < kMintCid); EXPECT(kMintCid < kDoubleCid); EXPECT(load_cid->range()->min().ConstantValue() == kSmiCid); EXPECT(load_cid->range()->max().ConstantValue() == kDoubleCid); } } // namespace dart