[llvm] [IR] Introduce `llvm.strlen.*` intrinsics (PR #135792)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 15 08:21:21 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
Author: Henry Jiang (mustartt)
<details>
<summary>Changes</summary>
This patch introduces the `llvm.strlen.*` overloaded intrinsics to unify the representations for different sized character types `char, wchar_t` and `size_t` return types, bringing them more in line with other memory intrinsics.
The intrinsic follows the same semantics as https://en.cppreference.com/w/c/string/byte/strlen and will lower to the appropriate library calls `strlen` or `wcslen`, or an inline loop otherwise.
The motivation for this are as followed:
- we can get support for folding on compares with 0 after loop idiom recognize, this is quite profitable on older codebases that have custom char types different than `wchar_t`.
- streamline analysis paths for future work for PGO library call profiling
Currently its split into multiple patches:
- add folding of compares and other patterns in InstCombine
- move frontend codegen to emit these intrinsics similar to memset
There also future work to implement PGO library argument based profiling to emit target optimized inline loops for both strlen and memset when profitable.
---
Full diff: https://github.com/llvm/llvm-project/pull/135792.diff
5 Files Affected:
- (modified) llvm/docs/LangRef.rst (+40)
- (modified) llvm/include/llvm/IR/IntrinsicInst.h (+19)
- (modified) llvm/include/llvm/IR/Intrinsics.td (+7)
- (modified) llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp (+100)
- (added) llvm/test/Transforms/PreISelIntrinsicLowering/strlen.ll (+196)
``````````diff
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index d242c945816cc..da7e4037afd40 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -15756,6 +15756,46 @@ If ``<count>`` is not a well-defined value, the behavior is undefined.
If ``<count>`` is not zero, ``<dest>`` should be well-defined, otherwise the
behavior is undefined.
+.. _int_strlen:
+
+'``llvm.strlen.*``' Intrinsic
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Syntax:
+"""""""
+
+This is an overloaded intrinsic. You can use llvm.strlen on any character bit
+width and for different address spaces.
+
+::
+
+ declare i64 @llvm.strlen.p0.i8(ptr <src>, i8 0)
+ declare i64 @llvm.strlen.p0.i16(ptr <src>, i16 0)
+ declare i64 @llvm.strlen.p0.i32(ptr <src>, i32 0)
+
+Overview:
+"""""""""
+
+The '``llvm.strlen.*``' intrinsics returns the length of the given
+null-terminated string ``str``. This will be replaced by the corresponding
+``strlen`` or ``wcslen`` library call if available and the string character
+type are compatible, otherwise an inline loop is emitted.
+
+Arguments:
+""""""""""
+
+The first argument is a pointer to the first character of the string. The
+second argument specifies the underlying type of the characters.
+
+Semantics:
+""""""""""
+
+The '``llvm.strlen.*``' intrinsics returns the number of characters in the
+character array whose first character is pointed to by ``<src>`` up to but
+not including the first null terminator character. If ``<src>`` is not a
+null-terminated string or if ``<src>`` is null, then the behavior is undefined.
+
+
.. _int_sqrt:
'``llvm.sqrt.*``' Intrinsic
diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h
index 93750d6e3845e..9e6248eac5d3a 100644
--- a/llvm/include/llvm/IR/IntrinsicInst.h
+++ b/llvm/include/llvm/IR/IntrinsicInst.h
@@ -1463,9 +1463,28 @@ class AnyMemMoveInst : public AnyMemTransferInst {
return false;
}
}
+
+ static bool classof(const Value *V) {
+ return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
+ }
+};
+
+/// This class represents any sized strlen intrinsic
+class StrlenInst : public IntrinsicInst {
+public:
+ static bool classof(const IntrinsicInst *I) {
+ return I->getIntrinsicID() == Intrinsic::strlen;
+ }
+
static bool classof(const Value *V) {
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
}
+
+ unsigned getOperandWidth() const {
+ const Type *IntTy = dyn_cast<IntegerType>(getArgOperand(1)->getType());
+ assert(IntTy && "strlen intrinsics argument must be an integer");
+ return IntTy->getIntegerBitWidth();
+ }
};
/// This represents the llvm.va_start intrinsic.
diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td
index d10b07ccd91c2..c0376f22f4d11 100644
--- a/llvm/include/llvm/IR/Intrinsics.td
+++ b/llvm/include/llvm/IR/Intrinsics.td
@@ -1032,6 +1032,13 @@ def int_experimental_memset_pattern
NoCapture<ArgIndex<0>>, WriteOnly<ArgIndex<0>>,
ImmArg<ArgIndex<3>>]>;
+def int_strlen : Intrinsic<
+ [llvm_anyint_ty],
+ [llvm_anyptr_ty, llvm_anyint_ty],
+ [IntrReadMem, IntrArgMemOnly,
+ NoCapture<ArgIndex<0>>, ReadOnly<ArgIndex<0>>,
+ NoUndef<RetIndex>, NonNull<ArgIndex<0>>]>;
+
// FIXME: Add version of these floating point intrinsics which allow non-default
// rounding modes and FP exception handling.
diff --git a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
index 9dc1764b49e46..83e4986606e6b 100644
--- a/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
+++ b/llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp
@@ -69,6 +69,7 @@ struct PreISelIntrinsicLowering {
static bool shouldExpandMemIntrinsicWithSize(Value *Size,
const TargetTransformInfo &TTI);
bool expandMemIntrinsicUses(Function &F) const;
+ bool expandStrlenIntrinsicUses(Function &F) const;
bool lowerIntrinsics(Module &M) const;
};
@@ -441,6 +442,102 @@ bool PreISelIntrinsicLowering::expandMemIntrinsicUses(Function &F) const {
return Changed;
}
+static ConstantInt *getByteWidth(Type *CharType) {
+ auto *IntTy = dyn_cast<IntegerType>(CharType);
+ assert(IntTy && "operand must be an integer type");
+ return ConstantInt::get(CharType->getContext(),
+ APInt(64, IntTy->getBitWidth() / 8));
+}
+
+// %ptr <- ptr
+// preheader:
+// br label %body
+// body:
+// %str = phi [%preheader %ptr], [%term %ptr.inc]
+// %char = load i8, ptr %str, align 1
+// %cmp.not = icmp eq %char, 0
+// %str.inc = gep %str, 1, 0
+// br %cond, label %body, label %term
+// term:
+// %end = phi [%term %ptr]
+// %base.int = ptrtoint %ptr
+// %str.int = ptrtoint %str
+// %len = sub i64 %str.int, %base.int
+static Value *expandStrlenAsLoop(Instruction *Instr, Value *Src,
+ Type *CharacterType,
+ const TargetLibraryInfo &TLI) {
+ auto *PtrTy = dyn_cast<PointerType>(Src->getType());
+ assert(PtrTy && "strlen intrinsic operand must be a pointer");
+
+ BasicBlock *Preheader = Instr->getParent();
+ BasicBlock *LoopTerm =
+ Preheader->splitBasicBlock(Instr, "strlen-loop-expansion-terminator");
+ BasicBlock *LoopBody =
+ BasicBlock::Create(Instr->getContext(), "strlen-loop-expansion",
+ Preheader->getParent(), LoopTerm);
+
+ BranchInst *BI = dyn_cast<BranchInst>(Preheader->getTerminator());
+ assert(BI && BI->isUnconditional() &&
+ "expected unconditional branch to be created");
+ BI->setOperand(0, LoopBody);
+
+ Value *NullTerm = ConstantInt::get(CharacterType, 0);
+ ConstantInt *ByteWidth = getByteWidth(CharacterType);
+
+ IRBuilder<> Builder(LoopBody);
+ PHINode *IncomingPtr = Builder.CreatePHI(PtrTy, 2, "str");
+ Value *Char = Builder.CreateLoad(CharacterType, IncomingPtr, "char");
+ Value *Cmp = Builder.CreateCmp(CmpInst::ICMP_EQ, Char, NullTerm, "cmp.not");
+ Value *IncPtr = Builder.CreatePtrAdd(IncomingPtr, ByteWidth, "str.inc");
+ Builder.CreateCondBr(Cmp, LoopTerm, LoopBody);
+
+ IncomingPtr->addIncoming(Src, Preheader);
+ IncomingPtr->addIncoming(IncPtr, LoopBody);
+
+ IRBuilder<> TBuilder(Instr);
+ Value *Result = TBuilder.CreateSub(
+ TBuilder.CreatePtrToInt(IncomingPtr,
+ TLI.getSizeTType(*Instr->getModule())),
+ TBuilder.CreatePtrToInt(Src, TLI.getSizeTType(*Instr->getModule())));
+ if (ByteWidth->getValue().ugt(1))
+ Result = TBuilder.CreateLShr(Result, ByteWidth, "str.len");
+ return Result;
+}
+
+bool PreISelIntrinsicLowering::expandStrlenIntrinsicUses(Function &F) const {
+ bool Changed = false;
+ for (User *U : llvm::make_early_inc_range(F.users())) {
+ Instruction *Inst = cast<Instruction>(U);
+ auto *Strlen = cast<StrlenInst>(Inst);
+ unsigned CharacterWidth = Strlen->getOperandWidth();
+ Function *ParentFunc = Strlen->getFunction();
+
+ IRBuilder<> Builder(Strlen);
+ const TargetLibraryInfo &TLI = LookupTLI(*ParentFunc);
+ const DataLayout &DL = ParentFunc->getDataLayout();
+
+ Value *Result = nullptr;
+ if (CharacterWidth == 8 &&
+ isLibFuncEmittable(F.getParent(), &TLI, LibFunc_strlen)) {
+ Result = emitStrLen(Strlen->getOperand(0), Builder, DL, &TLI);
+ } else if (CharacterWidth == 8 * TLI.getWCharSize(*F.getParent()) &&
+ isLibFuncEmittable(F.getParent(), &TLI, LibFunc_wcslen)) {
+ Result = emitWcsLen(Strlen->getOperand(0), Builder, DL, &TLI);
+ } else {
+ Result = expandStrlenAsLoop(Inst, Inst->getOperand(0),
+ Inst->getOperand(1)->getType(), TLI);
+ }
+ assert(Result && "failed to lower strlen intrinsic");
+ if (Result->getType() != Strlen->getType())
+ Result = Builder.CreateSExtOrTrunc(Result, Strlen->getType());
+
+ Strlen->replaceAllUsesWith(Result);
+ Strlen->eraseFromParent();
+ Changed = true;
+ }
+ return Changed;
+}
+
bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const {
bool Changed = false;
for (Function &F : M) {
@@ -455,6 +552,9 @@ bool PreISelIntrinsicLowering::lowerIntrinsics(Module &M) const {
case Intrinsic::experimental_memset_pattern:
Changed |= expandMemIntrinsicUses(F);
break;
+ case Intrinsic::strlen:
+ Changed |= expandStrlenIntrinsicUses(F);
+ break;
case Intrinsic::load_relative:
Changed |= lowerLoadRelative(F);
break;
diff --git a/llvm/test/Transforms/PreISelIntrinsicLowering/strlen.ll b/llvm/test/Transforms/PreISelIntrinsicLowering/strlen.ll
new file mode 100644
index 0000000000000..ce9abb571da58
--- /dev/null
+++ b/llvm/test/Transforms/PreISelIntrinsicLowering/strlen.ll
@@ -0,0 +1,196 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=pre-isel-intrinsic-lowering -S < %s | FileCheck %s
+
+declare i64 @llvm.strlen.p0.i8(ptr, i8)
+declare i64 @llvm.strlen.p0.i16(ptr, i16)
+declare i64 @llvm.strlen.p0.i32(ptr, i32)
+
+declare i64 @llvm.strlen.p1.i8(ptr addrspace(1), i8)
+declare i32 @llvm.strlen.i32.p0.i32(ptr, i32)
+
+declare void @before()
+declare void @after()
+
+define i64 @expand_as_libcall_strlen8(ptr %p) {
+; CHECK-LABEL: define i64 @expand_as_libcall_strlen8(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void @before()
+; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[P]])
+; CHECK-NEXT: call void @after()
+; CHECK-NEXT: ret i64 [[STRLEN]]
+;
+entry:
+ call void @before()
+ %0 = call i64 @llvm.strlen.p0.i8(ptr %p, i8 0)
+ call void @after()
+ ret i64 %0
+}
+
+define i32 @expand_as_libcall_strlen8_32bit(ptr %p) {
+; CHECK-LABEL: define i32 @expand_as_libcall_strlen8_32bit(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void @before()
+; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr [[P]])
+; CHECK-NEXT: [[TMP0:%.*]] = trunc i64 [[STRLEN]] to i32
+; CHECK-NEXT: call void @after()
+; CHECK-NEXT: ret i32 [[TMP0]]
+;
+entry:
+ call void @before()
+ %0 = call i32 @llvm.strlen.i32.p0.i8(ptr %p, i8 0)
+ call void @after()
+ ret i32 %0
+}
+
+define i64 @expand_as_libcall_strlen32(ptr %p) {
+; CHECK-LABEL: define i64 @expand_as_libcall_strlen32(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void @before()
+; CHECK-NEXT: [[WCSLEN:%.*]] = call i64 @wcslen(ptr [[P]])
+; CHECK-NEXT: call void @after()
+; CHECK-NEXT: ret i64 [[WCSLEN]]
+;
+entry:
+ call void @before()
+ %0 = call i64 @llvm.strlen.p0.i32(ptr %p, i32 0)
+ call void @after()
+ ret i64 %0
+}
+
+define i64 @expand_as_loop_strlen8(ptr %p) #0 {
+; CHECK-LABEL: define i64 @expand_as_loop_strlen8(
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: call void @before()
+; CHECK-NEXT: br label %[[STRLEN_LOOP_EXPANSION:.*]]
+; CHECK: [[STRLEN_LOOP_EXPANSION]]:
+; CHECK-NEXT: [[STR:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[STR_INC:%.*]], %[[STRLEN_LOOP_EXPANSION]] ]
+; CHECK-NEXT: [[CHAR:%.*]] = load i8, ptr [[STR]], align 1
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[CHAR]], 0
+; CHECK-NEXT: [[STR_INC]] = getelementptr i8, ptr [[STR]], i64 1
+; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[STRLEN_LOOP_EXPANSION_TERMINATOR:.*]], label %[[STRLEN_LOOP_EXPANSION]]
+; CHECK: [[STRLEN_LOOP_EXPANSION_TERMINATOR]]:
+; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[STR]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[TMP0]], [[TMP1]]
+; CHECK-NEXT: call void @after()
+; CHECK-NEXT: ret i64 [[TMP2]]
+;
+entry:
+ call void @before()
+ %0 = call i64 @llvm.strlen.p0.i8(ptr %p, i8 0)
+ call void @after()
+ ret i64 %0
+}
+
+define i32 @expand_as_loop_strlen8_32bit(ptr %p) #0 {
+; CHECK-LABEL: define i32 @expand_as_loop_strlen8_32bit(
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: call void @before()
+; CHECK-NEXT: br label %[[STRLEN_LOOP_EXPANSION:.*]]
+; CHECK: [[STRLEN_LOOP_EXPANSION]]:
+; CHECK-NEXT: [[STR:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[STR_INC:%.*]], %[[STRLEN_LOOP_EXPANSION]] ]
+; CHECK-NEXT: [[CHAR:%.*]] = load i8, ptr [[STR]], align 1
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[CHAR]], 0
+; CHECK-NEXT: [[STR_INC]] = getelementptr i8, ptr [[STR]], i64 1
+; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[STRLEN_LOOP_EXPANSION_TERMINATOR:.*]], label %[[STRLEN_LOOP_EXPANSION]]
+; CHECK: [[STRLEN_LOOP_EXPANSION_TERMINATOR]]:
+; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[STR]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[TMP0]], [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = trunc i64 [[TMP2]] to i32
+; CHECK-NEXT: call void @after()
+; CHECK-NEXT: ret i32 [[TMP3]]
+;
+entry:
+ call void @before()
+ %0 = call i32 @llvm.strlen.i32.p0.i8(ptr %p, i8 0)
+ call void @after()
+ ret i32 %0
+}
+
+define i64 @expand_as_loop_strlen16(ptr %p) #0 {
+; CHECK-LABEL: define i64 @expand_as_loop_strlen16(
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: call void @before()
+; CHECK-NEXT: br label %[[STRLEN_LOOP_EXPANSION:.*]]
+; CHECK: [[STRLEN_LOOP_EXPANSION]]:
+; CHECK-NEXT: [[STR:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[STR_INC:%.*]], %[[STRLEN_LOOP_EXPANSION]] ]
+; CHECK-NEXT: [[CHAR:%.*]] = load i16, ptr [[STR]], align 2
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i16 [[CHAR]], 0
+; CHECK-NEXT: [[STR_INC]] = getelementptr i8, ptr [[STR]], i64 2
+; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[STRLEN_LOOP_EXPANSION_TERMINATOR:.*]], label %[[STRLEN_LOOP_EXPANSION]]
+; CHECK: [[STRLEN_LOOP_EXPANSION_TERMINATOR]]:
+; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[STR]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[TMP0]], [[TMP1]]
+; CHECK-NEXT: [[STR_LEN:%.*]] = lshr i64 [[TMP2]], 2
+; CHECK-NEXT: call void @after()
+; CHECK-NEXT: ret i64 [[STR_LEN]]
+;
+entry:
+ call void @before()
+ %0 = call i64 @llvm.strlen.p0.i16(ptr %p, i16 0)
+ call void @after()
+ ret i64 %0
+}
+
+define i64 @expand_as_loop_strlen32(ptr %p) #0 {
+; CHECK-LABEL: define i64 @expand_as_loop_strlen32(
+; CHECK-SAME: ptr [[P:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: call void @before()
+; CHECK-NEXT: br label %[[STRLEN_LOOP_EXPANSION:.*]]
+; CHECK: [[STRLEN_LOOP_EXPANSION]]:
+; CHECK-NEXT: [[STR:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[STR_INC:%.*]], %[[STRLEN_LOOP_EXPANSION]] ]
+; CHECK-NEXT: [[CHAR:%.*]] = load i32, ptr [[STR]], align 4
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[CHAR]], 0
+; CHECK-NEXT: [[STR_INC]] = getelementptr i8, ptr [[STR]], i64 4
+; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[STRLEN_LOOP_EXPANSION_TERMINATOR:.*]], label %[[STRLEN_LOOP_EXPANSION]]
+; CHECK: [[STRLEN_LOOP_EXPANSION_TERMINATOR]]:
+; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[STR]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[TMP0]], [[TMP1]]
+; CHECK-NEXT: [[STR_LEN:%.*]] = lshr i64 [[TMP2]], 4
+; CHECK-NEXT: call void @after()
+; CHECK-NEXT: ret i64 [[STR_LEN]]
+;
+entry:
+ call void @before()
+ %0 = call i64 @llvm.strlen.p0.i32(ptr %p, i32 0)
+ call void @after()
+ ret i64 %0
+}
+
+define i64 @expanded_loop_keep_addrspace(ptr addrspace(1) %p) #0 {
+; CHECK-LABEL: define i64 @expanded_loop_keep_addrspace(
+; CHECK-SAME: ptr addrspace(1) [[P:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[STRLEN_LOOP_EXPANSION:.*]]
+; CHECK: [[STRLEN_LOOP_EXPANSION]]:
+; CHECK-NEXT: [[STR:%.*]] = phi ptr addrspace(1) [ [[P]], %[[ENTRY]] ], [ [[STR_INC:%.*]], %[[STRLEN_LOOP_EXPANSION]] ]
+; CHECK-NEXT: [[CHAR:%.*]] = load i8, ptr addrspace(1) [[STR]], align 1
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i8 [[CHAR]], 0
+; CHECK-NEXT: [[STR_INC]] = getelementptr i8, ptr addrspace(1) [[STR]], i64 1
+; CHECK-NEXT: br i1 [[CMP_NOT]], label %[[STRLEN_LOOP_EXPANSION_TERMINATOR:.*]], label %[[STRLEN_LOOP_EXPANSION]]
+; CHECK: [[STRLEN_LOOP_EXPANSION_TERMINATOR]]:
+; CHECK-NEXT: [[TMP0:%.*]] = ptrtoint ptr addrspace(1) [[STR]] to i64
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr addrspace(1) [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 [[TMP0]], [[TMP1]]
+; CHECK-NEXT: ret i64 [[TMP2]]
+;
+entry:
+ %0 = call i64 @llvm.strlen.p1.i8(ptr addrspace(1) %p, i8 0)
+ ret i64 %0
+}
+
+attributes #0 = { "no-builtins" }
+
+!llvm.module.flags = !{!0}
+!0 = !{i32 1, !"wchar_size", i32 4}
+
``````````
</details>
https://github.com/llvm/llvm-project/pull/135792
More information about the llvm-commits
mailing list