[llvm] ffdc7df - [SimplifyLibCalls] Explicitly annotate memchr selects (#222734)

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 10 11:58:20 PDT 2026


Author: Aiden Grossman
Date: 2026-09-10T11:58:14-07:00
New Revision: ffdc7df10d016542cee44dec8b777161a309bd55

URL: https://github.com/llvm/llvm-project/commit/ffdc7df10d016542cee44dec8b777161a309bd55
DIFF: https://github.com/llvm/llvm-project/commit/ffdc7df10d016542cee44dec8b777161a309bd55.diff

LOG: [SimplifyLibCalls] Explicitly annotate memchr selects (#222734)

SimplifyLibCalls creates a variety of selects when optimizing calls to
memchr. Explicitly annotate them as unknown as the conditions always
depend on a non-constant parameter to memchr with which we cannot infer
probability information without value profile data.

Added: 
    

Modified: 
    llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
    llvm/test/Transforms/InstCombine/memchr-11.ll
    llvm/test/Transforms/InstCombine/memchr-2.ll
    llvm/test/Transforms/InstCombine/memchr-3.ll
    llvm/test/Transforms/InstCombine/memchr-6.ll
    llvm/test/Transforms/InstCombine/memchr.ll
    llvm/utils/profcheck-xfail.txt

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
index f2b8ac606cfc1..5da7349b4d9c9 100644
--- a/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp
@@ -30,6 +30,7 @@
 #include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/PatternMatch.h"
+#include "llvm/IR/ProfDataUtils.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/KnownBits.h"
@@ -45,6 +46,8 @@
 using namespace llvm;
 using namespace PatternMatch;
 
+#define DEBUG_TYPE "simplify-lib-calls"
+
 static cl::opt<bool>
     EnableUnsafeFPShrink("enable-double-float-shrink", cl::Hidden,
                          cl::init(false),
@@ -505,6 +508,10 @@ static Value* memChrToCharCompare(CallInst *CI, Value *NBytes,
     Value *Zero = ConstantInt::get(NBytes->getType(), 0);
     Value *And = B.CreateICmpNE(NBytes, Zero);
     Cmp = B.CreateLogicalAnd(And, Cmp);
+    // The and above is based on the byte count and the query, neither of which
+    // we know without value profiling, so mark the profile as unknown.
+    if (auto *SI = dyn_cast<SelectInst>(Cmp))
+      setExplicitlyUnknownBranchWeightsIfProfiled(*SI, DEBUG_TYPE);
   }
 
   Value *NullPtr = Constant::getNullValue(CI->getType());
@@ -1362,7 +1369,11 @@ Value *LibCallSimplifier::optimizeMemChr(CallInst *CI, IRBuilderBase &B) {
       // Slice off the character's high end bits.
       CharVal = B.CreateTrunc(CharVal, B.getInt8Ty());
       Value *Cmp = B.CreateICmpEQ(Val, CharVal, "memchr.char0cmp");
-      return B.CreateSelect(Cmp, SrcStr, NullPtr, "memchr.sel");
+      // The condition depends on the value of the string being equal to the
+      // query, neither of which we know without value profiling, so mark the
+      // profile unknown.
+      return B.CreateSelectWithUnknownProfile(Cmp, SrcStr, NullPtr, DEBUG_TYPE,
+                                              "memchr.sel");
     }
   }
 
@@ -1384,7 +1395,9 @@ Value *LibCallSimplifier::optimizeMemChr(CallInst *CI, IRBuilderBase &B) {
                                  "memchr.cmp");
     Value *SrcPlus = B.CreateInBoundsGEP(B.getInt8Ty(), SrcStr, B.getInt64(Pos),
                                          "memchr.ptr");
-    return B.CreateSelect(Cmp, NullPtr, SrcPlus);
+    // The condition is dependent upon the value of n, which we cannot infer
+    // without value profiling, so mark the profile unknown.
+    return B.CreateSelectWithUnknownProfile(Cmp, NullPtr, SrcPlus, DEBUG_TYPE);
   }
 
   if (Str.size() == 0)
@@ -1423,14 +1436,20 @@ Value *LibCallSimplifier::optimizeMemChr(CallInst *CI, IRBuilderBase &B) {
       Value *NGtPos = B.CreateICmp(ICmpInst::ICMP_UGT, Size, PosVal);
       Value *And = B.CreateAnd(CEqSPos, NGtPos);
       Value *SrcPlus = B.CreateInBoundsGEP(B.getInt8Ty(), SrcStr, PosVal);
-      Sel1 = B.CreateSelect(And, SrcPlus, NullPtr, "memchr.sel1");
+      // The condition depends on the value of the query and size, neither of
+      // which we know without value profiling, so mark the profile unknown.
+      Sel1 = B.CreateSelectWithUnknownProfile(And, SrcPlus, NullPtr, DEBUG_TYPE,
+                                              "memchr.sel1");
     }
 
     Value *Str0 = ConstantInt::get(Int8Ty, Str[0]);
     Value *CEqS0 = B.CreateICmpEQ(Str0, CharVal);
     Value *NNeZ = B.CreateICmpNE(Size, ConstantInt::get(SizeTy, 0));
     Value *And = B.CreateAnd(NNeZ, CEqS0);
-    return B.CreateSelect(And, SrcStr, Sel1, "memchr.sel2");
+    // The condition depends on the value of the query and size, neither of
+    // which we know without value profiling, so mark the profile unknown.
+    return B.CreateSelectWithUnknownProfile(And, SrcStr, Sel1, DEBUG_TYPE,
+                                            "memchr.sel2");
   }
 
   if (!LenC) {
@@ -1524,8 +1543,13 @@ Value *LibCallSimplifier::optimizeMemChr(CallInst *CI, IRBuilderBase &B) {
 
   // Finally merge both checks and cast to pointer type. The inttoptr
   // implicitly zexts the i1 to intptr type.
-  return B.CreateIntToPtr(B.CreateLogicalAnd(Bounds, Bits, "memchr"),
-                          CI->getType());
+  Value *Memchr = B.CreateLogicalAnd(Bounds, Bits, "memchr");
+  // We construct an and between the value of the memory and the bytes to search
+  // for. We cannot infer how often this would be true without value profiling
+  // for the query, so mark the profile unknown.
+  if (auto *SI = dyn_cast<SelectInst>(Memchr))
+    setExplicitlyUnknownBranchWeightsIfProfiled(*SI, DEBUG_TYPE);
+  return B.CreateIntToPtr(Memchr, CI->getType());
 }
 
 // Optimize a memcmp or, when StrNCmp is true, strncmp call CI with constant

diff  --git a/llvm/test/Transforms/InstCombine/memchr-11.ll b/llvm/test/Transforms/InstCombine/memchr-11.ll
index 426b34463b915..1fd9e04cd901e 100644
--- a/llvm/test/Transforms/InstCombine/memchr-11.ll
+++ b/llvm/test/Transforms/InstCombine/memchr-11.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 ;
 ; Verify that the result of memchr calls used in equality expressions
@@ -27,12 +27,12 @@ define i1 @fold_memchr_a_c_5_eq_a(i32 %c) {
 ; the first argument is an arbitrary, including potentially past-the-end,
 ; pointer, this is safe because a5 is dereferenceable.
 
-define i1 @fold_memchr_a_c_n_eq_a(i32 %c, i64 %n) {
+define i1 @fold_memchr_a_c_n_eq_a(i32 %c, i64 %n) !prof !0 {
 ; CHECK-LABEL: @fold_memchr_a_c_n_eq_a(
 ; CHECK-NEXT:    [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8
 ; CHECK-NEXT:    [[CHAR0CMP:%.*]] = icmp eq i8 [[TMP1]], 49
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[N:%.*]], 0
-; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i1 [[CHAR0CMP]], i1 false
+; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[TMP2]], i1 [[CHAR0CMP]], i1 false, !prof [[PROF1:![0-9]+]]
 ; CHECK-NEXT:    ret i1 [[TMP3]]
 ;
   %q = call ptr @memchr(ptr @a5, i32 %c, i64 %n)
@@ -117,3 +117,8 @@ define i1 @call_memchr_s_c_n_eq_s(ptr %s, i32 %c, i64 %n) {
   %cmp = icmp eq ptr %p, %s
   ret i1 %cmp
 }
+
+!0 = !{!"function_entry_count", i32 10}
+;.
+; CHECK: [[PROF1]] = !{!"unknown", !"simplify-lib-calls"}
+;.

diff  --git a/llvm/test/Transforms/InstCombine/memchr-2.ll b/llvm/test/Transforms/InstCombine/memchr-2.ll
index fc6d674387065..9fcd3c2505579 100644
--- a/llvm/test/Transforms/InstCombine/memchr-2.ll
+++ b/llvm/test/Transforms/InstCombine/memchr-2.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
 ; Verify that memchr calls with constant arrays, or constant characters,
@@ -86,10 +86,10 @@ define ptr @fold_memchr_a123f45_500_9() {
 
 ; Fold memchr(a12345, '\03', n) to n < 3 ? null : a12345 + 2.
 
-define ptr @fold_a12345_3_n(i64 %n) {
+define ptr @fold_a12345_3_n(i64 %n) !prof !0 {
 ; CHECK-LABEL: @fold_a12345_3_n(
 ; CHECK-NEXT:    [[MEMCHR_CMP:%.*]] = icmp ult i64 [[N:%.*]], 3
-; CHECK-NEXT:    [[RES:%.*]] = select i1 [[MEMCHR_CMP]], ptr null, ptr getelementptr inbounds nuw (i8, ptr @a12345, i64 2)
+; CHECK-NEXT:    [[RES:%.*]] = select i1 [[MEMCHR_CMP]], ptr null, ptr getelementptr inbounds nuw (i8, ptr @a12345, i64 2), !prof [[PROF1:![0-9]+]]
 ; CHECK-NEXT:    ret ptr [[RES]]
 ;
 
@@ -124,3 +124,8 @@ define ptr @call_ax_1_n(i64 %n) {
   %res = call ptr @memchr(ptr @ax, i32 1, i64 %n)
   ret ptr %res
 }
+
+!0 = !{!"function_entry_count", i32 10}
+;.
+; CHECK: [[PROF1]] = !{!"unknown", !"simplify-lib-calls"}
+;.

diff  --git a/llvm/test/Transforms/InstCombine/memchr-3.ll b/llvm/test/Transforms/InstCombine/memchr-3.ll
index 0fc3a7acd0d66..849a4b03160dc 100644
--- a/llvm/test/Transforms/InstCombine/memchr-3.ll
+++ b/llvm/test/Transforms/InstCombine/memchr-3.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
 ; Verify that the special case of memchr calls with the size of 1 are
@@ -37,11 +37,11 @@ define ptr @fold_memchr_a12345_2_1() {
 ; Fold memchr(ax, 257, 1) to (unsigned char)*ax == 1 ? ax : null
 ; to verify the constant 257 is converted to unsigned char (yielding 1).
 
-define ptr @fold_memchr_ax_257_1(i32 %chr, i64 %n) {
+define ptr @fold_memchr_ax_257_1(i32 %chr, i64 %n) !prof !0 {
 ; CHECK-LABEL: @fold_memchr_ax_257_1(
 ; CHECK-NEXT:    [[MEMCHR_CHAR0:%.*]] = load i8, ptr @ax, align 1
 ; CHECK-NEXT:    [[MEMCHR_CHAR0CMP:%.*]] = icmp eq i8 [[MEMCHR_CHAR0]], 1
-; CHECK-NEXT:    [[MEMCHR_SEL:%.*]] = select i1 [[MEMCHR_CHAR0CMP]], ptr @ax, ptr null
+; CHECK-NEXT:    [[MEMCHR_SEL:%.*]] = select i1 [[MEMCHR_CHAR0CMP]], ptr @ax, ptr null, !prof [[PROF1:![0-9]+]]
 ; CHECK-NEXT:    ret ptr [[MEMCHR_SEL]]
 ;
 
@@ -64,3 +64,8 @@ define ptr @fold_memchr_ax_c_1(i32 %chr, i64 %n) {
   %res = call ptr @memchr(ptr @ax, i32 %chr, i64 1)
   ret ptr %res
 }
+
+!0 = !{!"function_entry_count", i32 10}
+;.
+; CHECK: [[PROF1]] = !{!"unknown", !"simplify-lib-calls"}
+;.

diff  --git a/llvm/test/Transforms/InstCombine/memchr-6.ll b/llvm/test/Transforms/InstCombine/memchr-6.ll
index 3c55f418cd8e0..465f1929cd810 100644
--- a/llvm/test/Transforms/InstCombine/memchr-6.ll
+++ b/llvm/test/Transforms/InstCombine/memchr-6.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 ;
 ; Verify that memchr calls with a string consisting of all the same
@@ -63,17 +63,17 @@ define ptr @fold_memchr_a11111_c_n(i32 %C, i64 %N) {
 ; Fold memchr(a111122, C, N) to
 ;   N != 0 && C == 1 ? a111122 : N > 4 && C == 2 ? a111122 + 4 : null.
 
-define ptr @fold_memchr_a111122_c_n(i32 %C, i64 %N) {
+define ptr @fold_memchr_a111122_c_n(i32 %C, i64 %N) !prof !0 {
 ; CHECK-LABEL: @fold_memchr_a111122_c_n(
 ; CHECK-NEXT:    [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i8
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i8 [[TMP1]], 2
 ; CHECK-NEXT:    [[TMP3:%.*]] = icmp ugt i64 [[N:%.*]], 4
 ; CHECK-NEXT:    [[TMP4:%.*]] = and i1 [[TMP2]], [[TMP3]]
-; CHECK-NEXT:    [[MEMCHR_SEL1:%.*]] = select i1 [[TMP4]], ptr getelementptr inbounds nuw (i8, ptr @a111122, i64 4), ptr null
+; CHECK-NEXT:    [[MEMCHR_SEL1:%.*]] = select i1 [[TMP4]], ptr getelementptr inbounds nuw (i8, ptr @a111122, i64 4), ptr null, !prof [[PROF1:![0-9]+]]
 ; CHECK-NEXT:    [[TMP5:%.*]] = icmp eq i8 [[TMP1]], 1
 ; CHECK-NEXT:    [[TMP6:%.*]] = icmp ne i64 [[N]], 0
 ; CHECK-NEXT:    [[TMP7:%.*]] = and i1 [[TMP6]], [[TMP5]]
-; CHECK-NEXT:    [[MEMCHR_SEL2:%.*]] = select i1 [[TMP7]], ptr @a111122, ptr [[MEMCHR_SEL1]]
+; CHECK-NEXT:    [[MEMCHR_SEL2:%.*]] = select i1 [[TMP7]], ptr @a111122, ptr [[MEMCHR_SEL1]], !prof [[PROF1]]
 ; CHECK-NEXT:    ret ptr [[MEMCHR_SEL2]]
 ;
 
@@ -138,3 +138,8 @@ define ptr @call_memchr_a1110111_c_n(i32 %C, i64 %N) {
   %ret = call ptr @memchr(ptr @a1110111, i32 %C, i64 %N)
   ret ptr %ret
 }
+
+!0 = !{!"function_entry_count", i32 10}
+;.
+; CHECK: [[PROF1]] = !{!"unknown", !"simplify-lib-calls"}
+;.

diff  --git a/llvm/test/Transforms/InstCombine/memchr.ll b/llvm/test/Transforms/InstCombine/memchr.ll
index 89509b079d849..e2bd66c45e01d 100644
--- a/llvm/test/Transforms/InstCombine/memchr.ll
+++ b/llvm/test/Transforms/InstCombine/memchr.ll
@@ -1,4 +1,4 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals smart
 ; Test that the memchr library call simplifier works correctly.
 ; RUN: opt < %s -passes=instcombine -S | FileCheck %s
 
@@ -120,7 +120,7 @@ define void @test10() {
 }
 
 ; Check transformation memchr("\r\n", C, 3) != nullptr -> (C & 9217) != 0
-define i1 @test11(i32 %C) {
+define i1 @test11(i32 %C) !prof !0 {
 ; CHECK-LABEL: @test11(
 ; CHECK-NEXT:    [[TMP1:%.*]] = trunc i32 [[C:%.*]] to i16
 ; CHECK-NEXT:    [[TMP2:%.*]] = and i16 [[TMP1]], 255
@@ -128,7 +128,7 @@ define i1 @test11(i32 %C) {
 ; CHECK-NEXT:    [[TMP3:%.*]] = shl nuw i16 1, [[TMP2]]
 ; CHECK-NEXT:    [[TMP4:%.*]] = and i16 [[TMP3]], 9217
 ; CHECK-NEXT:    [[MEMCHR_BITS:%.*]] = icmp ne i16 [[TMP4]], 0
-; CHECK-NEXT:    [[MEMCHR:%.*]] = select i1 [[MEMCHR_BOUNDS]], i1 [[MEMCHR_BITS]], i1 false
+; CHECK-NEXT:    [[MEMCHR:%.*]] = select i1 [[MEMCHR_BOUNDS]], i1 [[MEMCHR_BITS]], i1 false, !prof [[PROF1:![0-9]+]]
 ; CHECK-NEXT:    ret i1 [[MEMCHR]]
 ;
   %dst = call ptr @memchr(ptr @newlines, i32 %C, i32 3)
@@ -231,3 +231,8 @@ define ptr @test19(ptr %str, i32 %c) null_pointer_is_valid {
   %ret = call ptr @memchr(ptr %str, i32 %c, i32 5)
   ret ptr %ret
 }
+
+!0 = !{!"function_entry_count", i32 10}
+;.
+; CHECK: [[PROF1]] = !{!"unknown", !"simplify-lib-calls"}
+;.

diff  --git a/llvm/utils/profcheck-xfail.txt b/llvm/utils/profcheck-xfail.txt
index 2014275cb89fc..874d86760f84d 100644
--- a/llvm/utils/profcheck-xfail.txt
+++ b/llvm/utils/profcheck-xfail.txt
@@ -47,13 +47,6 @@ Transforms/InstCombine/ldexp-ext.ll
 Transforms/InstCombine/ldexp.ll
 Transforms/InstCombine/logical-select.ll
 Transforms/InstCombine/lshr.ll
-Transforms/InstCombine/memchr-11.ll
-Transforms/InstCombine/memchr-2.ll
-Transforms/InstCombine/memchr-3.ll
-Transforms/InstCombine/memchr-6.ll
-Transforms/InstCombine/memchr-7.ll
-Transforms/InstCombine/memchr-9.ll
-Transforms/InstCombine/memchr.ll
 Transforms/InstCombine/memrchr-3.ll
 Transforms/InstCombine/memrchr-4.ll
 Transforms/InstCombine/minmax-fold.ll
@@ -74,9 +67,6 @@ Transforms/InstCombine/shift.ll
 Transforms/InstCombine/simplify-demanded-fpclass.ll
 Transforms/InstCombine/sink-not-into-another-hand-of-logical-and.ll
 Transforms/InstCombine/sink-not-into-another-hand-of-logical-or.ll
-Transforms/InstCombine/strchr-1.ll
-Transforms/InstCombine/strchr-3.ll
-Transforms/InstCombine/strlen-1.ll
 Transforms/InstCombine/strrchr-3.ll
 Transforms/InstCombine/truncating-saturate.ll
 Transforms/InstCombine/unordered-fcmp-select.ll
@@ -116,5 +106,4 @@ Transforms/TailCallElim/debugloc.ll
 Transforms/TailCallElim/dropping_debugloc_acc_rec_inst_rnew.ll
 Transforms/TailCallElim/inf-recursion.ll
 Transforms/Util/control-flow-hub-finalize-same-succ-crash.ll
-Transforms/Util/libcalls-opt-remarks.ll
 Transforms/Util/lowerswitch.ll


        


More information about the llvm-commits mailing list