Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -276,6 +276,14 @@ /// pointer, return 'len+1'. If we can't, return 0. uint64_t GetStringLength(const Value *V, unsigned CharSize = 8); + /// This function returns call pointer argument that is considered the same by + /// aliasing rules. You CAN'T use it to replace one value with another. + const Value *getArgumentAliasingToReturnedPointer(ImmutableCallSite CS); + inline Value *getArgumentAliasingToReturnedPointer(CallSite CS) { + return const_cast( + getArgumentAliasingToReturnedPointer(ImmutableCallSite(CS))); + } + /// This method strips off any GEP address adjustments and pointer casts from /// the specified value, returning the original object being addressed. Note /// that the returned value has pointer type if the specified value does. If Index: llvm/lib/Analysis/BasicAliasAnalysis.cpp =================================================================== --- llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -132,16 +132,8 @@ /// Returns true if the pointer is one which would have been considered an /// escape by isNonEscapingLocalObject. static bool isEscapeSource(const Value *V) { - if (auto CS = ImmutableCallSite(V)) { - // launder_invariant_group captures its argument only by returning it, - // so it might not be considered an escape by isNonEscapingLocalObject. - // Note that adding similar special cases for intrinsics in CaptureTracking - // requires handling them here too. - if (CS.getIntrinsicID() == Intrinsic::launder_invariant_group) - return false; - + if (ImmutableCallSite(V)) return true; - } if (isa(V)) return true; @@ -438,11 +430,14 @@ const GEPOperator *GEPOp = dyn_cast(Op); if (!GEPOp) { - if (auto CS = ImmutableCallSite(V)) - if (const Value *RV = CS.getReturnedArgOperand()) { - V = RV; + if (auto CS = ImmutableCallSite(V)) { + // Note: getArgumentAliasingToReturnedPointer keeps CaptureTracking in + // sync, which is needed for correctness. + if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) { + V = RP; continue; } + } // If it's not a GEP, hand it off to SimplifyInstruction to see if it // can come up with something. This matches what GetUnderlyingObject does. Index: llvm/lib/Analysis/CaptureTracking.cpp =================================================================== --- llvm/lib/Analysis/CaptureTracking.cpp +++ llvm/lib/Analysis/CaptureTracking.cpp @@ -249,8 +249,12 @@ // launder.invariant.group only captures pointer by returning it, // so the pointer wasn't captured if returned pointer is not captured. - // Note that adding similar special cases for intrinsics requires handling - // them in 'isEscapeSource' in BasicAA. + // This intrinsic is not marked as nocapture, because it would require + // to mark the argument as returned, which would make the launder useless. + // NOTE: CaptureTracking users should not assume that only functions + // marked with nocapture do not capture. This means that places like + // GetUnderlyingObject in ValueTracking or DecomposeGEPExpression + // in BasicAA also assume this aliasing property of the launder. if (CS.getIntrinsicID() == Intrinsic::launder_invariant_group) { AddUses(I); break; Index: llvm/lib/Analysis/Loads.cpp =================================================================== --- llvm/lib/Analysis/Loads.cpp +++ llvm/lib/Analysis/Loads.cpp @@ -107,14 +107,11 @@ return isDereferenceableAndAlignedPointer(ASC->getOperand(0), Align, Size, DL, CtxI, DT, Visited); - if (auto CS = ImmutableCallSite(V)) { - if (const Value *RV = CS.getReturnedArgOperand()) - return isDereferenceableAndAlignedPointer(RV, Align, Size, DL, CtxI, DT, + if (auto CS = ImmutableCallSite(V)) + if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) + return isDereferenceableAndAlignedPointer(RP, Align, Size, DL, CtxI, DT, Visited); - if (CS.getIntrinsicID() == Intrinsic::launder_invariant_group) - return isDereferenceableAndAlignedPointer(CS->getOperand(0), Align, Size, - DL, CtxI, DT, Visited); - } + // If we don't know, assume the worst. return false; } Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -1956,8 +1956,8 @@ if (auto CS = ImmutableCallSite(V)) { if (CS.isReturnNonNull()) return true; - if (CS.getIntrinsicID() == Intrinsic::ID::launder_invariant_group) - return isKnownNonZero(CS->getOperand(0), Depth + 1, Q); + if (const auto *RP = getArgumentAliasingToReturnedPointer(CS)) + return isKnownNonZero(RP, Depth + 1, Q); } } @@ -3384,6 +3384,17 @@ return Len == ~0ULL ? 1 : Len; } +const Value *llvm::getArgumentAliasingToReturnedPointer(ImmutableCallSite CS) { + assert(CS && + "getArgumentAliasingToReturnedPointer only works on nonnull CallSite"); + if (const Value *RV = CS.getReturnedArgOperand()) + return RV; + // This can be used only as a aliasing property. + if (CS.getIntrinsicID() == Intrinsic::launder_invariant_group) + return CS.getArgOperand(0); + return nullptr; +} + /// \p PN defines a loop-variant pointer to an object. Check if the /// previous iteration of the loop was referring to the same object as \p PN. static bool isSameUnderlyingObjectInLoop(const PHINode *PN, @@ -3429,11 +3440,14 @@ // An alloca can't be further simplified. return V; } else { - if (auto CS = CallSite(V)) - if (Value *RV = CS.getReturnedArgOperand()) { - V = RV; + if (auto CS = CallSite(V)) { + // Note: getArgumentAliasingToReturnedPointer keeps CaptureTracking in + // sync, which is needed for correctness. + if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) { + V = RP; continue; } + } // See if InstructionSimplify knows any relevant tricks. if (Instruction *I = dyn_cast(V)) Index: llvm/test/Analysis/ValueTracking/known-nonnull-at.ll =================================================================== --- llvm/test/Analysis/ValueTracking/known-nonnull-at.ll +++ llvm/test/Analysis/ValueTracking/known-nonnull-at.ll @@ -98,3 +98,24 @@ unreachable } +declare i8* @returningPtr(i8* returned %p) + +define i1 @nonnullReturnTest(i8* nonnull %x) { +; CHECK-LABEL: @nonnullReturnTest( +; CHECK-NEXT: %x2 = call i8* @returningPtr(i8* %x) +; CHECK-NEXT: ret i1 false + %x2 = call i8* @returningPtr(i8* %x) + %null_check = icmp eq i8* %x2, null + ret i1 %null_check +} + +define i1 @unknownReturnTest(i8* %x) { +; CHECK-LABEL: @unknownReturnTest( +; CHECK-NEXT: %x2 = call i8* @returningPtr(i8* %x) +; CHECK-NEXT: %null_check = icmp eq i8* %x2, null +; CHECK-NEXT: ret i1 %null_check + %x2 = call i8* @returningPtr(i8* %x) + %null_check = icmp eq i8* %x2, null + ret i1 %null_check +} + Index: llvm/test/Transforms/Inline/launder.invariant.group.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Inline/launder.invariant.group.ll @@ -0,0 +1,31 @@ +; RUN: opt -S -inline < %s | FileCheck %s +; RUN: opt -S -O3 < %s | FileCheck %s + +; This test checks if value returned from the launder is considered aliasing +; with its argument. Due to bug caused by handling launder in capture tracking +; sometimes it would be considered noalias. + +%struct.A = type <{ i32 (...)**, i32, [4 x i8] }> + +; CHECK: define i32 @bar(%struct.A* noalias +define i32 @bar(%struct.A* noalias) { +; CHECK-NOT: noalias + %2 = bitcast %struct.A* %0 to i8* + %3 = call i8* @llvm.launder.invariant.group.p0i8(i8* %2) + %4 = getelementptr inbounds i8, i8* %3, i64 8 + %5 = bitcast i8* %4 to i32* + store i32 42, i32* %5, align 8 + %6 = getelementptr inbounds %struct.A, %struct.A* %0, i64 0, i32 1 + %7 = load i32, i32* %6, align 8 + ret i32 %7 +} + +; CHECK-LABEL: define i32 @foo(%struct.A* noalias +define i32 @foo(%struct.A* noalias) { + ; CHECK-NOT: call i32 @bar( + ; CHECK-NOT: noalias + %2 = tail call i32 @bar(%struct.A* %0) + ret i32 %2 +} + +declare i8* @llvm.launder.invariant.group.p0i8(i8*)