Teach SimplifyLibCalls how to optimize strrchr.
authorBenjamin Kramer <benny.kra@googlemail.com>
Wed, 29 Sep 2010 21:50:51 +0000 (21:50 +0000)
committerBenjamin Kramer <benny.kra@googlemail.com>
Wed, 29 Sep 2010 21:50:51 +0000 (21:50 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@115091 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Transforms/Scalar/SimplifyLibCalls.cpp
test/Transforms/SimplifyLibCalls/StrRChr.ll [new file with mode: 0644]

index d7ce53f3671530bbea4ec5f3429f14bcce1b4c41..fffd3250738af23157648b07bf62310a94aa72e8 100644 (file)
@@ -271,6 +271,47 @@ struct StrChrOpt : public LibCallOptimization {
   }
 };
 
+//===---------------------------------------===//
+// 'strrchr' Optimizations
+
+struct StrRChrOpt : public LibCallOptimization {
+  virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
+    // Verify the "strrchr" function prototype.
+    const FunctionType *FT = Callee->getFunctionType();
+    if (FT->getNumParams() != 2 ||
+        FT->getReturnType() != Type::getInt8PtrTy(*Context) ||
+        FT->getParamType(0) != FT->getReturnType())
+      return 0;
+
+    Value *SrcStr = CI->getArgOperand(0);
+    ConstantInt *CharC = dyn_cast<ConstantInt>(CI->getArgOperand(1));
+
+    // Cannot fold anything if we're not looking for a constant.
+    if (!CharC)
+      return 0;
+
+    std::string Str;
+    if (!GetConstantStringInfo(SrcStr, Str)) {
+      // strrchr(s, 0) -> strchr(s, 0)
+      if (TD && CharC->isZero())
+        return EmitStrChr(SrcStr, '\0', B, TD);
+      return 0;
+    }
+
+    // strrchr can find the nul character.
+    Str += '\0';
+
+    // Compute the offset.
+    size_t I = Str.rfind(CharC->getSExtValue());
+    if (I == std::string::npos) // Didn't find the char. Return null.
+      return Constant::getNullValue(CI->getType());
+
+    // strrchr(s+n,c) -> gep(s+n+i,c)
+    Value *Idx = ConstantInt::get(Type::getInt64Ty(*Context), I);
+    return B.CreateGEP(SrcStr, Idx, "strrchr");
+  }
+};
+
 //===---------------------------------------===//
 // 'strcmp' Optimizations
 
@@ -1220,8 +1261,8 @@ namespace {
   class SimplifyLibCalls : public FunctionPass {
     StringMap<LibCallOptimization*> Optimizations;
     // String and Memory LibCall Optimizations
-    StrCatOpt StrCat; StrNCatOpt StrNCat; StrChrOpt StrChr; StrCmpOpt StrCmp;
-    StrNCmpOpt StrNCmp; StrCpyOpt StrCpy; StrCpyOpt StrCpyChk;
+    StrCatOpt StrCat; StrNCatOpt StrNCat; StrChrOpt StrChr; StrRChrOpt StrRChr;
+    StrCmpOpt StrCmp; StrNCmpOpt StrNCmp; StrCpyOpt StrCpy; StrCpyOpt StrCpyChk;
     StrNCpyOpt StrNCpy; StrLenOpt StrLen;
     StrToOpt StrTo; StrStrOpt StrStr;
     MemCmpOpt MemCmp; MemCpyOpt MemCpy; MemMoveOpt MemMove; MemSetOpt MemSet;
@@ -1269,6 +1310,7 @@ void SimplifyLibCalls::InitOptimizations() {
   Optimizations["strcat"] = &StrCat;
   Optimizations["strncat"] = &StrNCat;
   Optimizations["strchr"] = &StrChr;
+  Optimizations["strrchr"] = &StrRChr;
   Optimizations["strcmp"] = &StrCmp;
   Optimizations["strncmp"] = &StrNCmp;
   Optimizations["strcpy"] = &StrCpy;
@@ -2173,10 +2215,6 @@ bool SimplifyLibCalls::doInitialization(Module &M) {
 // stpcpy:
 //   * stpcpy(str, "literal") ->
 //           llvm.memcpy(str,"literal",strlen("literal")+1,1)
-// strrchr:
-//   * strrchr(s,c) -> reverse_offset_of_in(c,s)
-//      (if c is a constant integer and s is a constant string)
-//   * strrchr(s1,0) -> strchr(s1,0)
 //
 // strpbrk:
 //   * strpbrk(s,a) -> offset_in_for(s,a)
diff --git a/test/Transforms/SimplifyLibCalls/StrRChr.ll b/test/Transforms/SimplifyLibCalls/StrRChr.ll
new file mode 100644 (file)
index 0000000..2259fc0
--- /dev/null
@@ -0,0 +1,23 @@
+; Test that the StrRChrOptimizer works correctly
+; RUN: opt < %s -simplify-libcalls -S | FileCheck %s
+
+target datalayout = "-p:64:64:64"
+
+@hello = constant [14 x i8] c"hello world\5Cn\00"
+@null = constant [1 x i8] zeroinitializer
+
+declare i8* @strrchr(i8*, i32)
+
+define void @foo(i8* %bar) {
+       %hello_p = getelementptr [14 x i8]* @hello, i32 0, i32 0
+       %null_p = getelementptr [1 x i8]* @null, i32 0, i32 0
+       %world = call i8* @strrchr(i8* %hello_p, i32 119)
+; CHECK: getelementptr i8* %hello_p, i64 6
+       %ignore = call i8* @strrchr(i8* %null_p, i32 119)
+; CHECK-NOT: call i8* strrchr
+       %null = call i8* @strrchr(i8* %hello_p, i32 0)
+; CHECK: getelementptr i8* %hello_p, i64 13
+       %strchr = call i8* @strrchr(i8* %bar, i32 0)
+; CHECK: call i8* @strchr(i8* %bar, i32 0)
+       ret void
+}