forked from OSchip/llvm-project
				
			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 llvm-svn: 275027
This commit is contained in:
		
							parent
							
								
									3b66caa290
								
							
						
					
					
						commit
						d66a7b05db
					
				| 
						 | 
					@ -42,6 +42,7 @@ using namespace llvm;
 | 
				
			||||||
STATISTIC(NumReadNone, "Number of functions marked readnone");
 | 
					STATISTIC(NumReadNone, "Number of functions marked readnone");
 | 
				
			||||||
STATISTIC(NumReadOnly, "Number of functions marked readonly");
 | 
					STATISTIC(NumReadOnly, "Number of functions marked readonly");
 | 
				
			||||||
STATISTIC(NumNoCapture, "Number of arguments marked nocapture");
 | 
					STATISTIC(NumNoCapture, "Number of arguments marked nocapture");
 | 
				
			||||||
 | 
					STATISTIC(NumReturned, "Number of arguments marked returned");
 | 
				
			||||||
STATISTIC(NumReadNoneArg, "Number of arguments marked readnone");
 | 
					STATISTIC(NumReadNoneArg, "Number of arguments marked readnone");
 | 
				
			||||||
STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly");
 | 
					STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly");
 | 
				
			||||||
STATISTIC(NumNoAlias, "Number of function returns marked noalias");
 | 
					STATISTIC(NumNoAlias, "Number of function returns marked noalias");
 | 
				
			||||||
| 
						 | 
					@ -483,6 +484,45 @@ determinePointerReadAttrs(Argument *A,
 | 
				
			||||||
  return IsRead ? Attribute::ReadOnly : Attribute::ReadNone;
 | 
					  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<Value *, 2> RetArgs;
 | 
				
			||||||
 | 
					    for (BasicBlock &BB : *F)
 | 
				
			||||||
 | 
					      if (auto *Ret = dyn_cast<ReturnInst>(BB.getTerminator())) {
 | 
				
			||||||
 | 
					        // Note that stripPointerCasts should look through functions with
 | 
				
			||||||
 | 
					        // returned arguments.
 | 
				
			||||||
 | 
					        Value *RetVal = Ret->getReturnValue()->stripPointerCasts();
 | 
				
			||||||
 | 
					        if (RetVal->getType() == F->getReturnType() && isa<Argument>(RetVal))
 | 
				
			||||||
 | 
					          RetArgs.insert(RetVal);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (RetArgs.size() == 1) {
 | 
				
			||||||
 | 
					      auto *A = cast<Argument>(*RetArgs.begin());
 | 
				
			||||||
 | 
					      A->addAttr(AttributeSet::get(F->getContext(), A->getArgNo() + 1, B));
 | 
				
			||||||
 | 
					      ++NumReturned;
 | 
				
			||||||
 | 
					      Changed = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Changed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Deduce nocapture attributes for the SCC.
 | 
					/// Deduce nocapture attributes for the SCC.
 | 
				
			||||||
static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) {
 | 
					static bool addArgumentAttrs(const SCCNodeSet &SCCNodes) {
 | 
				
			||||||
  bool Changed = false;
 | 
					  bool Changed = false;
 | 
				
			||||||
| 
						 | 
					@ -1024,6 +1064,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C,
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool Changed = false;
 | 
					  bool Changed = false;
 | 
				
			||||||
 | 
					  Changed |= addArgumentReturnedAttrs(SCCNodes);
 | 
				
			||||||
  Changed |= addReadAttrs(SCCNodes, AARGetter);
 | 
					  Changed |= addReadAttrs(SCCNodes, AARGetter);
 | 
				
			||||||
  Changed |= addArgumentAttrs(SCCNodes);
 | 
					  Changed |= addArgumentAttrs(SCCNodes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1089,6 +1130,7 @@ static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) {
 | 
				
			||||||
    SCCNodes.insert(F);
 | 
					    SCCNodes.insert(F);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Changed |= addArgumentReturnedAttrs(SCCNodes);
 | 
				
			||||||
  Changed |= addReadAttrs(SCCNodes, AARGetter);
 | 
					  Changed |= addReadAttrs(SCCNodes, AARGetter);
 | 
				
			||||||
  Changed |= addArgumentAttrs(SCCNodes);
 | 
					  Changed |= addArgumentAttrs(SCCNodes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ define i32* @b(i32 *%q) {
 | 
				
			||||||
	ret i32* %tmp
 | 
						ret i32* %tmp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; CHECK: define i32* @c(i32* readnone %r)
 | 
					; CHECK: define i32* @c(i32* readnone returned %r)
 | 
				
			||||||
@g = global i32 0
 | 
					@g = global i32 0
 | 
				
			||||||
define i32* @c(i32 *%r) {
 | 
					define i32* @c(i32 *%r) {
 | 
				
			||||||
	%a = icmp eq i32* %r, null
 | 
						%a = icmp eq i32* %r, null
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
; RUN: opt < %s -functionattrs -S | FileCheck %s
 | 
					; RUN: opt < %s -functionattrs -S | FileCheck %s
 | 
				
			||||||
@g = global i32* null		; <i32**> [#uses=1]
 | 
					@g = global i32* null		; <i32**> [#uses=1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; CHECK: define i32* @c1(i32* readnone %q)
 | 
					; CHECK: define i32* @c1(i32* readnone returned %q)
 | 
				
			||||||
define i32* @c1(i32* %q) {
 | 
					define i32* @c1(i32* %q) {
 | 
				
			||||||
	ret i32* %q
 | 
						ret i32* %q
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -140,7 +140,7 @@ define void @test1_1(i8* %x1_1, i8* %y1_1) {
 | 
				
			||||||
  ret void
 | 
					  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) {
 | 
					define i8* @test1_2(i8* %x1_2, i8* %y1_2) {
 | 
				
			||||||
  call void @test1_1(i8* %x1_2, i8* %y1_2)
 | 
					  call void @test1_1(i8* %x1_2, i8* %y1_2)
 | 
				
			||||||
  store i32* null, i32** @g
 | 
					  store i32* null, i32** @g
 | 
				
			||||||
| 
						 | 
					@ -168,7 +168,7 @@ define void @test4_1(i8* %x4_1) {
 | 
				
			||||||
  ret void
 | 
					  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) {
 | 
					define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2) {
 | 
				
			||||||
  call void @test4_1(i8* null)
 | 
					  call void @test4_1(i8* null)
 | 
				
			||||||
  store i32* null, i32** @g
 | 
					  store i32* null, i32** @g
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ define void @test1_2(i8* %x1_2, i8* %y1_2, i8* %z1_2) {
 | 
				
			||||||
  ret void
 | 
					  ret void
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
; CHECK: define i8* @test2(i8* readnone %p)
 | 
					; CHECK: define i8* @test2(i8* readnone returned %p)
 | 
				
			||||||
define i8* @test2(i8* %p) {
 | 
					define i8* @test2(i8* %p) {
 | 
				
			||||||
  store i32 0, i32* @x
 | 
					  store i32 0, i32* @x
 | 
				
			||||||
  ret i8* %p
 | 
					  ret i8* %p
 | 
				
			||||||
| 
						 | 
					@ -53,7 +53,7 @@ define void @test7_1(i32* inalloca %a) {
 | 
				
			||||||
  ret void
 | 
					  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) {
 | 
					define i32* @test8_1(i32* %p) {
 | 
				
			||||||
entry:
 | 
					entry:
 | 
				
			||||||
  ret i32* %p
 | 
					  ret i32* %p
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue