[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