| //===- SeedCollectorTest.cpp ----------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Transforms/Vectorize/SandboxVectorizer/SeedCollector.h" |
| #include "llvm/Analysis/AliasAnalysis.h" |
| #include "llvm/Analysis/AssumptionCache.h" |
| #include "llvm/Analysis/BasicAliasAnalysis.h" |
| #include "llvm/Analysis/LoopInfo.h" |
| #include "llvm/Analysis/TargetLibraryInfo.h" |
| #include "llvm/AsmParser/Parser.h" |
| #include "llvm/IR/Dominators.h" |
| #include "llvm/SandboxIR/Function.h" |
| #include "llvm/SandboxIR/Instruction.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "llvm/Testing/Support/SupportHelpers.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| // TODO: gcc-10 has a bug that causes the below line not to compile due to some |
| // macro-magic in gunit in combination with a class with pure-virtual |
| // function. Once gcc-10 is no longer supported, replace this function with |
| // something like the following: |
| // |
| // EXPECT_THAT(SB, testing::ElementsAre(St0, St1, St2, St3)); |
| static void |
| ExpectThatElementsAre(sandboxir::SeedBundle &SR, |
| llvm::ArrayRef<sandboxir::Instruction *> Contents) { |
| EXPECT_EQ(range_size(SR), Contents.size()); |
| auto CI = Contents.begin(); |
| if (range_size(SR) == Contents.size()) |
| for (auto &S : SR) |
| EXPECT_EQ(S, *CI++); |
| } |
| |
| struct SeedBundleTest : public testing::Test { |
| LLVMContext C; |
| std::unique_ptr<Module> M; |
| |
| void parseIR(LLVMContext &C, const char *IR) { |
| SMDiagnostic Err; |
| M = parseAssemblyString(IR, Err, C); |
| if (!M) |
| Err.print("LegalityTest", errs()); |
| } |
| BasicBlock *getBasicBlockByName(Function &F, StringRef Name) { |
| for (BasicBlock &BB : F) |
| if (BB.getName() == Name) |
| return &BB; |
| llvm_unreachable("Expected to find basic block!"); |
| } |
| }; |
| |
| // Stub class to make the abstract base class testable. |
| class SeedBundleForTest : public sandboxir::SeedBundle { |
| public: |
| using sandboxir::SeedBundle::SeedBundle; |
| void insert(sandboxir::Instruction *I, ScalarEvolution &SE) override { |
| insertAt(Seeds.end(), I); |
| } |
| }; |
| |
| TEST_F(SeedBundleTest, SeedBundle) { |
| parseIR(C, R"IR( |
| define void @foo(float %v0, i32 %i0, i16 %i1, i8 %i2) { |
| bb: |
| %add0 = fadd float %v0, %v0 |
| %add1 = fadd float %v0, %v0 |
| %add2 = add i8 %i2, %i2 |
| %add3 = add i16 %i1, %i1 |
| %add4 = add i32 %i0, %i0 |
| %add5 = add i16 %i1, %i1 |
| %add6 = add i8 %i2, %i2 |
| %add7 = add i8 %i2, %i2 |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| DataLayout DL(M->getDataLayout()); |
| auto *BB = &*F.begin(); |
| auto It = BB->begin(); |
| auto *I0 = &*It++; |
| auto *I1 = &*It++; |
| // Assume first two instructions are identical in the number of bits. |
| const unsigned IOBits = sandboxir::Utils::getNumBits(I0, DL); |
| // Constructor |
| SeedBundleForTest SBO(I0); |
| EXPECT_EQ(*SBO.begin(), I0); |
| // getNumUnusedBits after constructor |
| EXPECT_EQ(SBO.getNumUnusedBits(), IOBits); |
| // setUsed |
| SBO.setUsed(I0); |
| // allUsed |
| EXPECT_TRUE(SBO.allUsed()); |
| // isUsed |
| EXPECT_TRUE(SBO.isUsed(0)); |
| // getNumUnusedBits after setUsed |
| EXPECT_EQ(SBO.getNumUnusedBits(), 0u); |
| // insertAt |
| SBO.insertAt(SBO.end(), I1); |
| EXPECT_NE(*SBO.begin(), I1); |
| // getNumUnusedBits after insertAt |
| EXPECT_EQ(SBO.getNumUnusedBits(), IOBits); |
| // allUsed |
| EXPECT_FALSE(SBO.allUsed()); |
| // getFirstUnusedElement |
| EXPECT_EQ(SBO.getFirstUnusedElementIdx(), 1u); |
| |
| SmallVector<sandboxir::Instruction *> Insts; |
| // add2 through add7 |
| Insts.push_back(&*It++); |
| Insts.push_back(&*It++); |
| Insts.push_back(&*It++); |
| Insts.push_back(&*It++); |
| Insts.push_back(&*It++); |
| Insts.push_back(&*It++); |
| unsigned BundleBits = 0; |
| for (auto &S : Insts) |
| BundleBits += sandboxir::Utils::getNumBits(S); |
| // Ensure the instructions are as expected. |
| EXPECT_EQ(BundleBits, 88u); |
| auto Seeds = Insts; |
| // Constructor |
| SeedBundleForTest SB1(std::move(Seeds)); |
| // getNumUnusedBits after constructor |
| EXPECT_EQ(SB1.getNumUnusedBits(), BundleBits); |
| // setUsed with index |
| SB1.setUsed(1); |
| // getFirstUnusedElementIdx |
| EXPECT_EQ(SB1.getFirstUnusedElementIdx(), 0u); |
| SB1.setUsed(unsigned(0)); |
| // getFirstUnusedElementIdx not at end |
| EXPECT_EQ(SB1.getFirstUnusedElementIdx(), 2u); |
| |
| // getSlice is (StartIdx, MaxVecRegBits, ForcePowerOf2). It's easier to |
| // compare test cases without the parameter-name comments inline. |
| auto Slice0 = SB1.getSlice(2, 64, true); |
| EXPECT_THAT(Slice0, |
| testing::ElementsAre(Insts[2], Insts[3], Insts[4], Insts[5])); |
| auto Slice1 = SB1.getSlice(2, 72, true); |
| EXPECT_THAT(Slice1, |
| testing::ElementsAre(Insts[2], Insts[3], Insts[4], Insts[5])); |
| auto Slice2 = SB1.getSlice(2, 80, true); |
| EXPECT_THAT(Slice2, |
| testing::ElementsAre(Insts[2], Insts[3], Insts[4], Insts[5])); |
| |
| SB1.setUsed(2); |
| auto Slice3 = SB1.getSlice(3, 64, false); |
| EXPECT_THAT(Slice3, testing::ElementsAre(Insts[3], Insts[4], Insts[5])); |
| // getSlice empty case |
| SB1.setUsed(3); |
| auto Slice4 = SB1.getSlice(4, /* MaxVecRegBits */ 8, |
| /* ForcePowerOf2 */ true); |
| EXPECT_EQ(Slice4.size(), 0u); |
| } |
| |
| TEST_F(SeedBundleTest, MemSeedBundle) { |
| parseIR(C, R"IR( |
| define void @foo(ptr %ptrA, float %val, ptr %ptr) { |
| bb: |
| %gep0 = getelementptr float, ptr %ptr, i32 0 |
| %gep1 = getelementptr float, ptr %ptr, i32 1 |
| %gep2 = getelementptr float, ptr %ptr, i32 3 |
| %gep3 = getelementptr float, ptr %ptr, i32 4 |
| store float %val, ptr %gep0 |
| store float %val, ptr %gep1 |
| store float %val, ptr %gep2 |
| store float %val, ptr %gep3 |
| |
| load float, ptr %gep0 |
| load float, ptr %gep1 |
| load float, ptr %gep2 |
| load float, ptr %gep3 |
| |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto *BB = &*F.begin(); |
| auto It = std::next(BB->begin(), 4); |
| auto *S0 = cast<sandboxir::StoreInst>(&*It++); |
| auto *S1 = cast<sandboxir::StoreInst>(&*It++); |
| auto *S2 = cast<sandboxir::StoreInst>(&*It++); |
| auto *S3 = cast<sandboxir::StoreInst>(&*It++); |
| |
| // Single instruction constructor; test insert out of memory order |
| sandboxir::StoreSeedBundle SB(S3); |
| SB.insert(S1, SE); |
| SB.insert(S2, SE); |
| SB.insert(S0, SE); |
| EXPECT_THAT(SB, testing::ElementsAre(S0, S1, S2, S3)); |
| |
| // Instruction list constructor; test list out of order |
| auto *L0 = cast<sandboxir::LoadInst>(&*It++); |
| auto *L1 = cast<sandboxir::LoadInst>(&*It++); |
| auto *L2 = cast<sandboxir::LoadInst>(&*It++); |
| auto *L3 = cast<sandboxir::LoadInst>(&*It++); |
| SmallVector<sandboxir::Instruction *> Loads; |
| Loads.push_back(L1); |
| Loads.push_back(L3); |
| Loads.push_back(L2); |
| Loads.push_back(L0); |
| sandboxir::LoadSeedBundle LB(std::move(Loads), SE); |
| EXPECT_THAT(LB, testing::ElementsAre(L0, L1, L2, L3)); |
| } |
| |
| TEST_F(SeedBundleTest, Container) { |
| parseIR(C, R"IR( |
| define void @foo(ptr %ptrA, float %val, ptr %ptrB) { |
| bb: |
| %gepA0 = getelementptr float, ptr %ptrA, i32 0 |
| %gepA1 = getelementptr float, ptr %ptrA, i32 1 |
| %gepB0 = getelementptr float, ptr %ptrB, i32 0 |
| %gepB1 = getelementptr float, ptr %ptrB, i32 1 |
| store float %val, ptr %gepA0 |
| store float %val, ptr %gepA1 |
| store float %val, ptr %gepB0 |
| store float %val, ptr %gepB1 |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto &BB = *F.begin(); |
| auto It = std::next(BB.begin(), 4); |
| auto *S0 = cast<sandboxir::StoreInst>(&*It++); |
| auto *S1 = cast<sandboxir::StoreInst>(&*It++); |
| auto *S2 = cast<sandboxir::StoreInst>(&*It++); |
| auto *S3 = cast<sandboxir::StoreInst>(&*It++); |
| sandboxir::SeedContainer SC(SE); |
| // Check begin() end() when empty. |
| EXPECT_EQ(SC.begin(), SC.end()); |
| |
| SC.insert(S0, /*AllowDiffTypes=*/false); |
| SC.insert(S1, /*AllowDiffTypes=*/false); |
| SC.insert(S2, /*AllowDiffTypes=*/false); |
| SC.insert(S3, /*AllowDiffTypes=*/false); |
| unsigned Cnt = 0; |
| SmallVector<sandboxir::SeedBundle *> Bndls; |
| for (auto &SeedBndl : SC) { |
| EXPECT_EQ(SeedBndl.size(), 2u); |
| ++Cnt; |
| Bndls.push_back(&SeedBndl); |
| } |
| EXPECT_EQ(Cnt, 2u); |
| |
| // Mark them "Used" to check if operator++ skips them in the next loop. |
| for (auto *SeedBndl : Bndls) |
| for (auto Lane : seq<unsigned>(SeedBndl->size())) |
| SeedBndl->setUsed(Lane); |
| // Check if iterator::operator++ skips used lanes. |
| Cnt = 0; |
| for (auto &SeedBndl : SC) { |
| (void)SeedBndl; |
| ++Cnt; |
| } |
| EXPECT_EQ(Cnt, 0u); |
| } |
| |
| TEST_F(SeedBundleTest, ConsecutiveStores) { |
| // Where "Consecutive" means the stores address consecutive locations in |
| // memory, but not in program order. Check to see that the collector puts them |
| // in the proper order for vectorization. |
| parseIR(C, R"IR( |
| define void @foo(ptr noalias %ptr, float %val) { |
| bb: |
| %ptr0 = getelementptr float, ptr %ptr, i32 0 |
| %ptr1 = getelementptr float, ptr %ptr, i32 1 |
| %ptr2 = getelementptr float, ptr %ptr, i32 2 |
| %ptr3 = getelementptr float, ptr %ptr, i32 3 |
| store float %val, ptr %ptr0 |
| store float %val, ptr %ptr2 |
| store float %val, ptr %ptr1 |
| store float %val, ptr %ptr3 |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto BB = F.begin(); |
| sandboxir::SeedCollector SC(&*BB, SE, /*CollectStores=*/true, |
| /*CollectLoads=*/false); |
| |
| // Find the stores |
| auto It = std::next(BB->begin(), 4); |
| // StX with X as the order by offset in memory |
| auto *St0 = &*It++; |
| auto *St2 = &*It++; |
| auto *St1 = &*It++; |
| auto *St3 = &*It++; |
| |
| auto StoreSeedsRange = SC.getStoreSeeds(); |
| auto &SB = *StoreSeedsRange.begin(); |
| // Expect just one vector of store seeds |
| EXPECT_EQ(range_size(StoreSeedsRange), 1u); |
| ExpectThatElementsAre(SB, {St0, St1, St2, St3}); |
| } |
| |
| TEST_F(SeedBundleTest, StoresWithGaps) { |
| parseIR(C, R"IR( |
| define void @foo(ptr noalias %ptr, float %val) { |
| bb: |
| %ptr0 = getelementptr float, ptr %ptr, i32 0 |
| %ptr1 = getelementptr float, ptr %ptr, i32 3 |
| %ptr2 = getelementptr float, ptr %ptr, i32 5 |
| %ptr3 = getelementptr float, ptr %ptr, i32 7 |
| store float %val, ptr %ptr0 |
| store float %val, ptr %ptr2 |
| store float %val, ptr %ptr1 |
| store float %val, ptr %ptr3 |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto BB = F.begin(); |
| sandboxir::SeedCollector SC(&*BB, SE, /*CollectStores=*/true, |
| /*CollectLoads=*/false); |
| |
| // Find the stores |
| auto It = std::next(BB->begin(), 4); |
| // StX with X as the order by offset in memory |
| auto *St0 = &*It++; |
| auto *St2 = &*It++; |
| auto *St1 = &*It++; |
| auto *St3 = &*It++; |
| |
| auto StoreSeedsRange = SC.getStoreSeeds(); |
| auto &SB = *StoreSeedsRange.begin(); |
| // Expect just one vector of store seeds |
| EXPECT_EQ(range_size(StoreSeedsRange), 1u); |
| ExpectThatElementsAre(SB, {St0, St1, St2, St3}); |
| // Check that the EraseInstr callback works. |
| |
| // TODO: Range_size counts fully used-bundles even though the iterator skips |
| // them. Further, iterating over anything other than the Bundles in a |
| // SeedContainer includes used seeds. So for now just check that removing all |
| // the seeds from a bundle also empties the bundle. |
| St0->eraseFromParent(); |
| St1->eraseFromParent(); |
| St2->eraseFromParent(); |
| St3->eraseFromParent(); |
| size_t nonEmptyBundleCount = 0; |
| for (auto &B : SC.getStoreSeeds()) { |
| (void)B; |
| nonEmptyBundleCount++; |
| } |
| EXPECT_EQ(nonEmptyBundleCount, 0u); |
| } |
| |
| TEST_F(SeedBundleTest, VectorStores) { |
| parseIR(C, R"IR( |
| define void @foo(ptr noalias %ptr, <2 x float> %val0, i64 %val1) { |
| bb: |
| %ptr0 = getelementptr float, ptr %ptr, i32 0 |
| %ptr1 = getelementptr float, ptr %ptr, i32 1 |
| %ptr2 = getelementptr i64, ptr %ptr, i32 2 |
| store <2 x float> %val0, ptr %ptr1 |
| store <2 x float> %val0, ptr %ptr0 |
| store atomic i64 %val1, ptr %ptr2 unordered, align 8 |
| store volatile i64 %val1, ptr %ptr2 |
| |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto BB = F.begin(); |
| sandboxir::SeedCollector SC(&*BB, SE, /*CollectStores=*/true, |
| /*CollectLoads=*/false); |
| |
| // Find the stores |
| auto It = std::next(BB->begin(), 3); |
| // StX with X as the order by offset in memory |
| auto *St1 = &*It++; |
| auto *St0 = &*It++; |
| |
| auto StoreSeedsRange = SC.getStoreSeeds(); |
| EXPECT_EQ(range_size(StoreSeedsRange), 1u); |
| auto &SB = *StoreSeedsRange.begin(); |
| // isValidMemSeed check: The atomic and volatile stores should not |
| // be included in the bundle, but the vector stores should be. |
| ExpectThatElementsAre(SB, {St0, St1}); |
| } |
| |
| TEST_F(SeedBundleTest, MixedScalarVectors) { |
| parseIR(C, R"IR( |
| define void @foo(ptr noalias %ptr, float %v, <2 x float> %val) { |
| bb: |
| %ptr0 = getelementptr float, ptr %ptr, i32 0 |
| %ptr1 = getelementptr float, ptr %ptr, i32 1 |
| %ptr3 = getelementptr float, ptr %ptr, i32 3 |
| store float %v, ptr %ptr0 |
| store float %v, ptr %ptr3 |
| store <2 x float> %val, ptr %ptr1 |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto BB = F.begin(); |
| sandboxir::SeedCollector SC(&*BB, SE, /*CollectStores=*/true, |
| /*CollectLoads=*/false); |
| |
| // Find the stores |
| auto It = std::next(BB->begin(), 3); |
| // StX with X as the order by offset in memory |
| auto *St0 = &*It++; |
| auto *St3 = &*It++; |
| auto *St1 = &*It++; |
| |
| auto StoreSeedsRange = SC.getStoreSeeds(); |
| EXPECT_EQ(range_size(StoreSeedsRange), 1u); |
| auto &SB = *StoreSeedsRange.begin(); |
| // isValidMemSeedCheck here: all of the three stores should be included. |
| ExpectThatElementsAre(SB, {St0, St1, St3}); |
| } |
| |
| TEST_F(SeedBundleTest, DiffTypes) { |
| parseIR(C, R"IR( |
| define void @foo(ptr noalias %ptr, i8 %v, i16 %v16) { |
| bb: |
| %ptr0 = getelementptr i8, ptr %ptr, i32 0 |
| %ptr1 = getelementptr i8, ptr %ptr, i32 1 |
| %ptr3 = getelementptr i8, ptr %ptr, i32 3 |
| store i8 %v, ptr %ptr0 |
| store i8 %v, ptr %ptr3 |
| store i16 %v16, ptr %ptr1 |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto BB = F.begin(); |
| auto It = std::next(BB->begin(), 3); |
| auto *St0 = &*It++; |
| auto *St3 = &*It++; |
| auto *St1 = &*It++; |
| |
| sandboxir::SeedCollector SC(&*BB, SE, /*CollectStores=*/true, |
| /*CollectLoads=*/false, /*AllowDiffTypes=*/true); |
| |
| auto StoreSeedsRange = SC.getStoreSeeds(); |
| EXPECT_EQ(range_size(StoreSeedsRange), 1u); |
| auto &SB = *StoreSeedsRange.begin(); |
| ExpectThatElementsAre(SB, {St0, St1, St3}); |
| } |
| |
| TEST_F(SeedBundleTest, VectorLoads) { |
| parseIR(C, R"IR( |
| define void @foo(ptr noalias %ptr, <2 x float> %val0) { |
| bb: |
| %ptr0 = getelementptr float, ptr %ptr, i32 0 |
| %ptr1 = getelementptr float, ptr %ptr, i32 1 |
| %r0 = load <2 x float>, ptr %ptr0 |
| %r1 = load <2 x float>, ptr %ptr1 |
| %r2 = load atomic i64, ptr %ptr0 unordered, align 8 |
| %r3 = load volatile i64, ptr %ptr1 |
| %r4 = load void()*, ptr %ptr1 |
| |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| DominatorTree DT(LLVMF); |
| TargetLibraryInfoImpl TLII(M->getTargetTriple()); |
| TargetLibraryInfo TLI(TLII); |
| DataLayout DL(M->getDataLayout()); |
| LoopInfo LI(DT); |
| AssumptionCache AC(LLVMF); |
| ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); |
| |
| sandboxir::Context Ctx(C); |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto BB = F.begin(); |
| sandboxir::SeedCollector SC(&*BB, SE, /*CollectStores=*/false, |
| /*CollectLoads=*/true); |
| |
| // Find the loads |
| auto It = std::next(BB->begin(), 2); |
| // StX with X as the order by offset in memory |
| auto *Ld0 = cast<sandboxir::LoadInst>(&*It++); |
| auto *Ld1 = cast<sandboxir::LoadInst>(&*It++); |
| |
| auto LoadSeedsRange = SC.getLoadSeeds(); |
| EXPECT_EQ(range_size(LoadSeedsRange), 2u); |
| auto &SB = *LoadSeedsRange.begin(); |
| // isValidMemSeed check: The atomic and volatile loads should not |
| // be included in the bundle, the vector stores should be, but the |
| // void-typed load should not. |
| ExpectThatElementsAre(SB, {Ld0, Ld1}); |
| } |