diff --git a/llvm/lib/Analysis/CaptureTracking.cpp b/llvm/lib/Analysis/CaptureTracking.cpp --- a/llvm/lib/Analysis/CaptureTracking.cpp +++ b/llvm/lib/Analysis/CaptureTracking.cpp @@ -27,6 +27,7 @@ #include "llvm/IR/Dominators.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Operator.h" #include "llvm/Support/CommandLine.h" using namespace llvm; @@ -73,6 +74,47 @@ return O->getPointerDereferenceableBytes(DL, CanBeNull, CanBeFreed); } +/// This method is used to find the underlying object that cannot leak bits. We +/// cannot use getUnderlyingObject() because it can look through intrinsics like +/// ptrmask, where ptrmask(P, M) == P leaks bits. +static const Value *getNonCapturingUnderlyingObject(const Value *V) { + if (!V->getType()->isPointerTy()) + return V; + SmallDenseMap Visited; + + std::function Visit = [&](const Value *V) { + const Value *NewV = V; + if (Visited.contains(V)) + return Visited[V]; + if (auto *GEP = dyn_cast(V)) + NewV = Visit(GEP->getPointerOperand()); + if (auto *PN = dyn_cast(V)) { + // We can look through PHIs if each underlying value has the same + // underlying object, or is the phi itself. + const Value *NewPhiV = PN; + Visited[V] = PN; + for (const Value *Incoming : PN->incoming_values()) { + const Value *IncomingUnderlying = + Visit(Incoming); + if (IncomingUnderlying == PN || IncomingUnderlying == NewPhiV) + continue; + if (NewPhiV == PN) + // Found a new possible underlying object. + NewPhiV = IncomingUnderlying; + else + // There are >= 2 possible underlying objects. We cannot determine a + // new underlying object. + break; + } + NewV = NewPhiV; + }; + Visited[V] = NewV; + return NewV; + }; + + return Visit(V); +} + namespace { struct SimpleCaptureTracker : public CaptureTracker { explicit SimpleCaptureTracker( @@ -411,7 +453,11 @@ if (IsDereferenceableOrNull && IsDereferenceableOrNull(O, DL)) return UseCaptureKind::NO_CAPTURE; } - } + } else if (cast(I)->isEquality() && + getNonCapturingUnderlyingObject(I->getOperand(Idx)) == + getNonCapturingUnderlyingObject(I->getOperand(OtherIdx))) + // Equality comparisons against the same object to do not capture. + return UseCaptureKind::NO_CAPTURE; // Otherwise, be conservative. There are crazy ways to capture pointers // using comparisons. diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll @@ -333,13 +333,13 @@ ret void } -; FNATTR: define i1 @identity_icmp(ptr readnone %p) +; FNATTR: define i1 @identity_icmp(ptr nocapture readnone %p) define i1 @identity_icmp(ptr %p) { %r = icmp eq ptr %p, %p ret i1 %r } -; FNATTR: define i1 @compare_against_offset(ptr readnone %p) +; FNATTR: define i1 @compare_against_offset(ptr nocapture readnone %p) define i1 @compare_against_offset(ptr %p) { %offset = getelementptr inbounds i32, ptr %p, i64 1 %r = icmp eq ptr %p, %offset @@ -353,7 +353,7 @@ ret i1 %r } -; FNATTR: define i1 @compare_offsets(ptr readnone %p) +; FNATTR: define i1 @compare_offsets(ptr nocapture readnone %p) define i1 @compare_offsets(ptr %p) { %offset1 = getelementptr inbounds i32, ptr %p, i64 1 %offset2 = getelementptr inbounds i32, ptr %p, i64 2 @@ -361,7 +361,7 @@ ret i1 %r } -; FNATTR: define void @phi_induction(ptr writeonly %p, i64 %n, i32 %x) +; FNATTR: define void @phi_induction(ptr nocapture writeonly %p, i64 %n, i32 %x) define void @phi_induction(ptr %p, i64 %n, i32 %x) { start: %end = getelementptr inbounds i32, ptr %p, i64 %n diff --git a/llvm/unittests/Analysis/CaptureTrackingTest.cpp b/llvm/unittests/Analysis/CaptureTrackingTest.cpp --- a/llvm/unittests/Analysis/CaptureTrackingTest.cpp +++ b/llvm/unittests/Analysis/CaptureTrackingTest.cpp @@ -105,11 +105,10 @@ BasicBlock *BB = &F->getEntryBlock(); Instruction *Call = &*BB->begin(); Instruction *CmpXChg = Call->getNextNode(); - Instruction *ICmp = CmpXChg->getNextNode(); CollectingCaptureTracker CT; PointerMayBeCaptured(Arg, &CT); - EXPECT_EQ(7u, CT.Captures.size()); + EXPECT_EQ(5u, CT.Captures.size()); // Call arg 1 EXPECT_EQ(Call, CT.Captures[0]->getUser()); EXPECT_EQ(0u, CT.Captures[0]->getOperandNo()); @@ -125,10 +124,4 @@ // Cmpxchg new value operand EXPECT_EQ(CmpXChg, CT.Captures[4]->getUser()); EXPECT_EQ(2u, CT.Captures[4]->getOperandNo()); - // ICmp first operand - EXPECT_EQ(ICmp, CT.Captures[5]->getUser()); - EXPECT_EQ(0u, CT.Captures[5]->getOperandNo()); - // ICmp second operand - EXPECT_EQ(ICmp, CT.Captures[6]->getUser()); - EXPECT_EQ(1u, CT.Captures[6]->getOperandNo()); }