-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[InstCombine] Remove redundant alignment assumptions. #123348
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
Conversation
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-transforms Author: Florian Hahn (fhahn) ChangesUse known bits to remove redundant alignment assumptions. Libc++ now adds alignment assumptions for std::vector::begin() and std::vector::end(), so I expect we will see quite a bit more assumptions in C++ [1]. Try to clean up some redundant ones to start with. [1] #108961 Full diff: https://github.com/llvm/llvm-project/pull/123348.diff 2 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 842881156dc67f..ab628e94235913 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -3199,12 +3199,13 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
// TODO: apply range metadata for range check patterns?
}
- // Separate storage assumptions apply to the underlying allocations, not any
- // particular pointer within them. When evaluating the hints for AA purposes
- // we getUnderlyingObject them; by precomputing the answers here we can
- // avoid having to do so repeatedly there.
for (unsigned Idx = 0; Idx < II->getNumOperandBundles(); Idx++) {
OperandBundleUse OBU = II->getOperandBundleAt(Idx);
+
+ // Separate storage assumptions apply to the underlying allocations, not
+ // any particular pointer within them. When evaluating the hints for AA
+ // purposes we getUnderlyingObject them; by precomputing the answers here
+ // we can avoid having to do so repeatedly there.
if (OBU.getTagName() == "separate_storage") {
assert(OBU.Inputs.size() == 2);
auto MaybeSimplifyHint = [&](const Use &U) {
@@ -3218,6 +3219,28 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
MaybeSimplifyHint(OBU.Inputs[0]);
MaybeSimplifyHint(OBU.Inputs[1]);
}
+
+ // Try to remove redundant alignment assumptions.
+ if (OBU.getTagName() == "align" && OBU.Inputs.size() == 2) {
+ RetainedKnowledge RK = getKnowledgeFromBundle(
+ *cast<AssumeInst>(II), II->bundle_op_info_begin()[Idx]);
+ if (!RK || RK.AttrKind != Attribute::Alignment ||
+ !isPowerOf2_64(RK.ArgValue))
+ continue;
+
+ // Try to get the instruction before the assumption to use as context.
+ Instruction *CtxI = nullptr;
+ if (CtxI && II->getParent()->begin() != II->getIterator())
+ CtxI = II->getPrevNode();
+
+ auto Known = computeKnownBits(RK.WasOn, 1, CtxI);
+ unsigned KnownAlign = 1 << Known.countMinTrailingZeros();
+ if (KnownAlign < RK.ArgValue)
+ continue;
+
+ auto *New = CallBase::removeOperandBundle(II, OBU.getTagID());
+ return New;
+ }
}
// Convert nonnull assume like:
diff --git a/llvm/test/Transforms/InstCombine/assume-align.ll b/llvm/test/Transforms/InstCombine/assume-align.ll
index f0e02574330861..962ad4f792a0d1 100644
--- a/llvm/test/Transforms/InstCombine/assume-align.ll
+++ b/llvm/test/Transforms/InstCombine/assume-align.ll
@@ -175,7 +175,6 @@ define ptr @dont_fold_assume_align_zero_of_loaded_pointer_into_align_metadata(pt
define ptr @redundant_assume_align_1(ptr %p) {
; CHECK-LABEL: @redundant_assume_align_1(
; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i32 1) ]
; CHECK-NEXT: call void @foo(ptr [[P2]])
; CHECK-NEXT: ret ptr [[P2]]
;
@@ -189,7 +188,6 @@ define ptr @redundant_assume_align_1(ptr %p) {
define ptr @redundant_assume_align_8_via_align_metadata(ptr %p) {
; CHECK-LABEL: @redundant_assume_align_8_via_align_metadata(
; CHECK-NEXT: [[P2:%.*]] = load ptr, ptr [[P:%.*]], align 8, !align [[META0:![0-9]+]]
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P2]], i32 8) ]
; CHECK-NEXT: call void @foo(ptr [[P2]])
; CHECK-NEXT: ret ptr [[P2]]
;
@@ -214,8 +212,7 @@ define ptr @assume_align_16_via_align_metadata(ptr %p) {
define ptr @redundant_assume_align_8_via_align_attribute(ptr align 8 %p) {
; CHECK-LABEL: @redundant_assume_align_8_via_align_attribute(
-; CHECK-NEXT: call void @llvm.assume(i1 true) [ "align"(ptr [[P:%.*]], i32 8) ]
-; CHECK-NEXT: call void @foo(ptr [[P]])
+; CHECK-NEXT: call void @foo(ptr [[P:%.*]])
; CHECK-NEXT: ret ptr [[P]]
;
call void @llvm.assume(i1 true) [ "align"(ptr %p, i32 8) ]
|
Can this ever delete unrecoverable information? |
// Try to get the instruction before the assumption to use as context. | ||
Instruction *CtxI = nullptr; | ||
if (CtxI && II->getParent()->begin() != II->getIterator()) | ||
CtxI = II->getPrevNode(); |
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.
Hm, I don't get how this works. Don't we scan forward across guaranteed-to-transfer instructions and should find the assume that way? Esp. as we disable the ephemeral value handling for these 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.
At the moment, assumes aren't used by IC's computeKnownBits in general because it doesn't pass through AssumptionCache (probably good to improve separately), so this here was intended to just help with using other context-sensitive info.
I've removed it for now.
CtxI = II->getPrevNode(); | ||
|
||
auto Known = computeKnownBits(RK.WasOn, 1, CtxI); | ||
unsigned KnownAlign = 1 << Known.countMinTrailingZeros(); |
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 overflow?
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.
Yep, should be fixed in the latest version, although I am not sure how to create a test case where it would actually overflow
18fce21
to
be0e9ee
Compare
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 this ever delete unrecoverable information?
The main case where we can lose info is if we derive the alignment from an argument and later inline the function; then the align arg attribute will be gone. Updated to only do this for pointers not derived from an argument.
Without support for GEP recurrences, we would also lose information in some cases when using the alignment of a call result, but that should be fixed with #123518
https://github.com/dtcxzyw/llvm-opt-benchmark/pull/1984/files shows the impact with with both #123518 and this PR
CtxI = II->getPrevNode(); | ||
|
||
auto Known = computeKnownBits(RK.WasOn, 1, CtxI); | ||
unsigned KnownAlign = 1 << Known.countMinTrailingZeros(); |
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.
Yep, should be fixed in the latest version, although I am not sure how to create a test case where it would actually overflow
Hmm, maybe we should re-emit alignment assumptions during inlining? |
Eventually yes/maybe. The problem is that adding assumptions at the moment can lead to worse results, so before emitting more assumptions I think we should make sure we can clean up ones that don't add any value. This patch is a small step towards getting better at cleaning things up. @dtcxzyw's https://github.com/dtcxzyw/llvm-opt-benchmark/ is super helpful at surfacing cases where assumptions (or dropping them) causes issues. The current patch together with #123518 is a first step that shouldn't cause any regressions. |
2100fea
to
36756d6
Compare
if (!UO || isa<Argument>(UO)) | ||
continue; | ||
|
||
KnownBits Known = computeKnownBits(RK.WasOn, 0, nullptr); |
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.
Comment on why the nullptr context here? Though I think we'd be better off adding an AllowEphemeralValues option to SimplifyQuery and use it as appropriate. We can safely set AllowEphemeralValues=true when inferring alignment and can drop the current special case for align operand bundles.
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.
Added a comment (we don't want to use the assumption we are about to remove).
Though I think we'd be better off adding an AllowEphemeralValues option to SimplifyQuery and use it as appropriate. We can safely set AllowEphemeralValues=true when inferring alignment and can drop the current special case for align operand bundles.
Can be done as separate improvement IIUC, assuming you mean the special case below?
// Allow AllowEphemerals in isValidAssumeForContext, as the CxtI might <<<
// be the producer of the pointer in the bundle. At the moment, align
// assumptions aren't optimized away.
if (RK.WasOn == V && RK.AttrKind == Attribute::Alignment &&
isPowerOf2_64(RK.ArgValue) &&
isValidAssumeForContext(I, Q.CxtI, Q.DT, /*AllowEphemerals*/ true))
Known.Zero.setLowBits(Log2_64(RK.ArgValue));
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.
Yes. We set AllowEphemerals=true there on the assumption that known bits won't be used to remove align operand bundles -- which is no longer true.
36756d6
to
31df383
Compare
31df383
to
6cdf9e6
Compare
6cdf9e6
to
44f742f
Compare
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.
ping :)
Rebased on current main
, it looks as-is there's only a single case I could spot where alignment gets narrowed/regressed: https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2725/files/50758450294569ef6ac6f41d50dcbc7f189257b1#diff-40a5ffbdcae402ed7681965b1723569e2cafa9ceb214ba4b248aab028c07dbda
This case and more can be further strengthed by #123518.
if (!UO || isa<Argument>(UO)) | ||
continue; | ||
|
||
KnownBits Known = computeKnownBits(RK.WasOn, 0, nullptr); |
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.
Added a comment (we don't want to use the assumption we are about to remove).
Though I think we'd be better off adding an AllowEphemeralValues option to SimplifyQuery and use it as appropriate. We can safely set AllowEphemeralValues=true when inferring alignment and can drop the current special case for align operand bundles.
Can be done as separate improvement IIUC, assuming you mean the special case below?
// Allow AllowEphemerals in isValidAssumeForContext, as the CxtI might <<<
// be the producer of the pointer in the bundle. At the moment, align
// assumptions aren't optimized away.
if (RK.WasOn == V && RK.AttrKind == Attribute::Alignment &&
isPowerOf2_64(RK.ArgValue) &&
isValidAssumeForContext(I, Q.CxtI, Q.DT, /*AllowEphemerals*/ true))
Known.Zero.setLowBits(Log2_64(RK.ArgValue));
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.
The tests seems kind of not really related to the change being implemented?
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.
Value::MaxAlignmentExponent
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.
Done thanks
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.
Why is this assume being dropped? It does not look redundant.
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.
Missing handling for RK.IRArgValue
not being a constant, in which case RK.ArgValue = 1.
Should be fixed now
44f742f
to
38beef2
Compare
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.
The tests seems kind of not really related to the change being implemented?
Yeah a few were related to folding alignment assumption into loads, which isn't part of the PR. removed
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.
Missing handling for RK.IRArgValue
not being a constant, in which case RK.ArgValue = 1.
Should be fixed now
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.
Done thanks
0bd6728
to
1f6207a
Compare
ping :) |
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.
LG. Thanks.
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.
As we emit a bunch of alignment assumptions, can we turn the tag name align
into a tag ID OB_align
?
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.
Yep, put up #158078
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.
RetainedKnowledge RK = getKnowledgeFromBundle( | |
*cast<AssumeInst>(II), II->bundle_op_info_begin()[Idx]); | |
RetainedKnowledge RK = getKnowledgeFromOperandInAssume( | |
*cast<AssumeInst>(II), Idx); |
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.
Updated, thanks, it required adjusting Idx
by II->arg_size(0
1f6207a
to
697bdf2
Compare
Failures are
which seem broken in Linux Precommit CI for the last few days consistently |
…3348) Use known bits to remove redundant alignment assumptions. Libc++ now adds alignment assumptions for std::vector::begin() and std::vector::end(), so I expect we will see quite a bit more assumptions in C++ [1]. Try to clean up some redundant ones to start with. [1] llvm/llvm-project#108961 PR: llvm/llvm-project#123348
Use known bits to remove redundant alignment assumptions.
Libc++ now adds alignment assumptions for std::vector::begin() and std::vector::end(), so I expect we will see quite a bit more assumptions in C++ [1]. Try to clean up some redundant ones to start with.
[1] #108961