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 @@ -569,6 +577,7 @@ Optional isImpliedCondition(const Value *LHS, const Value *RHS, const DataLayout &DL, bool LHSIsTrue = true, unsigned Depth = 0); + } // end namespace llvm #endif // LLVM_ANALYSIS_VALUETRACKING_H 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,12 @@ 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)) { + 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 does 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,6 +1956,7 @@ if (auto CS = ImmutableCallSite(V)) { if (CS.isReturnNonNull()) return true; + // FIXME: This could perhaps use getArgumentAliasingToReturnedPointer if (CS.getIntrinsicID() == Intrinsic::ID::launder_invariant_group) return isKnownNonZero(CS->getOperand(0), Depth + 1, Q); } @@ -3384,6 +3385,16 @@ return Len == ~0ULL ? 1 : Len; } +const Value *llvm::getArgumentAliasingToReturnedPointer(ImmutableCallSite CS) { + assert(CS); + 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,12 @@ // 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)) { + if (auto *RP = getArgumentAliasingToReturnedPointer(CS)) { + V = RP; continue; } + } // See if InstructionSimplify knows any relevant tricks. if (Instruction *I = dyn_cast(V)) 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*)