From 772d8502adb73de6cd0fb0320cd3da48e86b4c04 Mon Sep 17 00:00:00 2001 From: Hal Finkel Date: Sun, 10 Jul 2016 22:02:55 +0000 Subject: [PATCH] Let FuncAttrs infer the 'returned' argument attribute A function can have one argument with the 'returned' attribute, indicating that the associated argument is always the return value of the function. Add FuncAttrs inference logic. Differential Revision: http://reviews.llvm.org/D22202 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@275027 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Transforms/IPO/FunctionAttrs.cpp | 42 ++++++++++++++++++++++ .../FunctionAttrs/2009-01-02-LocalStores.ll | 2 +- test/Transforms/FunctionAttrs/nocapture.ll | 6 ++-- test/Transforms/FunctionAttrs/readattrs.ll | 4 +-- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/lib/Transforms/IPO/FunctionAttrs.cpp b/lib/Transforms/IPO/FunctionAttrs.cpp index fff54408541..3f87a1b233f 100644 --- a/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/lib/Transforms/IPO/FunctionAttrs.cpp @@ -42,6 +42,7 @@ using namespace llvm; STATISTIC(NumReadNone, "Number of functions marked readnone"); STATISTIC(NumReadOnly, "Number of functions marked readonly"); STATISTIC(NumNoCapture, "Number of arguments marked nocapture"); +STATISTIC(NumReturned, "Number of arguments marked returned"); STATISTIC(NumReadNoneArg, "Number of arguments marked readnone"); STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly"); STATISTIC(NumNoAlias, "Number of function returns marked noalias"); @@ -483,6 +484,45 @@ determinePointerReadAttrs(Argument *A, return IsRead ? Attribute::ReadOnly : Attribute::ReadNone; } +/// Deduce returned attributes for the SCC. +static bool addArgumentReturnedAttrs(const SCCNodeSet &SCCNodes) { + bool Changed = false; + + AttrBuilder B; + B.addAttribute(Attribute::Returned); + + // Check each function in turn, determining if an argument is always returned. + for (Function *F : SCCNodes) { + // We can infer and propagate function attributes only when we know that the + // definition we'll get at link time is *exactly* the definition we see now. + // For more details, see GlobalValue::mayBeDerefined. + if (!F->hasExactDefinition()) + continue; + + if (F->getReturnType()->isVoidTy()) + continue; + + SmallPtrSet RetArgs; + for (BasicBlock &BB : *F) + if (auto *Ret = dyn_cast(BB.getTerminator())) { + // Note that stripPointerCasts should look through functions with + // returned arguments. + Value *RetVal = Ret->getReturnValue()->stripPointerCasts(); + if (RetVal->getType() == F->getReturnType() && isa(RetVal)) + RetArgs.insert(RetVal); + } + + if (RetArgs.size() == 1) { + auto *A = cast(*RetArgs.begin()); + A->addAttr(AttributeSet::get(F->getContext(), A->getArgNo() + 1, B)); + ++NumReturned; + Changed = true; + } + } + + return Changed; +} + /// Deduce nocapture attributes for the SCC. static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) { bool Changed = false; @@ -1024,6 +1064,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, } bool Changed = false; + Changed |= addArgumentReturnedAttrs(SCCNodes); Changed |= addReadAttrs(SCCNodes, AARGetter); Changed |= addArgumentAttrs(SCCNodes); @@ -1089,6 +1130,7 @@ static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { SCCNodes.insert(F); } + Changed |= addArgumentReturnedAttrs(SCCNodes); Changed |= addReadAttrs(SCCNodes, AARGetter); Changed |= addArgumentAttrs(SCCNodes); diff --git a/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll b/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll index ec1db095728..0d0231b42ae 100644 --- a/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll +++ b/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll @@ -14,7 +14,7 @@ define i32* @b(i32 *%q) { ret i32* %tmp } -; CHECK: define i32* @c(i32* readnone %r) +; CHECK: define i32* @c(i32* readnone returned %r) @g = global i32 0 define i32* @c(i32 *%r) { %a = icmp eq i32* %r, null diff --git a/test/Transforms/FunctionAttrs/nocapture.ll b/test/Transforms/FunctionAttrs/nocapture.ll index 020d8bcd4c7..e137a3800ce 100644 --- a/test/Transforms/FunctionAttrs/nocapture.ll +++ b/test/Transforms/FunctionAttrs/nocapture.ll @@ -1,7 +1,7 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s @g = global i32* null ; [#uses=1] -; CHECK: define i32* @c1(i32* readnone %q) +; CHECK: define i32* @c1(i32* readnone returned %q) define i32* @c1(i32* %q) { ret i32* %q } @@ -140,7 +140,7 @@ define void @test1_1(i8* %x1_1, i8* %y1_1) { ret void } -; CHECK: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* %y1_2) +; CHECK: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2) define i8* @test1_2(i8* %x1_2, i8* %y1_2) { call void @test1_1(i8* %x1_2, i8* %y1_2) store i32* null, i32** @g @@ -168,7 +168,7 @@ define void @test4_1(i8* %x4_1) { ret void } -; CHECK: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone %y4_2, i8* nocapture readnone %z4_2) +; CHECK: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2) define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2) { call void @test4_1(i8* null) store i32* null, i32** @g diff --git a/test/Transforms/FunctionAttrs/readattrs.ll b/test/Transforms/FunctionAttrs/readattrs.ll index 65861a55fc2..988557e2715 100644 --- a/test/Transforms/FunctionAttrs/readattrs.ll +++ b/test/Transforms/FunctionAttrs/readattrs.ll @@ -11,7 +11,7 @@ define void @test1_2(i8* %x1_2, i8* %y1_2, i8* %z1_2) { ret void } -; CHECK: define i8* @test2(i8* readnone %p) +; CHECK: define i8* @test2(i8* readnone returned %p) define i8* @test2(i8* %p) { store i32 0, i32* @x ret i8* %p @@ -53,7 +53,7 @@ define void @test7_1(i32* inalloca %a) { ret void } -; CHECK: define i32* @test8_1(i32* readnone %p) +; CHECK: define i32* @test8_1(i32* readnone returned %p) define i32* @test8_1(i32* %p) { entry: ret i32* %p -- 2.11.0