| // 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 |