//===- 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 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(&*LLVMBB->begin()); sandboxir::Context Ctx(C); sandboxir::Function *F = Ctx.createFunction(LLVMF); auto *BB = &*F->begin(); auto *Ld = cast(&*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(&*It++); auto *L1 = cast(&*It++); auto *L2 = cast(&*It++); [[maybe_unused]] auto *L3 = cast(&*It++); auto *V2L0 = cast(&*It++); auto *V2L1 = cast(&*It++); auto *V2L2 = cast(&*It++); auto *V2L3 = cast(&*It++); [[maybe_unused]] auto *V3L0 = cast(&*It++); auto *V3L1 = cast(&*It++); [[maybe_unused]] auto *V3L2 = cast(&*It++); [[maybe_unused]] auto *V3L3 = cast(&*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(Ctx.getValue(&*Foo.begin())); auto FooIt = FooBB->begin(); auto Add = cast(&*FooIt++); auto *S0 = cast(&*FooIt++); auto *RetF = cast(&*FooIt++); // getExpectedValue EXPECT_EQ(sandboxir::Utils::getExpectedValue(Add), Add); EXPECT_EQ(sandboxir::Utils::getExpectedValue(S0), cast(S0)->getValueOperand()); EXPECT_EQ(sandboxir::Utils::getExpectedValue(RetF), cast(RetF)->getReturnValue()); // getExpectedType EXPECT_EQ(sandboxir::Utils::getExpectedType(Add), Add->getType()); EXPECT_EQ(sandboxir::Utils::getExpectedType(S0), cast(S0)->getValueOperand()->getType()); EXPECT_EQ(sandboxir::Utils::getExpectedType(RetF), cast(RetF)->getReturnValue()->getType()); // getExpectedValue for void returns llvm::Function &Bar = *M->getFunction("bar"); Ctx.createFunction(&Bar); auto *BarBB = cast(Ctx.getValue(&*Bar.begin())); auto BarIt = BarBB->begin(); auto *RetV = cast(&*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(&*It++); auto *L1 = cast(&*It++); auto *L2 = cast(&*It++); auto *L3 = cast(&*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(&*It++); auto *St1 = cast(&*It++); auto *St2 = cast(&*It++); auto *St3 = cast(&*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)); }