From 22a7dfea585703d6755db69b83e29a0e6ee10369 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne <peter@pcc.me.uk> Date: Sun, 30 Oct 2011 17:30:44 +0000 Subject: [PATCH] Add support for lazily linking bitcode files (using a new -mlink-bitcode-file flag), and more generally llvm::Modules, before running optimisations. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@143314 91177308-0d34-0410-b5e6-96231b3b80d8 --- examples/clang-interpreter/CMakeLists.txt | 1 + examples/clang-interpreter/Makefile | 2 +- .../clang/Basic/DiagnosticFrontendKinds.td | 2 + include/clang/CodeGen/CodeGenAction.h | 6 ++ include/clang/Driver/CC1Options.td | 2 + include/clang/Frontend/CodeGenOptions.h | 3 + lib/CodeGen/CodeGenAction.cpp | 59 +++++++++++++++++-- lib/Frontend/CompilerInvocation.cpp | 1 + test/CodeGen/link-bitcode-file.c | 24 ++++++++ tools/driver/CMakeLists.txt | 3 +- tools/driver/Makefile | 2 +- 11 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 test/CodeGen/link-bitcode-file.c diff --git a/examples/clang-interpreter/CMakeLists.txt b/examples/clang-interpreter/CMakeLists.txt index a747b92a76c..4d5d4bf9445 100644 --- a/examples/clang-interpreter/CMakeLists.txt +++ b/examples/clang-interpreter/CMakeLists.txt @@ -25,6 +25,7 @@ set(LLVM_LINK_COMPONENTS bitwriter codegen ipo + linker selectiondag ) diff --git a/examples/clang-interpreter/Makefile b/examples/clang-interpreter/Makefile index b565bb172b0..696b99ffdbf 100644 --- a/examples/clang-interpreter/Makefile +++ b/examples/clang-interpreter/Makefile @@ -16,7 +16,7 @@ NO_INSTALL = 1 TOOL_NO_EXPORTS = 1 LINK_COMPONENTS := jit interpreter nativecodegen bitreader bitwriter ipo \ - selectiondag asmparser instrumentation + linker selectiondag asmparser instrumentation USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a clangCodeGen.a \ clangParse.a clangSema.a clangStaticAnalyzerFrontend.a \ clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 82f91961e71..c9f5a5c4863 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -19,6 +19,8 @@ def err_fe_invalid_ast_action : Error<"invalid action for AST input">, // Error generated by the backend. def err_fe_inline_asm : Error<"%0">, CatInlineAsm; def note_fe_inline_asm_here : Note<"instantiated into assembly here">; +def err_fe_cannot_link_module : Error<"cannot link module '%0': %1">, + DefaultFatal; diff --git a/include/clang/CodeGen/CodeGenAction.h b/include/clang/CodeGen/CodeGenAction.h index f1a2f6eb455..9697bc62afd 100644 --- a/include/clang/CodeGen/CodeGenAction.h +++ b/include/clang/CodeGen/CodeGenAction.h @@ -25,6 +25,7 @@ class CodeGenAction : public ASTFrontendAction { private: unsigned Act; llvm::OwningPtr<llvm::Module> TheModule; + llvm::Module *LinkModule; llvm::LLVMContext *VMContext; bool OwnsVMContext; @@ -46,6 +47,11 @@ protected: public: ~CodeGenAction(); + /// setLinkModule - Set the link module to be used by this action. If a link + /// module is not provided, and CodeGenOptions::LinkBitcodeFile is non-empty, + /// the action will load it from the specified file. + void setLinkModule(llvm::Module *Mod) { LinkModule = Mod; } + /// takeModule - Take the generated LLVM module, for use after the action has /// been run. The result may be null on failure. llvm::Module *takeModule(); diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index c5d232abf66..1a6008e7797 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -196,6 +196,8 @@ def mms_bitfields : Flag<"-mms-bitfields">, HelpText<"Set the default structure layout to be compatible with the Microsoft compiler standard.">; def mstackrealign : Flag<"-mstackrealign">, HelpText<"Force realign the stack at entry to every function.">; +def mlink_bitcode_file : Separate<"-mlink-bitcode-file">, + HelpText<"Link the given bitcode file before performing optimizations.">; def O : Joined<"-O">, HelpText<"Optimization level">; def Os : Flag<"-Os">, HelpText<"Optimize for size">; def Oz : Flag<"-Oz">, HelpText<"Optimize for size, regardless of performance">; diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index 5b698889f6e..9962ea8cf92 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -128,6 +128,9 @@ public: /// The float precision limit to use, if non-empty. std::string LimitFloatPrecision; + /// The name of the bitcode file to link before optzns. + std::string LinkBitcodeFile; + /// The kind of inlining to perform. InliningMethod Inlining; diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index 11d6075582f..db2bab9bf61 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "clang/CodeGen/CodeGenAction.h" +#include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/AST/ASTConsumer.h" @@ -18,9 +19,11 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/LLVMContext.h" +#include "llvm/Linker.h" #include "llvm/Module.h" #include "llvm/Pass.h" #include "llvm/ADT/OwningPtr.h" +#include "llvm/Bitcode/ReaderWriter.h" #include "llvm/Support/IRReader.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" @@ -42,7 +45,7 @@ namespace clang { llvm::OwningPtr<CodeGenerator> Gen; - llvm::OwningPtr<llvm::Module> TheModule; + llvm::OwningPtr<llvm::Module> TheModule, LinkModule; public: BackendConsumer(BackendAction action, DiagnosticsEngine &_Diags, @@ -50,7 +53,9 @@ namespace clang { const TargetOptions &targetopts, const LangOptions &langopts, bool TimePasses, - const std::string &infile, raw_ostream *OS, + const std::string &infile, + llvm::Module *LinkModule, + raw_ostream *OS, LLVMContext &C) : Diags(_Diags), Action(action), @@ -59,11 +64,13 @@ namespace clang { LangOpts(langopts), AsmOutStream(OS), LLVMIRGeneration("LLVM IR Generation Time"), - Gen(CreateLLVMCodeGen(Diags, infile, compopts, C)) { + Gen(CreateLLVMCodeGen(Diags, infile, compopts, C)), + LinkModule(LinkModule) { llvm::TimePassesIsEnabled = TimePasses; } llvm::Module *takeModule() { return TheModule.take(); } + llvm::Module *takeLinkModule() { return LinkModule.take(); } virtual void Initialize(ASTContext &Ctx) { Context = &Ctx; @@ -122,6 +129,17 @@ namespace clang { assert(TheModule.get() == M && "Unexpected module change during IR generation"); + // Link LinkModule into this module if present, preserving its validity. + if (LinkModule) { + std::string ErrorMsg; + if (Linker::LinkModules(M, LinkModule.get(), Linker::PreserveSource, + &ErrorMsg)) { + Diags.Report(diag::err_fe_cannot_link_module) + << LinkModule->getModuleIdentifier() << ErrorMsg; + return; + } + } + // Install an inline asm handler so that diagnostics get printed through // our diagnostics hooks. LLVMContext &Ctx = TheModule->getContext(); @@ -238,7 +256,8 @@ void BackendConsumer::InlineAsmDiagHandler2(const llvm::SMDiagnostic &D, // CodeGenAction::CodeGenAction(unsigned _Act, LLVMContext *_VMContext) - : Act(_Act), VMContext(_VMContext ? _VMContext : new LLVMContext), + : Act(_Act), LinkModule(0), + VMContext(_VMContext ? _VMContext : new LLVMContext), OwnsVMContext(!_VMContext) {} CodeGenAction::~CodeGenAction() { @@ -254,6 +273,10 @@ void CodeGenAction::EndSourceFileAction() { if (!getCompilerInstance().hasASTConsumer()) return; + // If we were given a link module, release consumer's ownership of it. + if (LinkModule) + BEConsumer->takeLinkModule(); + // Steal the module from the consumer. TheModule.reset(BEConsumer->takeModule()); } @@ -294,12 +317,36 @@ ASTConsumer *CodeGenAction::CreateASTConsumer(CompilerInstance &CI, if (BA != Backend_EmitNothing && !OS) return 0; + llvm::Module *LinkModuleToUse = LinkModule; + + // If we were not given a link module, and the user requested that one be + // loaded from bitcode, do so now. + const std::string &LinkBCFile = CI.getCodeGenOpts().LinkBitcodeFile; + if (!LinkModuleToUse && !LinkBCFile.empty()) { + std::string ErrorStr; + + llvm::MemoryBuffer *BCBuf = + CI.getFileManager().getBufferForFile(LinkBCFile, &ErrorStr); + if (!BCBuf) { + CI.getDiagnostics().Report(diag::err_cannot_open_file) + << LinkBCFile << ErrorStr; + return 0; + } + + LinkModuleToUse = getLazyBitcodeModule(BCBuf, *VMContext, &ErrorStr); + if (!LinkModuleToUse) { + CI.getDiagnostics().Report(diag::err_cannot_open_file) + << LinkBCFile << ErrorStr; + return 0; + } + } + BEConsumer = new BackendConsumer(BA, CI.getDiagnostics(), CI.getCodeGenOpts(), CI.getTargetOpts(), CI.getLangOpts(), - CI.getFrontendOpts().ShowTimers, InFile, OS.take(), - *VMContext); + CI.getFrontendOpts().ShowTimers, InFile, + LinkModuleToUse, OS.take(), *VMContext); return BEConsumer; } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index a4851f1740a..6950647cab4 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1078,6 +1078,7 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.EmitGcovNotes = Args.hasArg(OPT_femit_coverage_notes); Opts.CoverageFile = Args.getLastArgValue(OPT_coverage_file); Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); + Opts.LinkBitcodeFile = Args.getLastArgValue(OPT_mlink_bitcode_file); if (Arg *A = Args.getLastArg(OPT_fobjc_dispatch_method_EQ)) { StringRef Name = A->getValue(Args); diff --git a/test/CodeGen/link-bitcode-file.c b/test/CodeGen/link-bitcode-file.c new file mode 100644 index 00000000000..77404062ecd --- /dev/null +++ b/test/CodeGen/link-bitcode-file.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple i386-pc-linux-gnu -DBITCODE -emit-llvm-bc -o %t.bc %s +// RUN: %clang_cc1 -triple i386-pc-linux-gnu -mlink-bitcode-file %t.bc -O3 -emit-llvm -o - %s | FileCheck -check-prefix=CHECK-NO-BC %s +// RUN: %clang_cc1 -triple i386-pc-linux-gnu -DBITCODE -mlink-bitcode-file %t.bc -O3 -emit-llvm -o - %s 2>&1 | FileCheck -check-prefix=CHECK-BC %s + +int f(void); + +#ifdef BITCODE + +// CHECK-BC: 'f': symbol multiply defined +int f(void) { + return 42; +} + +#else + +// CHECK-NO-BC: define i32 @g +// CHECK-NO-BC: ret i32 42 +int g(void) { + return f(); +} + +// CHECK-NO-BC: define i32 @f + +#endif diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index e6d0f1ac376..0df92105274 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -1,11 +1,11 @@ set( LLVM_USED_LIBS + clangFrontendTool clangAST clangAnalysis clangBasic clangCodeGen clangDriver clangFrontend - clangFrontendTool clangIndex clangLex clangParse @@ -26,6 +26,7 @@ set( LLVM_LINK_COMPONENTS codegen instrumentation ipo + linker selectiondag ) diff --git a/tools/driver/Makefile b/tools/driver/Makefile index 6b34a991bf1..c7d91a728d2 100644 --- a/tools/driver/Makefile +++ b/tools/driver/Makefile @@ -30,7 +30,7 @@ TOOL_INFO_PLIST := Info.plist include $(CLANG_LEVEL)/../../Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter codegen \ - instrumentation ipo selectiondag + instrumentation ipo linker selectiondag USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ -- GitLab