//===- SchedulerTest.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/Scheduler.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/BasicAliasAnalysis.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/AsmParser/Parser.h" #include "llvm/IR/Dominators.h" #include "llvm/SandboxIR/Context.h" #include "llvm/SandboxIR/Function.h" #include "llvm/SandboxIR/Instruction.h" #include "llvm/Support/SourceMgr.h" #include "gmock/gmock-matchers.h" #include "gtest/gtest.h" using namespace llvm; struct SchedulerTest : public testing::Test { LLVMContext C; std::unique_ptr M; std::unique_ptr AC; std::unique_ptr DT; std::unique_ptr BAA; std::unique_ptr AA; void parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; M = parseAssemblyString(IR, Err, C); if (!M) Err.print("SchedulerTest", errs()); } AAResults &getAA(llvm::Function &LLVMF) { TargetLibraryInfoImpl TLII; TargetLibraryInfo TLI(TLII); AA = std::make_unique(TLI); AC = std::make_unique(LLVMF); DT = std::make_unique(LLVMF); BAA = std::make_unique(M->getDataLayout(), LLVMF, TLI, *AC, DT.get()); AA->addAAResult(*BAA); return *AA; } }; static sandboxir::BasicBlock *getBasicBlockByName(sandboxir::Function *F, StringRef Name) { for (sandboxir::BasicBlock &BB : *F) if (BB.getName() == Name) return &BB; llvm_unreachable("Expected to find basic block!"); } TEST_F(SchedulerTest, SchedBundle) { parseIR(C, R"IR( define void @foo(ptr %ptr, i8 %v0, i8 %v1) { store i8 %v0, ptr %ptr %other = add i8 %v0, %v1 store i8 %v1, ptr %ptr ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); auto *F = Ctx.createFunction(LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *S0 = cast(&*It++); auto *Other = &*It++; auto *S1 = cast(&*It++); auto *Ret = cast(&*It++); sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx); DAG.extend({&*BB->begin(), BB->getTerminator()}); auto *SN0 = DAG.getNode(S0); auto *SN1 = DAG.getNode(S1); sandboxir::SchedBundle Bndl({SN0, SN1}); // Check getTop(). EXPECT_EQ(Bndl.getTop(), SN0); // Check getBot(). EXPECT_EQ(Bndl.getBot(), SN1); // Check cluster(). Bndl.cluster(S1->getIterator()); { auto It = BB->begin(); EXPECT_EQ(&*It++, Other); EXPECT_EQ(&*It++, S0); EXPECT_EQ(&*It++, S1); EXPECT_EQ(&*It++, Ret); S0->moveBefore(Other); } Bndl.cluster(S0->getIterator()); { auto It = BB->begin(); EXPECT_EQ(&*It++, S0); EXPECT_EQ(&*It++, S1); EXPECT_EQ(&*It++, Other); EXPECT_EQ(&*It++, Ret); S1->moveAfter(Other); } Bndl.cluster(Other->getIterator()); { auto It = BB->begin(); EXPECT_EQ(&*It++, S0); EXPECT_EQ(&*It++, S1); EXPECT_EQ(&*It++, Other); EXPECT_EQ(&*It++, Ret); S1->moveAfter(Other); } Bndl.cluster(Ret->getIterator()); { auto It = BB->begin(); EXPECT_EQ(&*It++, Other); EXPECT_EQ(&*It++, S0); EXPECT_EQ(&*It++, S1); EXPECT_EQ(&*It++, Ret); Other->moveBefore(S1); } Bndl.cluster(BB->end()); { auto It = BB->begin(); EXPECT_EQ(&*It++, Other); EXPECT_EQ(&*It++, Ret); EXPECT_EQ(&*It++, S0); EXPECT_EQ(&*It++, S1); Ret->moveAfter(S1); Other->moveAfter(S0); } // Check iterators. EXPECT_THAT(Bndl, testing::ElementsAre(SN0, SN1)); EXPECT_THAT((const sandboxir::SchedBundle &)Bndl, testing::ElementsAre(SN0, SN1)); } TEST_F(SchedulerTest, Basic) { parseIR(C, R"IR( define void @foo(ptr %ptr, i8 %v0, i8 %v1) { store i8 %v0, ptr %ptr store i8 %v1, ptr %ptr ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); auto *F = Ctx.createFunction(LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *S0 = cast(&*It++); auto *S1 = cast(&*It++); auto *Ret = cast(&*It++); { // Schedule all instructions in sequence. sandboxir::Scheduler Sched(getAA(*LLVMF), Ctx); EXPECT_TRUE(Sched.trySchedule({Ret})); EXPECT_TRUE(Sched.trySchedule({S1})); EXPECT_TRUE(Sched.trySchedule({S0})); } { // Skip instructions. sandboxir::Scheduler Sched(getAA(*LLVMF), Ctx); EXPECT_TRUE(Sched.trySchedule({Ret})); EXPECT_TRUE(Sched.trySchedule({S0})); } { // Try invalid scheduling. Dependency S0->S1. sandboxir::Scheduler Sched(getAA(*LLVMF), Ctx); EXPECT_TRUE(Sched.trySchedule({Ret})); EXPECT_FALSE(Sched.trySchedule({S0, S1})); } } TEST_F(SchedulerTest, Bundles) { parseIR(C, R"IR( define void @foo(ptr noalias %ptr0, ptr noalias %ptr1) { %ld0 = load i8, ptr %ptr0 %ld1 = load i8, ptr %ptr1 store i8 %ld0, ptr %ptr0 store i8 %ld1, ptr %ptr1 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); auto *F = Ctx.createFunction(LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *L0 = cast(&*It++); auto *L1 = cast(&*It++); auto *S0 = cast(&*It++); auto *S1 = cast(&*It++); auto *Ret = cast(&*It++); sandboxir::Scheduler Sched(getAA(*LLVMF), Ctx); EXPECT_TRUE(Sched.trySchedule({Ret})); EXPECT_TRUE(Sched.trySchedule({S0, S1})); EXPECT_TRUE(Sched.trySchedule({L0, L1})); } TEST_F(SchedulerTest, RescheduleAlreadyScheduled) { parseIR(C, R"IR( define void @foo(ptr noalias %ptr0, ptr noalias %ptr1) { %ld0 = load i8, ptr %ptr0 %ld1 = load i8, ptr %ptr1 %add0 = add i8 %ld0, %ld0 %add1 = add i8 %ld1, %ld1 store i8 %add0, ptr %ptr0 store i8 %add1, ptr %ptr1 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); auto *F = Ctx.createFunction(LLVMF); auto *BB = &*F->begin(); auto It = BB->begin(); auto *L0 = cast(&*It++); auto *L1 = cast(&*It++); auto *Add0 = cast(&*It++); auto *Add1 = cast(&*It++); auto *S0 = cast(&*It++); auto *S1 = cast(&*It++); auto *Ret = cast(&*It++); sandboxir::Scheduler Sched(getAA(*LLVMF), Ctx); EXPECT_TRUE(Sched.trySchedule({Ret})); EXPECT_TRUE(Sched.trySchedule({S0, S1})); EXPECT_TRUE(Sched.trySchedule({L0, L1})); // At this point Add0 and Add1 should have been individually scheduled // as single bundles. // Check if rescheduling works. EXPECT_TRUE(Sched.trySchedule({Add0, Add1})); EXPECT_TRUE(Sched.trySchedule({L0, L1})); } TEST_F(SchedulerTest, DontCrossBBs) { parseIR(C, R"IR( define void @foo(ptr noalias %ptr0, ptr noalias %ptr1, i8 %v0, i8 %v1) { bb0: %add0 = add i8 %v0, 0 %add1 = add i8 %v1, 1 br label %bb1 bb1: store i8 %add0, ptr %ptr0 store i8 %add1, ptr %ptr1 ret void } )IR"); llvm::Function *LLVMF = &*M->getFunction("foo"); sandboxir::Context Ctx(C); auto *F = Ctx.createFunction(LLVMF); auto *BB0 = getBasicBlockByName(F, "bb0"); auto *BB1 = getBasicBlockByName(F, "bb1"); auto It = BB0->begin(); auto *Add0 = &*It++; auto *Add1 = &*It++; It = BB1->begin(); auto *S0 = cast(&*It++); auto *S1 = cast(&*It++); auto *Ret = cast(&*It++); { // Schedule bottom-up sandboxir::Scheduler Sched(getAA(*LLVMF), Ctx); EXPECT_TRUE(Sched.trySchedule({Ret})); EXPECT_TRUE(Sched.trySchedule({S0, S1})); // Scheduling across blocks should fail. EXPECT_FALSE(Sched.trySchedule({Add0, Add1})); } { // Schedule top-down sandboxir::Scheduler Sched(getAA(*LLVMF), Ctx); EXPECT_TRUE(Sched.trySchedule({Add0, Add1})); // Scheduling across blocks should fail. EXPECT_FALSE(Sched.trySchedule({S0, S1})); } }