|  | //===- UtilsTest.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/Utils.h" | 
|  | #include "llvm/Analysis/AssumptionCache.h" | 
|  | #include "llvm/Analysis/BasicAliasAnalysis.h" | 
|  | #include "llvm/Analysis/LoopInfo.h" | 
|  | #include "llvm/Analysis/TargetLibraryInfo.h" | 
|  | #include "llvm/AsmParser/Parser.h" | 
|  | #include "llvm/IR/BasicBlock.h" | 
|  | #include "llvm/IR/DataLayout.h" | 
|  | #include "llvm/IR/Dominators.h" | 
|  | #include "llvm/IR/Function.h" | 
|  | #include "llvm/IR/Instruction.h" | 
|  | #include "llvm/IR/Module.h" | 
|  | #include "llvm/SandboxIR/Constant.h" | 
|  | #include "llvm/SandboxIR/Context.h" | 
|  | #include "llvm/SandboxIR/Function.h" | 
|  | #include "llvm/Support/SourceMgr.h" | 
|  | #include "gtest/gtest.h" | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | struct UtilsTest : 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("UtilsTest", 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(UtilsTest, getMemoryLocation) { | 
|  | parseIR(C, R"IR( | 
|  | define void @foo(ptr %arg0) { | 
|  | %ld = load i8, ptr %arg0 | 
|  | ret void | 
|  | } | 
|  | )IR"); | 
|  | llvm::Function *LLVMF = &*M->getFunction("foo"); | 
|  | auto *LLVMBB = &*LLVMF->begin(); | 
|  | auto *LLVMLd = cast<llvm::LoadInst>(&*LLVMBB->begin()); | 
|  | sandboxir::Context Ctx(C); | 
|  | sandboxir::Function *F = Ctx.createFunction(LLVMF); | 
|  | auto *BB = &*F->begin(); | 
|  | auto *Ld = cast<sandboxir::LoadInst>(&*BB->begin()); | 
|  | EXPECT_EQ(sandboxir::Utils::memoryLocationGetOrNone(Ld), | 
|  | MemoryLocation::getOrNone(LLVMLd)); | 
|  | } | 
|  |  | 
|  | TEST_F(UtilsTest, GetPointerDiffInBytes) { | 
|  | parseIR(C, R"IR( | 
|  | define void @foo(ptr %ptr) { | 
|  | %gep0 = getelementptr inbounds float, ptr %ptr, i64 0 | 
|  | %gep1 = getelementptr inbounds float, ptr %ptr, i64 1 | 
|  | %gep2 = getelementptr inbounds float, ptr %ptr, i64 2 | 
|  | %gep3 = getelementptr inbounds float, ptr %ptr, i64 3 | 
|  |  | 
|  | %ld0 = load float, ptr %gep0 | 
|  | %ld1 = load float, ptr %gep1 | 
|  | %ld2 = load float, ptr %gep2 | 
|  | %ld3 = load float, ptr %gep3 | 
|  |  | 
|  | %v2ld0 = load <2 x float>, ptr %gep0 | 
|  | %v2ld1 = load <2 x float>, ptr %gep1 | 
|  | %v2ld2 = load <2 x float>, ptr %gep2 | 
|  | %v2ld3 = load <2 x float>, ptr %gep3 | 
|  |  | 
|  | %v3ld0 = load <3 x float>, ptr %gep0 | 
|  | %v3ld1 = load <3 x float>, ptr %gep1 | 
|  | %v3ld2 = load <3 x float>, ptr %gep2 | 
|  | %v3ld3 = load <3 x float>, ptr %gep3 | 
|  | ret void | 
|  | } | 
|  | )IR"); | 
|  | llvm::Function &LLVMF = *M->getFunction("foo"); | 
|  | DominatorTree DT(LLVMF); | 
|  | TargetLibraryInfoImpl TLII; | 
|  | TargetLibraryInfo TLI(TLII); | 
|  | DataLayout DL(M->getDataLayout()); | 
|  | AssumptionCache AC(LLVMF); | 
|  | BasicAAResult BAA(DL, LLVMF, TLI, AC, &DT); | 
|  | AAResults AA(TLI); | 
|  | AA.addAAResult(BAA); | 
|  | LoopInfo LI(DT); | 
|  | ScalarEvolution SE(LLVMF, TLI, AC, DT, LI); | 
|  | sandboxir::Context Ctx(C); | 
|  |  | 
|  | auto &F = *Ctx.createFunction(&LLVMF); | 
|  | auto &BB = *F.begin(); | 
|  | auto It = std::next(BB.begin(), 4); | 
|  | auto *L0 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *L1 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *L2 = cast<sandboxir::LoadInst>(&*It++); | 
|  | [[maybe_unused]] auto *L3 = cast<sandboxir::LoadInst>(&*It++); | 
|  |  | 
|  | auto *V2L0 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *V2L1 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *V2L2 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *V2L3 = cast<sandboxir::LoadInst>(&*It++); | 
|  |  | 
|  | [[maybe_unused]] auto *V3L0 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *V3L1 = cast<sandboxir::LoadInst>(&*It++); | 
|  | [[maybe_unused]] auto *V3L2 = cast<sandboxir::LoadInst>(&*It++); | 
|  | [[maybe_unused]] auto *V3L3 = cast<sandboxir::LoadInst>(&*It++); | 
|  |  | 
|  | // getPointerDiffInBytes | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(L0, L1, SE), 4); | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(L0, L2, SE), 8); | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(L1, L0, SE), -4); | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(L0, V2L0, SE), 0); | 
|  |  | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(L0, V2L1, SE), 4); | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(L0, V3L1, SE), 4); | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(V2L0, V2L2, SE), 8); | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(V2L0, V2L3, SE), 12); | 
|  | EXPECT_EQ(*sandboxir::Utils::getPointerDiffInBytes(V2L3, V2L0, SE), -12); | 
|  |  | 
|  | // atLowerAddress | 
|  | EXPECT_TRUE(sandboxir::Utils::atLowerAddress(L0, L1, SE)); | 
|  | EXPECT_FALSE(sandboxir::Utils::atLowerAddress(L1, L0, SE)); | 
|  | EXPECT_FALSE(sandboxir::Utils::atLowerAddress(L3, V3L3, SE)); | 
|  | } | 
|  |  | 
|  | TEST_F(UtilsTest, GetExpected) { | 
|  | parseIR(C, R"IR( | 
|  | define float @foo(float %v, ptr %ptr) { | 
|  | %add = fadd float %v, %v | 
|  | store float %v, ptr %ptr | 
|  | ret float %v | 
|  | } | 
|  | define void @bar(float %v, ptr %ptr) { | 
|  | ret void | 
|  | } | 
|  | )IR"); | 
|  | llvm::Function &Foo = *M->getFunction("foo"); | 
|  | sandboxir::Context Ctx(C); | 
|  |  | 
|  | Ctx.createFunction(&Foo); | 
|  | auto *FooBB = cast<sandboxir::BasicBlock>(Ctx.getValue(&*Foo.begin())); | 
|  | auto FooIt = FooBB->begin(); | 
|  | auto Add = cast<sandboxir::Instruction>(&*FooIt++); | 
|  | auto *S0 = cast<sandboxir::Instruction>(&*FooIt++); | 
|  | auto *RetF = cast<sandboxir::Instruction>(&*FooIt++); | 
|  | // getExpectedValue | 
|  | EXPECT_EQ(sandboxir::Utils::getExpectedValue(Add), Add); | 
|  | EXPECT_EQ(sandboxir::Utils::getExpectedValue(S0), | 
|  | cast<sandboxir::StoreInst>(S0)->getValueOperand()); | 
|  | EXPECT_EQ(sandboxir::Utils::getExpectedValue(RetF), | 
|  | cast<sandboxir::ReturnInst>(RetF)->getReturnValue()); | 
|  | // getExpectedType | 
|  | EXPECT_EQ(sandboxir::Utils::getExpectedType(Add), Add->getType()); | 
|  | EXPECT_EQ(sandboxir::Utils::getExpectedType(S0), | 
|  | cast<sandboxir::StoreInst>(S0)->getValueOperand()->getType()); | 
|  | EXPECT_EQ(sandboxir::Utils::getExpectedType(RetF), | 
|  | cast<sandboxir::ReturnInst>(RetF)->getReturnValue()->getType()); | 
|  |  | 
|  | // getExpectedValue for void returns | 
|  | llvm::Function &Bar = *M->getFunction("bar"); | 
|  | Ctx.createFunction(&Bar); | 
|  | auto *BarBB = cast<sandboxir::BasicBlock>(Ctx.getValue(&*Bar.begin())); | 
|  | auto BarIt = BarBB->begin(); | 
|  | auto *RetV = cast<sandboxir::Instruction>(&*BarIt++); | 
|  | EXPECT_EQ(sandboxir::Utils::getExpectedValue(RetV), nullptr); | 
|  | } | 
|  |  | 
|  | TEST_F(UtilsTest, GetNumBits) { | 
|  | parseIR(C, R"IR( | 
|  | define void @foo(float %arg0, double %arg1, i8 %arg2, i64 %arg3, ptr %arg4) { | 
|  | bb0: | 
|  | %ld0 = load float, ptr %arg4 | 
|  | %ld1 = load double, ptr %arg4 | 
|  | %ld2 = load i8, ptr %arg4 | 
|  | %ld3 = load i64, ptr %arg4 | 
|  | ret void | 
|  | } | 
|  | )IR"); | 
|  | llvm::Function &Foo = *M->getFunction("foo"); | 
|  | sandboxir::Context Ctx(C); | 
|  | sandboxir::Function *F = Ctx.createFunction(&Foo); | 
|  | const DataLayout &DL = M->getDataLayout(); | 
|  | // getNumBits for scalars via the Value overload | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(F->getArg(0), DL), | 
|  | DL.getTypeSizeInBits(Type::getFloatTy(C))); | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(F->getArg(1), DL), | 
|  | DL.getTypeSizeInBits(Type::getDoubleTy(C))); | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(F->getArg(2), DL), 8u); | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(F->getArg(3), DL), 64u); | 
|  |  | 
|  | auto &BB = *F->begin(); | 
|  | auto It = BB.begin(); | 
|  | auto *L0 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *L1 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *L2 = cast<sandboxir::LoadInst>(&*It++); | 
|  | auto *L3 = cast<sandboxir::LoadInst>(&*It++); | 
|  | // getNumBits for scalars via the Instruction overload | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(L0), | 
|  | DL.getTypeSizeInBits(Type::getFloatTy(C))); | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(L1), | 
|  | DL.getTypeSizeInBits(Type::getDoubleTy(C))); | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(L2), 8u); | 
|  | EXPECT_EQ(sandboxir::Utils::getNumBits(L3), 64u); | 
|  | } | 
|  |  | 
|  | TEST_F(UtilsTest, GetMemBase) { | 
|  | parseIR(C, R"IR( | 
|  | define void @foo(ptr %ptrA, float %val, ptr %ptrB) { | 
|  | bb: | 
|  | %gepA0 = getelementptr float, ptr %ptrA, i32 0 | 
|  | %gepA1 = getelementptr float, ptr %ptrA, i32 1 | 
|  | %gepB0 = getelementptr float, ptr %ptrB, i32 0 | 
|  | %gepB1 = getelementptr float, ptr %ptrB, i32 1 | 
|  | store float %val, ptr %gepA0 | 
|  | store float %val, ptr %gepA1 | 
|  | store float %val, ptr %gepB0 | 
|  | store float %val, ptr %gepB1 | 
|  | ret void | 
|  | } | 
|  | )IR"); | 
|  | llvm::Function &Foo = *M->getFunction("foo"); | 
|  | sandboxir::Context Ctx(C); | 
|  | sandboxir::Function *F = Ctx.createFunction(&Foo); | 
|  |  | 
|  | auto It = std::next(F->begin()->begin(), 4); | 
|  | auto *St0 = cast<sandboxir::StoreInst>(&*It++); | 
|  | auto *St1 = cast<sandboxir::StoreInst>(&*It++); | 
|  | auto *St2 = cast<sandboxir::StoreInst>(&*It++); | 
|  | auto *St3 = cast<sandboxir::StoreInst>(&*It++); | 
|  | EXPECT_EQ(sandboxir::Utils::getMemInstructionBase(St0), | 
|  | sandboxir::Utils::getMemInstructionBase(St1)); | 
|  | EXPECT_EQ(sandboxir::Utils::getMemInstructionBase(St2), | 
|  | sandboxir::Utils::getMemInstructionBase(St3)); | 
|  | EXPECT_NE(sandboxir::Utils::getMemInstructionBase(St0), | 
|  | sandboxir::Utils::getMemInstructionBase(St3)); | 
|  | } |