Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -150,6 +150,60 @@ return true; } +static bool isStrLenOfValue(Value *Len, Value *Src, const TargetLibraryInfo *TLI) { + auto *Strlen = dyn_cast(Len); + if (!Strlen) + return false; + + // Is the inner call really strlen()? + Function *InnerCallee = Strlen->getCalledFunction(); + if (!InnerCallee) + return false; + + LibFunc Func; + if (!TLI->getLibFunc(*InnerCallee, Func) || !TLI->has(Func) || + Func != LibFunc_strlen) + return false; + + return Strlen->getOperand(0) == Src; +} + +static Value *getStringLengthFromMalloc(Value *Dst, Value *Src, CallInst *CI, + IRBuilder<> &B, + const TargetLibraryInfo *TLI) { + auto *Malloc = dyn_cast(Dst); + if (!Malloc) + return nullptr; + + // Is the inner call really malloc()? + Function *InnerCallee = Malloc->getCalledFunction(); + if (!InnerCallee) + return nullptr; + + LibFunc Func; + if (!TLI->getLibFunc(*InnerCallee, Func) || !TLI->has(Func) || + Func != LibFunc_malloc) + return nullptr; + + auto *Op = cast(Malloc->getOperand(0)); + if (isStrLenOfValue(Op, Src, TLI)) { + return B.CreateAdd(Op, ConstantInt::get(Op->getType(), 1)); + } + + auto *BOp = dyn_cast(Op); + if (!BOp) + return nullptr; + + for (unsigned I = 0; I < 2; ++I) { + auto *Op = BOp->getOperand(I); + if (isStrLenOfValue(Op, Src, TLI)) { + return B.CreateAdd(Op, ConstantInt::get(Op->getType(), 1)); + } + } + + return nullptr; +} + //===----------------------------------------------------------------------===// // String and Memory Library Call Optimizations //===----------------------------------------------------------------------===// @@ -371,14 +425,21 @@ // See if we can get the length of the input string. uint64_t Len = GetStringLength(Src); - if (Len == 0) - return nullptr; + if (Len > 0) { + // We have enough information to now generate the memcpy call to do the + // copy for us. Make a memcpy to copy the nul byte with align = 1. + B.CreateMemCpy(Dst, 1, Src, 1, + ConstantInt::get(DL.getIntPtrType(CI->getContext()), Len)); + return Dst; + } - // We have enough information to now generate the memcpy call to do the - // copy for us. Make a memcpy to copy the nul byte with align = 1. - B.CreateMemCpy(Dst, 1, Src, 1, - ConstantInt::get(DL.getIntPtrType(CI->getContext()), Len)); - return Dst; + // strcpy(malloc(len + 1), str) -> memcpy(malloc(len), str, len + 1) + if (Value *Size = getStringLengthFromMalloc(Dst, Src, CI, B, TLI)) { + B.CreateMemCpy(Dst, 1, Src, 1, Size); + return Dst; + } + + return nullptr; } Value *LibCallSimplifier::optimizeStpCpy(CallInst *CI, IRBuilder<> &B) { Index: test/Transforms/InstCombine/malloc-strcpy.ll =================================================================== --- test/Transforms/InstCombine/malloc-strcpy.ll +++ test/Transforms/InstCombine/malloc-strcpy.ll @@ -0,0 +1,81 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +define i8* @test_ok(i8* nocapture readonly %s) { +; CHECK-LABEL: @test_ok( +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strlen(i8* [[S:%.*]]) +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[CALL]], 1 +; CHECK-NEXT: [[CALL1:%.*]] = tail call noalias i8* @malloc(i32 [[ADD]]) +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[CALL]], 1 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[CALL1]], i8* align 1 [[S]], i32 [[TMP1]], i1 false) +; CHECK-NEXT: ret i8* [[CALL1]] +; + %call = tail call i32 @strlen(i8* %s) #3 + %add = add i32 %call, 1 + %call1 = tail call noalias i8* @malloc(i32 %add) #4 + %call2 = tail call i8* @strcpy(i8* %call1, i8* %s) #4 + ret i8* %call1 +} + + +declare noalias i8* @malloc(i32) +declare i32 @strlen(i8* nocapture) +declare i8* @strcpy(i8*, i8* nocapture readonly) + + +define i8* @test_const_ok(i8* nocapture readonly %s, i32 %n) { +; CHECK-LABEL: @test_const_ok( +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strlen(i8* [[S:%.*]]) +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[CALL]], [[N:%.*]] +; CHECK-NEXT: [[CALL1:%.*]] = tail call noalias i8* @malloc(i32 [[ADD]]) +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[CALL]], 1 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[CALL1]], i8* align 1 [[S]], i32 [[TMP1]], i1 false) +; CHECK-NEXT: ret i8* [[CALL1]] +; + %call = tail call i32 @strlen(i8* %s) #3 + %add = add i32 %call, %n + %call1 = tail call noalias i8* @malloc(i32 %add) #4 + %call2 = tail call i8* @strcpy(i8* %call1, i8* %s) #4 + ret i8* %call1 +} + + +define i8* @test_strlen(i8* nocapture readonly %s, i32 %n) { +; CHECK-LABEL: @test_strlen( +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strlen(i8* [[S:%.*]]) +; CHECK-NEXT: [[CALL1:%.*]] = tail call noalias i8* @malloc(i32 [[CALL]]) +; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[CALL]], 1 +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[CALL1]], i8* align 1 [[S]], i32 [[TMP1]], i1 false) +; CHECK-NEXT: ret i8* [[CALL1]] +; + %call = tail call i32 @strlen(i8* %s) #3 + %call1 = tail call noalias i8* @malloc(i32 %call) #4 + %call2 = tail call i8* @strcpy(i8* %call1, i8* %s) #4 + ret i8* %call1 +} + + +define i8* @test_different_str(i8* nocapture readonly %s, i8* nocapture readonly %p) { +; CHECK-LABEL: @test_different_str( +; CHECK-NEXT: [[CALL:%.*]] = tail call i32 @strlen(i8* [[P:%.*]]) +; CHECK-NEXT: [[ADD:%.*]] = add i32 [[CALL]], 1 +; CHECK-NEXT: [[CALL1:%.*]] = tail call noalias i8* @malloc(i32 [[ADD]]) +; CHECK-NEXT: [[CALL2:%.*]] = tail call i8* @strcpy(i8* [[CALL1]], i8* [[S:%.*]]) +; CHECK-NEXT: ret i8* [[CALL1]] +; + %call = tail call i32 @strlen(i8* %p) #3 + %add = add i32 %call, 1 + %call1 = tail call noalias i8* @malloc(i32 %add) #4 + %call2 = tail call i8* @strcpy(i8* %call1, i8* %s) #4 + ret i8* %call1 +} + + +define i8* @test_no_malloc(i8* returned %s, i8* nocapture readonly %p) { +; CHECK-LABEL: @test_no_malloc( +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @strcpy(i8* [[S:%.*]], i8* [[P:%.*]]) +; CHECK-NEXT: ret i8* [[S]] +; + %call = tail call i8* @strcpy(i8* %s, i8* %p) #4 + ret i8* %s +}