Index: include/llvm/Transforms/Utils/BuildLibCalls.h =================================================================== --- include/llvm/Transforms/Utils/BuildLibCalls.h +++ include/llvm/Transforms/Utils/BuildLibCalls.h @@ -63,6 +63,10 @@ Value *emitStrChr(Value *Ptr, char C, IRBuilder<> &B, const TargetLibraryInfo *TLI); + /// Emit a call to the strcmp function to the builder. + Value *emitStrCmp(Value *Ptr1, Value *Ptr2, IRBuilder<> &B, + const DataLayout &DL, const TargetLibraryInfo *TLI); + /// Emit a call to the strncmp function to the builder. Value *emitStrNCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilder<> &B, const DataLayout &DL, const TargetLibraryInfo *TLI); Index: lib/Transforms/Utils/BuildLibCalls.cpp =================================================================== --- lib/Transforms/Utils/BuildLibCalls.cpp +++ lib/Transforms/Utils/BuildLibCalls.cpp @@ -835,6 +835,13 @@ {castToCStr(Ptr, B), ConstantInt::get(I32Ty, C)}, B, TLI); } +Value *llvm::emitStrCmp(Value *Ptr1, Value *Ptr2, IRBuilder<> &B, + const DataLayout &DL, const TargetLibraryInfo *TLI) { + return emitLibCall(LibFunc_strcmp, B.getInt32Ty(), + {B.getInt8PtrTy(), B.getInt8PtrTy()}, + {castToCStr(Ptr1, B), castToCStr(Ptr2, B)}, B, TLI); +} + Value *llvm::emitStrNCmp(Value *Ptr1, Value *Ptr2, Value *Len, IRBuilder<> &B, const DataLayout &DL, const TargetLibraryInfo *TLI) { LLVMContext &Context = B.GetInsertBlock()->getContext(); Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -379,21 +379,45 @@ Value *LibCallSimplifier::optimizeStrNCmp(CallInst *CI, IRBuilder<> &B) { Value *Str1P = CI->getArgOperand(0), *Str2P = CI->getArgOperand(1); + Value *Len = CI->getArgOperand(2); if (Str1P == Str2P) // strncmp(x,x,n) -> 0 return ConstantInt::get(CI->getType(), 0); // Get the length argument if it is constant. uint64_t Length; - if (ConstantInt *LengthArg = dyn_cast(CI->getArgOperand(2))) + if (ConstantInt *LengthArg = dyn_cast(Len)) { Length = LengthArg->getZExtValue(); - else - return nullptr; + } else { + // strncmp(x,y,strlen(x|y)+1) -> strcmp(x,y) + Value *Call; + if (match(Len, m_Add(m_Value(Call), m_One()))) { + auto *Strlen = dyn_cast(Call); + if (!Strlen) + return nullptr; + auto *StrlenStr = Strlen->getOperand(0); + + // Is the inner call strlen()? + Function *InnerCallee = Strlen->getCalledFunction(); + if (!InnerCallee) + return nullptr; + + LibFunc Func; + if (!TLI->getLibFunc(*InnerCallee, Func) || !TLI->has(Func) || + Func != LibFunc_strlen) + return nullptr; + + if (StrlenStr != Str1P && StrlenStr != Str2P) + return nullptr; + + return emitStrCmp(Str1P, Str2P, B, DL, TLI); + } + } if (Length == 0) // strncmp(x,y,0) -> 0 return ConstantInt::get(CI->getType(), 0); if (Length == 1) // strncmp(x,y,1) -> memcmp(x,y,1) - return emitMemCmp(Str1P, Str2P, CI->getArgOperand(2), B, DL, TLI); + return emitMemCmp(Str1P, Str2P, Len, B, DL, TLI); StringRef Str1, Str2; bool HasStr1 = getConstantStringInfo(Str1P, Str1); Index: test/Transforms/InstCombine/strncmp-strlen.ll =================================================================== --- test/Transforms/InstCombine/strncmp-strlen.ll +++ test/Transforms/InstCombine/strncmp-strlen.ll @@ -0,0 +1,97 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; Test that the strncmp library call simplifier works correctly. +; RUN: opt < %s -instcombine -S | FileCheck %s + +declare i32 @strncmp(i8* nocapture, i8* nocapture, i64) +declare i64 @strlen(i8* nocapture) +declare void @extra_use(i64) + +define i32 @strncmp_strlen(i8* %s, i8* %k) { +; CHECK-LABEL: @strncmp_strlen( +; CHECK-NEXT: [[STRCMP:%.*]] = call i32 @strcmp(i8* [[S:%.*]], i8* [[K:%.*]]) +; CHECK-NEXT: ret i32 [[STRCMP]] +; + %len = tail call i64 @strlen(i8* %s) + %len1 = add i64 %len, 1 + %r = tail call i32 @strncmp(i8* %s, i8* %k, i64 %len1) + ret i32 %r +} + +define i32 @strncmp_strlen2(i8* %s, i8* %k) { +; CHECK-LABEL: @strncmp_strlen2( +; CHECK-NEXT: [[STRCMP:%.*]] = call i32 @strcmp(i8* [[S:%.*]], i8* [[K:%.*]]) +; CHECK-NEXT: ret i32 [[STRCMP]] +; + %len = tail call i64 @strlen(i8* %k) + %len1 = add i64 %len, 1 + %r = tail call i32 @strncmp(i8* %s, i8* %k, i64 %len1) + ret i32 %r +} + +define i32 @strncmp_strlen_multiuse(i8* %s, i8* %k) { +; CHECK-LABEL: @strncmp_strlen_multiuse( +; CHECK-NEXT: [[LEN:%.*]] = tail call i64 @strlen(i8* [[S:%.*]]) +; CHECK-NEXT: call void @extra_use(i64 [[LEN]]) +; CHECK-NEXT: [[STRCMP:%.*]] = call i32 @strcmp(i8* [[S]], i8* [[K:%.*]]) +; CHECK-NEXT: ret i32 [[STRCMP]] +; + %len = tail call i64 @strlen(i8* %s) + call void @extra_use(i64 %len) + %len1 = add i64 %len, 1 + %r = tail call i32 @strncmp(i8* %s, i8* %k, i64 %len1) + ret i32 %r +} + +define i32 @strncmp_strlen_multiuse2(i8* %s, i8* %k) { +; CHECK-LABEL: @strncmp_strlen_multiuse2( +; CHECK-NEXT: [[LEN:%.*]] = tail call i64 @strlen(i8* [[S:%.*]]) +; CHECK-NEXT: [[LEN1:%.*]] = add i64 [[LEN]], 1 +; CHECK-NEXT: call void @extra_use(i64 [[LEN1]]) +; CHECK-NEXT: [[STRCMP:%.*]] = call i32 @strcmp(i8* [[S]], i8* [[K:%.*]]) +; CHECK-NEXT: ret i32 [[STRCMP]] +; + %len = tail call i64 @strlen(i8* %s) + %len1 = add i64 %len, 1 + call void @extra_use(i64 %len1) + %r = tail call i32 @strncmp(i8* %s, i8* %k, i64 %len1) + ret i32 %r +} + +; Negative tests + +define i32 @strncmp_strlen_neg1(i8* %s, i8* %k) { +; CHECK-LABEL: @strncmp_strlen_neg1( +; CHECK-NEXT: [[LEN:%.*]] = tail call i64 @strlen(i8* [[S:%.*]]) +; CHECK-NEXT: [[R:%.*]] = tail call i32 @strncmp(i8* [[S]], i8* [[K:%.*]], i64 [[LEN]]) +; CHECK-NEXT: ret i32 [[R]] +; + %len = tail call i64 @strlen(i8* %s) + %r = tail call i32 @strncmp(i8* %s, i8* %k, i64 %len) + ret i32 %r +} + +define i32 @strncmp_strlen_neg2(i8* %s, i8* %k) { +; CHECK-LABEL: @strncmp_strlen_neg2( +; CHECK-NEXT: [[LEN:%.*]] = tail call i64 @strlen(i8* [[S:%.*]]) +; CHECK-NEXT: [[LEN1:%.*]] = add i64 [[LEN]], 2 +; CHECK-NEXT: [[R:%.*]] = tail call i32 @strncmp(i8* [[S]], i8* [[K:%.*]], i64 [[LEN1]]) +; CHECK-NEXT: ret i32 [[R]] +; + %len = tail call i64 @strlen(i8* %s) + %len1 = add i64 %len, 2 + %r = tail call i32 @strncmp(i8* %s, i8* %k, i64 %len1) + ret i32 %r +} + +define i32 @strncmp_strlen_neg3(i8* %s, i8* %k, i8* %d) { +; CHECK-LABEL: @strncmp_strlen_neg3( +; CHECK-NEXT: [[LEN:%.*]] = tail call i64 @strlen(i8* [[D:%.*]]) +; CHECK-NEXT: [[LEN1:%.*]] = add i64 [[LEN]], 1 +; CHECK-NEXT: [[R:%.*]] = tail call i32 @strncmp(i8* [[S:%.*]], i8* [[K:%.*]], i64 [[LEN1]]) +; CHECK-NEXT: ret i32 [[R]] +; + %len = tail call i64 @strlen(i8* %d) + %len1 = add i64 %len, 1 + %r = tail call i32 @strncmp(i8* %s, i8* %k, i64 %len1) + ret i32 %r +}