[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