[llvm] [SimplifyCFG] Optimize select over pointers to eliminate no-op load/store (PR #179277)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 17 01:06:32 PDT 2026
https://github.com/dnsampaio updated https://github.com/llvm/llvm-project/pull/179277
>From d96e2caa59ef1bf975eae253278944edcd3b3919 Mon Sep 17 00:00:00 2001
From: Diogo Sampaio <diogo.sampaio at openchip.com>
Date: Mon, 2 Feb 2026 17:01:31 +0100
Subject: [PATCH] [SimplifyCFG] Optimize select over pointers to eliminate
no-op load/store operations
Transform a pattern where one case of a select over pointers leads to a
store(load) of the same address, avoiding the load and store all together.
Pattern:
%gepA = getelementptr ... %a, ...
%gepB = getelementptr ... %b, ...
%sel = select i1 %cond, ptr %gepA, ptr %gepB
%val = load ... %sel
store ... %val, %gepA
When %sel chooses %gepA, the load and store
access the same address - this is a no-op.
This transforms it to:
br i1 %cond, label %cont, label %do_ldst
do_ldst:
%gepA = getelementptr ... %a, ...
%gepB = getelementptr ... %b, ...
%val = load ... %gepB
store ... %val, %gepA
br label %cont
cont:
...
This optimization is beneficial over loops by minimizing the memory
footprint.
---
llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 195 ++++++++
.../SimplifyCFG/select-pointer-noop-store.ll | 456 ++++++++++++++++++
2 files changed, 651 insertions(+)
create mode 100644 llvm/test/Transforms/SimplifyCFG/select-pointer-noop-store.ll
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index e7a6c9354953a..f2b7bac8deb3e 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -348,6 +348,195 @@ isSelectInRoleOfConjunctionOrDisjunction(const SelectInst *SI) {
} // end anonymous namespace
+/// Transform a pattern where one case of a select over pointers leads to a
+/// store(load) of the same address, avoiding the load and store all together.
+///
+/// Pattern:
+/// %gepA = getelementptr ... %a, ...
+/// %gepB = getelementptr ... %b, ...
+/// %sel = select i1 %cond, ptr %gepA, ptr %gepB
+/// %val = load ... %sel
+/// store ... %val, %gepA
+///
+/// When %sel chooses %gepA, the load and store
+/// access the same address - this is a no-op.
+///
+/// This transforms it to:
+/// br i1 %cond, label %cont, label %do_ldst
+/// do_ldst:
+/// %gepA = getelementptr ... %a, ...
+/// %gepB = getelementptr ... %b, ...
+/// %val = load ... %gepB
+/// store ... %val, %gepA
+/// br label %cont
+/// cont:
+/// ...
+static bool foldSelectPointerLoadStoreToNoop(BasicBlock *BB,
+ DomTreeUpdater *DTU) {
+ for (Instruction &I : *BB) {
+ auto *Sel = dyn_cast<SelectInst>(&I);
+ if (!Sel || !Sel->getType()->isPointerTy() || !Sel->hasOneUse())
+ continue;
+
+ Value *Cond = Sel->getCondition();
+ Value *TruePtr = Sel->getTrueValue();
+ Value *FalsePtr = Sel->getFalseValue();
+
+ // Select must be used by a GEP
+ auto *LoadGEP = dyn_cast<GetElementPtrInst>(*Sel->user_begin());
+ if (!LoadGEP || LoadGEP->getParent() != BB ||
+ LoadGEP->getPointerOperand() != Sel || !LoadGEP->hasOneUse())
+ continue;
+
+ // LoadGEP must be used by a load
+ auto *LI = dyn_cast<LoadInst>(*LoadGEP->user_begin());
+ if (!LI || LI->getParent() != BB || !LI->hasOneUse() || LI->isVolatile())
+ continue;
+
+ // Load must be used by a store
+ auto *SI = dyn_cast<StoreInst>(*LI->user_begin());
+ if (!SI || SI->getParent() != BB || SI->getValueOperand() != LI ||
+ SI->isVolatile())
+ continue;
+
+ // Store destination must be a GEP with single use
+ auto *StoreGEP = dyn_cast<GetElementPtrInst>(SI->getPointerOperand());
+ if (!StoreGEP || StoreGEP->getParent() != BB || !StoreGEP->hasOneUse())
+ continue;
+
+ // Check if indices match between load GEP and store GEP
+ if (LoadGEP->getNumIndices() != StoreGEP->getNumIndices())
+ continue;
+
+ bool IndicesMatch = true;
+ for (auto [LoadIdx, StoreIdx] :
+ zip(LoadGEP->indices(), StoreGEP->indices())) {
+ if (LoadIdx != StoreIdx) {
+ IndicesMatch = false;
+ break;
+ }
+ }
+ if (!IndicesMatch)
+ continue;
+
+ // Check if one of the select operands matches the store GEP's base pointer
+ Value *StoreBase = StoreGEP->getPointerOperand();
+ Value *EffectivePtr = nullptr; // The pointer that does actual work
+ bool NoopOnTrue = false;
+
+ if (TruePtr == StoreBase) {
+ EffectivePtr = FalsePtr;
+ NoopOnTrue = true;
+ } else if (FalsePtr == StoreBase) {
+ EffectivePtr = TruePtr;
+ NoopOnTrue = false;
+ } else {
+ continue;
+ }
+
+ // Check that no instruction between the load and store may write to
+ // memory or have side effects.
+ bool HasInterferingInst = false;
+ for (auto It = std::next(LI->getIterator()); &*It != SI; ++It) {
+ if (It->mayWriteToMemory() || It->mayHaveSideEffects()) {
+ HasInterferingInst = true;
+ break;
+ }
+ }
+ if (HasInterferingInst)
+ continue;
+
+ LLVM_DEBUG(dbgs() << "FOLDING SELECT LOAD STORE TO NOOP: " << *Sel << "\n");
+ LLVM_DEBUG(dbgs() << " LoadGEP: " << *LoadGEP << "\n");
+ LLVM_DEBUG(dbgs() << " Load: " << *LI << "\n");
+ LLVM_DEBUG(dbgs() << " StoreGEP: " << *StoreGEP << "\n");
+ LLVM_DEBUG(dbgs() << " Store: " << *SI << "\n");
+ LLVM_DEBUG(dbgs() << " Noop on " << (NoopOnTrue ? "true" : "false")
+ << "\n");
+
+ // Create a branch that skips the load/store
+ Function *F = BB->getParent();
+ BasicBlock *DoLdStBB =
+ BasicBlock::Create(F->getContext(), BB->getName() + ".do_ldst", F);
+ BasicBlock *ContBB =
+ BB->splitBasicBlock(SI->getIterator(), BB->getName() + ".cont");
+
+ // Remove the unconditional branch created by splitBasicBlock
+ BB->getTerminator()->eraseFromParent();
+
+ // Create conditional branch: skip load/store on noop case.
+ // Preserve the select's branch weights on the new conditional branch.
+ SmallVector<uint32_t, 2> SelectWeights(2);
+ auto *SelMD = Sel->getMetadata(LLVMContext::MD_prof);
+ bool HasWeights = extractBranchWeights(*Sel, SelectWeights);
+
+ IRBuilder<> Builder(BB);
+ CondBrInst *NewBI;
+ if (NoopOnTrue)
+ NewBI = Builder.CreateCondBr(Cond, ContBB, DoLdStBB);
+ else
+ NewBI = Builder.CreateCondBr(Cond, DoLdStBB, ContBB);
+
+ NewBI->setDebugLoc(Sel->getDebugLoc());
+ if (HasWeights)
+ setBranchWeights(*NewBI, SelectWeights,
+ /*IsExpected=*/hasBranchWeightOrigin(SelMD));
+
+ // Build the load/store in DoLdStBB
+ Builder.SetInsertPoint(DoLdStBB);
+
+ // Create new load GEP with the effective pointer
+ SmallVector<Value *, 4> Indices(LoadGEP->indices());
+ auto *NewLoadGEP = Builder.CreateGEP(
+ LoadGEP->getSourceElementType(), EffectivePtr, Indices,
+ LoadGEP->getName() + ".eff", LoadGEP->getNoWrapFlags());
+
+ if (auto *NewLoadGEPInst = dyn_cast<GetElementPtrInst>(NewLoadGEP))
+ NewLoadGEPInst->setDebugLoc(LoadGEP->getDebugLoc());
+
+ // Create new load
+ auto *NewLoad = Builder.CreateAlignedLoad(
+ LI->getType(), NewLoadGEP, LI->getAlign(), LI->getName() + ".eff");
+
+ NewLoad->setDebugLoc(LI->getDebugLoc());
+ // Create new store GEP
+ SmallVector<Value *, 4> StoreIndices(StoreGEP->indices());
+ auto *NewStoreGEP = Builder.CreateGEP(
+ StoreGEP->getSourceElementType(), StoreBase, StoreIndices,
+ StoreGEP->getName() + ".eff", StoreGEP->getNoWrapFlags());
+
+ if (auto *NewStoreGEPInst = dyn_cast<GetElementPtrInst>(NewStoreGEP))
+ NewStoreGEPInst->setDebugLoc(StoreGEP->getDebugLoc());
+
+ // Create new store
+ Builder.CreateAlignedStore(NewLoad, NewStoreGEP, SI->getAlign())
+ ->setDebugLoc(SI->getDebugLoc());
+
+ // Branch to continuation
+ Builder.CreateBr(ContBB)->setDebugLoc(Sel->getDebugLoc());
+
+ // Remove the original instructions
+ SI->eraseFromParent();
+ LI->eraseFromParent();
+ StoreGEP->eraseFromParent();
+ LoadGEP->eraseFromParent();
+ Sel->eraseFromParent();
+
+ // Update dominator tree
+ if (DTU) {
+ SmallVector<DominatorTree::UpdateType, 4> Updates;
+ Updates.push_back({DominatorTree::Insert, BB, DoLdStBB});
+ Updates.push_back({DominatorTree::Insert, BB, ContBB});
+ Updates.push_back({DominatorTree::Insert, DoLdStBB, ContBB});
+ DTU->applyUpdates(Updates);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
/// Return true if all the PHI nodes in the basic block \p BB
/// receive compatible (identical) incoming values when coming from
/// all of the predecessor blocks that are specified in \p IncomingBlocks.
@@ -8966,6 +9155,12 @@ bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) {
break;
}
+ // Check for select over pointers that leads to a load/store where one
+ // case is a no-op. Transform to conditional branch.
+ if (!RequireAndPreserveDomTree)
+ if (foldSelectPointerLoadStoreToNoop(BB, DTU))
+ Changed |= requestResimplify();
+
return Changed;
}
diff --git a/llvm/test/Transforms/SimplifyCFG/select-pointer-noop-store.ll b/llvm/test/Transforms/SimplifyCFG/select-pointer-noop-store.ll
new file mode 100644
index 0000000000000..50ad4a6100ae3
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/select-pointer-noop-store.ll
@@ -0,0 +1,456 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -S -passes=simplifycfg < %s | FileCheck %s
+
+; Test the optimization that transforms select-over-pointers followed by
+; GEP, load/store where one case is a no-op (load and store to same address).
+;
+; Pattern: A[i] = P[i] ? B[i] : A[i]
+; When P[i] is false, we load from A[i] and store to A[i] - a no-op.
+; The optimization creates a branch to skip the load/store in that case.
+;
+; Requires GEPs for both load and store with matching indices, as this is
+; required for the vectorizer to convert conditional load/stores to masked
+; store.
+
+;----------------------------------------------------------------------------
+; POSITIVE CASES - should be transformed
+;----------------------------------------------------------------------------
+
+; Basic case: noop when condition is true (TrueValue matches store base)
+define void @noop_on_true(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @noop_on_true(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_CONT:.*]], label %[[ENTRY_DO_LDST:.*]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr float, ptr [[B]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load float, ptr [[GEP_EFF]], align 4
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 4
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ ; When cond is true: sel = %a, load from a[i], store to a[i] -> noop
+ ; When cond is false: sel = %b, load from b[i], store to a[i] -> effective
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; Basic case: noop when condition is false (FalseValue matches store base)
+define void @noop_on_false(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @noop_on_false(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_DO_LDST:.*]], label %[[ENTRY_CONT:.*]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load float, ptr [[GEP_EFF]], align 4
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr float, ptr [[B]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 4
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ ; When cond is true: sel = %a, load from a[i], store to b[i] -> effective
+ ; When cond is false: sel = %b, load from b[i], store to b[i] -> noop
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %b, i64 %idx
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; With inbounds GEPs - should preserve inbounds attribute
+define void @with_inbounds(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @with_inbounds(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_CONT:.*]], label %[[ENTRY_DO_LDST:.*]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr inbounds float, ptr [[B]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load float, ptr [[GEP_EFF]], align 4
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr inbounds float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 4
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr inbounds float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr inbounds float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; With multiple indices in GEP
+define void @multi_index_gep(ptr %a, ptr %b, i1 %cond, i64 %idx1, i64 %idx2) {
+; CHECK-LABEL: define void @multi_index_gep(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX1:%.*]], i64 [[IDX2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_CONT:.*]], label %[[ENTRY_DO_LDST:.*]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr [10 x float], ptr [[B]], i64 [[IDX1]], i64 [[IDX2]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load float, ptr [[GEP_EFF]], align 4
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr [10 x float], ptr [[A]], i64 [[IDX1]], i64 [[IDX2]]
+; CHECK-NEXT: store float [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 4
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr [10 x float], ptr %sel, i64 %idx1, i64 %idx2
+ %val = load float, ptr %gep
+ %store_gep = getelementptr [10 x float], ptr %a, i64 %idx1, i64 %idx2
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; With integer type instead of float
+define void @integer_type(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @integer_type(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_CONT:.*]], label %[[ENTRY_DO_LDST:.*]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr i32, ptr [[B]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load i32, ptr [[GEP_EFF]], align 4
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr i32, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store i32 [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 4
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr i32, ptr %sel, i64 %idx
+ %val = load i32, ptr %gep
+ %store_gep = getelementptr i32, ptr %a, i64 %idx
+ store i32 %val, ptr %store_gep
+ ret void
+}
+
+; With double type and alignment
+define void @double_type_aligned(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @double_type_aligned(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_CONT:.*]], label %[[ENTRY_DO_LDST:.*]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr double, ptr [[B]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load double, ptr [[GEP_EFF]], align 8
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr double, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store double [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 8
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr double, ptr %sel, i64 %idx
+ %val = load double, ptr %gep, align 8
+ %store_gep = getelementptr double, ptr %a, i64 %idx
+ store double %val, ptr %store_gep, align 8
+ ret void
+}
+
+; Branch weights from the select should be preserved on the conditional branch
+; Noop on true: select weights [10, 90] -> br true(skip) weight 10, false(work) weight 90
+define void @noop_on_true_with_weights(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @noop_on_true_with_weights(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_CONT:.*]], label %[[ENTRY_DO_LDST:.*]], !prof [[PROF0:![0-9]+]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr float, ptr [[B]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load float, ptr [[GEP_EFF]], align 4
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 4
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b, !prof !0
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; Noop on false: select weights [70, 30] -> br true(work) weight 70, false(skip) weight 30
+define void @noop_on_false_with_weights(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @noop_on_false_with_weights(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[ENTRY_DO_LDST:.*]], label %[[ENTRY_CONT:.*]], !prof [[PROF1:![0-9]+]]
+; CHECK: [[ENTRY_CONT]]:
+; CHECK-NEXT: ret void
+; CHECK: [[ENTRY_DO_LDST]]:
+; CHECK-NEXT: [[GEP_EFF:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL_EFF:%.*]] = load float, ptr [[GEP_EFF]], align 4
+; CHECK-NEXT: [[STORE_GEP_EFF:%.*]] = getelementptr float, ptr [[B]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL_EFF]], ptr [[STORE_GEP_EFF]], align 4
+; CHECK-NEXT: br label %[[ENTRY_CONT]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b, !prof !1
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %b, i64 %idx
+ store float %val, ptr %store_gep
+ ret void
+}
+
+;----------------------------------------------------------------------------
+; NEGATIVE CASES - should NOT be transformed
+;----------------------------------------------------------------------------
+
+; Store base doesn't match either select operand
+define void @no_match_store_base(ptr %a, ptr %b, ptr %c, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @no_match_store_base(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], ptr [[C:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[C]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %c, i64 %idx
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; Different indices between load GEP and store GEP
+define void @different_indices(ptr %a, ptr %b, i1 %cond, i64 %idx1, i64 %idx2) {
+; CHECK-LABEL: define void @different_indices(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX1:%.*]], i64 [[IDX2:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX1]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX2]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx1
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx2
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; Volatile load - don't transform
+define void @volatile_load(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @volatile_load(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load volatile float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load volatile float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret void
+}
+
+; Volatile store - don't transform
+define void @volatile_store(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @volatile_store(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store volatile float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store volatile float %val, ptr %store_gep
+ ret void
+}
+
+; Select has multiple uses - don't transform
+define ptr @select_multi_use(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define ptr @select_multi_use(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret ptr [[SEL]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret ptr %sel
+}
+
+; LoadGEP has multiple uses - don't transform
+define ptr @load_gep_multi_use(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define ptr @load_gep_multi_use(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret ptr [[GEP]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret ptr %gep
+}
+
+; StoreGEP has multiple uses - don't transform
+define ptr @store_gep_multi_use(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define ptr @store_gep_multi_use(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret ptr [[STORE_GEP]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret ptr %store_gep
+}
+
+; Load has multiple uses - don't transform
+define float @load_multi_use(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define float @load_multi_use(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret float [[VAL]]
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep
+ ret float %val
+}
+
+; Direct pointer (no GEP) - don't transform
+define void @direct_ptr_no_gep(ptr %a, ptr %b, i1 %cond) {
+; CHECK-LABEL: define void @direct_ptr_no_gep(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[SEL]], align 4
+; CHECK-NEXT: store float [[VAL]], ptr [[A]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %val = load float, ptr %sel
+ store float %val, ptr %a
+ ret void
+}
+
+; Store doesn't use loaded value - don't transform
+define void @store_different_value(ptr %a, ptr %b, i1 %cond, i64 %idx, float %other) {
+; CHECK-LABEL: define void @store_different_value(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]], float [[OTHER:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[OTHER]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %other, ptr %store_gep
+ ret void
+}
+
+define void @preserve_select(ptr %a, ptr %b, i1 %cond, i64 %idx) {
+; CHECK-LABEL: define void @preserve_select(
+; CHECK-SAME: ptr [[A:%.*]], ptr [[B:%.*]], i1 [[COND:%.*]], i64 [[IDX:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[COND]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT: [[GEP:%.*]] = getelementptr float, ptr [[SEL]], i64 [[IDX]]
+; CHECK-NEXT: [[VAL:%.*]] = load float, ptr [[GEP]], align 4
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: [[STORE_GEP:%.*]] = getelementptr float, ptr [[A]], i64 [[IDX]]
+; CHECK-NEXT: store float [[VAL]], ptr [[STORE_GEP]], align 4
+; CHECK-NEXT: ret void
+;
+entry:
+ %sel = select i1 %cond, ptr %a, ptr %b
+ %gep = getelementptr float, ptr %sel, i64 %idx
+ %val = load float, ptr %gep, align 4
+ call void @side_effect()
+ %store_gep = getelementptr float, ptr %a, i64 %idx
+ store float %val, ptr %store_gep, align 4
+ ret void
+}
+
+declare void @side_effect()
+
+!0 = !{!"branch_weights", i32 10, i32 90}
+!1 = !{!"branch_weights", !"expected", i32 70, i32 30}
+
+;.
+; CHECK: [[PROF0]] = !{!"branch_weights", i32 10, i32 90}
+; CHECK: [[PROF1]] = !{!"branch_weights", !"expected", i32 70, i32 30}
+;.
More information about the llvm-commits
mailing list