diff --git a/llvm/include/llvm/IR/CallSite.h b/llvm/include/llvm/IR/CallSite.h index 1eb33a53571e..e08c7198a76d 100644 --- a/llvm/include/llvm/IR/CallSite.h +++ b/llvm/include/llvm/IR/CallSite.h @@ -583,6 +583,11 @@ public: dataOperandHasImpliedAttr(OpNo + 1, Attribute::ReadNone); } + bool doesNotReadMemory(unsigned OpNo) const { + return dataOperandHasImpliedAttr(OpNo + 1, Attribute::WriteOnly) || + dataOperandHasImpliedAttr(OpNo + 1, Attribute::ReadNone); + } + /// Return true if the return value is known to be not null. /// This may be because it has the nonnull attribute, or because at least /// one byte is dereferenceable and the pointer is in addrspace(0). diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp index 55f40a348399..d3898aad8a78 100644 --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -749,7 +749,11 @@ ModRefInfo BasicAAResult::getModRefInfo(ImmutableCallSite CS, // as an argument, and itself doesn't capture it. if (!isa(Object) && CS.getInstruction() != Object && isNonEscapingLocalObject(Object)) { - bool PassedAsArg = false; + + // Optimistically assume that call doesn't touch Object and check this + // assumption in the following loop. + ModRefInfo Result = MRI_NoModRef; + unsigned OperandNo = 0; for (auto CI = CS.data_operands_begin(), CE = CS.data_operands_end(); CI != CE; ++CI, ++OperandNo) { @@ -761,20 +765,38 @@ ModRefInfo BasicAAResult::getModRefInfo(ImmutableCallSite CS, OperandNo < CS.getNumArgOperands() && !CS.isByValArgument(OperandNo))) continue; + // Call doesn't access memory through this operand, so we don't care + // if it aliases with Object. + if (CS.doesNotAccessMemory(OperandNo)) + continue; + // If this is a no-capture pointer argument, see if we can tell that it - // is impossible to alias the pointer we're checking. If not, we have to - // assume that the call could touch the pointer, even though it doesn't - // escape. + // is impossible to alias the pointer we're checking. AliasResult AR = getBestAAResults().alias(MemoryLocation(*CI), MemoryLocation(Object)); - if (AR) { - PassedAsArg = true; - break; + + // Operand doesnt alias 'Object', continue looking for other aliases + if (AR == NoAlias) + continue; + // Operand aliases 'Object', but call doesn't modify it. Strengthen + // initial assumption and keep looking in case if there are more aliases. + if (CS.onlyReadsMemory(OperandNo)) { + Result = static_cast(Result | MRI_Ref); + continue; } + // Operand aliases 'Object' but call only writes into it. + if (CS.doesNotReadMemory(OperandNo)) { + Result = static_cast(Result | MRI_Mod); + continue; + } + // This operand aliases 'Object' and call reads and writes into it. + Result = MRI_ModRef; + break; } - if (!PassedAsArg) - return MRI_NoModRef; + // Early return if we improved mod ref information + if (Result != MRI_ModRef) + return Result; } // If the CallSite is to malloc or calloc, we can assume that it doesn't diff --git a/llvm/test/Analysis/BasicAA/call-attrs.ll b/llvm/test/Analysis/BasicAA/call-attrs.ll new file mode 100644 index 000000000000..9cd17e486799 --- /dev/null +++ b/llvm/test/Analysis/BasicAA/call-attrs.ll @@ -0,0 +1,42 @@ +; RUN: opt < %s -basicaa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s + +declare void @readonly_attr(i8* readonly nocapture) +declare void @writeonly_attr(i8* writeonly nocapture) +declare void @readnone_attr(i8* readnone nocapture) + +declare void @readonly_func(i8* nocapture) readonly +declare void @writeonly_func(i8* nocapture) writeonly +declare void @readnone_func(i8* nocapture) readnone + +declare void @read_write(i8* writeonly nocapture, i8* readonly nocapture, i8* readnone nocapture) + +declare void @func() + +define void @test(i8* noalias %p) { +entry: + call void @readonly_attr(i8* %p) + call void @readonly_func(i8* %p) + + call void @writeonly_attr(i8* %p) + call void @writeonly_func(i8* %p) + + call void @readnone_attr(i8* %p) + call void @readnone_func(i8* %p) + + call void @read_write(i8* %p, i8* %p, i8* %p) + + call void @func() ["deopt" (i8* %p)] + call void @writeonly_attr(i8* %p) ["deopt" (i8* %p)] + + ret void +} + +; CHECK: Just Ref: Ptr: i8* %p <-> call void @readonly_attr(i8* %p) +; CHECK: Just Ref: Ptr: i8* %p <-> call void @readonly_func(i8* %p) +; CHECK: Just Mod: Ptr: i8* %p <-> call void @writeonly_attr(i8* %p) +; CHECK: Just Mod: Ptr: i8* %p <-> call void @writeonly_func(i8* %p) +; CHECK: NoModRef: Ptr: i8* %p <-> call void @readnone_attr(i8* %p) +; CHECK: NoModRef: Ptr: i8* %p <-> call void @readnone_func(i8* %p) +; CHECK: Both ModRef: Ptr: i8* %p <-> call void @read_write(i8* %p, i8* %p, i8* %p) +; CHECK: Just Ref: Ptr: i8* %p <-> call void @func() [ "deopt"(i8* %p) ] +; CHECK: Both ModRef: Ptr: i8* %p <-> call void @writeonly_attr(i8* %p) [ "deopt"(i8* %p) ] diff --git a/llvm/test/Transforms/NewGVN/readattrs.ll b/llvm/test/Transforms/NewGVN/readattrs.ll index be5fbf5a806f..29ddb97ca1bb 100644 --- a/llvm/test/Transforms/NewGVN/readattrs.ll +++ b/llvm/test/Transforms/NewGVN/readattrs.ll @@ -1,4 +1,3 @@ -; XFAIL: * ; RUN: opt -newgvn -S -o - < %s | FileCheck %s target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"