+ /// @brief Perform the printf optimization.
+ virtual bool OptimizeCall(CallInst* ci, SimplifyLibCalls& SLC) {
+ // If the call has more than 2 operands, we can't optimize it
+ if (ci->getNumOperands() > 3 || ci->getNumOperands() <= 2)
+ return false;
+
+ // If the result of the printf call is used, none of these optimizations
+ // can be made.
+ if (!ci->use_empty())
+ return false;
+
+ // All the optimizations depend on the length of the first argument and the
+ // fact that it is a constant string array. Check that now
+ uint64_t len = 0;
+ ConstantArray* CA = 0;
+ if (!getConstantStringLength(ci->getOperand(1), len, &CA))
+ return false;
+
+ if (len != 2 && len != 3)
+ return false;
+
+ // The first character has to be a %
+ if (ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(0)))
+ if (CI->getZExtValue() != '%')
+ return false;
+
+ // Get the second character and switch on its value
+ ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(1));
+ switch (CI->getZExtValue()) {
+ case 's':
+ {
+ if (len != 3 ||
+ dyn_cast<ConstantInt>(CA->getOperand(2))->getZExtValue() != '\n')
+ return false;
+
+ // printf("%s\n",str) -> puts(str)
+ Function* puts_func = SLC.get_puts();
+ if (!puts_func)
+ return false;
+ std::vector<Value*> args;
+ args.push_back(CastToCStr(ci->getOperand(2), *ci));
+ new CallInst(puts_func,args,ci->getName(),ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,len));
+ break;
+ }
+ case 'c':
+ {
+ // printf("%c",c) -> putchar(c)
+ if (len != 2)
+ return false;
+
+ Function* putchar_func = SLC.get_putchar();
+ if (!putchar_func)
+ return false;
+ CastInst* cast = CastInst::createSExtOrBitCast(
+ ci->getOperand(2), Type::IntTy, CI->getName()+".int", ci);
+ new CallInst(putchar_func, cast, "", ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy, 1));
+ break;
+ }
+ default:
+ return false;
+ }
+ ci->eraseFromParent();
+ return true;
+ }
+} PrintfOptimizer;
+
+/// This LibCallOptimization will simplify calls to the "fprintf" library
+/// function. It looks for cases where the result of fprintf is not used and the
+/// operation can be reduced to something simpler.
+/// @brief Simplify the fprintf library function.
+struct FPrintFOptimization : public LibCallOptimization {
+public:
+ /// @brief Default Constructor
+ FPrintFOptimization() : LibCallOptimization("fprintf",
+ "Number of 'fprintf' calls simplified") {}
+
+ /// @brief Make sure that the "fprintf" function has the right prototype
+ virtual bool ValidateCalledFunction(const Function* f, SimplifyLibCalls& SLC){
+ // Just make sure this has at least 2 arguments
+ return (f->arg_size() >= 2);
+ }
+
+ /// @brief Perform the fprintf optimization.
+ virtual bool OptimizeCall(CallInst* ci, SimplifyLibCalls& SLC) {
+ // If the call has more than 3 operands, we can't optimize it
+ if (ci->getNumOperands() > 4 || ci->getNumOperands() <= 2)
+ return false;
+
+ // If the result of the fprintf call is used, none of these optimizations
+ // can be made.
+ if (!ci->use_empty())
+ return false;
+
+ // All the optimizations depend on the length of the second argument and the
+ // fact that it is a constant string array. Check that now
+ uint64_t len = 0;
+ ConstantArray* CA = 0;
+ if (!getConstantStringLength(ci->getOperand(2), len, &CA))
+ return false;
+
+ if (ci->getNumOperands() == 3) {
+ // Make sure there's no % in the constant array
+ for (unsigned i = 0; i < len; ++i) {
+ if (ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(i))) {
+ // Check for the null terminator
+ if (CI->getZExtValue() == '%')
+ return false; // we found end of string
+ } else {
+ return false;
+ }
+ }
+
+ // fprintf(file,fmt) -> fwrite(fmt,strlen(fmt),file)
+ const Type* FILEptr_type = ci->getOperand(1)->getType();
+ Function* fwrite_func = SLC.get_fwrite(FILEptr_type);
+ if (!fwrite_func)
+ return false;
+
+ // Make sure that the fprintf() and fwrite() functions both take the
+ // same type of char pointer.
+ if (ci->getOperand(2)->getType() !=
+ fwrite_func->getFunctionType()->getParamType(0))
+ return false;
+
+ std::vector<Value*> args;
+ args.push_back(ci->getOperand(2));
+ args.push_back(ConstantInt::get(SLC.getIntPtrType(),len));
+ args.push_back(ConstantInt::get(SLC.getIntPtrType(),1));
+ args.push_back(ci->getOperand(1));
+ new CallInst(fwrite_func,args,ci->getName(),ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,len));
+ ci->eraseFromParent();
+ return true;
+ }
+
+ // The remaining optimizations require the format string to be length 2
+ // "%s" or "%c".
+ if (len != 2)
+ return false;
+
+ // The first character has to be a %
+ if (ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(0)))
+ if (CI->getZExtValue() != '%')
+ return false;
+
+ // Get the second character and switch on its value
+ ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(1));
+ switch (CI->getZExtValue()) {
+ case 's':
+ {
+ uint64_t len = 0;
+ ConstantArray* CA = 0;
+ if (getConstantStringLength(ci->getOperand(3), len, &CA)) {
+ // fprintf(file,"%s",str) -> fwrite(str,strlen(str),1,file)
+ const Type* FILEptr_type = ci->getOperand(1)->getType();
+ Function* fwrite_func = SLC.get_fwrite(FILEptr_type);
+ if (!fwrite_func)
+ return false;
+ std::vector<Value*> args;
+ args.push_back(CastToCStr(ci->getOperand(3), *ci));
+ args.push_back(ConstantInt::get(SLC.getIntPtrType(),len));
+ args.push_back(ConstantInt::get(SLC.getIntPtrType(),1));
+ args.push_back(ci->getOperand(1));
+ new CallInst(fwrite_func,args,ci->getName(),ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,len));
+ } else {
+ // fprintf(file,"%s",str) -> fputs(str,file)
+ const Type* FILEptr_type = ci->getOperand(1)->getType();
+ Function* fputs_func = SLC.get_fputs(FILEptr_type);
+ if (!fputs_func)
+ return false;
+ std::vector<Value*> args;
+ args.push_back(CastToCStr(ci->getOperand(3), *ci));
+ args.push_back(ci->getOperand(1));
+ new CallInst(fputs_func,args,ci->getName(),ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,len));
+ }
+ break;
+ }
+ case 'c':
+ {
+ // fprintf(file,"%c",c) -> fputc(c,file)
+ const Type* FILEptr_type = ci->getOperand(1)->getType();
+ Function* fputc_func = SLC.get_fputc(FILEptr_type);
+ if (!fputc_func)
+ return false;
+ CastInst* cast = CastInst::createSExtOrBitCast(
+ ci->getOperand(3), Type::IntTy, CI->getName()+".int", ci);
+ new CallInst(fputc_func,cast,ci->getOperand(1),"",ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,1));
+ break;
+ }
+ default:
+ return false;
+ }
+ ci->eraseFromParent();
+ return true;
+ }
+} FPrintFOptimizer;
+
+/// This LibCallOptimization will simplify calls to the "sprintf" library
+/// function. It looks for cases where the result of sprintf is not used and the
+/// operation can be reduced to something simpler.
+/// @brief Simplify the sprintf library function.
+struct SPrintFOptimization : public LibCallOptimization {
+public:
+ /// @brief Default Constructor
+ SPrintFOptimization() : LibCallOptimization("sprintf",
+ "Number of 'sprintf' calls simplified") {}
+
+ /// @brief Make sure that the "fprintf" function has the right prototype
+ virtual bool ValidateCalledFunction(const Function *f, SimplifyLibCalls &SLC){
+ // Just make sure this has at least 2 arguments
+ return (f->getReturnType() == Type::IntTy && f->arg_size() >= 2);
+ }
+
+ /// @brief Perform the sprintf optimization.
+ virtual bool OptimizeCall(CallInst *ci, SimplifyLibCalls &SLC) {
+ // If the call has more than 3 operands, we can't optimize it
+ if (ci->getNumOperands() > 4 || ci->getNumOperands() < 3)
+ return false;
+
+ // All the optimizations depend on the length of the second argument and the
+ // fact that it is a constant string array. Check that now
+ uint64_t len = 0;
+ ConstantArray* CA = 0;
+ if (!getConstantStringLength(ci->getOperand(2), len, &CA))
+ return false;
+
+ if (ci->getNumOperands() == 3) {
+ if (len == 0) {
+ // If the length is 0, we just need to store a null byte
+ new StoreInst(ConstantInt::get(Type::SByteTy,0),ci->getOperand(1),ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,0));
+ ci->eraseFromParent();
+ return true;
+ }
+
+ // Make sure there's no % in the constant array
+ for (unsigned i = 0; i < len; ++i) {
+ if (ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(i))) {
+ // Check for the null terminator
+ if (CI->getZExtValue() == '%')
+ return false; // we found a %, can't optimize
+ } else {
+ return false; // initializer is not constant int, can't optimize
+ }
+ }
+
+ // Increment length because we want to copy the null byte too
+ len++;
+
+ // sprintf(str,fmt) -> llvm.memcpy(str,fmt,strlen(fmt),1)
+ Function* memcpy_func = SLC.get_memcpy();
+ if (!memcpy_func)
+ return false;
+ std::vector<Value*> args;
+ args.push_back(ci->getOperand(1));
+ args.push_back(ci->getOperand(2));
+ args.push_back(ConstantInt::get(SLC.getIntPtrType(),len));
+ args.push_back(ConstantInt::get(Type::UIntTy,1));
+ new CallInst(memcpy_func,args,"",ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,len));
+ ci->eraseFromParent();
+ return true;
+ }
+
+ // The remaining optimizations require the format string to be length 2
+ // "%s" or "%c".
+ if (len != 2)
+ return false;
+
+ // The first character has to be a %
+ if (ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(0)))
+ if (CI->getZExtValue() != '%')
+ return false;
+
+ // Get the second character and switch on its value
+ ConstantInt* CI = dyn_cast<ConstantInt>(CA->getOperand(1));
+ switch (CI->getZExtValue()) {
+ case 's': {
+ // sprintf(dest,"%s",str) -> llvm.memcpy(dest, str, strlen(str)+1, 1)
+ Function* strlen_func = SLC.get_strlen();
+ Function* memcpy_func = SLC.get_memcpy();
+ if (!strlen_func || !memcpy_func)
+ return false;
+
+ Value *Len = new CallInst(strlen_func, CastToCStr(ci->getOperand(3), *ci),
+ ci->getOperand(3)->getName()+".len", ci);
+ Value *Len1 = BinaryOperator::createAdd(Len,
+ ConstantInt::get(Len->getType(), 1),
+ Len->getName()+"1", ci);
+ if (Len1->getType() != SLC.getIntPtrType())
+ Len1 = CastInst::createIntegerCast(Len1, SLC.getIntPtrType(), false,
+ Len1->getName(), ci);
+ std::vector<Value*> args;
+ args.push_back(CastToCStr(ci->getOperand(1), *ci));
+ args.push_back(CastToCStr(ci->getOperand(3), *ci));
+ args.push_back(Len1);
+ args.push_back(ConstantInt::get(Type::UIntTy,1));
+ new CallInst(memcpy_func, args, "", ci);
+
+ // The strlen result is the unincremented number of bytes in the string.
+ if (!ci->use_empty()) {
+ if (Len->getType() != ci->getType())
+ Len = CastInst::createIntegerCast(Len, ci->getType(), false,
+ Len->getName(), ci);
+ ci->replaceAllUsesWith(Len);
+ }
+ ci->eraseFromParent();
+ return true;
+ }
+ case 'c': {
+ // sprintf(dest,"%c",chr) -> store chr, dest
+ CastInst* cast = CastInst::createTruncOrBitCast(
+ ci->getOperand(3), Type::SByteTy, "char", ci);
+ new StoreInst(cast, ci->getOperand(1), ci);
+ GetElementPtrInst* gep = new GetElementPtrInst(ci->getOperand(1),
+ ConstantInt::get(Type::UIntTy,1),ci->getOperand(1)->getName()+".end",
+ ci);
+ new StoreInst(ConstantInt::get(Type::SByteTy,0),gep,ci);
+ ci->replaceAllUsesWith(ConstantInt::get(Type::IntTy,1));
+ ci->eraseFromParent();
+ return true;
+ }
+ }
+ return false;
+ }
+} SPrintFOptimizer;
+
+/// This LibCallOptimization will simplify calls to the "fputs" library
+/// function. It looks for cases where the result of fputs is not used and the
+/// operation can be reduced to something simpler.
+/// @brief Simplify the puts library function.
+struct PutsOptimization : public LibCallOptimization {
+public:
+ /// @brief Default Constructor
+ PutsOptimization() : LibCallOptimization("fputs",
+ "Number of 'fputs' calls simplified") {}