-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[Loads] Respect UseDerefAtPointSemantics in isDerefAndAlignedPointer. #123196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Loads] Respect UseDerefAtPointSemantics in isDerefAndAlignedPointer. #123196
Conversation
@llvm/pr-subscribers-llvm-ir @llvm/pr-subscribers-llvm-analysis Author: Florian Hahn (fhahn) ChangesIf a pointer gets freed, it may not be dereferenceable any longer, even though there is a dominating dereferenceable assumption. As first step, only consider assumptions if the function doesn't free memory if UseDerefAtPointSemantics is used. Full diff: https://github.com/llvm/llvm-project/pull/123196.diff 3 Files Affected:
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 7bbd469bd035d3..4cc93a8daeb0f1 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -25,6 +25,8 @@
using namespace llvm;
+extern cl::opt<unsigned> UseDerefAtPointSemantics;
+
static bool isAligned(const Value *Base, Align Alignment,
const DataLayout &DL) {
return Base->getPointerAlignment(DL) >= Alignment;
@@ -168,7 +170,8 @@ static bool isDereferenceableAndAlignedPointer(
Size, DL, CtxI, AC, DT, TLI,
Visited, MaxDepth);
- if (CtxI) {
+ if (CtxI &&
+ (!UseDerefAtPointSemantics || CtxI->getFunction()->doesNotFreeMemory())) {
/// Look through assumes to see if both dereferencability and alignment can
/// be proven by an assume if needed.
RetainedKnowledge AlignRK;
diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp
index b2ee75811fbb7d..03961f34a18b8f 100644
--- a/llvm/lib/IR/Value.cpp
+++ b/llvm/lib/IR/Value.cpp
@@ -36,7 +36,7 @@
using namespace llvm;
-static cl::opt<unsigned> UseDerefAtPointSemantics(
+cl::opt<unsigned> UseDerefAtPointSemantics(
"use-dereferenceable-at-point-semantics", cl::Hidden, cl::init(false),
cl::desc("Deref attributes and metadata infer facts at definition only"));
diff --git a/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll b/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll
index 572511a5ffb926..7b41c9b3197aa6 100644
--- a/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll
+++ b/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll
@@ -1411,7 +1411,7 @@ exit:
}
; %a may be freeed between the dereferenceable assumption and accesses.
-; FIXME: It is not safe to use with -use-dereferenceable-at-point-semantics.
+; It is not safe to use with -use-dereferenceable-at-point-semantics.
define void @may_free_align_deref_assumption_in_header_constant_trip_count_loop_invariant_ptr(ptr noalias %a, ptr noalias %b, ptr noalias %c) {
; CHECK-LABEL: define void @may_free_align_deref_assumption_in_header_constant_trip_count_loop_invariant_ptr(
; CHECK-SAME: ptr noalias [[A:%.*]], ptr noalias [[B:%.*]], ptr noalias [[C:%.*]]) {
@@ -1422,16 +1422,29 @@ define void @may_free_align_deref_assumption_in_header_constant_trip_count_loop_
; CHECK: [[VECTOR_PH]]:
; CHECK-NEXT: br label %[[VECTOR_BODY:.*]]
; CHECK: [[VECTOR_BODY]]:
-; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[PRED_LOAD_CONTINUE2:.*]] ]
; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[B]], i64 [[TMP0]]
; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i32 0
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP2]], align 4
; CHECK-NEXT: [[TMP3:%.*]] = icmp sge <2 x i32> [[WIDE_LOAD]], zeroinitializer
-; CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[A]], align 4
+; CHECK-NEXT: [[TMP4:%.*]] = xor <2 x i1> [[TMP3]], splat (i1 true)
+; CHECK-NEXT: [[TMP5:%.*]] = extractelement <2 x i1> [[TMP4]], i32 0
+; CHECK-NEXT: br i1 [[TMP5]], label %[[PRED_LOAD_IF:.*]], label %[[PRED_LOAD_CONTINUE:.*]]
+; CHECK: [[PRED_LOAD_IF]]:
; CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[A]], align 4
-; CHECK-NEXT: [[TMP13:%.*]] = insertelement <2 x i32> poison, i32 [[TMP5]], i32 0
-; CHECK-NEXT: [[TMP11:%.*]] = insertelement <2 x i32> [[TMP13]], i32 [[TMP15]], i32 1
+; CHECK-NEXT: [[TMP7:%.*]] = insertelement <2 x i32> poison, i32 [[TMP15]], i32 0
+; CHECK-NEXT: br label %[[PRED_LOAD_CONTINUE]]
+; CHECK: [[PRED_LOAD_CONTINUE]]:
+; CHECK-NEXT: [[TMP12:%.*]] = phi <2 x i32> [ poison, %[[VECTOR_BODY]] ], [ [[TMP7]], %[[PRED_LOAD_IF]] ]
+; CHECK-NEXT: [[TMP13:%.*]] = extractelement <2 x i1> [[TMP4]], i32 1
+; CHECK-NEXT: br i1 [[TMP13]], label %[[PRED_LOAD_IF1:.*]], label %[[PRED_LOAD_CONTINUE2]]
+; CHECK: [[PRED_LOAD_IF1]]:
+; CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[A]], align 4
+; CHECK-NEXT: [[TMP16:%.*]] = insertelement <2 x i32> [[TMP12]], i32 [[TMP14]], i32 1
+; CHECK-NEXT: br label %[[PRED_LOAD_CONTINUE2]]
+; CHECK: [[PRED_LOAD_CONTINUE2]]:
+; CHECK-NEXT: [[TMP11:%.*]] = phi <2 x i32> [ [[TMP12]], %[[PRED_LOAD_CONTINUE]] ], [ [[TMP16]], %[[PRED_LOAD_IF1]] ]
; CHECK-NEXT: [[PREDPHI:%.*]] = select <2 x i1> [[TMP3]], <2 x i32> [[WIDE_LOAD]], <2 x i32> [[TMP11]]
; CHECK-NEXT: [[TMP8:%.*]] = getelementptr inbounds i32, ptr [[C]], i64 [[TMP0]]
; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds i32, ptr [[TMP8]], i32 0
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nofree
on functions describes external effects. The function can still allocate and free memory internally. Value::canBeFreed() has some logic that supports this for function arguments, where the distinction doesn't matter.
2eaae5d
to
4a46cd0
Compare
@@ -168,7 +170,7 @@ static bool isDereferenceableAndAlignedPointer( | |||
Size, DL, CtxI, AC, DT, TLI, | |||
Visited, MaxDepth); | |||
|
|||
if (CtxI) { | |||
if (CtxI && (!UseDerefAtPointSemantics || !V->canBeFreed())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this unconditional, regardless of the value of UseDerefAtPointSemantics? The way I'd view this is that UseDerefAtPointSemantics is for dereferenceable argument attributes. We can/should make assumptions always use at-point semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My plan was first making things correct with the flag, then change the behavior for the attribute in assumes + langref change separately.
I can also pull it in here, although I still need to inspect the other uses of dereferenceable assumptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, that's fin by me.
@@ -3,7 +3,7 @@ | |||
|
|||
declare void @llvm.assume(i1) | |||
|
|||
define void @deref_assumption_in_header_constant_trip_count(ptr noalias %a, ptr noalias %b, ptr noalias %c) nofree { | |||
define void @deref_assumption_in_header_constant_trip_count(ptr noalias %a, ptr noalias %b, ptr noalias %c) nofree nosync{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I consider this a bug in our nofree semantics, and a large part of the reason why this never made progress :)
Frontends often have knowledge that a certain pointer is not going to be freed in the function (including via synchronization), but they don't have knowledge about whether it may be involved in synchronization. The current semantics make it impossible to convey such facts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
If a pointer gets freed, it may not be dereferenceable any longer, even though there is a dominating dereferenceable assumption. As first step, only consider assumptions if the function doesn't free memory if UseDerefAtPointSemantics is used.
4a46cd0
to
1de8962
Compare
…nedPointer. (#123196) If a pointer gets freed, it may not be dereferenceable any longer, even though there is a dominating dereferenceable assumption. As first step, only consider assumptions if the pointer value cannot be freed if UseDerefAtPointSemantics is used. PR: llvm/llvm-project#123196
#126117) Update LangRef and code using `Dereferenceable` in assume bundles to only use the information if it is safe at the point of use. `Dereferenceable` in an assume bundle is only guaranteed at the point of the assumption, but may not be guaranteed at later points, because the pointer may have been freed. Update code using `Dereferenceable` to only use it if the pointer cannot be freed. This can further be refined to check if the pointer could be freed between assume and use. This follows up on #123196. With that change, it should be safe to expose dereferenceable assumptions more widely as in #121789 PR: #126117
…s at assume. (#126117) Update LangRef and code using `Dereferenceable` in assume bundles to only use the information if it is safe at the point of use. `Dereferenceable` in an assume bundle is only guaranteed at the point of the assumption, but may not be guaranteed at later points, because the pointer may have been freed. Update code using `Dereferenceable` to only use it if the pointer cannot be freed. This can further be refined to check if the pointer could be freed between assume and use. This follows up on llvm/llvm-project#123196. With that change, it should be safe to expose dereferenceable assumptions more widely as in llvm/llvm-project#121789 PR: llvm/llvm-project#126117
llvm#126117) Update LangRef and code using `Dereferenceable` in assume bundles to only use the information if it is safe at the point of use. `Dereferenceable` in an assume bundle is only guaranteed at the point of the assumption, but may not be guaranteed at later points, because the pointer may have been freed. Update code using `Dereferenceable` to only use it if the pointer cannot be freed. This can further be refined to check if the pointer could be freed between assume and use. This follows up on llvm#123196. With that change, it should be safe to expose dereferenceable assumptions more widely as in llvm#121789 PR: llvm#126117
llvm#126117) Update LangRef and code using `Dereferenceable` in assume bundles to only use the information if it is safe at the point of use. `Dereferenceable` in an assume bundle is only guaranteed at the point of the assumption, but may not be guaranteed at later points, because the pointer may have been freed. Update code using `Dereferenceable` to only use it if the pointer cannot be freed. This can further be refined to check if the pointer could be freed between assume and use. This follows up on llvm#123196. With that change, it should be safe to expose dereferenceable assumptions more widely as in llvm#121789 PR: llvm#126117
If a pointer gets freed, it may not be dereferenceable any longer, even though there is a dominating dereferenceable assumption. As first step, only consider assumptions if the pointer value cannot be freed if UseDerefAtPointSemantics is used.