// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "test/unittests/compiler/live-range-builder.h" #include "test/unittests/test-utils.h" // TODO(mtrofin): would we want to centralize this definition? #ifdef DEBUG #define V8_ASSERT_DEBUG_DEATH(statement, regex) \ ASSERT_DEATH_IF_SUPPORTED(statement, regex) #define DISABLE_IN_RELEASE(Name) Name #else #define V8_ASSERT_DEBUG_DEATH(statement, regex) statement #define DISABLE_IN_RELEASE(Name) DISABLED_##Name #endif // DEBUG namespace v8 { namespace internal { namespace compiler { class LiveRangeUnitTest : public TestWithZone { public: // Split helper, to avoid int->LifetimePosition conversion nuisance. LiveRange* Split(LiveRange* range, int pos) { return range->SplitAt(LifetimePosition::FromInt(pos), zone()); } TopLevelLiveRange* Splinter(TopLevelLiveRange* top, int start, int end, int new_id = 0) { if (top->splinter() == nullptr) { TopLevelLiveRange* ret = new (zone()) TopLevelLiveRange(new_id, MachineRepresentation::kTagged); top->SetSplinter(ret); } top->Splinter(LifetimePosition::FromInt(start), LifetimePosition::FromInt(end), zone()); return top->splinter(); } // Ranges first and second match structurally. bool RangesMatch(LiveRange* first, LiveRange* second) { if (first->Start() != second->Start() || first->End() != second->End()) { return false; } UseInterval* i1 = first->first_interval(); UseInterval* i2 = second->first_interval(); while (i1 != nullptr && i2 != nullptr) { if (i1->start() != i2->start() || i1->end() != i2->end()) return false; i1 = i1->next(); i2 = i2->next(); } if (i1 != nullptr || i2 != nullptr) return false; UsePosition* p1 = first->first_pos(); UsePosition* p2 = second->first_pos(); while (p1 != nullptr && p2 != nullptr) { if (p1->pos() != p2->pos()) return false; p1 = p1->next(); p2 = p2->next(); } if (p1 != nullptr || p2 != nullptr) return false; return true; } }; TEST_F(LiveRangeUnitTest, InvalidConstruction) { // Build a range manually, because the builder guards against empty cases. TopLevelLiveRange* range = new (zone()) TopLevelLiveRange(1, MachineRepresentation::kTagged); V8_ASSERT_DEBUG_DEATH( range->AddUseInterval(LifetimePosition::FromInt(0), LifetimePosition::FromInt(0), zone()), ".*"); } TEST_F(LiveRangeUnitTest, SplitInvalidStart) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Build(0, 1); V8_ASSERT_DEBUG_DEATH(Split(range, 0), ".*"); } TEST_F(LiveRangeUnitTest, DISABLE_IN_RELEASE(InvalidSplitEnd)) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Build(0, 1); ASSERT_DEATH_IF_SUPPORTED(Split(range, 1), ".*"); } TEST_F(LiveRangeUnitTest, DISABLE_IN_RELEASE(SplitInvalidPreStart)) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Build(1, 2); ASSERT_DEATH_IF_SUPPORTED(Split(range, 0), ".*"); } TEST_F(LiveRangeUnitTest, DISABLE_IN_RELEASE(SplitInvalidPostEnd)) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Build(0, 1); ASSERT_DEATH_IF_SUPPORTED(Split(range, 2), ".*"); } TEST_F(LiveRangeUnitTest, SplitSingleIntervalNoUsePositions) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Build(0, 2); LiveRange* child = Split(range, 1); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Build(0, 1); LiveRange* expected_bottom = TestRangeBuilder(zone()).Build(1, 2); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitManyIntervalNoUsePositionsBetween) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 2).Add(4, 6).Build(); LiveRange* child = Split(range, 3); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Build(0, 2); LiveRange* expected_bottom = TestRangeBuilder(zone()).Build(4, 6); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitManyIntervalNoUsePositionsFront) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 2).Add(4, 6).Build(); LiveRange* child = Split(range, 1); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Build(0, 1); LiveRange* expected_bottom = TestRangeBuilder(zone()).Add(1, 2).Add(4, 6).Build(); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitManyIntervalNoUsePositionsAfter) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 2).Add(4, 6).Build(); LiveRange* child = Split(range, 5); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Add(0, 2).Add(4, 5).Build(); LiveRange* expected_bottom = TestRangeBuilder(zone()).Build(5, 6); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitSingleIntervalUsePositions) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 3).AddUse(0).AddUse(2).Build(); LiveRange* child = Split(range, 1); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Add(0, 1).AddUse(0).Build(); LiveRange* expected_bottom = TestRangeBuilder(zone()).Add(1, 3).AddUse(2).Build(); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitSingleIntervalUsePositionsAtPos) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 3).AddUse(0).AddUse(2).Build(); LiveRange* child = Split(range, 2); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Add(0, 2).AddUse(0).AddUse(2).Build(); LiveRange* expected_bottom = TestRangeBuilder(zone()).Build(2, 3); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitManyIntervalUsePositionsBetween) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 2).Add(4, 6).AddUse(1).AddUse(5).Build(); LiveRange* child = Split(range, 3); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Add(0, 2).AddUse(1).Build(); LiveRange* expected_bottom = TestRangeBuilder(zone()).Add(4, 6).AddUse(5).Build(); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitManyIntervalUsePositionsAtInterval) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 2).Add(4, 6).AddUse(1).AddUse(4).Build(); LiveRange* child = Split(range, 4); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Add(0, 2).AddUse(1).Build(); LiveRange* expected_bottom = TestRangeBuilder(zone()).Add(4, 6).AddUse(4).Build(); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitManyIntervalUsePositionsFront) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 2).Add(4, 6).AddUse(1).AddUse(5).Build(); LiveRange* child = Split(range, 1); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Add(0, 1).AddUse(1).Build(); LiveRange* expected_bottom = TestRangeBuilder(zone()).Add(1, 2).Add(4, 6).AddUse(5).Build(); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplitManyIntervalUsePositionsAfter) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 2).Add(4, 6).AddUse(1).AddUse(5).Build(); LiveRange* child = Split(range, 5); EXPECT_NE(nullptr, range->next()); EXPECT_EQ(child, range->next()); LiveRange* expected_top = TestRangeBuilder(zone()).Add(0, 2).Add(4, 5).AddUse(1).AddUse(5).Build(); LiveRange* expected_bottom = TestRangeBuilder(zone()).Build(5, 6); EXPECT_TRUE(RangesMatch(expected_top, range)); EXPECT_TRUE(RangesMatch(expected_bottom, child)); } TEST_F(LiveRangeUnitTest, SplinterSingleInterval) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Build(0, 6); TopLevelLiveRange* splinter = Splinter(range, 3, 5); EXPECT_EQ(nullptr, range->next()); EXPECT_EQ(nullptr, splinter->next()); EXPECT_EQ(range, splinter->splintered_from()); TopLevelLiveRange* expected_source = TestRangeBuilder(zone()).Add(0, 3).Add(5, 6).Build(); TopLevelLiveRange* expected_splinter = TestRangeBuilder(zone()).Build(3, 5); EXPECT_TRUE(RangesMatch(expected_source, range)); EXPECT_TRUE(RangesMatch(expected_splinter, splinter)); } TEST_F(LiveRangeUnitTest, MergeSingleInterval) { TopLevelLiveRange* original = TestRangeBuilder(zone()).Build(0, 6); TopLevelLiveRange* splinter = Splinter(original, 3, 5); original->Merge(splinter, zone()); TopLevelLiveRange* result = TestRangeBuilder(zone()).Build(0, 6); LiveRange* child_1 = Split(result, 3); Split(child_1, 5); EXPECT_TRUE(RangesMatch(result, original)); } TEST_F(LiveRangeUnitTest, SplinterMultipleIntervalsOutside) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); TopLevelLiveRange* splinter = Splinter(range, 2, 6); EXPECT_EQ(nullptr, range->next()); EXPECT_EQ(nullptr, splinter->next()); EXPECT_EQ(range, splinter->splintered_from()); TopLevelLiveRange* expected_source = TestRangeBuilder(zone()).Add(0, 2).Add(6, 8).Build(); TopLevelLiveRange* expected_splinter = TestRangeBuilder(zone()).Add(2, 3).Add(5, 6).Build(); EXPECT_TRUE(RangesMatch(expected_source, range)); EXPECT_TRUE(RangesMatch(expected_splinter, splinter)); } TEST_F(LiveRangeUnitTest, MergeMultipleIntervalsOutside) { TopLevelLiveRange* original = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); TopLevelLiveRange* splinter = Splinter(original, 2, 6); original->Merge(splinter, zone()); TopLevelLiveRange* result = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); LiveRange* child_1 = Split(result, 2); Split(child_1, 6); EXPECT_TRUE(RangesMatch(result, original)); } TEST_F(LiveRangeUnitTest, SplinterMultipleIntervalsInside) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); V8_ASSERT_DEBUG_DEATH(Splinter(range, 3, 5), ".*"); } TEST_F(LiveRangeUnitTest, SplinterMultipleIntervalsLeft) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); TopLevelLiveRange* splinter = Splinter(range, 2, 4); EXPECT_EQ(nullptr, range->next()); EXPECT_EQ(nullptr, splinter->next()); EXPECT_EQ(range, splinter->splintered_from()); TopLevelLiveRange* expected_source = TestRangeBuilder(zone()).Add(0, 2).Add(5, 8).Build(); TopLevelLiveRange* expected_splinter = TestRangeBuilder(zone()).Build(2, 3); EXPECT_TRUE(RangesMatch(expected_source, range)); EXPECT_TRUE(RangesMatch(expected_splinter, splinter)); } TEST_F(LiveRangeUnitTest, MergeMultipleIntervalsLeft) { TopLevelLiveRange* original = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); TopLevelLiveRange* splinter = Splinter(original, 2, 4); original->Merge(splinter, zone()); TopLevelLiveRange* result = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); Split(result, 2); EXPECT_TRUE(RangesMatch(result, original)); } TEST_F(LiveRangeUnitTest, SplinterMultipleIntervalsRight) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); TopLevelLiveRange* splinter = Splinter(range, 4, 6); EXPECT_EQ(nullptr, range->next()); EXPECT_EQ(nullptr, splinter->next()); EXPECT_EQ(range, splinter->splintered_from()); TopLevelLiveRange* expected_source = TestRangeBuilder(zone()).Add(0, 3).Add(6, 8).Build(); TopLevelLiveRange* expected_splinter = TestRangeBuilder(zone()).Build(5, 6); EXPECT_TRUE(RangesMatch(expected_source, range)); EXPECT_TRUE(RangesMatch(expected_splinter, splinter)); } TEST_F(LiveRangeUnitTest, SplinterMergeMultipleTimes) { TopLevelLiveRange* range = TestRangeBuilder(zone()).Add(0, 3).Add(5, 10).Add(12, 16).Build(); Splinter(range, 4, 6); Splinter(range, 8, 14); TopLevelLiveRange* splinter = range->splinter(); EXPECT_EQ(nullptr, range->next()); EXPECT_EQ(nullptr, splinter->next()); EXPECT_EQ(range, splinter->splintered_from()); TopLevelLiveRange* expected_source = TestRangeBuilder(zone()).Add(0, 3).Add(6, 8).Add(14, 16).Build(); TopLevelLiveRange* expected_splinter = TestRangeBuilder(zone()).Add(5, 6).Add(8, 10).Add(12, 14).Build(); EXPECT_TRUE(RangesMatch(expected_source, range)); EXPECT_TRUE(RangesMatch(expected_splinter, splinter)); } TEST_F(LiveRangeUnitTest, MergeMultipleIntervalsRight) { TopLevelLiveRange* original = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); TopLevelLiveRange* splinter = Splinter(original, 4, 6); original->Merge(splinter, zone()); TopLevelLiveRange* result = TestRangeBuilder(zone()).Add(0, 3).Add(5, 8).Build(); LiveRange* child_1 = Split(result, 5); Split(child_1, 6); EXPECT_TRUE(RangesMatch(result, original)); } TEST_F(LiveRangeUnitTest, MergeAfterSplitting) { TopLevelLiveRange* original = TestRangeBuilder(zone()).Build(0, 8); TopLevelLiveRange* splinter = Splinter(original, 4, 6); LiveRange* original_child = Split(original, 2); Split(original_child, 7); original->Merge(splinter, zone()); TopLevelLiveRange* result = TestRangeBuilder(zone()).Build(0, 8); LiveRange* child_1 = Split(result, 2); LiveRange* child_2 = Split(child_1, 4); LiveRange* child_3 = Split(child_2, 6); Split(child_3, 7); EXPECT_TRUE(RangesMatch(result, original)); } TEST_F(LiveRangeUnitTest, IDGeneration) { TopLevelLiveRange* vreg = TestRangeBuilder(zone()).Id(2).Build(0, 100); EXPECT_EQ(2, vreg->vreg()); EXPECT_EQ(0, vreg->relative_id()); TopLevelLiveRange* splinter = new (zone()) TopLevelLiveRange(101, MachineRepresentation::kTagged); vreg->SetSplinter(splinter); vreg->Splinter(LifetimePosition::FromInt(4), LifetimePosition::FromInt(12), zone()); EXPECT_EQ(101, splinter->vreg()); EXPECT_EQ(1, splinter->relative_id()); LiveRange* child = vreg->SplitAt(LifetimePosition::FromInt(50), zone()); EXPECT_EQ(2, child->relative_id()); LiveRange* splinter_child = splinter->SplitAt(LifetimePosition::FromInt(8), zone()); EXPECT_EQ(1, splinter->relative_id()); EXPECT_EQ(3, splinter_child->relative_id()); vreg->Merge(splinter, zone()); EXPECT_EQ(1, splinter->relative_id()); } } // namespace compiler } // namespace internal } // namespace v8