[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