
It should not be possible to construct one without a triple. It would also be nice to delete TargetLibraryInfoWrapperPass, but that is more difficult.
1193 lines
41 KiB
C++
1193 lines
41 KiB
C++
//===- DependencyGraphTest.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/DependencyGraph.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/DataLayout.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 DependencyGraphTest : public testing::Test {
|
|
LLVMContext C;
|
|
std::unique_ptr<Module> M;
|
|
std::unique_ptr<AssumptionCache> AC;
|
|
std::unique_ptr<DominatorTree> DT;
|
|
std::unique_ptr<BasicAAResult> BAA;
|
|
std::unique_ptr<AAResults> AA;
|
|
std::unique_ptr<TargetLibraryInfoImpl> TLII;
|
|
std::unique_ptr<TargetLibraryInfo> TLI;
|
|
|
|
void parseIR(LLVMContext &C, const char *IR) {
|
|
SMDiagnostic Err;
|
|
M = parseAssemblyString(IR, Err, C);
|
|
if (!M) {
|
|
Err.print("DependencyGraphTest", errs());
|
|
return;
|
|
}
|
|
|
|
TLII = std::make_unique<TargetLibraryInfoImpl>(M->getTargetTriple());
|
|
TLI = std::make_unique<TargetLibraryInfo>(*TLII);
|
|
}
|
|
|
|
AAResults &getAA(llvm::Function &LLVMF) {
|
|
AA = std::make_unique<AAResults>(*TLI);
|
|
AC = std::make_unique<AssumptionCache>(LLVMF);
|
|
DT = std::make_unique<DominatorTree>(LLVMF);
|
|
BAA = std::make_unique<BasicAAResult>(M->getDataLayout(), LLVMF, *TLI, *AC,
|
|
DT.get());
|
|
AA->addAAResult(*BAA);
|
|
return *AA;
|
|
}
|
|
/// \Returns true if there is a dependency: SrcN->DstN.
|
|
bool memDependency(sandboxir::DGNode *SrcN, sandboxir::DGNode *DstN) {
|
|
if (auto *MemDstN = dyn_cast<sandboxir::MemDGNode>(DstN))
|
|
return MemDstN->hasMemPred(SrcN);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
TEST_F(DependencyGraphTest, isStackSaveOrRestoreIntrinsic) {
|
|
parseIR(C, R"IR(
|
|
declare void @llvm.sideeffect()
|
|
define void @foo(i8 %v1, ptr %ptr) {
|
|
%add = add i8 %v1, %v1
|
|
%stacksave = call ptr @llvm.stacksave()
|
|
call void @llvm.stackrestore(ptr %stacksave)
|
|
call void @llvm.sideeffect()
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
sandboxir::Function *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
auto *Add = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *StackSave = cast<sandboxir::CallInst>(&*It++);
|
|
auto *StackRestore = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Other = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
using DGNode = sandboxir::DGNode;
|
|
EXPECT_FALSE(DGNode::isStackSaveOrRestoreIntrinsic(Add));
|
|
EXPECT_TRUE(DGNode::isStackSaveOrRestoreIntrinsic(StackSave));
|
|
EXPECT_TRUE(DGNode::isStackSaveOrRestoreIntrinsic(StackRestore));
|
|
EXPECT_FALSE(DGNode::isStackSaveOrRestoreIntrinsic(Other));
|
|
EXPECT_FALSE(DGNode::isStackSaveOrRestoreIntrinsic(Ret));
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, Instruction_isMemDepCandidate) {
|
|
parseIR(C, R"IR(
|
|
declare void @llvm.fake.use(...)
|
|
declare void @llvm.sideeffect()
|
|
declare void @llvm.pseudoprobe(i64, i64, i32, i64)
|
|
declare void @bar()
|
|
define void @foo(i8 %v1, ptr %ptr) {
|
|
%add0 = add i8 %v1, %v1
|
|
%ld0 = load i8, ptr %ptr
|
|
store i8 %v1, ptr %ptr
|
|
call void @llvm.sideeffect()
|
|
call void @llvm.pseudoprobe(i64 42, i64 1, i32 0, i64 -1)
|
|
call void @llvm.fake.use(ptr %ptr)
|
|
call void @bar()
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
sandboxir::Function *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *Ld0 = cast<sandboxir::LoadInst>(&*It++);
|
|
auto *St0 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *SideEffect0 = cast<sandboxir::CallInst>(&*It++);
|
|
auto *PseudoProbe0 = cast<sandboxir::CallInst>(&*It++);
|
|
auto *OtherIntrinsic0 = cast<sandboxir::CallInst>(&*It++);
|
|
auto *CallBar = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
using DGNode = sandboxir::DGNode;
|
|
|
|
EXPECT_FALSE(DGNode::isMemDepCandidate(Add0));
|
|
EXPECT_TRUE(DGNode::isMemDepCandidate(Ld0));
|
|
EXPECT_TRUE(DGNode::isMemDepCandidate(St0));
|
|
EXPECT_FALSE(DGNode::isMemDepCandidate(SideEffect0));
|
|
EXPECT_FALSE(DGNode::isMemDepCandidate(PseudoProbe0));
|
|
EXPECT_TRUE(DGNode::isMemDepCandidate(OtherIntrinsic0));
|
|
EXPECT_TRUE(DGNode::isMemDepCandidate(CallBar));
|
|
EXPECT_FALSE(DGNode::isMemDepCandidate(Ret));
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, Instruction_isMemIntrinsic) {
|
|
parseIR(C, R"IR(
|
|
declare void @llvm.sideeffect()
|
|
declare void @llvm.pseudoprobe(i64)
|
|
declare void @llvm.assume(i1)
|
|
|
|
define void @foo(ptr %ptr, i1 %cond) {
|
|
call void @llvm.sideeffect()
|
|
call void @llvm.pseudoprobe(i64 42)
|
|
call void @llvm.assume(i1 %cond)
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
sandboxir::Function *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
auto *SideEffect = cast<sandboxir::IntrinsicInst>(&*It++);
|
|
auto *PseudoProbe = cast<sandboxir::IntrinsicInst>(&*It++);
|
|
auto *OtherIntrinsic = cast<sandboxir::IntrinsicInst>(&*It++);
|
|
|
|
using DGNode = sandboxir::DGNode;
|
|
EXPECT_FALSE(DGNode::isMemIntrinsic(SideEffect));
|
|
EXPECT_FALSE(DGNode::isMemIntrinsic(PseudoProbe));
|
|
EXPECT_TRUE(DGNode::isMemIntrinsic(OtherIntrinsic));
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, MemDGNode) {
|
|
parseIR(C, R"IR(
|
|
declare void @llvm.sideeffect()
|
|
declare void @llvm.pseudoprobe(i64, i64, i32, i64)
|
|
declare void @llvm.fake.use(...)
|
|
declare void @bar()
|
|
define void @foo(i8 %v1, ptr %ptr) {
|
|
store i8 %v1, ptr %ptr
|
|
%ld0 = load i8, ptr %ptr
|
|
%add = add i8 %v1, %v1
|
|
%stacksave = call ptr @llvm.stacksave()
|
|
call void @llvm.stackrestore(ptr %stacksave)
|
|
call void @llvm.sideeffect()
|
|
call void @llvm.pseudoprobe(i64 42, i64 1, i32 0, i64 -1)
|
|
call void @llvm.fake.use(ptr %ptr)
|
|
call void @bar()
|
|
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 *Store = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Load = cast<sandboxir::LoadInst>(&*It++);
|
|
auto *Add = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *StackSave = cast<sandboxir::CallInst>(&*It++);
|
|
auto *StackRestore = cast<sandboxir::CallInst>(&*It++);
|
|
auto *SideEffect = cast<sandboxir::CallInst>(&*It++);
|
|
auto *PseudoProbe = cast<sandboxir::CallInst>(&*It++);
|
|
auto *FakeUse = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Call = cast<sandboxir::CallInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Store)));
|
|
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Load)));
|
|
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Add)));
|
|
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(StackSave)));
|
|
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(StackRestore)));
|
|
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(SideEffect)));
|
|
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(PseudoProbe)));
|
|
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(FakeUse)));
|
|
EXPECT_TRUE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Call)));
|
|
EXPECT_FALSE(isa<llvm::sandboxir::MemDGNode>(DAG.getNode(Ret)));
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, 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<sandboxir::StoreInst>(&*It++);
|
|
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
auto Span = DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
// Check extend().
|
|
EXPECT_EQ(Span.top(), &*BB->begin());
|
|
EXPECT_EQ(Span.bottom(), BB->getTerminator());
|
|
|
|
auto *N0 = cast<sandboxir::MemDGNode>(DAG.getNode(S0));
|
|
auto *N1 = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
auto *N2 = DAG.getNode(Ret);
|
|
|
|
// Check getInstruction().
|
|
EXPECT_EQ(N0->getInstruction(), S0);
|
|
EXPECT_EQ(N1->getInstruction(), S1);
|
|
// Check hasMemPred()
|
|
EXPECT_TRUE(N1->hasMemPred(N0));
|
|
EXPECT_FALSE(N0->hasMemPred(N1));
|
|
|
|
// Check preds().
|
|
EXPECT_TRUE(N0->preds(DAG).empty());
|
|
EXPECT_THAT(N1->preds(DAG), testing::ElementsAre(N0));
|
|
|
|
// Check memPreds().
|
|
EXPECT_TRUE(N0->memPreds().empty());
|
|
EXPECT_THAT(N1->memPreds(), testing::ElementsAre(N0));
|
|
EXPECT_TRUE(N2->preds(DAG).empty());
|
|
|
|
// Check memSuccs().
|
|
EXPECT_THAT(N0->memSuccs(), testing::ElementsAre(N1));
|
|
|
|
// Check UnscheduledSuccs.
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u); // N1
|
|
EXPECT_EQ(N1->getNumUnscheduledSuccs(), 0u);
|
|
EXPECT_EQ(N2->getNumUnscheduledSuccs(), 0u);
|
|
|
|
// Check decrUnscheduledSuccs.
|
|
N0->decrUnscheduledSuccs();
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 0u);
|
|
#ifndef NDEBUG
|
|
EXPECT_DEATH(N0->decrUnscheduledSuccs(), ".*Counting.*");
|
|
#endif // NDEBUG
|
|
|
|
// Check scheduled(), setScheduled().
|
|
EXPECT_FALSE(N0->scheduled());
|
|
N0->setScheduled(true);
|
|
EXPECT_TRUE(N0->scheduled());
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, AddRemoveMemPred) {
|
|
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<sandboxir::StoreInst>(&*It++);
|
|
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
auto *N0 = cast<sandboxir::MemDGNode>(DAG.getNode(S0));
|
|
auto *N1 = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
|
|
// Check removeMemPred().
|
|
EXPECT_FALSE(N0->memSuccs().empty());
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u);
|
|
N1->removeMemPred(N0);
|
|
EXPECT_TRUE(N1->memPreds().empty());
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 0u);
|
|
|
|
// Check addMemPred().
|
|
N1->addMemPred(N0);
|
|
EXPECT_THAT(N1->memPreds(), testing::UnorderedElementsAre(N0));
|
|
EXPECT_THAT(N0->memSuccs(), testing::UnorderedElementsAre(N1));
|
|
EXPECT_THAT(N0->getNumUnscheduledSuccs(), 1u);
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, Preds) {
|
|
parseIR(C, R"IR(
|
|
declare ptr @bar(i8)
|
|
define i8 @foo(i8 %v0, i8 %v1) {
|
|
%add0 = add i8 %v0, %v0
|
|
%add1 = add i8 %v1, %v1
|
|
%add2 = add i8 %add0, %add1
|
|
%ptr = call ptr @bar(i8 %add1)
|
|
store i8 %add2, ptr %ptr
|
|
ret i8 %add2
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
|
|
auto *AddN0 = DAG.getNode(cast<sandboxir::BinaryOperator>(&*It++));
|
|
auto *AddN1 = DAG.getNode(cast<sandboxir::BinaryOperator>(&*It++));
|
|
auto *AddN2 = DAG.getNode(cast<sandboxir::BinaryOperator>(&*It++));
|
|
auto *CallN = DAG.getNode(cast<sandboxir::CallInst>(&*It++));
|
|
auto *StN = DAG.getNode(cast<sandboxir::StoreInst>(&*It++));
|
|
auto *RetN = DAG.getNode(cast<sandboxir::ReturnInst>(&*It++));
|
|
|
|
// Check preds().
|
|
EXPECT_THAT(AddN0->preds(DAG), testing::ElementsAre());
|
|
EXPECT_THAT(AddN1->preds(DAG), testing::ElementsAre());
|
|
EXPECT_THAT(AddN2->preds(DAG), testing::ElementsAre(AddN0, AddN1));
|
|
EXPECT_THAT(CallN->preds(DAG), testing::ElementsAre(AddN1));
|
|
EXPECT_THAT(StN->preds(DAG),
|
|
testing::UnorderedElementsAre(CallN, CallN, AddN2));
|
|
EXPECT_THAT(RetN->preds(DAG), testing::ElementsAre(AddN2));
|
|
|
|
// Check UnscheduledSuccs.
|
|
EXPECT_EQ(AddN0->getNumUnscheduledSuccs(), 1u); // AddN2
|
|
EXPECT_EQ(AddN1->getNumUnscheduledSuccs(), 2u); // AddN2, CallN
|
|
EXPECT_EQ(AddN2->getNumUnscheduledSuccs(), 2u); // StN, RetN
|
|
EXPECT_EQ(CallN->getNumUnscheduledSuccs(), 2u); // StN, StN
|
|
EXPECT_EQ(StN->getNumUnscheduledSuccs(), 0u);
|
|
EXPECT_EQ(RetN->getNumUnscheduledSuccs(), 0u);
|
|
}
|
|
|
|
// Make sure we don't get null predecessors even if they are outside the DAG.
|
|
TEST_F(DependencyGraphTest, NonNullPreds) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %val) {
|
|
%gep = getelementptr i8, ptr %ptr, i32 0
|
|
store i8 %val, ptr %gep
|
|
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();
|
|
[[maybe_unused]] auto *GEP = cast<sandboxir::GetElementPtrInst>(&*It++);
|
|
auto *S0 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
// The DAG doesn't include GEP.
|
|
DAG.extend({S0, Ret});
|
|
|
|
auto *S0N = DAG.getNode(S0);
|
|
// S0 has one operand (the GEP) that is outside the DAG and no memory
|
|
// predecessors. So pred_begin() should be == pred_end().
|
|
auto PredIt = S0N->preds_begin(DAG);
|
|
auto PredItE = S0N->preds_end(DAG);
|
|
EXPECT_EQ(PredIt, PredItE);
|
|
// Check preds().
|
|
for (auto *PredN : S0N->preds(DAG))
|
|
EXPECT_NE(PredN, nullptr);
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, MemDGNode_getPrevNode_getNextNode) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
|
|
store i8 %v0, ptr %ptr
|
|
add i8 %v0, %v0
|
|
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<sandboxir::StoreInst>(&*It++);
|
|
[[maybe_unused]] auto *Add = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
[[maybe_unused]] auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
|
|
auto *S0N = cast<sandboxir::MemDGNode>(DAG.getNode(S0));
|
|
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
|
|
EXPECT_EQ(S0N->getPrevNode(), nullptr);
|
|
EXPECT_EQ(S0N->getNextNode(), S1N);
|
|
|
|
EXPECT_EQ(S1N->getPrevNode(), S0N);
|
|
EXPECT_EQ(S1N->getNextNode(), nullptr);
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, DGNodeRange) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v0, i8 %v1) {
|
|
add i8 %v0, %v0
|
|
store i8 %v0, ptr %ptr
|
|
add i8 %v0, %v0
|
|
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 *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *S0 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Add1 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
|
|
auto *S0N = cast<sandboxir::MemDGNode>(DAG.getNode(S0));
|
|
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
|
|
// Check getTopMemDGNode().
|
|
using B = sandboxir::MemDGNodeIntervalBuilder;
|
|
using InstrInterval = sandboxir::Interval<sandboxir::Instruction>;
|
|
EXPECT_EQ(B::getTopMemDGNode(InstrInterval(S0, S0), DAG), S0N);
|
|
EXPECT_EQ(B::getTopMemDGNode(InstrInterval(S0, Ret), DAG), S0N);
|
|
EXPECT_EQ(B::getTopMemDGNode(InstrInterval(Add0, Add1), DAG), S0N);
|
|
EXPECT_EQ(B::getTopMemDGNode(InstrInterval(Add0, Add0), DAG), nullptr);
|
|
|
|
// Check getBotMemDGNode().
|
|
EXPECT_EQ(B::getBotMemDGNode(InstrInterval(S1, S1), DAG), S1N);
|
|
EXPECT_EQ(B::getBotMemDGNode(InstrInterval(Add0, S1), DAG), S1N);
|
|
EXPECT_EQ(B::getBotMemDGNode(InstrInterval(Add0, Ret), DAG), S1N);
|
|
EXPECT_EQ(B::getBotMemDGNode(InstrInterval(Ret, Ret), DAG), nullptr);
|
|
|
|
// Check empty range.
|
|
EXPECT_THAT(sandboxir::MemDGNodeIntervalBuilder::makeEmpty(),
|
|
testing::ElementsAre());
|
|
|
|
// Returns the pointers in Range.
|
|
auto getPtrVec = [](const auto &Range) {
|
|
SmallVector<const sandboxir::DGNode *> Vec;
|
|
for (const sandboxir::DGNode &N : Range)
|
|
Vec.push_back(&N);
|
|
return Vec;
|
|
};
|
|
// Both TopN and BotN are memory.
|
|
EXPECT_THAT(
|
|
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({S0, S1}, DAG)),
|
|
testing::ElementsAre(S0N, S1N));
|
|
// Only TopN is memory.
|
|
EXPECT_THAT(
|
|
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({S0, Ret}, DAG)),
|
|
testing::ElementsAre(S0N, S1N));
|
|
EXPECT_THAT(
|
|
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({S0, Add1}, DAG)),
|
|
testing::ElementsAre(S0N));
|
|
// Only BotN is memory.
|
|
EXPECT_THAT(
|
|
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, S1}, DAG)),
|
|
testing::ElementsAre(S0N, S1N));
|
|
EXPECT_THAT(
|
|
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, S0}, DAG)),
|
|
testing::ElementsAre(S0N));
|
|
// Neither TopN or BotN is memory.
|
|
EXPECT_THAT(
|
|
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, Ret}, DAG)),
|
|
testing::ElementsAre(S0N, S1N));
|
|
EXPECT_THAT(
|
|
getPtrVec(sandboxir::MemDGNodeIntervalBuilder::make({Add0, Add0}, DAG)),
|
|
testing::ElementsAre());
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, AliasingStores) {
|
|
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();
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
auto It = BB->begin();
|
|
auto *Store0N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::StoreInst>(&*It++)));
|
|
auto *Store1N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::StoreInst>(&*It++)));
|
|
auto *RetN = DAG.getNode(cast<sandboxir::ReturnInst>(&*It++));
|
|
EXPECT_TRUE(Store0N->memPreds().empty());
|
|
EXPECT_THAT(Store1N->memPreds(), testing::ElementsAre(Store0N));
|
|
EXPECT_TRUE(RetN->preds(DAG).empty());
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, NonAliasingStores) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr noalias %ptr0, ptr noalias %ptr1, i8 %v0, i8 %v1) {
|
|
store i8 %v0, ptr %ptr0
|
|
store i8 %v1, ptr %ptr1
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
auto It = BB->begin();
|
|
auto *Store0N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::StoreInst>(&*It++)));
|
|
auto *Store1N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::StoreInst>(&*It++)));
|
|
auto *RetN = DAG.getNode(cast<sandboxir::ReturnInst>(&*It++));
|
|
// We expect no dependencies because the stores don't alias.
|
|
EXPECT_TRUE(Store0N->memPreds().empty());
|
|
EXPECT_TRUE(Store1N->memPreds().empty());
|
|
EXPECT_TRUE(RetN->preds(DAG).empty());
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, VolatileLoads) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr noalias %ptr0, ptr noalias %ptr1) {
|
|
%ld0 = load volatile i8, ptr %ptr0
|
|
%ld1 = load volatile i8, ptr %ptr1
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
auto It = BB->begin();
|
|
auto *Ld0N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::LoadInst>(&*It++)));
|
|
auto *Ld1N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::LoadInst>(&*It++)));
|
|
auto *RetN = DAG.getNode(cast<sandboxir::ReturnInst>(&*It++));
|
|
EXPECT_TRUE(Ld0N->memPreds().empty());
|
|
EXPECT_THAT(Ld1N->memPreds(), testing::ElementsAre(Ld0N));
|
|
EXPECT_TRUE(RetN->preds(DAG).empty());
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, VolatileStores) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr noalias %ptr0, ptr noalias %ptr1, i8 %v) {
|
|
store volatile i8 %v, ptr %ptr0
|
|
store volatile i8 %v, ptr %ptr1
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()});
|
|
auto It = BB->begin();
|
|
auto *Store0N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::StoreInst>(&*It++)));
|
|
auto *Store1N = cast<sandboxir::MemDGNode>(
|
|
DAG.getNode(cast<sandboxir::StoreInst>(&*It++)));
|
|
auto *RetN = DAG.getNode(cast<sandboxir::ReturnInst>(&*It++));
|
|
EXPECT_TRUE(Store0N->memPreds().empty());
|
|
EXPECT_THAT(Store1N->memPreds(), testing::ElementsAre(Store0N));
|
|
EXPECT_TRUE(RetN->preds(DAG).empty());
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, Call) {
|
|
parseIR(C, R"IR(
|
|
declare void @bar1()
|
|
declare void @bar2()
|
|
define void @foo(float %v1, float %v2) {
|
|
call void @bar1()
|
|
%add = fadd float %v1, %v2
|
|
call void @bar2()
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function *LLVMF = M->getFunction("foo");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()->getPrevNode()});
|
|
|
|
auto It = BB->begin();
|
|
auto *Call1N = cast<sandboxir::MemDGNode>(DAG.getNode(&*It++));
|
|
auto *AddN = DAG.getNode(&*It++);
|
|
auto *Call2N = cast<sandboxir::MemDGNode>(DAG.getNode(&*It++));
|
|
|
|
EXPECT_THAT(Call1N->memPreds(), testing::ElementsAre());
|
|
EXPECT_THAT(AddN->preds(DAG), testing::ElementsAre());
|
|
EXPECT_THAT(Call2N->memPreds(), testing::ElementsAre(Call1N));
|
|
}
|
|
|
|
// Check that there is a dependency: stacksave -> alloca -> stackrestore.
|
|
TEST_F(DependencyGraphTest, StackSaveRestoreInAlloca) {
|
|
parseIR(C, R"IR(
|
|
declare ptr @llvm.stacksave()
|
|
declare void @llvm.stackrestore(ptr %ptr)
|
|
|
|
define void @foo() {
|
|
%stack0 = call ptr @llvm.stacksave() ; Should depend on store
|
|
%alloca0 = alloca inalloca i8 ; Should depend on stacksave
|
|
call void @llvm.stackrestore(ptr %stack0) ; Should depend transiently on %alloca0
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function *LLVMF = M->getFunction("foo");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()->getPrevNode()});
|
|
|
|
auto It = BB->begin();
|
|
auto *StackSaveN = DAG.getNode(&*It++);
|
|
auto *AllocaN = DAG.getNode(&*It++);
|
|
auto *StackRestoreN = DAG.getNode(&*It++);
|
|
|
|
EXPECT_TRUE(memDependency(AllocaN, StackRestoreN));
|
|
EXPECT_TRUE(memDependency(StackSaveN, AllocaN));
|
|
}
|
|
|
|
// Checks that stacksave and stackrestore depend on other mem instrs.
|
|
TEST_F(DependencyGraphTest, StackSaveRestoreDependOnOtherMem) {
|
|
parseIR(C, R"IR(
|
|
declare ptr @llvm.stacksave()
|
|
declare void @llvm.stackrestore(ptr %ptr)
|
|
|
|
define void @foo(i8 %v0, i8 %v1, ptr %ptr) {
|
|
store volatile i8 %v0, ptr %ptr, align 4
|
|
%stack0 = call ptr @llvm.stacksave() ; Should depend on store
|
|
call void @llvm.stackrestore(ptr %stack0) ; Should depend on stacksave
|
|
store volatile i8 %v1, ptr %ptr, align 4 ; Should depend on stackrestore
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function *LLVMF = M->getFunction("foo");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()->getPrevNode()});
|
|
|
|
auto It = BB->begin();
|
|
auto *Store0N = DAG.getNode(&*It++);
|
|
auto *StackSaveN = DAG.getNode(&*It++);
|
|
auto *StackRestoreN = DAG.getNode(&*It++);
|
|
auto *Store1N = DAG.getNode(&*It++);
|
|
|
|
EXPECT_TRUE(memDependency(Store0N, StackSaveN));
|
|
EXPECT_TRUE(memDependency(StackSaveN, StackRestoreN));
|
|
EXPECT_TRUE(memDependency(StackRestoreN, Store1N));
|
|
}
|
|
|
|
// Make sure there is a dependency between a stackrestore and an alloca.
|
|
TEST_F(DependencyGraphTest, StackRestoreAndInAlloca) {
|
|
parseIR(C, R"IR(
|
|
declare void @llvm.stackrestore(ptr %ptr)
|
|
|
|
define void @foo(ptr %ptr) {
|
|
call void @llvm.stackrestore(ptr %ptr)
|
|
%alloca0 = alloca inalloca i8 ; Should depend on stackrestore
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function *LLVMF = M->getFunction("foo");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()->getPrevNode()});
|
|
|
|
auto It = BB->begin();
|
|
auto *StackRestoreN = DAG.getNode(&*It++);
|
|
auto *AllocaN = DAG.getNode(&*It++);
|
|
|
|
EXPECT_TRUE(memDependency(StackRestoreN, AllocaN));
|
|
}
|
|
|
|
// Make sure there is a dependency between the alloca and stacksave
|
|
TEST_F(DependencyGraphTest, StackSaveAndInAlloca) {
|
|
parseIR(C, R"IR(
|
|
declare ptr @llvm.stacksave()
|
|
|
|
define void @foo(ptr %ptr) {
|
|
%alloca0 = alloca inalloca i8 ; Should depend on stackrestore
|
|
%stack0 = call ptr @llvm.stacksave() ; Should depend on alloca0
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function *LLVMF = M->getFunction("foo");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()->getPrevNode()});
|
|
|
|
auto It = BB->begin();
|
|
auto *AllocaN = DAG.getNode(&*It++);
|
|
auto *StackSaveN = DAG.getNode(&*It++);
|
|
|
|
EXPECT_TRUE(memDependency(AllocaN, StackSaveN));
|
|
}
|
|
|
|
// A non-InAlloca in a stacksave-stackrestore region does not need extra
|
|
// dependencies.
|
|
TEST_F(DependencyGraphTest, StackSaveRestoreNoInAlloca) {
|
|
parseIR(C, R"IR(
|
|
declare ptr @llvm.stacksave()
|
|
declare void @llvm.stackrestore(ptr %ptr)
|
|
declare void @use(ptr %ptr)
|
|
|
|
define void @foo() {
|
|
%stack = call ptr @llvm.stacksave()
|
|
%alloca1 = alloca i8 ; No dependency
|
|
call void @llvm.stackrestore(ptr %stack)
|
|
ret void
|
|
}
|
|
)IR");
|
|
Function *LLVMF = M->getFunction("foo");
|
|
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *BB = &*F->begin();
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({&*BB->begin(), BB->getTerminator()->getPrevNode()});
|
|
|
|
auto It = BB->begin();
|
|
auto *StackSaveN = DAG.getNode(&*It++);
|
|
auto *AllocaN = DAG.getNode(&*It++);
|
|
auto *StackRestoreN = DAG.getNode(&*It++);
|
|
|
|
EXPECT_FALSE(memDependency(StackSaveN, AllocaN));
|
|
EXPECT_FALSE(memDependency(AllocaN, StackRestoreN));
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, Extend) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %v3, i8 %v4, i8 %v5) {
|
|
store i8 %v1, ptr %ptr
|
|
store i8 %v2, ptr %ptr
|
|
store i8 %v3, ptr %ptr
|
|
store i8 %v4, ptr %ptr
|
|
store i8 %v5, 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 *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S2 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S3 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S4 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S5 = cast<sandboxir::StoreInst>(&*It++);
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
{
|
|
// Scenario 1: Build new DAG
|
|
auto NewIntvl = DAG.extend({S3, S3});
|
|
EXPECT_EQ(NewIntvl, sandboxir::Interval<sandboxir::Instruction>(S3, S3));
|
|
EXPECT_EQ(DAG.getInterval().top(), S3);
|
|
EXPECT_EQ(DAG.getInterval().bottom(), S3);
|
|
[[maybe_unused]] auto *S3N = cast<sandboxir::MemDGNode>(DAG.getNode(S3));
|
|
// Check UnscheduledSuccs.
|
|
EXPECT_EQ(S3N->getNumUnscheduledSuccs(), 0u);
|
|
}
|
|
{
|
|
// Scenario 2: Extend below
|
|
auto NewIntvl = DAG.extend({S5, S5});
|
|
EXPECT_EQ(NewIntvl, sandboxir::Interval<sandboxir::Instruction>(S4, S5));
|
|
auto *S3N = cast<sandboxir::MemDGNode>(DAG.getNode(S3));
|
|
auto *S4N = cast<sandboxir::MemDGNode>(DAG.getNode(S4));
|
|
auto *S5N = cast<sandboxir::MemDGNode>(DAG.getNode(S5));
|
|
EXPECT_TRUE(S4N->hasMemPred(S3N));
|
|
EXPECT_TRUE(S5N->hasMemPred(S4N));
|
|
EXPECT_TRUE(S5N->hasMemPred(S3N));
|
|
// Check UnscheduledSuccs.
|
|
EXPECT_EQ(S3N->getNumUnscheduledSuccs(), 2u); // S4N, S5N
|
|
EXPECT_EQ(S4N->getNumUnscheduledSuccs(), 1u); // S5N
|
|
EXPECT_EQ(S5N->getNumUnscheduledSuccs(), 0u);
|
|
}
|
|
{
|
|
// Scenario 3: Extend above
|
|
auto NewIntvl = DAG.extend({S1, S2});
|
|
EXPECT_EQ(NewIntvl, sandboxir::Interval<sandboxir::Instruction>(S1, S2));
|
|
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
auto *S2N = cast<sandboxir::MemDGNode>(DAG.getNode(S2));
|
|
auto *S3N = cast<sandboxir::MemDGNode>(DAG.getNode(S3));
|
|
auto *S4N = cast<sandboxir::MemDGNode>(DAG.getNode(S4));
|
|
auto *S5N = cast<sandboxir::MemDGNode>(DAG.getNode(S5));
|
|
|
|
EXPECT_TRUE(S2N->hasMemPred(S1N));
|
|
|
|
EXPECT_TRUE(S3N->hasMemPred(S2N));
|
|
EXPECT_TRUE(S3N->hasMemPred(S1N));
|
|
|
|
EXPECT_TRUE(S4N->hasMemPred(S3N));
|
|
EXPECT_TRUE(S4N->hasMemPred(S2N));
|
|
EXPECT_TRUE(S4N->hasMemPred(S1N));
|
|
|
|
EXPECT_TRUE(S5N->hasMemPred(S4N));
|
|
EXPECT_TRUE(S5N->hasMemPred(S3N));
|
|
EXPECT_TRUE(S5N->hasMemPred(S2N));
|
|
EXPECT_TRUE(S5N->hasMemPred(S1N));
|
|
|
|
// Check UnscheduledSuccs.
|
|
EXPECT_EQ(S1N->getNumUnscheduledSuccs(), 4u); // S2N, S3N, S4N, S5N
|
|
EXPECT_EQ(S2N->getNumUnscheduledSuccs(), 3u); // S3N, S4N, S5N
|
|
EXPECT_EQ(S3N->getNumUnscheduledSuccs(), 2u); // S4N, S5N
|
|
EXPECT_EQ(S4N->getNumUnscheduledSuccs(), 1u); // S5N
|
|
EXPECT_EQ(S5N->getNumUnscheduledSuccs(), 0u);
|
|
}
|
|
|
|
{
|
|
// Check UnscheduledSuccs when a node is scheduled
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({S2, S2});
|
|
auto *S2N = cast<sandboxir::MemDGNode>(DAG.getNode(S2));
|
|
S2N->setScheduled(true);
|
|
|
|
DAG.extend({S1, S1});
|
|
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
EXPECT_EQ(S1N->getNumUnscheduledSuccs(), 0u); // S1 is scheduled
|
|
}
|
|
}
|
|
|
|
// Check that the DAG gets updated when we create a new instruction.
|
|
TEST_F(DependencyGraphTest, CreateInstrCallback) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %v3, i8 %new1, i8 %new2) {
|
|
store i8 %v1, ptr %ptr
|
|
store i8 %v2, ptr %ptr
|
|
store i8 %v3, 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 *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S2 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S3 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
// Create a DAG spanning S1 to S3.
|
|
DAG.extend({S1, S3});
|
|
auto *ArgNew1 = F->getArg(4);
|
|
auto *ArgNew2 = F->getArg(5);
|
|
auto *Ptr = S1->getPointerOperand();
|
|
|
|
auto *S1MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
auto *S2MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S2));
|
|
auto *S3MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S3));
|
|
sandboxir::MemDGNode *New1MemN = nullptr;
|
|
sandboxir::MemDGNode *New2MemN = nullptr;
|
|
{
|
|
// Create a new store before S3 (within the span of the DAG).
|
|
sandboxir::StoreInst *NewS =
|
|
sandboxir::StoreInst::create(ArgNew1, Ptr, Align(8), S3->getIterator(),
|
|
/*IsVolatile=*/true, Ctx);
|
|
// Check the MemDGNode chain.
|
|
New1MemN = cast<sandboxir::MemDGNode>(DAG.getNode(NewS));
|
|
EXPECT_EQ(S2MemN->getNextNode(), New1MemN);
|
|
EXPECT_EQ(New1MemN->getPrevNode(), S2MemN);
|
|
EXPECT_EQ(New1MemN->getNextNode(), S3MemN);
|
|
EXPECT_EQ(S3MemN->getPrevNode(), New1MemN);
|
|
|
|
// Check dependencies.
|
|
EXPECT_TRUE(memDependency(S1MemN, New1MemN));
|
|
EXPECT_TRUE(memDependency(S2MemN, New1MemN));
|
|
EXPECT_TRUE(memDependency(New1MemN, S3MemN));
|
|
}
|
|
{
|
|
// Create a new store before Ret (outside the current DAG).
|
|
sandboxir::StoreInst *NewS =
|
|
sandboxir::StoreInst::create(ArgNew2, Ptr, Align(8), Ret->getIterator(),
|
|
/*IsVolatile=*/true, Ctx);
|
|
// Check the MemDGNode chain.
|
|
New2MemN = cast<sandboxir::MemDGNode>(DAG.getNode(NewS));
|
|
EXPECT_EQ(S3MemN->getNextNode(), New2MemN);
|
|
EXPECT_EQ(New2MemN->getPrevNode(), S3MemN);
|
|
EXPECT_EQ(New2MemN->getNextNode(), nullptr);
|
|
|
|
// Check dependencies.
|
|
EXPECT_TRUE(memDependency(S1MemN, New2MemN));
|
|
EXPECT_TRUE(memDependency(S2MemN, New2MemN));
|
|
EXPECT_TRUE(memDependency(New1MemN, New2MemN));
|
|
EXPECT_TRUE(memDependency(S3MemN, New2MemN));
|
|
}
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, EraseInstrCallback) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %v3, i8 %v4, i8 %arg) {
|
|
store i8 %v1, ptr %ptr
|
|
store i8 %v2, ptr %ptr
|
|
store i8 %v3, ptr %ptr
|
|
store i8 %v4, 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 *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S2 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S3 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S4NotInDAG = cast<sandboxir::StoreInst>(&*It++);
|
|
|
|
// Check erase instruction callback.
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({S1, S3});
|
|
auto *S1MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
auto *S2MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S2));
|
|
auto *S3MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S3));
|
|
EXPECT_EQ(S1MemN->getNumUnscheduledSuccs(), 2u);
|
|
EXPECT_EQ(S2MemN->getNumUnscheduledSuccs(), 1u);
|
|
EXPECT_EQ(S3MemN->getNumUnscheduledSuccs(), 0u);
|
|
S2->eraseFromParent();
|
|
// Check that the DAG Node for S2 no longer exists.
|
|
auto *DeletedN = DAG.getNode(S2);
|
|
EXPECT_TRUE(DeletedN == nullptr);
|
|
// Check that dependencies are maintained.
|
|
EXPECT_THAT(S3MemN->preds(DAG), testing::UnorderedElementsAre(S1MemN));
|
|
// Also check that UnscheduledSuccs was updated for S1.
|
|
EXPECT_EQ(S1MemN->getNumUnscheduledSuccs(), 1u);
|
|
|
|
// Check the MemDGNode chain.
|
|
EXPECT_EQ(S1MemN->getNextNode(), S3MemN);
|
|
EXPECT_EQ(S3MemN->getPrevNode(), S1MemN);
|
|
|
|
// Check the chain when we erase the top node.
|
|
S1->eraseFromParent();
|
|
EXPECT_EQ(S3MemN->getPrevNode(), nullptr);
|
|
|
|
// Check that we don't crash if we erase a node not in the DAG.
|
|
S4NotInDAG->eraseFromParent();
|
|
}
|
|
|
|
// Same but check that we don't update UnscheduledSuccs when Node is scheduled.
|
|
TEST_F(DependencyGraphTest, EraseInstrCallbackScheduled) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %v3, i8 %v4, i8 %arg) {
|
|
store i8 %v1, ptr %ptr
|
|
store i8 %v2, 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 *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S2 = cast<sandboxir::StoreInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({S1, S2});
|
|
auto *S1MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
auto *S2MemN = cast<sandboxir::MemDGNode>(DAG.getNode(S2));
|
|
EXPECT_EQ(S1MemN->getNumUnscheduledSuccs(), 1u);
|
|
EXPECT_EQ(S2MemN->getNumUnscheduledSuccs(), 0u);
|
|
// Mark S2 as scheduled and erase it.
|
|
S2MemN->setScheduled(true);
|
|
S2->eraseFromParent();
|
|
EXPECT_EQ(DAG.getNode(S2), nullptr);
|
|
// Check that we did not update S1's UnscheduledSuccs
|
|
EXPECT_EQ(S1MemN->getNumUnscheduledSuccs(), 1u);
|
|
}
|
|
|
|
TEST_F(DependencyGraphTest, MoveInstrCallback) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, ptr %ptr2, i8 %v1, i8 %v2, i8 %v3, i8 %arg) {
|
|
%ld0 = load i8, ptr %ptr2
|
|
store i8 %v1, ptr %ptr
|
|
store i8 %v2, ptr %ptr
|
|
store i8 %v3, 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 *Ld = cast<sandboxir::LoadInst>(&*It++);
|
|
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S2 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S3 = cast<sandboxir::StoreInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({Ld, S3});
|
|
auto *LdN = cast<sandboxir::MemDGNode>(DAG.getNode(Ld));
|
|
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
auto *S2N = cast<sandboxir::MemDGNode>(DAG.getNode(S2));
|
|
EXPECT_EQ(S1N->getPrevNode(), LdN);
|
|
S1->moveBefore(Ld);
|
|
EXPECT_EQ(S1N->getPrevNode(), nullptr);
|
|
EXPECT_EQ(S1N->getNextNode(), LdN);
|
|
EXPECT_EQ(LdN->getPrevNode(), S1N);
|
|
EXPECT_EQ(LdN->getNextNode(), S2N);
|
|
}
|
|
|
|
// Check that the mem chain is maintained correctly when the move destination is
|
|
// not a mem node.
|
|
TEST_F(DependencyGraphTest, MoveInstrCallbackWithNonMemInstrs) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v1, i8 %v2, i8 %arg) {
|
|
%ld = load i8, ptr %ptr
|
|
%zext1 = zext i8 %arg to i32
|
|
%zext2 = zext i8 %arg to i32
|
|
store i8 %v1, ptr %ptr
|
|
store i8 %v2, 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 *Ld = cast<sandboxir::LoadInst>(&*It++);
|
|
[[maybe_unused]] auto *Zext1 = cast<sandboxir::CastInst>(&*It++);
|
|
auto *Zext2 = cast<sandboxir::CastInst>(&*It++);
|
|
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S2 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Ret = cast<sandboxir::ReturnInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({Ld, S2});
|
|
auto *LdN = cast<sandboxir::MemDGNode>(DAG.getNode(Ld));
|
|
auto *S1N = cast<sandboxir::MemDGNode>(DAG.getNode(S1));
|
|
auto *S2N = cast<sandboxir::MemDGNode>(DAG.getNode(S2));
|
|
EXPECT_EQ(LdN->getNextNode(), S1N);
|
|
EXPECT_EQ(S1N->getNextNode(), S2N);
|
|
|
|
S1->moveBefore(Zext2);
|
|
EXPECT_EQ(LdN->getNextNode(), S1N);
|
|
EXPECT_EQ(S1N->getNextNode(), S2N);
|
|
|
|
// Try move right after the end of the DAGInterval.
|
|
S1->moveBefore(Ret);
|
|
EXPECT_EQ(S2N->getNextNode(), S1N);
|
|
EXPECT_EQ(S1N->getNextNode(), nullptr);
|
|
}
|
|
|
|
// Extending an "Old" interval with no mem instructions.
|
|
TEST_F(DependencyGraphTest, ExtendDAGWithNoMem) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(ptr %ptr, i8 %v, i8 %v0, i8 %v1, i8 %v2, i8 %v3) {
|
|
store i8 %v0, ptr %ptr
|
|
store i8 %v1, ptr %ptr
|
|
%zext1 = zext i8 %v to i32
|
|
%zext2 = zext i8 %v to i32
|
|
store i8 %v2, ptr %ptr
|
|
store i8 %v3, 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<sandboxir::StoreInst>(&*It++);
|
|
auto *S1 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *Z1 = cast<sandboxir::CastInst>(&*It++);
|
|
auto *Z2 = cast<sandboxir::CastInst>(&*It++);
|
|
auto *S2 = cast<sandboxir::StoreInst>(&*It++);
|
|
auto *S3 = cast<sandboxir::StoreInst>(&*It++);
|
|
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
// Create a non-empty DAG that contains no memory instructions.
|
|
DAG.extend({Z1, Z2});
|
|
// Now extend it downwards.
|
|
DAG.extend({S2, S3});
|
|
EXPECT_TRUE(memDependency(DAG.getNode(S2), DAG.getNode(S3)));
|
|
|
|
// Same but upwards.
|
|
DAG.clear();
|
|
DAG.extend({Z1, Z2});
|
|
DAG.extend({S0, S1});
|
|
EXPECT_TRUE(memDependency(DAG.getNode(S0), DAG.getNode(S1)));
|
|
}
|
|
|
|
// Setting a Use with a setOperand(), RUW, RAUW etc. can add/remove use-def
|
|
// edges. This needs to maintain the UnscheduledSuccs counter.
|
|
TEST_F(DependencyGraphTest, MaintainUnscheduledSuccsOnUseSet) {
|
|
parseIR(C, R"IR(
|
|
define void @foo(i8 %v0, i8 %v1) {
|
|
%add0 = add i8 %v0, %v1
|
|
%add1 = add i8 %add0, %v1
|
|
ret void
|
|
}
|
|
)IR");
|
|
llvm::Function *LLVMF = &*M->getFunction("foo");
|
|
sandboxir::Context Ctx(C);
|
|
auto *F = Ctx.createFunction(LLVMF);
|
|
auto *Arg0 = F->getArg(0);
|
|
auto *BB = &*F->begin();
|
|
auto It = BB->begin();
|
|
auto *Add0 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
auto *Add1 = cast<sandboxir::BinaryOperator>(&*It++);
|
|
sandboxir::DependencyGraph DAG(getAA(*LLVMF), Ctx);
|
|
DAG.extend({Add0, Add1});
|
|
auto *N0 = DAG.getNode(Add0);
|
|
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u);
|
|
// Now change %add1 operand to not use %add0.
|
|
Add1->setOperand(0, Arg0);
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 0u);
|
|
// Restore it: %add0 is now used by %add1.
|
|
Add1->setOperand(0, Add0);
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u);
|
|
|
|
// RAUW
|
|
Add0->replaceAllUsesWith(Arg0);
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 0u);
|
|
// Restore it: %add0 is now used by %add1.
|
|
Add1->setOperand(0, Add0);
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u);
|
|
|
|
// RUWIf
|
|
Add0->replaceUsesWithIf(Arg0, [](const auto &U) { return true; });
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 0u);
|
|
// Restore it: %add0 is now used by %add1.
|
|
Add1->setOperand(0, Add0);
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u);
|
|
|
|
// RUOW
|
|
Add1->replaceUsesOfWith(Add0, Arg0);
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 0u);
|
|
// Restore it: %add0 is now used by %add1.
|
|
Add1->setOperand(0, Add0);
|
|
EXPECT_EQ(N0->getNumUnscheduledSuccs(), 1u);
|
|
}
|