[llvm] [SandboxIR] Add BasicBlock and adds functionality to Function and Context (PR #97637)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 8 14:16:47 PDT 2024
https://github.com/vporpo updated https://github.com/llvm/llvm-project/pull/97637
>From f41e973d4d8b5b4a39164fb3cf3f18934f795dae Mon Sep 17 00:00:00 2001
From: Vasileios Porpodas <vporpodas at google.com>
Date: Wed, 3 Jul 2024 14:08:43 -0700
Subject: [PATCH] [SandboxIR] Add BasicBlock and adds functionality to Function
and Context
We can now create SandboxIR from LLVM IR using the Context::create* functions.
---
llvm/include/llvm/SandboxIR/SandboxIR.h | 158 +++++++++++++
.../llvm/SandboxIR/SandboxIRValues.def | 1 +
llvm/lib/SandboxIR/SandboxIR.cpp | 219 +++++++++++++++++-
llvm/unittests/SandboxIR/SandboxIRTest.cpp | 135 +++++++++++
4 files changed, 509 insertions(+), 4 deletions(-)
diff --git a/llvm/include/llvm/SandboxIR/SandboxIR.h b/llvm/include/llvm/SandboxIR/SandboxIR.h
index ab6273a7ace66..7834c96b34169 100644
--- a/llvm/include/llvm/SandboxIR/SandboxIR.h
+++ b/llvm/include/llvm/SandboxIR/SandboxIR.h
@@ -62,12 +62,15 @@
#include "llvm/IR/User.h"
#include "llvm/IR/Value.h"
#include "llvm/Support/raw_ostream.h"
+#include <iterator>
namespace llvm {
namespace sandboxir {
+class Function;
class Context;
+class Instruction;
/// A SandboxIR Value has users. This is the base class.
class Value {
@@ -106,6 +109,8 @@ class Value {
/// NOTE: Some SBInstructions, like Packs, may include more than one value.
llvm::Value *Val = nullptr;
+ friend class Context; // For getting `Val`.
+
/// All values point to the context.
Context &Ctx;
// This is used by eraseFromParent().
@@ -205,6 +210,48 @@ class Constant : public sandboxir::User {
#endif
};
+/// The BasicBlock::iterator.
+class BBIterator {
+public:
+ using difference_type = std::ptrdiff_t;
+ using value_type = Instruction;
+ using pointer = value_type *;
+ using reference = value_type &;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+private:
+ llvm::BasicBlock *BB;
+ llvm::BasicBlock::iterator It;
+ Context *Ctx;
+ pointer getInstr(llvm::BasicBlock::iterator It) const;
+
+public:
+ BBIterator() : BB(nullptr), Ctx(nullptr) {}
+ BBIterator(llvm::BasicBlock *BB, llvm::BasicBlock::iterator It, Context *Ctx)
+ : BB(BB), It(It), Ctx(Ctx) {}
+ reference operator*() const { return *getInstr(It); }
+ BBIterator &operator++();
+ BBIterator operator++(int) {
+ auto Copy = *this;
+ ++*this;
+ return Copy;
+ }
+ BBIterator &operator--();
+ BBIterator operator--(int) {
+ auto Copy = *this;
+ --*this;
+ return Copy;
+ }
+ bool operator==(const BBIterator &Other) const {
+ assert(Ctx == Other.Ctx && "BBIterators in different context!");
+ return It == Other.It;
+ }
+ bool operator!=(const BBIterator &Other) const { return !(*this == Other); }
+ /// \Returns the SBInstruction that corresponds to this iterator, or null if
+ /// the instruction is not found in the IR-to-SandboxIR tables.
+ pointer get() const { return getInstr(It); }
+};
+
/// A sandboxir::User with operands and opcode.
class Instruction : public sandboxir::User {
public:
@@ -231,6 +278,8 @@ class Instruction : public sandboxir::User {
return OS;
}
#endif
+ /// This is used by BasicBlock::iterator.
+ virtual unsigned getNumOfIRInstrs() const = 0;
/// For isa/dyn_cast.
static bool classof(const sandboxir::Value *From);
@@ -256,6 +305,7 @@ class OpaqueInst : public sandboxir::Instruction {
static bool classof(const sandboxir::Value *From) {
return From->getSubclassID() == ClassID::Opaque;
}
+ unsigned getNumOfIRInstrs() const final { return 1u; }
#ifndef NDEBUG
void verify() const final {
// Nothing to do
@@ -270,6 +320,54 @@ class OpaqueInst : public sandboxir::Instruction {
#endif
};
+class BasicBlock : public Value {
+ /// Builds a graph that contains all values in \p BB in their original form
+ /// i.e., no vectorization is taking place here.
+ void buildBasicBlockFromLLVMIR(llvm::BasicBlock *LLVMBB);
+ friend class Context; // For `buildBasicBlockFromIR`
+
+public:
+ BasicBlock(llvm::BasicBlock *BB, Context &SBCtx)
+ : Value(ClassID::Block, BB, SBCtx) {
+ buildBasicBlockFromLLVMIR(BB);
+ }
+ ~BasicBlock() = default;
+ /// For isa/dyn_cast.
+ static bool classof(const Value *From) {
+ return From->getSubclassID() == Value::ClassID::Block;
+ }
+ Function *getParent() const;
+ using iterator = BBIterator;
+ iterator begin() const;
+ iterator end() const {
+ auto *BB = cast<llvm::BasicBlock>(Val);
+ return iterator(BB, BB->end(), &Ctx);
+ }
+ std::reverse_iterator<iterator> rbegin() const {
+ return std::make_reverse_iterator(end());
+ }
+ std::reverse_iterator<iterator> rend() const {
+ return std::make_reverse_iterator(begin());
+ }
+ Context &getContext() const { return Ctx; }
+ Instruction *getTerminator() const;
+ bool empty() const { return begin() == end(); }
+ Instruction &front() const;
+ Instruction &back() const;
+
+#ifndef NDEBUG
+ void verify() const final {
+ assert(isa<llvm::BasicBlock>(Val) && "Expected BasicBlock!");
+ }
+ friend raw_ostream &operator<<(raw_ostream &OS, const BasicBlock &SBBB) {
+ SBBB.dump(OS);
+ return OS;
+ }
+ void dump(raw_ostream &OS) const final;
+ LLVM_DUMP_METHOD void dump() const final;
+#endif
+};
+
class Context {
protected:
LLVMContext &LLVMCtx;
@@ -278,12 +376,54 @@ class Context {
DenseMap<llvm::Value *, std::unique_ptr<sandboxir::Value>>
LLVMValueToValueMap;
+ /// Take ownership of VPtr and store it in `LLVMValueToValueMap`.
+ Value *registerValue(std::unique_ptr<Value> &&VPtr);
+
+ Value *getOrCreateValueInternal(llvm::Value *V, llvm::User *U = nullptr);
+
+ template <typename SBT, typename LLVMT>
+ SBT *getOrCreateGeneric(llvm::Value *LLVMV) {
+ auto Pair = LLVMValueToValueMap.insert({LLVMV, nullptr});
+ auto It = Pair.first;
+ if (Pair.second) {
+ It->second = std::make_unique<SBT>(cast<LLVMT>(LLVMV), *this);
+ return cast<SBT>(It->second.get());
+ }
+ return cast<SBT>(It->second.get());
+ }
+
+ Value *getOrCreateValue(llvm::Value *LLVMV) {
+ return getOrCreateValueInternal(LLVMV, 0);
+ }
+
+ BasicBlock *createBasicBlock(llvm::BasicBlock *BB);
+
+ friend class BasicBlock; // For getOrCreateValue().
+
public:
Context(LLVMContext &LLVMCtx) : LLVMCtx(LLVMCtx) {}
+
sandboxir::Value *getValue(llvm::Value *V) const;
+ const sandboxir::Value *getValue(const llvm::Value *V) const {
+ return getValue(const_cast<llvm::Value *>(V));
+ }
+
+ Function *createFunction(llvm::Function *F);
+
+ /// \Returns the number of values registered with Context.
+ size_t getNumValues() const { return LLVMValueToValueMap.size(); }
};
class Function : public sandboxir::Value {
+ /// Helper for mapped_iterator.
+ struct LLVMBBToBB {
+ Context &Ctx;
+ LLVMBBToBB(Context &Ctx) : Ctx(Ctx) {}
+ BasicBlock &operator()(llvm::BasicBlock &LLVMBB) const {
+ return *cast<BasicBlock>(Ctx.getValue(&LLVMBB));
+ }
+ };
+
public:
Function(llvm::Function *F, sandboxir::Context &Ctx)
: sandboxir::Value(ClassID::Function, F, Ctx) {}
@@ -292,6 +432,24 @@ class Function : public sandboxir::Value {
return From->getSubclassID() == ClassID::Function;
}
+ Argument *getArg(unsigned Idx) const {
+ llvm::Argument *Arg = cast<llvm::Function>(Val)->getArg(Idx);
+ return cast<Argument>(Ctx.getValue(Arg));
+ }
+
+ size_t arg_size() const { return cast<llvm::Function>(Val)->arg_size(); }
+ bool arg_empty() const { return cast<llvm::Function>(Val)->arg_empty(); }
+
+ using iterator = mapped_iterator<llvm::Function::iterator, LLVMBBToBB>;
+ iterator begin() const {
+ LLVMBBToBB BBGetter(Ctx);
+ return iterator(cast<llvm::Function>(Val)->begin(), BBGetter);
+ }
+ iterator end() const {
+ LLVMBBToBB BBGetter(Ctx);
+ return iterator(cast<llvm::Function>(Val)->end(), BBGetter);
+ }
+
#ifndef NDEBUG
void verify() const final {
assert(isa<llvm::Function>(Val) && "Expected Function!");
diff --git a/llvm/include/llvm/SandboxIR/SandboxIRValues.def b/llvm/include/llvm/SandboxIR/SandboxIRValues.def
index 474b151ae03a4..b090ade3ea0ca 100644
--- a/llvm/include/llvm/SandboxIR/SandboxIRValues.def
+++ b/llvm/include/llvm/SandboxIR/SandboxIRValues.def
@@ -17,6 +17,7 @@ DEF_VALUE(Argument, Argument)
#define DEF_USER(ID, CLASS)
#endif
DEF_USER(User, User)
+DEF_VALUE(Block, BasicBlock)
DEF_USER(Constant, Constant)
#ifndef DEF_INSTR
diff --git a/llvm/lib/SandboxIR/SandboxIR.cpp b/llvm/lib/SandboxIR/SandboxIR.cpp
index 47e1ae4422c98..8e8ddc6638ab2 100644
--- a/llvm/lib/SandboxIR/SandboxIR.cpp
+++ b/llvm/lib/SandboxIR/SandboxIR.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/SandboxIR/SandboxIR.h"
+#include "llvm/IR/Constants.h"
#include "llvm/Support/Debug.h"
#include <sstream>
@@ -15,7 +16,7 @@ using namespace llvm::sandboxir;
Value::Value(ClassID SubclassID, llvm::Value *Val, Context &Ctx)
: SubclassID(SubclassID), Val(Val), Ctx(Ctx) {
#ifndef NDEBUG
- UID = 0; // FIXME: Once SBContext is available.
+ UID = Ctx.getNumValues();
#endif
}
@@ -47,8 +48,7 @@ void Value::dumpCommonPrefix(raw_ostream &OS) const {
}
void Value::dumpCommonSuffix(raw_ostream &OS) const {
- OS << " ; " << getName() << " (" << getSubclassIDStr(SubclassID) << ") "
- << this;
+ OS << " ; " << getName() << " (" << getSubclassIDStr(SubclassID) << ")";
}
void Value::printAsOperandCommon(raw_ostream &OS) const {
@@ -93,6 +93,33 @@ void User::dumpCommonHeader(raw_ostream &OS) const {
}
#endif // NDEBUG
+BBIterator &BBIterator::operator++() {
+ auto ItE = BB->end();
+ assert(It != ItE && "Already at end!");
+ ++It;
+ if (It == ItE)
+ return *this;
+ Instruction &NextI = *cast<sandboxir::Instruction>(Ctx->getValue(&*It));
+ unsigned Num = NextI.getNumOfIRInstrs();
+ assert(Num > 0 && "Bad getNumOfIRInstrs()");
+ It = std::next(It, Num - 1);
+ return *this;
+}
+
+BBIterator &BBIterator::operator--() {
+ assert(It != BB->begin() && "Already at begin!");
+ if (It == BB->end()) {
+ --It;
+ return *this;
+ }
+ Instruction &CurrI = **this;
+ unsigned Num = CurrI.getNumOfIRInstrs();
+ assert(Num > 0 && "Bad getNumOfIRInstrs()");
+ assert(std::prev(It, Num - 1) != BB->begin() && "Already at begin!");
+ It = std::prev(It, Num);
+ return *this;
+}
+
const char *Instruction::getOpcodeName(Opcode Opc) {
switch (Opc) {
#define DEF_VALUE(ID, CLASS)
@@ -147,7 +174,7 @@ void Constant::dump() const {
void Function::dumpNameAndArgs(raw_ostream &OS) const {
auto *F = cast<llvm::Function>(Val);
- OS << *getType() << " @" << F->getName() << "(";
+ OS << *F->getReturnType() << " @" << F->getName() << "(";
auto NumArgs = F->arg_size();
for (auto [Idx, Arg] : enumerate(F->args())) {
auto *SBArg = cast_or_null<Argument>(Ctx.getValue(&Arg));
@@ -163,6 +190,17 @@ void Function::dumpNameAndArgs(raw_ostream &OS) const {
void Function::dump(raw_ostream &OS) const {
dumpNameAndArgs(OS);
OS << " {\n";
+ auto *LLVMF = cast<llvm::Function>(Val);
+ interleave(
+ *LLVMF,
+ [this, &OS](const llvm::BasicBlock &LLVMBB) {
+ auto *BB = cast_or_null<BasicBlock>(Ctx.getValue(&LLVMBB));
+ if (BB == nullptr)
+ OS << "NULL";
+ else
+ OS << *BB;
+ },
+ [&OS] { OS << "\n"; });
OS << "}\n";
}
void Function::dump() const {
@@ -171,9 +209,182 @@ void Function::dump() const {
}
#endif // NDEBUG
+BasicBlock::iterator::pointer
+BasicBlock::iterator::getInstr(llvm::BasicBlock::iterator It) const {
+ return cast_or_null<Instruction>(Ctx->getValue(&*It));
+}
+
+Value *Context::registerValue(std::unique_ptr<Value> &&VPtr) {
+ assert(VPtr->getSubclassID() != Value::ClassID::User &&
+ "Can't register a user!");
+ Value *V = VPtr.get();
+ llvm::Value *Key = V->Val;
+ LLVMValueToValueMap[Key] = std::move(VPtr);
+ return V;
+}
+
+Value *Context::getOrCreateValueInternal(llvm::Value *LLVMV, llvm::User *U) {
+ auto Pair = LLVMValueToValueMap.insert({LLVMV, nullptr});
+ auto It = Pair.first;
+ if (!Pair.second)
+ return It->second.get();
+
+ if (auto *C = dyn_cast<llvm::Constant>(LLVMV)) {
+ for (llvm::Value *COp : C->operands())
+ getOrCreateValueInternal(COp, C);
+ It->second = std::make_unique<Constant>(C, *this);
+ return It->second.get();
+ }
+ if (auto *Arg = dyn_cast<llvm::Argument>(LLVMV)) {
+ It->second = std::make_unique<Argument>(Arg, *this);
+ return It->second.get();
+ }
+ if (auto *BB = dyn_cast<llvm::BasicBlock>(LLVMV)) {
+ assert(isa<BlockAddress>(U) &&
+ "This won't create a SBBB, don't call this function directly!");
+ if (auto *SBBB = getValue(BB))
+ return SBBB;
+ return nullptr;
+ }
+ assert(isa<llvm::Instruction>(LLVMV) && "Expected Instruction");
+ It->second =
+ std::make_unique<OpaqueInst>(cast<llvm::Instruction>(LLVMV), *this);
+ return It->second.get();
+}
+
+BasicBlock *Context::createBasicBlock(llvm::BasicBlock *LLVMBB) {
+ assert(getValue(LLVMBB) == nullptr && "Already exists!");
+ auto NewBBPtr = std::make_unique<BasicBlock>(LLVMBB, *this);
+ auto *BB = cast<BasicBlock>(registerValue(std::move(NewBBPtr)));
+ // Create SandboxIR for BB's body.
+ BB->buildBasicBlockFromLLVMIR(LLVMBB);
+ return BB;
+}
+
Value *Context::getValue(llvm::Value *V) const {
auto It = LLVMValueToValueMap.find(V);
if (It != LLVMValueToValueMap.end())
return It->second.get();
return nullptr;
}
+
+Function *Context::createFunction(llvm::Function *F) {
+ assert(getValue(F) == nullptr && "Already exists!");
+ auto NewFPtr = std::make_unique<Function>(F, *this);
+ // Create arguments.
+ for (auto &Arg : F->args())
+ getOrCreateGeneric<Argument, llvm::Argument>(&Arg);
+ // Create BBs.
+ for (auto &BB : *F)
+ createBasicBlock(&BB);
+ auto *SBF = cast<Function>(registerValue(std::move(NewFPtr)));
+ return SBF;
+}
+
+Function *BasicBlock::getParent() const {
+ auto *BB = cast<llvm::BasicBlock>(Val);
+ auto *F = BB->getParent();
+ if (F == nullptr)
+ // Detached
+ return nullptr;
+ return cast_or_null<Function>(Ctx.getValue(F));
+}
+
+void BasicBlock::buildBasicBlockFromLLVMIR(llvm::BasicBlock *LLVMBB) {
+ for (llvm::Instruction &IRef : reverse(*LLVMBB)) {
+ llvm::Instruction *I = &IRef;
+ Ctx.getOrCreateValue(I);
+ for (auto [OpIdx, Op] : enumerate(I->operands())) {
+ // Skip instruction's label operands
+ if (isa<llvm::BasicBlock>(Op))
+ continue;
+ // Skip metadata
+ if (isa<llvm::MetadataAsValue>(Op))
+ continue;
+ // Skip asm
+ if (isa<llvm::InlineAsm>(Op))
+ continue;
+ Ctx.getOrCreateValue(Op);
+ }
+ }
+#if !defined(NDEBUG) && defined(SBVEC_EXPENSIVE_CHECKS)
+ verify();
+#endif
+}
+
+BasicBlock::iterator BasicBlock::begin() const {
+ llvm::BasicBlock *BB = cast<llvm::BasicBlock>(Val);
+ llvm::BasicBlock::iterator It = BB->begin();
+ if (!BB->empty()) {
+ auto *V = Ctx.getValue(&*BB->begin());
+ assert(V != nullptr && "No SandboxIR for BB->begin()!");
+ auto *I = cast<Instruction>(V);
+ unsigned Num = I->getNumOfIRInstrs();
+ assert(Num >= 1u && "Bad getNumOfIRInstrs()");
+ It = std::next(It, Num - 1);
+ }
+ return iterator(BB, It, &Ctx);
+}
+
+Instruction *BasicBlock::getTerminator() const {
+ auto *TerminatorV =
+ Ctx.getValue(cast<llvm::BasicBlock>(Val)->getTerminator());
+ return cast_or_null<Instruction>(TerminatorV);
+}
+
+Instruction &BasicBlock::front() const {
+ auto *BB = cast<llvm::BasicBlock>(Val);
+ assert(!BB->empty() && "Empty block!");
+ auto *SBI = cast<Instruction>(getContext().getValue(&*BB->begin()));
+ assert(SBI != nullptr && "Expected Instr!");
+ return *SBI;
+}
+
+Instruction &BasicBlock::back() const {
+ auto *BB = cast<llvm::BasicBlock>(Val);
+ assert(!BB->empty() && "Empty block!");
+ auto *SBI = cast<Instruction>(getContext().getValue(&*BB->rbegin()));
+ assert(SBI != nullptr && "Expected Instr!");
+ return *SBI;
+}
+
+#ifndef NDEBUG
+void BasicBlock::dump(raw_ostream &OS) const {
+ llvm::BasicBlock *BB = cast<llvm::BasicBlock>(Val);
+ const auto &Name = BB->getName();
+ OS << Name;
+ if (!Name.empty())
+ OS << ":\n";
+ // If there are Instructions in the BB that are not mapped to SandboxIR, then
+ // use a crash-proof dump.
+ if (any_of(*BB, [this](llvm::Instruction &I) {
+ return Ctx.getValue(&I) == nullptr;
+ })) {
+ OS << "<Crash-proof mode!>\n";
+ DenseSet<Instruction *> Visited;
+ for (llvm::Instruction &IRef : *BB) {
+ Value *SBV = Ctx.getValue(&IRef);
+ if (SBV == nullptr)
+ OS << IRef << " *** No SandboxIR ***\n";
+ else {
+ auto *SBI = dyn_cast<Instruction>(SBV);
+ if (SBI == nullptr) {
+ OS << IRef << " *** Not a SBInstruction!!! ***\n";
+ } else {
+ if (Visited.insert(SBI).second)
+ OS << *SBI << "\n";
+ }
+ }
+ }
+ } else {
+ for (auto &SBI : *this) {
+ SBI.dump(OS);
+ OS << "\n";
+ }
+ }
+}
+void BasicBlock::dump() const {
+ dump(dbgs());
+ dbgs() << "\n";
+}
+#endif // NDEBUG
diff --git a/llvm/unittests/SandboxIR/SandboxIRTest.cpp b/llvm/unittests/SandboxIR/SandboxIRTest.cpp
index 0b0409aa15c4f..e523ae90966d7 100644
--- a/llvm/unittests/SandboxIR/SandboxIRTest.cpp
+++ b/llvm/unittests/SandboxIR/SandboxIRTest.cpp
@@ -27,6 +27,12 @@ struct SandboxIRTest : public testing::Test {
if (!M)
Err.print("SandboxIRTest", errs());
}
+ BasicBlock *getBasicBlockByName(Function &F, StringRef Name) {
+ for (BasicBlock &BB : F)
+ if (BB.getName() == Name)
+ return &BB;
+ llvm_unreachable("Expected to find basic block!");
+ }
};
TEST_F(SandboxIRTest, UserInstantiation) {
@@ -89,4 +95,133 @@ define void @foo(i32 %v1) {
EXPECT_FALSE(isa<sandboxir::User>(Arg0));
EXPECT_TRUE(isa<sandboxir::User>(Const0));
EXPECT_TRUE(isa<sandboxir::User>(OpaqueI));
+
+#ifndef NDEBUG
+ // The dump() functions should be very forgiving and should not crash even if
+ // sandboxir has not been built properly.
+ F.dump();
+ Arg0.dump();
+ Const0.dump();
+ OpaqueI.dump();
+#endif
+}
+
+TEST_F(SandboxIRTest, Function) {
+ parseIR(C, R"IR(
+define void @foo(i32 %arg0, i32 %arg1) {
+bb0:
+ br label %bb1
+bb1:
+ ret void
+}
+)IR");
+ llvm::Function *LLVMF = &*M->getFunction("foo");
+ llvm::Argument *LLVMArg0 = LLVMF->getArg(0);
+ llvm::Argument *LLVMArg1 = LLVMF->getArg(1);
+
+ sandboxir::Context Ctx(C);
+ sandboxir::Function *F = Ctx.createFunction(LLVMF);
+
+ // Check F arguments
+ EXPECT_EQ(F->arg_size(), 2u);
+ EXPECT_FALSE(F->arg_empty());
+ EXPECT_EQ(F->getArg(0), Ctx.getValue(LLVMArg0));
+ EXPECT_EQ(F->getArg(1), Ctx.getValue(LLVMArg1));
+
+ // Check F.begin(), F.end(), Function::iterator
+ llvm::BasicBlock *LLVMBB = &*LLVMF->begin();
+ for (sandboxir::BasicBlock &BB : *F) {
+ EXPECT_EQ(&BB, Ctx.getValue(LLVMBB));
+ LLVMBB = LLVMBB->getNextNode();
+ }
+
+#ifndef NDEBUG
+ {
+ // Check F.dumpNameAndArgs()
+ std::string Buff;
+ raw_string_ostream BS(Buff);
+ F->dumpNameAndArgs(BS);
+ EXPECT_EQ(Buff, "void @foo(i32 %arg0, i32 %arg1)");
+ }
+ {
+ // Check F.dump()
+ std::string Buff;
+ raw_string_ostream BS(Buff);
+ BS << "\n";
+ F->dump(BS);
+ EXPECT_EQ(Buff, R"IR(
+void @foo(i32 %arg0, i32 %arg1) {
+bb0:
+ br label %bb1 ; SB3. (Opaque)
+
+bb1:
+ ret void ; SB5. (Opaque)
+}
+)IR");
+ }
+#endif // NDEBUG
+}
+
+TEST_F(SandboxIRTest, BasicBlock) {
+ parseIR(C, R"IR(
+define void @foo(i32 %v1) {
+bb0:
+ br label %bb1
+bb1:
+ ret void
+}
+)IR");
+ llvm::Function *LLVMF = &*M->getFunction("foo");
+ llvm::BasicBlock *LLVMBB0 = getBasicBlockByName(*LLVMF, "bb0");
+ llvm::BasicBlock *LLVMBB1 = getBasicBlockByName(*LLVMF, "bb1");
+
+ sandboxir::Context Ctx(C);
+ sandboxir::Function *F = Ctx.createFunction(LLVMF);
+ auto &BB0 = cast<sandboxir::BasicBlock>(*Ctx.getValue(LLVMBB0));
+ auto &BB1 = cast<sandboxir::BasicBlock>(*Ctx.getValue(LLVMBB1));
+
+ // Check BB::classof()
+ EXPECT_TRUE(isa<sandboxir::Value>(BB0));
+ EXPECT_FALSE(isa<sandboxir::User>(BB0));
+ EXPECT_FALSE(isa<sandboxir::Instruction>(BB0));
+ EXPECT_FALSE(isa<sandboxir::Constant>(BB0));
+ EXPECT_FALSE(isa<sandboxir::Argument>(BB0));
+
+ // Check BB.getParent()
+ EXPECT_EQ(BB0.getParent(), F);
+ EXPECT_EQ(BB1.getParent(), F);
+
+ // Check BBIterator, BB.begin(), BB.end().
+ llvm::Instruction *LLVMI = &*LLVMBB0->begin();
+ for (sandboxir::Instruction &I : BB0) {
+ EXPECT_EQ(&I, Ctx.getValue(LLVMI));
+ LLVMI = LLVMI->getNextNode();
+ }
+ LLVMI = &*LLVMBB1->begin();
+ for (sandboxir::Instruction &I : BB1) {
+ EXPECT_EQ(&I, Ctx.getValue(LLVMI));
+ LLVMI = LLVMI->getNextNode();
+ }
+
+ // Check BB.getTerminator()
+ EXPECT_EQ(BB0.getTerminator(), Ctx.getValue(LLVMBB0->getTerminator()));
+ EXPECT_EQ(BB1.getTerminator(), Ctx.getValue(LLVMBB1->getTerminator()));
+
+ // Check BB.rbegin(), BB.rend()
+ EXPECT_EQ(&*BB0.rbegin(), BB0.getTerminator());
+ EXPECT_EQ(&*std::prev(BB0.rend()), &*BB0.begin());
+
+#ifndef NDEBUG
+ {
+ // Check BB.dump()
+ std::string Buff;
+ raw_string_ostream BS(Buff);
+ BS << "\n";
+ BB0.dump(BS);
+ EXPECT_EQ(Buff, R"IR(
+bb0:
+ br label %bb1 ; SB2. (Opaque)
+)IR");
+ }
+#endif // NDEBUG
}
More information about the llvm-commits
mailing list