Index: include/llvm/Transforms/Utils/BuildLibCalls.h =================================================================== --- include/llvm/Transforms/Utils/BuildLibCalls.h +++ include/llvm/Transforms/Utils/BuildLibCalls.h @@ -16,6 +16,7 @@ #define LLVM_TRANSFORMS_UTILS_BUILDLIBCALLS_H #include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/ADT/APInt.h" #include "llvm/IR/IRBuilder.h" namespace llvm { @@ -159,6 +160,9 @@ Value *emitFReadUnlocked(Value *Ptr, Value *Size, Value *N, Value *File, IRBuilder<> &B, const DataLayout &DL, const TargetLibraryInfo *TLI); -} + /// Emit a call to the printf function. Vector Args contains an arguments. + Value *emitPrintF(ArrayRef Args, IRBuilder<> &B, + const TargetLibraryInfo *TLI); + } #endif Index: lib/Transforms/Utils/BuildLibCalls.cpp =================================================================== --- lib/Transforms/Utils/BuildLibCalls.cpp +++ lib/Transforms/Utils/BuildLibCalls.cpp @@ -1198,3 +1198,18 @@ CI->setCallingConv(Fn->getCallingConv()); return CI; } + +Value *llvm::emitPrintF(ArrayRef Args, IRBuilder<> &B, + const TargetLibraryInfo *TLI) { + if (!TLI->has(LibFunc_printf)) + return nullptr; + + Module *M = B.GetInsertBlock()->getModule(); + FunctionType *FT = FunctionType::get(B.getInt32Ty(), B.getInt8PtrTy(), true); + Value *PrintF = M->getOrInsertFunction("printf", FT); + inferLibFuncAttributes(*M->getFunction("printf"), *TLI); + CallInst *CI = B.CreateCall(PrintF, Args, "printf"); + if (const Function *F = dyn_cast(PrintF->stripPointerCasts())) + CI->setCallingConv(F->getCallingConv()); + return CI; +} Index: lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- lib/Transforms/Utils/SimplifyLibCalls.cpp +++ lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -150,6 +150,93 @@ return true; } +static Value *optimizeFormatString(StringRef FmtStr, unsigned FmtStrIndex, + CallInst *CI, IRBuilder<> &B, + SmallVector &Args) { + assert(CI->getNumArgOperands() > FmtStrIndex + 1 && + "Nothing to fold in optimizeFormatString"); + std::string NewFmtStr = FmtStr; + bool Changed = false; + unsigned OpCounter = FmtStrIndex; + const unsigned FmtTypeIdLen = 2; // support %s, %d, etc for now + + int NextChar; + StringRef Str; + Value *Op; + for (unsigned I = 0; I < NewFmtStr.size() - 1; ++I) { + if (NewFmtStr[I] != '%') + continue; + + NextChar = NewFmtStr[I + 1]; + if (NextChar == '%') // skip %% + continue; + + ++OpCounter; + Op = CI->getArgOperand(OpCounter); + if (!isa(Op)) + continue; + + Changed = false; + switch (NextChar) { + case 's': { + if (getConstantStringInfo(Op, Str)) { + NewFmtStr.replace(I, FmtTypeIdLen, Str); + Changed = true; + // dont move to next char when printf("%s%d", "", 1) + if (Str.empty()) + --I; + } else { + Args.push_back(Op); + } + break; + } + + case 'c': { + if (ConstantInt *CInt = dyn_cast(Op)) { + NewFmtStr.replace(I, FmtTypeIdLen, + std::string(1, CInt->getZExtValue())); + Changed = true; + } else { + Args.push_back(Op); + } + break; + } + + case 'u': { + if (ConstantInt *CInt = dyn_cast(Op)) { + NewFmtStr.replace(I, FmtTypeIdLen, + CInt->getValue().toString(10, false /* isSigned */)); + Changed = true; + } else { + Args.push_back(Op); + } + break; + } + + case 'i': + case 'd': { + if (ConstantInt *CInt = dyn_cast(Op)) { + NewFmtStr.replace(I, FmtTypeIdLen, + CInt->getValue().toString(10, true /* isSigned */)); + Changed = true; + } else { + Args.push_back(Op); + } + break; + } + + default: { + Args.push_back(Op); + break; + } + } + + ++I; + } + + return Changed ? B.CreateGlobalStringPtr(NewFmtStr, "str") : nullptr; +} + //===----------------------------------------------------------------------===// // String and Memory Library Call Optimizations //===----------------------------------------------------------------------===// @@ -1783,12 +1870,19 @@ Value *LibCallSimplifier::optimizePrintFString(CallInst *CI, IRBuilder<> &B) { // Check for a fixed format string. StringRef FormatStr; - if (!getConstantStringInfo(CI->getArgOperand(0), FormatStr)) + Value *Op1 = CI->getArgOperand(0); + if (!getConstantStringInfo(Op1, FormatStr)) return nullptr; - // Empty format string -> noop. - if (FormatStr.empty()) // Tolerate printf's declared void. - return CI->use_empty() ? (Value *)CI : ConstantInt::get(CI->getType(), 0); + // Optimize format string with args + if (CI->getNumArgOperands() > 1) { + SmallVector Args; + Args.push_back(Op1); + if (Value *V = optimizeFormatString(FormatStr, 0, CI, B, Args)) { + Args[0] = castToCStr(V, B); // Use new format arg + return emitPrintF(Args, B, TLI); + } + } // Do not do any of the following transformations if the printf return value // is used, in general the printf return value is not compatible with either @@ -1796,6 +1890,10 @@ if (!CI->use_empty()) return nullptr; + // Empty format string -> noop. + if (FormatStr.empty()) + return ConstantInt::get(CI->getType(), 0); + // printf("x") -> putchar('x'), even for "%" and "%%". if (FormatStr.size() == 1 || FormatStr == "%%") return emitPutChar(B.getInt32(FormatStr[0]), B, TLI); Index: test/CodeGen/X86/no-plt-libcalls.ll =================================================================== --- test/CodeGen/X86/no-plt-libcalls.ll +++ test/CodeGen/X86/no-plt-libcalls.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; Check if "RtLibUseGOT" works correctly when lib calls are simplified. ; RUN: opt < %s -instcombine -S | FileCheck %s @@ -5,10 +6,13 @@ @hello_world = constant [13 x i8] c"hello world\0A\00" declare i32 @printf(i8*, ...) define void @printf_call() { +; CHECK-LABEL: @printf_call( +; CHECK-NEXT: [[PUTS:%.*]] = call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @str.1, i64 0, i64 0)) +; CHECK-NEXT: ret void +; %fmt = getelementptr [4 x i8], [4 x i8]* @percent_s, i32 0, i32 0 %str = getelementptr [13 x i8], [13 x i8]* @hello_world, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt, i8* %str) -; CHECK: call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @hello_world, i64 0, i64 0)) ret void } Index: test/Transforms/InstCombine/format-str.ll =================================================================== --- test/Transforms/InstCombine/format-str.ll +++ test/Transforms/InstCombine/format-str.ll @@ -0,0 +1,141 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +@.str = private unnamed_addr constant [9 x i8] c"char: %c\00", align 1 +@.str.1 = private unnamed_addr constant [8 x i8] c"int: %d\00", align 1 +@.str.2 = private unnamed_addr constant [8 x i8] c"int: %i\00", align 1 +@.str.3 = private unnamed_addr constant [9 x i8] c"uint: %u\00", align 1 +@.str.4 = private unnamed_addr constant [8 x i8] c"str: %s\00", align 1 +@.str.5 = private unnamed_addr constant [4 x i8] c"str\00", align 1 +@.str.6 = private unnamed_addr constant [36 x i8] c"char %c, int %d, string %s, uint %u\00", align 1 +@.str.7 = private unnamed_addr constant [12 x i8] c"%s: int: %d\00", align 1 +@.str.8 = private unnamed_addr constant [5 x i8] c"test\00", align 1 +@.str.9 = private unnamed_addr constant [13 x i8] c"%s: char: %d\00", align 1 +@.str.10 = private unnamed_addr constant [13 x i8] c"%s: uint: %u\00", align 1 +@.str.11 = private unnamed_addr constant [13 x i8] c"%d: char: %d\00", align 1 +@.str.12 = private unnamed_addr constant [5 x i8] c"%s%d\00", align 1 +@.str.13 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 + +; CHECK: @str = private unnamed_addr constant [8 x i8] c"char: A\00" +; CHECK: @str.1 = private unnamed_addr constant [7 x i8] c"int: 5\00" +; CHECK: @str.2 = private unnamed_addr constant [7 x i8] c"int: 6\00" +; CHECK: @str.3 = private unnamed_addr constant [8 x i8] c"uint: 7\00" +; CHECK: @str.4 = private unnamed_addr constant [9 x i8] c"str: str\00" +; CHECK: @str.5 = private unnamed_addr constant [34 x i8] c"char A, int 5, string str, uint 7\00" +; CHECK: @str.6 = private unnamed_addr constant [14 x i8] c"test: int: %d\00" +; CHECK: @str.7 = private unnamed_addr constant [13 x i8] c"%s: char: 65\00" +; CHECK: @str.8 = private unnamed_addr constant [15 x i8] c"test: uint: %u\00" +; CHECK: @str.9 = private unnamed_addr constant [12 x i8] c"5: char: %d\00" +; CHECK: @str.10 = private unnamed_addr constant [3 x i8] c"%d\00" + + +declare i32 @putchar(i32) +declare i32 @printf(i8* nocapture readonly, ...) + +define void @print_const_char() { +; CHECK-LABEL: @print_const_char( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @str, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str, i32 0, i32 0), i32 65) + ret void +} + +define void @print_const_signed() { +; CHECK-LABEL: @print_const_signed( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @str.1, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.1, i32 0, i32 0), i32 5) + ret void +} + + +define void @print_const_signed2() { +; CHECK-LABEL: @print_const_signed2( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @str.2, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i32 6) + ret void +} + + +define void @print_const_unsigned() { +; CHECK-LABEL: @print_const_unsigned( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @str.3, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.3, i32 0, i32 0), i32 7) + ret void +} + + +define void @print_const_str() { +; CHECK-LABEL: @print_const_str( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @str.4, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.4, i32 0, i32 0), i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.5, i32 0, i32 0)) + ret void +} + + +define void @print_all() { +; CHECK-LABEL: @print_all( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([34 x i8], [34 x i8]* @str.5, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([36 x i8], [36 x i8]* @.str.6, i32 0, i32 0), i32 65, i32 5, i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.5, i32 0, i32 0), i32 7) + ret void +} + + +define void @print_partial(i32 %n) { +; CHECK-LABEL: @print_partial( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @str.6, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.7, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.8, i32 0, i32 0), i32 %n) + ret void +} + + +define void @print_partial2(i8* %s) { +; CHECK-LABEL: @print_partial2( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @str.7, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.9, i32 0, i32 0), i8* %s, i32 65) + ret void +} + + +define void @print_partial3(i32 %n) { +; CHECK-LABEL: @print_partial3( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @str.8, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.10, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.8, i32 0, i32 0), i32 %n) + ret void +} + + +define void @print_partial4(i8 signext %c) { +; CHECK-LABEL: @print_partial4( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @str.9, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %conv = sext i8 %c to i32 + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.11, i32 0, i32 0), i32 5, i32 %conv) + ret void +} + +define void @print_empty_str_signed() { +; CHECK-LABEL: @print_empty_str_signed( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @str.10, i64 0, i64 0)) +; CHECK-NEXT: ret void +; + %call = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str.12, i64 0, i64 0), i8* getelementptr inbounds ([1 x i8], [1 x i8]* @.str.13, i64 0, i64 0), i32 5) + ret void +} Index: test/Transforms/InstCombine/printf-1.ll =================================================================== --- test/Transforms/InstCombine/printf-1.ll +++ test/Transforms/InstCombine/printf-1.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; Test that the printf library call simplifier works correctly. ; ; RUN: opt < %s -instcombine -S | FileCheck %s @@ -22,110 +23,164 @@ define void @test_simplify1() { ; CHECK-LABEL: @test_simplify1( +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_simplify1( +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [1 x i8], [1 x i8]* @empty, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt) ret void -; CHECK-NEXT: ret void } ; Check printf("x") -> putchar('x'), even for '%'. define void @test_simplify2() { ; CHECK-LABEL: @test_simplify2( +; CHECK-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 104) +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_simplify2( +; CHECK-IPRINTF-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 104) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [2 x i8], [2 x i8]* @h, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt) -; CHECK-NEXT: call i32 @putchar(i32 104) ret void -; CHECK-NEXT: ret void } ; Special case: printf("%%") -> putchar('%'). define void @test_simplify2b() { ; CHECK-LABEL: @test_simplify2b( +; CHECK-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 37) +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_simplify2b( +; CHECK-IPRINTF-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 37) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [3 x i8], [3 x i8]* @h2, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt) -; CHECK-NEXT: call i32 @putchar(i32 37) ret void -; CHECK-NEXT: ret void } define void @test_simplify3() { ; CHECK-LABEL: @test_simplify3( +; CHECK-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 37) +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_simplify3( +; CHECK-IPRINTF-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 37) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [2 x i8], [2 x i8]* @percent, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt) -; CHECK-NEXT: call i32 @putchar(i32 37) ret void -; CHECK-NEXT: ret void } ; Check printf("foo\n") -> puts("foo"). define void @test_simplify4() { ; CHECK-LABEL: @test_simplify4( +; CHECK-NEXT: [[PUTS:%.*]] = call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @str, i32 0, i32 0)) +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_simplify4( +; CHECK-IPRINTF-NEXT: [[PUTS:%.*]] = call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @str, i32 0, i32 0)) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [13 x i8], [13 x i8]* @hello_world, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt) -; CHECK-NEXT: call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[$STR]], i32 0, i32 0)) ret void -; CHECK-NEXT: ret void } ; Check printf("%c", chr) -> putchar(chr). define void @test_simplify5() { ; CHECK-LABEL: @test_simplify5( +; CHECK-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 104) +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_simplify5( +; CHECK-IPRINTF-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 104) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [3 x i8], [3 x i8]* @percent_c, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt, i8 104) -; CHECK-NEXT: call i32 @putchar(i32 104) ret void -; CHECK-NEXT: ret void } ; Check printf("%s\n", str) -> puts(str). define void @test_simplify6() { ; CHECK-LABEL: @test_simplify6( +; CHECK-NEXT: [[PUTS:%.*]] = call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @str.3, i32 0, i32 0)) +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_simplify6( +; CHECK-IPRINTF-NEXT: [[PUTS:%.*]] = call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @str.3, i32 0, i32 0)) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [4 x i8], [4 x i8]* @percent_s, i32 0, i32 0 %str = getelementptr [13 x i8], [13 x i8]* @hello_world, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt, i8* %str) -; CHECK-NEXT: call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @hello_world, i32 0, i32 0)) ret void -; CHECK-NEXT: ret void } ; Check printf(format, ...) -> iprintf(format, ...) if no floating point. define void @test_simplify7() { +; CHECK-LABEL: @test_simplify7( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @str.4, i32 0, i32 0)) +; CHECK-NEXT: ret void +; ; CHECK-IPRINTF-LABEL: @test_simplify7( +; CHECK-IPRINTF-NEXT: [[TMP1:%.*]] = call i32 (i8*, ...) @iprintf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @str.4, i32 0, i32 0)) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [3 x i8], [3 x i8]* @percent_d, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt, i32 187) -; CHECK-IPRINTF-NEXT: call i32 (i8*, ...) @iprintf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @percent_d, i32 0, i32 0), i32 187) ret void -; CHECK-IPRINTF-NEXT: ret void } define void @test_no_simplify1() { +; CHECK-LABEL: @test_no_simplify1( +; CHECK-NEXT: [[TMP1:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @percent_f, i32 0, i32 0), double 1.870000e+00) +; CHECK-NEXT: ret void +; ; CHECK-IPRINTF-LABEL: @test_no_simplify1( +; CHECK-IPRINTF-NEXT: [[TMP1:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @percent_f, i32 0, i32 0), double 1.870000e+00) +; CHECK-IPRINTF-NEXT: ret void +; %fmt = getelementptr [3 x i8], [3 x i8]* @percent_f, i32 0, i32 0 call i32 (i8*, ...) @printf(i8* %fmt, double 1.87) -; CHECK-IPRINTF-NEXT: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @percent_f, i32 0, i32 0), double 1.870000e+00) ret void -; CHECK-IPRINTF-NEXT: ret void } define void @test_no_simplify2(i8* %fmt, double %d) { ; CHECK-LABEL: @test_no_simplify2( +; CHECK-NEXT: [[TMP1:%.*]] = call i32 (i8*, ...) @printf(i8* [[FMT:%.*]], double [[D:%.*]]) +; CHECK-NEXT: ret void +; +; CHECK-IPRINTF-LABEL: @test_no_simplify2( +; CHECK-IPRINTF-NEXT: [[TMP1:%.*]] = call i32 (i8*, ...) @printf(i8* [[FMT:%.*]], double [[D:%.*]]) +; CHECK-IPRINTF-NEXT: ret void +; call i32 (i8*, ...) @printf(i8* %fmt, double %d) -; CHECK-NEXT: call i32 (i8*, ...) @printf(i8* %fmt, double %d) ret void -; CHECK-NEXT: ret void } define i32 @test_no_simplify3() { ; CHECK-LABEL: @test_no_simplify3( +; CHECK-NEXT: [[RET:%.*]] = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @h, i32 0, i32 0)) +; CHECK-NEXT: ret i32 [[RET]] +; +; CHECK-IPRINTF-LABEL: @test_no_simplify3( +; CHECK-IPRINTF-NEXT: [[TMP1:%.*]] = call i32 (i8*, ...) @iprintf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @h, i32 0, i32 0)) +; CHECK-IPRINTF-NEXT: ret i32 [[TMP1]] +; %fmt = getelementptr [2 x i8], [2 x i8]* @h, i32 0, i32 0 %ret = call i32 (i8*, ...) @printf(i8* %fmt) -; CHECK-NEXT: call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @h, i32 0, i32 0)) ret i32 %ret -; CHECK-NEXT: ret i32 %ret } Index: test/Transforms/InstCombine/printf-2.ll =================================================================== --- test/Transforms/InstCombine/printf-2.ll +++ test/Transforms/InstCombine/printf-2.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; Test that the printf library call simplifier works correctly. ; ; RUN: opt < %s -instcombine -S | FileCheck %s @@ -16,38 +17,42 @@ define void @test_simplify1() { ; CHECK-LABEL: @test_simplify1( +; CHECK-NEXT: [[PUTCHAR:%.*]] = call i32 @putchar(i32 104) +; CHECK-NEXT: ret void +; %fmt = getelementptr [2 x i8], [2 x i8]* @h, i32 0, i32 0 call void (i8*, ...) @printf(i8* %fmt) -; CHECK-NEXT: call i32 @putchar(i32 104) ret void -; CHECK-NEXT: ret void } define void @test_simplify2() { ; CHECK-LABEL: @test_simplify2( +; CHECK-NEXT: [[PUTS:%.*]] = call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @str, i32 0, i32 0)) +; CHECK-NEXT: ret void +; %fmt = getelementptr [13 x i8], [13 x i8]* @hello_world, i32 0, i32 0 call void (i8*, ...) @printf(i8* %fmt) -; CHECK-NEXT: call i32 @puts(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @str, i32 0, i32 0)) ret void -; CHECK-NEXT: ret void } define void @test_simplify6() { ; CHECK-LABEL: @test_simplify6( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) bitcast (void (i8*, ...)* @printf to i32 (i8*, ...)*)(i8* getelementptr inbounds ([14 x i8], [14 x i8]* @str.1, i32 0, i32 0)) +; CHECK-NEXT: ret void +; %fmt = getelementptr [4 x i8], [4 x i8]* @percent_s, i32 0, i32 0 %str = getelementptr [13 x i8], [13 x i8]* @hello_world, i32 0, i32 0 call void (i8*, ...) @printf(i8* %fmt, i8* %str) -; CHECK-NEXT: call i32 @puts(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @hello_world, i32 0, i32 0)) ret void -; CHECK-NEXT: ret void } define void @test_simplify7() { ; CHECK-LABEL: @test_simplify7( +; CHECK-NEXT: [[PRINTF:%.*]] = call i32 (i8*, ...) bitcast (void (i8*, ...)* @printf to i32 (i8*, ...)*)(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @str.2, i32 0, i32 0)) +; CHECK-NEXT: ret void +; %fmt = getelementptr [3 x i8], [3 x i8]* @format_str, i32 0, i32 0 %str = getelementptr [2 x i8], [2 x i8]* @charstr, i32 0, i32 0 call void (i8*, ...) @printf(i8* %fmt, i8* %str) -; CHECK-NEXT: call i32 @putchar(i32 97) ret void -; CHECK-NEXT: ret void }