Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -2402,12 +2402,7 @@ } Value *LibCallSimplifier::optimizeSnPrintFString(CallInst *CI, IRBuilder<> &B) { - // Check for size ConstantInt *Size = dyn_cast(CI->getArgOperand(1)); - if (!Size) - return nullptr; - - uint64_t N = Size->getZExtValue(); // Check for a fixed format string. StringRef FormatStr; if (!getConstantStringInfo(CI->getArgOperand(2), FormatStr)) @@ -2420,6 +2415,9 @@ if (FormatStr.find('%') != StringRef::npos) return nullptr; // we found a format specifier, bail out. + if (!Size) + return nullptr; + uint64_t N = Size->getZExtValue(); if (N == 0) return ConstantInt::get(CI->getType(), FormatStr.size()); else if (N < FormatStr.size() + 1) @@ -2441,6 +2439,9 @@ // Decode the second character of the format string. if (FormatStr[1] == 'c') { + if (!Size) + return nullptr; + uint64_t N = Size->getZExtValue(); if (N == 0) return ConstantInt::get(CI->getType(), 1); else if (N == 1) @@ -2461,9 +2462,17 @@ if (FormatStr[1] == 's') { // snprintf(dest, size, "%s", str) to llvm.memcpy(dest, str, len+1, 1) StringRef Str; - if (!getConstantStringInfo(CI->getArgOperand(3), Str)) + if (!getConstantStringInfo(CI->getArgOperand(3), Str)) { + if (CI->use_empty()) + // snprintf (d, size, "%s", s) -> memccpy (d, s, '\0', size). + return emitMemCCpy(CI->getOperand(0), CI->getArgOperand(3), + B.getInt32('\0'), CI->getArgOperand(1), B, TLI); return nullptr; + } + if (!Size) + return nullptr; + uint64_t N = Size->getZExtValue(); if (N == 0) return ConstantInt::get(CI->getType(), Str.size()); else if (N < Str.size() + 1) Index: test/Transforms/InstCombine/snprintf-memccpy.ll =================================================================== --- test/Transforms/InstCombine/snprintf-memccpy.ll +++ test/Transforms/InstCombine/snprintf-memccpy.ll @@ -0,0 +1,23 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +@.str = private constant [3 x i8] c"%s\00", align 1 +declare i32 @snprintf(i8*, i64, i8*, ...) + +define i32 @test_string_to_buf_retval_used(i8* %buf, i64 %n, i8* %str) { +; CHECK-LABEL: @test_string_to_buf_retval_used( +; CHECK-NEXT: [[CALL:%.*]] = call i32 (i8*, i64, i8*, ...) @snprintf(i8* [[BUF:%.*]], i64 [[N:%.*]], i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i8* [[STR:%.*]]) +; CHECK-NEXT: ret i32 [[CALL]] +; + %call = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %buf, i64 %n, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i8* %str) + ret i32 %call +} + +define void @test_string_to_buf_retval_unused(i8* %buf, i64 %n, i8* %str) { +; CHECK-LABEL: @test_string_to_buf_retval_unused( +; CHECK-NEXT: [[MEMCCPY:%.*]] = call i8* @memccpy(i8* [[BUF:%.*]], i8* [[STR:%.*]], i32 0, i64 [[N:%.*]]) +; CHECK-NEXT: ret void +; + %call = call i32 (i8*, i64, i8*, ...) @snprintf(i8* %buf, i64 %n, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i8* %str) + ret void +}