Index: include/llvm/Transforms/Utils/SimplifyLibCalls.h =================================================================== --- include/llvm/Transforms/Utils/SimplifyLibCalls.h +++ include/llvm/Transforms/Utils/SimplifyLibCalls.h @@ -175,6 +175,7 @@ Value *optimizeMemCmp(CallInst *CI, IRBuilder<> &B); Value *optimizeBCmp(CallInst *CI, IRBuilder<> &B); Value *optimizeMemCmpBCmpCommon(CallInst *CI, IRBuilder<> &B); + Value *optimizeMemCCpy(CallInst *CI, IRBuilder<> &B); Value *optimizeMemPCpy(CallInst *CI, IRBuilder<> &B); Value *optimizeMemCpy(CallInst *CI, IRBuilder<> &B); Value *optimizeMemMove(CallInst *CI, IRBuilder<> &B); Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -1118,6 +1118,32 @@ return CI->getArgOperand(0); } +Value *LibCallSimplifier::optimizeMemCCpy(CallInst *CI, IRBuilder<> &B) { + Value *Dst = CI->getArgOperand(0); + Value *Src = CI->getArgOperand(1); + ConstantInt *StopChar = dyn_cast(CI->getArgOperand(2)); + ConstantInt *N = dyn_cast(CI->getArgOperand(3)); + StringRef SrcStr; + if (!N) + return nullptr; + // memccpy(d, s, c, 0) -> nullptr + if (N->isNullValue()) + return Constant::getNullValue(CI->getType()); + if (!getConstantStringInfo(Src, SrcStr) || !StopChar) + return nullptr; + + size_t Pos = SrcStr.find(StopChar->getSExtValue()); + if (Pos == StringRef::npos) + // memccpy(d, s, c, 0) and c not in s -> nullptr + return Constant::getNullValue(CI->getType()); + + Value *NewN = + ConstantInt::get(N->getType(), std::min(Pos + 1, N->getZExtValue())); + // memccpy -> llvm.memcpy + B.CreateMemCpy(Dst, 1, Src, 1, NewN); + return B.CreateInBoundsGEP(B.getInt8Ty(), Dst, NewN); +} + Value *LibCallSimplifier::optimizeMemPCpy(CallInst *CI, IRBuilder<> &B) { Value *Dst = CI->getArgOperand(0); Value *N = CI->getArgOperand(2); @@ -2752,6 +2778,8 @@ return optimizeMemCmp(CI, Builder); case LibFunc_memcpy: return optimizeMemCpy(CI, Builder); + case LibFunc_memccpy: + return optimizeMemCCpy(CI, Builder); case LibFunc_mempcpy: return optimizeMemPCpy(CI, Builder); case LibFunc_memmove: Index: test/Transforms/InstCombine/memccpy.ll =================================================================== --- test/Transforms/InstCombine/memccpy.ll +++ test/Transforms/InstCombine/memccpy.ll @@ -0,0 +1,80 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +@hello = private constant [11 x i8] c"helloworld\00", align 1 + +declare i8* @memccpy(i8*, i8*, i32, i64) + +define i8* @memccpy_to_memcpy(i8* %dst) { +; CHECK-LABEL: @memccpy_to_memcpy( +; CHECK-NEXT: [[TMP1:%.*]] = bitcast i8* [[DST:%.*]] to i64* +; CHECK-NEXT: store i64 8245940763182785896, i64* [[TMP1]], align 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 8 +; CHECK-NEXT: ret i8* [[TMP2]] +; + %call = call i8* @memccpy(i8* %dst, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 114, i64 12) ; 114 is 'r' + ret i8* %call +} + +define i8* @memccpy_to_memcpy2(i8* %dst) { +; CHECK-LABEL: @memccpy_to_memcpy2( +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(5) [[DST:%.*]], i8* nonnull align 1 dereferenceable(5) getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i64 5, i1 false) +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, i8* [[DST]], i64 5 +; CHECK-NEXT: ret i8* [[TMP1]] +; + %call = call i8* @memccpy(i8* %dst, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 114, i64 5) + ret i8* %call +} + +define void @memccpy_to_memcpy3(i8* %dst) { +; CHECK-LABEL: @memccpy_to_memcpy3( +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* nonnull align 1 dereferenceable(5) [[DST:%.*]], i8* nonnull align 1 dereferenceable(5) getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i64 5, i1 false) +; CHECK-NEXT: ret void +; + %call = call i8* @memccpy(i8* %dst, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 114, i64 5) + ret void +} + +define i8* @memccpy_to_null(i8* %dst, i8* %src, i32 %c) { +; CHECK-LABEL: @memccpy_to_null( +; CHECK-NEXT: ret i8* null +; + %call = call i8* @memccpy(i8* %dst, i8* %src, i32 %c, i64 0) + ret i8* %call +} + +define i8* @memccpy_to_null2(i8* %dst) { +; CHECK-LABEL: @memccpy_to_null2( +; CHECK-NEXT: ret i8* null +; + %call = call i8* @memccpy(i8* %dst, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 115, i64 5) ; 115 is 's' + ret i8* %call +} + +; Negative tests +define i8* @unknown_src(i8* %dst, i8* %src) { +; CHECK-LABEL: @unknown_src( +; CHECK-NEXT: [[CALL:%.*]] = call i8* @memccpy(i8* [[DST:%.*]], i8* [[SRC:%.*]], i32 114, i64 12) +; CHECK-NEXT: ret i8* [[CALL]] +; + %call = call i8* @memccpy(i8* %dst, i8* %src, i32 114, i64 12) + ret i8* %call +} + +define i8* @unknown_stop_char(i8* %dst, i32 %c) { +; CHECK-LABEL: @unknown_stop_char( +; CHECK-NEXT: [[CALL:%.*]] = call i8* @memccpy(i8* [[DST:%.*]], i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 [[C:%.*]], i64 12) +; CHECK-NEXT: ret i8* [[CALL]] +; + %call = call i8* @memccpy(i8* %dst, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 %c, i64 12) + ret i8* %call +} + +define i8* @unknown_size_n(i8* %dst, i64 %n) { +; CHECK-LABEL: @unknown_size_n( +; CHECK-NEXT: [[CALL:%.*]] = call i8* @memccpy(i8* [[DST:%.*]], i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 114, i64 [[N:%.*]]) +; CHECK-NEXT: ret i8* [[CALL]] +; + %call = call i8* @memccpy(i8* %dst, i8* getelementptr inbounds ([11 x i8], [11 x i8]* @hello, i64 0, i64 0), i32 114, i64 %n) + ret i8* %call +}