mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-24 12:20:00 +00:00
instcombine: Migrate strchr and strrchr optimizations
This patch migrates the strchr and strrchr optimizations from the simplify-libcalls pass into the instcombine library call simplifier. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@165875 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
73d8a5864f
commit
186f8d90df
@ -134,94 +134,6 @@ static bool IsOnlyUsedInEqualityComparison(Value *V, Value *With) {
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
//===---------------------------------------===//
|
||||
// 'strchr' Optimizations
|
||||
|
||||
struct StrChrOpt : public LibCallOptimization {
|
||||
virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
|
||||
// Verify the "strchr" function prototype.
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
if (FT->getNumParams() != 2 ||
|
||||
FT->getReturnType() != B.getInt8PtrTy() ||
|
||||
FT->getParamType(0) != FT->getReturnType() ||
|
||||
!FT->getParamType(1)->isIntegerTy(32))
|
||||
return 0;
|
||||
|
||||
Value *SrcStr = CI->getArgOperand(0);
|
||||
|
||||
// If the second operand is non-constant, see if we can compute the length
|
||||
// of the input string and turn this into memchr.
|
||||
ConstantInt *CharC = dyn_cast<ConstantInt>(CI->getArgOperand(1));
|
||||
if (CharC == 0) {
|
||||
// These optimizations require DataLayout.
|
||||
if (!TD) return 0;
|
||||
|
||||
uint64_t Len = GetStringLength(SrcStr);
|
||||
if (Len == 0 || !FT->getParamType(1)->isIntegerTy(32))// memchr needs i32.
|
||||
return 0;
|
||||
|
||||
return EmitMemChr(SrcStr, CI->getArgOperand(1), // include nul.
|
||||
ConstantInt::get(TD->getIntPtrType(*Context), Len),
|
||||
B, TD, TLI);
|
||||
}
|
||||
|
||||
// Otherwise, the character is a constant, see if the first argument is
|
||||
// a string literal. If so, we can constant fold.
|
||||
StringRef Str;
|
||||
if (!getConstantStringInfo(SrcStr, Str))
|
||||
return 0;
|
||||
|
||||
// Compute the offset, make sure to handle the case when we're searching for
|
||||
// zero (a weird way to spell strlen).
|
||||
size_t I = CharC->getSExtValue() == 0 ?
|
||||
Str.size() : Str.find(CharC->getSExtValue());
|
||||
if (I == StringRef::npos) // Didn't find the char. strchr returns null.
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// strchr(s+n,c) -> gep(s+n+i,c)
|
||||
return B.CreateGEP(SrcStr, B.getInt64(I), "strchr");
|
||||
}
|
||||
};
|
||||
|
||||
//===---------------------------------------===//
|
||||
// 'strrchr' Optimizations
|
||||
|
||||
struct StrRChrOpt : public LibCallOptimization {
|
||||
virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
|
||||
// Verify the "strrchr" function prototype.
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
if (FT->getNumParams() != 2 ||
|
||||
FT->getReturnType() != B.getInt8PtrTy() ||
|
||||
FT->getParamType(0) != FT->getReturnType() ||
|
||||
!FT->getParamType(1)->isIntegerTy(32))
|
||||
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;
|
||||
|
||||
StringRef Str;
|
||||
if (!getConstantStringInfo(SrcStr, Str)) {
|
||||
// strrchr(s, 0) -> strchr(s, 0)
|
||||
if (TD && CharC->isZero())
|
||||
return EmitStrChr(SrcStr, '\0', B, TD, TLI);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compute the offset.
|
||||
size_t I = CharC->getSExtValue() == 0 ?
|
||||
Str.size() : Str.rfind(CharC->getSExtValue());
|
||||
if (I == StringRef::npos) // Didn't find the char. Return null.
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// strrchr(s+n,c) -> gep(s+n+i,c)
|
||||
return B.CreateGEP(SrcStr, B.getInt64(I), "strrchr");
|
||||
}
|
||||
};
|
||||
|
||||
//===---------------------------------------===//
|
||||
// 'strcmp' Optimizations
|
||||
|
||||
@ -1466,7 +1378,6 @@ namespace {
|
||||
|
||||
StringMap<LibCallOptimization*> Optimizations;
|
||||
// String and Memory LibCall Optimizations
|
||||
StrChrOpt StrChr; StrRChrOpt StrRChr;
|
||||
StrCmpOpt StrCmp; StrNCmpOpt StrNCmp;
|
||||
StrCpyOpt StrCpy; StrCpyOpt StrCpyChk;
|
||||
StpCpyOpt StpCpy; StpCpyOpt StpCpyChk;
|
||||
@ -1541,8 +1452,6 @@ void SimplifyLibCalls::AddOpt(LibFunc::Func F1, LibFunc::Func F2,
|
||||
/// we know.
|
||||
void SimplifyLibCalls::InitOptimizations() {
|
||||
// String and Memory LibCall Optimizations
|
||||
Optimizations["strchr"] = &StrChr;
|
||||
Optimizations["strrchr"] = &StrRChr;
|
||||
Optimizations["strcmp"] = &StrCmp;
|
||||
Optimizations["strncmp"] = &StrNCmp;
|
||||
Optimizations["strcpy"] = &StrCpy;
|
||||
|
@ -320,6 +320,88 @@ struct StrNCatOpt : public StrCatOpt {
|
||||
}
|
||||
};
|
||||
|
||||
struct StrChrOpt : public LibCallOptimization {
|
||||
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
|
||||
// Verify the "strchr" function prototype.
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
if (FT->getNumParams() != 2 ||
|
||||
FT->getReturnType() != B.getInt8PtrTy() ||
|
||||
FT->getParamType(0) != FT->getReturnType() ||
|
||||
!FT->getParamType(1)->isIntegerTy(32))
|
||||
return 0;
|
||||
|
||||
Value *SrcStr = CI->getArgOperand(0);
|
||||
|
||||
// If the second operand is non-constant, see if we can compute the length
|
||||
// of the input string and turn this into memchr.
|
||||
ConstantInt *CharC = dyn_cast<ConstantInt>(CI->getArgOperand(1));
|
||||
if (CharC == 0) {
|
||||
// These optimizations require DataLayout.
|
||||
if (!TD) return 0;
|
||||
|
||||
uint64_t Len = GetStringLength(SrcStr);
|
||||
if (Len == 0 || !FT->getParamType(1)->isIntegerTy(32))// memchr needs i32.
|
||||
return 0;
|
||||
|
||||
return EmitMemChr(SrcStr, CI->getArgOperand(1), // include nul.
|
||||
ConstantInt::get(TD->getIntPtrType(*Context), Len),
|
||||
B, TD, TLI);
|
||||
}
|
||||
|
||||
// Otherwise, the character is a constant, see if the first argument is
|
||||
// a string literal. If so, we can constant fold.
|
||||
StringRef Str;
|
||||
if (!getConstantStringInfo(SrcStr, Str))
|
||||
return 0;
|
||||
|
||||
// Compute the offset, make sure to handle the case when we're searching for
|
||||
// zero (a weird way to spell strlen).
|
||||
size_t I = CharC->getSExtValue() == 0 ?
|
||||
Str.size() : Str.find(CharC->getSExtValue());
|
||||
if (I == StringRef::npos) // Didn't find the char. strchr returns null.
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// strchr(s+n,c) -> gep(s+n+i,c)
|
||||
return B.CreateGEP(SrcStr, B.getInt64(I), "strchr");
|
||||
}
|
||||
};
|
||||
|
||||
struct StrRChrOpt : public LibCallOptimization {
|
||||
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
|
||||
// Verify the "strrchr" function prototype.
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
if (FT->getNumParams() != 2 ||
|
||||
FT->getReturnType() != B.getInt8PtrTy() ||
|
||||
FT->getParamType(0) != FT->getReturnType() ||
|
||||
!FT->getParamType(1)->isIntegerTy(32))
|
||||
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;
|
||||
|
||||
StringRef Str;
|
||||
if (!getConstantStringInfo(SrcStr, Str)) {
|
||||
// strrchr(s, 0) -> strchr(s, 0)
|
||||
if (TD && CharC->isZero())
|
||||
return EmitStrChr(SrcStr, '\0', B, TD, TLI);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compute the offset.
|
||||
size_t I = CharC->getSExtValue() == 0 ?
|
||||
Str.size() : Str.rfind(CharC->getSExtValue());
|
||||
if (I == StringRef::npos) // Didn't find the char. Return null.
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// strrchr(s+n,c) -> gep(s+n+i,c)
|
||||
return B.CreateGEP(SrcStr, B.getInt64(I), "strrchr");
|
||||
}
|
||||
};
|
||||
|
||||
} // End anonymous namespace.
|
||||
|
||||
namespace llvm {
|
||||
@ -340,6 +422,8 @@ class LibCallSimplifierImpl {
|
||||
// String and memory library call optimizations.
|
||||
StrCatOpt StrCat;
|
||||
StrNCatOpt StrNCat;
|
||||
StrChrOpt StrChr;
|
||||
StrRChrOpt StrRChr;
|
||||
|
||||
void initOptimizations();
|
||||
public:
|
||||
@ -364,6 +448,8 @@ void LibCallSimplifierImpl::initOptimizations() {
|
||||
// String and memory library call optimizations.
|
||||
Optimizations["strcat"] = &StrCat;
|
||||
Optimizations["strncat"] = &StrNCat;
|
||||
Optimizations["strchr"] = &StrChr;
|
||||
Optimizations["strrchr"] = &StrRChr;
|
||||
}
|
||||
|
||||
Value *LibCallSimplifierImpl::optimizeCall(CallInst *CI) {
|
||||
|
54
test/Transforms/InstCombine/strchr-1.ll
Normal file
54
test/Transforms/InstCombine/strchr-1.ll
Normal file
@ -0,0 +1,54 @@
|
||||
; Test that the strchr library call simplifier works correctly.
|
||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
|
||||
|
||||
@hello = constant [14 x i8] c"hello world\5Cn\00"
|
||||
@null = constant [1 x i8] zeroinitializer
|
||||
@chp = global i8* zeroinitializer
|
||||
|
||||
declare i8* @strchr(i8*, i32)
|
||||
|
||||
define void @test_simplify1() {
|
||||
; CHECK: store i8* getelementptr inbounds ([14 x i8]* @hello, i32 0, i32 6)
|
||||
; CHECK-NOT: call i8* @strchr
|
||||
; CHECK: ret void
|
||||
|
||||
%str = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8* @strchr(i8* %str, i32 119)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_simplify2() {
|
||||
; CHECK: store i8* null, i8** @chp, align 4
|
||||
; CHECK-NOT: call i8* @strchr
|
||||
; CHECK: ret void
|
||||
|
||||
%str = getelementptr [1 x i8]* @null, i32 0, i32 0
|
||||
%dst = call i8* @strchr(i8* %str, i32 119)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_simplify3() {
|
||||
; CHECK: store i8* getelementptr inbounds ([14 x i8]* @hello, i32 0, i32 13)
|
||||
; CHECK-NOT: call i8* @strchr
|
||||
; CHECK: ret void
|
||||
|
||||
%src = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8* @strchr(i8* %src, i32 0)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_simplify4(i32 %chr) {
|
||||
; CHECK: call i8* @memchr
|
||||
; CHECK-NOT: call i8* @strchr
|
||||
; CHECK: ret void
|
||||
|
||||
%src = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8* @strchr(i8* %src, i32 %chr)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
21
test/Transforms/InstCombine/strchr-2.ll
Normal file
21
test/Transforms/InstCombine/strchr-2.ll
Normal file
@ -0,0 +1,21 @@
|
||||
; Test that the strchr libcall simplifier works correctly.
|
||||
;
|
||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
|
||||
|
||||
@hello = constant [14 x i8] c"hello world\5Cn\00"
|
||||
@chr = global i8 zeroinitializer
|
||||
|
||||
declare i8 @strchr(i8*, i32)
|
||||
|
||||
define void @test_nosimplify1() {
|
||||
; CHECK: test_nosimplify1
|
||||
; CHECK: call i8 @strchr
|
||||
; CHECK: ret void
|
||||
|
||||
%str = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8 @strchr(i8* %str, i32 119)
|
||||
store i8 %dst, i8* @chr
|
||||
ret void
|
||||
}
|
54
test/Transforms/InstCombine/strrchr-1.ll
Normal file
54
test/Transforms/InstCombine/strrchr-1.ll
Normal file
@ -0,0 +1,54 @@
|
||||
; Test that the strrchr library call simplifier works correctly.
|
||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
|
||||
|
||||
@hello = constant [14 x i8] c"hello world\5Cn\00"
|
||||
@null = constant [1 x i8] zeroinitializer
|
||||
@chp = global i8* zeroinitializer
|
||||
|
||||
declare i8* @strrchr(i8*, i32)
|
||||
|
||||
define void @test_simplify1() {
|
||||
; CHECK: store i8* getelementptr inbounds ([14 x i8]* @hello, i32 0, i32 6)
|
||||
; CHECK-NOT: call i8* @strrchr
|
||||
; CHECK: ret void
|
||||
|
||||
%str = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8* @strrchr(i8* %str, i32 119)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_simplify2() {
|
||||
; CHECK: store i8* null, i8** @chp, align 4
|
||||
; CHECK-NOT: call i8* @strrchr
|
||||
; CHECK: ret void
|
||||
|
||||
%str = getelementptr [1 x i8]* @null, i32 0, i32 0
|
||||
%dst = call i8* @strrchr(i8* %str, i32 119)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_simplify3() {
|
||||
; CHECK: store i8* getelementptr inbounds ([14 x i8]* @hello, i32 0, i32 13)
|
||||
; CHECK-NOT: call i8* @strrchr
|
||||
; CHECK: ret void
|
||||
|
||||
%src = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8* @strrchr(i8* %src, i32 0)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @test_nosimplify1(i32 %chr) {
|
||||
; CHECK: @test_nosimplify1
|
||||
; CHECK: call i8* @strrchr
|
||||
; CHECK: ret void
|
||||
|
||||
%src = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8* @strrchr(i8* %src, i32 %chr)
|
||||
store i8* %dst, i8** @chp
|
||||
ret void
|
||||
}
|
21
test/Transforms/InstCombine/strrchr-2.ll
Normal file
21
test/Transforms/InstCombine/strrchr-2.ll
Normal file
@ -0,0 +1,21 @@
|
||||
; Test that the strrchr libcall simplifier works correctly.
|
||||
;
|
||||
; RUN: opt < %s -instcombine -S | FileCheck %s
|
||||
|
||||
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128"
|
||||
|
||||
@hello = constant [14 x i8] c"hello world\5Cn\00"
|
||||
@chr = global i8 zeroinitializer
|
||||
|
||||
declare i8 @strrchr(i8*, i32)
|
||||
|
||||
define void @test_nosimplify1() {
|
||||
; CHECK: test_nosimplify1
|
||||
; CHECK: call i8 @strrchr
|
||||
; CHECK: ret void
|
||||
|
||||
%str = getelementptr [14 x i8]* @hello, i32 0, i32 0
|
||||
%dst = call i8 @strrchr(i8* %str, i32 119)
|
||||
store i8 %dst, i8* @chr
|
||||
ret void
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
; Test that the StrChrOptimizer works correctly
|
||||
; RUN: opt < %s -simplify-libcalls -S | FileCheck %s
|
||||
|
||||
; This transformation requires the pointer size, as it assumes that size_t is
|
||||
; the size of a pointer.
|
||||
target datalayout = "-p:64:64:64"
|
||||
|
||||
@hello = constant [14 x i8] c"hello world\5Cn\00"
|
||||
@null = constant [1 x i8] zeroinitializer
|
||||
|
||||
declare i8* @strchr(i8*, i32)
|
||||
|
||||
define i32 @foo(i32 %index) {
|
||||
%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* @strchr(i8* %hello_p, i32 119)
|
||||
; CHECK: getelementptr i8* %hello_p, i64 6
|
||||
%ignore = call i8* @strchr(i8* %null_p, i32 119)
|
||||
; CHECK-NOT: call i8* strchr
|
||||
%null = call i8* @strchr(i8* %hello_p, i32 0)
|
||||
; CHECK: getelementptr i8* %hello_p, i64 13
|
||||
%result = call i8* @strchr(i8* %hello_p, i32 %index)
|
||||
; CHECK: call i8* @memchr(i8* %hello_p, i32 %index, i64 14)
|
||||
ret i32 %index
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
; 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user