Index: llvm/lib/IR/Value.cpp =================================================================== --- llvm/lib/IR/Value.cpp +++ llvm/lib/IR/Value.cpp @@ -728,6 +728,84 @@ return stripPointerCastsAndOffsets(this, Func); } +// Return true if the memory object referred to by V can by freed in the scope +// for which the SSA value defining the allocation is statically defined. E.g. +// deallocation after the static scope of a value does not count. +static bool canBeFreed(const Value *V) { + assert(V->getType()->isPointerTy()); + + // Cases that can simply never be deallocated + // *) Global variables are never deallocated. + // *) Constants aren't allocated per se, thus not deallocated either. + // *) An alloca is deallocated when the function returns + if (isa(V) || isa(V) || isa(V)) + return false; + + if (auto *A = dyn_cast(V)) { + // An argument to a function which neither frees, nor can arrange + // for another thread to free on it's behalf, can not be freed in it's + // defined scope. + // TODO: Generalize this case for noalias call results + const Function *F = A->getParent(); + if (F->doesNotFreeMemory() && F->hasNoSync()) + return false; + // If there's another copy of V which is freed, then the noalias fact + // must not have held (i.e. UB). Note that unlike the function attribute + // equivalent, nofree on an argument implies another thread can't free it + // on F's behalf either. + if (A->hasNoFreeAttr() && A->hasNoAliasAttr()) + return false; + + // Freeing requires writing to the object. If there were another written + // copy of the pointer, then the noalias fact would not have held. We do + // need to know that no other thread is freeing the object on this + // functions behalf though. + // TODO: There's an additional case we can add where the function only + // accesses argument memory, and all pointer arguments are nocapture. + // Might be better in attribute inference though as it basically implies + // NoSync on the function. + if (A->onlyReadsMemory() && A->hasNoAliasAttr() && F->hasNoSync()) + return false; + } + + // With garbage collection, deallocation typically occurs solely at or after + // safepoints. If we're compiling for a collector which uses the + // gc.statepoint infrastructure, safepoints aren't explicitly present + // in the IR until after lowering from abstract to physical machine model. + // The collector could chose to mix explicit deallocation and gc'd objects + // which is why we need the explicit opt in on a per collector basis. + const Function *F = nullptr; + if (auto *I = dyn_cast(V)) + F = I->getFunction(); + if (auto *A = dyn_cast(V)) + F = A->getParent(); + + if (!F || !F->hasGC()) + return true; + + const auto &GCName = F->getGC(); + const StringRef StatepointExampleName("statepoint-example"); + if (GCName != StatepointExampleName) + return true; + + auto *PT = cast(V->getType()); + if (PT->getAddressSpace() != 1) + // For the sake of this example GC, we arbitrarily pick addrspace(1) as our + // GC managed heap. This must match the same check in + // RewriteStatepointsForGC (and probably needs better factored.) + return true; + + // It's cheaper to scan for a declaration than to scan for a use in this + // function. Note that gc.statepoint is a type overloaded function so the + // usual trick of requesting declaration of the intrinsic from the module + // doesn't work. + for (auto &Fn : *F->getParent()) + if (Fn.getIntrinsicID() == Intrinsic::experimental_gc_statepoint) + return true; + return false; +} + + uint64_t Value::getPointerDereferenceableBytes(const DataLayout &DL, bool &CanBeNull, bool &CanBeFreed) const { @@ -735,7 +813,7 @@ uint64_t DerefBytes = 0; CanBeNull = false; - CanBeFreed = UseDerefAtPointSemantics; + CanBeFreed = UseDerefAtPointSemantics && canBeFreed(this); if (const Argument *A = dyn_cast(this)) { DerefBytes = A->getDereferenceableBytes(); if (DerefBytes == 0) { @@ -790,7 +868,6 @@ DerefBytes = DL.getTypeStoreSize(AI->getAllocatedType()).getKnownMinSize(); CanBeNull = false; - CanBeFreed = false; } } else if (auto *GV = dyn_cast(this)) { if (GV->getValueType()->isSized() && !GV->hasExternalWeakLinkage()) { @@ -798,7 +875,6 @@ // CanBeNull flag. DerefBytes = DL.getTypeStoreSize(GV->getValueType()).getFixedSize(); CanBeNull = false; - CanBeFreed = false; } } return DerefBytes; Index: llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll =================================================================== --- llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll +++ llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll @@ -7,13 +7,12 @@ ; conceptually live forever. But there may be non-managed objects which are ; freed. ; CHECK-LABEL: 'abstract_model' -; CHECK-NOT: %gc_ptr +; CHECK: %gc_ptr ; CHECK-NOT: %other_ptr ; FIXME: Can infer the gc pointer case define void @abstract_model(i32 addrspace(1)* dereferenceable(8) %gc_ptr, i32* dereferenceable(8) %other_ptr) gc "statepoint-example" { -; CHECK: The following are dereferenceable: entry: call void @mayfree() load i32, i32 addrspace(1)* %gc_ptr Index: llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll =================================================================== --- llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll +++ llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll @@ -245,16 +245,14 @@ ; CHECK-LABEL: 'negative' ; GLOBAL: %p ; POINT-NOT: %p -define void @negative(i32* dereferenceable(8) %p) nofree nosync { +define void @negative(i32* dereferenceable(8) %p) { call void @mayfree() %v = load i32, i32* %p ret void } ; CHECK-LABEL: 'infer_func_attrs1' -; GLOBAL: %p -; POINT-NOT: %p -; FIXME: Can be inferred from attributes +; CHECK: %p define void @infer_func_attrs1(i32* dereferenceable(8) %p) nofree nosync { call void @mayfree() %v = load i32, i32* %p @@ -272,9 +270,7 @@ } ; CHECK-LABEL: 'infer_noalias1' -; GLOBAL: %p -; POINT-NOT: %p -; FIXME: Can be inferred from attributes +; CHECK: %p define void @infer_noalias1(i32* dereferenceable(8) noalias nofree %p) { call void @mayfree() %v = load i32, i32* %p @@ -282,9 +278,7 @@ } ; CHECK-LABEL: 'infer_noalias2' -; GLOBAL: %p -; POINT-NOT: %p -; FIXME: Can be inferred from attributes +; CHECK: %p define void @infer_noalias2(i32* dereferenceable(8) noalias readonly %p) nosync { call void @mayfree() %v = load i32, i32* %p