blob: 82b230d50c4ec9538b52ac7963aaa83a2c78ea96 [file] [log] [blame]
//===- 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;
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;
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;
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);
SC.insert(S1);
SC.insert(S2);
SC.insert(S3);
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);
}