[llvm] [IR] Introduce `llvm.strlen.*` intrinsics (PR #135792)
Henry Jiang via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 15 08:49:17 PDT 2025
https://github.com/mustartt updated https://github.com/llvm/llvm-project/pull/135792
>From 393655e5bcbcb227f3966aabf31661734d410a04 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry.jiang1 at ibm.com>
Date: Mon, 31 Mar 2025 20:11:07 -0400
Subject: [PATCH] Add strlen intrinsic
---
llvm/docs/LangRef.rst | 40 ++++
llvm/include/llvm/IR/IntrinsicInst.h | 19 ++
llvm/include/llvm/IR/Intrinsics.td | 7 +
llvm/lib/CodeGen/PreISelIntrinsicLowering.cpp | 101 +++++++++
.../PreISelIntrinsicLowering/strlen.ll | 196 ++++++++++++++++++
5 files changed, 363 insertions(+)
create mode 100644 llvm/test/Transforms/PreISelIntrinsicLowering/strlen.ll
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..5428cfcf07d71 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,103 @@ 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->getValue().exactLogBase2(),
+ "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 +553,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..06eb7948f0bab
--- /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]], 1
+; 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]], 2
+; 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}
+
More information about the llvm-commits
mailing list