| //===- SandboxIRTest.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/SandboxIR/SandboxIR.h" |
| #include "llvm/AsmParser/Parser.h" |
| #include "llvm/IR/BasicBlock.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/Instruction.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| struct SandboxIRTest : 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("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, ClassID) { |
| parseIR(C, R"IR( |
| define void @foo(i32 %v1) { |
| %add = add i32 %v1, 42 |
| ret void |
| } |
| )IR"); |
| llvm::Function *LLVMF = &*M->getFunction("foo"); |
| llvm::BasicBlock *LLVMBB = &*LLVMF->begin(); |
| llvm::Instruction *LLVMAdd = &*LLVMBB->begin(); |
| auto *LLVMC = cast<llvm::Constant>(LLVMAdd->getOperand(1)); |
| |
| sandboxir::Context Ctx(C); |
| sandboxir::Function *F = Ctx.createFunction(LLVMF); |
| sandboxir::Argument *Arg0 = F->getArg(0); |
| sandboxir::BasicBlock *BB = &*F->begin(); |
| sandboxir::Instruction *AddI = &*BB->begin(); |
| sandboxir::OpaqueInst *OpaqueI = cast<sandboxir::OpaqueInst>(AddI); |
| sandboxir::Constant *Const0 = cast<sandboxir::Constant>(Ctx.getValue(LLVMC)); |
| |
| EXPECT_TRUE(isa<sandboxir::Function>(F)); |
| EXPECT_FALSE(isa<sandboxir::Function>(Arg0)); |
| EXPECT_FALSE(isa<sandboxir::Function>(BB)); |
| EXPECT_FALSE(isa<sandboxir::Function>(AddI)); |
| EXPECT_FALSE(isa<sandboxir::Function>(Const0)); |
| EXPECT_FALSE(isa<sandboxir::Function>(OpaqueI)); |
| |
| EXPECT_FALSE(isa<sandboxir::Argument>(F)); |
| EXPECT_TRUE(isa<sandboxir::Argument>(Arg0)); |
| EXPECT_FALSE(isa<sandboxir::Argument>(BB)); |
| EXPECT_FALSE(isa<sandboxir::Argument>(AddI)); |
| EXPECT_FALSE(isa<sandboxir::Argument>(Const0)); |
| EXPECT_FALSE(isa<sandboxir::Argument>(OpaqueI)); |
| |
| EXPECT_TRUE(isa<sandboxir::Constant>(F)); |
| EXPECT_FALSE(isa<sandboxir::Constant>(Arg0)); |
| EXPECT_FALSE(isa<sandboxir::Constant>(BB)); |
| EXPECT_FALSE(isa<sandboxir::Constant>(AddI)); |
| EXPECT_TRUE(isa<sandboxir::Constant>(Const0)); |
| EXPECT_FALSE(isa<sandboxir::Constant>(OpaqueI)); |
| |
| EXPECT_FALSE(isa<sandboxir::OpaqueInst>(F)); |
| EXPECT_FALSE(isa<sandboxir::OpaqueInst>(Arg0)); |
| EXPECT_FALSE(isa<sandboxir::OpaqueInst>(BB)); |
| EXPECT_TRUE(isa<sandboxir::OpaqueInst>(AddI)); |
| EXPECT_FALSE(isa<sandboxir::OpaqueInst>(Const0)); |
| EXPECT_TRUE(isa<sandboxir::OpaqueInst>(OpaqueI)); |
| |
| EXPECT_FALSE(isa<sandboxir::Instruction>(F)); |
| EXPECT_FALSE(isa<sandboxir::Instruction>(Arg0)); |
| EXPECT_FALSE(isa<sandboxir::Instruction>(BB)); |
| EXPECT_TRUE(isa<sandboxir::Instruction>(AddI)); |
| EXPECT_FALSE(isa<sandboxir::Instruction>(Const0)); |
| EXPECT_TRUE(isa<sandboxir::Instruction>(OpaqueI)); |
| |
| EXPECT_FALSE(isa<sandboxir::User>(F)); |
| EXPECT_FALSE(isa<sandboxir::User>(Arg0)); |
| EXPECT_FALSE(isa<sandboxir::User>(BB)); |
| EXPECT_TRUE(isa<sandboxir::User>(AddI)); |
| EXPECT_TRUE(isa<sandboxir::User>(Const0)); |
| EXPECT_TRUE(isa<sandboxir::User>(OpaqueI)); |
| |
| #ifndef NDEBUG |
| std::string Buff; |
| raw_string_ostream BS(Buff); |
| F->dump(BS); |
| Arg0->dump(BS); |
| BB->dump(BS); |
| AddI->dump(BS); |
| Const0->dump(BS); |
| OpaqueI->dump(BS); |
| #endif |
| } |
| |
| TEST_F(SandboxIRTest, Use) { |
| parseIR(C, R"IR( |
| define i32 @foo(i32 %v0, i32 %v1) { |
| %add0 = add i32 %v0, %v1 |
| ret i32 %add0 |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| sandboxir::Context Ctx(C); |
| |
| BasicBlock *LLVMBB = &*LLVMF.begin(); |
| auto LLVMBBIt = LLVMBB->begin(); |
| Instruction *LLVMI0 = &*LLVMBBIt++; |
| Instruction *LLVMRet = &*LLVMBBIt++; |
| Argument *LLVMArg0 = LLVMF.getArg(0); |
| Argument *LLVMArg1 = LLVMF.getArg(1); |
| |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto &BB = *F.begin(); |
| auto *Arg0 = F.getArg(0); |
| auto *Arg1 = F.getArg(1); |
| auto It = BB.begin(); |
| auto *I0 = &*It++; |
| auto *Ret = &*It++; |
| |
| SmallVector<sandboxir::Argument *> Args{Arg0, Arg1}; |
| unsigned OpIdx = 0; |
| for (sandboxir::Use Use : I0->operands()) { |
| // Check Use.getOperandNo(). |
| EXPECT_EQ(Use.getOperandNo(), OpIdx); |
| // Check Use.getUser(). |
| EXPECT_EQ(Use.getUser(), I0); |
| // Check Use.getContext(). |
| EXPECT_EQ(Use.getContext(), &Ctx); |
| // Check Use.get(). |
| sandboxir::Value *Op = Use.get(); |
| EXPECT_EQ(Op, Ctx.getValue(LLVMI0->getOperand(OpIdx))); |
| // Check Use.getUser(). |
| EXPECT_EQ(Use.getUser(), I0); |
| // Check implicit cast to Value. |
| sandboxir::Value *Cast = Use; |
| EXPECT_EQ(Cast, Op); |
| // Check that Use points to the correct operand. |
| EXPECT_EQ(Op, Args[OpIdx]); |
| // Check getOperand(). |
| EXPECT_EQ(Op, I0->getOperand(OpIdx)); |
| // Check getOperandUse(). |
| EXPECT_EQ(Use, I0->getOperandUse(OpIdx)); |
| ++OpIdx; |
| } |
| EXPECT_EQ(OpIdx, 2u); |
| |
| // Check Use.operator==() and Use.operator!=(). |
| sandboxir::Use UseA = I0->getOperandUse(0); |
| sandboxir::Use UseB = I0->getOperandUse(0); |
| EXPECT_TRUE(UseA == UseB); |
| EXPECT_FALSE(UseA != UseB); |
| |
| // Check getNumOperands(). |
| EXPECT_EQ(I0->getNumOperands(), 2u); |
| EXPECT_EQ(Ret->getNumOperands(), 1u); |
| |
| EXPECT_EQ(Ret->getOperand(0), I0); |
| |
| #ifndef NDEBUG |
| // Check Use.dump() |
| std::string Buff; |
| raw_string_ostream BS(Buff); |
| BS << "\n"; |
| I0->getOperandUse(0).dump(BS); |
| EXPECT_EQ(Buff, R"IR( |
| Def: i32 %v0 ; SB1. (Argument) |
| User: %add0 = add i32 %v0, %v1 ; SB4. (Opaque) |
| OperandNo: 0 |
| )IR"); |
| #endif // NDEBUG |
| |
| // Check Value.user_begin(). |
| sandboxir::Value::user_iterator UIt = I0->user_begin(); |
| sandboxir::User *U = *UIt; |
| EXPECT_EQ(U, Ret); |
| // Check Value.uses(). |
| EXPECT_EQ(range_size(I0->uses()), 1u); |
| EXPECT_EQ((*I0->uses().begin()).getUser(), Ret); |
| // Check Value.users(). |
| EXPECT_EQ(range_size(I0->users()), 1u); |
| EXPECT_EQ(*I0->users().begin(), Ret); |
| // Check Value.getNumUses(). |
| EXPECT_EQ(I0->getNumUses(), 1u); |
| // Check Value.hasNUsesOrMore(). |
| EXPECT_TRUE(I0->hasNUsesOrMore(0u)); |
| EXPECT_TRUE(I0->hasNUsesOrMore(1u)); |
| EXPECT_FALSE(I0->hasNUsesOrMore(2u)); |
| // Check Value.hasNUses(). |
| EXPECT_FALSE(I0->hasNUses(0u)); |
| EXPECT_TRUE(I0->hasNUses(1u)); |
| EXPECT_FALSE(I0->hasNUses(2u)); |
| |
| // Check User.setOperand(). |
| Ret->setOperand(0, Arg0); |
| EXPECT_EQ(Ret->getOperand(0), Arg0); |
| EXPECT_EQ(Ret->getOperandUse(0).get(), Arg0); |
| EXPECT_EQ(LLVMRet->getOperand(0), LLVMArg0); |
| |
| Ret->setOperand(0, Arg1); |
| EXPECT_EQ(Ret->getOperand(0), Arg1); |
| EXPECT_EQ(Ret->getOperandUse(0).get(), Arg1); |
| EXPECT_EQ(LLVMRet->getOperand(0), LLVMArg1); |
| } |
| |
| TEST_F(SandboxIRTest, RUOW) { |
| parseIR(C, R"IR( |
| declare void @bar0() |
| declare void @bar1() |
| |
| @glob0 = global ptr @bar0 |
| @glob1 = global ptr @bar1 |
| |
| define i32 @foo(i32 %arg0, i32 %arg1) { |
| %add0 = add i32 %arg0, %arg1 |
| %gep1 = getelementptr i8, ptr @glob0, i32 1 |
| %gep2 = getelementptr i8, ptr @glob1, i32 1 |
| ret i32 %add0 |
| } |
| )IR"); |
| llvm::Function &LLVMF = *M->getFunction("foo"); |
| sandboxir::Context Ctx(C); |
| |
| auto &F = *Ctx.createFunction(&LLVMF); |
| auto &BB = *F.begin(); |
| auto *Arg0 = F.getArg(0); |
| auto *Arg1 = F.getArg(1); |
| auto It = BB.begin(); |
| auto *I0 = &*It++; |
| auto *I1 = &*It++; |
| auto *I2 = &*It++; |
| auto *Ret = &*It++; |
| |
| bool Replaced; |
| // Try to replace an operand that doesn't match. |
| Replaced = I0->replaceUsesOfWith(Ret, Arg1); |
| EXPECT_FALSE(Replaced); |
| EXPECT_EQ(I0->getOperand(0), Arg0); |
| EXPECT_EQ(I0->getOperand(1), Arg1); |
| |
| // Replace I0 operands when operands differ. |
| Replaced = I0->replaceUsesOfWith(Arg0, Arg1); |
| EXPECT_TRUE(Replaced); |
| EXPECT_EQ(I0->getOperand(0), Arg1); |
| EXPECT_EQ(I0->getOperand(1), Arg1); |
| |
| // Replace I0 operands when operands are the same. |
| Replaced = I0->replaceUsesOfWith(Arg1, Arg0); |
| EXPECT_TRUE(Replaced); |
| EXPECT_EQ(I0->getOperand(0), Arg0); |
| EXPECT_EQ(I0->getOperand(1), Arg0); |
| |
| // Replace Ret operand. |
| Replaced = Ret->replaceUsesOfWith(I0, Arg0); |
| EXPECT_TRUE(Replaced); |
| EXPECT_EQ(Ret->getOperand(0), Arg0); |
| |
| // Check RAUW on constant. |
| auto *Glob0 = cast<sandboxir::Constant>(I1->getOperand(0)); |
| auto *Glob1 = cast<sandboxir::Constant>(I2->getOperand(0)); |
| auto *Glob0Op = Glob0->getOperand(0); |
| Glob0->replaceUsesOfWith(Glob0Op, Glob1); |
| EXPECT_EQ(Glob0->getOperand(0), Glob1); |
| } |
| |
| TEST_F(SandboxIRTest, RAUW_RUWIf) { |
| parseIR(C, R"IR( |
| define void @foo(ptr %ptr) { |
| %ld0 = load float, ptr %ptr |
| %ld1 = load float, ptr %ptr |
| store float %ld0, ptr %ptr |
| store float %ld0, ptr %ptr |
| ret void |
| } |
| )IR"); |
| llvm::Function &LLVMF = *M->getFunction("foo"); |
| sandboxir::Context Ctx(C); |
| llvm::BasicBlock *LLVMBB = &*LLVMF.begin(); |
| |
| Ctx.createFunction(&LLVMF); |
| auto *BB = cast<sandboxir::BasicBlock>(Ctx.getValue(LLVMBB)); |
| auto It = BB->begin(); |
| sandboxir::Instruction *Ld0 = &*It++; |
| sandboxir::Instruction *Ld1 = &*It++; |
| sandboxir::Instruction *St0 = &*It++; |
| sandboxir::Instruction *St1 = &*It++; |
| // Check RUWIf when the lambda returns false. |
| Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return false; }); |
| EXPECT_EQ(St0->getOperand(0), Ld0); |
| EXPECT_EQ(St1->getOperand(0), Ld0); |
| // Check RUWIf when the lambda returns true. |
| Ld0->replaceUsesWithIf(Ld1, [](const sandboxir::Use &Use) { return true; }); |
| EXPECT_EQ(St0->getOperand(0), Ld1); |
| EXPECT_EQ(St1->getOperand(0), Ld1); |
| St0->setOperand(0, Ld0); |
| St1->setOperand(0, Ld0); |
| // Check RUWIf user == St0. |
| Ld0->replaceUsesWithIf( |
| Ld1, [St0](const sandboxir::Use &Use) { return Use.getUser() == St0; }); |
| EXPECT_EQ(St0->getOperand(0), Ld1); |
| EXPECT_EQ(St1->getOperand(0), Ld0); |
| St0->setOperand(0, Ld0); |
| // Check RUWIf user == St1. |
| Ld0->replaceUsesWithIf( |
| Ld1, [St1](const sandboxir::Use &Use) { return Use.getUser() == St1; }); |
| EXPECT_EQ(St0->getOperand(0), Ld0); |
| EXPECT_EQ(St1->getOperand(0), Ld1); |
| St1->setOperand(0, Ld0); |
| // Check RAUW. |
| Ld1->replaceAllUsesWith(Ld0); |
| EXPECT_EQ(St0->getOperand(0), Ld0); |
| EXPECT_EQ(St1->getOperand(0), Ld0); |
| } |
| |
| // Check that the operands/users are counted correctly. |
| // I1 |
| // / \ |
| // \ / |
| // I2 |
| TEST_F(SandboxIRTest, DuplicateUses) { |
| parseIR(C, R"IR( |
| define void @foo(i8 %v) { |
| %I1 = add i8 %v, %v |
| %I2 = add i8 %I1, %I1 |
| ret void |
| } |
| )IR"); |
| Function &LLVMF = *M->getFunction("foo"); |
| sandboxir::Context Ctx(C); |
| auto *F = Ctx.createFunction(&LLVMF); |
| auto *BB = &*F->begin(); |
| auto It = BB->begin(); |
| auto *I1 = &*It++; |
| auto *I2 = &*It++; |
| EXPECT_EQ(range_size(I1->users()), 2u); |
| EXPECT_EQ(range_size(I2->operands()), 2u); |
| } |
| |
| 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 |
| } |