[llvm] [SimplifyCFG] Sink stores and loads with differing alignments (PR #215858)
Sayan Sivakumaran via llvm-commits
llvm-commits at lists.llvm.org
Sun Aug 23 17:03:26 PDT 2026
https://github.com/sivakusayan updated https://github.com/llvm/llvm-project/pull/215858
>From d06e3a0ba38beb1f2f2bdeca0743d3e465917a0b Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Sun, 16 Aug 2026 17:27:22 -0500
Subject: [PATCH 1/2] Pre-commit tests
---
.../SimplifyCFG/X86/sink-common-code.ll | 116 ++++++++++++++++++
1 file changed, 116 insertions(+)
diff --git a/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll b/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll
index 8daa2334a4a68..aea969ef74e9c 100644
--- a/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll
+++ b/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll
@@ -2177,6 +2177,122 @@ join:
ret i32 %phi
}
+define void @store_different_alignments(i1 %c, ptr %p1, ptr %p2) {
+; CHECK-LABEL: @store_different_alignments(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[IF1:%.*]], label [[IF2:%.*]]
+; CHECK: if1:
+; CHECK-NEXT: store i8 0, ptr [[P1:%.*]], align 2
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: if2:
+; CHECK-NEXT: store i8 0, ptr [[P2:%.*]], align 1
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %c, label %if1, label %if2
+
+if1:
+ store i8 0, ptr %p1, align 2
+ br label %end
+
+if2:
+ store i8 0, ptr %p2, align 1
+ br label %end
+
+end:
+ ret void
+}
+
+define void @store_different_alignments_incompatible_attributes(i1 %c, ptr %p1, ptr %p2) {
+; CHECK-LABEL: @store_different_alignments_incompatible_attributes(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[IF1:%.*]], label [[IF2:%.*]]
+; CHECK: if1:
+; CHECK-NEXT: store volatile i8 0, ptr [[P1:%.*]], align 2
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: if2:
+; CHECK-NEXT: store i8 0, ptr [[P2:%.*]], align 1
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: ret void
+;
+entry:
+ br i1 %c, label %if1, label %if2
+
+if1:
+ store volatile i8 0, ptr %p1, align 2
+ br label %end
+
+if2:
+ store i8 0, ptr %p2, align 1
+ br label %end
+
+end:
+ ret void
+}
+
+define i8 @load_different_alignments(i1 %c, ptr %p1, ptr %p2) {
+; CHECK-LABEL: @load_different_alignments(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[IF1:%.*]], label [[IF2:%.*]]
+; CHECK: if1:
+; CHECK-NEXT: [[RET1:%.*]] = load i8, ptr [[P1:%.*]], align 2
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: if2:
+; CHECK-NEXT: [[RET2:%.*]] = load i8, ptr [[P2:%.*]], align 1
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[RET:%.*]] = phi i8 [ [[RET1]], [[IF1]] ], [ [[RET2]], [[IF2]] ]
+; CHECK-NEXT: ret i8 [[RET]]
+;
+entry:
+ br i1 %c, label %if1, label %if2
+
+if1:
+ %ret1 = load i8, ptr %p1, align 2
+ br label %end
+
+if2:
+ %ret2 = load i8, ptr %p2, align 1
+ br label %end
+
+end:
+ %ret = phi i8 [ %ret1, %if1 ], [ %ret2, %if2 ]
+ ret i8 %ret
+}
+
+define i8 @load_different_alignments_incompatible_attributes(i1 %c, ptr %p1, ptr %p2) {
+; CHECK-LABEL: @load_different_alignments_incompatible_attributes(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: br i1 [[C:%.*]], label [[IF1:%.*]], label [[IF2:%.*]]
+; CHECK: if1:
+; CHECK-NEXT: [[RET1:%.*]] = load volatile i8, ptr [[P1:%.*]], align 2
+; CHECK-NEXT: br label [[END:%.*]]
+; CHECK: if2:
+; CHECK-NEXT: [[RET2:%.*]] = load i8, ptr [[P2:%.*]], align 1
+; CHECK-NEXT: br label [[END]]
+; CHECK: end:
+; CHECK-NEXT: [[RET:%.*]] = phi i8 [ [[RET1]], [[IF1]] ], [ [[RET2]], [[IF2]] ]
+; CHECK-NEXT: ret i8 [[RET]]
+;
+entry:
+ br i1 %c, label %if1, label %if2
+
+if1:
+ %ret1 = load volatile i8, ptr %p1, align 2
+ br label %end
+
+if2:
+ %ret2 = load i8, ptr %p2, align 1
+ br label %end
+
+end:
+ %ret = phi i8 [ %ret1, %if1 ], [ %ret2, %if2 ]
+ ret i8 %ret
+}
+
declare void @dummy()
declare void @use.ptr(ptr)
>From ab0b2b07c73ad31db0897105351068268c88e481 Mon Sep 17 00:00:00 2001
From: Sayan Sivakumaran <sivakusayan at gmail.com>
Date: Sun, 23 Aug 2026 19:02:00 -0500
Subject: [PATCH 2/2] [SimplifyCFG] Enable sinking stores and loads with
differing alignments
---
llvm/include/llvm/IR/Instruction.h | 7 ++++++
llvm/lib/IR/Instruction.cpp | 18 +++++++++++++++
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 5 ++--
.../SimplifyCFG/X86/sink-common-code.ll | 23 ++++---------------
4 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/llvm/include/llvm/IR/Instruction.h b/llvm/include/llvm/IR/Instruction.h
index 13646ec66d6b3..c6c9e0eb0e637 100644
--- a/llvm/include/llvm/IR/Instruction.h
+++ b/llvm/include/llvm/IR/Instruction.h
@@ -724,6 +724,13 @@ class Instruction : public User,
/// V and this instruction.
LLVM_ABI void andIRFlags(const Value *V);
+ /// Restricts this instruction's attributes to those which are shared
+ /// with I. This will essentially:
+ /// 1) Do a logical 'and' of this instruction's flags with I's flags.
+ /// 2) Find a common alignment with I, if both instructions support a notion
+ /// of alignment.
+ LLVM_ABI void intersectIRAttributes(const Instruction *I);
+
/// Merge 2 debug locations and apply it to the Instruction. If the
/// instruction is a CallIns, we need to traverse the inline chain to find
/// the common scope. This is not efficient for N-way merging as each time
diff --git a/llvm/lib/IR/Instruction.cpp b/llvm/lib/IR/Instruction.cpp
index 099a54e684c96..84c6ac0d20dfb 100644
--- a/llvm/lib/IR/Instruction.cpp
+++ b/llvm/lib/IR/Instruction.cpp
@@ -813,6 +813,24 @@ void Instruction::andIRFlags(const Value *V) {
DestICmp->setSameSign(DestICmp->hasSameSign() && SrcICmp->hasSameSign());
}
+void Instruction::intersectIRAttributes(const Instruction *I) {
+ // TODO: Should we intersect metadata here too?
+ andIRFlags(I);
+
+ auto IsLoadOrStore = [](const Instruction *I) {
+ return isa<LoadInst>(I) || isa<StoreInst>(I);
+ };
+ if (IsLoadOrStore(I) && IsLoadOrStore(this)) {
+ Align Common =
+ std::min(getLoadStoreAlignment(this), getLoadStoreAlignment(I));
+ assert(isAligned(Common, getLoadStoreAlignment(this).value()) &&
+ isAligned(Common, getLoadStoreAlignment(I).value()) &&
+ "Common alignment doesn't satisfy alignment constraints?");
+
+ setLoadStoreAlignment(this, Common);
+ }
+}
+
const char *Instruction::getOpcodeName(unsigned OpCode) {
switch (OpCode) {
// Terminators
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 089892dc573f3..e29a682154e69 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -2248,7 +2248,8 @@ static bool canSinkInstructions(
const Instruction *I0 = Insts.front();
const auto I0MMRA = MMRAMetadata(*I0);
for (auto *I : Insts) {
- if (!I->isSameOperationAs(I0, Instruction::CompareUsingIntersectedAttrs))
+ if (!I->isSameOperationAs(I0, Instruction::CompareUsingIntersectedAttrs |
+ Instruction::CompareIgnoringAlignment))
return false;
// Treat MMRAs conservatively. This pass can be quite aggressive and
@@ -2379,7 +2380,7 @@ static void sinkLastInstruction(ArrayRef<BasicBlock*> Blocks) {
// instead of using complex API for N-way merge.
I0->applyMergedLocation(I0->getDebugLoc(), I->getDebugLoc());
combineMetadataForCSE(I0, I, true);
- I0->andIRFlags(I);
+ I0->intersectIRAttributes(I);
if (auto *CB = dyn_cast<CallBase>(I0)) {
bool Success = CB->tryIntersectAttributes(cast<CallBase>(I));
assert(Success && "We should not be trying to sink callbases "
diff --git a/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll b/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll
index aea969ef74e9c..4f98cfd39f6ee 100644
--- a/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll
+++ b/llvm/test/Transforms/SimplifyCFG/X86/sink-common-code.ll
@@ -2180,14 +2180,8 @@ join:
define void @store_different_alignments(i1 %c, ptr %p1, ptr %p2) {
; CHECK-LABEL: @store_different_alignments(
; CHECK-NEXT: entry:
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF1:%.*]], label [[IF2:%.*]]
-; CHECK: if1:
-; CHECK-NEXT: store i8 0, ptr [[P1:%.*]], align 2
-; CHECK-NEXT: br label [[END:%.*]]
-; CHECK: if2:
-; CHECK-NEXT: store i8 0, ptr [[P2:%.*]], align 1
-; CHECK-NEXT: br label [[END]]
-; CHECK: end:
+; CHECK-NEXT: [[P1_P2:%.*]] = select i1 [[C:%.*]], ptr [[P1:%.*]], ptr [[P2:%.*]]
+; CHECK-NEXT: store i8 0, ptr [[P1_P2]], align 1
; CHECK-NEXT: ret void
;
entry:
@@ -2236,16 +2230,9 @@ end:
define i8 @load_different_alignments(i1 %c, ptr %p1, ptr %p2) {
; CHECK-LABEL: @load_different_alignments(
; CHECK-NEXT: entry:
-; CHECK-NEXT: br i1 [[C:%.*]], label [[IF1:%.*]], label [[IF2:%.*]]
-; CHECK: if1:
-; CHECK-NEXT: [[RET1:%.*]] = load i8, ptr [[P1:%.*]], align 2
-; CHECK-NEXT: br label [[END:%.*]]
-; CHECK: if2:
-; CHECK-NEXT: [[RET2:%.*]] = load i8, ptr [[P2:%.*]], align 1
-; CHECK-NEXT: br label [[END]]
-; CHECK: end:
-; CHECK-NEXT: [[RET:%.*]] = phi i8 [ [[RET1]], [[IF1]] ], [ [[RET2]], [[IF2]] ]
-; CHECK-NEXT: ret i8 [[RET]]
+; CHECK-NEXT: [[P1_P2:%.*]] = select i1 [[C:%.*]], ptr [[P1:%.*]], ptr [[P2:%.*]]
+; CHECK-NEXT: [[RET2:%.*]] = load i8, ptr [[P1_P2]], align 1
+; CHECK-NEXT: ret i8 [[RET2]]
;
entry:
br i1 %c, label %if1, label %if2
More information about the llvm-commits
mailing list