From 9d434dbff3eb0501efc3457acec2401afdffef2f Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Thu, 17 Nov 2011 01:27:36 +0000 Subject: [PATCH] Add support for custom names for library functions in TargetLibraryInfo. Add a custom name for fwrite and fputs on x86-32 OSX. Make SimplifyLibCalls honor the custom names for fwrite and fputs. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@144876 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Target/TargetLibraryInfo.h | 56 +++++++++++++++++-- include/llvm/Transforms/Utils/BuildLibCalls.h | 6 +- lib/Target/TargetLibraryInfo.cpp | 25 +++++++++ lib/Transforms/Scalar/SimplifyLibCalls.cpp | 20 ++++--- lib/Transforms/Utils/BuildLibCalls.cpp | 18 ++++-- test/Transforms/SimplifyLibCalls/osx-names.ll | 30 ++++++++++ 6 files changed, 134 insertions(+), 21 deletions(-) create mode 100644 test/Transforms/SimplifyLibCalls/osx-names.ll diff --git a/include/llvm/Target/TargetLibraryInfo.h b/include/llvm/Target/TargetLibraryInfo.h index 02a1a3ca23a..7770a11ac0d 100644 --- a/include/llvm/Target/TargetLibraryInfo.h +++ b/include/llvm/Target/TargetLibraryInfo.h @@ -11,6 +11,7 @@ #define LLVM_TARGET_TARGETLIBRARYINFO_H #include "llvm/Pass.h" +#include "llvm/ADT/DenseMap.h" namespace llvm { class Triple; @@ -37,7 +38,14 @@ namespace llvm { /// int fiprintf(FILE *stream, const char *format, ...); fiprintf, - + + // size_t fwrite(const void *ptr, size_t size, size_t nitems, + // FILE *stream); + fwrite, + + // int fputs(const char *s, FILE *stream); + fputs, + NumLibFuncs }; } @@ -46,7 +54,23 @@ namespace llvm { /// library functions are available for the current target, and allows a /// frontend to disable optimizations through -fno-builtin etc. class TargetLibraryInfo : public ImmutablePass { - unsigned char AvailableArray[(LibFunc::NumLibFuncs+7)/8]; + unsigned char AvailableArray[(LibFunc::NumLibFuncs+3)/4]; + llvm::DenseMap CustomNames; + static const char* StandardNames[LibFunc::NumLibFuncs]; + + enum AvailabilityState { + StandardName = 3, // (memset to all ones) + CustomName = 1, + Unavailable = 0 // (memset to all zeros) + }; + void setState(LibFunc::Func F, AvailabilityState State) { + AvailableArray[F/4] &= ~(3 << 2*(F&3)); + AvailableArray[F/4] |= State << 2*(F&3); + } + AvailabilityState getState(LibFunc::Func F) const { + return static_cast((AvailableArray[F/4] >> 2*(F&3)) & 3); + } + public: static char ID; TargetLibraryInfo(); @@ -56,19 +80,39 @@ public: /// has - This function is used by optimizations that want to match on or form /// a given library function. bool has(LibFunc::Func F) const { - return (AvailableArray[F/8] & (1 << (F&7))) != 0; + return getState(F) != Unavailable; + } + + StringRef getName(LibFunc::Func F) const { + AvailabilityState State = getState(F); + if (State == Unavailable) + return StringRef(); + if (State == StandardName) + return StandardNames[F]; + assert(State == CustomName); + return CustomNames.find(F)->second; } /// setUnavailable - this can be used by whatever sets up TargetLibraryInfo to /// ban use of specific library functions. void setUnavailable(LibFunc::Func F) { - AvailableArray[F/8] &= ~(1 << (F&7)); + setState(F, Unavailable); } void setAvailable(LibFunc::Func F) { - AvailableArray[F/8] |= 1 << (F&7); + setState(F, StandardName); } - + + void setAvailableWithName(LibFunc::Func F, StringRef Name) { + if (StandardNames[F] != Name) { + setState(F, CustomName); + CustomNames[F] = Name; + assert(CustomNames.find(F) != CustomNames.end()); + } else { + setState(F, StandardName); + } + } + /// disableAllFunctions - This disables all builtins, which is used for /// options like -fno-builtin. void disableAllFunctions(); diff --git a/include/llvm/Transforms/Utils/BuildLibCalls.h b/include/llvm/Transforms/Utils/BuildLibCalls.h index 53ff2823932..17cd58eb014 100644 --- a/include/llvm/Transforms/Utils/BuildLibCalls.h +++ b/include/llvm/Transforms/Utils/BuildLibCalls.h @@ -20,6 +20,7 @@ namespace llvm { class Value; class TargetData; + class TargetLibraryInfo; /// CastToCStr - Return V if it is an i8*, otherwise cast it to i8*. Value *CastToCStr(Value *V, IRBuilder<> &B); @@ -86,12 +87,13 @@ namespace llvm { /// EmitFPutS - Emit a call to the puts function. Str is required to be a /// pointer and File is a pointer to FILE. - void EmitFPutS(Value *Str, Value *File, IRBuilder<> &B, const TargetData *TD); + void EmitFPutS(Value *Str, Value *File, IRBuilder<> &B, const TargetData *TD, + const TargetLibraryInfo *TLI); /// EmitFWrite - 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. void EmitFWrite(Value *Ptr, Value *Size, Value *File, IRBuilder<> &B, - const TargetData *TD); + const TargetData *TD, const TargetLibraryInfo *TLI); /// SimplifyFortifiedLibCalls - Helper class for folding checked library /// calls (e.g. __strcpy_chk) into their unchecked counterparts. diff --git a/lib/Target/TargetLibraryInfo.cpp b/lib/Target/TargetLibraryInfo.cpp index 709dfd283f9..aa2e014b2bc 100644 --- a/lib/Target/TargetLibraryInfo.cpp +++ b/lib/Target/TargetLibraryInfo.cpp @@ -20,6 +20,19 @@ INITIALIZE_PASS(TargetLibraryInfo, "targetlibinfo", "Target Library Information", false, true) char TargetLibraryInfo::ID = 0; +const char* TargetLibraryInfo::StandardNames[LibFunc::NumLibFuncs] = + { + "memset", + "memcpy", + "memmove", + "memset_pattern16", + "iprintf", + "siprintf", + "fiprintf", + "fwrite", + "fputs" + }; + /// initialize - Initialize the set of available library functions based on the /// specified target triple. This should be carefully written so that a missing /// target triple gets a sane set of defaults. @@ -38,6 +51,17 @@ static void initialize(TargetLibraryInfo &TLI, const Triple &T) { TLI.setUnavailable(LibFunc::memset_pattern16); } + if (T.isMacOSX() && T.getArch() == Triple::x86 && + !T.isMacOSXVersionLT(10, 7)) { + // x86-32 OSX has a scheme where fwrite and fputs (and some other functions + // we don't care about) have two versions; on recent OSX, the one we want + // has a $UNIX2003 suffix. The two implementations are identical except + // for the return value in some edge cases. However, we don't want to + // generate code that depends on the old symbols. + TLI.setAvailableWithName(LibFunc::fwrite, "fwrite$UNIX2003"); + TLI.setAvailableWithName(LibFunc::fputs, "fputs$UNIX2003"); + } + // iprintf and friends are only available on XCore and TCE. if (T.getArch() != Triple::xcore && T.getArch() != Triple::tce) { TLI.setUnavailable(LibFunc::iprintf); @@ -64,6 +88,7 @@ TargetLibraryInfo::TargetLibraryInfo(const Triple &T) : ImmutablePass(ID) { TargetLibraryInfo::TargetLibraryInfo(const TargetLibraryInfo &TLI) : ImmutablePass(ID) { memcpy(AvailableArray, TLI.AvailableArray, sizeof(AvailableArray)); + CustomNames = TLI.CustomNames; } diff --git a/lib/Transforms/Scalar/SimplifyLibCalls.cpp b/lib/Transforms/Scalar/SimplifyLibCalls.cpp index e90df011c53..6e169de8291 100644 --- a/lib/Transforms/Scalar/SimplifyLibCalls.cpp +++ b/lib/Transforms/Scalar/SimplifyLibCalls.cpp @@ -1323,7 +1323,7 @@ struct FPutsOpt : public LibCallOptimization { if (!Len) return 0; EmitFWrite(CI->getArgOperand(0), ConstantInt::get(TD->getIntPtrType(*Context), Len-1), - CI->getArgOperand(1), B, TD); + CI->getArgOperand(1), B, TD, TLI); return CI; // Known to have no uses (see above). } }; @@ -1351,7 +1351,7 @@ struct FPrintFOpt : public LibCallOptimization { EmitFWrite(CI->getArgOperand(1), ConstantInt::get(TD->getIntPtrType(*Context), FormatStr.size()), - CI->getArgOperand(0), B, TD); + CI->getArgOperand(0), B, TD, TLI); return ConstantInt::get(CI->getType(), FormatStr.size()); } @@ -1373,7 +1373,7 @@ struct FPrintFOpt : public LibCallOptimization { // fprintf(F, "%s", str) --> fputs(str, F) if (!CI->getArgOperand(2)->getType()->isPointerTy() || !CI->use_empty()) return 0; - EmitFPutS(CI->getArgOperand(2), CI->getArgOperand(0), B, TD); + EmitFPutS(CI->getArgOperand(2), CI->getArgOperand(0), B, TD, TLI); return CI; } return 0; @@ -1469,6 +1469,7 @@ namespace { SimplifyLibCalls() : FunctionPass(ID), StrCpy(false), StrCpyChk(true) { initializeSimplifyLibCallsPass(*PassRegistry::getPassRegistry()); } + void AddOpt(LibFunc::Func F, LibCallOptimization* Opt); void InitOptimizations(); bool runOnFunction(Function &F); @@ -1499,6 +1500,11 @@ FunctionPass *llvm::createSimplifyLibCallsPass() { return new SimplifyLibCalls(); } +void SimplifyLibCalls::AddOpt(LibFunc::Func F, LibCallOptimization* Opt) { + if (TLI->has(F)) + Optimizations[TLI->getName(F)] = Opt; +} + /// Optimizations - Populate the Optimizations map with all the optimizations /// we know. void SimplifyLibCalls::InitOptimizations() { @@ -1524,9 +1530,9 @@ void SimplifyLibCalls::InitOptimizations() { Optimizations["strcspn"] = &StrCSpn; Optimizations["strstr"] = &StrStr; Optimizations["memcmp"] = &MemCmp; - if (TLI->has(LibFunc::memcpy)) Optimizations["memcpy"] = &MemCpy; + AddOpt(LibFunc::memcpy, &MemCpy); Optimizations["memmove"] = &MemMove; - if (TLI->has(LibFunc::memset)) Optimizations["memset"] = &MemSet; + AddOpt(LibFunc::memset, &MemSet); // _chk variants of String and Memory LibCall Optimizations. Optimizations["__strcpy_chk"] = &StrCpyChk; @@ -1579,8 +1585,8 @@ void SimplifyLibCalls::InitOptimizations() { // Formatting and IO Optimizations Optimizations["sprintf"] = &SPrintF; Optimizations["printf"] = &PrintF; - Optimizations["fwrite"] = &FWrite; - Optimizations["fputs"] = &FPuts; + AddOpt(LibFunc::fwrite, &FWrite); + AddOpt(LibFunc::fputs, &FPuts); Optimizations["fprintf"] = &FPrintF; Optimizations["puts"] = &Puts; } diff --git a/lib/Transforms/Utils/BuildLibCalls.cpp b/lib/Transforms/Utils/BuildLibCalls.cpp index fc741106dd6..a80830328d5 100644 --- a/lib/Transforms/Utils/BuildLibCalls.cpp +++ b/lib/Transforms/Utils/BuildLibCalls.cpp @@ -20,6 +20,9 @@ #include "llvm/Module.h" #include "llvm/Support/IRBuilder.h" #include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetLibraryInfo.h" +#include "llvm/LLVMContext.h" +#include "llvm/Intrinsics.h" #include "llvm/ADT/SmallString.h" using namespace llvm; @@ -297,20 +300,21 @@ void llvm::EmitFPutC(Value *Char, Value *File, IRBuilder<> &B, /// EmitFPutS - Emit a call to the puts function. Str is required to be a /// pointer and File is a pointer to FILE. void llvm::EmitFPutS(Value *Str, Value *File, IRBuilder<> &B, - const TargetData *TD) { + const TargetData *TD, const TargetLibraryInfo *TLI) { Module *M = B.GetInsertBlock()->getParent()->getParent(); AttributeWithIndex AWI[3]; AWI[0] = AttributeWithIndex::get(1, Attribute::NoCapture); AWI[1] = AttributeWithIndex::get(2, Attribute::NoCapture); AWI[2] = AttributeWithIndex::get(~0u, Attribute::NoUnwind); + StringRef FPutsName = TLI->getName(LibFunc::fputs); Constant *F; if (File->getType()->isPointerTy()) - F = M->getOrInsertFunction("fputs", AttrListPtr::get(AWI, 3), + F = M->getOrInsertFunction(FPutsName, AttrListPtr::get(AWI, 3), B.getInt32Ty(), B.getInt8PtrTy(), File->getType(), NULL); else - F = M->getOrInsertFunction("fputs", B.getInt32Ty(), + F = M->getOrInsertFunction(FPutsName, B.getInt32Ty(), B.getInt8PtrTy(), File->getType(), NULL); CallInst *CI = B.CreateCall2(F, CastToCStr(Str, B), File, "fputs"); @@ -322,23 +326,25 @@ void llvm::EmitFPutS(Value *Str, Value *File, IRBuilder<> &B, /// EmitFWrite - 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. void llvm::EmitFWrite(Value *Ptr, Value *Size, Value *File, - IRBuilder<> &B, const TargetData *TD) { + IRBuilder<> &B, const TargetData *TD, + const TargetLibraryInfo *TLI) { Module *M = B.GetInsertBlock()->getParent()->getParent(); AttributeWithIndex AWI[3]; AWI[0] = AttributeWithIndex::get(1, Attribute::NoCapture); AWI[1] = AttributeWithIndex::get(4, Attribute::NoCapture); AWI[2] = AttributeWithIndex::get(~0u, Attribute::NoUnwind); LLVMContext &Context = B.GetInsertBlock()->getContext(); + StringRef FWriteName = TLI->getName(LibFunc::fwrite); Constant *F; if (File->getType()->isPointerTy()) - F = M->getOrInsertFunction("fwrite", AttrListPtr::get(AWI, 3), + F = M->getOrInsertFunction(FWriteName, AttrListPtr::get(AWI, 3), TD->getIntPtrType(Context), B.getInt8PtrTy(), TD->getIntPtrType(Context), TD->getIntPtrType(Context), File->getType(), NULL); else - F = M->getOrInsertFunction("fwrite", TD->getIntPtrType(Context), + F = M->getOrInsertFunction(FWriteName, TD->getIntPtrType(Context), B.getInt8PtrTy(), TD->getIntPtrType(Context), TD->getIntPtrType(Context), diff --git a/test/Transforms/SimplifyLibCalls/osx-names.ll b/test/Transforms/SimplifyLibCalls/osx-names.ll new file mode 100644 index 00000000000..e321d1dd317 --- /dev/null +++ b/test/Transforms/SimplifyLibCalls/osx-names.ll @@ -0,0 +1,30 @@ +; RUN: opt < %s -simplify-libcalls -S | FileCheck %s +; +; On OSX x86-32, fwrite and fputs aren't called fwrite and fputs. +; Make sure we use the correct names. + +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128-n8:16:32-S128" +target triple = "i386-apple-macosx10.7.2" + +%struct.__sFILE = type { i8*, i32, i32, i16, i16, %struct.__sbuf, i32, i8*, i32 (i8*)*, i32 (i8*, i8*, i32)*, i64 (i8*, i64, i32)*, i32 (i8*, i8*, i32)*, %struct.__sbuf, %struct.__sFILEX*, i32, [3 x i8], [1 x i8], %struct.__sbuf, i32, i64 } +%struct.__sbuf = type { i8*, i32 } +%struct.__sFILEX = type opaque + +@.str = private unnamed_addr constant [13 x i8] c"Hello world\0A\00", align 1 +@.str2 = private unnamed_addr constant [3 x i8] c"%s\00", align 1 + +define void @test1(%struct.__sFILE* %stream) nounwind { +; CHECK: define void @test1 +; CHECK: call i32 @"fwrite$UNIX2003" + %call = tail call i32 (%struct.__sFILE*, i8*, ...)* @fprintf(%struct.__sFILE* %stream, i8* getelementptr inbounds ([13 x i8]* @.str, i32 0, i32 0)) nounwind + ret void +} + +define void @test2(%struct.__sFILE* %stream, i8* %str) nounwind ssp { +; CHECK: define void @test2 +; CHECK: call i32 @"fputs$UNIX2003" + %call = tail call i32 (%struct.__sFILE*, i8*, ...)* @fprintf(%struct.__sFILE* %stream, i8* getelementptr inbounds ([3 x i8]* @.str2, i32 0, i32 0), i8* %str) nounwind + ret void +} + +declare i32 @fprintf(%struct.__sFILE*, i8*, ...) nounwind -- 2.34.1