429 lines · cpp
1//===--- AliasAnalysisTest.cpp - Mixed TBAA unit tests --------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/Analysis/AliasAnalysis.h"10#include "llvm/ADT/SetVector.h"11#include "llvm/Analysis/AssumptionCache.h"12#include "llvm/Analysis/BasicAliasAnalysis.h"13#include "llvm/Analysis/TargetLibraryInfo.h"14#include "llvm/AsmParser/Parser.h"15#include "llvm/IR/Constants.h"16#include "llvm/IR/InstIterator.h"17#include "llvm/IR/Instructions.h"18#include "llvm/IR/LLVMContext.h"19#include "llvm/IR/LegacyPassManager.h"20#include "llvm/IR/Module.h"21#include "llvm/InitializePasses.h"22#include "llvm/Support/SourceMgr.h"23#include "gtest/gtest.h"24 25using namespace llvm;26 27// Set up some test passes.28namespace llvm {29void initializeAATestPassPass(PassRegistry&);30void initializeTestCustomAAWrapperPassPass(PassRegistry&);31}32 33namespace {34struct AATestPass : FunctionPass {35 static char ID;36 AATestPass() : FunctionPass(ID) {37 initializeAATestPassPass(*PassRegistry::getPassRegistry());38 }39 40 void getAnalysisUsage(AnalysisUsage &AU) const override {41 AU.addRequired<AAResultsWrapperPass>();42 AU.setPreservesAll();43 }44 45 bool runOnFunction(Function &F) override {46 AliasAnalysis &AA = getAnalysis<AAResultsWrapperPass>().getAAResults();47 48 SetVector<Value *> Pointers;49 for (Argument &A : F.args())50 if (A.getType()->isPointerTy())51 Pointers.insert(&A);52 for (Instruction &I : instructions(F))53 if (I.getType()->isPointerTy())54 Pointers.insert(&I);55 56 for (Value *P1 : Pointers)57 for (Value *P2 : Pointers)58 (void)AA.alias(P1, LocationSize::beforeOrAfterPointer(), P2,59 LocationSize::beforeOrAfterPointer());60 61 return false;62 }63};64}65 66char AATestPass::ID = 0;67INITIALIZE_PASS_BEGIN(AATestPass, "aa-test-pas", "Alias Analysis Test Pass",68 false, true)69INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)70INITIALIZE_PASS_END(AATestPass, "aa-test-pass", "Alias Analysis Test Pass",71 false, true)72 73namespace {74/// A test customizable AA result. It merely accepts a callback to run whenever75/// it receives an alias query. Useful for testing that a particular AA result76/// is reached.77struct TestCustomAAResult : AAResultBase {78 std::function<void()> CB;79 80 explicit TestCustomAAResult(std::function<void()> CB)81 : AAResultBase(), CB(std::move(CB)) {}82 TestCustomAAResult(TestCustomAAResult &&Arg)83 : AAResultBase(std::move(Arg)), CB(std::move(Arg.CB)) {}84 85 bool invalidate(Function &, const PreservedAnalyses &) { return false; }86 87 AliasResult alias(const MemoryLocation &LocA, const MemoryLocation &LocB,88 AAQueryInfo &AAQI, const Instruction *) {89 CB();90 return AliasResult::MayAlias;91 }92};93}94 95namespace {96/// A wrapper pass for the legacy pass manager to use with the above custom AA97/// result.98class TestCustomAAWrapperPass : public ImmutablePass {99 std::function<void()> CB;100 std::unique_ptr<TestCustomAAResult> Result;101 102public:103 static char ID;104 105 explicit TestCustomAAWrapperPass(106 std::function<void()> CB = std::function<void()>())107 : ImmutablePass(ID), CB(std::move(CB)) {108 initializeTestCustomAAWrapperPassPass(*PassRegistry::getPassRegistry());109 }110 111 void getAnalysisUsage(AnalysisUsage &AU) const override {112 AU.setPreservesAll();113 AU.addRequired<TargetLibraryInfoWrapperPass>();114 }115 116 bool doInitialization(Module &M) override {117 Result.reset(new TestCustomAAResult(std::move(CB)));118 return true;119 }120 121 bool doFinalization(Module &M) override {122 Result.reset();123 return true;124 }125 126 TestCustomAAResult &getResult() { return *Result; }127 const TestCustomAAResult &getResult() const { return *Result; }128};129}130 131char TestCustomAAWrapperPass::ID = 0;132INITIALIZE_PASS_BEGIN(TestCustomAAWrapperPass, "test-custom-aa",133 "Test Custom AA Wrapper Pass", false, true)134INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)135INITIALIZE_PASS_END(TestCustomAAWrapperPass, "test-custom-aa",136 "Test Custom AA Wrapper Pass", false, true)137 138namespace {139 140class AliasAnalysisTest : public testing::Test {141protected:142 LLVMContext C;143 Module M;144 TargetLibraryInfoImpl TLII;145 TargetLibraryInfo TLI;146 std::unique_ptr<AssumptionCache> AC;147 std::unique_ptr<BasicAAResult> BAR;148 std::unique_ptr<AAResults> AAR;149 150 AliasAnalysisTest()151 : M("AliasAnalysisTest", C), TLII(M.getTargetTriple()), TLI(TLII) {}152 153 AAResults &getAAResults(Function &F) {154 // Reset the Function AA results first to clear out any references.155 AAR.reset(new AAResults(TLI));156 157 // Build the various AA results and register them.158 AC.reset(new AssumptionCache(F));159 BAR.reset(new BasicAAResult(M.getDataLayout(), F, TLI, *AC));160 AAR->addAAResult(*BAR);161 162 return *AAR;163 }164};165 166TEST_F(AliasAnalysisTest, getModRefInfo) {167 // Setup function.168 FunctionType *FTy =169 FunctionType::get(Type::getVoidTy(C), std::vector<Type *>(), false);170 auto *F = Function::Create(FTy, Function::ExternalLinkage, "f", M);171 auto *BB = BasicBlock::Create(C, "entry", F);172 auto IntType = Type::getInt32Ty(C);173 auto PtrType = PointerType::get(C, 0);174 auto *Value = ConstantInt::get(IntType, 42);175 auto *Addr = ConstantPointerNull::get(PtrType);176 auto Alignment = Align(IntType->getBitWidth() / 8);177 178 auto *Store1 = new StoreInst(Value, Addr, BB);179 auto *Load1 = new LoadInst(IntType, Addr, "load", BB);180 auto *Add1 = BinaryOperator::CreateAdd(Value, Value, "add", BB);181 auto *VAArg1 = new VAArgInst(Addr, PtrType, "vaarg", BB);182 auto *CmpXChg1 = new AtomicCmpXchgInst(183 Addr, ConstantInt::get(IntType, 0), ConstantInt::get(IntType, 1),184 Alignment, AtomicOrdering::Monotonic, AtomicOrdering::Monotonic,185 SyncScope::System, BB);186 auto *AtomicRMW = new AtomicRMWInst(187 AtomicRMWInst::Xchg, Addr, ConstantInt::get(IntType, 1), Alignment,188 AtomicOrdering::Monotonic, SyncScope::System, BB);189 190 FunctionType *FooBarTy = FunctionType::get(Type::getVoidTy(C), {}, false);191 Function::Create(FooBarTy, Function::ExternalLinkage, "foo", &M);192 auto *BarF = Function::Create(FooBarTy, Function::ExternalLinkage, "bar", &M);193 BarF->setDoesNotAccessMemory();194 195 const Instruction *Foo = CallInst::Create(M.getFunction("foo"), {}, BB);196 const Instruction *Bar = CallInst::Create(M.getFunction("bar"), {}, BB);197 198 ReturnInst::Create(C, nullptr, BB);199 200 auto &AA = getAAResults(*F);201 202 // Check basic results203 EXPECT_EQ(AA.getModRefInfo(Store1, MemoryLocation()), ModRefInfo::Mod);204 EXPECT_EQ(AA.getModRefInfo(Store1, std::nullopt), ModRefInfo::Mod);205 EXPECT_EQ(AA.getModRefInfo(Load1, MemoryLocation()), ModRefInfo::Ref);206 EXPECT_EQ(AA.getModRefInfo(Load1, std::nullopt), ModRefInfo::Ref);207 EXPECT_EQ(AA.getModRefInfo(Add1, MemoryLocation()), ModRefInfo::NoModRef);208 EXPECT_EQ(AA.getModRefInfo(Add1, std::nullopt), ModRefInfo::NoModRef);209 EXPECT_EQ(AA.getModRefInfo(VAArg1, MemoryLocation()), ModRefInfo::ModRef);210 EXPECT_EQ(AA.getModRefInfo(VAArg1, std::nullopt), ModRefInfo::ModRef);211 EXPECT_EQ(AA.getModRefInfo(CmpXChg1, MemoryLocation()), ModRefInfo::ModRef);212 EXPECT_EQ(AA.getModRefInfo(CmpXChg1, std::nullopt), ModRefInfo::ModRef);213 EXPECT_EQ(AA.getModRefInfo(AtomicRMW, MemoryLocation()), ModRefInfo::ModRef);214 EXPECT_EQ(AA.getModRefInfo(AtomicRMW, std::nullopt), ModRefInfo::ModRef);215 EXPECT_EQ(AA.getModRefInfo(Store1, Load1), ModRefInfo::ModRef);216 EXPECT_EQ(AA.getModRefInfo(Store1, Store1), ModRefInfo::ModRef);217 EXPECT_EQ(AA.getModRefInfo(Store1, Add1), ModRefInfo::NoModRef);218 EXPECT_EQ(AA.getModRefInfo(Store1, Foo), ModRefInfo::ModRef);219 EXPECT_EQ(AA.getModRefInfo(Store1, Bar), ModRefInfo::NoModRef);220 EXPECT_EQ(AA.getModRefInfo(Foo, Bar), ModRefInfo::NoModRef);221 EXPECT_EQ(AA.getModRefInfo(Foo, Foo), ModRefInfo::ModRef);222}223 224static Instruction *getInstructionByName(Function &F, StringRef Name) {225 for (auto &I : instructions(F))226 if (I.getName() == Name)227 return &I;228 llvm_unreachable("Expected to find instruction!");229}230 231TEST_F(AliasAnalysisTest, BatchAAPhiCycles) {232 LLVMContext C;233 SMDiagnostic Err;234 std::unique_ptr<Module> M = parseAssemblyString(R"(235 define void @f(ptr noalias %a, i1 %c) {236 entry:237 br label %loop238 239 loop:240 %phi = phi ptr [ null, %entry ], [ %a2, %loop ]241 %offset1 = phi i64 [ 0, %entry ], [ %offset2, %loop]242 %offset2 = add i64 %offset1, 1243 %a1 = getelementptr i8, ptr %a, i64 %offset1244 %a2 = getelementptr i8, ptr %a, i64 %offset2245 %s1 = select i1 %c, ptr %a1, ptr %phi246 %s2 = select i1 %c, ptr %a2, ptr %a1247 br label %loop248 }249 )", Err, C);250 251 Function *F = M->getFunction("f");252 Instruction *Phi = getInstructionByName(*F, "phi");253 Instruction *A1 = getInstructionByName(*F, "a1");254 Instruction *A2 = getInstructionByName(*F, "a2");255 Instruction *S1 = getInstructionByName(*F, "s1");256 Instruction *S2 = getInstructionByName(*F, "s2");257 MemoryLocation PhiLoc(Phi, LocationSize::precise(1));258 MemoryLocation A1Loc(A1, LocationSize::precise(1));259 MemoryLocation A2Loc(A2, LocationSize::precise(1));260 MemoryLocation S1Loc(S1, LocationSize::precise(1));261 MemoryLocation S2Loc(S2, LocationSize::precise(1));262 263 auto &AA = getAAResults(*F);264 EXPECT_EQ(AliasResult::NoAlias, AA.alias(A1Loc, A2Loc));265 EXPECT_EQ(AliasResult::MayAlias, AA.alias(PhiLoc, A1Loc));266 EXPECT_EQ(AliasResult::MayAlias, AA.alias(S1Loc, S2Loc));267 268 BatchAAResults BatchAA(AA);269 EXPECT_EQ(AliasResult::NoAlias, BatchAA.alias(A1Loc, A2Loc));270 EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(PhiLoc, A1Loc));271 EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(S1Loc, S2Loc));272 273 BatchAAResults BatchAA2(AA);274 EXPECT_EQ(AliasResult::NoAlias, BatchAA2.alias(A1Loc, A2Loc));275 EXPECT_EQ(AliasResult::MayAlias, BatchAA2.alias(S1Loc, S2Loc));276 EXPECT_EQ(AliasResult::MayAlias, BatchAA2.alias(PhiLoc, A1Loc));277}278 279TEST_F(AliasAnalysisTest, BatchAAPhiAssumption) {280 LLVMContext C;281 SMDiagnostic Err;282 std::unique_ptr<Module> M = parseAssemblyString(R"(283 define void @f(ptr %a.base, ptr %b.base, i1 %c) {284 entry:285 br label %loop286 287 loop:288 %a = phi ptr [ %a.next, %loop ], [ %a.base, %entry ]289 %b = phi ptr [ %b.next, %loop ], [ %b.base, %entry ]290 %a.next = getelementptr i8, ptr %a, i64 1291 %b.next = getelementptr i8, ptr %b, i64 1292 br label %loop293 }294 )", Err, C);295 296 Function *F = M->getFunction("f");297 Instruction *A = getInstructionByName(*F, "a");298 Instruction *B = getInstructionByName(*F, "b");299 Instruction *ANext = getInstructionByName(*F, "a.next");300 Instruction *BNext = getInstructionByName(*F, "b.next");301 MemoryLocation ALoc(A, LocationSize::precise(1));302 MemoryLocation BLoc(B, LocationSize::precise(1));303 MemoryLocation ANextLoc(ANext, LocationSize::precise(1));304 MemoryLocation BNextLoc(BNext, LocationSize::precise(1));305 306 auto &AA = getAAResults(*F);307 EXPECT_EQ(AliasResult::MayAlias, AA.alias(ALoc, BLoc));308 EXPECT_EQ(AliasResult::MayAlias, AA.alias(ANextLoc, BNextLoc));309 310 BatchAAResults BatchAA(AA);311 EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(ALoc, BLoc));312 EXPECT_EQ(AliasResult::MayAlias, BatchAA.alias(ANextLoc, BNextLoc));313}314 315// Check that two aliased GEPs with non-constant offsets are correctly316// analyzed and their relative offset can be requested from AA.317TEST_F(AliasAnalysisTest, PartialAliasOffset) {318 LLVMContext C;319 SMDiagnostic Err;320 std::unique_ptr<Module> M = parseAssemblyString(R"(321 define void @foo(ptr %arg, i32 %i) {322 bb:323 %i2 = zext i32 %i to i64324 %i3 = getelementptr inbounds float, ptr %arg, i64 %i2325 %i4 = bitcast ptr %i3 to ptr326 %L1 = load <2 x float>, ptr %i4, align 16327 %i7 = add nuw nsw i32 %i, 1328 %i8 = zext i32 %i7 to i64329 %i9 = getelementptr inbounds float, ptr %arg, i64 %i8330 %L2 = load float, ptr %i9, align 4331 ret void332 }333 )",334 Err, C);335 336 if (!M)337 Err.print("PartialAliasOffset", errs());338 339 Function *F = M->getFunction("foo");340 const auto Loc1 = MemoryLocation::get(getInstructionByName(*F, "L1"));341 const auto Loc2 = MemoryLocation::get(getInstructionByName(*F, "L2"));342 343 auto &AA = getAAResults(*F);344 345 const auto AR = AA.alias(Loc1, Loc2);346 EXPECT_EQ(AR, AliasResult::PartialAlias);347 EXPECT_EQ(4, AR.getOffset());348}349 350// Check that swapping the order of parameters to `AA.alias()` changes offset351// sign and that the sign is such that FirstLoc + Offset == SecondLoc.352TEST_F(AliasAnalysisTest, PartialAliasOffsetSign) {353 LLVMContext C;354 SMDiagnostic Err;355 std::unique_ptr<Module> M = parseAssemblyString(R"(356 define void @f(ptr %p) {357 %L1 = load i64, ptr %p358 %p.i8 = bitcast ptr %p to ptr359 %q = getelementptr i8, ptr %p.i8, i32 1360 %L2 = load i8, ptr %q361 ret void362 }363 )",364 Err, C);365 366 if (!M)367 Err.print("PartialAliasOffsetSign", errs());368 369 Function *F = M->getFunction("f");370 const auto Loc1 = MemoryLocation::get(getInstructionByName(*F, "L1"));371 const auto Loc2 = MemoryLocation::get(getInstructionByName(*F, "L2"));372 373 auto &AA = getAAResults(*F);374 375 auto AR = AA.alias(Loc1, Loc2);376 EXPECT_EQ(AR, AliasResult::PartialAlias);377 EXPECT_EQ(1, AR.getOffset());378 379 AR = AA.alias(Loc2, Loc1);380 EXPECT_EQ(AR, AliasResult::PartialAlias);381 EXPECT_EQ(-1, AR.getOffset());382}383class AAPassInfraTest : public testing::Test {384protected:385 LLVMContext C;386 SMDiagnostic Err;387 std::unique_ptr<Module> M;388 389public:390 AAPassInfraTest()391 : M(parseAssemblyString("define i32 @f(ptr %x, ptr %y) {\n"392 "entry:\n"393 " %lx = load i32, ptr %x\n"394 " %ly = load i32, ptr %y\n"395 " %sum = add i32 %lx, %ly\n"396 " ret i32 %sum\n"397 "}\n",398 Err, C)) {399 assert(M && "Failed to build the module!");400 }401};402 403TEST_F(AAPassInfraTest, injectExternalAA) {404 legacy::PassManager PM;405 406 // Register our custom AA's wrapper pass manually.407 bool IsCustomAAQueried = false;408 PM.add(new TestCustomAAWrapperPass([&] { IsCustomAAQueried = true; }));409 410 // Now add the external AA wrapper with a lambda which queries for the411 // wrapper around our custom AA and adds it to the results.412 PM.add(createExternalAAWrapperPass([](Pass &P, Function &, AAResults &AAR) {413 if (auto *WrapperPass = P.getAnalysisIfAvailable<TestCustomAAWrapperPass>())414 AAR.addAAResult(WrapperPass->getResult());415 }));416 417 // And run a pass that will make some alias queries. This will automatically418 // trigger the rest of the alias analysis stack to be run. It is analagous to419 // building a full pass pipeline with any of the existing pass manager420 // builders.421 PM.add(new AATestPass());422 PM.run(*M);423 424 // Finally, ensure that our custom AA was indeed queried.425 EXPECT_TRUE(IsCustomAAQueried);426}427 428} // end anonymous namspace429