1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-20 19:42:54 +02:00
llvm-mirror/include/llvm/Transforms/Utils/SimplifyLibCalls.h
Fangrui Song a99d809eec [SimplifyLibcalls] Don't replace locked IO (fgetc/fgets/fputc/fputs/fread/fwrite) with unlocked IO (*_unlocked)
This essentially reverts some of the SimplifyLibcalls part changes of D45736 [SimplifyLibcalls] Replace locked IO with unlocked IO.

C11 7.21.5.2 The fflush function

> If stream is a null pointer, the fflush function performs this flushing action on all streams for which the behavior is defined above.

i.e. fopen'ed FILE* is inherently captured.

POSIX.1-2017 getc_unlocked, getchar_unlocked, putc_unlocked, putchar_unlocked - stdio with explicit client locking

> These functions can safely be used in a multi-threaded program if and only if they are called while the invoking thread owns the ( FILE *) object, as is the case after a successful call to the flockfile() or ftrylockfile() functions.

After a thread fopen'ed a FILE*, when it is calling foobar() which is now replaced by foobar_unlocked(),
if another thread is concurrently calling fflush(0), the behavior is undefined.

C11 7.22.4.4 The exit function

> Next, all open streams with unwritten buffered data are flushed, all open streams are closed, and all files created by the tmpfile function are removed.

The replacement is only feasible if the program is single threaded, or exit or fflush(0) is never called.
See also http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20180528/556615.html
for how the replacement makes libc interceptors difficult to implement.

dalias: in a worst case, it's unbounded data corruption because of concurrent access to pointers
without synchronization.  f->wpos or rpos could get outside of the buffer, thread A could do
f->wpos += j after knowing j is in bounds, while thread B also changes it concurrently.

This can produce exploitable conditions depending on libc internals.

Revert the SimplifyLibcalls part change because the cons obviously
overweigh the pros.  Even when the replacement is feasible, the benefit
is indemonstrable, more so in an application instead of an artificial
glibc benchmark.  Theoretically the replacement could be beneficial when
calling getc_unlocked/putc_unlocked in a loop, but then it is better
using a blocked IO operation and the user is likely aware of that.

The function attribute inference is still useful and thus kept.

Reviewed By: xbolva00

Differential Revision: https://reviews.llvm.org/D75933
2020-03-10 11:11:58 -07:00

248 lines
11 KiB
C++

//===- SimplifyLibCalls.h - Library call simplifier -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file exposes an interface to build some C language libcalls for
// optimization passes that need to call the various functions.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_TRANSFORMS_UTILS_SIMPLIFYLIBCALLS_H
#define LLVM_TRANSFORMS_UTILS_SIMPLIFYLIBCALLS_H
#include "llvm/ADT/STLExtras.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
namespace llvm {
class StringRef;
class Value;
class CallInst;
class DataLayout;
class Instruction;
class IRBuilderBase;
class TargetLibraryInfo;
class BasicBlock;
class Function;
class OptimizationRemarkEmitter;
class BlockFrequencyInfo;
class ProfileSummaryInfo;
/// This class implements simplifications for calls to fortified library
/// functions (__st*cpy_chk, __memcpy_chk, __memmove_chk, __memset_chk), to,
/// when possible, replace them with their non-checking counterparts.
/// Other optimizations can also be done, but it's possible to disable them and
/// only simplify needless use of the checking versions (when the object size
/// is unknown) by passing true for OnlyLowerUnknownSize.
class FortifiedLibCallSimplifier {
private:
const TargetLibraryInfo *TLI;
bool OnlyLowerUnknownSize;
public:
FortifiedLibCallSimplifier(const TargetLibraryInfo *TLI,
bool OnlyLowerUnknownSize = false);
/// Take the given call instruction and return a more
/// optimal value to replace the instruction with or 0 if a more
/// optimal form can't be found.
/// The call must not be an indirect call.
Value *optimizeCall(CallInst *CI, IRBuilderBase &B);
private:
Value *optimizeMemCpyChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemMoveChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemSetChk(CallInst *CI, IRBuilderBase &B);
/// Str/Stp cpy are similar enough to be handled in the same functions.
Value *optimizeStrpCpyChk(CallInst *CI, IRBuilderBase &B, LibFunc Func);
Value *optimizeStrpNCpyChk(CallInst *CI, IRBuilderBase &B, LibFunc Func);
Value *optimizeStrLenChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemCCpyChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeSNPrintfChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeSPrintfChk(CallInst *CI,IRBuilderBase &B);
Value *optimizeStrCatChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrLCat(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrNCatChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrLCpyChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeVSNPrintfChk(CallInst *CI, IRBuilderBase &B);
Value *optimizeVSPrintfChk(CallInst *CI, IRBuilderBase &B);
/// Checks whether the call \p CI to a fortified libcall is foldable
/// to the non-fortified version.
///
/// \param CI the call to the fortified libcall.
///
/// \param ObjSizeOp the index of the object size parameter of this chk
/// function. Not optional since this is mandatory.
///
/// \param SizeOp optionally set to the parameter index of an explicit buffer
/// size argument. For instance, set to '2' for __strncpy_chk.
///
/// \param StrOp optionally set to the parameter index of the source string
/// parameter to strcpy-like functions, where only the strlen of the source
/// will be writtin into the destination.
///
/// \param FlagsOp optionally set to the parameter index of a 'flags'
/// parameter. These are used by an implementation to opt-into stricter
/// checking.
bool isFortifiedCallFoldable(CallInst *CI, unsigned ObjSizeOp,
Optional<unsigned> SizeOp = None,
Optional<unsigned> StrOp = None,
Optional<unsigned> FlagsOp = None);
};
/// LibCallSimplifier - This class implements a collection of optimizations
/// that replace well formed calls to library functions with a more optimal
/// form. For example, replacing 'printf("Hello!")' with 'puts("Hello!")'.
class LibCallSimplifier {
private:
FortifiedLibCallSimplifier FortifiedSimplifier;
const DataLayout &DL;
const TargetLibraryInfo *TLI;
OptimizationRemarkEmitter &ORE;
BlockFrequencyInfo *BFI;
ProfileSummaryInfo *PSI;
bool UnsafeFPShrink;
function_ref<void(Instruction *, Value *)> Replacer;
function_ref<void(Instruction *)> Eraser;
/// Internal wrapper for RAUW that is the default implementation.
///
/// Other users may provide an alternate function with this signature instead
/// of this one.
static void replaceAllUsesWithDefault(Instruction *I, Value *With) {
I->replaceAllUsesWith(With);
}
/// Internal wrapper for eraseFromParent that is the default implementation.
static void eraseFromParentDefault(Instruction *I) { I->eraseFromParent(); }
/// Replace an instruction's uses with a value using our replacer.
void replaceAllUsesWith(Instruction *I, Value *With);
/// Erase an instruction from its parent with our eraser.
void eraseFromParent(Instruction *I);
/// Replace an instruction with a value and erase it from its parent.
void substituteInParent(Instruction *I, Value *With) {
replaceAllUsesWith(I, With);
eraseFromParent(I);
}
Value *foldMallocMemset(CallInst *Memset, IRBuilderBase &B);
public:
LibCallSimplifier(
const DataLayout &DL, const TargetLibraryInfo *TLI,
OptimizationRemarkEmitter &ORE,
BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI,
function_ref<void(Instruction *, Value *)> Replacer =
&replaceAllUsesWithDefault,
function_ref<void(Instruction *)> Eraser = &eraseFromParentDefault);
/// optimizeCall - Take the given call instruction and return a more
/// optimal value to replace the instruction with or 0 if a more
/// optimal form can't be found. Note that the returned value may
/// be equal to the instruction being optimized. In this case all
/// other instructions that use the given instruction were modified
/// and the given instruction is dead.
/// The call must not be an indirect call.
Value *optimizeCall(CallInst *CI, IRBuilderBase &B);
private:
// String and Memory Library Call Optimizations
Value *optimizeStrCat(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrNCat(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrChr(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrRChr(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrCmp(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrNCmp(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrNDup(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrCpy(CallInst *CI, IRBuilderBase &B);
Value *optimizeStpCpy(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrNCpy(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrLen(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrPBrk(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrTo(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrSpn(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrCSpn(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrStr(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemChr(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemRChr(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemCmp(CallInst *CI, IRBuilderBase &B);
Value *optimizeBCmp(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemCmpBCmpCommon(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemCCpy(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemPCpy(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemCpy(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemMove(CallInst *CI, IRBuilderBase &B);
Value *optimizeMemSet(CallInst *CI, IRBuilderBase &B);
Value *optimizeRealloc(CallInst *CI, IRBuilderBase &B);
Value *optimizeWcslen(CallInst *CI, IRBuilderBase &B);
Value *optimizeBCopy(CallInst *CI, IRBuilderBase &B);
// Wrapper for all String/Memory Library Call Optimizations
Value *optimizeStringMemoryLibCall(CallInst *CI, IRBuilderBase &B);
// Math Library Optimizations
Value *optimizeCAbs(CallInst *CI, IRBuilderBase &B);
Value *optimizePow(CallInst *CI, IRBuilderBase &B);
Value *replacePowWithExp(CallInst *Pow, IRBuilderBase &B);
Value *replacePowWithSqrt(CallInst *Pow, IRBuilderBase &B);
Value *optimizeExp2(CallInst *CI, IRBuilderBase &B);
Value *optimizeFMinFMax(CallInst *CI, IRBuilderBase &B);
Value *optimizeLog(CallInst *CI, IRBuilderBase &B);
Value *optimizeSqrt(CallInst *CI, IRBuilderBase &B);
Value *optimizeSinCosPi(CallInst *CI, IRBuilderBase &B);
Value *optimizeTan(CallInst *CI, IRBuilderBase &B);
// Wrapper for all floating point library call optimizations
Value *optimizeFloatingPointLibCall(CallInst *CI, LibFunc Func,
IRBuilderBase &B);
// Integer Library Call Optimizations
Value *optimizeFFS(CallInst *CI, IRBuilderBase &B);
Value *optimizeFls(CallInst *CI, IRBuilderBase &B);
Value *optimizeAbs(CallInst *CI, IRBuilderBase &B);
Value *optimizeIsDigit(CallInst *CI, IRBuilderBase &B);
Value *optimizeIsAscii(CallInst *CI, IRBuilderBase &B);
Value *optimizeToAscii(CallInst *CI, IRBuilderBase &B);
Value *optimizeAtoi(CallInst *CI, IRBuilderBase &B);
Value *optimizeStrtol(CallInst *CI, IRBuilderBase &B);
// Formatting and IO Library Call Optimizations
Value *optimizeErrorReporting(CallInst *CI, IRBuilderBase &B,
int StreamArg = -1);
Value *optimizePrintF(CallInst *CI, IRBuilderBase &B);
Value *optimizeSPrintF(CallInst *CI, IRBuilderBase &B);
Value *optimizeSnPrintF(CallInst *CI, IRBuilderBase &B);
Value *optimizeFPrintF(CallInst *CI, IRBuilderBase &B);
Value *optimizeFWrite(CallInst *CI, IRBuilderBase &B);
Value *optimizeFPuts(CallInst *CI, IRBuilderBase &B);
Value *optimizePuts(CallInst *CI, IRBuilderBase &B);
// Helper methods
Value *emitStrLenMemCpy(Value *Src, Value *Dst, uint64_t Len,
IRBuilderBase &B);
void classifyArgUse(Value *Val, Function *F, bool IsFloat,
SmallVectorImpl<CallInst *> &SinCalls,
SmallVectorImpl<CallInst *> &CosCalls,
SmallVectorImpl<CallInst *> &SinCosCalls);
Value *optimizePrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSPrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeSnPrintFString(CallInst *CI, IRBuilderBase &B);
Value *optimizeFPrintFString(CallInst *CI, IRBuilderBase &B);
/// hasFloatVersion - Checks if there is a float version of the specified
/// function by checking for an existing function with name FuncName + f
bool hasFloatVersion(StringRef FuncName);
/// Shared code to optimize strlen+wcslen.
Value *optimizeStringLength(CallInst *CI, IRBuilderBase &B, unsigned CharSize);
};
} // End llvm namespace
#endif