Index: clang/lib/Driver/ToolChains/Darwin.cpp =================================================================== --- clang/lib/Driver/ToolChains/Darwin.cpp +++ clang/lib/Driver/ToolChains/Darwin.cpp @@ -469,6 +469,13 @@ } } + // Instruct the linker to honor nobuiltin/freestanding during LTO. + if (Args.hasArg(options::OPT_fno_builtin) || + Args.hasArg(options::OPT_ffreestanding)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-lto-nobuiltin"); + } + // It seems that the 'e' option is completely ignored for dynamic executables // (the default), and with static executables, the last one wins, as expected. Args.AddAllArgs(CmdArgs, {options::OPT_d_Flag, options::OPT_s, options::OPT_t, Index: llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h =================================================================== --- llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h +++ llvm/include/llvm/LTO/legacy/LTOCodeGenerator.h @@ -143,7 +143,7 @@ /// don't who (LTOCodeGenerator or the output file) will last longer. bool compile_to_file(const char **Name, bool DisableVerify, bool DisableInline, bool DisableGVNLoadPRE, - bool DisableVectorization); + bool DisableVectorization, bool DisableLTOBuiltins); /// As with compile_to_file(), this function compiles the merged module into /// single output file. Instead of returning the output file path to the @@ -153,13 +153,14 @@ /// successful. std::unique_ptr compile(bool DisableVerify, bool DisableInline, bool DisableGVNLoadPRE, - bool DisableVectorization); + bool DisableVectorization, + bool DisableLTOBuiltins); /// Optimizes the merged module. Returns true on success. /// /// Calls \a verifyMergedModuleOnce(). bool optimize(bool DisableVerify, bool DisableInline, bool DisableGVNLoadPRE, - bool DisableVectorization); + bool DisableVectorization, bool DisableLTOBuiltins); /// Compiles the merged optimized module into a single output file. It brings /// the output to a buffer, and returns the buffer to the caller. Return NULL Index: llvm/include/llvm/LTO/legacy/ThinLTOCodeGenerator.h =================================================================== --- llvm/include/llvm/LTO/legacy/ThinLTOCodeGenerator.h +++ llvm/include/llvm/LTO/legacy/ThinLTOCodeGenerator.h @@ -206,6 +206,8 @@ TMBuilder.Options = std::move(Options); } + void setNoBuiltin(bool NoBuiltin) { DisableLTOBuiltins = NoBuiltin; } + /// CodeModel void setCodePICModel(Optional Model) { TMBuilder.RelocModel = Model; @@ -323,6 +325,10 @@ /// importing or optimization. bool CodeGenOnly = false; + /// Flag to indicate that the optimizer should not assume builtins are present + /// on the target. + bool DisableLTOBuiltins = false; + /// IR Optimization Level [0-3]. unsigned OptLevel = 3; }; Index: llvm/lib/LTO/LTOCodeGenerator.cpp =================================================================== --- llvm/lib/LTO/LTOCodeGenerator.cpp +++ llvm/lib/LTO/LTOCodeGenerator.cpp @@ -310,9 +310,10 @@ bool LTOCodeGenerator::compile_to_file(const char **Name, bool DisableVerify, bool DisableInline, bool DisableGVNLoadPRE, - bool DisableVectorization) { + bool DisableVectorization, + bool DisableLTOBuiltins) { if (!optimize(DisableVerify, DisableInline, DisableGVNLoadPRE, - DisableVectorization)) + DisableVectorization, DisableLTOBuiltins)) return false; return compileOptimizedToFile(Name); @@ -320,9 +321,10 @@ std::unique_ptr LTOCodeGenerator::compile(bool DisableVerify, bool DisableInline, - bool DisableGVNLoadPRE, bool DisableVectorization) { + bool DisableGVNLoadPRE, bool DisableVectorization, + bool DisableLTOBuiltins) { if (!optimize(DisableVerify, DisableInline, DisableGVNLoadPRE, - DisableVectorization)) + DisableVectorization, DisableLTOBuiltins)) return nullptr; return compileOptimized(); @@ -518,7 +520,8 @@ /// Optimize merged modules using various IPO passes bool LTOCodeGenerator::optimize(bool DisableVerify, bool DisableInline, bool DisableGVNLoadPRE, - bool DisableVectorization) { + bool DisableVectorization, + bool DisableLTOBuiltins) { if (!this->determineTarget()) return false; @@ -554,6 +557,8 @@ if (!DisableInline) PMB.Inliner = createFunctionInliningPass(); PMB.LibraryInfo = new TargetLibraryInfoImpl(TargetTriple); + if (DisableLTOBuiltins) + PMB.LibraryInfo->disableAllFunctions(); PMB.OptLevel = OptLevel; PMB.VerifyInput = !DisableVerify; PMB.VerifyOutput = !DisableVerify; Index: llvm/lib/LTO/ThinLTOCodeGenerator.cpp =================================================================== --- llvm/lib/LTO/ThinLTOCodeGenerator.cpp +++ llvm/lib/LTO/ThinLTOCodeGenerator.cpp @@ -187,10 +187,12 @@ } static void optimizeModule(Module &TheModule, TargetMachine &TM, - unsigned OptLevel) { + unsigned OptLevel, bool DisableLTOBuiltins) { // Populate the PassManager PassManagerBuilder PMB; PMB.LibraryInfo = new TargetLibraryInfoImpl(TM.getTargetTriple()); + if (DisableLTOBuiltins) + PMB.LibraryInfo->disableAllFunctions(); PMB.Inliner = createFunctionInliningPass(); // FIXME: should get it from the bitcode? PMB.OptLevel = OptLevel; @@ -401,7 +403,8 @@ const GVSummaryMapTy &DefinedGlobals, const ThinLTOCodeGenerator::CachingOptions &CacheOptions, bool DisableCodeGen, StringRef SaveTempsDir, - unsigned OptLevel, unsigned count) { + bool DisableLTOBuiltins, unsigned OptLevel, + unsigned count) { // "Benchmark"-like optimization: single-source case bool SingleModule = (ModuleMap.size() == 1); @@ -433,7 +436,7 @@ saveTempBitcode(TheModule, SaveTempsDir, count, ".3.imported.bc"); } - optimizeModule(TheModule, TM, OptLevel); + optimizeModule(TheModule, TM, OptLevel, DisableLTOBuiltins); saveTempBitcode(TheModule, SaveTempsDir, count, ".4.opt.bc"); @@ -759,7 +762,7 @@ initTMBuilder(TMBuilder, Triple(TheModule.getTargetTriple())); // Optimize now - optimizeModule(TheModule, *TMBuilder.create(), OptLevel); + optimizeModule(TheModule, *TMBuilder.create(), OptLevel, DisableLTOBuiltins); } /** @@ -991,7 +994,7 @@ *TheModule, *Index, ModuleMap, *TMBuilder.create(), ImportList, ExportList, GUIDPreservedSymbols, ModuleToDefinedGVSummaries[ModuleIdentifier], CacheOptions, - DisableCodeGen, SaveTempsDir, OptLevel, count); + DisableCodeGen, SaveTempsDir, DisableLTOBuiltins, OptLevel, count); // Commit to the cache (if enabled) CacheEntry.write(*OutputBuffer); Index: llvm/test/ThinLTO/X86/tli-nobuiltin.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/tli-nobuiltin.ll @@ -0,0 +1,46 @@ +; Test -lto-nobuiltin option for libLTO. +; RUN: llvm-as < %s > %t.bc + +; Regular run: expects fprintf to be turned into fwrite +; RUN: llvm-lto %t.bc -exported-symbol=_foo -o %t.o +; RUN: llvm-nm %t.o | FileCheck %s --check-prefix=LTO +; LTO: fwrite + +; Nobuiltin run: expects fprintf to NOT be turned into fwrite +; RUN: llvm-lto %t.bc -lto-nobuiltin -exported-symbol=_foo -o %t.o +; RUN: llvm-nm %t.o | FileCheck %s --check-prefix=LTO-NOBUILTIN +; LTO-NOBUILTIN: fprintf + +; Same with ThinLTO now. +; RUN: opt -module-hash -module-summary %s -o %t.bc + +; Regular run: expects fprintf to be turned into fwrite +; RUN: llvm-lto -exported-symbol=_foo -thinlto-action=run %t.bc +; RUN: llvm-nm %t.bc.thinlto.o | FileCheck %s --check-prefix=ThinLTO +; ThinLTO: fwrite + +; Nobuiltin run: expects fprintf to NOT be turned into fwrite +; RUN: llvm-lto -lto-nobuiltin -exported-symbol=_foo -thinlto-action=run %t.bc +; RUN: llvm-nm %t.bc.thinlto.o | FileCheck %s --check-prefix=ThinLTO-NOBUILTIN +; ThinLTO-NOBUILTIN: fprintf + + +target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-apple-macosx10.11.0" + +declare i32 @fprintf(%FILE*, i8*, ...) + +%FILE = type { } + +@hello_world = constant [13 x i8] c"hello world\0A\00" +@percent_s = constant [3 x i8] c"%s\00" + +; Check fprintf(fp, "%s", str) -> fputs(str, fp) only when builtins are enabled + +define void @foo(%FILE* %fp) { + %fmt = getelementptr [3 x i8], [3 x i8]* @percent_s, i32 0, i32 0 + %str = getelementptr [13 x i8], [13 x i8]* @hello_world, i32 0, i32 0 + call i32 (%FILE*, i8*, ...) @fprintf(%FILE* %fp, i8* %fmt, i8* %str) + ret void +} + Index: llvm/tools/llvm-lto/llvm-lto.cpp =================================================================== --- llvm/tools/llvm-lto/llvm-lto.cpp +++ llvm/tools/llvm-lto/llvm-lto.cpp @@ -63,6 +63,10 @@ "disable-lto-vectorization", cl::init(false), cl::desc("Do not run loop or slp vectorization during LTO")); +static cl::opt + DisableLTOBuiltins("lto-nobuiltin", cl::init(false), + cl::desc("Disable builtins (TLI) during LTO")); + static cl::opt UseDiagnosticHandler( "use-diagnostic-handler", cl::init(false), cl::desc("Use a diagnostic handler to test the handler interface")); @@ -433,6 +437,7 @@ ThinGenerator.setCodePICModel(getRelocModel()); ThinGenerator.setTargetOptions(Options); ThinGenerator.setCacheDir(ThinLTOCacheDir); + ThinGenerator.setNoBuiltin(DisableLTOBuiltins); // Add all the exported symbols to the table of symbols to preserve. for (unsigned i = 0; i < ExportedSymbols.size(); ++i) @@ -877,7 +882,7 @@ if (!OutputFilename.empty()) { if (!CodeGen.optimize(DisableVerify, DisableInline, DisableGVNLoadPRE, - DisableLTOVectorization)) { + DisableLTOVectorization, DisableLTOBuiltins)) { // Diagnostic messages should have been printed by the handler. error("error optimizing the code"); } @@ -919,7 +924,8 @@ const char *OutputName = nullptr; if (!CodeGen.compile_to_file(&OutputName, DisableVerify, DisableInline, - DisableGVNLoadPRE, DisableLTOVectorization)) + DisableGVNLoadPRE, DisableLTOVectorization, + DisableLTOBuiltins)) error("error compiling the code"); // Diagnostic messages should have been printed by the handler. Index: llvm/tools/lto/lto.cpp =================================================================== --- llvm/tools/lto/lto.cpp +++ llvm/tools/lto/lto.cpp @@ -44,9 +44,13 @@ DisableGVNLoadPRE("disable-gvn-loadpre", cl::init(false), cl::desc("Do not run the GVN load PRE pass")); +static cl::opt DisableLTOVectorization( + "disable-lto-vectorization", cl::init(false), + cl::desc("Do not run loop or slp vectorization during LTO")); + static cl::opt -DisableLTOVectorization("disable-lto-vectorization", cl::init(false), - cl::desc("Do not run loop or slp vectorization during LTO")); + DisableLTOBuiltins("lto-nobuiltin", cl::init(false), + cl::desc("Disable builtins (TLI) during LTO")); #ifdef NDEBUG static bool VerifyByDefault = false; @@ -412,7 +416,7 @@ LibLTOCodeGenerator *CG = unwrap(cg); CG->NativeObjectFile = CG->compile(DisableVerify, DisableInline, DisableGVNLoadPRE, - DisableLTOVectorization); + DisableLTOVectorization, DisableLTOBuiltins); if (!CG->NativeObjectFile) return nullptr; *length = CG->NativeObjectFile->getBufferSize(); @@ -422,7 +426,7 @@ bool lto_codegen_optimize(lto_code_gen_t cg) { maybeParseOptions(cg); return !unwrap(cg)->optimize(DisableVerify, DisableInline, DisableGVNLoadPRE, - DisableLTOVectorization); + DisableLTOVectorization, DisableLTOBuiltins); } const void *lto_codegen_compile_optimized(lto_code_gen_t cg, size_t *length) { @@ -439,7 +443,7 @@ maybeParseOptions(cg); return !unwrap(cg)->compile_to_file( name, DisableVerify, DisableInline, DisableGVNLoadPRE, - DisableLTOVectorization); + DisableLTOVectorization, DisableLTOBuiltins); } void lto_codegen_debug_options(lto_code_gen_t cg, const char *opt) { @@ -464,6 +468,7 @@ lto_initialize(); ThinLTOCodeGenerator *CodeGen = new ThinLTOCodeGenerator(); CodeGen->setTargetOptions(InitTargetOptionsFromCodeGenFlags()); + CodeGen->setNoBuiltin(DisableLTOBuiltins); if (OptLevel.getNumOccurrences()) { if (OptLevel < '0' || OptLevel > '3')