//===- LoadsTest.cpp - local load analysis unit tests ---------------------===// // // 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/Analysis/Loads.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/TargetLibraryInfo.h" #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 parseIR(LLVMContext &C, const char *IR) { SMDiagnostic Err; std::unique_ptr Mod = parseAssemblyString(IR, Err, C); if (!Mod) Err.print("AnalysisTests", errs()); return Mod; } TEST(LoadsTest, FindAvailableLoadedValueSameBasePtrConstantOffsetsNullAA) { LLVMContext C; std::unique_ptr M = parseIR(C, R"IR( target datalayout = "p:64:64:64:32" %class = type <{ i32, i32 }> define i32 @f() { entry: %o = alloca %class %f1 = getelementptr inbounds %class, %class* %o, i32 0, i32 0 store i32 42, i32* %f1 %f2 = getelementptr inbounds %class, %class* %o, i32 0, i32 1 store i32 43, i32* %f2 %v = load i32, i32* %f1 ret i32 %v } )IR"); auto *GV = M->getNamedValue("f"); ASSERT_TRUE(GV); auto *F = dyn_cast(GV); ASSERT_TRUE(F); Instruction *Inst = &F->front().front(); auto *AI = dyn_cast(Inst); ASSERT_TRUE(AI); Inst = &*++F->front().rbegin(); auto *LI = dyn_cast(Inst); ASSERT_TRUE(LI); BasicBlock::iterator BBI(LI); Value *Loaded = FindAvailableLoadedValue( LI, LI->getParent(), BBI, 0, nullptr, nullptr); ASSERT_TRUE(Loaded); auto *CI = dyn_cast(Loaded); ASSERT_TRUE(CI); ASSERT_TRUE(CI->equalsInt(42)); } TEST(LoadsTest, CanReplacePointersIfEqual) { LLVMContext C; std::unique_ptr M = parseIR(C, R"IR( @y = common global [1 x i32] zeroinitializer, align 4 @x = common global [1 x i32] zeroinitializer, align 4 declare void @use(i32*) define void @f(i32* %p1, i32* %p2, i64 %i) { call void @use(i32* getelementptr inbounds ([1 x i32], [1 x i32]* @y, i64 0, i64 0)) %p1_idx = getelementptr inbounds i32, i32* %p1, i64 %i call void @use(i32* %p1_idx) %icmp = icmp eq i32* %p1, getelementptr inbounds ([1 x i32], [1 x i32]* @y, i64 0, i64 0) %ptrInt = ptrtoint i32* %p1 to i64 ret void } )IR"); const DataLayout &DL = M->getDataLayout(); auto *GV = M->getNamedValue("f"); ASSERT_TRUE(GV); auto *F = dyn_cast(GV); ASSERT_TRUE(F); Value *P1 = &*F->arg_begin(); Value *P2 = F->getArg(1); Value *NullPtr = Constant::getNullValue(P1->getType()); auto InstIter = F->front().begin(); CallInst *UserOfY = cast(&*InstIter); Value *ConstDerefPtr = UserOfY->getArgOperand(0); // We cannot replace two pointers in arbitrary instructions unless we are // replacing with null, a constant dereferencable pointer or they have the // same underlying object. EXPECT_FALSE(canReplacePointersIfEqual(ConstDerefPtr, P1, DL)); EXPECT_FALSE(canReplacePointersIfEqual(P1, P2, DL)); EXPECT_TRUE(canReplacePointersIfEqual(P1, ConstDerefPtr, DL)); EXPECT_TRUE(canReplacePointersIfEqual(P1, NullPtr, DL)); GetElementPtrInst *BasedOnP1 = cast(&*++InstIter); EXPECT_TRUE(canReplacePointersIfEqual(BasedOnP1, P1, DL)); EXPECT_FALSE(canReplacePointersIfEqual(BasedOnP1, P2, DL)); // We can replace two arbitrary pointers in icmp and ptrtoint instructions. auto P1UseIter = P1->use_begin(); const Use &PtrToIntUse = *P1UseIter; const Use &IcmpUse = *++P1UseIter; const Use &GEPUse = *++P1UseIter; EXPECT_FALSE(canReplacePointersInUseIfEqual(GEPUse, P2, DL)); EXPECT_TRUE(canReplacePointersInUseIfEqual(PtrToIntUse, P2, DL)); EXPECT_TRUE(canReplacePointersInUseIfEqual(IcmpUse, P2, DL)); } TEST(LoadsTest, IsDerefReadOnlyLoop) { LLVMContext C; std::unique_ptr M = parseIR(C, R"IR( define i64 @f1() { entry: %p1 = alloca [1024 x i8] %p2 = alloca [1024 x i8] br label %loop loop: %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ] %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index %ld1 = load i8, ptr %arrayidx, align 1 %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index %ld2 = load i8, ptr %arrayidx1, align 1 %cmp3 = icmp eq i8 %ld1, %ld2 br i1 %cmp3, label %loop.inc, label %loop.end loop.inc: %index.next = add i64 %index, 1 %exitcond = icmp ne i64 %index.next, 67 br i1 %exitcond, label %loop, label %loop.end loop.end: %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ] ret i64 %retval } define i64 @f2(ptr %p1) { entry: %p2 = alloca [1024 x i8] br label %loop loop: %index = phi i64 [ %index.next, %loop.inc ], [ 3, %entry ] %arrayidx = getelementptr inbounds i8, ptr %p1, i64 %index %ld1 = load i8, ptr %arrayidx, align 1 %arrayidx1 = getelementptr inbounds i8, ptr %p2, i64 %index %ld2 = load i8, ptr %arrayidx1, align 1 %cmp3 = icmp eq i8 %ld1, %ld2 br i1 %cmp3, label %loop.inc, label %loop.end loop.inc: %index.next = add i64 %index, 1 %exitcond = icmp ne i64 %index.next, 67 br i1 %exitcond, label %loop, label %loop.end loop.end: %retval = phi i64 [ %index, %loop ], [ 67, %loop.inc ] ret i64 %retval } )IR"); auto *GV1 = M->getNamedValue("f1"); auto *GV2 = M->getNamedValue("f2"); ASSERT_TRUE(GV1 && GV2); auto *F1 = dyn_cast(GV1); auto *F2 = dyn_cast(GV2); ASSERT_TRUE(F1 && F2); TargetLibraryInfoImpl TLII; TargetLibraryInfo TLI(TLII); auto IsDerefReadOnlyLoop = [&TLI](Function *F) -> bool { AssumptionCache AC(*F); DominatorTree DT(*F); LoopInfo LI(DT); ScalarEvolution SE(*F, TLI, AC, DT, LI); Function::iterator FI = F->begin(); // First basic block is entry - skip it. BasicBlock *Header = &*(++FI); assert(Header->getName() == "loop"); Loop *L = LI.getLoopFor(Header); return isDereferenceableReadOnlyLoop(L, &SE, &DT, &AC); }; ASSERT_TRUE(IsDerefReadOnlyLoop(F1)); ASSERT_FALSE(IsDerefReadOnlyLoop(F2)); }