Index: lib/Analysis/CaptureTracking.cpp =================================================================== --- lib/Analysis/CaptureTracking.cpp +++ lib/Analysis/CaptureTracking.cpp @@ -331,14 +331,32 @@ AddUses(I); break; case Instruction::ICmp: { - // Don't count comparisons of a no-alias return value against null as - // captures. This allows us to ignore comparisons of malloc results - // with null, for example. - if (ConstantPointerNull *CPN = - dyn_cast(I->getOperand(1))) + if (auto *CPN = dyn_cast(I->getOperand(1))) { + // Don't count comparisons of a no-alias return value against null as + // captures. This allows us to ignore comparisons of malloc results + // with null, for example. if (CPN->getType()->getAddressSpace() == 0) if (isNoAliasCall(V->stripPointerCasts())) break; + auto *O = I->getOperand(0)->stripPointerCasts(); + if (!I->getFunction()->nullPointerIsDefined()) { + // An inbounds GEP can either be a valid pointer (pointing into + // or to the end of an allocation), or be null in the default + // address space. So for an inbounds GEPs there is no way to let + // the pointer escape using clever GEP hacking because doing so + // would make the pointer point outside of the allocated object + // and thus make the GEP result a poison value. + if (auto *GEP = dyn_cast(O)) + if (GEP->isInBounds()) + break; + // Comparing a dereferenceable_or_null argument against null + // cannot lead to pointer escapes, because if it is not null it + // must be a valid (in-bounds) pointer. + bool CanBeNull; + if (O->getPointerDereferenceableBytes(I->getModule()->getDataLayout(), CanBeNull)) + break; + } + } // Comparison against value stored in global variable. Given the pointer // does not escape, its value cannot be guessed and stored separately in a // global variable. Index: test/Transforms/FunctionAttrs/nocapture.ll =================================================================== --- test/Transforms/FunctionAttrs/nocapture.ll +++ test/Transforms/FunctionAttrs/nocapture.ll @@ -253,5 +253,33 @@ ret void } +; CHECK: define i1 @captureICmp(i32* readnone %x) +define i1 @captureICmp(i32* %x) { + %1 = icmp eq i32* %x, null + ret i1 %1 +} + +; CHECK: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x) +define i1 @nocaptureInboundsGEPICmp(i32* %x) { + %1 = getelementptr inbounds i32, i32* %x, i32 5 + %2 = bitcast i32* %1 to i8* + %3 = icmp eq i8* %2, null + ret i1 %3 +} + +; CHECK: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x) +define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) { + %1 = bitcast i32* %x to i8* + %2 = icmp eq i8* %1, null + ret i1 %2 +} + +; CHECK: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x) +define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" { + %1 = bitcast i32* %x to i8* + %2 = icmp eq i8* %1, null + ret i1 %2 +} + declare i8* @llvm.launder.invariant.group.p0i8(i8*) declare i8* @llvm.strip.invariant.group.p0i8(i8*)