//===--- PartitionUtilsTest.cpp -------------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/SILOptimizer/Utils/PartitionUtils.h" #include "gtest/gtest.h" #include using namespace swift; using namespace swift::PartitionPrimitives; //===----------------------------------------------------------------------===// // Utilities //===----------------------------------------------------------------------===// struct Partition::PartitionTester { const Partition &p; PartitionTester(const Partition &p) : p(p) {} unsigned getRegion(unsigned elt) const { return unsigned(p.elementToRegionMap.at(Element(elt))); } }; namespace { using PartitionTester = Partition::PartitionTester; struct MockedPartitionOpEvaluator final : PartitionOpEvaluatorBaseImpl { MockedPartitionOpEvaluator(Partition &workingPartition, SendingOperandSetFactory &ptrSetFactory, SendingOperandToStateMap &operandToStateMap) : PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory, operandToStateMap) {} // Just say that we always have a disconnected value. SILIsolationInfo getIsolationRegionInfo(Element elt) const { return SILIsolationInfo::getDisconnected(false /*isUnsafeNonIsolated*/); } bool shouldTryToSquelchErrors() const { return false; } static SILLocation getLoc(SILInstruction *inst) { return SILLocation::invalid(); } static SILLocation getLoc(Operand *op) { return SILLocation::invalid(); } static SILIsolationInfo getIsolationInfo(const PartitionOp &partitionOp) { return {}; } static SILInstruction *getSourceInst(const PartitionOp &partitionOp) { return nullptr; } static bool doesFunctionHaveSendingResult(const PartitionOp &partitionOp) { return false; } }; } // namespace namespace { struct MockedPartitionOpEvaluatorWithFailureCallback final : PartitionOpEvaluatorBaseImpl< MockedPartitionOpEvaluatorWithFailureCallback> { using FailureCallbackTy = std::function; FailureCallbackTy failureCallback; MockedPartitionOpEvaluatorWithFailureCallback( Partition &workingPartition, SendingOperandSetFactory &ptrSetFactory, SendingOperandToStateMap &operandToStateMap, FailureCallbackTy failureCallback) : PartitionOpEvaluatorBaseImpl(workingPartition, ptrSetFactory, operandToStateMap), failureCallback(failureCallback) {} void handleError(PartitionOpError error) { switch (error.getKind()) { case PartitionOpError::UnknownCodePattern: case PartitionOpError::SentNeverSendable: case PartitionOpError::AssignNeverSendableIntoSendingResult: case PartitionOpError::InOutSendingNotInitializedAtExit: case PartitionOpError::InOutSendingNotDisconnectedAtExit: case PartitionOpError::NonSendableIsolationCrossingResult: llvm_unreachable("Unsupported"); case PartitionOpError::LocalUseAfterSend: { auto state = error.getLocalUseAfterSendError(); failureCallback(*state.op, state.sentElement, state.sendingOp); } } } // Just say that we always have a disconnected value. SILIsolationInfo getIsolationRegionInfo(Element elt) const { return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/); } bool shouldTryToSquelchErrors() const { return false; } static SILLocation getLoc(SILInstruction *inst) { return SILLocation::invalid(); } static SILLocation getLoc(Operand *op) { return SILLocation::invalid(); } static SILIsolationInfo getIsolationInfo(const PartitionOp &partitionOp) { return {}; } static SILInstruction *getSourceInst(const PartitionOp &partitionOp) { return nullptr; } }; } // namespace //===----------------------------------------------------------------------===// // Tests //===----------------------------------------------------------------------===// // When we send we need a specific send instruction. We do not ever // actually dereference the instruction, so just use some invalid ptr values so // we can compare. Operand *operandSingletons[5] = { (Operand *)0xDEAD0000, (Operand *)0xFEAD0000, (Operand *)0xAEDF0000, (Operand *)0xFEDA0000, (Operand *)0xFBDA0000, }; SILInstruction *instSingletons[5] = { (SILInstruction *)0xBEAD0000, (SILInstruction *)0xBEAD0000, (SILInstruction *)0xBEDF0000, (SILInstruction *)0xBEDA0000, (SILInstruction *)0xBBDA0000, }; SILLocation fakeLoc = SILLocation::invalid(); // This test tests that if a series of merges is split between two partitions // p1 and p2, but also applied in its entirety to p3, then joining p1 and p2 // yields p3. TEST(PartitionUtilsTest, TestMergeAndJoin) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap sendingOpToStateMap(historyFactory); Partition p1(historyFactory.get()); Partition p2(historyFactory.get()); Partition p3(historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, sendingOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3))}); } { MockedPartitionOpEvaluator eval(p2, factory, sendingOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(5)), PartitionOp::AssignFresh(Element(6)), PartitionOp::AssignFresh(Element(7)), PartitionOp::AssignFresh(Element(0))}); } { MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3)), PartitionOp::AssignFresh(Element(4)), PartitionOp::AssignFresh(Element(5))}); } EXPECT_FALSE(Partition::equals(p1, p2)); EXPECT_FALSE(Partition::equals(p2, p3)); EXPECT_FALSE(Partition::equals(p1, p3)); { MockedPartitionOpEvaluator eval(p1, factory, sendingOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(4)), PartitionOp::AssignFresh(Element(5)), PartitionOp::AssignFresh(Element(6)), PartitionOp::AssignFresh(Element(7)), PartitionOp::AssignFresh(Element(8))}); } { MockedPartitionOpEvaluator eval(p2, factory, sendingOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3)), PartitionOp::AssignFresh(Element(4)), PartitionOp::AssignFresh(Element(8))}); } { MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(6)), PartitionOp::AssignFresh(Element(7)), PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(8))}); } EXPECT_TRUE(Partition::equals(p1, p2)); EXPECT_TRUE(Partition::equals(p2, p3)); EXPECT_TRUE(Partition::equals(p1, p3)); auto expect_join_eq = [&]() { Partition joined = Partition::join(p1, p2); EXPECT_TRUE(Partition::equals(p3, joined)); }; auto apply_to_p1_and_p3 = [&](PartitionOp op) { { MockedPartitionOpEvaluator eval(p1, factory, sendingOpToStateMap); eval.apply(op); } { MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap); eval.apply(op); } expect_join_eq(); }; auto apply_to_p2_and_p3 = [&](PartitionOp op) { { MockedPartitionOpEvaluator eval(p2, factory, sendingOpToStateMap); eval.apply(op); } { MockedPartitionOpEvaluator eval(p3, factory, sendingOpToStateMap); eval.apply(op); } expect_join_eq(); }; apply_to_p1_and_p3(PartitionOp::Merge(Element(1), Element(2))); apply_to_p2_and_p3(PartitionOp::Merge(Element(7), Element(8))); apply_to_p1_and_p3(PartitionOp::Merge(Element(2), Element(7))); apply_to_p2_and_p3(PartitionOp::Merge(Element(1), Element(3))); apply_to_p1_and_p3(PartitionOp::Merge(Element(3), Element(4))); EXPECT_FALSE(Partition::equals(p1, p2)); EXPECT_FALSE(Partition::equals(p2, p3)); EXPECT_FALSE(Partition::equals(p1, p3)); apply_to_p2_and_p3(PartitionOp::Merge(Element(2), Element(5))); apply_to_p1_and_p3(PartitionOp::Merge(Element(5), Element(6))); apply_to_p2_and_p3(PartitionOp::Merge(Element(1), Element(6))); apply_to_p1_and_p3(PartitionOp::Merge(Element(2), Element(6))); apply_to_p2_and_p3(PartitionOp::Merge(Element(3), Element(7))); apply_to_p1_and_p3(PartitionOp::Merge(Element(7), Element(8))); } TEST(PartitionUtilsTest, Join1) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Element data1[] = {Element(0), Element(1), Element(2), Element(3), Element(4), Element(5)}; Partition p1 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data1), historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(0), Element(0)), PartitionOp::Assign(Element(1), Element(0)), PartitionOp::Assign(Element(2), Element(2)), PartitionOp::Assign(Element(3), Element(3)), PartitionOp::Assign(Element(4), Element(3)), PartitionOp::Assign(Element(5), Element(2))}); } Partition p2 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data1), historyFactory.get()); { MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(0), Element(0)), PartitionOp::Assign(Element(1), Element(0)), PartitionOp::Assign(Element(2), Element(2)), PartitionOp::Assign(Element(3), Element(3)), PartitionOp::Assign(Element(4), Element(3)), PartitionOp::Assign(Element(5), Element(5))}); } auto result = Partition::join(p1, p2); PartitionTester tester(result); ASSERT_EQ(tester.getRegion(0), 0); ASSERT_EQ(tester.getRegion(1), 0); ASSERT_EQ(tester.getRegion(2), 2); ASSERT_EQ(tester.getRegion(3), 3); ASSERT_EQ(tester.getRegion(4), 3); ASSERT_EQ(tester.getRegion(5), 2); } TEST(PartitionUtilsTest, Join2) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Element data1[] = {Element(0), Element(1), Element(2), Element(3), Element(4), Element(5)}; Partition p1 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data1), historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(0), Element(0)), PartitionOp::Assign(Element(1), Element(0)), PartitionOp::Assign(Element(2), Element(2)), PartitionOp::Assign(Element(3), Element(3)), PartitionOp::Assign(Element(4), Element(3)), PartitionOp::Assign(Element(5), Element(2))}); } Element data2[] = {Element(4), Element(5), Element(6), Element(7), Element(8), Element(9)}; Partition p2 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data2), historyFactory.get()); { MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(4), Element(4)), PartitionOp::Assign(Element(5), Element(5)), PartitionOp::Assign(Element(6), Element(4)), PartitionOp::Assign(Element(7), Element(7)), PartitionOp::Assign(Element(8), Element(7)), PartitionOp::Assign(Element(9), Element(4))}); } auto result = Partition::join(p1, p2); PartitionTester tester(result); ASSERT_EQ(tester.getRegion(0), 0); ASSERT_EQ(tester.getRegion(1), 0); ASSERT_EQ(tester.getRegion(2), 2); ASSERT_EQ(tester.getRegion(3), 3); ASSERT_EQ(tester.getRegion(4), 3); ASSERT_EQ(tester.getRegion(5), 2); ASSERT_EQ(tester.getRegion(6), 3); ASSERT_EQ(tester.getRegion(7), 7); ASSERT_EQ(tester.getRegion(8), 7); ASSERT_EQ(tester.getRegion(9), 3); } TEST(PartitionUtilsTest, Join2Reversed) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Element data1[] = {Element(0), Element(1), Element(2), Element(3), Element(4), Element(5)}; Partition p1 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data1), historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(0), Element(0)), PartitionOp::Assign(Element(1), Element(0)), PartitionOp::Assign(Element(2), Element(2)), PartitionOp::Assign(Element(3), Element(3)), PartitionOp::Assign(Element(4), Element(3)), PartitionOp::Assign(Element(5), Element(2))}); } Element data2[] = {Element(4), Element(5), Element(6), Element(7), Element(8), Element(9)}; Partition p2 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data2), historyFactory.get()); { MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(4), Element(4)), PartitionOp::Assign(Element(5), Element(5)), PartitionOp::Assign(Element(6), Element(4)), PartitionOp::Assign(Element(7), Element(7)), PartitionOp::Assign(Element(8), Element(7)), PartitionOp::Assign(Element(9), Element(4))}); } auto result = Partition::join(p2, p1); PartitionTester tester(result); ASSERT_EQ(tester.getRegion(0), 0); ASSERT_EQ(tester.getRegion(1), 0); ASSERT_EQ(tester.getRegion(2), 2); ASSERT_EQ(tester.getRegion(3), 3); ASSERT_EQ(tester.getRegion(4), 3); ASSERT_EQ(tester.getRegion(5), 2); ASSERT_EQ(tester.getRegion(6), 3); ASSERT_EQ(tester.getRegion(7), 7); ASSERT_EQ(tester.getRegion(8), 7); ASSERT_EQ(tester.getRegion(9), 3); } TEST(PartitionUtilsTest, JoinLarge) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Element data1[] = { Element(0), Element(1), Element(2), Element(3), Element(4), Element(5), Element(6), Element(7), Element(8), Element(9), Element(10), Element(11), Element(12), Element(13), Element(14), Element(15), Element(16), Element(17), Element(18), Element(19), Element(20), Element(21), Element(22), Element(23), Element(24), Element(25), Element(26), Element(27), Element(28), Element(29)}; Partition p1 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data1), historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(0), Element(29)), PartitionOp::Assign(Element(1), Element(17)), PartitionOp::Assign(Element(2), Element(0)), PartitionOp::Assign(Element(3), Element(12)), PartitionOp::Assign(Element(4), Element(13)), PartitionOp::Assign(Element(5), Element(9)), PartitionOp::Assign(Element(6), Element(15)), PartitionOp::Assign(Element(7), Element(27)), PartitionOp::Assign(Element(8), Element(3)), PartitionOp::Assign(Element(9), Element(3)), PartitionOp::Assign(Element(10), Element(3)), PartitionOp::Assign(Element(11), Element(21)), PartitionOp::Assign(Element(12), Element(14)), PartitionOp::Assign(Element(13), Element(25)), PartitionOp::Assign(Element(14), Element(1)), PartitionOp::Assign(Element(15), Element(25)), PartitionOp::Assign(Element(16), Element(12)), PartitionOp::Assign(Element(17), Element(3)), PartitionOp::Assign(Element(18), Element(25)), PartitionOp::Assign(Element(19), Element(13)), PartitionOp::Assign(Element(20), Element(19)), PartitionOp::Assign(Element(21), Element(7)), PartitionOp::Assign(Element(22), Element(19)), PartitionOp::Assign(Element(23), Element(27)), PartitionOp::Assign(Element(24), Element(1)), PartitionOp::Assign(Element(25), Element(9)), PartitionOp::Assign(Element(26), Element(18)), PartitionOp::Assign(Element(27), Element(29)), PartitionOp::Assign(Element(28), Element(28)), PartitionOp::Assign(Element(29), Element(13))}); } Element data2[] = { Element(15), Element(16), Element(17), Element(18), Element(19), Element(20), Element(21), Element(22), Element(23), Element(24), Element(25), Element(26), Element(27), Element(28), Element(29), Element(30), Element(31), Element(32), Element(33), Element(34), Element(35), Element(36), Element(37), Element(38), Element(39), Element(40), Element(41), Element(42), Element(43), Element(44)}; Partition p2 = Partition::separateRegions(fakeLoc, llvm::ArrayRef(data2), historyFactory.get()); { MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(15), Element(31)), PartitionOp::Assign(Element(16), Element(34)), PartitionOp::Assign(Element(17), Element(35)), PartitionOp::Assign(Element(18), Element(41)), PartitionOp::Assign(Element(19), Element(15)), PartitionOp::Assign(Element(20), Element(32)), PartitionOp::Assign(Element(21), Element(17)), PartitionOp::Assign(Element(22), Element(31)), PartitionOp::Assign(Element(23), Element(21)), PartitionOp::Assign(Element(24), Element(33)), PartitionOp::Assign(Element(25), Element(25)), PartitionOp::Assign(Element(26), Element(31)), PartitionOp::Assign(Element(27), Element(16)), PartitionOp::Assign(Element(28), Element(35)), PartitionOp::Assign(Element(29), Element(40)), PartitionOp::Assign(Element(30), Element(33)), PartitionOp::Assign(Element(31), Element(34)), PartitionOp::Assign(Element(32), Element(22)), PartitionOp::Assign(Element(33), Element(42)), PartitionOp::Assign(Element(34), Element(37)), PartitionOp::Assign(Element(35), Element(34)), PartitionOp::Assign(Element(36), Element(18)), PartitionOp::Assign(Element(37), Element(32)), PartitionOp::Assign(Element(38), Element(22)), PartitionOp::Assign(Element(39), Element(44)), PartitionOp::Assign(Element(40), Element(20)), PartitionOp::Assign(Element(41), Element(37)), PartitionOp::Assign(Element(43), Element(29)), PartitionOp::Assign(Element(44), Element(25))}); } auto result = Partition::join(p1, p2); PartitionTester tester(result); ASSERT_EQ(tester.getRegion(0), 0); ASSERT_EQ(tester.getRegion(1), 1); ASSERT_EQ(tester.getRegion(2), 0); ASSERT_EQ(tester.getRegion(3), 3); ASSERT_EQ(tester.getRegion(4), 4); ASSERT_EQ(tester.getRegion(5), 5); ASSERT_EQ(tester.getRegion(6), 6); ASSERT_EQ(tester.getRegion(7), 3); ASSERT_EQ(tester.getRegion(8), 3); ASSERT_EQ(tester.getRegion(9), 3); ASSERT_EQ(tester.getRegion(10), 3); ASSERT_EQ(tester.getRegion(11), 11); ASSERT_EQ(tester.getRegion(12), 0); ASSERT_EQ(tester.getRegion(13), 13); ASSERT_EQ(tester.getRegion(14), 1); ASSERT_EQ(tester.getRegion(15), 13); ASSERT_EQ(tester.getRegion(16), 0); ASSERT_EQ(tester.getRegion(17), 3); ASSERT_EQ(tester.getRegion(18), 13); ASSERT_EQ(tester.getRegion(19), 13); ASSERT_EQ(tester.getRegion(20), 13); ASSERT_EQ(tester.getRegion(21), 3); ASSERT_EQ(tester.getRegion(22), 13); ASSERT_EQ(tester.getRegion(23), 3); ASSERT_EQ(tester.getRegion(24), 1); ASSERT_EQ(tester.getRegion(25), 3); ASSERT_EQ(tester.getRegion(26), 13); ASSERT_EQ(tester.getRegion(27), 0); ASSERT_EQ(tester.getRegion(28), 3); ASSERT_EQ(tester.getRegion(29), 13); ASSERT_EQ(tester.getRegion(30), 1); ASSERT_EQ(tester.getRegion(31), 0); ASSERT_EQ(tester.getRegion(32), 13); ASSERT_EQ(tester.getRegion(33), 33); ASSERT_EQ(tester.getRegion(34), 34); ASSERT_EQ(tester.getRegion(35), 34); ASSERT_EQ(tester.getRegion(36), 13); ASSERT_EQ(tester.getRegion(37), 13); ASSERT_EQ(tester.getRegion(38), 13); ASSERT_EQ(tester.getRegion(39), 39); ASSERT_EQ(tester.getRegion(40), 13); ASSERT_EQ(tester.getRegion(41), 13); ASSERT_EQ(tester.getRegion(42), 33); ASSERT_EQ(tester.getRegion(43), 13); ASSERT_EQ(tester.getRegion(44), 3); } // This test tests the semantics of assignment. TEST(PartitionUtilsTest, TestAssign) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Partition p1(historyFactory.get()); Partition p2(historyFactory.get()); Partition p3(historyFactory.get()); MockedPartitionOpEvaluator evalP1(p1, factory, transferringOpToStateMap); evalP1.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3))}); MockedPartitionOpEvaluator evalP2(p2, factory, transferringOpToStateMap); evalP2.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3))}); MockedPartitionOpEvaluator evalP3(p3, factory, transferringOpToStateMap); evalP3.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3))}); // expected: p1: ((Element(0)) (Element(1)) (Element(2)) (Element(3))), p2: // ((Element(0)) (Element(1)) (Element(2)) (Element(3))), p3: ((Element(0)) // (Element(1)) (Element(2)) (Element(3))) EXPECT_TRUE(Partition::equals(p1, p2)); EXPECT_TRUE(Partition::equals(p2, p3)); EXPECT_TRUE(Partition::equals(p1, p3)); evalP1.apply(PartitionOp::Assign(Element(0), Element(1))); evalP2.apply(PartitionOp::Assign(Element(1), Element(0))); evalP3.apply(PartitionOp::Assign(Element(2), Element(1))); // expected: p1: ((0 1) (Element(2)) (Element(3))), p2: ((0 1) (Element(2)) // (Element(3))), p3: ((Element(0)) (1 2) (Element(3))) EXPECT_TRUE(Partition::equals(p1, p2)); EXPECT_FALSE(Partition::equals(p2, p3)); EXPECT_FALSE(Partition::equals(p1, p3)); evalP1.apply(PartitionOp::Assign(Element(2), Element(0))); evalP2.apply(PartitionOp::Assign(Element(2), Element(1))); evalP3.apply(PartitionOp::Assign(Element(0), Element(2))); // expected: p1: ((0 1 2) (Element(3))), p2: ((0 1 2) (Element(3))), p3: ((0 1 // 2) (Element(3))) EXPECT_TRUE(Partition::equals(p1, p2)); EXPECT_TRUE(Partition::equals(p2, p3)); EXPECT_TRUE(Partition::equals(p1, p3)); evalP1.apply(PartitionOp::Assign(Element(0), Element(3))); evalP2.apply(PartitionOp::Assign(Element(1), Element(3))); evalP3.apply(PartitionOp::Assign(Element(2), Element(3))); // expected: p1: ((1 2) (0 3)), p2: ((0 2) (1 3)), p3: ((0 1) (2 3)) EXPECT_FALSE(Partition::equals(p1, p2)); EXPECT_FALSE(Partition::equals(p2, p3)); EXPECT_FALSE(Partition::equals(p1, p3)); evalP1.apply(PartitionOp::Assign(Element(1), Element(0))); evalP2.apply(PartitionOp::Assign(Element(2), Element(1))); evalP3.apply(PartitionOp::Assign(Element(0), Element(2))); // expected: p1: ((Element(2)) (0 1 3)), p2: ((Element(0)) (1 2 3)), p3: // ((Element(1)) (0 2 3)) EXPECT_FALSE(Partition::equals(p1, p2)); EXPECT_FALSE(Partition::equals(p2, p3)); EXPECT_FALSE(Partition::equals(p1, p3)); evalP1.apply(PartitionOp::Assign(Element(2), Element(3))); evalP2.apply(PartitionOp::Assign(Element(0), Element(3))); evalP3.apply(PartitionOp::Assign(Element(1), Element(3))); // expected: p1: ((0 1 2 3)), p2: ((0 1 2 3)), p3: ((0 1 2 3)) EXPECT_TRUE(Partition::equals(p1, p2)); EXPECT_TRUE(Partition::equals(p2, p3)); EXPECT_TRUE(Partition::equals(p1, p3)); } // This test tests that consumption consumes entire regions as expected TEST(PartitionUtilsTest, TestConsumeAndRequire) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Partition p(historyFactory.get()); { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3)), PartitionOp::AssignFresh(Element(4)), PartitionOp::AssignFresh(Element(5)), PartitionOp::AssignFresh(Element(6)), PartitionOp::AssignFresh(Element(7)), PartitionOp::AssignFresh(Element(8)), PartitionOp::AssignFresh(Element(9)), PartitionOp::AssignFresh(Element(10)), PartitionOp::AssignFresh(Element(11)), PartitionOp::Assign(Element(1), Element(0)), PartitionOp::Assign(Element(2), Element(1)), PartitionOp::Assign(Element(4), Element(3)), PartitionOp::Assign(Element(5), Element(4)), PartitionOp::Assign(Element(7), Element(6)), PartitionOp::Assign(Element(9), Element(8)), // expected: p: ((0 1 2) (3 4 5) (6 7) (8 9) (Element(10)) // (Element(11))) PartitionOp::Send(Element(2), operandSingletons[0]), PartitionOp::Send(Element(7), operandSingletons[1]), PartitionOp::Send(Element(10), operandSingletons[2])}); } // expected: p: ({0 1 2 6 7 10} (3 4 5) (8 9) (Element(11))) auto never_called = [](const PartitionOp &, unsigned, Operand *) { EXPECT_TRUE(false); }; int times_called = 0; auto increment_times_called = [&](const PartitionOp &, unsigned, Operand *) { times_called++; }; { MockedPartitionOpEvaluatorWithFailureCallback eval( p, factory, transferringOpToStateMap, increment_times_called); eval.apply({PartitionOp::Require(Element(0)), PartitionOp::Require(Element(1)), PartitionOp::Require(Element(2))}); } EXPECT_EQ(times_called, 3); { MockedPartitionOpEvaluatorWithFailureCallback eval( p, factory, transferringOpToStateMap, never_called); eval.apply({PartitionOp::Require(Element(3)), PartitionOp::Require(Element(4)), PartitionOp::Require(Element(5))}); } { MockedPartitionOpEvaluatorWithFailureCallback eval( p, factory, transferringOpToStateMap, increment_times_called); eval.apply( {PartitionOp::Require(Element(6)), PartitionOp::Require(Element(7))}); } { MockedPartitionOpEvaluatorWithFailureCallback eval( p, factory, transferringOpToStateMap, never_called); eval.apply( {PartitionOp::Require(Element(8)), PartitionOp::Require(Element(9))}); } { MockedPartitionOpEvaluatorWithFailureCallback eval( p, factory, transferringOpToStateMap, increment_times_called); eval.apply(PartitionOp::Require(Element(10))); } { MockedPartitionOpEvaluatorWithFailureCallback eval( p, factory, transferringOpToStateMap, never_called); eval.apply(PartitionOp::Require(Element(11))); } EXPECT_EQ(times_called, 6); } // This test tests that the copy constructor is usable to create fresh // copies of partitions TEST(PartitionUtilsTest, TestCopyConstructor) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Partition p1(historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply(PartitionOp::AssignFresh(Element(0))); } // Make copy. Partition p2 = p1; // Change p1 again. { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply(PartitionOp::Send(Element(0), operandSingletons[0])); } { bool failure = false; MockedPartitionOpEvaluatorWithFailureCallback eval( p1, factory, transferringOpToStateMap, [&](const PartitionOp &, unsigned, Operand *) { failure = true; }); eval.apply(PartitionOp::Require(Element(0))); EXPECT_TRUE(failure); } { MockedPartitionOpEvaluatorWithFailureCallback eval( p2, factory, transferringOpToStateMap, [](const PartitionOp &, unsigned, Operand *) { EXPECT_TRUE(false); }); eval.apply(PartitionOp::Require(Element(0))); } } TEST(PartitionUtilsTest, TestUndoTransfer) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); Partition p(historyFactory.get()); MockedPartitionOpEvaluatorWithFailureCallback eval( p, factory, transferringOpToStateMap, [&](const PartitionOp &, unsigned, Operand *) { EXPECT_TRUE(false); }); // Shouldn't error on this. eval.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::Send(Element(0), operandSingletons[0]), PartitionOp::UndoSend(Element(0), instSingletons[0]), PartitionOp::Require(Element(0), instSingletons[0])}); } TEST(PartitionUtilsTest, TestLastEltInTransferredRegion) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); // First make sure that we do this correctly with an assign fresh. Partition p(historyFactory.get()); { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::Send(Element(0), operandSingletons[0]), PartitionOp::AssignFresh(Element(0))}); } p.validateRegionToSendingOpMapRegions(); // Now make sure that we do this correctly with assign. Partition p2(historyFactory.get()); { MockedPartitionOpEvaluator eval(p2, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2)), PartitionOp::Send(Element(0), operandSingletons[0]), PartitionOp::Assign(Element(0), Element(2))}); } p2.validateRegionToSendingOpMapRegions(); } TEST(PartitionUtilsTest, TestHistory_CreateVariable) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SmallVector joinedHistories; SendingOperandToStateMap transferringOpToStateMap(historyFactory); // First make sure that we do this correctly with an assign fresh. Partition p(historyFactory.get()); { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1))}); } Partition pSnapshot = p; { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(2))}); } p.popHistory(joinedHistories); EXPECT_TRUE(Partition::equals(p, pSnapshot)); EXPECT_TRUE(joinedHistories.empty()); } TEST(PartitionUtilsTest, TestHistory_AssignRegion) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); SmallVector joinedHistories; // First make sure that we do this correctly with an assign fresh. Partition p(historyFactory.get()); { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(0)), PartitionOp::AssignFresh(Element(1)), PartitionOp::AssignFresh(Element(2))}); } Partition pSnapshot = p; { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(1), Element(2))}); } Partition pSnapshot2 = p; { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(0), Element(2))}); } p.popHistory(joinedHistories); EXPECT_TRUE(Partition::equals(p, pSnapshot2)); EXPECT_TRUE(joinedHistories.empty()); p.popHistory(joinedHistories); EXPECT_TRUE(Partition::equals(p, pSnapshot)); EXPECT_TRUE(joinedHistories.empty()); } TEST(PartitionUtilsTest, TestHistory_BuildNewRegionRepIsMergee) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); SmallVector joinedHistories; Partition p(historyFactory.get()); { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3)), PartitionOp::AssignFresh(Element(10)), PartitionOp::AssignFresh(Element(0)), PartitionOp::Assign(Element(3), Element(2)), PartitionOp::Assign(Element(10), Element(2)), PartitionOp::Merge(Element(2), Element(0))}); } Partition pSnapshot = p; { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(1), Element(2))}); } Partition pSnapshot2 = p; { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::Assign(Element(0), Element(2))}); } // Even though we pushed a new instruction, nothing changed in our region. EXPECT_TRUE(Partition::equals(p, pSnapshot2)); // We pop but nothing changes since we did not need to change anything. p.popHistory(joinedHistories); EXPECT_TRUE(Partition::equals(p, pSnapshot2)); EXPECT_TRUE(joinedHistories.empty()); // We pop a last time to return to our original value. p.popHistory(joinedHistories); EXPECT_TRUE(Partition::equals(p, pSnapshot)); EXPECT_TRUE(joinedHistories.empty()); } TEST(PartitionUtilsTest, TestHistory_ReturnFalseWhenNoneLeft) { llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SmallVector joinedHistories; SendingOperandToStateMap transferringOpToStateMap(historyFactory); Partition p(historyFactory.get()); EXPECT_FALSE(p.popHistory(joinedHistories)); EXPECT_TRUE(joinedHistories.empty()); { MockedPartitionOpEvaluator eval(p, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(2)), PartitionOp::AssignFresh(Element(3))}); } EXPECT_TRUE(p.popHistory(joinedHistories)); EXPECT_TRUE(joinedHistories.empty()); EXPECT_FALSE(p.popHistory(joinedHistories)); EXPECT_TRUE(joinedHistories.empty()); } TEST(PartitionUtilsTest, TestHistory_JoiningTwoEmpty) { // Make sure that we do sane things when we join empty history. llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SmallVector joinedHistories; Partition p1(historyFactory.get()); Partition p2(historyFactory.get()); auto result = Partition::join(p1, p2); EXPECT_TRUE(result.begin() == result.end()); EXPECT_FALSE(result.hasHistory()); } TEST(PartitionUtilsTest, TestHistory_JoiningNotEmptyAndEmpty) { // Make sure that we do sane things when we join empty history. llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SmallVector joinedHistories; SendingOperandToStateMap transferringOpToStateMap(historyFactory); Partition p1(historyFactory.get()); Partition p2(historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(2))}); } EXPECT_TRUE(p1.historySize() == 2); EXPECT_TRUE(p2.historySize() == 0); auto result = Partition::join(p1, p2); EXPECT_TRUE(std::next(result.begin()) == result.end()); // Since p2 doesn't have any history, we do not actually perform any join and // thus do not insert a CFGHistory change. EXPECT_TRUE(result.historySize() == 2); } TEST(PartitionUtilsTest, TestHistory_JoiningEmptyAndNotEmpty) { // Make sure that we do sane things when we join empty history. llvm::BumpPtrAllocator allocator; Partition::SendingOperandSetFactory factory(allocator); IsolationHistory::Factory historyFactory(allocator); SendingOperandToStateMap transferringOpToStateMap(historyFactory); SmallVector joinedHistories; Partition p1(historyFactory.get()); Partition p2(historyFactory.get()); { MockedPartitionOpEvaluator eval(p1, factory, transferringOpToStateMap); eval.apply({PartitionOp::AssignFresh(Element(2))}); } EXPECT_TRUE(p1.historySize() == 2); EXPECT_TRUE(p2.historySize() == 0); auto result = Partition::join(p1, p2); EXPECT_TRUE(std::next(result.begin()) == result.end()); // Since p2 doesn't have any history, we do not actually perform any join and // thus do not insert a CFGHistory change. EXPECT_TRUE(result.historySize() == 2); }