[llvm] [InstCombine] fold addrcast+load through selects (PR #176352)

Theodoros Theodoridis via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 21 00:58:13 PST 2026


https://github.com/thetheodor updated https://github.com/llvm/llvm-project/pull/176352

>From bb676f4dab5597d18aeea103797bceab269026f3 Mon Sep 17 00:00:00 2001
From: Theodoros Theodoridis <ttheodoridis at nvidia.com>
Date: Wed, 14 Jan 2026 14:25:42 +0000
Subject: [PATCH 1/6] Add test cases

---
 .../InstCombine/load-addrspacecast-select.ll  | 166 ++++++++++++++++++
 1 file changed, 166 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll

diff --git a/llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll b/llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll
new file mode 100644
index 0000000000000..3323367a34e0b
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll
@@ -0,0 +1,166 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+;   load(addrspacecast(select(A, B))) --> select(load(addrspacecast(A)), load(addrspacecast(B)))
+
+define i32 @load_addrspacecast_select_alloca(i1 %cond) {
+; CHECK-LABEL: @load_addrspacecast_select_alloca(
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 42, ptr [[A]], align 4
+; CHECK-NEXT:    store i32 100, ptr [[B]], align 4
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[RESULT:%.*]] = load i32, ptr addrspace(5) [[CAST]], align 4
+; CHECK-NEXT:    ret i32 [[RESULT]]
+;
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  store i32 42, ptr %a, align 4
+  store i32 100, ptr %b, align 4
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load i32, ptr addrspace(5) %cast, align 4
+  ret i32 %load
+}
+
+define <2 x i32> @load_addrspacecast_select_alloca_vec(i1 %cond) {
+; CHECK-LABEL: @load_addrspacecast_select_alloca_vec(
+; CHECK-NEXT:    [[A:%.*]] = alloca <2 x i32>, align 8
+; CHECK-NEXT:    [[B:%.*]] = alloca <2 x i32>, align 8
+; CHECK-NEXT:    store <2 x i32> <i32 42, i32 43>, ptr [[A]], align 8
+; CHECK-NEXT:    store <2 x i32> <i32 100, i32 101>, ptr [[B]], align 8
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[RESULT:%.*]] = load <2 x i32>, ptr addrspace(5) [[CAST]], align 8
+; CHECK-NEXT:    ret <2 x i32> [[RESULT]]
+;
+  %a = alloca <2 x i32>, align 8
+  %b = alloca <2 x i32>, align 8
+  store <2 x i32> <i32 42, i32 43>, ptr %a, align 8
+  store <2 x i32> <i32 100, i32 101>, ptr %b, align 8
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load <2 x i32>, ptr addrspace(5) %cast, align 8
+  ret <2 x i32> %load
+}
+
+; Negative tests:
+
+
+define i32 @load_addrspacecast_select_multi_use(i1 %cond, ptr %out) {
+; CHECK-LABEL: @load_addrspacecast_select_multi_use(
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 42, ptr [[A]], align 4
+; CHECK-NEXT:    store i32 100, ptr [[B]], align 4
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[LOAD:%.*]] = load i32, ptr addrspace(5) [[CAST]], align 4
+; CHECK-NEXT:    store ptr [[SEL]], ptr [[OUT:%.*]], align 8
+; CHECK-NEXT:    ret i32 [[LOAD]]
+;
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  store i32 42, ptr %a, align 4
+  store i32 100, ptr %b, align 4
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load i32, ptr addrspace(5) %cast, align 4
+  ; Extra use of the select prevents transformation
+  store ptr %sel, ptr %out, align 8
+  ret i32 %load
+}
+
+define i32 @load_addrspacecast_multi_use(i1 %cond, ptr addrspace(5) %out) {
+; CHECK-LABEL: @load_addrspacecast_multi_use(
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 42, ptr [[A]], align 4
+; CHECK-NEXT:    store i32 100, ptr [[B]], align 4
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[LOAD:%.*]] = load i32, ptr addrspace(5) [[CAST]], align 4
+; CHECK-NEXT:    store ptr addrspace(5) [[CAST]], ptr addrspace(5) [[OUT:%.*]], align 8
+; CHECK-NEXT:    ret i32 [[LOAD]]
+;
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  store i32 42, ptr %a, align 4
+  store i32 100, ptr %b, align 4
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load i32, ptr addrspace(5) %cast, align 4
+  ; Extra use of the cast prevents transformation
+  store ptr addrspace(5) %cast, ptr addrspace(5) %out, align 8
+  ret i32 %load
+}
+
+define i32 @load_addrspacecast_select_on_args(i1 %cond, ptr %a, ptr %b) {
+; CHECK-LABEL: @load_addrspacecast_select_on_args(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[LOAD:%.*]] = load i32, ptr addrspace(5) [[CAST]], align 4
+; CHECK-NEXT:    ret i32 [[LOAD]]
+;
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load i32, ptr addrspace(5) %cast, align 4
+  ret i32 %load
+}
+
+; dereferenceable is not enough to trigger the transformation
+define i32 @load_addrspacecast_select_on_args_dereferenceable(i1 %cond, ptr dereferenceable(4) %a, ptr dereferenceable(4) %b) {
+; CHECK-LABEL: @load_addrspacecast_select_on_args_dereferenceable(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A:%.*]], ptr [[B:%.*]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[LOAD:%.*]] = load i32, ptr addrspace(5) [[CAST]], align 4
+; CHECK-NEXT:    ret i32 [[LOAD]]
+;
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load i32, ptr addrspace(5) %cast, align 4
+  ret i32 %load
+}
+
+define i32 @load_addrspacecast_select_volatile(i1 %cond) {
+; CHECK-LABEL: @load_addrspacecast_select_volatile(
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 42, ptr [[A]], align 4
+; CHECK-NEXT:    store i32 100, ptr [[B]], align 4
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[LOAD:%.*]] = load volatile i32, ptr addrspace(5) [[CAST]], align 4
+; CHECK-NEXT:    ret i32 [[LOAD]]
+;
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  store i32 42, ptr %a, align 4
+  store i32 100, ptr %b, align 4
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load volatile i32, ptr addrspace(5) %cast, align 4
+  ret i32 %load
+}
+
+define i32 @load_addrspacecast_select_atomic(i1 %cond) {
+; CHECK-LABEL: @load_addrspacecast_select_atomic(
+; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
+; CHECK-NEXT:    store i32 42, ptr [[A]], align 4
+; CHECK-NEXT:    store i32 100, ptr [[B]], align 4
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
+; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
+; CHECK-NEXT:    [[LOAD:%.*]] = load atomic i32, ptr addrspace(5) [[CAST]] seq_cst, align 4
+; CHECK-NEXT:    ret i32 [[LOAD]]
+;
+  %a = alloca i32, align 4
+  %b = alloca i32, align 4
+  store i32 42, ptr %a, align 4
+  store i32 100, ptr %b, align 4
+  %sel = select i1 %cond, ptr %a, ptr %b
+  %cast = addrspacecast ptr %sel to ptr addrspace(5)
+  %load = load atomic i32, ptr addrspace(5) %cast seq_cst, align 4
+  ret i32 %load
+}

>From c4ba7e61f3ff1e8b70b97bc56936b01d4a7091de Mon Sep 17 00:00:00 2001
From: Theodoros Theodoridis <ttheodoridis at nvidia.com>
Date: Wed, 14 Jan 2026 14:26:06 +0000
Subject: [PATCH 2/6] Fold addrspacecast+load through select

Add support for

load(addrspacecast(select(Cond, &V1, &V2))) =>
select(Cond, load(addrspacecast(&V1)), load(addrspacecast(&V2)))

Note: alive does not support addrspacecasts and thus proofs are omitted.
---
 .../InstCombine/InstCombineInternal.h         |  4 +-
 .../InstCombineLoadStoreAlloca.cpp            | 49 +++++++++++++++++--
 .../InstCombine/InstructionCombining.cpp      |  7 +--
 .../InstCombine/load-addrspacecast-select.ll  | 16 +-----
 4 files changed, 53 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 8c8b300bb2002..ee1d0cb8af3a7 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -668,9 +668,11 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   /// other operand, try to fold the binary operator into the select arguments.
   /// This also works for Cast instructions, which obviously do not have a
   /// second operand.
+  /// If \p FoldWithoutSimplify is true, fold even if neither arm simplifies.
   Instruction *FoldOpIntoSelect(Instruction &Op, SelectInst *SI,
                                 bool FoldWithMultiUse = false,
-                                bool SimplifyBothArms = false);
+                                bool SimplifyBothArms = false,
+                                bool FoldWithoutSimplify = false);
 
   Instruction *foldBinOpSelectBinOp(BinaryOperator &Op);
 
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 5bca0bb8846c0..c03bc33765b27 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1137,13 +1137,51 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
     // but it would not be valid if we transformed it to load from null
     // unconditionally.
     //
+    // Check if both arms of a select are safe to load unconditionally.
+    auto CanSpeculateThroughSelect = [&](SelectInst *SI) {
+      Align Alignment = LI.getAlign();
+      return isSafeToLoadUnconditionally(SI->getTrueValue(), LI.getType(),
+                                         Alignment, DL, SI) &&
+             isSafeToLoadUnconditionally(SI->getFalseValue(), LI.getType(),
+                                         Alignment, DL, SI);
+    };
+
+    // Fold:
+    //   load(addrspacecast(select(Cond, &V1, &V2)))
+    // into:
+    //   load(select(Cond, addrspacecast(&V1), addrspacecast(&V2)))
+    // Then the transformation load(select) -> select(load) below will complete
+    // the fold:
+    //   select(Cond, load(addrspacecast(&V1)), load(addrspacecast(&V2)))
+    //
+    // We only fold if the subsequent load(select) transformation will succeed.
+    if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Op)) {
+      unsigned TargetAddressSpace = ASC->getDestAddressSpace();
+      if (auto *SI = dyn_cast<SelectInst>(ASC->getOperand(0))) {
+        if (CanSpeculateThroughSelect(SI)) {
+          if (Instruction *NewSel = FoldOpIntoSelect(
+                  *ASC, SI, /*FoldWithMultiUse=*/false,
+                  /*SimplifyBothArms=*/false, /*FoldWithoutSimplify=*/true)) {
+            unsigned LHSTargetAddressSpace =
+                NewSel->getOperand(1)->getType()->getPointerAddressSpace();
+            unsigned RHSTargetAddressSpace =
+                NewSel->getOperand(2)->getType()->getPointerAddressSpace();
+            assert(LHSTargetAddressSpace == TargetAddressSpace &&
+                   RHSTargetAddressSpace == TargetAddressSpace &&
+                   "Expected same target address space for both arms");
+            NewSel->insertBefore(LI.getIterator());
+            // Replace the load's operand and continue processing with the new
+            // select. The load(select) transformation below will complete the
+            // fold.
+            return replaceOperand(LI, 0, NewSel);
+          }
+        }
+      }
+    }
     if (SelectInst *SI = dyn_cast<SelectInst>(Op)) {
       // load (select (Cond, &V1, &V2))  --> select(Cond, load &V1, load &V2).
-      Align Alignment = LI.getAlign();
-      if (isSafeToLoadUnconditionally(SI->getOperand(1), LI.getType(),
-                                      Alignment, DL, SI) &&
-          isSafeToLoadUnconditionally(SI->getOperand(2), LI.getType(),
-                                      Alignment, DL, SI)) {
+      if (CanSpeculateThroughSelect(SI)) {
+        Align Alignment = LI.getAlign();
         LoadInst *V1 =
             Builder.CreateLoad(LI.getType(), SI->getOperand(1),
                                SI->getOperand(1)->getName() + ".val");
@@ -1151,6 +1189,7 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
             Builder.CreateLoad(LI.getType(), SI->getOperand(2),
                                SI->getOperand(2)->getName() + ".val");
         assert(LI.isUnordered() && "implied by above");
+
         V1->setAlignment(Alignment);
         V1->setAtomic(LI.getOrdering(), LI.getSyncScopeID());
         V2->setAlignment(Alignment);
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index a23d121ace0a3..50979663de168 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -1781,7 +1781,8 @@ static Value *foldOperationIntoSelectOperand(Instruction &I, SelectInst *SI,
 
 Instruction *InstCombinerImpl::FoldOpIntoSelect(Instruction &Op, SelectInst *SI,
                                                 bool FoldWithMultiUse,
-                                                bool SimplifyBothArms) {
+                                                bool SimplifyBothArms,
+                                                bool FoldWithoutSimplify) {
   // Don't modify shared select instructions unless set FoldWithMultiUse
   if (!SI->hasOneUser() && !FoldWithMultiUse)
     return nullptr;
@@ -1822,10 +1823,10 @@ Instruction *InstCombinerImpl::FoldOpIntoSelect(Instruction &Op, SelectInst *SI,
   Value *NewTV = simplifyOperationIntoSelectOperand(Op, SI, /*IsTrueArm=*/true);
   Value *NewFV =
       simplifyOperationIntoSelectOperand(Op, SI, /*IsTrueArm=*/false);
-  if (!NewTV && !NewFV)
+  if (!NewTV && !NewFV && !FoldWithoutSimplify)
     return nullptr;
 
-  if (SimplifyBothArms && !(NewTV && NewFV))
+  if (SimplifyBothArms && !(NewTV && NewFV) && !FoldWithoutSimplify)
     return nullptr;
 
   // Create an instruction for the arm that did not fold.
diff --git a/llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll b/llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll
index 3323367a34e0b..748f64b93d652 100644
--- a/llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll
+++ b/llvm/test/Transforms/InstCombine/load-addrspacecast-select.ll
@@ -5,13 +5,7 @@
 
 define i32 @load_addrspacecast_select_alloca(i1 %cond) {
 ; CHECK-LABEL: @load_addrspacecast_select_alloca(
-; CHECK-NEXT:    [[A:%.*]] = alloca i32, align 4
-; CHECK-NEXT:    [[B:%.*]] = alloca i32, align 4
-; CHECK-NEXT:    store i32 42, ptr [[A]], align 4
-; CHECK-NEXT:    store i32 100, ptr [[B]], align 4
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
-; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
-; CHECK-NEXT:    [[RESULT:%.*]] = load i32, ptr addrspace(5) [[CAST]], align 4
+; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[COND:%.*]], i32 42, i32 100
 ; CHECK-NEXT:    ret i32 [[RESULT]]
 ;
   %a = alloca i32, align 4
@@ -26,13 +20,7 @@ define i32 @load_addrspacecast_select_alloca(i1 %cond) {
 
 define <2 x i32> @load_addrspacecast_select_alloca_vec(i1 %cond) {
 ; CHECK-LABEL: @load_addrspacecast_select_alloca_vec(
-; CHECK-NEXT:    [[A:%.*]] = alloca <2 x i32>, align 8
-; CHECK-NEXT:    [[B:%.*]] = alloca <2 x i32>, align 8
-; CHECK-NEXT:    store <2 x i32> <i32 42, i32 43>, ptr [[A]], align 8
-; CHECK-NEXT:    store <2 x i32> <i32 100, i32 101>, ptr [[B]], align 8
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], ptr [[A]], ptr [[B]]
-; CHECK-NEXT:    [[CAST:%.*]] = addrspacecast ptr [[SEL]] to ptr addrspace(5)
-; CHECK-NEXT:    [[RESULT:%.*]] = load <2 x i32>, ptr addrspace(5) [[CAST]], align 8
+; CHECK-NEXT:    [[RESULT:%.*]] = select i1 [[COND:%.*]], <2 x i32> <i32 42, i32 43>, <2 x i32> <i32 100, i32 101>
 ; CHECK-NEXT:    ret <2 x i32> [[RESULT]]
 ;
   %a = alloca <2 x i32>, align 8

>From c547e3a5f634dce01efd678a640597563c74768a Mon Sep 17 00:00:00 2001
From: Theodoros Theodoridis <ttheodoridis at nvidia.com>
Date: Mon, 19 Jan 2026 06:02:06 +0000
Subject: [PATCH 3/6] Simplify

---
 .../InstCombine/InstCombineInternal.h         |  4 +-
 .../InstCombineLoadStoreAlloca.cpp            | 79 +++++++------------
 .../InstCombine/InstructionCombining.cpp      |  7 +-
 3 files changed, 32 insertions(+), 58 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index ee1d0cb8af3a7..8c8b300bb2002 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -668,11 +668,9 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   /// other operand, try to fold the binary operator into the select arguments.
   /// This also works for Cast instructions, which obviously do not have a
   /// second operand.
-  /// If \p FoldWithoutSimplify is true, fold even if neither arm simplifies.
   Instruction *FoldOpIntoSelect(Instruction &Op, SelectInst *SI,
                                 bool FoldWithMultiUse = false,
-                                bool SimplifyBothArms = false,
-                                bool FoldWithoutSimplify = false);
+                                bool SimplifyBothArms = false);
 
   Instruction *foldBinOpSelectBinOp(BinaryOperator &Op);
 
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index c03bc33765b27..69ceeeace022a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1137,59 +1137,36 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
     // but it would not be valid if we transformed it to load from null
     // unconditionally.
     //
-    // Check if both arms of a select are safe to load unconditionally.
-    auto CanSpeculateThroughSelect = [&](SelectInst *SI) {
-      Align Alignment = LI.getAlign();
-      return isSafeToLoadUnconditionally(SI->getTrueValue(), LI.getType(),
-                                         Alignment, DL, SI) &&
-             isSafeToLoadUnconditionally(SI->getFalseValue(), LI.getType(),
-                                         Alignment, DL, SI);
-    };
-
-    // Fold:
-    //   load(addrspacecast(select(Cond, &V1, &V2)))
-    // into:
-    //   load(select(Cond, addrspacecast(&V1), addrspacecast(&V2)))
-    // Then the transformation load(select) -> select(load) below will complete
-    // the fold:
-    //   select(Cond, load(addrspacecast(&V1)), load(addrspacecast(&V2)))
-    //
-    // We only fold if the subsequent load(select) transformation will succeed.
-    if (auto *ASC = dyn_cast<AddrSpaceCastInst>(Op)) {
-      unsigned TargetAddressSpace = ASC->getDestAddressSpace();
-      if (auto *SI = dyn_cast<SelectInst>(ASC->getOperand(0))) {
-        if (CanSpeculateThroughSelect(SI)) {
-          if (Instruction *NewSel = FoldOpIntoSelect(
-                  *ASC, SI, /*FoldWithMultiUse=*/false,
-                  /*SimplifyBothArms=*/false, /*FoldWithoutSimplify=*/true)) {
-            unsigned LHSTargetAddressSpace =
-                NewSel->getOperand(1)->getType()->getPointerAddressSpace();
-            unsigned RHSTargetAddressSpace =
-                NewSel->getOperand(2)->getType()->getPointerAddressSpace();
-            assert(LHSTargetAddressSpace == TargetAddressSpace &&
-                   RHSTargetAddressSpace == TargetAddressSpace &&
-                   "Expected same target address space for both arms");
-            NewSel->insertBefore(LI.getIterator());
-            // Replace the load's operand and continue processing with the new
-            // select. The load(select) transformation below will complete the
-            // fold.
-            return replaceOperand(LI, 0, NewSel);
-          }
-        }
-      }
-    }
-    if (SelectInst *SI = dyn_cast<SelectInst>(Op)) {
+
+    AddrSpaceCastInst *ASC = dyn_cast<AddrSpaceCastInst>(Op);
+    Value *SelectOp = Op;
+    if (ASC && ASC->getOperand(0)->hasOneUse())
+      SelectOp = ASC->getOperand(0);
+    if (SelectInst *SI = dyn_cast<SelectInst>(SelectOp)) {
       // load (select (Cond, &V1, &V2))  --> select(Cond, load &V1, load &V2).
-      if (CanSpeculateThroughSelect(SI)) {
-        Align Alignment = LI.getAlign();
-        LoadInst *V1 =
-            Builder.CreateLoad(LI.getType(), SI->getOperand(1),
-                               SI->getOperand(1)->getName() + ".val");
-        LoadInst *V2 =
-            Builder.CreateLoad(LI.getType(), SI->getOperand(2),
-                               SI->getOperand(2)->getName() + ".val");
+      // or
+      // load (select (Cond, addrspacecast(&V1), addrspacecast(&V2))) -->
+      //  select(Cond, load (addrspacecast(&V1)), load (addrspacecast(&V2))).
+      Align Alignment = LI.getAlign();
+      if (isSafeToLoadUnconditionally(SI->getOperand(1), LI.getType(),
+                                      Alignment, DL, SI) &&
+          isSafeToLoadUnconditionally(SI->getOperand(2), LI.getType(),
+                                      Alignment, DL, SI)) {
+
+        auto MaybeCastedLoadOperant = [&](Value *Op) {
+          if (ASC)
+            return Builder.CreateAddrSpaceCast(Op, ASC->getType(),
+                                               Op->getName() + ".cast");
+          return Op;
+        };
+        Value *LoadOp1 = MaybeCastedLoadOperant(SI->getOperand(1));
+        LoadInst *V1 = Builder.CreateLoad(LI.getType(), LoadOp1,
+                                          LoadOp1->getName() + ".val");
+
+        Value *LoadOp2 = MaybeCastedLoadOperant(SI->getOperand(2));
+        LoadInst *V2 = Builder.CreateLoad(LI.getType(), LoadOp2,
+                                          LoadOp2->getName() + ".val");
         assert(LI.isUnordered() && "implied by above");
-
         V1->setAlignment(Alignment);
         V1->setAtomic(LI.getOrdering(), LI.getSyncScopeID());
         V2->setAlignment(Alignment);
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 50979663de168..a23d121ace0a3 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -1781,8 +1781,7 @@ static Value *foldOperationIntoSelectOperand(Instruction &I, SelectInst *SI,
 
 Instruction *InstCombinerImpl::FoldOpIntoSelect(Instruction &Op, SelectInst *SI,
                                                 bool FoldWithMultiUse,
-                                                bool SimplifyBothArms,
-                                                bool FoldWithoutSimplify) {
+                                                bool SimplifyBothArms) {
   // Don't modify shared select instructions unless set FoldWithMultiUse
   if (!SI->hasOneUser() && !FoldWithMultiUse)
     return nullptr;
@@ -1823,10 +1822,10 @@ Instruction *InstCombinerImpl::FoldOpIntoSelect(Instruction &Op, SelectInst *SI,
   Value *NewTV = simplifyOperationIntoSelectOperand(Op, SI, /*IsTrueArm=*/true);
   Value *NewFV =
       simplifyOperationIntoSelectOperand(Op, SI, /*IsTrueArm=*/false);
-  if (!NewTV && !NewFV && !FoldWithoutSimplify)
+  if (!NewTV && !NewFV)
     return nullptr;
 
-  if (SimplifyBothArms && !(NewTV && NewFV) && !FoldWithoutSimplify)
+  if (SimplifyBothArms && !(NewTV && NewFV))
     return nullptr;
 
   // Create an instruction for the arm that did not fold.

>From 5983a7220c0065298e4ce51ffb039453fece79d8 Mon Sep 17 00:00:00 2001
From: Theodoros Theodoridis <ttheodoridis at nvidia.com>
Date: Mon, 19 Jan 2026 06:19:49 +0000
Subject: [PATCH 4/6] Fix comment

---
 llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 69ceeeace022a..25399ecf6192d 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1145,7 +1145,7 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
     if (SelectInst *SI = dyn_cast<SelectInst>(SelectOp)) {
       // load (select (Cond, &V1, &V2))  --> select(Cond, load &V1, load &V2).
       // or
-      // load (select (Cond, addrspacecast(&V1), addrspacecast(&V2))) -->
+      // load (addrspacecast(select (Cond, &V1, &V2))) -->
       //  select(Cond, load (addrspacecast(&V1)), load (addrspacecast(&V2))).
       Align Alignment = LI.getAlign();
       if (isSafeToLoadUnconditionally(SI->getOperand(1), LI.getType(),

>From ce488b3e6f04cb22322f6eb9e9f477655d1c5770 Mon Sep 17 00:00:00 2001
From: Theodoros Theodoridis <ttheodoridis at nvidia.com>
Date: Wed, 21 Jan 2026 09:56:01 +0100
Subject: [PATCH 5/6] Update
 llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp

Co-authored-by: Nikita Popov <github at npopov.com>
---
 llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 25399ecf6192d..a9716ed7cbf7c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1153,7 +1153,7 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
           isSafeToLoadUnconditionally(SI->getOperand(2), LI.getType(),
                                       Alignment, DL, SI)) {
 
-        auto MaybeCastedLoadOperant = [&](Value *Op) {
+        auto MaybeCastedLoadOperand = [&](Value *Op) {
           if (ASC)
             return Builder.CreateAddrSpaceCast(Op, ASC->getType(),
                                                Op->getName() + ".cast");

>From c21512139b0a4083360fb0053c01c1d661e152eb Mon Sep 17 00:00:00 2001
From: Theodoros Theodoridis <ttheodoridis at nvidia.com>
Date: Wed, 21 Jan 2026 08:57:38 +0000
Subject: [PATCH 6/6] Fix lambda names

---
 .../lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index a9716ed7cbf7c..82d5df6a3cb29 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1159,11 +1159,11 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
                                                Op->getName() + ".cast");
           return Op;
         };
-        Value *LoadOp1 = MaybeCastedLoadOperant(SI->getOperand(1));
+        Value *LoadOp1 = MaybeCastedLoadOperand(SI->getOperand(1));
         LoadInst *V1 = Builder.CreateLoad(LI.getType(), LoadOp1,
                                           LoadOp1->getName() + ".val");
 
-        Value *LoadOp2 = MaybeCastedLoadOperant(SI->getOperand(2));
+        Value *LoadOp2 = MaybeCastedLoadOperand(SI->getOperand(2));
         LoadInst *V2 = Builder.CreateLoad(LI.getType(), LoadOp2,
                                           LoadOp2->getName() + ".val");
         assert(LI.isUnordered() && "implied by above");



More information about the llvm-commits mailing list