[SimplifyLibcalls] Replace locked IO with unlocked IO

Summary: If file stream arg is not captured and source is fopen, we could replace IO calls by unlocked IO ("_unlocked" function variants) to gain better speed,

Reviewers: efriedma, RKSimon, spatel, sanjoy, hfinkel, majnemer, lebedev.ri, rja

Reviewed By: rja

Subscribers: rja, srhines, efriedma, lebedev.ri, llvm-commits

Differential Revision: https://reviews.llvm.org/D45736

llvm-svn: 332452
This commit is contained in:
David Bolvansky
2018-05-16 11:39:52 +00:00
parent 5647e89f5a
commit ca22d427b9
9 changed files with 531 additions and 19 deletions

View File

@@ -21,6 +21,7 @@
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/Utils/Local.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
@@ -127,6 +128,28 @@ static Value *convertStrToNumber(CallInst *CI, StringRef &Str, int64_t Base) {
return ConstantInt::get(CI->getType(), Result);
}
static bool isLocallyOpenedFile(Value *File, CallInst *CI, IRBuilder<> &B,
const TargetLibraryInfo *TLI) {
CallInst *FOpen = dyn_cast<CallInst>(File);
if (!FOpen)
return false;
Function *InnerCallee = FOpen->getCalledFunction();
if (!InnerCallee)
return false;
LibFunc Func;
if (!TLI->getLibFunc(*InnerCallee, Func) || !TLI->has(Func) ||
Func != LibFunc_fopen)
return false;
inferLibFuncAttributes(*CI->getCalledFunction(), *TLI);
if (PointerMayBeCaptured(File, true, true))
return false;
return true;
}
//===----------------------------------------------------------------------===//
// String and Memory Library Call Optimizations
//===----------------------------------------------------------------------===//
@@ -2076,22 +2099,27 @@ Value *LibCallSimplifier::optimizeFWrite(CallInst *CI, IRBuilder<> &B) {
// Get the element size and count.
ConstantInt *SizeC = dyn_cast<ConstantInt>(CI->getArgOperand(1));
ConstantInt *CountC = dyn_cast<ConstantInt>(CI->getArgOperand(2));
if (!SizeC || !CountC)
return nullptr;
uint64_t Bytes = SizeC->getZExtValue() * CountC->getZExtValue();
if (SizeC && CountC) {
uint64_t Bytes = SizeC->getZExtValue() * CountC->getZExtValue();
// If this is writing zero records, remove the call (it's a noop).
if (Bytes == 0)
return ConstantInt::get(CI->getType(), 0);
// If this is writing zero records, remove the call (it's a noop).
if (Bytes == 0)
return ConstantInt::get(CI->getType(), 0);
// If this is writing one byte, turn it into fputc.
// This optimisation is only valid, if the return value is unused.
if (Bytes == 1 && CI->use_empty()) { // fwrite(S,1,1,F) -> fputc(S[0],F)
Value *Char = B.CreateLoad(castToCStr(CI->getArgOperand(0), B), "char");
Value *NewCI = emitFPutC(Char, CI->getArgOperand(3), B, TLI);
return NewCI ? ConstantInt::get(CI->getType(), 1) : nullptr;
// If this is writing one byte, turn it into fputc.
// This optimisation is only valid, if the return value is unused.
if (Bytes == 1 && CI->use_empty()) { // fwrite(S,1,1,F) -> fputc(S[0],F)
Value *Char = B.CreateLoad(castToCStr(CI->getArgOperand(0), B), "char");
Value *NewCI = emitFPutC(Char, CI->getArgOperand(3), B, TLI);
return NewCI ? ConstantInt::get(CI->getType(), 1) : nullptr;
}
}
if (isLocallyOpenedFile(CI->getArgOperand(3), CI, B, TLI))
return emitFWriteUnlocked(CI->getArgOperand(0), CI->getArgOperand(1),
CI->getArgOperand(2), CI->getArgOperand(3), B, DL,
TLI);
return nullptr;
}
@@ -2103,9 +2131,15 @@ Value *LibCallSimplifier::optimizeFPuts(CallInst *CI, IRBuilder<> &B) {
if (CI->getParent()->getParent()->optForSize())
return nullptr;
// We can't optimize if return value is used.
if (!CI->use_empty())
return nullptr;
// Check if has any use
if (!CI->use_empty()) {
if (isLocallyOpenedFile(CI->getArgOperand(1), CI, B, TLI))
return emitFPutSUnlocked(CI->getArgOperand(0), CI->getArgOperand(1), B,
TLI);
else
// We can't optimize if return value is used.
return nullptr;
}
// fputs(s,F) --> fwrite(s,1,strlen(s),F)
uint64_t Len = GetStringLength(CI->getArgOperand(0));
@@ -2119,6 +2153,40 @@ Value *LibCallSimplifier::optimizeFPuts(CallInst *CI, IRBuilder<> &B) {
CI->getArgOperand(1), B, DL, TLI);
}
Value *LibCallSimplifier::optimizeFPutc(CallInst *CI, IRBuilder<> &B) {
optimizeErrorReporting(CI, B, 1);
if (isLocallyOpenedFile(CI->getArgOperand(1), CI, B, TLI))
return emitFPutCUnlocked(CI->getArgOperand(0), CI->getArgOperand(1), B,
TLI);
return nullptr;
}
Value *LibCallSimplifier::optimizeFGetc(CallInst *CI, IRBuilder<> &B) {
if (isLocallyOpenedFile(CI->getArgOperand(0), CI, B, TLI))
return emitFGetCUnlocked(CI->getArgOperand(0), B, TLI);
return nullptr;
}
Value *LibCallSimplifier::optimizeFGets(CallInst *CI, IRBuilder<> &B) {
if (isLocallyOpenedFile(CI->getArgOperand(2), CI, B, TLI))
return emitFGetSUnlocked(CI->getArgOperand(0), CI->getArgOperand(1),
CI->getArgOperand(2), B, TLI);
return nullptr;
}
Value *LibCallSimplifier::optimizeFRead(CallInst *CI, IRBuilder<> &B) {
if (isLocallyOpenedFile(CI->getArgOperand(3), CI, B, TLI))
return emitFReadUnlocked(CI->getArgOperand(0), CI->getArgOperand(1),
CI->getArgOperand(2), CI->getArgOperand(3), B, DL,
TLI);
return nullptr;
}
Value *LibCallSimplifier::optimizePuts(CallInst *CI, IRBuilder<> &B) {
// Check for a constant string.
StringRef Str;
@@ -2412,8 +2480,16 @@ Value *LibCallSimplifier::optimizeCall(CallInst *CI) {
return optimizeFPrintF(CI, Builder);
case LibFunc_fwrite:
return optimizeFWrite(CI, Builder);
case LibFunc_fread:
return optimizeFRead(CI, Builder);
case LibFunc_fputs:
return optimizeFPuts(CI, Builder);
case LibFunc_fgets:
return optimizeFGets(CI, Builder);
case LibFunc_fputc:
return optimizeFPutc(CI, Builder);
case LibFunc_fgetc:
return optimizeFGetc(CI, Builder);
case LibFunc_puts:
return optimizePuts(CI, Builder);
case LibFunc_perror:
@@ -2421,8 +2497,6 @@ Value *LibCallSimplifier::optimizeCall(CallInst *CI) {
case LibFunc_vfprintf:
case LibFunc_fiprintf:
return optimizeErrorReporting(CI, Builder, 0);
case LibFunc_fputc:
return optimizeErrorReporting(CI, Builder, 1);
default:
return nullptr;
}