mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-25 04:39:51 +00:00
instcombine: Migrate memcmp optimizations
This patch migrates the memcmp optimizations from the simplify-libcalls pass into the instcombine library call simplifier. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@167683 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
6e1591a5d5
commit
bb51ec8b62
@ -104,54 +104,6 @@ static bool CallHasFloatingPointArgument(const CallInst *CI) {
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
namespace {
|
||||
//===---------------------------------------===//
|
||||
// 'memcmp' Optimizations
|
||||
|
||||
struct MemCmpOpt : public LibCallOptimization {
|
||||
virtual Value *CallOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
if (FT->getNumParams() != 3 || !FT->getParamType(0)->isPointerTy() ||
|
||||
!FT->getParamType(1)->isPointerTy() ||
|
||||
!FT->getReturnType()->isIntegerTy(32))
|
||||
return 0;
|
||||
|
||||
Value *LHS = CI->getArgOperand(0), *RHS = CI->getArgOperand(1);
|
||||
|
||||
if (LHS == RHS) // memcmp(s,s,x) -> 0
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// Make sure we have a constant length.
|
||||
ConstantInt *LenC = dyn_cast<ConstantInt>(CI->getArgOperand(2));
|
||||
if (!LenC) return 0;
|
||||
uint64_t Len = LenC->getZExtValue();
|
||||
|
||||
if (Len == 0) // memcmp(s1,s2,0) -> 0
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// memcmp(S1,S2,1) -> *(unsigned char*)LHS - *(unsigned char*)RHS
|
||||
if (Len == 1) {
|
||||
Value *LHSV = B.CreateZExt(B.CreateLoad(CastToCStr(LHS, B), "lhsc"),
|
||||
CI->getType(), "lhsv");
|
||||
Value *RHSV = B.CreateZExt(B.CreateLoad(CastToCStr(RHS, B), "rhsc"),
|
||||
CI->getType(), "rhsv");
|
||||
return B.CreateSub(LHSV, RHSV, "chardiff");
|
||||
}
|
||||
|
||||
// Constant folding: memcmp(x, y, l) -> cnst (all arguments are constant)
|
||||
StringRef LHSStr, RHSStr;
|
||||
if (getConstantStringInfo(LHS, LHSStr) &&
|
||||
getConstantStringInfo(RHS, RHSStr)) {
|
||||
// Make sure we're not reading out-of-bounds memory.
|
||||
if (Len > LHSStr.size() || Len > RHSStr.size())
|
||||
return 0;
|
||||
uint64_t Ret = memcmp(LHSStr.data(), RHSStr.data(), Len);
|
||||
return ConstantInt::get(CI->getType(), Ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
//===---------------------------------------===//
|
||||
// 'memcpy' Optimizations
|
||||
|
||||
@ -887,7 +839,7 @@ namespace {
|
||||
|
||||
StringMap<LibCallOptimization*> Optimizations;
|
||||
// Memory LibCall Optimizations
|
||||
MemCmpOpt MemCmp; MemCpyOpt MemCpy; MemMoveOpt MemMove; MemSetOpt MemSet;
|
||||
MemCpyOpt MemCpy; MemMoveOpt MemMove; MemSetOpt MemSet;
|
||||
// Math Library Optimizations
|
||||
CosOpt Cos; PowOpt Pow; Exp2Opt Exp2;
|
||||
UnaryDoubleFPOpt UnaryDoubleFP, UnsafeUnaryDoubleFP;
|
||||
@ -954,7 +906,6 @@ void SimplifyLibCalls::AddOpt(LibFunc::Func F1, LibFunc::Func F2,
|
||||
/// we know.
|
||||
void SimplifyLibCalls::InitOptimizations() {
|
||||
// Memory LibCall Optimizations
|
||||
Optimizations["memcmp"] = &MemCmp;
|
||||
AddOpt(LibFunc::memcpy, &MemCpy);
|
||||
Optimizations["memmove"] = &MemMove;
|
||||
AddOpt(LibFunc::memset, &MemSet);
|
||||
|
@ -914,6 +914,51 @@ struct StrStrOpt : public LibCallOptimization {
|
||||
}
|
||||
};
|
||||
|
||||
struct MemCmpOpt : public LibCallOptimization {
|
||||
virtual Value *callOptimizer(Function *Callee, CallInst *CI, IRBuilder<> &B) {
|
||||
FunctionType *FT = Callee->getFunctionType();
|
||||
if (FT->getNumParams() != 3 || !FT->getParamType(0)->isPointerTy() ||
|
||||
!FT->getParamType(1)->isPointerTy() ||
|
||||
!FT->getReturnType()->isIntegerTy(32))
|
||||
return 0;
|
||||
|
||||
Value *LHS = CI->getArgOperand(0), *RHS = CI->getArgOperand(1);
|
||||
|
||||
if (LHS == RHS) // memcmp(s,s,x) -> 0
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// Make sure we have a constant length.
|
||||
ConstantInt *LenC = dyn_cast<ConstantInt>(CI->getArgOperand(2));
|
||||
if (!LenC) return 0;
|
||||
uint64_t Len = LenC->getZExtValue();
|
||||
|
||||
if (Len == 0) // memcmp(s1,s2,0) -> 0
|
||||
return Constant::getNullValue(CI->getType());
|
||||
|
||||
// memcmp(S1,S2,1) -> *(unsigned char*)LHS - *(unsigned char*)RHS
|
||||
if (Len == 1) {
|
||||
Value *LHSV = B.CreateZExt(B.CreateLoad(CastToCStr(LHS, B), "lhsc"),
|
||||
CI->getType(), "lhsv");
|
||||
Value *RHSV = B.CreateZExt(B.CreateLoad(CastToCStr(RHS, B), "rhsc"),
|
||||
CI->getType(), "rhsv");
|
||||
return B.CreateSub(LHSV, RHSV, "chardiff");
|
||||
}
|
||||
|
||||
// Constant folding: memcmp(x, y, l) -> cnst (all arguments are constant)
|
||||
StringRef LHSStr, RHSStr;
|
||||
if (getConstantStringInfo(LHS, LHSStr) &&
|
||||
getConstantStringInfo(RHS, RHSStr)) {
|
||||
// Make sure we're not reading out-of-bounds memory.
|
||||
if (Len > LHSStr.size() || Len > RHSStr.size())
|
||||
return 0;
|
||||
uint64_t Ret = memcmp(LHSStr.data(), RHSStr.data(), Len);
|
||||
return ConstantInt::get(CI->getType(), Ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // End anonymous namespace.
|
||||
|
||||
namespace llvm {
|
||||
@ -932,7 +977,7 @@ class LibCallSimplifierImpl {
|
||||
StpCpyChkOpt StpCpyChk;
|
||||
StrNCpyChkOpt StrNCpyChk;
|
||||
|
||||
// String and memory library call optimizations.
|
||||
// String library call optimizations.
|
||||
StrCatOpt StrCat;
|
||||
StrNCatOpt StrNCat;
|
||||
StrChrOpt StrChr;
|
||||
@ -949,6 +994,9 @@ class LibCallSimplifierImpl {
|
||||
StrCSpnOpt StrCSpn;
|
||||
StrStrOpt StrStr;
|
||||
|
||||
// Memory library call optimizations.
|
||||
MemCmpOpt MemCmp;
|
||||
|
||||
void initOptimizations();
|
||||
void addOpt(LibFunc::Func F, LibCallOptimization* Opt);
|
||||
public:
|
||||
@ -972,7 +1020,7 @@ void LibCallSimplifierImpl::initOptimizations() {
|
||||
Optimizations["__strncpy_chk"] = &StrNCpyChk;
|
||||
Optimizations["__stpncpy_chk"] = &StrNCpyChk;
|
||||
|
||||
// String and memory library call optimizations.
|
||||
// String library call optimizations.
|
||||
addOpt(LibFunc::strcat, &StrCat);
|
||||
addOpt(LibFunc::strncat, &StrNCat);
|
||||
addOpt(LibFunc::strchr, &StrChr);
|
||||
@ -994,6 +1042,9 @@ void LibCallSimplifierImpl::initOptimizations() {
|
||||
addOpt(LibFunc::strspn, &StrSpn);
|
||||
addOpt(LibFunc::strcspn, &StrCSpn);
|
||||
addOpt(LibFunc::strstr, &StrStr);
|
||||
|
||||
// Memory library call optimizations.
|
||||
addOpt(LibFunc::memcmp, &MemCmp);
|
||||
}
|
||||
|
||||
Value *LibCallSimplifierImpl::optimizeCall(CallInst *CI) {
|
||||
|
63
test/Transforms/InstCombine/memcmp-1.ll
Normal file
63
test/Transforms/InstCombine/memcmp-1.ll
Normal file
@ -0,0 +1,63 @@
|
||||
; Test that the memcmp 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"
|
||||
|
||||
@foo = constant [4 x i8] c"foo\00"
|
||||
@hel = constant [4 x i8] c"hel\00"
|
||||
@hello_u = constant [8 x i8] c"hello_u\00"
|
||||
|
||||
declare i32 @memcmp(i8*, i8*, i32)
|
||||
|
||||
; Check memcmp(mem, mem, size) -> 0.
|
||||
|
||||
define i32 @test_simplify1(i8* %mem, i32 %size) {
|
||||
; CHECK: @test_simplify1
|
||||
%ret = call i32 @memcmp(i8* %mem, i8* %mem, i32 %size)
|
||||
ret i32 %ret
|
||||
; CHECK: ret i32 0
|
||||
}
|
||||
|
||||
; Check memcmp(mem1, mem2, 0) -> 0.
|
||||
|
||||
define i32 @test_simplify2(i8* %mem1, i8* %mem2) {
|
||||
; CHECK: @test_simplify2
|
||||
%ret = call i32 @memcmp(i8* %mem1, i8* %mem2, i32 0)
|
||||
ret i32 %ret
|
||||
; CHECK: ret i32 0
|
||||
}
|
||||
|
||||
;; Check memcmp(mem1, mem2, 1) -> *(unsigned char*)mem1 - *(unsigned char*)mem2.
|
||||
|
||||
define i32 @test_simplify3(i8* %mem1, i8* %mem2) {
|
||||
; CHECK: @test_simplify3
|
||||
%ret = call i32 @memcmp(i8* %mem1, i8* %mem2, i32 1)
|
||||
; CHECK: [[LOAD1:%[a-z]+]] = load i8* %mem1, align 1
|
||||
; CHECK: [[ZEXT1:%[a-z]+]] = zext i8 [[LOAD1]] to i32
|
||||
; CHECK: [[LOAD2:%[a-z]+]] = load i8* %mem2, align 1
|
||||
; CHECK: [[ZEXT2:%[a-z]+]] = zext i8 [[LOAD2]] to i32
|
||||
; CHECK: [[RET:%[a-z]+]] = sub i32 [[ZEXT1]], [[ZEXT2]]
|
||||
ret i32 %ret
|
||||
; CHECK: ret i32 [[RET]]
|
||||
}
|
||||
|
||||
; Check memcmp(mem1, mem2, size) -> cnst, where all arguments are constants.
|
||||
|
||||
define i32 @test_simplify4() {
|
||||
; CHECK: @test_simplify4
|
||||
%mem1 = getelementptr [4 x i8]* @hel, i32 0, i32 0
|
||||
%mem2 = getelementptr [8 x i8]* @hello_u, i32 0, i32 0
|
||||
%ret = call i32 @memcmp(i8* %mem1, i8* %mem2, i32 3)
|
||||
ret i32 %ret
|
||||
; CHECK: ret i32 0
|
||||
}
|
||||
|
||||
define i32 @test_simplify5() {
|
||||
; CHECK: @test_simplify5
|
||||
%mem1 = getelementptr [4 x i8]* @hel, i32 0, i32 0
|
||||
%mem2 = getelementptr [4 x i8]* @foo, i32 0, i32 0
|
||||
%ret = call i32 @memcmp(i8* %mem1, i8* %mem2, i32 3)
|
||||
ret i32 %ret
|
||||
; CHECK: ret i32 2
|
||||
}
|
17
test/Transforms/InstCombine/memcmp-2.ll
Normal file
17
test/Transforms/InstCombine/memcmp-2.ll
Normal file
@ -0,0 +1,17 @@
|
||||
; Test that the memcmp 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"
|
||||
|
||||
declare i32* @memcmp(i8*, i8*, i32)
|
||||
|
||||
; Check that memcmp functions with the wrong prototype aren't simplified.
|
||||
|
||||
define i32* @test_no_simplify1(i8* %mem, i32 %size) {
|
||||
; CHECK: @test_no_simplify1
|
||||
%ret = call i32* @memcmp(i8* %mem, i8* %mem, i32 %size)
|
||||
; CHECK-NEXT: call i32* @memcmp
|
||||
ret i32* %ret
|
||||
; CHECK-NEXT: ret i32* %ret
|
||||
}
|
@ -67,12 +67,14 @@ define i32 @test5() {
|
||||
}
|
||||
|
||||
; strncmp(x,y,1) -> memcmp(x,y,1)
|
||||
; TODO: Once the memcmp simplifier gets moved into the instcombine pass
|
||||
; the following memcmp will be folded into two loads and a subtract.
|
||||
define i32 @test6(i8* %str1, i8* %str2) {
|
||||
; CHECK: @test6
|
||||
; CHECK: call i32 @memcmp
|
||||
; CHECK: ret i32 %memcmp
|
||||
; CHECK: [[LOAD1:%[a-z]+]] = load i8* %str1, align 1
|
||||
; CHECK: [[ZEXT1:%[a-z]+]] = zext i8 [[LOAD1]] to i32
|
||||
; CHECK: [[LOAD2:%[a-z]+]] = load i8* %str2, align 1
|
||||
; CHECK: [[ZEXT2:%[a-z]+]] = zext i8 [[LOAD2]] to i32
|
||||
; CHECK: [[RET:%[a-z]+]] = sub i32 [[ZEXT1]], [[ZEXT2]]
|
||||
; CHECK: ret i32 [[RET]]
|
||||
|
||||
%temp1 = call i32 @strncmp(i8* %str1, i8* %str2, i32 1)
|
||||
ret i32 %temp1
|
||||
|
@ -1,35 +0,0 @@
|
||||
; Test that the memcmpOptimizer works correctly
|
||||
; RUN: opt < %s -simplify-libcalls -S | FileCheck %s
|
||||
|
||||
@h = constant [2 x i8] c"h\00" ; <[2 x i8]*> [#uses=0]
|
||||
@hel = constant [4 x i8] c"hel\00" ; <[4 x i8]*> [#uses=0]
|
||||
@hello_u = constant [8 x i8] c"hello_u\00" ; <[8 x i8]*> [#uses=0]
|
||||
|
||||
declare i32 @memcmp(i8*, i8*, i32)
|
||||
|
||||
define void @test(i8* %P, i8* %Q, i32 %N, i32* %IP, i1* %BP) {
|
||||
%A = call i32 @memcmp( i8* %P, i8* %P, i32 %N ) ; <i32> [#uses=1]
|
||||
; CHECK-NOT: call {{.*}} memcmp
|
||||
; CHECK: store volatile
|
||||
store volatile i32 %A, i32* %IP
|
||||
%B = call i32 @memcmp( i8* %P, i8* %Q, i32 0 ) ; <i32> [#uses=1]
|
||||
; CHECK-NOT: call {{.*}} memcmp
|
||||
; CHECK: store volatile
|
||||
store volatile i32 %B, i32* %IP
|
||||
%C = call i32 @memcmp( i8* %P, i8* %Q, i32 1 ) ; <i32> [#uses=1]
|
||||
; CHECK: load
|
||||
; CHECK: zext
|
||||
; CHECK: load
|
||||
; CHECK: zext
|
||||
; CHECK: sub
|
||||
; CHECK: store volatile
|
||||
store volatile i32 %C, i32* %IP
|
||||
%F = call i32 @memcmp(i8* getelementptr ([4 x i8]* @hel, i32 0, i32 0),
|
||||
i8* getelementptr ([8 x i8]* @hello_u, i32 0, i32 0),
|
||||
i32 3)
|
||||
; CHECK-NOT: call {{.*}} memcmp
|
||||
; CHECK: store volatile
|
||||
store volatile i32 %F, i32* %IP
|
||||
ret void
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user