[llvm] [InstCombine] Preserve all load metadata when folding load of select (PR #206663)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 9 02:20:13 PDT 2026
https://github.com/ayrai-gb updated https://github.com/llvm/llvm-project/pull/206663
>From 214a3d654b01cf12e63ae6712e33ac52a932f0b3 Mon Sep 17 00:00:00 2001
From: Ayush Rai <your-email at amd.com>
Date: Tue, 30 Jun 2026 10:20:19 +0530
Subject: [PATCH 1/2] [InstCombine] Preserve all load metadata when folding
load of select
---
.../InstCombine/InstCombineLoadStoreAlloca.cpp | 11 +++++++----
.../test/Transforms/InstCombine/loadstore-metadata.ll | 10 +++++-----
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 99ed22c97c3a8..90d8f24103326 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1162,10 +1162,13 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
Builder.CreateLoad(LI.getType(), LoadOp2, LI.getProperties(),
LoadOp2->getName() + ".val");
assert(LI.isUnordered() && "implied by above");
- // It is safe to copy any metadata that does not trigger UB. Copy any
- // poison-generating metadata.
- V1->copyMetadata(LI, Metadata::PoisonGeneratingIDs);
- V2->copyMetadata(LI, Metadata::PoisonGeneratingIDs);
+ // Each new load performs the same access as the original load in one
+ // arm of the select, and executes only when that arm is selected, so
+ // every fact the original load carried still holds. Clone all load
+ // metadata (not just the poison-generating kinds) to preserve !tbaa,
+ // !invariant.group, !nontemporal, !dereferenceable, etc.
+ copyMetadataForLoad(*V1, LI);
+ copyMetadataForLoad(*V2, LI);
return SelectInst::Create(SI->getCondition(), V1, V2, "", nullptr,
ProfcheckDisableMetadataFixes ? nullptr : SI);
}
diff --git a/llvm/test/Transforms/InstCombine/loadstore-metadata.ll b/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
index 02552df831196..add4e8145aa21 100644
--- a/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
+++ b/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
@@ -204,14 +204,14 @@ define ptr @preserve_load_metadata_after_select_transform1(i1 %c, ptr dereferenc
; CHECK-LABEL: define ptr @preserve_load_metadata_after_select_transform1(
; CHECK-SAME: i1 [[C:%.*]], ptr dereferenceable(8) [[A:%.*]], ptr dereferenceable(8) [[B:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[B_VAL:%.*]] = load ptr, ptr [[B]], align 1, !nonnull [[META6]], !align [[META8]]
-; CHECK-NEXT: [[A_VAL:%.*]] = load ptr, ptr [[A]], align 1, !nonnull [[META6]], !align [[META8]]
+; CHECK-NEXT: [[B_VAL:%.*]] = load ptr, ptr [[B]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !invariant.load [[META6]], !nontemporal [[META7]], !nonnull [[META6]], !dereferenceable [[META8]], !invariant.group [[META6]], !align [[META8]], !llvm.access.group [[META6]], !noundef [[META6]]
+; CHECK-NEXT: [[A_VAL:%.*]] = load ptr, ptr [[A]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !invariant.load [[META6]], !nontemporal [[META7]], !nonnull [[META6]], !dereferenceable [[META8]], !invariant.group [[META6]], !align [[META8]], !llvm.access.group [[META6]], !noundef [[META6]]
; CHECK-NEXT: [[L_SEL:%.*]] = select i1 [[C]], ptr [[B_VAL]], ptr [[A_VAL]]
; CHECK-NEXT: ret ptr [[L_SEL]]
;
entry:
%ptr.sel = select i1 %c, ptr %b, ptr %a
- %l.sel = load ptr, ptr %ptr.sel, align 1, !tbaa !0, !llvm.access.group !7, !dereferenceable !9, !noundef !{}, !invariant.load !7, !align !9, !nonnull !{}
+ %l.sel = load ptr, ptr %ptr.sel, align 1, !tbaa !0, !llvm.access.group !7, !dereferenceable !9, !noundef !{}, !invariant.load !7, !align !9, !nonnull !{}, !nontemporal !8, !invariant.group !7
ret ptr %l.sel
}
@@ -220,8 +220,8 @@ define i32 @preserve_load_metadata_after_select_transform_range(i1 %c, ptr deref
; CHECK-LABEL: define i32 @preserve_load_metadata_after_select_transform_range(
; CHECK-SAME: i1 [[C:%.*]], ptr dereferenceable(8) [[A:%.*]], ptr dereferenceable(8) [[B:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[B_VAL:%.*]] = load i32, ptr [[B]], align 1, !range [[RNG11:![0-9]+]]
-; CHECK-NEXT: [[A_VAL:%.*]] = load i32, ptr [[A]], align 1, !range [[RNG11]]
+; CHECK-NEXT: [[B_VAL:%.*]] = load i32, ptr [[B]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !range [[RNG11:![0-9]+]], !invariant.load [[META6]], !llvm.access.group [[META6]], !noundef [[META6]]
+; CHECK-NEXT: [[A_VAL:%.*]] = load i32, ptr [[A]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !range [[RNG11]], !invariant.load [[META6]], !llvm.access.group [[META6]], !noundef [[META6]]
; CHECK-NEXT: [[L_SEL:%.*]] = select i1 [[C]], i32 [[B_VAL]], i32 [[A_VAL]]
; CHECK-NEXT: ret i32 [[L_SEL]]
;
>From 996ef17cff995e5f7bb3ed78c58e55ef219627e1 Mon Sep 17 00:00:00 2001
From: Ayush Rai <your-email at amd.com>
Date: Tue, 7 Jul 2026 17:10:42 +0530
Subject: [PATCH 2/2] [InstCombine] Only preserve speculation-safe metadata
when folding load of select
---
.../InstCombine/InstCombineLoadStoreAlloca.cpp | 11 ++++++-----
.../Transforms/InstCombine/loadstore-metadata.ll | 16 ++++++++++------
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
index 90d8f24103326..33406be37be8c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp
@@ -1162,13 +1162,14 @@ Instruction *InstCombinerImpl::visitLoadInst(LoadInst &LI) {
Builder.CreateLoad(LI.getType(), LoadOp2, LI.getProperties(),
LoadOp2->getName() + ".val");
assert(LI.isUnordered() && "implied by above");
- // Each new load performs the same access as the original load in one
- // arm of the select, and executes only when that arm is selected, so
- // every fact the original load carried still holds. Clone all load
- // metadata (not just the poison-generating kinds) to preserve !tbaa,
- // !invariant.group, !nontemporal, !dereferenceable, etc.
+ // The original load is replaced by two loads that execute
+ // unconditionally, so any metadata implying immediate UB (e.g.
+ // !noundef, !dereferenceable, !invariant.load) must not be carried
+ // over. Copy all metadata and then drop the UB-implying kinds.
copyMetadataForLoad(*V1, LI);
+ V1->dropUBImplyingAttrsAndMetadata();
copyMetadataForLoad(*V2, LI);
+ V2->dropUBImplyingAttrsAndMetadata();
return SelectInst::Create(SI->getCondition(), V1, V2, "", nullptr,
ProfcheckDisableMetadataFixes ? nullptr : SI);
}
diff --git a/llvm/test/Transforms/InstCombine/loadstore-metadata.ll b/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
index add4e8145aa21..268fed7f1991f 100644
--- a/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
+++ b/llvm/test/Transforms/InstCombine/loadstore-metadata.ll
@@ -199,13 +199,15 @@ entry:
ret i32 %c
}
-; Preserve none-UB metadata on loads.
+; The two new loads execute unconditionally, so UB-implying (!noundef,
+; !dereferenceable, !invariant.load) and AA/type metadata (!tbaa,
+; !invariant.group, ...) are dropped; only poison-generating metadata is kept.
define ptr @preserve_load_metadata_after_select_transform1(i1 %c, ptr dereferenceable(8) %a, ptr dereferenceable(8) %b) {
; CHECK-LABEL: define ptr @preserve_load_metadata_after_select_transform1(
; CHECK-SAME: i1 [[C:%.*]], ptr dereferenceable(8) [[A:%.*]], ptr dereferenceable(8) [[B:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[B_VAL:%.*]] = load ptr, ptr [[B]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !invariant.load [[META6]], !nontemporal [[META7]], !nonnull [[META6]], !dereferenceable [[META8]], !invariant.group [[META6]], !align [[META8]], !llvm.access.group [[META6]], !noundef [[META6]]
-; CHECK-NEXT: [[A_VAL:%.*]] = load ptr, ptr [[A]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !invariant.load [[META6]], !nontemporal [[META7]], !nonnull [[META6]], !dereferenceable [[META8]], !invariant.group [[META6]], !align [[META8]], !llvm.access.group [[META6]], !noundef [[META6]]
+; CHECK-NEXT: [[B_VAL:%.*]] = load ptr, ptr [[B]], align 1, !nonnull [[META6]], !align [[META8]]
+; CHECK-NEXT: [[A_VAL:%.*]] = load ptr, ptr [[A]], align 1, !nonnull [[META6]], !align [[META8]]
; CHECK-NEXT: [[L_SEL:%.*]] = select i1 [[C]], ptr [[B_VAL]], ptr [[A_VAL]]
; CHECK-NEXT: ret ptr [[L_SEL]]
;
@@ -215,13 +217,15 @@ entry:
ret ptr %l.sel
}
-; Preserve none-UB metadata on loads.
+; The two new loads execute unconditionally, so UB-implying (!noundef,
+; !dereferenceable, !invariant.load) and AA/type metadata (!tbaa,
+; !invariant.group, ...) are dropped; only poison-generating metadata is kept.
define i32 @preserve_load_metadata_after_select_transform_range(i1 %c, ptr dereferenceable(8) %a, ptr dereferenceable(8) %b) {
; CHECK-LABEL: define i32 @preserve_load_metadata_after_select_transform_range(
; CHECK-SAME: i1 [[C:%.*]], ptr dereferenceable(8) [[A:%.*]], ptr dereferenceable(8) [[B:%.*]]) {
; CHECK-NEXT: [[ENTRY:.*:]]
-; CHECK-NEXT: [[B_VAL:%.*]] = load i32, ptr [[B]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !range [[RNG11:![0-9]+]], !invariant.load [[META6]], !llvm.access.group [[META6]], !noundef [[META6]]
-; CHECK-NEXT: [[A_VAL:%.*]] = load i32, ptr [[A]], align 1, !tbaa [[SCALAR_TYPE_TBAA0]], !range [[RNG11]], !invariant.load [[META6]], !llvm.access.group [[META6]], !noundef [[META6]]
+; CHECK-NEXT: [[B_VAL:%.*]] = load i32, ptr [[B]], align 1, !range [[RNG11:![0-9]+]]
+; CHECK-NEXT: [[A_VAL:%.*]] = load i32, ptr [[A]], align 1, !range [[RNG11]]
; CHECK-NEXT: [[L_SEL:%.*]] = select i1 [[C]], i32 [[B_VAL]], i32 [[A_VAL]]
; CHECK-NEXT: ret i32 [[L_SEL]]
;
More information about the llvm-commits
mailing list