Repository: llvm/llvm-project
Pull request #129776: [InstCombine] Enable select freeze poison folding when storing value
Author: login: jmciver
Labels: name: llvm:instcombine
description: Covers the InstCombine, InstSimplify and AggressiveInstCombine passes
name: llvm:ir
name: llvm:transforms
Created: 2025-03-04T21:21:37Z | Merged: 2025-03-20T03:48:59Z

The non-freeze poison argument to select can be one of the following: global,
constant, and noundef arguments.

Alive2 test validation: https://alive2.llvm.org/ce/z/jbtCS6


Diff:
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 4c14dcfb4d75f..d998816a8c2e5 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4812,19 +4812,23 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&](Type *Ty) {
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *V = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
-        C = ConstantInt::getAllOnesValue(Ty);
+        V = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
-        C = ConstantInt::getTrue(Ty);
+        V = ConstantInt::getTrue(Ty);
+      else if (match(U, m_c_Select(m_Specific(&I), m_Value(V)))) {
+        if (!isGuaranteedNotToBeUndefOrPoison(V, &AC, &I, &DT))
+          V = NullValue;
+      }
 
       if (!BestValue)
-        BestValue = C;
-      else if (BestValue != C)
+        BestValue = V;
+      else if (BestValue != V)
         BestValue = NullValue;
     }
     assert(BestValue && "Must have at least one use");
@@ -4840,10 +4844,28 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
     return replaceInstUsesWith(I, getUndefReplacement(I.getType()));
   }
 
+  auto getFreezeVectorReplacement = [](Constant *C) -> Constant * {
+    Type *Ty = C->getType();
+    auto *VTy = dyn_cast<FixedVectorType>(Ty);
+    if (!VTy)
+      return nullptr;
+    unsigned NumElts = VTy->getNumElements();
+    Constant *BestValue = Constant::getNullValue(VTy->getScalarType());
+    for (unsigned i = 0; i != NumElts; ++i) {
+      Constant *EltC = C->getAggregateElement(i);
+      if (EltC && !match(EltC, m_Undef())) {
+        BestValue = EltC;
+        break;
+      }
+    }
+    return Constant::replaceUndefsWith(C, BestValue);
+  };
+
   Constant *C;
-  if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
-    Constant *ReplaceC = getUndefReplacement(I.getType()->getScalarType());
-    return replaceInstUsesWith(I, Constant::replaceUndefsWith(C, ReplaceC));
+  if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement() &&
+      !C->containsConstantExpression()) {
+    if (Constant *Repl = getFreezeVectorReplacement(C))
+      return replaceInstUsesWith(I, Repl);
   }
 
   // Replace uses of Op with freeze(Op).
diff --git a/llvm/test/Transforms/InstCombine/freeze-phi.ll b/llvm/test/Transforms/InstCombine/freeze-phi.ll
index e94af34bf1cd6..cdc9a5efe5933 100644
--- a/llvm/test/Transforms/InstCombine/freeze-phi.ll
+++ b/llvm/test/Transforms/InstCombine/freeze-phi.ll
@@ -53,7 +53,7 @@ define <2 x i32> @vec_undef(i1 %cond) {
 ; CHECK:       B:
 ; CHECK-NEXT:    br label [[C]]
 ; CHECK:       C:
-; CHECK-NEXT:    [[Y:%.*]] = phi <2 x i32> [ <i32 0, i32 1>, [[A]] ], [ <i32 2, i32 0>, [[B]] ]
+; CHECK-NEXT:    [[Y:%.*]] = phi <2 x i32> [ <i32 0, i32 1>, [[A]] ], [ splat (i32 2), [[B]] ]
 ; CHECK-NEXT:    ret <2 x i32> [[Y]]
 ;
   br i1 %cond, label %A, label %B
diff --git a/llvm/test/Transforms/InstCombine/freeze.ll b/llvm/test/Transforms/InstCombine/freeze.ll
index 23785eba05554..8875ce1c566f3 100644
--- a/llvm/test/Transforms/InstCombine/freeze.ll
+++ b/llvm/test/Transforms/InstCombine/freeze.ll
@@ -90,7 +90,7 @@ define void @or_select_multipleuses_logical(i32 %x, i1 %y) {
 
 define <3 x i4> @partial_undef_vec() {
 ; CHECK-LABEL: @partial_undef_vec(
-; CHECK-NEXT:    ret <3 x i4> <i4 0, i4 1, i4 0>
+; CHECK-NEXT:    ret <3 x i4> splat (i4 1)
 ;
   %f = freeze <3 x i4> <i4 poison, i4 1, i4 undef>
   ret <3 x i4> %f
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index cc25f4ce24d9a..2078b795817f8 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -2652,8 +2652,7 @@ define <2 x i8> @partial_cond_freeze_constant_true_val_vec(<2 x i8> %x) {
 
 define <2 x i8> @partial_cond_freeze_constant_false_val_vec(<2 x i8> %x) {
 ; CHECK-LABEL: @partial_cond_freeze_constant_false_val_vec(
-; CHECK-NEXT:    [[S1:%.*]] = insertelement <2 x i8> [[X:%.*]], i8 2, i64 1
-; CHECK-NEXT:    ret <2 x i8> [[S1]]
+; CHECK-NEXT:    ret <2 x i8> [[X:%.*]]
 ;
   %cond.fr = freeze <2 x i1> <i1 true, i1 undef>
   %s = select <2 x i1> %cond.fr, <2 x i8> %x, <2 x i8> <i8 1, i8 2>
@@ -2662,7 +2661,7 @@ define <2 x i8> @partial_cond_freeze_constant_false_val_vec(<2 x i8> %x) {
 
 define <2 x i8> @partial_cond_freeze_both_arms_constant_vec() {
 ; CHECK-LABEL: @partial_cond_freeze_both_arms_constant_vec(
-; CHECK-NEXT:    ret <2 x i8> <i8 42, i8 2>
+; CHECK-NEXT:    ret <2 x i8> <i8 42, i8 43>
 ;
   %cond.fr = freeze <2 x i1> <i1 false, i1 undef>
   %s = select <2 x i1> %cond.fr, <2 x i8> <i8 1, i8 2>, <2 x i8> <i8 42, i8 43>
@@ -4904,8 +4903,7 @@ define i32 @src_simplify_2x_at_once_and(i32 %x, i32 %y) {
 
 define void @select_freeze_poison_parameter(ptr noundef %addr.src, ptr %addr.tgt, i1 %cond) {
 ; CHECK-LABEL: @select_freeze_poison_parameter(
-; CHECK-NEXT:    [[ADDR_SRC:%.*]] = select i1 [[COND:%.*]], ptr [[ADDR_SRC1:%.*]], ptr null
-; CHECK-NEXT:    store ptr [[ADDR_SRC]], ptr [[ADDR_TGT:%.*]], align 8
+; CHECK-NEXT:    store ptr [[ADDR_SRC:%.*]], ptr [[ADDR_TGT:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %freeze = freeze ptr poison
@@ -4914,12 +4912,47 @@ define void @select_freeze_poison_parameter(ptr noundef %addr.src, ptr %addr.tgt
   ret void
 }
 
+define i8 @select_freeze_poison_different_parameters(i8 noundef %x, i8 noundef %y, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: @select_freeze_poison_different_parameters(
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[COND1:%.*]], i8 [[X:%.*]], i8 0
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[COND2:%.*]], i8 [[Y:%.*]], i8 0
+; CHECK-NEXT:    [[CONJ:%.*]] = and i8 [[SEL1]], [[SEL2]]
+; CHECK-NEXT:    ret i8 [[CONJ]]
+;
+  %freeze = freeze i8 poison
+  %sel1 = select i1 %cond1, i8 %x, i8 %freeze
+  %sel2 = select i1 %cond2, i8 %y, i8 %freeze
+  %conj = and i8 %sel1, %sel2
+  ret i8 %conj
+}
+
+define i8 @select_or_freeze_poison_parameter(i8 noundef %x, i1 %cond1) {
+; CHECK-LABEL: @select_or_freeze_poison_parameter(
+; CHECK-NEXT:    ret i8 -1
+;
+  %freeze = freeze i8 poison
+  %sel1 = select i1 %cond1, i8 -1, i8 %freeze
+  %or1 = or i8 %x, %freeze
+  %conj = and i8 %sel1, %or1
+  ret i8 %conj
+}
+
+define i8 @selects_freeze_poison_parameter(i8 noundef %x, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: @selects_freeze_poison_parameter(
+; CHECK-NEXT:    ret i8 [[X:%.*]]
+;
+  %freeze = freeze i8 poison
+  %sel1 = select i1 %cond1, i8 %x, i8 %freeze
+  %sel2 = select i1 %cond2, i8 %x, i8 %freeze
+  %conj = and i8 %sel1, %sel2
+  ret i8 %conj
+}
+
 @glb = global ptr null
 
 define void @select_freeze_poison_global(ptr %addr.tgt, i1 %cond) {
 ; CHECK-LABEL: @select_freeze_poison_global(
-; CHECK-NEXT:    [[SELECT_ADDR:%.*]] = select i1 [[COND:%.*]], ptr @glb, ptr null
-; CHECK-NEXT:    store ptr [[SELECT_ADDR]], ptr [[ADDR_TGT:%.*]], align 8
+; CHECK-NEXT:    store ptr @glb, ptr [[ADDR_TGT:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %freeze = freeze ptr poison
@@ -4930,8 +4963,7 @@ define void @select_freeze_poison_global(ptr %addr.tgt, i1 %cond) {
 
 define void @select_freeze_poison_constant(ptr %addr.tgt, i1 %cond) {
 ; CHECK-LABEL: @select_freeze_poison_constant(
-; CHECK-NEXT:    [[SELECT_ADDR:%.*]] = select i1 [[COND:%.*]], i32 72, i32 0
-; CHECK-NEXT:    store i32 [[SELECT_ADDR]], ptr [[ADDR_TGT:%.*]], align 4
+; CHECK-NEXT:    store i32 72, ptr [[ADDR_TGT:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %freeze = freeze i32 poison
@@ -4939,3 +4971,80 @@ define void @select_freeze_poison_constant(ptr %addr.tgt, i1 %cond) {
   store i32 %select.addr, ptr %addr.tgt
   ret void
 }
+
+define <2 x i8> @select_freeze_poison_mask_vector(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_poison_mask_vector(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> zeroinitializer
+; CHECK-NEXT:    ret <2 x i8> [[SEL]]
+;
+  %freeze = freeze <2 x i8> <i8 0, i8 poison>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+define <2 x i8> @selects_freeze_poison_mask_vector(<2 x i8> noundef %x, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: @selects_freeze_poison_mask_vector(
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND1:%.*]], i1 [[COND2:%.*]], i1 false
+; CHECK-NEXT:    [[X:%.*]] = select i1 [[TMP1]], <2 x i8> [[X1:%.*]], <2 x i8> zeroinitializer
+; CHECK-NEXT:    ret <2 x i8> [[X]]
+;
+  %freeze = freeze <2 x i8> <i8 0, i8 poison>
+  %sel1 = select i1 %cond1, <2 x i8> %x, <2 x i8> %freeze
+  %sel2 = select i1 %cond2, <2 x i8> %x, <2 x i8> %freeze
+  %conj = and <2 x i8> %sel1, %sel2
+  ret <2 x i8> %conj
+}
+
+define <2 x i8> @select_freeze_poison_splat_vector(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_poison_splat_vector(
+; CHECK-NEXT:    ret <2 x i8> [[Y:%.*]]
+;
+  %freeze = freeze <2 x i8> <i8 poison, i8 poison>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+define <2 x i8> @selects_freeze_poison_splat_vector(<2 x i8> noundef %x, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: @selects_freeze_poison_splat_vector(
+; CHECK-NEXT:    ret <2 x i8> [[X:%.*]]
+;
+  %freeze = freeze <2 x i8> <i8 poison, i8 poison>
+  %sel1 = select i1 %cond1, <2 x i8> %x, <2 x i8> %freeze
+  %sel2 = select i1 %cond2, <2 x i8> %x, <2 x i8> %freeze
+  %conj = and <2 x i8> %sel1, %sel2
+  ret <2 x i8> %conj
+}
+
+define <2 x i8> @select_freeze_constant_vector(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_constant_vector(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> zeroinitializer
+; CHECK-NEXT:    ret <2 x i8> [[SEL]]
+;
+  %freeze = freeze <2 x i8> <i8 0, i8 0>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+define <2 x i8> @select_freeze_constant_expression_vector_add(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_constant_expression_vector_add(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> splat (i8 3)
+; CHECK-NEXT:    ret <2 x i8> [[SEL]]
+;
+  %freeze = freeze <2 x i8> <i8 poison, i8 add (i8 1, i8 2)>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+%struct.1 = type {i32, i32}
+@glb.struct.1 = global %struct.1 {i32 1, i32 2}
+
+define <2 x ptr> @select_freeze_constant_expression_vector_gep(i1 %cond, <2 x ptr> noundef %y) {
+; CHECK-LABEL: @select_freeze_constant_expression_vector_gep(
+; CHECK-NEXT:    [[FREEZE:%.*]] = freeze <2 x ptr> <ptr poison, ptr getelementptr inbounds nuw (i8, ptr @glb.struct.1, i64 800)>
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x ptr> [[Y:%.*]], <2 x ptr> [[FREEZE]]
+; CHECK-NEXT:    ret <2 x ptr> [[SEL]]
+;
+  %freeze = freeze <2 x ptr> <ptr poison, ptr getelementptr inbounds (%struct.1, ptr @glb.struct.1, i64 100)>
+  %sel = select i1 %cond, <2 x ptr> %y, <2 x ptr> %freeze
+  ret <2 x ptr> %sel
+}


Reviews:
user: login: dtcxzyw
body: We can do this fold in InstSimplify: https://alive2.llvm.org/ce/z/Dm53TP

However, we should wait for the following things before working on more simplifications with `freeze poison`:
1. Remove `freeze poison -> null` canonicalization in InstCombine
2. Replace existing undef handling logic with `freeze poison`

See also https://github.com/llvm/llvm-project/pull/119884#issuecomment-2548042978

state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-05T06:39:23Z
user: login: dtcxzyw
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T04:35:55Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T05:02:32Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-06T05:02:40Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T04:00:45Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-07T04:14:17Z
user: login: dtcxzyw
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-07T05:14:59Z
user: login: dtcxzyw
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-03-07T05:22:12Z
user: login: dtcxzyw
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-07T05:56:17Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T05:18:02Z
user: login: dtcxzyw
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-08T07:36:33Z
user: login: nikic
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-08T07:44:32Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T05:27:33Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T05:07:39Z
user: login: dtcxzyw
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-10T06:51:34Z
user: login: nikic
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T09:04:51Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T04:34:46Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T04:34:50Z
user: login: dtcxzyw
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-11T05:05:16Z
user: login: nikic
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T09:31:55Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T05:37:50Z
user: login: dtcxzyw
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-19T13:09:03Z
user: login: nikic
body: Some nits, but the general approach looks good to me now.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T14:55:14Z
user: login: nikic
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T21:07:16Z
user: login: nikic
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T21:08:50Z
user: login: jmciver
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:26:29Z
user: login: nikic
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T22:39:58Z
user: login: dtcxzyw
body: LG
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-20T01:09:26Z

Review comments:
diff_hunk: @@ -3124,6 +3124,19 @@ inline auto m_c_LogicalOp(const LHS &L, const RHS &R) {
   return m_LogicalOp<LHS, RHS, /*Commutable=*/true>(L, R);
 }
 
+struct GuaranteedNotToBeUndefOrPoison_match {
+  template <typename ITy> bool match(ITy *V) {
+    if (auto *AsValue = dyn_cast<Value>(V))
+      return isGuaranteedNotToBeUndefOrPoison(AsValue);
path: llvm/include/llvm/IR/PatternMatch.h
user: login: dtcxzyw
body: I don't like this helper. Some context information (e.g., AC/DT/CxtI) is available in InstCombine.
They are useful to get a more precise analysis result.
created_at: 2025-03-06T04:27:10Z
author_association: MEMBER
diff_hunk: @@ -4813,15 +4813,22 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
   auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
-
+      else if (I.hasOneUse() &&
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: If `I` has only one use, it must have only one user. I would like to hoist this logic out of the loop.
```
if (I.hasOneUse() && match(I->user_back(), m_c_Select(m_Specific(&I), m_Value(Arm)) && isGuaranteedNotToBeUndefOrPoison(Arm, &AC, &DT, &I))
  return Arm;

// existing code
Constant *BestValue = nullptr;
Constant *NullValue = Constant::getNullValue(Ty);
...
```

created_at: 2025-03-06T04:35:42Z
author_association: MEMBER
diff_hunk: @@ -3124,6 +3124,19 @@ inline auto m_c_LogicalOp(const LHS &L, const RHS &R) {
   return m_LogicalOp<LHS, RHS, /*Commutable=*/true>(L, R);
 }
 
+struct GuaranteedNotToBeUndefOrPoison_match {
+  template <typename ITy> bool match(ITy *V) {
+    if (auto *AsValue = dyn_cast<Value>(V))
+      return isGuaranteedNotToBeUndefOrPoison(AsValue);
path: llvm/include/llvm/IR/PatternMatch.h
user: login: jmciver
body: I'll fix the helper to take caller context.
created_at: 2025-03-06T05:02:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4813,15 +4813,22 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
   auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
-
+      else if (I.hasOneUse() &&
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: Good point! I'll fix this as well.
created_at: 2025-03-06T05:02:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -3124,6 +3124,19 @@ inline auto m_c_LogicalOp(const LHS &L, const RHS &R) {
   return m_LogicalOp<LHS, RHS, /*Commutable=*/true>(L, R);
 }
 
+struct GuaranteedNotToBeUndefOrPoison_match {
+  template <typename ITy> bool match(ITy *V) {
+    if (auto *AsValue = dyn_cast<Value>(V))
+      return isGuaranteedNotToBeUndefOrPoison(AsValue);
path: llvm/include/llvm/IR/PatternMatch.h
user: login: jmciver
body: I ended up removing the helper.
created_at: 2025-03-07T04:00:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4813,15 +4813,22 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
   auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
-
+      else if (I.hasOneUse() &&
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: Updated as per your recommendations. 
created_at: 2025-03-07T04:14:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,16 +4812,21 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *SelectArgument = nullptr;
+    if (I.hasOneUse() &&
+        match(I.user_back(),
+              m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+        isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+      return SelectArgument;
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: ```suggestion
    Constant *BestValue = nullptr;
    Constant *NullValue = Constant::getNullValue(Ty);
```
created_at: 2025-03-07T05:12:17Z
author_association: MEMBER
diff_hunk: @@ -4812,16 +4812,21 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *SelectArgument = nullptr;
+    if (I.hasOneUse() &&
+        match(I.user_back(),
+              m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+        isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+      return SelectArgument;
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: ```suggestion
      Constant *C = NullValue;
```
created_at: 2025-03-07T05:12:23Z
author_association: MEMBER
diff_hunk: @@ -4812,16 +4812,21 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *SelectArgument = nullptr;
+    if (I.hasOneUse() &&
+        match(I.user_back(),
+              m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+        isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+      return SelectArgument;
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
-
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: Please drop unnecessary changes.
created_at: 2025-03-07T05:12:44Z
author_association: MEMBER
diff_hunk: @@ -4842,8 +4847,11 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
 
   Constant *C;
   if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: Can you add some vector tests?
```
define <2 x i8> @src(i1 %cond, <2 x i8> noundef %y) {
  %undef = freeze <2 x i8> <i8 0, i8 poison>
  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %undef
  ret <2 x i8> %sel
}

define <2 x i8> @tgt(i1 %cond, <2 x i8> noundef %y) {
  ret <2 x i8> %y
}
```
It is a miscompilation: https://alive2.llvm.org/ce/z/8Q3XYH

created_at: 2025-03-07T05:22:07Z
author_association: MEMBER
diff_hunk: @@ -4842,8 +4847,11 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
 
   Constant *C;
   if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: BTW, as it is not suitable to handle this fold under `getUndefReplacement`, can we move the logic into `InstCombinerImpl::visitSelect`?
```
Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
...
    if (match(TrueVal, m_OneUse(m_Freeze(m_Poison()))) &&
        isGuaranteedNotToBeUndefOrPoison(FalseVal, &AC, &I, &DT))
      return replaceInstUsesWith(FalseVal);
    if (match(FalseVal, m_OneUse(m_Freeze(m_Poison()))) &&
        isGuaranteedNotToBeUndefOrPoison(TrueVal, &AC, &I, &DT))
      return replaceInstUsesWith(TrueVal);
```

created_at: 2025-03-07T05:56:17Z
author_association: MEMBER
diff_hunk: @@ -4842,8 +4847,11 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
 
   Constant *C;
   if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: Thanks for pointing out the miscompilation!

The reason I did not place this logic into `InstCombinerImpl::visitSelect` was the canonical form produced by `InstCombinerImpl::visitFreeze` occurs first and thus ruins the match on freeze poison when visitation is performed on the select instruction.

I experimented a bit and derived a two match expression in the lambda function.

Added basic vector tests. Alive2 validation: https://alive2.llvm.org/ce/z/YFA-5S
created_at: 2025-03-08T05:18:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4842,8 +4847,11 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
 
   Constant *C;
   if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: > the canonical form produced by InstCombinerImpl::visitFreeze occurs first and thus ruins the match on freeze poison when visitation is performed on the select instruction.

Should we remove these canonicalizations first?


created_at: 2025-03-08T07:36:33Z
author_association: MEMBER
diff_hunk: @@ -4812,7 +4812,14 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
+  auto getUndefReplacement = [&I, &AC = this->AC,
+                              &DT = this->DT](Type *Ty) -> Value * {
+    Value *SelectArgument = nullptr;
+    if (match(&I, m_OneUse(m_Freeze(m_Poison()))) &&
+        match(I.user_back(),
+              m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+        isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+      return SelectArgument;
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: Why is the select being handled separately from the rest? It's not particularly likely that this happens, but conceptually we should handle this exactly the same as the rest, by trying to find the best replacement for all users. This might be multiple selects with the same "other" value. This will also generalize to other patterns where we need to replace with a value rather than constant (say icmp eq/ne, where we'd also want to replace with the other op).
created_at: 2025-03-08T07:44:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,7 +4812,14 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
+  auto getUndefReplacement = [&I, &AC = this->AC,
+                              &DT = this->DT](Type *Ty) -> Value * {
+    Value *SelectArgument = nullptr;
+    if (match(&I, m_OneUse(m_Freeze(m_Poison()))) &&
+        match(I.user_back(),
+              m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+        isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+      return SelectArgument;
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: The transformation was based on a single use of freeze poison so we hoisted it out of the use analysis loop.

To verify we would want to be able to do something like the following?
https://alive2.llvm.org/ce/z/pyGVuw
created_at: 2025-03-09T05:27:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,7 +4812,14 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
+  auto getUndefReplacement = [&I, &AC = this->AC,
+                              &DT = this->DT](Type *Ty) -> Value * {
+    Value *SelectArgument = nullptr;
+    if (match(&I, m_OneUse(m_Freeze(m_Poison()))) &&
+        match(I.user_back(),
+              m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+        isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+      return SelectArgument;
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: @nikic I moved the select testing back into the analysis loop. It does develop the best replacement for the Alive2 test case based on the previous post: https://alive2.llvm.org/ce/z/pyGVuw.

Does this match what you are suggesting?
created_at: 2025-03-10T05:07:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: I don't really like to put the logic inside this lambda function. IIRC `getUndefReplacement` returns a **scalar** constant to fill in "holes" in a constant vector.

created_at: 2025-03-10T06:32:26Z
author_association: MEMBER
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
+      else if (match(&I, m_Freeze(m_Poison())) &&
+               match(U, m_c_Select(m_Specific(&I), m_Value(C)))) {
+        if (!isGuaranteedNotToBeUndefOrPoison(C, &AC, &I, &DT))
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: We can sink this check (without context information) after picking out a common replacement.

created_at: 2025-03-10T06:51:21Z
author_association: MEMBER
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: Not really. The *primary* purpose is to replace a freeze(poison) fully, see the `if (match(Op0, m_Undef()))` branch below. However, it is *also* used to handle the case where we have something like `freeze <i8 0, i8 poison>`, where we're only replacing elements.

TBH I find it somewhat doubtful to use the same helper for both. I think it would make more sense to separate out the vector handling and make that one just fill the gaps with the first non-undef element. I think creating a vector splat is going to be more useful than creating a a mixed vector constant where some elements have beneficial values for some operation.

Also, I think that code is currently incorrect because it fails to handle potentially poisonous constant expressions.

Maybe we should clean up that code first.
created_at: 2025-03-10T09:04:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: From a high level should my implementation plan be?

1. Simplify getUndefReplacement helper to only handle select scalars
2. Implement poisonous constant expressions
3. Implement vector splat generation
created_at: 2025-03-11T04:34:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
+      else if (match(&I, m_Freeze(m_Poison())) &&
+               match(U, m_c_Select(m_Specific(&I), m_Value(C)))) {
+        if (!isGuaranteedNotToBeUndefOrPoison(C, &AC, &I, &DT))
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: I believe we need `isGuaranteedNotToBeUndefOrPoison` to check each of the select instructions otherwise we might incorrectly pick the replacement, i.e., is the select argument nonundef.

This is what I am thinking:
```c++
auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
    Value *BestValue = nullptr;
    Value *NullValue = Constant::getNullValue(Ty);
    for (const auto *U : I.users()) {
      Value *C = NullValue;
      Value *SelectArgument = nullptr;
      if (match(U, m_Or(m_Value(), m_Value())))
        C = ConstantInt::getAllOnesValue(Ty);
      else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
        C = ConstantInt::getTrue(Ty);
      else if (match(&I, m_Freeze(m_Poison())) &&
               match(U, m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
               isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
        C = SelectArgument;

      if (!BestValue)
        BestValue = C;
      else if (BestValue != C)
        BestValue = NullValue;
    }
    assert(BestValue && "Must have at least one use");
    return BestValue;
  };
```
Let me know if I am misinterpreting your suggestion.

created_at: 2025-03-11T04:34:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
+      else if (match(&I, m_Freeze(m_Poison())) &&
+               match(U, m_c_Select(m_Specific(&I), m_Value(C)))) {
+        if (!isGuaranteedNotToBeUndefOrPoison(C, &AC, &I, &DT))
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: See the replacement selection logic below:
```
if (!BestValue)
  BestValue = C;
else if (BestValue != C)
  BestValue = NullValue;
```
It guarantees that all the replacements are the same.

This is what I have in mind:
```
auto getScalarUndefReplacement = ...;
Constant *C;
if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
  Constant *ReplaceC = getScalarUndefReplacement(I.getType()->getScalarType());
  return replaceInstUsesWith(I, Constant::replaceUndefsWith(C, ReplaceC));
}

auto getUndefReplacement = [&I] {
    Value *BestValue = nullptr;
    for (const auto *U : I.users()) {
      Value *C = nullptr;
      if (!(match(&I, m_Freeze(m_Poison())) &&
               match(U, m_c_Select(m_Specific(&I), m_Value(C))))
        return nullptr;

      if (!BestValue)
        BestValue = C;
      else if (BestValue != C)
        return nullptr;
    }
    return BestValue;
  };
if (auto *Rep = getUndefReplacement())
  if (isGuaranteedNotToBeUndefOrPoison(Rep))
    return replaceInstUsesWith(I, Rep);
```

created_at: 2025-03-11T05:05:16Z
author_association: MEMBER
diff_hunk: @@ -4831,18 +4831,38 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
     return BestValue;
   };
 
+  auto getValueReplacement = [&I, &AC = this->AC, &DT = this->DT]() -> Value * {
+    Value *BestValue = nullptr;
+    for (const auto *U : I.users()) {
+      Value *C = nullptr;
+      Value *SelectArgument = nullptr;
+      if (match(U, m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+          isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+        C = SelectArgument;
+
+      if (!BestValue)
+        BestValue = C;
+      else if (BestValue != C)
+        return nullptr;
+    }
+    return BestValue;
+  };
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: Splitting the value and constant cases is also not ideal, because values can be constants. So e.g. if we have a `select i1 %c, i32 -1, i32 %freeze` and a `or i32 %freeze, %y` then the best choice would be to use `-1`, which allows both the select and the or to fold. We'd do that if both cases are combined, but not if they are separated.
created_at: 2025-03-13T09:31:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4831,18 +4831,38 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
     return BestValue;
   };
 
+  auto getValueReplacement = [&I, &AC = this->AC, &DT = this->DT]() -> Value * {
+    Value *BestValue = nullptr;
+    for (const auto *U : I.users()) {
+      Value *C = nullptr;
+      Value *SelectArgument = nullptr;
+      if (match(U, m_c_Select(m_Specific(&I), m_Value(SelectArgument))) &&
+          isGuaranteedNotToBeUndefOrPoison(SelectArgument, &AC, &I, &DT))
+        C = SelectArgument;
+
+      if (!BestValue)
+        BestValue = C;
+      else if (BestValue != C)
+        return nullptr;
+    }
+    return BestValue;
+  };
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: Moved value and constant cases back together.
created_at: 2025-03-14T05:37:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4840,10 +4845,27 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
     return replaceInstUsesWith(I, getUndefReplacement(I.getType()));
   }
 
+  auto getFreezeVectorReplacement = [](Constant *C) -> Constant * {
+    Type *Ty = C->getType();
+    auto *VTy = dyn_cast<FixedVectorType>(Ty);
+    if (!VTy)
+      return nullptr;
+    unsigned NumElts = VTy->getNumElements();
+    Constant *BestValue = Constant::getNullValue(VTy->getScalarType());
+    for (unsigned i = 0; i != NumElts; ++i) {
+      Constant *EltC = C->getAggregateElement(i);
+      if (EltC && !match(EltC, m_Undef())) {
+        BestValue = EltC;
+        break;
+      }
+    }
+    return Constant::replaceUndefsWith(C, BestValue);
+  };
+
   Constant *C;
   if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
-    Constant *ReplaceC = getUndefReplacement(I.getType()->getScalarType());
-    return replaceInstUsesWith(I, Constant::replaceUndefsWith(C, ReplaceC));
+    if (auto Repl = getFreezeVectorReplacement(C))
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: dtcxzyw
body: ```suggestion
    if (Constant *Repl = getFreezeVectorReplacement(C))
```
created_at: 2025-03-19T13:08:07Z
author_association: MEMBER
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: ```suggestion
  auto getUndefReplacement = [&](Type *Ty) {
```
created_at: 2025-03-19T14:50:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: This was C for Constant -- should adjust the name.
created_at: 2025-03-19T14:51:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
+      else if (match(&I, m_Freeze(m_Poison())) &&
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: Shouldn't need this check? All of this is under the assumption of freeze(undef/poison).
created_at: 2025-03-19T14:52:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4840,10 +4845,27 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
     return replaceInstUsesWith(I, getUndefReplacement(I.getType()));
   }
 
+  auto getFreezeVectorReplacement = [](Constant *C) -> Constant * {
+    Type *Ty = C->getType();
+    auto *VTy = dyn_cast<FixedVectorType>(Ty);
+    if (!VTy)
+      return nullptr;
+    unsigned NumElts = VTy->getNumElements();
+    Constant *BestValue = Constant::getNullValue(VTy->getScalarType());
+    for (unsigned i = 0; i != NumElts; ++i) {
+      Constant *EltC = C->getAggregateElement(i);
+      if (EltC && !match(EltC, m_Undef())) {
+        BestValue = EltC;
+        break;
+      }
+    }
+    return Constant::replaceUndefsWith(C, BestValue);
+  };
+
   Constant *C;
   if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: Should also check !containsConstantExpression() here, otherwise this fold is incorrect.
created_at: 2025-03-19T14:54:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: nikic
body: What I meant here is to use `[&]` to capture everything instead of listing all the variables. You can still use AC and DT if you do that.
created_at: 2025-03-19T21:07:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4930,12 +4963,88 @@ define void @select_freeze_poison_global(ptr %addr.tgt, i1 %cond) {
 
 define void @select_freeze_poison_constant(ptr %addr.tgt, i1 %cond) {
 ; CHECK-LABEL: @select_freeze_poison_constant(
-; CHECK-NEXT:    [[SELECT_ADDR:%.*]] = select i1 [[COND:%.*]], i32 72, i32 0
-; CHECK-NEXT:    store i32 [[SELECT_ADDR]], ptr [[ADDR_TGT:%.*]], align 4
+; CHECK-NEXT:    store i32 72, ptr [[ADDR_TGT:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %freeze = freeze i32 poison
   %select.addr = select i1 %cond, i32 72, i32 %freeze
   store i32 %select.addr, ptr %addr.tgt
   ret void
 }
+
+define <2 x i8> @select_freeze_poison_mask_vector(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_poison_mask_vector(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> zeroinitializer
+; CHECK-NEXT:    ret <2 x i8> [[SEL]]
+;
+  %freeze = freeze <2 x i8> <i8 0, i8 poison>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+define <2 x i8> @selects_freeze_poison_mask_vector(<2 x i8> noundef %x, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: @selects_freeze_poison_mask_vector(
+; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[COND1:%.*]], i1 [[COND2:%.*]], i1 false
+; CHECK-NEXT:    [[X:%.*]] = select i1 [[TMP1]], <2 x i8> [[X1:%.*]], <2 x i8> zeroinitializer
+; CHECK-NEXT:    ret <2 x i8> [[X]]
+;
+  %freeze = freeze <2 x i8> <i8 0, i8 poison>
+  %sel1 = select i1 %cond1, <2 x i8> %x, <2 x i8> %freeze
+  %sel2 = select i1 %cond2, <2 x i8> %x, <2 x i8> %freeze
+  %conj = and <2 x i8> %sel1, %sel2
+  ret <2 x i8> %conj
+}
+
+define <2 x i8> @select_freeze_poison_splat_vector(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_poison_splat_vector(
+; CHECK-NEXT:    ret <2 x i8> [[Y:%.*]]
+;
+  %freeze = freeze <2 x i8> <i8 poison, i8 poison>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+define <2 x i8> @selects_freeze_poison_splat_vector(<2 x i8> noundef %x, i1 %cond1, i1 %cond2) {
+; CHECK-LABEL: @selects_freeze_poison_splat_vector(
+; CHECK-NEXT:    ret <2 x i8> [[X:%.*]]
+;
+  %freeze = freeze <2 x i8> <i8 poison, i8 poison>
+  %sel1 = select i1 %cond1, <2 x i8> %x, <2 x i8> %freeze
+  %sel2 = select i1 %cond2, <2 x i8> %x, <2 x i8> %freeze
+  %conj = and <2 x i8> %sel1, %sel2
+  ret <2 x i8> %conj
+}
+
+define <2 x i8> @select_freeze_constant_vector(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_constant_vector(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> zeroinitializer
+; CHECK-NEXT:    ret <2 x i8> [[SEL]]
+;
+  %freeze = freeze <2 x i8> <i8 0, i8 0>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+define <2 x i8> @select_freeze_constant_expression_vector_add(i1 %cond, <2 x i8> noundef %y) {
+; CHECK-LABEL: @select_freeze_constant_expression_vector_add(
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> splat (i8 3)
+; CHECK-NEXT:    ret <2 x i8> [[SEL]]
+;
+  %freeze = freeze <2 x i8> <i8 poison, i8 add (i8 1, i8 2)>
+  %sel = select i1 %cond, <2 x i8> %y, <2 x i8> %freeze
+  ret <2 x i8> %sel
+}
+
+%struct.1 = type {i32, i32}
+@glb.struct.1 = global %struct.1 {i32 1, i32 2}
+
+define <2 x ptr> @select_freeze_constant_expression_vector_gep(i1 %cond, <2 x ptr> noundef %y) {
+; CHECK-LABEL: @select_freeze_constant_expression_vector_gep(
+; CHECK-NEXT:    [[FREEZE:%.*]] = freeze <2 x ptr> <ptr poison, ptr getelementptr inbounds nuw (i8, ptr @glb.struct.1, i64 4)>
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[COND:%.*]], <2 x ptr> [[Y:%.*]], <2 x ptr> [[FREEZE]]
+; CHECK-NEXT:    ret <2 x ptr> [[SEL]]
+;
+  %freeze = freeze <2 x ptr> <ptr poison, ptr getelementptr (%struct.1, ptr @glb.struct.1, i64 0, i32 1)>
path: llvm/test/Transforms/InstCombine/select.ll
user: login: nikic
body: ```suggestion
  %freeze = freeze <2 x ptr> <ptr poison, ptr getelementptr inbounds (%struct.1, ptr @glb.struct.1, i64 100)>
```
I'd do something like this, to make the constant expression indirectly poison.
created_at: 2025-03-19T21:08:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -4812,15 +4812,20 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   //
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
-  auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+  auto getUndefReplacement = [&I, &AC = this->AC, &DT = this->DT](Type *Ty) {
path: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
user: login: jmciver
body: My mistake; I will fix that.
created_at: 2025-03-19T22:26:29Z
author_association: CONTRIBUTOR

PR comments:
user: login: jmciver
created_at: 2025-03-04T21:21:51Z
body: * **#129776** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/129776?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/129776?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* **#129775** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/129775?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: llvmbot
created_at: 2025-03-05T03:52:06Z
body: 
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-ir

Author: John McIver (jmciver)

<details>
<summary>Changes</summary>

The non-freeze poison argument to select can be one of the following: global,
constant, and noundef arguments.

Alive2 test validation: https://alive2.llvm.org/ce/z/jbtCS6

---
Full diff: https://github.com/llvm/llvm-project/pull/129776.diff


3 Files Affected:

- (modified) llvm/include/llvm/IR/PatternMatch.h (+13) 
- (modified) llvm/lib/Transforms/InstCombine/InstructionCombining.cpp (+16-6) 
- (modified) llvm/test/Transforms/InstCombine/select.ll (+3-6) 


``````````diff
diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index b3eeb1d7ba88a..c66235f33cd9e 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -3124,6 +3124,19 @@ inline auto m_c_LogicalOp(const LHS &L, const RHS &R) {
   return m_LogicalOp<LHS, RHS, /*Commutable=*/true>(L, R);
 }
 
+struct GuaranteedNotToBeUndefOrPoison_match {
+  template <typename ITy> bool match(ITy *V) {
+    if (auto *AsValue = dyn_cast<Value>(V))
+      return isGuaranteedNotToBeUndefOrPoison(AsValue);
+    else
+      return false;
+  }
+};
+
+inline GuaranteedNotToBeUndefOrPoison_match m_guaranteedNotToBeUndefOrPoison() {
+  return GuaranteedNotToBeUndefOrPoison_match();
+}
+
 } // end namespace PatternMatch
 } // end namespace llvm
 
diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
index 4c14dcfb4d75f..6b9254692a857 100644
--- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp
@@ -4813,15 +4813,22 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
   // TODO: This could use getBinopAbsorber() / getBinopIdentity() to avoid
   //       duplicating logic for binops at least.
   auto getUndefReplacement = [&I](Type *Ty) {
-    Constant *BestValue = nullptr;
-    Constant *NullValue = Constant::getNullValue(Ty);
+    Value *BestValue = nullptr;
+    Value *NullValue = Constant::getNullValue(Ty);
     for (const auto *U : I.users()) {
-      Constant *C = NullValue;
+      Value *C = NullValue;
       if (match(U, m_Or(m_Value(), m_Value())))
         C = ConstantInt::getAllOnesValue(Ty);
       else if (match(U, m_Select(m_Specific(&I), m_Constant(), m_Value())))
         C = ConstantInt::getTrue(Ty);
-
+      else if (I.hasOneUse() &&
+               match(U, m_c_Select(m_Specific(&I),
+                                   m_guaranteedNotToBeUndefOrPoison()))) {
+        if (match(U->getOperand(1), m_Specific(&I)))
+          C = U->getOperand(2);
+        else
+          C = U->getOperand(1);
+      }
       if (!BestValue)
         BestValue = C;
       else if (BestValue != C)
@@ -4842,8 +4849,11 @@ Instruction *InstCombinerImpl::visitFreeze(FreezeInst &I) {
 
   Constant *C;
   if (match(Op0, m_Constant(C)) && C->containsUndefOrPoisonElement()) {
-    Constant *ReplaceC = getUndefReplacement(I.getType()->getScalarType());
-    return replaceInstUsesWith(I, Constant::replaceUndefsWith(C, ReplaceC));
+    Value *Replace = getUndefReplacement(I.getType()->getScalarType());
+    if (Constant *ReplaceC = dyn_cast<Constant>(Replace))
+      return replaceInstUsesWith(I, Constant::replaceUndefsWith(C, ReplaceC));
+    else
+      return replaceInstUsesWith(I, Replace);
   }
 
   // Replace uses of Op with freeze(Op).
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index cc25f4ce24d9a..789d98197d878 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -4904,8 +4904,7 @@ define i32 @src_simplify_2x_at_once_and(i32 %x, i32 %y) {
 
 define void @select_freeze_poison_parameter(ptr noundef %addr.src, ptr %addr.tgt, i1 %cond) {
 ; CHECK-LABEL: @select_freeze_poison_parameter(
-; CHECK-NEXT:    [[ADDR_SRC:%.*]] = select i1 [[COND:%.*]], ptr [[ADDR_SRC1:%.*]], ptr null
-; CHECK-NEXT:    store ptr [[ADDR_SRC]], ptr [[ADDR_TGT:%.*]], align 8
+; CHECK-NEXT:    store ptr [[ADDR_SRC:%.*]], ptr [[ADDR_TGT:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %freeze = freeze ptr poison
@@ -4918,8 +4917,7 @@ define void @select_freeze_poison_parameter(ptr noundef %addr.src, ptr %addr.tgt
 
 define void @select_freeze_poison_global(ptr %addr.tgt, i1 %cond) {
 ; CHECK-LABEL: @select_freeze_poison_global(
-; CHECK-NEXT:    [[SELECT_ADDR:%.*]] = select i1 [[COND:%.*]], ptr @glb, ptr null
-; CHECK-NEXT:    store ptr [[SELECT_ADDR]], ptr [[ADDR_TGT:%.*]], align 8
+; CHECK-NEXT:    store ptr @glb, ptr [[ADDR_TGT:%.*]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %freeze = freeze ptr poison
@@ -4930,8 +4928,7 @@ define void @select_freeze_poison_global(ptr %addr.tgt, i1 %cond) {
 
 define void @select_freeze_poison_constant(ptr %addr.tgt, i1 %cond) {
 ; CHECK-LABEL: @select_freeze_poison_constant(
-; CHECK-NEXT:    [[SELECT_ADDR:%.*]] = select i1 [[COND:%.*]], i32 72, i32 0
-; CHECK-NEXT:    store i32 [[SELECT_ADDR]], ptr [[ADDR_TGT:%.*]], align 4
+; CHECK-NEXT:    store i32 72, ptr [[ADDR_TGT:%.*]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %freeze = freeze i32 poison

``````````

</details>

author_association: MEMBER
user: login: nikic
created_at: 2025-03-05T16:56:48Z
body: > We can do this fold in InstSimplify: https://alive2.llvm.org/ce/z/Dm53TP

The transform is only valid if the freeze(poison) is one-use. And I don't think that InstSimplify should be doing any use-based checks. So I think InstCombine is the right place for it.
author_association: CONTRIBUTOR
user: login: jmciver
created_at: 2025-03-06T00:16:54Z
body: @nikic and @dtcxzyw thanks for the feedback.

This patch does bypasses the need for freeze poison -> null canonicalization removal in InstCombine. To provide context I am seeing the lack of store of select freeze poison folding in uninitialized memory semantics work that I am doing with @nlopes.

I would like to incorporate this and then once freeze poison -> null canonicalization is removed from InstCombine refactor appropriately. Would this be acceptable?

author_association: CONTRIBUTOR
user: login: dtcxzyw
created_at: 2025-03-06T02:05:17Z
body: > I would like to incorporate this and then once freeze poison -> null canonicalization is removed from InstCombine refactor appropriately. Would this be acceptable?

I don't mean to block this patch. I just worry that these patches may not be well tested (fuzzers/compile-time tracker/llvm-opt-benchmark) until we remove the canonicalization.


author_association: MEMBER
user: login: jmciver
created_at: 2025-03-06T05:11:33Z
body: @dtcxzyw I was not aware I should be using a fuzzer. What tool would you recommend?

From a correctness standpoint the match is relatively narrow and I have discussed with @nlopes extensively. The patch has been tested with bootstrap build of LLVM running all regressions. Additionally I have used the llvm-test-suite for correctness checks.

I have sent a request to @nikita asking for compile-time tracker access and can report results once available.

I also have used the patch in conjunction with 15+ Phoronix benchmarks. The optimization involving the fold of global is something I have seen in our memory semantics work in testing FFTW.
author_association: CONTRIBUTOR
user: login: nunoplopes
created_at: 2025-03-06T09:34:31Z
body: FWIW, we have been using this patch internally and it helps substancial in a couple of benchmarks.
author_association: MEMBER
user: login: jmciver
created_at: 2025-03-07T04:33:21Z
body: Performance benchmarks are queued; I will post once available.
author_association: CONTRIBUTOR
user: login: jmciver
created_at: 2025-03-13T05:51:21Z
body: I refactored the select fold into helper getValueReplacement, which can make use of the freeze argument test in the caller: `match(Op0, m_Undef())`. The isGuaranteedNotToBeUndefOrPoison call is located in the lambda body. Multi-use
freeze instruction are not common so the execution impact should be generally low, but the readability is improved.

author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-13T05:52:02Z
body: <!--LLVM CODE FORMAT COMMENT: {clang-format}-->

:white_check_mark: With the latest revision this PR passed the C/C++ code formatter.
author_association: NONE
user: login: jmciver
created_at: 2025-03-14T05:41:13Z
body: @nikita I refactored the replacement of poison elements in constant vectors to use the first non-poison element. I am not sure if I need to do something for aggregates.

author_association: CONTRIBUTOR
user: login: jmciver
created_at: 2025-03-19T20:53:51Z
body: Update as per reviews. Add constant expression vector tests.
author_association: CONTRIBUTOR
user: login: jmciver
created_at: 2025-03-20T03:44:56Z
body: ### Merge activity

* **Mar 19, 11:44 PM EDT**: A user started a stack merge that includes this pull request via [Graphite](https://app.graphite.dev/github/pr/llvm/llvm-project/129776).
* **Mar 19, 11:47 PM EDT**: [Graphite](https://app.graphite.dev/github/pr/llvm/llvm-project/129776) rebased this pull request as part of a merge.
* **Mar 19, 11:49 PM EDT**: A user merged this pull request with [Graphite](https://app.graphite.dev/github/pr/llvm/llvm-project/129776).

author_association: CONTRIBUTOR
user: login: llvm-ci
created_at: 2025-03-20T03:57:29Z
body: LLVM Buildbot has detected a new failure on builder `openmp-offload-amdgpu-runtime` running on `omp-vega20-0` while building `llvm` at step 7 "Add check check-offload".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/30/builds/18017

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 7 (Add check check-offload) failure: test (failure)
******************** TEST 'libomptarget :: amdgcn-amd-amdhsa :: offloading/gpupgo/pgo1.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp    -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib  -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile      -Xarch_device -fprofile-generate
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile -Xarch_device -fprofile-generate
# note: command had no output on stdout or stderr
# RUN: at line 3
env LLVM_PROFILE_FILE=pgo1.c.llvm.profraw      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp 2>&1
# executed command: env LLVM_PROFILE_FILE=pgo1.c.llvm.profraw /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp
# note: command had no output on stdout or stderr
# RUN: at line 5
llvm-profdata show --all-functions --counts      amdgcn-amd-amdhsa.pgo1.c.llvm.profraw |      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix="LLVM-PGO"
# executed command: llvm-profdata show --all-functions --counts amdgcn-amd-amdhsa.pgo1.c.llvm.profraw
# note: command had no output on stdout or stderr
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix=LLVM-PGO
# note: command had no output on stdout or stderr
# RUN: at line 9
/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp    -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib  -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile      -Xarch_device -fprofile-instr-generate
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fcreate-profile -Xarch_device -fprofile-instr-generate
# note: command had no output on stdout or stderr
# RUN: at line 11
env LLVM_PROFILE_FILE=pgo1.c.clang.profraw      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp 2>&1
# executed command: env LLVM_PROFILE_FILE=pgo1.c.clang.profraw /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo1.c.tmp
# note: command had no output on stdout or stderr
# RUN: at line 13
llvm-profdata show --all-functions --counts      amdgcn-amd-amdhsa.pgo1.c.clang.profraw |      /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix="CLANG-PGO"
# executed command: llvm-profdata show --all-functions --counts amdgcn-amd-amdhsa.pgo1.c.clang.profraw
# note: command had no output on stdout or stderr
# executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c --check-prefix=CLANG-PGO
# .---command stderr------------
# | /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c:63:15: error: CLANG-PGO: expected string not found in input
# | // CLANG-PGO: Block counts: [11, 20]
# |               ^
# | <stdin>:5:19: note: scanning from here
# |  Function count: 0
# |                   ^
# | <stdin>:6:2: note: possible intended match here
# |  Block counts: [12, 20]
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo1.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
...

```

</details>
author_association: NONE
user: login: llvm-ci
created_at: 2025-03-20T04:12:24Z
body: LLVM Buildbot has detected a new failure on builder `openmp-offload-libc-amdgpu-runtime` running on `omp-vega20-1` while building `llvm` at step 7 "Add check check-offload".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/73/builds/14936

<details>
<summary>Here is the relevant piece of the build log for the reference</summary>

```
Step 7 (Add check check-offload) failure: test (failure)
******************** TEST 'libomptarget :: amdgcn-amd-amdhsa :: offloading/gpupgo/pgo2.c' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/clang -fopenmp    -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib  -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c -o /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp -Xoffload-linker -lc -Xoffload-linker -lm /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fprofile-generate
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c -o /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp -Xoffload-linker -lc -Xoffload-linker -lm /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fprofile-generate
# RUN: at line 2
env LLVM_PROFILE_FILE=pgo2.c.llvm.profraw      /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp 2>&1
# executed command: env LLVM_PROFILE_FILE=pgo2.c.llvm.profraw /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp
# RUN: at line 4
llvm-profdata show --all-functions --counts      pgo2.c.llvm.profraw | /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c      --check-prefix="LLVM-HOST"
# executed command: llvm-profdata show --all-functions --counts pgo2.c.llvm.profraw
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c --check-prefix=LLVM-HOST
# RUN: at line 7
llvm-profdata show --all-functions --counts      amdgcn-amd-amdhsa.pgo2.c.llvm.profraw      | /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c --check-prefix="LLVM-DEVICE"
# executed command: llvm-profdata show --all-functions --counts amdgcn-amd-amdhsa.pgo2.c.llvm.profraw
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c --check-prefix=LLVM-DEVICE
# RUN: at line 11
/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/clang -fopenmp    -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src  -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib  -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c -o /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp -Xoffload-linker -lc -Xoffload-linker -lm /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fprofile-instr-generate
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test -I /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -L /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -nogpulib -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -Wl,-rpath,/home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib -fopenmp-targets=amdgcn-amd-amdhsa /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c -o /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp -Xoffload-linker -lc -Xoffload-linker -lm /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./lib/libomptarget.devicertl.a -fprofile-instr-generate
# RUN: at line 12
env LLVM_PROFILE_FILE=pgo2.c.clang.profraw      /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp 2>&1
# executed command: env LLVM_PROFILE_FILE=pgo2.c.clang.profraw /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/offload/test/amdgcn-amd-amdhsa/offloading/gpupgo/Output/pgo2.c.tmp
# RUN: at line 14
llvm-profdata show --all-functions --counts      pgo2.c.clang.profraw | /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c      --check-prefix="CLANG-HOST"
# executed command: llvm-profdata show --all-functions --counts pgo2.c.clang.profraw
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c --check-prefix=CLANG-HOST
# RUN: at line 17
llvm-profdata show --all-functions --counts      amdgcn-amd-amdhsa.pgo2.c.clang.profraw |      /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c --check-prefix="CLANG-DEV"
# executed command: llvm-profdata show --all-functions --counts amdgcn-amd-amdhsa.pgo2.c.clang.profraw
# executed command: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.build/./bin/FileCheck /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c --check-prefix=CLANG-DEV
# .---command stderr------------
# | /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c:101:15: error: CLANG-DEV: expected string not found in input
# | // CLANG-DEV: Block counts: [11]
# |               ^
# | <stdin>:5:19: note: scanning from here
# |  Function count: 0
# |                   ^
# | <stdin>:6:2: note: possible intended match here
# |  Block counts: [14]
# |  ^
# | 
# | Input file: <stdin>
# | Check file: /home/ompworker/bbot/openmp-offload-libc-amdgpu-runtime/llvm.src/offload/test/offloading/gpupgo/pgo2.c
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
...

```

</details>
author_association: NONE