From 5a9e526f29cf8510ab5c3d566fbdcf47ac24e1e9 Mon Sep 17 00:00:00 2001 From: Vaivaswatha Nagaraj Date: Fri, 18 Dec 2015 11:02:52 +0000 Subject: [PATCH] GlobalsAA: Take advantage of ArgMemOnly, InaccessibleMemOnly and InaccessibleMemOrArgMemOnly attributes Summary: 1. Modify AnalyzeCallGraph() to retain function info for external functions if the function has [InaccessibleMemOr]ArgMemOnly flags. 2. When analyzing the use of a global is function parameter at a call site, mark the callee also as modifying the global appropriately. 3. Add additional test cases. Depends on D15499 Reviewers: hfinkel, jmolloy Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D15605 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@255994 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/GlobalsModRef.cpp | 17 ++++++- .../GlobalsModRef/argmemonly-escape.ll | 47 +++++++++++++++++++ test/Analysis/GlobalsModRef/modreftest.ll | 20 ++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 test/Analysis/GlobalsModRef/argmemonly-escape.ll diff --git a/lib/Analysis/GlobalsModRef.cpp b/lib/Analysis/GlobalsModRef.cpp index 86c2e501465..51a83d53277 100644 --- a/lib/Analysis/GlobalsModRef.cpp +++ b/lib/Analysis/GlobalsModRef.cpp @@ -376,6 +376,15 @@ bool GlobalsAAResult::AnalyzeUsesOfPointer(Value *V, } else { return true; // Argument of an unknown call. } + // If the Callee is not ReadNone, it may read the global, + // and if it is not ReadOnly, it may also write to it. + Function *CalleeF = CS.getCalledFunction(); + if (!CalleeF->doesNotAccessMemory()) { + if (Readers) + Readers->insert(CalleeF); + if (Writers && !CalleeF->onlyReadsMemory()) + Writers->insert(CalleeF); + } } } else if (ICmpInst *ICI = dyn_cast(I)) { if (!isa(ICI->getOperand(1))) @@ -507,7 +516,7 @@ void GlobalsAAResult::AnalyzeCallGraph(CallGraph &CG, Module &M) { if (F->isDeclaration()) { // Try to get mod/ref behaviour from function attributes. - if (F->doesNotAccessMemory()) { + if (F->doesNotAccessMemory() || F->onlyAccessesInaccessibleMemory()) { // Can't do better than that! } else if (F->onlyReadsMemory()) { FI.addModRefInfo(MRI_Ref); @@ -515,6 +524,12 @@ void GlobalsAAResult::AnalyzeCallGraph(CallGraph &CG, Module &M) { // This function might call back into the module and read a global - // consider every global as possibly being read by this function. FI.setMayReadAnyGlobal(); + } else if (F->onlyAccessesArgMemory() || + F->onlyAccessesInaccessibleMemOrArgMem()) { + // This function may only access (read/write) memory pointed to by its + // arguments. If this pointer is to a global, this escaping use of the + // pointer is captured in AnalyzeUsesOfPointer(). + FI.addModRefInfo(MRI_ModRef); } else { FI.addModRefInfo(MRI_ModRef); // Can't say anything useful unless it's an intrinsic - they don't diff --git a/test/Analysis/GlobalsModRef/argmemonly-escape.ll b/test/Analysis/GlobalsModRef/argmemonly-escape.ll new file mode 100644 index 00000000000..64c625810af --- /dev/null +++ b/test/Analysis/GlobalsModRef/argmemonly-escape.ll @@ -0,0 +1,47 @@ +; RUN: opt < %s -O1 -S -enable-non-lto-gmr=true | FileCheck %s + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.10.0" + +@a = internal global [3 x i32] zeroinitializer, align 4 + +; The important thing we're checking for here is the reload of (some element of) +; @a after the memset. + +; CHECK-LABEL: @main +; CHECK: load i32, i32* getelementptr {{.*}} @a +; CHECK-NEXT: call void @memsetp0i8i64{{.*}} @a +; CHECK-NEXT: load i32, i32* getelementptr {{.*}} @a +; CHECK-NEXT: call void @memsetp0i8i64A{{.*}} @a +; CHECK-NEXT: load i32, i32* getelementptr {{.*}} @a +; CHECK: icmp eq +; CHECK: br i1 + +define i32 @main() { +entry: + %0 = bitcast [3 x i32]* @a to i8* + %1 = load i32, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @a, i64 0, i64 2), align 4 + call void @memsetp0i8i64(i8* %0, i8 0, i64 4, i32 4, i1 false) + %2 = load i32, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @a, i64 0, i64 2), align 4 + call void @memsetp0i8i64A(i8* %0, i8 0, i64 4, i32 4, i1 false) + %3 = load i32, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @a, i64 0, i64 2), align 4 + %4 = add i32 %2, %3 + %cmp1 = icmp eq i32 %1, %4 + br i1 %cmp1, label %if.then, label %if.end + +if.then: ; preds = %entr + call void @abort() #3 + unreachable + +if.end: ; preds = %entry + ret i32 0 +} + +; Function Attrs: nounwind argmemonly +declare void @memsetp0i8i64(i8* nocapture, i8, i64, i32, i1) nounwind argmemonly + +; Function Attrs: nounwind inaccessiblemem_or_argmemonly +declare void @memsetp0i8i64A(i8* nocapture, i8, i64, i32, i1) nounwind inaccessiblemem_or_argmemonly + +; Function Attrs: noreturn nounwind +declare void @abort() noreturn nounwind diff --git a/test/Analysis/GlobalsModRef/modreftest.ll b/test/Analysis/GlobalsModRef/modreftest.ll index 07497705e65..2018b149fc0 100644 --- a/test/Analysis/GlobalsModRef/modreftest.ll +++ b/test/Analysis/GlobalsModRef/modreftest.ll @@ -16,3 +16,23 @@ define i32 @test(i32* %P) { define void @doesnotmodX() { ret void } + +declare void @InaccessibleMemOnlyFunc( ) #0 +declare void @InaccessibleMemOrArgMemOnlyFunc( ) #1 + +define i32 @test2(i32* %P) { +; CHECK: @test2 +; CHECK-NEXT: store i32 12, i32* @X +; CHECK-NEXT: call void @InaccessibleMemOnlyFunc() +; CHECK-NEXT: call void @InaccessibleMemOrArgMemOnlyFunc() +; CHECK-NOT: load i32 +; CHECK-NEXT: ret i32 12 + store i32 12, i32* @X + call void @InaccessibleMemOnlyFunc( ) + call void @InaccessibleMemOrArgMemOnlyFunc( ) + %V = load i32, i32* @X ; [#uses=1] + ret i32 %V +} + +attributes #0 = { inaccessiblememonly } +attributes #1 = { inaccessiblemem_or_argmemonly } -- 2.34.1