[FunctionAttrs] Infer WriteOnly Function Attribute

These changes expand the FunctionAttr logic in order to mark functions as
WriteOnly when appropriate. This is done through an additional bool variable
and extended logic.

Reviewers: hfinkel, jdoerfert

Differential Revision: https://reviews.llvm.org/D48387

llvm-svn: 340537
This commit is contained in:
Brian Homerding 2018-08-23 15:05:22 +00:00
parent 1715efd7ff
commit 3ecabd709f
3 changed files with 44 additions and 14 deletions

View File

@ -32,7 +32,8 @@ class Pass;
enum MemoryAccessKind {
MAK_ReadNone = 0,
MAK_ReadOnly = 1,
MAK_MayWrite = 2
MAK_MayWrite = 2,
MAK_WriteOnly = 3
};
/// Returns the memory access properties of this copy of the function.

View File

@ -66,6 +66,7 @@ using namespace llvm;
STATISTIC(NumReadNone, "Number of functions marked readnone");
STATISTIC(NumReadOnly, "Number of functions marked readonly");
STATISTIC(NumWriteOnly, "Number of functions marked writeonly");
STATISTIC(NumNoCapture, "Number of arguments marked nocapture");
STATISTIC(NumReturned, "Number of arguments marked returned");
STATISTIC(NumReadNoneArg, "Number of arguments marked readnone");
@ -113,12 +114,16 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody,
if (AliasAnalysis::onlyReadsMemory(MRB))
return MAK_ReadOnly;
// Conservatively assume it writes to memory.
if (AliasAnalysis::doesNotReadMemory(MRB))
return MAK_WriteOnly;
// Conservatively assume it reads and writes to memory.
return MAK_MayWrite;
}
// Scan the function body for instructions that may read or write memory.
bool ReadsMemory = false;
bool WritesMemory = false;
for (inst_iterator II = inst_begin(F), E = inst_end(F); II != E; ++II) {
Instruction *I = &*II;
@ -141,9 +146,9 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody,
continue;
if (!AliasAnalysis::onlyAccessesArgPointees(MRB)) {
// The call could access any memory. If that includes writes, give up.
// The call could access any memory. If that includes writes, note it.
if (isModSet(MRI))
return MAK_MayWrite;
WritesMemory = true;
// If it reads, note it.
if (isRefSet(MRI))
ReadsMemory = true;
@ -168,8 +173,8 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody,
continue;
if (isModSet(MRI))
// Writes non-local memory. Give up.
return MAK_MayWrite;
// Writes non-local memory.
WritesMemory = true;
if (isRefSet(MRI))
// Ok, it reads non-local memory.
ReadsMemory = true;
@ -198,14 +203,21 @@ static MemoryAccessKind checkFunctionMemoryAccess(Function &F, bool ThisBody,
// Any remaining instructions need to be taken seriously! Check if they
// read or write memory.
if (I->mayWriteToMemory())
// Writes memory. Just give up.
return MAK_MayWrite;
//
// Writes memory, remember that.
WritesMemory |= I->mayWriteToMemory();
// If this instruction may read memory, remember that.
ReadsMemory |= I->mayReadFromMemory();
}
if (WritesMemory) {
if (!ReadsMemory)
return MAK_WriteOnly;
else
return MAK_MayWrite;
}
return ReadsMemory ? MAK_ReadOnly : MAK_ReadNone;
}
@ -220,6 +232,7 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) {
// Check if any of the functions in the SCC read or write memory. If they
// write memory then they can't be marked readnone or readonly.
bool ReadsMemory = false;
bool WritesMemory = false;
for (Function *F : SCCNodes) {
// Call the callable parameter to look up AA results for this function.
AAResults &AAR = AARGetter(*F);
@ -234,6 +247,9 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) {
case MAK_ReadOnly:
ReadsMemory = true;
break;
case MAK_WriteOnly:
WritesMemory = true;
break;
case MAK_ReadNone:
// Nothing to do!
break;
@ -243,6 +259,9 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) {
// Success! Functions in this SCC do not access memory, or only read memory.
// Give them the appropriate attribute.
bool MadeChange = false;
assert(!(ReadsMemory && WritesMemory) &&
"Function marked read-only and write-only");
for (Function *F : SCCNodes) {
if (F->doesNotAccessMemory())
// Already perfect!
@ -252,16 +271,25 @@ static bool addReadAttrs(const SCCNodeSet &SCCNodes, AARGetterT &&AARGetter) {
// No change.
continue;
if (F->doesNotReadMemory() && WritesMemory)
continue;
MadeChange = true;
// Clear out any existing attributes.
F->removeFnAttr(Attribute::ReadOnly);
F->removeFnAttr(Attribute::ReadNone);
F->removeFnAttr(Attribute::WriteOnly);
// Add in the new attribute.
F->addFnAttr(ReadsMemory ? Attribute::ReadOnly : Attribute::ReadNone);
if (WritesMemory && !ReadsMemory)
F->addFnAttr(Attribute::WriteOnly);
else
F->addFnAttr(ReadsMemory ? Attribute::ReadOnly : Attribute::ReadNone);
if (ReadsMemory)
if (WritesMemory && !ReadsMemory)
++NumWriteOnly;
else if (ReadsMemory)
++NumReadOnly;
else
++NumReadNone;

View File

@ -63,7 +63,7 @@ define i32 @test3_yes(i8* %p) nounwind {
ret i32 %t
}
; CHECK: define i32 @test3_no(i8* nocapture %p) #1 {
; CHECK: define i32 @test3_no(i8* nocapture %p) #5 {
define i32 @test3_no(i8* %p) nounwind {
%t = va_arg i8* %p, i32, !tbaa !2
ret i32 %t
@ -73,11 +73,12 @@ declare void @callee(i32* %p) nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) nounwind
; CHECK: attributes #0 = { norecurse nounwind readnone }
; CHECK: attributes #1 = { norecurse nounwind }
; CHECK: attributes #1 = { norecurse nounwind writeonly }
; CHECK: attributes #2 = { nounwind readonly }
; CHECK: attributes #3 = { nounwind }
; CHECK: attributes #4 = { nounwind readnone }
; CHECK: attributes #5 = { argmemonly nounwind }
; CHECK: attributes #5 = { norecurse nounwind }
; CHECK: attributes #6 = { argmemonly nounwind }
; Root note.
!0 = !{ }