[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