forked from OSchip/llvm-project
[deref] Implement initial set of inference rules for deref-at-point
This implements a subset of the initial set of inference rules proposed in the llvm-dev thread "RFC: Decomposing deref(N) into deref(N) + nofree". The nolias one got moved to a separate review as there was some concerns raised which require further discussion. Differential Revision: https://reviews.llvm.org/D99135
This commit is contained in:
parent
c8ef98e5de
commit
4054b8322f
|
@ -728,6 +728,64 @@ Value::stripInBoundsOffsets(function_ref<void(const Value *)> Func) const {
|
|||
return stripPointerCastsAndOffsets<PSK_InBounds>(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
|
||||
// *) Constants aren't allocated per se, thus not deallocated either.
|
||||
if (isa<Constant>(V))
|
||||
return false;
|
||||
|
||||
const Function *F = nullptr;
|
||||
if (auto *I = dyn_cast<Instruction>(V))
|
||||
F = I->getFunction();
|
||||
if (auto *A = dyn_cast<Argument>(V))
|
||||
F = A->getParent();
|
||||
|
||||
if (!F)
|
||||
return true;
|
||||
|
||||
// A pointer to an object in a function which neither frees, nor can arrange
|
||||
// for another thread to free on its behalf, can not be freed in the scope
|
||||
// of the function.
|
||||
if (F->doesNotFreeMemory() && 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.
|
||||
if (!F->hasGC())
|
||||
return true;
|
||||
|
||||
const auto &GCName = F->getGC();
|
||||
const StringRef StatepointExampleName("statepoint-example");
|
||||
if (GCName != StatepointExampleName)
|
||||
return true;
|
||||
|
||||
auto *PT = cast<PointerType>(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 is 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 +793,7 @@ uint64_t Value::getPointerDereferenceableBytes(const DataLayout &DL,
|
|||
|
||||
uint64_t DerefBytes = 0;
|
||||
CanBeNull = false;
|
||||
CanBeFreed = UseDerefAtPointSemantics;
|
||||
CanBeFreed = UseDerefAtPointSemantics && canBeFreed(this);
|
||||
if (const Argument *A = dyn_cast<Argument>(this)) {
|
||||
DerefBytes = A->getDereferenceableBytes();
|
||||
if (DerefBytes == 0) {
|
||||
|
@ -798,7 +856,6 @@ uint64_t Value::getPointerDereferenceableBytes(const DataLayout &DL,
|
|||
// CanBeNull flag.
|
||||
DerefBytes = DL.getTypeStoreSize(GV->getValueType()).getFixedSize();
|
||||
CanBeNull = false;
|
||||
CanBeFreed = false;
|
||||
}
|
||||
}
|
||||
return DerefBytes;
|
||||
|
|
|
@ -7,13 +7,12 @@ target datalayout = "e-i32:32:64"
|
|||
; 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
|
||||
|
|
|
@ -245,16 +245,14 @@ define i32 @f_0(i32 %val) {
|
|||
; 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
|
||||
|
|
Loading…
Reference in New Issue