mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 19:23:23 +01:00
504eb62dfb
Summary: See D37528 for a previous (non-deferred) version of this patch and its description. Preserves dominance in a deferred manner using a new class DeferredDominance. This reduces the performance impact of updating the DominatorTree at every edge insertion and deletion. A user may call DDT->flush() within JumpThreading for an up-to-date DT. This patch currently has one flush() at the end of runImpl() to ensure DT is preserved across the pass. LVI is also preserved to help subsequent passes such as CorrelatedValuePropagation. LVI is simpler to maintain and is done immediately (not deferred). The code to perform the preversation was minimally altered and simply marked as preserved for the PassManager to be informed. This extends the analysis available to JumpThreading for future enhancements such as threading across loop headers. Reviewers: dberlin, kuhar, sebpop Reviewed By: kuhar, sebpop Subscribers: mgorny, dmgreen, kuba, rnk, rsmith, hiraditya, llvm-commits Differential Revision: https://reviews.llvm.org/D40146 llvm-svn: 322401
345 lines
12 KiB
C++
345 lines
12 KiB
C++
//===- llvm/unittests/IR/DeferredDominanceTest.cpp - DDT unit tests -------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/AsmParser/Parser.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/Dominators.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static std::unique_ptr<Module> makeLLVMModule(LLVMContext &Context,
|
|
StringRef ModuleStr) {
|
|
SMDiagnostic Err;
|
|
std::unique_ptr<Module> M = parseAssemblyString(ModuleStr, Err, Context);
|
|
assert(M && "Bad LLVM IR?");
|
|
return M;
|
|
}
|
|
|
|
TEST(DeferredDominance, BasicOperations) {
|
|
StringRef FuncName = "f";
|
|
StringRef ModuleString =
|
|
"define i32 @f(i32 %i, i32 *%p) {\n"
|
|
" bb0:\n"
|
|
" store i32 %i, i32 *%p\n"
|
|
" switch i32 %i, label %bb1 [\n"
|
|
" i32 0, label %bb2\n"
|
|
" i32 1, label %bb2\n"
|
|
" i32 2, label %bb3\n"
|
|
" ]\n"
|
|
" bb1:\n"
|
|
" ret i32 1\n"
|
|
" bb2:\n"
|
|
" ret i32 2\n"
|
|
" bb3:\n"
|
|
" ret i32 3\n"
|
|
"}\n";
|
|
// Make the module.
|
|
LLVMContext Context;
|
|
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
|
|
Function *F = M->getFunction(FuncName);
|
|
ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
|
|
|
|
// Make the DDT.
|
|
DominatorTree DT(*F);
|
|
DeferredDominance DDT(DT);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
|
|
Function::iterator FI = F->begin();
|
|
BasicBlock *BB0 = &*FI++;
|
|
BasicBlock *BB1 = &*FI++;
|
|
BasicBlock *BB2 = &*FI++;
|
|
BasicBlock *BB3 = &*FI++;
|
|
|
|
// Test discards of invalid self-domination updates. These use the single
|
|
// short-hand interface but are still queued inside DDT.
|
|
DDT.deleteEdge(BB0, BB0);
|
|
DDT.insertEdge(BB1, BB1);
|
|
|
|
// Delete edge bb0 -> bb3 and push the update twice to verify duplicate
|
|
// entries are discarded.
|
|
std::vector<DominatorTree::UpdateType> Updates;
|
|
Updates.reserve(4);
|
|
Updates.push_back({DominatorTree::Delete, BB0, BB3});
|
|
Updates.push_back({DominatorTree::Delete, BB0, BB3});
|
|
|
|
// Unnecessary Insert: no edge bb1 -> bb2 after change to bb0.
|
|
Updates.push_back({DominatorTree::Insert, BB1, BB2});
|
|
// Unnecessary Delete: edge exists bb0 -> bb1 after change to bb0.
|
|
Updates.push_back({DominatorTree::Delete, BB0, BB1});
|
|
|
|
// CFG Change: remove edge bb0 -> bb3 and one duplicate edge bb0 -> bb2.
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 4u);
|
|
BB0->getTerminator()->eraseFromParent();
|
|
BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
|
|
|
|
// Deletion of a BasicBlock is an immediate event. We remove all uses to the
|
|
// contained Instructions and change the Terminator to "unreachable" when
|
|
// queued for deletion. Its parent is still F until DDT.flush() is called. We
|
|
// don't defer this action because it can cause problems for other transforms
|
|
// or analysis as it's part of the actual CFG. We only defer updates to the
|
|
// DominatorTree. This code will crash if it is placed before the
|
|
// BranchInst::Create() call above.
|
|
ASSERT_FALSE(isa<UnreachableInst>(BB3->getTerminator()));
|
|
EXPECT_FALSE(DDT.pendingDeletedBB(BB3));
|
|
DDT.deleteBB(BB3);
|
|
EXPECT_TRUE(DDT.pendingDeletedBB(BB3));
|
|
ASSERT_TRUE(isa<UnreachableInst>(BB3->getTerminator()));
|
|
EXPECT_EQ(BB3->getParent(), F);
|
|
|
|
// Verify. Updates to DDT must be applied *after* all changes to the CFG
|
|
// (including block deletion).
|
|
DDT.applyUpdates(Updates);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
}
|
|
|
|
TEST(DeferredDominance, PairedUpdate) {
|
|
StringRef FuncName = "f";
|
|
StringRef ModuleString =
|
|
"define i32 @f(i32 %i, i32 *%p) {\n"
|
|
" bb0:\n"
|
|
" store i32 %i, i32 *%p\n"
|
|
" switch i32 %i, label %bb1 [\n"
|
|
" i32 0, label %bb2\n"
|
|
" i32 1, label %bb2\n"
|
|
" ]\n"
|
|
" bb1:\n"
|
|
" ret i32 1\n"
|
|
" bb2:\n"
|
|
" ret i32 2\n"
|
|
"}\n";
|
|
// Make the module.
|
|
LLVMContext Context;
|
|
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
|
|
Function *F = M->getFunction(FuncName);
|
|
ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
|
|
|
|
// Make the DDT.
|
|
DominatorTree DT(*F);
|
|
DeferredDominance DDT(DT);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
|
|
Function::iterator FI = F->begin();
|
|
BasicBlock *BB0 = &*FI++;
|
|
BasicBlock *BB1 = &*FI++;
|
|
BasicBlock *BB2 = &*FI++;
|
|
|
|
// CFG Change: only edge from bb0 is bb0 -> bb1.
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);
|
|
BB0->getTerminator()->eraseFromParent();
|
|
BranchInst::Create(BB1, BB0);
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
|
|
|
|
// Must be done after the CFG change. The applyUpdate() routine analyzes the
|
|
// current state of the CFG.
|
|
DDT.deleteEdge(BB0, BB2);
|
|
|
|
// CFG Change: bb0 now has bb0 -> bb1 and bb0 -> bb2.
|
|
// With this change no dominance has been altered from the original IR. DT
|
|
// doesn't care if the type of TerminatorInstruction changed, only if the
|
|
// unique edges have.
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
|
|
BB0->getTerminator()->eraseFromParent();
|
|
BranchInst::Create(BB1, BB2, ConstantInt::getTrue(F->getContext()), BB0);
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
|
|
|
|
// Must be done after the CFG change. The applyUpdate() routine analyzes the
|
|
// current state of the CFG. This DDT update pairs with the previous one and
|
|
// is cancelled out before ever applying updates to DT.
|
|
DDT.insertEdge(BB0, BB2);
|
|
|
|
// Test the empty DeletedBB list.
|
|
EXPECT_FALSE(DDT.pendingDeletedBB(BB0));
|
|
EXPECT_FALSE(DDT.pendingDeletedBB(BB1));
|
|
EXPECT_FALSE(DDT.pendingDeletedBB(BB2));
|
|
|
|
// The DT has no changes, this flush() simply returns a reference to the
|
|
// internal DT calculated at the beginning of this test.
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
}
|
|
|
|
TEST(DeferredDominance, ReplaceEntryBB) {
|
|
StringRef FuncName = "f";
|
|
StringRef ModuleString =
|
|
"define i32 @f() {\n"
|
|
"bb0:\n"
|
|
" br label %bb1\n"
|
|
" bb1:\n"
|
|
" ret i32 1\n"
|
|
"}\n";
|
|
// Make the module.
|
|
LLVMContext Context;
|
|
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
|
|
Function *F = M->getFunction(FuncName);
|
|
ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
|
|
|
|
// Make the DDT.
|
|
DominatorTree DT(*F);
|
|
DeferredDominance DDT(DT);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
|
|
Function::iterator FI = F->begin();
|
|
BasicBlock *BB0 = &*FI++;
|
|
BasicBlock *BB1 = &*FI++;
|
|
|
|
// Add a block as the new function entry BB. We also link it to BB0.
|
|
BasicBlock *NewEntry =
|
|
BasicBlock::Create(F->getContext(), "new_entry", F, BB0);
|
|
BranchInst::Create(BB0, NewEntry);
|
|
EXPECT_EQ(F->begin()->getName(), NewEntry->getName());
|
|
EXPECT_TRUE(&F->getEntryBlock() == NewEntry);
|
|
|
|
// Insert the new edge between new_eentry -> bb0. Without this the
|
|
// recalculate() call below will not actually recalculate the DT as there
|
|
// are no changes pending and no blocks deleted.
|
|
DDT.insertEdge(NewEntry, BB0);
|
|
|
|
// Changing the Entry BB requires a full recalulation.
|
|
DDT.recalculate(*F);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
|
|
// CFG Change: remove new_edge -> bb0 and redirect to new_edge -> bb1.
|
|
EXPECT_EQ(NewEntry->getTerminator()->getNumSuccessors(), 1u);
|
|
NewEntry->getTerminator()->eraseFromParent();
|
|
BranchInst::Create(BB1, NewEntry);
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
|
|
|
|
// Update the DDT. At this point bb0 now has no predecessors but is still a
|
|
// Child of F.
|
|
DDT.applyUpdates({{DominatorTree::Delete, NewEntry, BB0},
|
|
{DominatorTree::Insert, NewEntry, BB1}});
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
|
|
// Now remove bb0 from F.
|
|
ASSERT_FALSE(isa<UnreachableInst>(BB0->getTerminator()));
|
|
EXPECT_FALSE(DDT.pendingDeletedBB(BB0));
|
|
DDT.deleteBB(BB0);
|
|
EXPECT_TRUE(DDT.pendingDeletedBB(BB0));
|
|
ASSERT_TRUE(isa<UnreachableInst>(BB0->getTerminator()));
|
|
EXPECT_EQ(BB0->getParent(), F);
|
|
|
|
// Perform a full recalculation of the DDT. It is not necessary here but we
|
|
// do this to test the case when there are no pending DT updates but there are
|
|
// pending deleted BBs.
|
|
DDT.recalculate(*F);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
}
|
|
|
|
TEST(DeferredDominance, InheritedPreds) {
|
|
StringRef FuncName = "f";
|
|
StringRef ModuleString =
|
|
"define i32 @f(i32 %i, i32 *%p) {\n"
|
|
" bb0:\n"
|
|
" store i32 %i, i32 *%p\n"
|
|
" switch i32 %i, label %bb1 [\n"
|
|
" i32 2, label %bb2\n"
|
|
" i32 3, label %bb3\n"
|
|
" ]\n"
|
|
" bb1:\n"
|
|
" br label %bb3\n"
|
|
" bb2:\n"
|
|
" br label %bb3\n"
|
|
" bb3:\n"
|
|
" ret i32 3\n"
|
|
"}\n";
|
|
// Make the module.
|
|
LLVMContext Context;
|
|
std::unique_ptr<Module> M = makeLLVMModule(Context, ModuleString);
|
|
Function *F = M->getFunction(FuncName);
|
|
ASSERT_NE(F, nullptr) << "Couldn't get function " << FuncName << ".";
|
|
|
|
// Make the DDT.
|
|
DominatorTree DT(*F);
|
|
DeferredDominance DDT(DT);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
|
|
Function::iterator FI = F->begin();
|
|
BasicBlock *BB0 = &*FI++;
|
|
BasicBlock *BB1 = &*FI++;
|
|
BasicBlock *BB2 = &*FI++;
|
|
BasicBlock *BB3 = &*FI++;
|
|
|
|
// There are several CFG locations where we have:
|
|
//
|
|
// pred1..predN
|
|
// | |
|
|
// +> curr <+ converted into: pred1..predN curr
|
|
// | | |
|
|
// v +> succ <+
|
|
// succ
|
|
//
|
|
// There is a specific shape of this we have to be careful of:
|
|
//
|
|
// pred1..predN
|
|
// || |
|
|
// |+> curr <+ converted into: pred1..predN curr
|
|
// | | | |
|
|
// | v +> succ <+
|
|
// +-> succ
|
|
//
|
|
// While the final CFG form is functionally identical the updates to
|
|
// DDT are not. In the first case we must have DDT.insertEdge(Pred1, Succ)
|
|
// while in the latter case we must *NOT* have DDT.insertEdge(Pred1, Succ).
|
|
|
|
// CFG Change: bb0 now only has bb0 -> bb1 and bb0 -> bb3. We are preparing to
|
|
// remove bb2.
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 3u);
|
|
BB0->getTerminator()->eraseFromParent();
|
|
BranchInst::Create(BB1, BB3, ConstantInt::getTrue(F->getContext()), BB0);
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
|
|
|
|
// Remove bb2 from F. This has to happen before the call to applyUpdates() for
|
|
// DDT to detect there is no longer an edge between bb2 -> bb3. The deleteBB()
|
|
// method converts bb2's TI into "unreachable".
|
|
ASSERT_FALSE(isa<UnreachableInst>(BB2->getTerminator()));
|
|
EXPECT_FALSE(DDT.pendingDeletedBB(BB2));
|
|
DDT.deleteBB(BB2);
|
|
EXPECT_TRUE(DDT.pendingDeletedBB(BB2));
|
|
ASSERT_TRUE(isa<UnreachableInst>(BB2->getTerminator()));
|
|
EXPECT_EQ(BB2->getParent(), F);
|
|
|
|
// Queue up the DDT updates.
|
|
std::vector<DominatorTree::UpdateType> Updates;
|
|
Updates.reserve(4);
|
|
Updates.push_back({DominatorTree::Delete, BB0, BB2});
|
|
Updates.push_back({DominatorTree::Delete, BB2, BB3});
|
|
|
|
// Handle the specific shape case next.
|
|
// CFG Change: bb0 now only branches to bb3. We are preparing to remove bb1.
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 2u);
|
|
BB0->getTerminator()->eraseFromParent();
|
|
BranchInst::Create(BB3, BB0);
|
|
EXPECT_EQ(BB0->getTerminator()->getNumSuccessors(), 1u);
|
|
|
|
// Remove bb1 from F. This has to happen before the call to applyUpdates() for
|
|
// DDT to detect there is no longer an edge between bb1 -> bb3. The deleteBB()
|
|
// method converts bb1's TI into "unreachable".
|
|
ASSERT_FALSE(isa<UnreachableInst>(BB1->getTerminator()));
|
|
EXPECT_FALSE(DDT.pendingDeletedBB(BB1));
|
|
DDT.deleteBB(BB1);
|
|
EXPECT_TRUE(DDT.pendingDeletedBB(BB1));
|
|
ASSERT_TRUE(isa<UnreachableInst>(BB1->getTerminator()));
|
|
EXPECT_EQ(BB1->getParent(), F);
|
|
|
|
// Update the DDT. In this case we don't call DDT.insertEdge(BB0, BB3) because
|
|
// the edge previously existed at the start of this test when DT was first
|
|
// created.
|
|
Updates.push_back({DominatorTree::Delete, BB0, BB1});
|
|
Updates.push_back({DominatorTree::Delete, BB1, BB3});
|
|
|
|
// Verify everything.
|
|
DDT.applyUpdates(Updates);
|
|
ASSERT_TRUE(DDT.flush().verify());
|
|
}
|