[llvm] r332452 - [SimplifyLibcalls] Replace locked IO with unlocked IO

Benjamin Kramer via llvm-commits llvm-commits at lists.llvm.org
Wed May 16 14:54:40 PDT 2018


This triggered a really annoying miscompile (see r332531), and it's even
present in the test case. Please double check your tests, especially when
using update_test_checks.py.

Also you want to get these things reviewed by someone who's actually
familiar with the LLVM before they land.
On Wed, May 16, 2018 at 1:43 PM David Bolvansky via llvm-commits <
llvm-commits at lists.llvm.org> wrote:

> Author: xbolva00
> Date: Wed May 16 04:39:52 2018
> New Revision: 332452

> URL: http://llvm.org/viewvc/llvm-project?rev=332452&view=rev
> Log:
> [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

> Added:
>      llvm/trunk/test/Transforms/InstCombine/unlocked-stdio.ll
> Modified:
>      llvm/trunk/include/llvm/Analysis/TargetLibraryInfo.def
>      llvm/trunk/include/llvm/Transforms/Utils/BuildLibCalls.h
>      llvm/trunk/include/llvm/Transforms/Utils/SimplifyLibCalls.h
>      llvm/trunk/lib/Analysis/TargetLibraryInfo.cpp
>      llvm/trunk/lib/Transforms/Utils/BuildLibCalls.cpp
>      llvm/trunk/lib/Transforms/Utils/SimplifyLibCalls.cpp
>      llvm/trunk/test/Transforms/InferFunctionAttrs/annotate.ll
>      llvm/trunk/unittests/Analysis/TargetLibraryInfoTest.cpp

> Modified: llvm/trunk/include/llvm/Analysis/TargetLibraryInfo.def
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Analysis/TargetLibraryInfo.def?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/include/llvm/Analysis/TargetLibraryInfo.def (original)
> +++ llvm/trunk/include/llvm/Analysis/TargetLibraryInfo.def Wed May 16
04:39:52 2018
> @@ -637,12 +637,18 @@ TLI_DEFINE_STRING_INTERNAL("ffsll")
>   /// int fgetc(FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(fgetc)
>   TLI_DEFINE_STRING_INTERNAL("fgetc")
> +/// int fgetc_unlocked(FILE *stream);
> +TLI_DEFINE_ENUM_INTERNAL(fgetc_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("fgetc_unlocked")
>   /// int fgetpos(FILE *stream, fpos_t *pos);
>   TLI_DEFINE_ENUM_INTERNAL(fgetpos)
>   TLI_DEFINE_STRING_INTERNAL("fgetpos")
>   /// char *fgets(char *s, int n, FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(fgets)
>   TLI_DEFINE_STRING_INTERNAL("fgets")
> +/// char *fgets_unlocked(char *s, int n, FILE *stream);
> +TLI_DEFINE_ENUM_INTERNAL(fgets_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("fgets_unlocked")
>   /// int fileno(FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(fileno)
>   TLI_DEFINE_STRING_INTERNAL("fileno")
> @@ -709,12 +715,21 @@ TLI_DEFINE_STRING_INTERNAL("fprintf")
>   /// int fputc(int c, FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(fputc)
>   TLI_DEFINE_STRING_INTERNAL("fputc")
> +/// int fputc_unlocked(int c, FILE *stream);
> +TLI_DEFINE_ENUM_INTERNAL(fputc_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("fputc_unlocked")
>   /// int fputs(const char *s, FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(fputs)
>   TLI_DEFINE_STRING_INTERNAL("fputs")
> +/// int fputs_unlocked(const char *s, FILE *stream);
> +TLI_DEFINE_ENUM_INTERNAL(fputs_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("fputs_unlocked")
>   /// size_t fread(void *ptr, size_t size, size_t nitems, FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(fread)
>   TLI_DEFINE_STRING_INTERNAL("fread")
> +/// size_t fread_unlocked(void *ptr, size_t size, size_t nitems, FILE
*stream);
> +TLI_DEFINE_ENUM_INTERNAL(fread_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("fread_unlocked")
>   /// void free(void *ptr);
>   TLI_DEFINE_ENUM_INTERNAL(free)
>   TLI_DEFINE_STRING_INTERNAL("free")
> @@ -772,6 +787,9 @@ TLI_DEFINE_STRING_INTERNAL("funlockfile"
>   /// size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE
*stream);
>   TLI_DEFINE_ENUM_INTERNAL(fwrite)
>   TLI_DEFINE_STRING_INTERNAL("fwrite")
> +/// size_t fwrite_unlocked(const void *ptr, size_t size, size_t nitems,
FILE *stream);
> +TLI_DEFINE_ENUM_INTERNAL(fwrite_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("fwrite_unlocked")
>   /// int getc(FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(getc)
>   TLI_DEFINE_STRING_INTERNAL("getc")
> @@ -781,6 +799,9 @@ TLI_DEFINE_STRING_INTERNAL("getc_unlocke
>   /// int getchar(void);
>   TLI_DEFINE_ENUM_INTERNAL(getchar)
>   TLI_DEFINE_STRING_INTERNAL("getchar")
> +/// int getchar_unlocked(void);
> +TLI_DEFINE_ENUM_INTERNAL(getchar_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("getchar_unlocked")
>   /// char *getenv(const char *name);
>   TLI_DEFINE_ENUM_INTERNAL(getenv)
>   TLI_DEFINE_STRING_INTERNAL("getenv")
> @@ -986,9 +1007,15 @@ TLI_DEFINE_STRING_INTERNAL("printf")
>   /// int putc(int c, FILE *stream);
>   TLI_DEFINE_ENUM_INTERNAL(putc)
>   TLI_DEFINE_STRING_INTERNAL("putc")
> +/// int putc_unlocked(int c, FILE *stream);
> +TLI_DEFINE_ENUM_INTERNAL(putc_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("putc_unlocked")
>   /// int putchar(int c);
>   TLI_DEFINE_ENUM_INTERNAL(putchar)
>   TLI_DEFINE_STRING_INTERNAL("putchar")
> +/// int putchar_unlocked(int c);
> +TLI_DEFINE_ENUM_INTERNAL(putchar_unlocked)
> +TLI_DEFINE_STRING_INTERNAL("putchar_unlocked")
>   /// int puts(const char *s);
>   TLI_DEFINE_ENUM_INTERNAL(puts)
>   TLI_DEFINE_STRING_INTERNAL("puts")

> Modified: llvm/trunk/include/llvm/Transforms/Utils/BuildLibCalls.h
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/BuildLibCalls.h?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/include/llvm/Transforms/Utils/BuildLibCalls.h (original)
> +++ llvm/trunk/include/llvm/Transforms/Utils/BuildLibCalls.h Wed May 16
04:39:52 2018
> @@ -111,11 +111,21 @@ namespace llvm {
>     Value *emitFPutC(Value *Char, Value *File, IRBuilder<> &B,
>                      const TargetLibraryInfo *TLI);

> -  /// Emit a call to the puts function. Str is required to be a pointer
and
> +  /// Emit a call to the fputc_unlocked function. This assumes that Char
is an
> +  /// i32, and File is a pointer to FILE.
> +  Value *emitFPutCUnlocked(Value *Char, Value *File, IRBuilder<> &B,
> +                           const TargetLibraryInfo *TLI);
> +
> +  /// Emit a call to the fputs function. Str is required to be a pointer
and
>     /// File is a pointer to FILE.
>     Value *emitFPutS(Value *Str, Value *File, IRBuilder<> &B,
>                      const TargetLibraryInfo *TLI);

> +  /// Emit a call to the fputs_unlocked function. Str is required to be a
> +  /// pointer and File is a pointer to FILE.
> +  Value *emitFPutSUnlocked(Value *Str, Value *File, IRBuilder<> &B,
> +                           const TargetLibraryInfo *TLI);
> +
>     /// Emit a call to the fwrite function. This assumes that Ptr is a
pointer,
>     /// Size is an 'intptr_t', and File is a pointer to FILE.
>     Value *emitFWrite(Value *Ptr, Value *Size, Value *File, IRBuilder<> &B,
> @@ -128,6 +138,27 @@ namespace llvm {
>     /// Emit a call to the calloc function.
>     Value *emitCalloc(Value *Num, Value *Size, const AttributeList &Attrs,
>                       IRBuilder<> &B, const TargetLibraryInfo &TLI);
> +
> +  /// Emit a call to the fwrite_unlocked function. This assumes that Ptr
is a
> +  /// pointer, Size is an 'intptr_t', N is nmemb and File is a pointer
to FILE.
> +  Value *emitFWriteUnlocked(Value *Ptr, Value *Size, Value *N, Value
*File,
> +                            IRBuilder<> &B, const DataLayout &DL,
> +                            const TargetLibraryInfo *TLI);
> +
> +  /// Emit a call to the fgetc_unlocked function. File is a pointer to
FILE.
> +  Value *emitFGetCUnlocked(Value *File, IRBuilder<> &B,
> +                           const TargetLibraryInfo *TLI);
> +
> +  /// Emit a call to the fgets_unlocked function. Str is required to be a
> +  /// pointer, Size is an i32 and File is a pointer to FILE.
> +  Value *emitFGetSUnlocked(Value *Str, Value *Size, Value *File,
IRBuilder<> &B,
> +                           const TargetLibraryInfo *TLI);
> +
> +  /// Emit a call to the fread_unlocked function. This assumes that Ptr
is a
> +  /// pointer, Size is an 'intptr_t', N is nmemb and File is a pointer
to FILE.
> +  Value *emitFReadUnlocked(Value *Ptr, Value *Size, Value *N, Value
*File,
> +                           IRBuilder<> &B, const DataLayout &DL,
> +                           const TargetLibraryInfo *TLI);
>   }

>   #endif

> Modified: llvm/trunk/include/llvm/Transforms/Utils/SimplifyLibCalls.h
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/SimplifyLibCalls.h?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/include/llvm/Transforms/Utils/SimplifyLibCalls.h (original)
> +++ llvm/trunk/include/llvm/Transforms/Utils/SimplifyLibCalls.h Wed May
16 04:39:52 2018
> @@ -162,7 +162,11 @@ private:
>     Value *optimizeSnPrintF(CallInst *CI, IRBuilder<> &B);
>     Value *optimizeFPrintF(CallInst *CI, IRBuilder<> &B);
>     Value *optimizeFWrite(CallInst *CI, IRBuilder<> &B);
> +  Value *optimizeFRead(CallInst *CI, IRBuilder<> &B);
>     Value *optimizeFPuts(CallInst *CI, IRBuilder<> &B);
> +  Value *optimizeFGets(CallInst *CI, IRBuilder<> &B);
> +  Value *optimizeFPutc(CallInst *CI, IRBuilder<> &B);
> +  Value *optimizeFGetc(CallInst *CI, IRBuilder<> &B);
>     Value *optimizePuts(CallInst *CI, IRBuilder<> &B);

>     // Helper methods

> Modified: llvm/trunk/lib/Analysis/TargetLibraryInfo.cpp
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/TargetLibraryInfo.cpp?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/lib/Analysis/TargetLibraryInfo.cpp (original)
> +++ llvm/trunk/lib/Analysis/TargetLibraryInfo.cpp Wed May 16 04:39:52 2018
> @@ -62,6 +62,18 @@ static void initialize(TargetLibraryInfo
>                           }) &&
>            "TargetLibraryInfoImpl function names must be sorted");

> +  // Set IO unlocked variants as unavailable
> +  // Set them as available per system below
> +  TLI.setUnavailable(LibFunc_getchar_unlocked);
> +  TLI.setUnavailable(LibFunc_putc_unlocked);
> +  TLI.setUnavailable(LibFunc_putchar_unlocked);
> +  TLI.setUnavailable(LibFunc_fputc_unlocked);
> +  TLI.setUnavailable(LibFunc_fgetc_unlocked);
> +  TLI.setUnavailable(LibFunc_fread_unlocked);
> +  TLI.setUnavailable(LibFunc_fwrite_unlocked);
> +  TLI.setUnavailable(LibFunc_fputs_unlocked);
> +  TLI.setUnavailable(LibFunc_fgets_unlocked);
> +
>     bool ShouldExtI32Param = false, ShouldExtI32Return = false,
>          ShouldSignExtI32Param = false;
>     // PowerPC64, Sparc64, SystemZ need signext/zeroext on i32 parameters
and
> @@ -107,6 +119,12 @@ static void initialize(TargetLibraryInfo
>     // memset_pattern16 is only available on iOS 3.0 and Mac OS X 10.5 and
later.
>     // All versions of watchOS support it.
>     if (T.isMacOSX()) {
> +    // available IO unlocked variants on Mac OS X
> +    TLI.setAvailable(LibFunc_getc_unlocked);
> +    TLI.setAvailable(LibFunc_getchar_unlocked);
> +    TLI.setAvailable(LibFunc_putc_unlocked);
> +    TLI.setAvailable(LibFunc_putchar_unlocked);
> +
>       if (T.isMacOSXVersionLT(10, 5))
>         TLI.setUnavailable(LibFunc_memset_pattern16);
>     } else if (T.isiOS()) {
> @@ -265,7 +283,6 @@ static void initialize(TargetLibraryInfo
>       TLI.setUnavailable(LibFunc_ftello);
>       TLI.setUnavailable(LibFunc_ftrylockfile);
>       TLI.setUnavailable(LibFunc_funlockfile);
> -    TLI.setUnavailable(LibFunc_getc_unlocked);
>       TLI.setUnavailable(LibFunc_getitimer);
>       TLI.setUnavailable(LibFunc_getlogin_r);
>       TLI.setUnavailable(LibFunc_getpwnam);
> @@ -465,6 +482,20 @@ static void initialize(TargetLibraryInfo
>       TLI.setUnavailable(LibFunc_sinhl_finite);
>     }

> +  if (T.isGNUEnvironment() || (T.isAndroid() &&
!T.isAndroidVersionLT(28))) {
> +    // available IO unlocked variants on GNU/Linux and Android P or later
> +    TLI.setAvailable(LibFunc_getc_unlocked);
> +    TLI.setAvailable(LibFunc_getchar_unlocked);
> +    TLI.setAvailable(LibFunc_putc_unlocked);
> +    TLI.setAvailable(LibFunc_putchar_unlocked);
> +    TLI.setAvailable(LibFunc_fputc_unlocked);
> +    TLI.setAvailable(LibFunc_fgetc_unlocked);
> +    TLI.setAvailable(LibFunc_fread_unlocked);
> +    TLI.setAvailable(LibFunc_fwrite_unlocked);
> +    TLI.setAvailable(LibFunc_fputs_unlocked);
> +    TLI.setAvailable(LibFunc_fgets_unlocked);
> +  }
> +
>     // As currently implemented in clang, NVPTX code has no standard
library to
>     // speak of.  Headers provide a standard-ish library implementation,
but many
>     // of the signatures are wrong -- for example, many libm functions are
not
> @@ -807,6 +838,7 @@ bool TargetLibraryInfoImpl::isValidProto
>     case LibFunc_feof:
>     case LibFunc_fflush:
>     case LibFunc_fgetc:
> +  case LibFunc_fgetc_unlocked:
>     case LibFunc_fileno:
>     case LibFunc_flockfile:
>     case LibFunc_free:
> @@ -835,6 +867,7 @@ bool TargetLibraryInfoImpl::isValidProto
>       return (NumParams == 2 && FTy.getReturnType()->isPointerTy() &&
>               FTy.getParamType(1)->isPointerTy());
>     case LibFunc_fputc:
> +  case LibFunc_fputc_unlocked:
>     case LibFunc_fstat:
>     case LibFunc_frexp:
>     case LibFunc_frexpf:
> @@ -842,18 +875,22 @@ bool TargetLibraryInfoImpl::isValidProto
>     case LibFunc_fstatvfs:
>       return (NumParams == 2 && FTy.getParamType(1)->isPointerTy());
>     case LibFunc_fgets:
> +  case LibFunc_fgets_unlocked:
>       return (NumParams == 3 && FTy.getParamType(0)->isPointerTy() &&
>               FTy.getParamType(2)->isPointerTy());
>     case LibFunc_fread:
> +  case LibFunc_fread_unlocked:
>       return (NumParams == 4 && FTy.getParamType(0)->isPointerTy() &&
>               FTy.getParamType(3)->isPointerTy());
>     case LibFunc_fwrite:
> +  case LibFunc_fwrite_unlocked:
>       return (NumParams == 4 && FTy.getReturnType()->isIntegerTy() &&
>               FTy.getParamType(0)->isPointerTy() &&
>               FTy.getParamType(1)->isIntegerTy() &&
>               FTy.getParamType(2)->isIntegerTy() &&
>               FTy.getParamType(3)->isPointerTy());
>     case LibFunc_fputs:
> +  case LibFunc_fputs_unlocked:
>       return (NumParams >= 2 && FTy.getParamType(0)->isPointerTy() &&
>               FTy.getParamType(1)->isPointerTy());
>     case LibFunc_fscanf:
> @@ -866,6 +903,7 @@ bool TargetLibraryInfoImpl::isValidProto
>       return (NumParams >= 2 && FTy.getParamType(0)->isPointerTy() &&
>               FTy.getParamType(1)->isPointerTy());
>     case LibFunc_getchar:
> +  case LibFunc_getchar_unlocked:
>       return (NumParams == 0 && FTy.getReturnType()->isIntegerTy());
>     case LibFunc_gets:
>       return (NumParams == 1 && FTy.getParamType(0) == PCharTy);
> @@ -878,6 +916,7 @@ bool TargetLibraryInfoImpl::isValidProto
>       return (NumParams == 2 && FTy.getParamType(0)->isPointerTy() &&
>               FTy.getParamType(1)->isPointerTy());
>     case LibFunc_putc:
> +  case LibFunc_putc_unlocked:
>       return (NumParams == 2 && FTy.getParamType(1)->isPointerTy());
>     case LibFunc_pread:
>     case LibFunc_pwrite:
> @@ -1264,6 +1303,7 @@ bool TargetLibraryInfoImpl::isValidProto
>     case LibFunc_isascii:
>     case LibFunc_toascii:
>     case LibFunc_putchar:
> +  case LibFunc_putchar_unlocked:
>       return (NumParams == 1 && FTy.getReturnType()->isIntegerTy(32) &&
>               FTy.getReturnType() == FTy.getParamType(0));


> Modified: llvm/trunk/lib/Transforms/Utils/BuildLibCalls.cpp
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/BuildLibCalls.cpp?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/lib/Transforms/Utils/BuildLibCalls.cpp (original)
> +++ llvm/trunk/lib/Transforms/Utils/BuildLibCalls.cpp Wed May 16 04:39:52
2018
> @@ -386,6 +386,7 @@ bool llvm::inferLibFuncAttributes(Functi
>     case LibFunc_fseek:
>     case LibFunc_ftell:
>     case LibFunc_fgetc:
> +  case LibFunc_fgetc_unlocked:
>     case LibFunc_fseeko:
>     case LibFunc_ftello:
>     case LibFunc_fileno:
> @@ -404,6 +405,7 @@ bool llvm::inferLibFuncAttributes(Functi
>       Changed |= setOnlyReadsMemory(F);
>       return Changed;
>     case LibFunc_fputc:
> +  case LibFunc_fputc_unlocked:
>     case LibFunc_fstat:
>     case LibFunc_frexp:
>     case LibFunc_frexpf:
> @@ -413,21 +415,25 @@ bool llvm::inferLibFuncAttributes(Functi
>       Changed |= setDoesNotCapture(F, 1);
>       return Changed;
>     case LibFunc_fgets:
> +  case LibFunc_fgets_unlocked:
>       Changed |= setDoesNotThrow(F);
>       Changed |= setDoesNotCapture(F, 2);
>       return Changed;
>     case LibFunc_fread:
> +  case LibFunc_fread_unlocked:
>       Changed |= setDoesNotThrow(F);
>       Changed |= setDoesNotCapture(F, 0);
>       Changed |= setDoesNotCapture(F, 3);
>       return Changed;
>     case LibFunc_fwrite:
> +  case LibFunc_fwrite_unlocked:
>       Changed |= setDoesNotThrow(F);
>       Changed |= setDoesNotCapture(F, 0);
>       Changed |= setDoesNotCapture(F, 3);
>       // FIXME: readonly #1?
>       return Changed;
>     case LibFunc_fputs:
> +  case LibFunc_fputs_unlocked:
>       Changed |= setDoesNotThrow(F);
>       Changed |= setDoesNotCapture(F, 0);
>       Changed |= setDoesNotCapture(F, 1);
> @@ -458,6 +464,7 @@ bool llvm::inferLibFuncAttributes(Functi
>       return Changed;
>     case LibFunc_gets:
>     case LibFunc_getchar:
> +  case LibFunc_getchar_unlocked:
>       Changed |= setDoesNotThrow(F);
>       return Changed;
>     case LibFunc_getitimer:
> @@ -496,6 +503,7 @@ bool llvm::inferLibFuncAttributes(Functi
>       Changed |= setOnlyReadsMemory(F, 1);
>       return Changed;
>     case LibFunc_putc:
> +  case LibFunc_putc_unlocked:
>       Changed |= setDoesNotThrow(F);
>       Changed |= setDoesNotCapture(F, 1);
>       return Changed;
> @@ -516,6 +524,7 @@ bool llvm::inferLibFuncAttributes(Functi
>       Changed |= setOnlyReadsMemory(F, 1);
>       return Changed;
>     case LibFunc_putchar:
> +  case LibFunc_putchar_unlocked:
>       Changed |= setDoesNotThrow(F);
>       return Changed;
>     case LibFunc_popen:
> @@ -997,6 +1006,24 @@ Value *llvm::emitFPutC(Value *Char, Valu
>     return CI;
>   }

> +Value *llvm::emitFPutCUnlocked(Value *Char, Value *File, IRBuilder<> &B,
> +                               const TargetLibraryInfo *TLI) {
> +  if (!TLI->has(LibFunc_fputc_unlocked))
> +    return nullptr;
> +
> +  Module *M = B.GetInsertBlock()->getModule();
> +  Constant *F = M->getOrInsertFunction("fputc_unlocked", B.getInt32Ty(),
> +                                       B.getInt32Ty(), File->getType());
> +  if (File->getType()->isPointerTy())
> +    inferLibFuncAttributes(*M->getFunction("fputc_unlocked"), *TLI);
> +  Char = B.CreateIntCast(Char, B.getInt32Ty(), /*isSigned*/ true,
"chari");
> +  CallInst *CI = B.CreateCall(F, {Char, File}, "fputc_unlocked");
> +
> +  if (const Function *Fn = dyn_cast<Function>(F->stripPointerCasts()))
> +    CI->setCallingConv(Fn->getCallingConv());
> +  return CI;
> +}
> +
>   Value *llvm::emitFPutS(Value *Str, Value *File, IRBuilder<> &B,
>                          const TargetLibraryInfo *TLI) {
>     if (!TLI->has(LibFunc_fputs))
> @@ -1015,6 +1042,24 @@ Value *llvm::emitFPutS(Value *Str, Value
>     return CI;
>   }

> +Value *llvm::emitFPutSUnlocked(Value *Str, Value *File, IRBuilder<> &B,
> +                               const TargetLibraryInfo *TLI) {
> +  if (!TLI->has(LibFunc_fputs_unlocked))
> +    return nullptr;
> +
> +  Module *M = B.GetInsertBlock()->getModule();
> +  StringRef FPutsUnlockedName = TLI->getName(LibFunc_fputs_unlocked);
> +  Constant *F = M->getOrInsertFunction(FPutsUnlockedName, B.getInt32Ty(),
> +                                       B.getInt8PtrTy(),
File->getType());
> +  if (File->getType()->isPointerTy())
> +    inferLibFuncAttributes(*M->getFunction(FPutsUnlockedName), *TLI);
> +  CallInst *CI = B.CreateCall(F, {castToCStr(Str, B), File},
"fputs_unlocked");
> +
> +  if (const Function *Fn = dyn_cast<Function>(F->stripPointerCasts()))
> +    CI->setCallingConv(Fn->getCallingConv());
> +  return CI;
> +}
> +
>   Value *llvm::emitFWrite(Value *Ptr, Value *Size, Value *File,
IRBuilder<> &B,
>                           const DataLayout &DL, const TargetLibraryInfo
*TLI) {
>     if (!TLI->has(LibFunc_fwrite))
> @@ -1074,3 +1119,82 @@ Value *llvm::emitCalloc(Value *Num, Valu

>     return CI;
>   }
> +
> +Value *llvm::emitFWriteUnlocked(Value *Ptr, Value *Size, Value *N, Value
*File,
> +                                IRBuilder<> &B, const DataLayout &DL,
> +                                const TargetLibraryInfo *TLI) {
> +  if (!TLI->has(LibFunc_fwrite_unlocked))
> +    return nullptr;
> +
> +  Module *M = B.GetInsertBlock()->getModule();
> +  LLVMContext &Context = B.GetInsertBlock()->getContext();
> +  StringRef FWriteUnlockedName = TLI->getName(LibFunc_fwrite_unlocked);
> +  Constant *F = M->getOrInsertFunction(
> +      FWriteUnlockedName, DL.getIntPtrType(Context), B.getInt8PtrTy(),
> +      DL.getIntPtrType(Context), DL.getIntPtrType(Context),
File->getType());
> +
> +  if (File->getType()->isPointerTy())
> +    inferLibFuncAttributes(*M->getFunction(FWriteUnlockedName), *TLI);
> +  CallInst *CI = B.CreateCall(F, {castToCStr(Ptr, B), Size, N, File});
> +
> +  if (const Function *Fn = dyn_cast<Function>(F->stripPointerCasts()))
> +    CI->setCallingConv(Fn->getCallingConv());
> +  return CI;
> +}
> +
> +Value *llvm::emitFGetCUnlocked(Value *File, IRBuilder<> &B,
> +                               const TargetLibraryInfo *TLI) {
> +  if (!TLI->has(LibFunc_fgetc_unlocked))
> +    return nullptr;
> +
> +  Module *M = B.GetInsertBlock()->getModule();
> +  Constant *F =
> +      M->getOrInsertFunction("fgetc_unlocked", B.getInt32Ty(),
File->getType());
> +  if (File->getType()->isPointerTy())
> +    inferLibFuncAttributes(*M->getFunction("fgetc_unlocked"), *TLI);
> +  CallInst *CI = B.CreateCall(F, File, "fgetc_unlocked");
> +
> +  if (const Function *Fn = dyn_cast<Function>(F->stripPointerCasts()))
> +    CI->setCallingConv(Fn->getCallingConv());
> +  return CI;
> +}
> +
> +Value *llvm::emitFGetSUnlocked(Value *Str, Value *Size, Value *File,
> +                               IRBuilder<> &B, const TargetLibraryInfo
*TLI) {
> +  if (!TLI->has(LibFunc_fgets_unlocked))
> +    return nullptr;
> +
> +  Module *M = B.GetInsertBlock()->getModule();
> +  Constant *F =
> +      M->getOrInsertFunction("fgets_unlocked", B.getInt32Ty(),
B.getInt8PtrTy(),
> +                             B.getInt32Ty(), File->getType());
> +  inferLibFuncAttributes(*M->getFunction("fgets_unlocked"), *TLI);
> +  CallInst *CI =
> +      B.CreateCall(F, {castToCStr(Str, B), Size, File},
"fgets_unlocked");
> +
> +  if (const Function *Fn = dyn_cast<Function>(F->stripPointerCasts()))
> +    CI->setCallingConv(Fn->getCallingConv());
> +  return CI;
> +}
> +
> +Value *llvm::emitFReadUnlocked(Value *Ptr, Value *Size, Value *N, Value
*File,
> +                               IRBuilder<> &B, const DataLayout &DL,
> +                               const TargetLibraryInfo *TLI) {
> +  if (!TLI->has(LibFunc_fread_unlocked))
> +    return nullptr;
> +
> +  Module *M = B.GetInsertBlock()->getModule();
> +  LLVMContext &Context = B.GetInsertBlock()->getContext();
> +  StringRef FReadUnlockedName = TLI->getName(LibFunc_fread_unlocked);
> +  Constant *F = M->getOrInsertFunction(
> +      FReadUnlockedName, DL.getIntPtrType(Context), B.getInt8PtrTy(),
> +      DL.getIntPtrType(Context), DL.getIntPtrType(Context),
File->getType());
> +
> +  if (File->getType()->isPointerTy())
> +    inferLibFuncAttributes(*M->getFunction(FReadUnlockedName), *TLI);
> +  CallInst *CI = B.CreateCall(F, {castToCStr(Ptr, B), Size, N, File});
> +
> +  if (const Function *Fn = dyn_cast<Function>(F->stripPointerCasts()))
> +    CI->setCallingConv(Fn->getCallingConv());
> +  return CI;
> +}

> Modified: llvm/trunk/lib/Transforms/Utils/SimplifyLibCalls.cpp
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/SimplifyLibCalls.cpp?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/lib/Transforms/Utils/SimplifyLibCalls.cpp (original)
> +++ llvm/trunk/lib/Transforms/Utils/SimplifyLibCalls.cpp Wed May 16
04:39:52 2018
> @@ -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(CallIns
>     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
>     // 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 this is writing zero records, remove the call (it's a noop).
> -  if (Bytes == 0)
> -    return ConstantInt::get(CI->getType(), 0);
> +  if (SizeC && CountC) {
> +    uint64_t Bytes = SizeC->getZExtValue() * CountC->getZExtValue();

> -  // 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 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 (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(
>     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(
>         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(C
>         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(C
>       case LibFunc_vfprintf:
>       case LibFunc_fiprintf:
>         return optimizeErrorReporting(CI, Builder, 0);
> -    case LibFunc_fputc:
> -      return optimizeErrorReporting(CI, Builder, 1);
>       default:
>         return nullptr;
>       }

> Modified: llvm/trunk/test/Transforms/InferFunctionAttrs/annotate.ll
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InferFunctionAttrs/annotate.ll?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/test/Transforms/InferFunctionAttrs/annotate.ll (original)
> +++ llvm/trunk/test/Transforms/InferFunctionAttrs/annotate.ll Wed May 16
04:39:52 2018
> @@ -514,6 +514,9 @@ declare i32 @getc_unlocked(%opaque*)
>   ; CHECK: declare i32 @getchar()
>   declare i32 @getchar()

> +; CHECK: declare i32 @getchar_unlocked()
> +declare i32 @getchar_unlocked()
> +
>   ; CHECK: declare i8* @getenv(i8* nocapture) [[G1]]
>   declare i8* @getenv(i8*)

> @@ -700,6 +703,9 @@ declare i32 @putc(i32, %opaque*)
>   ; CHECK: declare i32 @putchar(i32)
>   declare i32 @putchar(i32)

> +; CHECK: declare i32 @putchar_unlocked(i32)
> +declare i32 @putchar_unlocked(i32)
> +
>   ; CHECK: declare i32 @puts(i8* nocapture readonly) [[G0]]
>   declare i32 @puts(i8*)


> Added: llvm/trunk/test/Transforms/InstCombine/unlocked-stdio.ll
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Transforms/InstCombine/unlocked-stdio.ll?rev=332452&view=auto

==============================================================================
> --- llvm/trunk/test/Transforms/InstCombine/unlocked-stdio.ll (added)
> +++ llvm/trunk/test/Transforms/InstCombine/unlocked-stdio.ll Wed May 16
04:39:52 2018
> @@ -0,0 +1,197 @@
> +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
> +; RUN: opt < %s -instcombine -S -mtriple=x86_64-unknown-linux-gnu |
FileCheck %s
> +
> +%struct._IO_FILE = type { i32, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*,
i8*, i8*, i8*, %struct._IO_marker*, %struct._IO_FILE*, i32, i32, i64, i16,
i8, [1 x i8], i8*, i64, i8*, i8*, i8*, i8*, i64, i32, [20 x i8] }
> +%struct._IO_marker = type { %struct._IO_marker*, %struct._IO_FILE*, i32 }
> +
> + at .str = private unnamed_addr constant [5 x i8] c"file\00", align 1
> + at .str.1 = private unnamed_addr constant [2 x i8] c"w\00", align 1
> + at .str.2 = private unnamed_addr constant [4 x i8] c"str\00", align 1
> + at global_file = common global %struct._IO_FILE* null, align 8
> +
> +define void @external_fgetc_test(%struct._IO_FILE* %f) {
> +; CHECK-LABEL: @external_fgetc_test(
> +; CHECK-NEXT:    [[CALL:%.*]] = call i32 @fgetc(%struct._IO_FILE*
[[F:%.*]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call i32 @fgetc(%struct._IO_FILE* %f)
> +  ret void
> +}
> +
> +declare i32 @fgetc(%struct._IO_FILE* nocapture) #0
> +
> +define void @external_fgetc_test2() {
> +; CHECK-LABEL: @external_fgetc_test2(
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[FPUTC_UNLOCKED:%.*]] = call i32 @fputc_unlocked(i32
99, %struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0))
> +  %call1 = call i32 @fputc(i32 99, %struct._IO_FILE* %call)
> +  ret void
> +}
> +
> +declare %struct._IO_FILE* @fopen(i8*, i8*)
> +declare i32 @fputc(i32, %struct._IO_FILE* nocapture) #0
> +
> +define internal void @fgetc_test() {
> +; CHECK-LABEL: @fgetc_test(
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[FGETC_UNLOCKED:%.*]] = call i32
@fgetc_unlocked(%struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0))
> +  %call1 = call i32 @fgetc(%struct._IO_FILE* %call)
> +  ret void
> +}
> +
> +define void @external_fgetc_internal_test() {
> +; CHECK-LABEL: @external_fgetc_internal_test(
> +; CHECK-NEXT:    call void @fgetc_test()
> +; CHECK-NEXT:    ret void
> +;
> +  call void @fgetc_test()
> +  ret void
> +}
> +
> +define internal void @fwrite_test() {
> +; CHECK-LABEL: @fwrite_test(
> +; CHECK-NEXT:    [[S:%.*]] = alloca [10 x i8], align 1
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8],
[10 x i8]* [[S]], i64 0, i64 0
> +; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fwrite_unlocked(i8* nonnull
[[ARRAYDECAY]], i64 10, i64 10, %struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %s = alloca [10 x i8], align 1
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0))
> +  %arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %s, i64 0,
i64 0
> +  %call1 = call i64 @fwrite(i8* nonnull %arraydecay, i64 10, i64 10,
%struct._IO_FILE* %call)
> +  ret void
> +}
> +
> +define internal void @fread_test() {
> +; CHECK-LABEL: @fread_test(
> +; CHECK-NEXT:    [[S:%.*]] = alloca [10 x i8], align 1
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8],
[10 x i8]* [[S]], i64 0, i64 0
> +; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fread_unlocked(i8* nonnull
[[ARRAYDECAY]], i64 10, i64 10, %struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %s = alloca [10 x i8], align 1
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0))
> +  %arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %s, i64 0,
i64 0
> +  %call1 = call i64 @fread(i8* nonnull %arraydecay, i64 10, i64 10,
%struct._IO_FILE* %call)
> +  ret void
> +}
> +
> +define internal void @fputs_test() {
> +; CHECK-LABEL: @fputs_test(
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fwrite_unlocked(i8*
getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3,
i64 1, %struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0))
> +  %call1 = call i32 @fputs(i8* getelementptr inbounds ([4 x i8], [4 x
i8]* @.str.2, i64 0, i64 0), %struct._IO_FILE* %call)
> +  ret void
> +}
> +
> +define internal void @fgets_test() {
> +; CHECK-LABEL: @fgets_test(
> +; CHECK-NEXT:    [[BUF:%.*]] = alloca [10 x i8], align 1
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i8],
[10 x i8]* [[BUF]], i64 0, i64 0
> +; CHECK-NEXT:    [[FGETS_UNLOCKED:%.*]] = call i32 @fgets_unlocked(i8*
nonnull [[ARRAYDECAY]], i32 10, %struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %buf = alloca [10 x i8], align 1
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0))
> +  %arraydecay = getelementptr inbounds [10 x i8], [10 x i8]* %buf, i64
0, i64 0
> +  %call1 = call i8* @fgets(i8* nonnull %arraydecay, i32 10,
%struct._IO_FILE* %call)
> +  ret void
> +}
> +
> +define internal void @fputc_test() {
> +; CHECK-LABEL: @fputc_test(
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[FPUTC_UNLOCKED:%.*]] = call i32 @fputc_unlocked(i32
99, %struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0))
> +  %call1 = call i32 @fputc(i32 99, %struct._IO_FILE* %call)
> +  ret void
> +}
> +
> +define i32 @main() {
> +; CHECK-LABEL: @main(
> +; CHECK-NEXT:    call void @fwrite_test()
> +; CHECK-NEXT:    call void @fread_test()
> +; CHECK-NEXT:    call void @fputs_test()
> +; CHECK-NEXT:    call void @fgets_test()
> +; CHECK-NEXT:    call void @fputc_test()
> +; CHECK-NEXT:    call void @fgetc_test()
> +; CHECK-NEXT:    ret i32 0
> +;
> +  call void @fwrite_test()
> +  call void @fread_test()
> +  call void @fputs_test()
> +  call void @fgets_test()
> +  call void @fputc_test()
> +  call void @fgetc_test()
> +  ret i32 0
> +}
> +
> +declare i32 @fclose(%struct._IO_FILE* nocapture)
> +
> +define void @test_with_fclose() {
> +; CHECK-LABEL: @test_with_fclose(
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @fwrite_unlocked(i8*
getelementptr inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3,
i64 1, %struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    [[CALL2:%.*]] = call i32 @fclose(%struct._IO_FILE*
[[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0)) #2
> +  %call1 = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x
i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* %call)
> +  %call2 = call i32 @fclose(%struct._IO_FILE* %call) #2
> +  ret void
> +}
> +
> +declare void @modify_file(%struct._IO_FILE*)
> +
> +define void @test_captured_by_function(){
> +; CHECK-LABEL: @test_captured_by_function(
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    call void @modify_file(%struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    [[CALL1:%.*]] = call i64 @fwrite(i8* getelementptr
inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1,
%struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    [[CALL2:%.*]] = call i32 @fclose(%struct._IO_FILE*
[[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0)) #2
> +  call void @modify_file(%struct._IO_FILE* %call) #2
> +  %call1 = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x
i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* %call)
> +  %call2 = call i32 @fclose(%struct._IO_FILE* %call) #2
> +  ret void
> +}
> +
> +define void @test_captured_by_global_value() {
> +; CHECK-LABEL: @test_captured_by_global_value(
> +; CHECK-NEXT:    [[CALL:%.*]] = call %struct._IO_FILE* @fopen(i8*
getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8*
getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i64 0, i64 0))
> +; CHECK-NEXT:    [[DOTCAST:%.*]] = ptrtoint %struct._IO_FILE* [[CALL]]
to i64
> +; CHECK-NEXT:    store i64 [[DOTCAST]], i64* bitcast (%struct._IO_FILE**
@global_file to i64*), align 8
> +; CHECK-NEXT:    [[CALL1:%.*]] = call i64 @fwrite(i8* getelementptr
inbounds ([4 x i8], [4 x i8]* @.str.2, i64 0, i64 0), i64 3, i64 1,
%struct._IO_FILE* [[CALL]])
> +; CHECK-NEXT:    [[CALL2:%.*]] = call i32 @fclose(%struct._IO_FILE*
[[CALL]])
> +; CHECK-NEXT:    ret void
> +;
> +  %call = call %struct._IO_FILE* @fopen(i8* getelementptr inbounds ([5 x
i8], [5 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([2 x i8],
[2 x i8]* @.str.1, i64 0, i64 0)) #2
> +  %.cast = ptrtoint %struct._IO_FILE* %call to i64
> +  store i64 %.cast, i64* bitcast (%struct._IO_FILE** @global_file to
i64*), align 8
> +  %call1 = call i64 @fwrite(i8* getelementptr inbounds ([4 x i8], [4 x
i8]* @.str.2, i64 0, i64 0), i64 3, i64 1, %struct._IO_FILE* %call)
> +  %call2 = call i32 @fclose(%struct._IO_FILE* %call) #2
> +  ret void
> +}
> +
> +
> +
> +declare i64 @fwrite(i8* nocapture, i64, i64, %struct._IO_FILE*
nocapture) #0
> +declare i64 @fread(i8* nocapture, i64, i64, %struct._IO_FILE* nocapture)
#0
> +declare i32 @fputs(i8* nocapture readonly, %struct._IO_FILE* nocapture)
#0
> +declare i8* @fgets(i8*, i32, %struct._IO_FILE* nocapture) #0

> Modified: llvm/trunk/unittests/Analysis/TargetLibraryInfoTest.cpp
> URL:
http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Analysis/TargetLibraryInfoTest.cpp?rev=332452&r1=332451&r2=332452&view=diff

==============================================================================
> --- llvm/trunk/unittests/Analysis/TargetLibraryInfoTest.cpp (original)
> +++ llvm/trunk/unittests/Analysis/TargetLibraryInfoTest.cpp Wed May 16
04:39:52 2018
> @@ -161,8 +161,10 @@ TEST_F(TargetLibraryInfoTest, ValidProto
>       "declare i32 @ffsl(i64)\n"
>       "declare i32 @ffsll(i64)\n"
>       "declare i32 @fgetc(%struct*)\n"
> +    "declare i32 @fgetc_unlocked(%struct*)\n"
>       "declare i32 @fgetpos(%struct*, i64*)\n"
>       "declare i8* @fgets(i8*, i32, %struct*)\n"
> +    "declare i8* @fgets_unlocked(i8*, i32, %struct*)\n"
>       "declare i32 @fileno(%struct*)\n"
>       "declare void @flockfile(%struct*)\n"
>       "declare double @floor(double)\n"
> @@ -182,7 +184,9 @@ TEST_F(TargetLibraryInfoTest, ValidProto
>       "declare x86_fp80 @fmodl(x86_fp80, x86_fp80)\n"
>       "declare i32 @fprintf(%struct*, i8*, ...)\n"
>       "declare i32 @fputc(i32, %struct*)\n"
> +    "declare i32 @fputc_unlocked(i32, %struct*)\n"
>       "declare i64 @fread(i8*, i64, i64, %struct*)\n"
> +    "declare i64 @fread_unlocked(i8*, i64, i64, %struct*)\n"
>       "declare void @free(i8*)\n"
>       "declare double @frexp(double, i32*)\n"
>       "declare float @frexpf(float, i32*)\n"
> @@ -199,6 +203,7 @@ TEST_F(TargetLibraryInfoTest, ValidProto
>       "declare i32 @getc(%struct*)\n"
>       "declare i32 @getc_unlocked(%struct*)\n"
>       "declare i32 @getchar()\n"
> +    "declare i32 @getchar_unlocked()\n"
>       "declare i8* @getenv(i8*)\n"
>       "declare i32 @getitimer(i32, %struct*)\n"
>       "declare i32 @getlogin_r(i8*, i64)\n"
> @@ -250,7 +255,9 @@ TEST_F(TargetLibraryInfoTest, ValidProto
>       "declare x86_fp80 @powl(x86_fp80, x86_fp80)\n"
>       "declare i32 @printf(i8*, ...)\n"
>       "declare i32 @putc(i32, %struct*)\n"
> +    "declare i32 @putc_unlocked(i32, %struct*)\n"
>       "declare i32 @putchar(i32)\n"
> +    "declare i32 @putchar_unlocked(i32)\n"
>       "declare i32 @puts(i8*)\n"
>       "declare void @qsort(i8*, i64, i64, i32 (i8*, i8*)*)\n"
>       "declare i64 @readlink(i8*, i8*, i64)\n"
> @@ -347,8 +354,10 @@ TEST_F(TargetLibraryInfoTest, ValidProto
>       "declare %struct* @fdopen(i32, i8*)\n"
>       "declare %struct* @fopen(i8*, i8*)\n"
>       "declare i32 @fputs(i8*, %struct*)\n"
> +    "declare i32 @fputs_unlocked(i8*, %struct*)\n"
>       "declare i32 @fstat(i32, %struct*)\n"
>       "declare i64 @fwrite(i8*, i64, i64, %struct*)\n"
> +    "declare i64 @fwrite_unlocked(i8*, i64, i64, %struct*)\n"
>       "declare i32 @lchown(i8*, i32, i32)\n"
>       "declare i32 @lstat(i8*, %struct*)\n"
>       "declare i64 @mktime(%struct*)\n"


> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits


More information about the llvm-commits mailing list