[llvm] 4054b83 - [deref] Implement initial set of inference rules for deref-at-point
Philip Reames via llvm-commits
llvm-commits at lists.llvm.org
Wed Mar 24 16:21:43 PDT 2021
Author: Philip Reames
Date: 2021-03-24T16:20:41-07:00
New Revision: 4054b8322fd8ef706bd486128a57bc8b709e2319
URL: https://github.com/llvm/llvm-project/commit/4054b8322fd8ef706bd486128a57bc8b709e2319
DIFF: https://github.com/llvm/llvm-project/commit/4054b8322fd8ef706bd486128a57bc8b709e2319.diff
LOG: [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
Added:
Modified:
llvm/lib/IR/Value.cpp
llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll
llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
Removed:
################################################################################
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index cfb91b55f707e..8c06d4fe22d9b 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -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;
diff --git a/llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll b/llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll
index bfa1f48797f26..70fd9eda5f8f2 100644
--- a/llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll
+++ b/llvm/test/Analysis/ValueTracking/deref-abstract-gc.ll
@@ -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
diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
index 86e6ce23d5863..1b66112db8bb6 100644
--- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
+++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
@@ -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
More information about the llvm-commits
mailing list