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

[EarlyCSE] Rematerialize alignment assumption. #109131

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

Conversation

fhahn
Copy link
Contributor

@fhahn fhahn commented Sep 18, 2024

If the load has align metadata, preserve it via an alignment
assumption. Note that this doesn't use salavageKnowledge, as we need
to create the assumption for the value we replaced the load with.

Comparison run dtcxzyw/llvm-opt-benchmark#1343 (comment)

Unfortunately the diff is too large to display, top changes

Top 5 improvements:
  typst-rs/4p30esqzpn2o5olu.ll 6364278007 6347884002 -0.26%
  llvm/StdLibraryFunctionsChecker.cpp.ll 25872800221 25821679537 -0.20%
  meshlab/arap.cpp.ll 16171781140 16149344101 -0.14%
  darktable/DngOpcodes.cpp.ll 2123576004 2120732832 -0.13%
  hermes/Passes.cpp.ll 1997252275 1994672931 -0.13%
Top 5 regressions:
  rust-analyzer-rs/4dj9fscdf5c509wz.ll 145086655 148887948 +2.62%
  typst-rs/p1dgiootfedk7bo.ll 488966323 501623041 +2.59%
  rust-analyzer-rs/1bjrygtvfxna7kin.ll 5561255586 5677108765 +2.08%
  actix-rs/125u7gvq3v36sg70.ll 98217713 99929856 +1.74%
  wasmtime-rs/3r0osxvwe4cd326n.ll 9141682789 9286492070 +1.58%

Overall: -0.00945255%

Copy link

github-actions bot commented Sep 19, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@fhahn fhahn marked this pull request as ready for review September 19, 2024 19:46
@llvmbot
Copy link
Collaborator

llvmbot commented Sep 19, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

Changes

If the load has align metadata, preserve it via an alignment
assumption. Note that this doesn't use salavageKnowledge, as we need
to create the assumption for the value we replaced the load with.

Comparison run dtcxzyw/llvm-opt-benchmark#1343 (comment)

Unfortunately the diff is too large to display, top changes

Top 5 improvements:
  typst-rs/4p30esqzpn2o5olu.ll 6364278007 6347884002 -0.26%
  llvm/StdLibraryFunctionsChecker.cpp.ll 25872800221 25821679537 -0.20%
  meshlab/arap.cpp.ll 16171781140 16149344101 -0.14%
  darktable/DngOpcodes.cpp.ll 2123576004 2120732832 -0.13%
  hermes/Passes.cpp.ll 1997252275 1994672931 -0.13%
Top 5 regressions:
  rust-analyzer-rs/4dj9fscdf5c509wz.ll 145086655 148887948 +2.62%
  typst-rs/p1dgiootfedk7bo.ll 488966323 501623041 +2.59%
  rust-analyzer-rs/1bjrygtvfxna7kin.ll 5561255586 5677108765 +2.08%
  actix-rs/125u7gvq3v36sg70.ll 98217713 99929856 +1.74%
  wasmtime-rs/3r0osxvwe4cd326n.ll 9141682789 9286492070 +1.58%

Overall: -0.00945255%

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

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/EarlyCSE.cpp (+13)
  • (modified) llvm/test/Transforms/EarlyCSE/materialize-align-assumptions.ll (+3)
diff --git a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
index cf11f5bc885a75..743b333987bc9a 100644
--- a/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
+++ b/llvm/lib/Transforms/Scalar/EarlyCSE.cpp
@@ -31,6 +31,7 @@
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Dominators.h"
 #include "llvm/IR/Function.h"
+#include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/InstrTypes.h"
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
@@ -1588,6 +1589,18 @@ bool EarlyCSE::processNode(DomTreeNode *Node) {
         if (InVal.IsLoad)
           if (auto *I = dyn_cast<Instruction>(Op))
             combineMetadataForCSE(I, &Inst, false);
+
+        // If the load has align metadata, preserve it via an alignment
+        // assumption. Note that this doesn't use salavageKnowledge, as we need
+        // to create the assumption for the value we replaced the load with.
+        if (auto *AlignMD = Inst.getMetadata(LLVMContext::MD_align)) {
+          auto *A = mdconst::extract<ConstantInt>(AlignMD->getOperand(0));
+          if (Op->getPointerAlignment(SQ.DL).value() % A->getZExtValue() != 0) {
+            IRBuilder B(&Inst);
+            B.CreateAlignmentAssumption(SQ.DL, Op, A);
+          }
+        }
+
         if (!Inst.use_empty())
           Inst.replaceAllUsesWith(Op);
         salvageKnowledge(&Inst, &AC);
diff --git a/llvm/test/Transforms/EarlyCSE/materialize-align-assumptions.ll b/llvm/test/Transforms/EarlyCSE/materialize-align-assumptions.ll
index ea63376957162b..628577b0975071 100644
--- a/llvm/test/Transforms/EarlyCSE/materialize-align-assumptions.ll
+++ b/llvm/test/Transforms/EarlyCSE/materialize-align-assumptions.ll
@@ -10,6 +10,7 @@ define ptr @align_replacement_does_not_have_align_metadata(ptr noalias %p) {
 ; CHECK-NEXT:    call void @foo(ptr [[L_1]])
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[L_1]], i64 4
 ; CHECK-NEXT:    store ptr [[GEP]], ptr [[P]], align 8
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[GEP]], i64 4) ]
 ; CHECK-NEXT:    ret ptr [[GEP]]
 ;
   %l.1 = load ptr, ptr %p, align 8
@@ -27,6 +28,7 @@ define ptr @align_replacement_does_not_have_align_metadata2(ptr noalias %p) {
 ; CHECK-NEXT:    [[L_1:%.*]] = load ptr, ptr [[P]], align 8
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[L_1]], i64 4
 ; CHECK-NEXT:    store ptr [[GEP]], ptr [[P]], align 8
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[GEP]], i64 4) ]
 ; CHECK-NEXT:    ret ptr [[GEP]]
 ;
   %l.1 = load ptr, ptr %p, align 8
@@ -54,6 +56,7 @@ define ptr @align_replacement_has_smaller_alignment(ptr noalias %p) {
 ; CHECK-SAME: ptr noalias [[P:%.*]]) {
 ; CHECK-NEXT:    [[L_1:%.*]] = load ptr, ptr [[P]], align 8, !align [[META0]]
 ; CHECK-NEXT:    call void @foo(ptr [[L_1]])
+; CHECK-NEXT:    call void @llvm.assume(i1 true) [ "align"(ptr [[L_1]], i64 8) ]
 ; CHECK-NEXT:    ret ptr [[L_1]]
 ;
   %l.1 = load ptr, ptr %p, align 8, !align !0

// If the load has align metadata, preserve it via an alignment
// assumption. Note that this doesn't use salavageKnowledge, as we need
// to create the assumption for the value we replaced the load with.
if (auto *AlignMD = Inst.getMetadata(LLVMContext::MD_align)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

You also need !noundef for this transform. !align by itself only returns poison, while the assumption converts it into IUB.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should be updated, thanks!

tmsri pushed a commit to tmsri/llvm-project that referenced this pull request Sep 19, 2024
@dtcxzyw
Copy link
Member

dtcxzyw commented Sep 20, 2024

Unfortunately the diff is too large to display

Fixed. See dtcxzyw/llvm-opt-benchmark#1345.

@nikic
Copy link
Contributor

nikic commented Sep 20, 2024

I'm still not convinced that doing something like this is a good idea. The core problem is that you can't tell whether the !align metadata is sufficiently valuable or not. In Rust, most pointer loads will have !align metadata. It's added because adding it is free and sometimes helps, but at the same time, nobody will particularly care if it gets dropped. Doing this conversion means we now have to make a tradeoff between adding the metadata, and the compile-time and optimization impact it will have.

I looked through the IR diffs, and there were plenty of minor optimization regressions and very few improvements. I think the two most common ones are:

  • Dead block not eliminated due to assume. (This is something I tried to fix in the past and it got reverted due to optimization regressions...)
  • Hoisting prevented because one block has an assume at the top and the other doesn't.

I also noticed that in some cases the same assume is repeated many times in the same block -- we should be optimizing those away.

Another interesting data point is that nearly all assumptions were redundant in the sense that they were directly followed by a load or gep+load with the same alignment. (Of course, most of our analysis cannot really use alignment on loads to reason about the alignment of the pointer at some other point.)

@fhahn
Copy link
Contributor Author

fhahn commented Sep 20, 2024

I'm still not convinced that doing something like this is a good idea. The core problem is that you can't tell whether the !align metadata is sufficiently valuable or not. In Rust, most pointer loads will have !align metadata. It's added because adding it is free and sometimes helps, but at the same time, nobody will particularly care if it gets dropped. Doing this conversion means we now have to make a tradeoff between adding the metadata, and the compile-time and optimization impact it will have.

Yeah, when originally working on #108958, I didn't expect much fallout as !align/alignment assumptions isn't super common in C/C++, forgetting about Rust.

I looked through the IR diffs, and there were plenty of minor optimization regressions and very few improvements. I think the two most common ones are:

  • Dead block not eliminated due to assume. (This is something I tried to fix in the past and it got reverted due to optimization regressions...)
  • Hoisting prevented because one block has an assume at the top and the other doesn't.

Yep, unfortunately there's a number of improvements that are still needed to avoid negative fallout from using assumptions...

I also noticed that in some cases the same assume is repeated many times in the same block -- we should be optimizing those away.

Another interesting data point is that nearly all assumptions were redundant in the sense that they were directly followed by a load or gep+load with the same alignment. (Of course, most of our analysis cannot really use alignment on loads to reason about the alignment of the pointer at some other point.)

We might be able to skip rematerializing in those cases by doing some extra analysis.

I can look into addressing some of the issues mentioned above.

Overall my main goal is to remove the blockers for #108958 :)

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