[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