Skip to content
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

[InstCombine] Remove redundant alignment assumptions. #123348

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

fhahn
Copy link
Contributor

@fhahn fhahn commented Jan 17, 2025

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

@llvmbot
Copy link
Member

llvmbot commented Jan 17, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/123348.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+27-4)
  • (modified) llvm/test/Transforms/InstCombine/assume-align.ll (+1-4)
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) ]

@goldsteinn
Copy link
Contributor

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();
Copy link
Contributor

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.

Copy link
Contributor Author

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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can overflow?

Copy link
Contributor Author

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

@fhahn fhahn force-pushed the ic-remove-redundant-align-assumes branch from 18fce21 to be0e9ee Compare January 21, 2025 12:29
Copy link
Contributor Author

@fhahn fhahn left a 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();
Copy link
Contributor Author

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

@goldsteinn
Copy link
Contributor

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.

Hmm, maybe we should re-emit alignment assumptions during inlining?

@fhahn
Copy link
Contributor Author

fhahn commented Jan 21, 2025

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.

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.

@fhahn fhahn force-pushed the ic-remove-redundant-align-assumes branch from 2100fea to 36756d6 Compare January 21, 2025 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants