Index: llvm/lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -76,6 +76,7 @@ STATISTIC(NumReturned, "Number of arguments marked returned"); STATISTIC(NumReadNoneArg, "Number of arguments marked readnone"); STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly"); +STATISTIC(NumWriteOnlyArg, "Number of arguments marked writeonly"); STATISTIC(NumNoAlias, "Number of function returns marked noalias"); STATISTIC(NumNonNullReturn, "Number of function returns marked nonnull"); STATISTIC(NumNoRecurse, "Number of functions marked as norecurse"); @@ -649,8 +650,8 @@ /// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone. static Attribute::AttrKind -determinePointerReadAttrs(Argument *A, - const SmallPtrSet &SCCNodes) { +determinePointerAccessAttrs(Argument *A, + const SmallPtrSet &SCCNodes) { SmallVector Worklist; SmallPtrSet Visited; @@ -659,7 +660,7 @@ return Attribute::None; bool IsRead = false; - // We don't need to track IsWritten. If A is written to, return immediately. + bool IsWrite = false; for (Use &U : A->uses()) { Visited.insert(&U); @@ -667,6 +668,10 @@ } while (!Worklist.empty()) { + if (IsWrite && IsRead) + // No point in searching further.. + return Attribute::None; + Use *U = Worklist.pop_back_val(); Instruction *I = cast(U->getUser()); @@ -763,6 +768,15 @@ IsRead = true; break; + case Instruction::Store: + // A volatile store has side effects beyond what writeonly can be relied + // upon. + if (cast(I)->isVolatile()) + return Attribute::None; + + IsWrite = true; + break; + case Instruction::ICmp: case Instruction::Ret: break; @@ -772,7 +786,14 @@ } } - return IsRead ? Attribute::ReadOnly : Attribute::ReadNone; + if (IsWrite && IsRead) + return Attribute::None; + else if (IsRead) + return Attribute::ReadOnly; + else if (IsWrite) + return Attribute::WriteOnly; + else + return Attribute::ReadNone; } /// Deduce returned attributes for the SCC. @@ -865,9 +886,10 @@ return Changed; } -static bool addReadAttr(Argument *A, Attribute::AttrKind R) { - assert((R == Attribute::ReadOnly || R == Attribute::ReadNone) - && "Must be a Read attribute."); +static bool addAccessAttr(Argument *A, Attribute::AttrKind R) { + assert((R == Attribute::ReadOnly || R == Attribute::ReadNone || + R == Attribute::WriteOnly) + && "Must be an access attribute."); assert(A && "Argument must not be null."); // If the argument already has the attribute, nothing needs to be done. @@ -880,7 +902,12 @@ A->removeAttr(Attribute::ReadOnly); A->removeAttr(Attribute::ReadNone); A->addAttr(R); - R == Attribute::ReadOnly ? ++NumReadOnlyArg : ++NumReadNoneArg; + if (R == Attribute::ReadOnly) + ++NumReadOnlyArg; + else if (R == Attribute::WriteOnly) + ++NumWriteOnlyArg; + else + ++NumReadNoneArg; return true; } @@ -945,15 +972,15 @@ // Otherwise, it's captured. Don't bother doing SCC analysis on it. } if (!HasNonLocalUses && !A->onlyReadsMemory()) { - // Can we determine that it's readonly/readnone without doing an SCC? - // Note that we don't allow any calls at all here, or else our result - // will be dependent on the iteration order through the functions in the - // SCC. + // Can we determine that it's readonly/readnone/writeonly without doing + // an SCC? Note that we don't allow any calls at all here, or else our + // result will be dependent on the iteration order through the + // functions in the SCC. SmallPtrSet Self; Self.insert(&*A); - Attribute::AttrKind R = determinePointerReadAttrs(&*A, Self); + Attribute::AttrKind R = determinePointerAccessAttrs(&*A, Self); if (R != Attribute::None) - if (addReadAttr(A, R)) + if (addAccessAttr(A, R)) Changed.insert(F); } } @@ -1023,10 +1050,10 @@ Changed.insert(A->getParent()); } - // We also want to compute readonly/readnone. With a small number of false - // negatives, we can assume that any pointer which is captured isn't going - // to be provably readonly or readnone, since by definition we can't - // analyze all uses of a captured pointer. + // We also want to compute readonly/readnone/writeonly. With a small number + // of false negatives, we can assume that any pointer which is captured + // isn't going to be provably readonly or readnone, since by definition + // we can't analyze all uses of a captured pointer. // // The false negatives happen when the pointer is captured by a function // that promises readonly/readnone behaviour on the pointer, then the @@ -1034,24 +1061,28 @@ // Also, a readonly/readnone pointer may be returned, but returning a // pointer is capturing it. - Attribute::AttrKind ReadAttr = Attribute::ReadNone; - for (unsigned i = 0, e = ArgumentSCC.size(); i != e; ++i) { + auto meetAccessAttr = [](Attribute::AttrKind A, Attribute::AttrKind B) { + if (A == B) + return A; + if (A == Attribute::ReadNone) + return B; + if (B == Attribute::ReadNone) + return A; + return Attribute::None; + }; + + Attribute::AttrKind AccessAttr = Attribute::ReadNone; + for (unsigned i = 0, e = ArgumentSCC.size(); + i != e && AccessAttr != Attribute::None; ++i) { Argument *A = ArgumentSCC[i]->Definition; - Attribute::AttrKind K = determinePointerReadAttrs(A, ArgumentSCCNodes); - if (K == Attribute::ReadNone) - continue; - if (K == Attribute::ReadOnly) { - ReadAttr = Attribute::ReadOnly; - continue; - } - ReadAttr = K; - break; + Attribute::AttrKind K = determinePointerAccessAttrs(A, ArgumentSCCNodes); + AccessAttr = meetAccessAttr(AccessAttr, K); } - if (ReadAttr != Attribute::None) { + if (AccessAttr != Attribute::None) { for (unsigned i = 0, e = ArgumentSCC.size(); i != e; ++i) { Argument *A = ArgumentSCC[i]->Definition; - if (addReadAttr(A, ReadAttr)) + if (addAccessAttr(A, AccessAttr)) Changed.insert(A->getParent()); } } Index: llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -15,7 +15,7 @@ ret void } -; CHECK: define void @test0_no(i32* nocapture %p) #1 { +; CHECK: define void @test0_no(i32* nocapture writeonly %p) #1 { define void @test0_no(i32* %p) nounwind { store i32 0, i32* %p, !tbaa !2 ret void Index: llvm/test/Feature/OperandBundles/pr26510.ll =================================================================== --- llvm/test/Feature/OperandBundles/pr26510.ll +++ llvm/test/Feature/OperandBundles/pr26510.ll @@ -10,7 +10,7 @@ declare void @foo() readnone -; CHECK-LABEL: define i8* @test(i8* %p) +; CHECK-LABEL: define i8* @test(i8* writeonly %p) ; CHECK: %a = alloca i8*, align 8 ; CHECK: store i8* %p, i8** %a, align 8 ; CHECK: call void @foo() [ "abc"(i8** %a) ] Index: llvm/test/Transforms/Coroutines/coro-async.ll =================================================================== --- llvm/test/Transforms/Coroutines/coro-async.ll +++ llvm/test/Transforms/Coroutines/coro-async.ll @@ -256,7 +256,7 @@ unreachable } -; CHECK-LABEL: define swiftcc void @my_async_function2(%async.task* %task, %async.actor* %actor, i8* %async.ctxt) +; CHECK-LABEL: define swiftcc void @my_async_function2(%async.task* %task, %async.actor* %actor, i8* writeonly %async.ctxt) ; CHECK-SAME: #[[FRAMEPOINTER:[0-9]+]] ; CHECK-SAME: !dbg ![[SP3:[0-9]+]] ; CHECK: store i8* %async.ctxt, @@ -269,7 +269,7 @@ ; CHECK: tail call swiftcc void @asyncSuspend(i8* [[CALLEE_CTXT]], %async.task* %task, %async.actor* %actor) ; CHECK: ret void -; CHECK-LABEL: define internal swiftcc void @my_async_function2.resume.0(i8* %0, i8* nocapture readnone %1, i8* nocapture readonly %2) +; CHECK-LABEL: define internal swiftcc void @my_async_function2.resume.0(i8* writeonly %0, i8* nocapture readnone %1, i8* nocapture readonly %2) ; CHECK-SAME: #[[FRAMEPOINTER]] ; CHECK-SAME: !dbg ![[SP4:[0-9]+]] ; CHECK: [[CALLEE_CTXT_ADDR:%.*]] = bitcast i8* %2 to i8** Index: llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll +++ llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll @@ -7,7 +7,7 @@ ret i32* %tmp } -; CHECK: define i32* @b(i32* %q) +; CHECK: define i32* @b(i32* writeonly %q) define i32* @b(i32 *%q) { %mem = alloca i32* store i32* %q, i32** %mem Index: llvm/test/Transforms/FunctionAttrs/nocapture.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -8,7 +8,7 @@ ret i32* %q } -; FNATTR: define void @c2(i32* %q) +; FNATTR: define void @c2(i32* writeonly %q) ; It would also be acceptable to mark %q as readnone. Update @c3 too. define void @c2(i32* %q) { store i32* %q, i32** @g @@ -259,7 +259,7 @@ ret void } -; FNATTR: @nocaptureStrip(i8* nocapture %p) +; FNATTR: @nocaptureStrip(i8* nocapture writeonly %p) define void @nocaptureStrip(i8* %p) { entry: %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) @@ -268,7 +268,7 @@ } @g3 = global i8* null -; FNATTR: define void @captureStrip(i8* %p) +; FNATTR: define void @captureStrip(i8* writeonly %p) define void @captureStrip(i8* %p) { %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p) store i8* %b, i8** @g3 Index: llvm/test/Transforms/FunctionAttrs/readattrs.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/readattrs.ll +++ llvm/test/Transforms/FunctionAttrs/readattrs.ll @@ -34,7 +34,7 @@ ret void } -; CHECK: define void @test5(i8** nocapture %p, i8* %q) +; CHECK: define void @test5(i8** nocapture writeonly %p, i8* writeonly %q) ; Missed optz'n: we could make %q readnone, but don't break test6! define void @test5(i8** %p, i8* %q) { store i8* %q, i8** %p @@ -42,7 +42,7 @@ } declare void @test6_1() -; CHECK: define void @test6_2(i8** nocapture %p, i8* %q) +; CHECK: define void @test6_2(i8** nocapture writeonly %p, i8* writeonly %q) ; This is not a missed optz'n. define void @test6_2(i8** %p, i8* %q) { store i8* %q, i8** %p @@ -68,7 +68,7 @@ ret i32* %p } -; CHECK: define void @test8_2(i32* %p) +; CHECK: define void @test8_2(i32* writeonly %p) define void @test8_2(i32* %p) { entry: %call = call i32* @test8_1(i32* %p) Index: llvm/test/Transforms/FunctionAttrs/writeonly.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -25,13 +25,13 @@ ret void } -; CHECK: define void @test_store(i8* nocapture %p) +; CHECK: define void @test_store(i8* nocapture writeonly %p) define void @test_store(i8* %p) { store i8 0, i8* %p ret void } -; CHECK: define void @test_addressing(i8* nocapture %p) +; CHECK: define void @test_addressing(i8* nocapture writeonly %p) define void @test_addressing(i8* %p) { %gep = getelementptr i8, i8* %p, i64 8 %bitcast = bitcast i8* %gep to i32*