Index: llvm/trunk/include/llvm/Transforms/IPO/FunctionAttrs.h =================================================================== --- llvm/trunk/include/llvm/Transforms/IPO/FunctionAttrs.h +++ llvm/trunk/include/llvm/Transforms/IPO/FunctionAttrs.h @@ -32,7 +32,8 @@ 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. Index: llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp +++ llvm/trunk/lib/Transforms/IPO/FunctionAttrs.cpp @@ -66,6 +66,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ // 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 @@ case MAK_ReadOnly: ReadsMemory = true; break; + case MAK_WriteOnly: + WritesMemory = true; + break; case MAK_ReadNone: // Nothing to do! break; @@ -243,6 +259,9 @@ // 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 @@ // 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; Index: llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ llvm/trunk/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -63,7 +63,7 @@ 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 @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 = !{ }