Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -150,6 +150,33 @@ return true; } +static bool isZeroString(Value *Str, CallInst *CI, + const TargetLibraryInfo *TLI) { + CallInst *Calloc = dyn_cast(Str); + if (!Calloc) + return false; + + Function *InnerCallee = Calloc->getCalledFunction(); + if (!InnerCallee) + return false; + + LibFunc Func; + if (!TLI->getLibFunc(*InnerCallee, Func) || !TLI->has(Func) || + Func != LibFunc_calloc) + return false; + + ConstantInt *N = dyn_cast(Calloc->getOperand(0)); + ConstantInt *Size = dyn_cast(Calloc->getOperand(1)); + + if (!N || !Size) + return false; + + if (N->getZExtValue() == 0 || Size->getZExtValue() == 0) + return false; + + return true; +} + //===----------------------------------------------------------------------===// // String and Memory Library Call Optimizations //===----------------------------------------------------------------------===// @@ -159,6 +186,11 @@ Value *Dst = CI->getArgOperand(0); Value *Src = CI->getArgOperand(1); + // strcat(calloc(...), str) -> strcpy(calloc(...), str)) + if (isZeroString(Dst, CI, TLI)) + return emitStrCpy(Dst, Src, B, TLI); + + // See if we can get the length of the input string. uint64_t Len = GetStringLength(Src); if (Len == 0) @@ -204,6 +236,12 @@ else return nullptr; + // strncat(calloc(...), str, len) -> strncpy(calloc(...), str, len)) + if (isZeroString(Dst, CI, TLI)) { + B.CreateMemCpy(Dst, 1, Src, 1, ConstantInt::get(DL.getIntPtrType(CI->getContext()), Len)); + return Dst; + } + // See if we can get the length of the input string. uint64_t SrcLen = GetStringLength(Src); if (SrcLen == 0) @@ -447,6 +485,10 @@ unsigned CharSize) { Value *Src = CI->getArgOperand(0); + // strlen(calloc(...)) + if (isZeroString(Src, CI, TLI)) + return ConstantInt::get(CI->getType(), 0); + // Constant folding: strlen("xyz") -> 3 if (uint64_t Len = GetStringLength(Src, CharSize)) return ConstantInt::get(CI->getType(), Len - 1); Index: test/Transforms/InstCombine/zero-string.ll =================================================================== --- test/Transforms/InstCombine/zero-string.ll +++ test/Transforms/InstCombine/zero-string.ll @@ -0,0 +1,122 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +target datalayout = "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128" + +declare i32 @strlen(i8* nocapture) +declare noalias i8* @calloc(i32, i32) +declare noalias i8* @malloc(i32) +declare i8* @strcat(i8*, i8* nocapture readonly) +declare i8* @strncat(i8*, i8* nocapture readonly, i32) + +define i32 @calloc_strlen() { +; CHECK-LABEL: @calloc_strlen( +; CHECK-NEXT: ret i32 0 +; + %call = tail call noalias i8* @calloc(i32 1, i32 10) + %call1 = tail call i32 @strlen(i8* %call) + ret i32 %call1 +} + +define i32 @calloc_strlen_not_const_nmemb(i32 %n) { +; CHECK-LABEL: @calloc_strlen_not_const_nmemb( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @calloc(i32 [[N:%.*]], i32 10) +; CHECK-NEXT: [[CALL1:%.*]] = tail call i32 @strlen(i8* [[CALL]]) +; CHECK-NEXT: ret i32 [[CALL1]] +; + %call = tail call noalias i8* @calloc(i32 %n, i32 10) + %call1 = tail call i32 @strlen(i8* %call) #4 + ret i32 %call1 +} + + +define i32 @calloc_strlen_not_const_size(i32 %size) { +; CHECK-LABEL: @calloc_strlen_not_const_size( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @calloc(i32 1, i32 [[SIZE:%.*]]) +; CHECK-NEXT: [[CALL1:%.*]] = tail call i32 @strlen(i8* [[CALL]]) +; CHECK-NEXT: ret i32 [[CALL1]] +; + %call = tail call noalias i8* @calloc(i32 1, i32 %size) + %call1 = tail call i32 @strlen(i8* %call) #4 + ret i32 %call1 +} + + +define i32 @calloc_strlen_not_const_args(i32 %n, i32 %size) { +; CHECK-LABEL: @calloc_strlen_not_const_args( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @calloc(i32 [[N:%.*]], i32 [[SIZE:%.*]]) +; CHECK-NEXT: [[CALL1:%.*]] = tail call i32 @strlen(i8* [[CALL]]) +; CHECK-NEXT: ret i32 [[CALL1]] +; + %call = tail call noalias i8* @calloc(i32 %n, i32 %size) + %call1 = tail call i32 @strlen(i8* %call) #4 + ret i32 %call1 +} + + +define i32 @malloc_strlen() { +; CHECK-LABEL: @malloc_strlen( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @malloc(i32 10) +; CHECK-NEXT: [[CALL1:%.*]] = tail call i32 @strlen(i8* [[CALL]]) +; CHECK-NEXT: ret i32 [[CALL1]] +; + %call = tail call noalias i8* @malloc(i32 10) + %call1 = tail call i32 @strlen(i8* %call) + ret i32 %call1 +} + +define i8* @malloc_strcat(i8* %str2) { +; CHECK-LABEL: @malloc_strcat( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @malloc(i32 10) +; CHECK-NEXT: [[CALL1:%.*]] = tail call i8* @strcat(i8* [[CALL]], i8* [[STR2:%.*]]) +; CHECK-NEXT: ret i8* [[CALL1]] +; + %call = tail call noalias i8* @malloc(i32 10) #3 + %call1 = tail call i8* @strcat(i8* %call, i8* %str2) + ret i8* %call1 +} + +define i8* @calloc_strcat(i8* %str2) { +; CHECK-LABEL: @calloc_strcat( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @calloc(i32 1, i32 10) +; CHECK-NEXT: [[STRCPY:%.*]] = call i8* @strcpy(i8* [[CALL]], i8* [[STR2:%.*]]) +; CHECK-NEXT: ret i8* [[STRCPY]] +; + %call = tail call noalias i8* @calloc(i32 1, i32 10) + %call1 = call i8* @strcat(i8* %call, i8* %str2) + ret i8* %call1 +} + + +define i8* @calloc_strncat(i8* %str2) { +; CHECK-LABEL: @calloc_strncat( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @calloc(i32 1, i32 10) +; CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[CALL]], i8* align 1 [[STR2:%.*]], i32 10, i1 false) +; CHECK-NEXT: ret i8* [[CALL]] +; + %call = tail call noalias i8* @calloc(i32 1, i32 10) + %call1 = tail call i8* @strncat(i8* %call, i8* %str2, i32 10) + ret i8* %call1 +} + +define i8* @malloc_strncat(i8* %str2) { +; CHECK-LABEL: @malloc_strncat( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @malloc(i32 10) +; CHECK-NEXT: [[CALL1:%.*]] = tail call i8* @strncat(i8* [[CALL]], i8* [[STR2:%.*]], i32 10) +; CHECK-NEXT: ret i8* [[CALL1]] +; + %call = tail call noalias i8* @malloc(i32 10) + %call1 = tail call i8* @strncat(i8* %call, i8* %str2, i32 10) + ret i8* %call1 +} + +define i8* @calloc_strncat_not_const(i8* %str2, i32 %n) { +; CHECK-LABEL: @calloc_strncat_not_const( +; CHECK-NEXT: [[CALL:%.*]] = tail call noalias i8* @calloc(i32 1, i32 10) +; CHECK-NEXT: [[CALL1:%.*]] = tail call i8* @strncat(i8* [[CALL]], i8* [[STR2:%.*]], i32 [[N:%.*]]) +; CHECK-NEXT: ret i8* [[CALL1]] +; + %call = tail call noalias i8* @calloc(i32 1, i32 10) #3 + %call1 = tail call i8* @strncat(i8* %call, i8* %str2, i32 %n) + ret i8* %call1 +}