From 47bc01786d4df534a3e8a1cc16dbf4c9606c50cc Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Mon, 21 May 2018 20:31:59 +0000 Subject: [PATCH] CodeGen, Driver: Start using direct split dwarf emission in clang. Fixes PR37466. Differential Revision: https://reviews.llvm.org/D47093 llvm-svn: 332885 --- clang/include/clang/Driver/CC1Options.td | 4 +- clang/lib/CodeGen/BackendUtil.cpp | 74 +++++++++++++++-------- clang/lib/Driver/ToolChains/Clang.cpp | 20 ++---- clang/test/CodeGen/split-debug-filename.c | 6 ++ clang/test/Driver/split-debug.c | 3 +- clang/test/Driver/split-debug.s | 3 +- clang/test/Misc/cc1as-split-dwarf.s | 25 ++++++++ clang/tools/driver/cc1as_main.cpp | 37 +++++++----- 8 files changed, 112 insertions(+), 60 deletions(-) create mode 100644 clang/test/Misc/cc1as-split-dwarf.s diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 03b43ddd48ff..78446304575d 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -619,6 +619,8 @@ def version : Flag<["-"], "version">, HelpText<"Print the compiler version">; def main_file_name : Separate<["-"], "main-file-name">, HelpText<"Main file name to use for debug info">; +def split_dwarf_file : Separate<["-"], "split-dwarf-file">, + HelpText<"File name to use for split dwarf debug info output">; } @@ -628,8 +630,6 @@ def fexternc_nounwind : Flag<["-"], "fexternc-nounwind">, HelpText<"Assume all functions with C linkage do not unwind">; def enable_split_dwarf : Flag<["-"], "enable-split-dwarf">, HelpText<"Use split dwarf/Fission">; -def split_dwarf_file : Separate<["-"], "split-dwarf-file">, - HelpText<"File name to use for split dwarf debug info output">; def fno_wchar : Flag<["-"], "fno-wchar">, HelpText<"Disable C++ builtin type wchar_t">; def fconstant_string_class : Separate<["-"], "fconstant-string-class">, diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 2ed06cd70860..9de6c8e03aba 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -104,7 +104,17 @@ class EmitAssemblyHelper { /// /// \return True on success. bool AddEmitPasses(legacy::PassManager &CodeGenPasses, BackendAction Action, - raw_pwrite_stream &OS); + raw_pwrite_stream &OS, raw_pwrite_stream *DwoOS); + + std::unique_ptr openOutputFile(StringRef Path) { + std::error_code EC; + auto F = make_unique(Path, EC, llvm::sys::fs::F_None); + if (EC) { + Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message(); + F.reset(); + } + return F; + } public: EmitAssemblyHelper(DiagnosticsEngine &_Diags, @@ -701,7 +711,8 @@ void EmitAssemblyHelper::CreateTargetMachine(bool MustCreateTM) { bool EmitAssemblyHelper::AddEmitPasses(legacy::PassManager &CodeGenPasses, BackendAction Action, - raw_pwrite_stream &OS) { + raw_pwrite_stream &OS, + raw_pwrite_stream *DwoOS) { // Add LibraryInfo. llvm::Triple TargetTriple(TheModule->getTargetTriple()); std::unique_ptr TLII( @@ -718,7 +729,7 @@ bool EmitAssemblyHelper::AddEmitPasses(legacy::PassManager &CodeGenPasses, if (CodeGenOpts.OptimizationLevel > 0) CodeGenPasses.add(createObjCARCContractPass()); - if (TM->addPassesToEmitFile(CodeGenPasses, OS, nullptr, CGFT, + if (TM->addPassesToEmitFile(CodeGenPasses, OS, DwoOS, CGFT, /*DisableVerify=*/!CodeGenOpts.VerifyModule)) { Diags.Report(diag::err_fe_unable_to_interface_with_target); return false; @@ -757,7 +768,7 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, CodeGenPasses.add( createTargetTransformInfoWrapperPass(getTargetIRAnalysis())); - std::unique_ptr ThinLinkOS; + std::unique_ptr ThinLinkOS, DwoOS; switch (Action) { case Backend_EmitNothing: @@ -766,18 +777,12 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, case Backend_EmitBC: if (CodeGenOpts.EmitSummaryIndex) { if (!CodeGenOpts.ThinLinkBitcodeFile.empty()) { - std::error_code EC; - ThinLinkOS.reset(new llvm::raw_fd_ostream( - CodeGenOpts.ThinLinkBitcodeFile, EC, - llvm::sys::fs::F_None)); - if (EC) { - Diags.Report(diag::err_fe_unable_to_open_output) << CodeGenOpts.ThinLinkBitcodeFile - << EC.message(); + ThinLinkOS = openOutputFile(CodeGenOpts.ThinLinkBitcodeFile); + if (!ThinLinkOS) return; - } } - PerModulePasses.add( - createWriteThinLTOBitcodePass(*OS, ThinLinkOS.get())); + PerModulePasses.add(createWriteThinLTOBitcodePass( + *OS, ThinLinkOS ? &ThinLinkOS->os() : nullptr)); } else PerModulePasses.add( @@ -790,7 +795,13 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, break; default: - if (!AddEmitPasses(CodeGenPasses, Action, *OS)) + if (!CodeGenOpts.SplitDwarfFile.empty()) { + DwoOS = openOutputFile(CodeGenOpts.SplitDwarfFile); + if (!DwoOS) + return; + } + if (!AddEmitPasses(CodeGenPasses, Action, *OS, + DwoOS ? &DwoOS->os() : nullptr)) return; } @@ -819,6 +830,11 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, PrettyStackTraceString CrashInfo("Code generation"); CodeGenPasses.run(*TheModule); } + + if (ThinLinkOS) + ThinLinkOS->keep(); + if (DwoOS) + DwoOS->keep(); } static PassBuilder::OptimizationLevel mapToLevel(const CodeGenOptions &Opts) { @@ -971,7 +987,7 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( // create that pass manager here and use it as needed below. legacy::PassManager CodeGenPasses; bool NeedCodeGen = false; - Optional ThinLinkOS; + std::unique_ptr ThinLinkOS, DwoOS; // Append any output we need to the pass manager. switch (Action) { @@ -981,17 +997,12 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( case Backend_EmitBC: if (CodeGenOpts.EmitSummaryIndex) { if (!CodeGenOpts.ThinLinkBitcodeFile.empty()) { - std::error_code EC; - ThinLinkOS.emplace(CodeGenOpts.ThinLinkBitcodeFile, EC, - llvm::sys::fs::F_None); - if (EC) { - Diags.Report(diag::err_fe_unable_to_open_output) - << CodeGenOpts.ThinLinkBitcodeFile << EC.message(); + ThinLinkOS = openOutputFile(CodeGenOpts.ThinLinkBitcodeFile); + if (!ThinLinkOS) return; - } } - MPM.addPass( - ThinLTOBitcodeWriterPass(*OS, ThinLinkOS ? &*ThinLinkOS : nullptr)); + MPM.addPass(ThinLTOBitcodeWriterPass(*OS, ThinLinkOS ? &ThinLinkOS->os() + : nullptr)); } else { MPM.addPass(BitcodeWriterPass(*OS, CodeGenOpts.EmitLLVMUseLists, CodeGenOpts.EmitSummaryIndex, @@ -1009,7 +1020,13 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( NeedCodeGen = true; CodeGenPasses.add( createTargetTransformInfoWrapperPass(getTargetIRAnalysis())); - if (!AddEmitPasses(CodeGenPasses, Action, *OS)) + if (!CodeGenOpts.SplitDwarfFile.empty()) { + DwoOS = openOutputFile(CodeGenOpts.SplitDwarfFile); + if (!DwoOS) + return; + } + if (!AddEmitPasses(CodeGenPasses, Action, *OS, + DwoOS ? &DwoOS->os() : nullptr)) // FIXME: Should we handle this error differently? return; break; @@ -1029,6 +1046,11 @@ void EmitAssemblyHelper::EmitAssemblyWithNewPassManager( PrettyStackTraceString CrashInfo("Code generation"); CodeGenPasses.run(*TheModule); } + + if (ThinLinkOS) + ThinLinkOS->keep(); + if (DwoOS) + DwoOS->keep(); } Expected clang::FindThinLTOModule(MemoryBufferRef MBRef) { diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 8d0766855604..22c028fefcf5 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -4802,12 +4802,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); } - // Handle the debug info splitting at object creation time if we're - // creating an object. - // TODO: Currently only works on linux with newer objcopy. - if (SplitDWARF && Output.getType() == types::TY_Object) - SplitDebugInfo(getToolChain(), C, *this, JA, Args, Output, SplitDWARFOut); - if (Arg *A = Args.getLastArg(options::OPT_pg)) if (Args.hasArg(options::OPT_fomit_frame_pointer)) D.Diag(diag::err_drv_argument_not_allowed_with) << "-fomit-frame-pointer" @@ -5464,19 +5458,17 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); + if (Args.hasArg(options::OPT_gsplit_dwarf) && + getToolChain().getTriple().isOSLinux()) { + CmdArgs.push_back("-split-dwarf-file"); + CmdArgs.push_back(SplitDebugName(Args, Input)); + } + assert(Input.isFilename() && "Invalid input."); CmdArgs.push_back(Input.getFilename()); const char *Exec = getToolChain().getDriver().getClangProgramPath(); C.addCommand(llvm::make_unique(JA, *this, Exec, CmdArgs, Inputs)); - - // Handle the debug info splitting at object creation time if we're - // creating an object. - // TODO: Currently only works on linux with newer objcopy. - if (Args.hasArg(options::OPT_gsplit_dwarf) && - getToolChain().getTriple().isOSLinux()) - SplitDebugInfo(getToolChain(), C, *this, JA, Args, Output, - SplitDebugName(Args, Input)); } // Begin OffloadBundler diff --git a/clang/test/CodeGen/split-debug-filename.c b/clang/test/CodeGen/split-debug-filename.c index 9ca7b0f3287a..b6c3b5634727 100644 --- a/clang/test/CodeGen/split-debug-filename.c +++ b/clang/test/CodeGen/split-debug-filename.c @@ -1,5 +1,8 @@ // RUN: %clang_cc1 -debug-info-kind=limited -split-dwarf-file foo.dwo -S -emit-llvm -o - %s | FileCheck %s // RUN: %clang_cc1 -debug-info-kind=limited -enable-split-dwarf -split-dwarf-file foo.dwo -S -emit-llvm -o - %s | FileCheck --check-prefix=VANILLA %s +// RUN: %clang_cc1 -debug-info-kind=limited -enable-split-dwarf -split-dwarf-file %t.dwo -emit-obj -o - %s | llvm-objdump -section-headers - | FileCheck --check-prefix=O %s +// RUN: llvm-objdump -section-headers %t.dwo | FileCheck --check-prefix=DWO %s + int main (void) { return 0; } @@ -10,3 +13,6 @@ int main (void) { // Testing to ensure that the dwo name is not output into the compile unit if // it's for vanilla split-dwarf rather than split-dwarf for implicit modules. // VANILLA-NOT: splitDebugFilename + +// O-NOT: .dwo +// DWO: .dwo diff --git a/clang/test/Driver/split-debug.c b/clang/test/Driver/split-debug.c index 52f53d3e712e..212c12f5395f 100644 --- a/clang/test/Driver/split-debug.c +++ b/clang/test/Driver/split-debug.c @@ -3,8 +3,7 @@ // RUN: %clang -target x86_64-unknown-linux-gnu -gsplit-dwarf -c -### %s 2> %t // RUN: FileCheck -check-prefix=CHECK-ACTIONS < %t %s // -// CHECK-ACTIONS: objcopy{{.*}}--extract-dwo{{.*}}"split-debug.dwo" -// CHECK-ACTIONS: objcopy{{.*}}--strip-dwo{{.*}}"split-debug.o" +// CHECK-ACTIONS: "-split-dwarf-file" "split-debug.dwo" // RUN: %clang -target x86_64-macosx -gsplit-dwarf -c -### %s 2> %t diff --git a/clang/test/Driver/split-debug.s b/clang/test/Driver/split-debug.s index 64e8f2fa03d2..6e6f8c5b8043 100644 --- a/clang/test/Driver/split-debug.s +++ b/clang/test/Driver/split-debug.s @@ -3,8 +3,7 @@ // RUN: %clang -target x86_64-unknown-linux-gnu -gsplit-dwarf -c -### %s 2> %t // RUN: FileCheck -check-prefix=CHECK-ACTIONS < %t %s // -// CHECK-ACTIONS: objcopy{{.*}}--extract-dwo{{.*}}"split-debug.dwo" -// CHECK-ACTIONS: objcopy{{.*}}--strip-dwo{{.*}}"split-debug.o" +// CHECK-ACTIONS: "-split-dwarf-file" "split-debug.dwo" // RUN: %clang -target x86_64-macosx -gsplit-dwarf -c -### %s 2> %t diff --git a/clang/test/Misc/cc1as-split-dwarf.s b/clang/test/Misc/cc1as-split-dwarf.s new file mode 100644 index 000000000000..5ac221b9105c --- /dev/null +++ b/clang/test/Misc/cc1as-split-dwarf.s @@ -0,0 +1,25 @@ +// RUN: %clang -cc1as -triple x86_64-pc-linux-gnu %s -filetype obj -o %t1 -split-dwarf-file %t2 +// RUN: llvm-objdump -s %t1 | FileCheck --check-prefix=O %s +// RUN: llvm-objdump -s %t2 | FileCheck --check-prefix=DWO %s + +// O-NOT: Contents of section +// O: Contents of section .strtab: +// O-NOT: Contents of section +// O: Contents of section .text: +// O-NEXT: 0000 c3 +// O-NEXT: Contents of section .symtab: +// O-NOT: Contents of section +.globl main +main: +.Ltmp1: +ret +.Ltmp2: + +// DWO-NOT: Contents of section +// DWO: Contents of section .strtab: +// DWO-NOT: Contents of section +// DWO: Contents of section .foo.dwo: +// DWO-NEXT: 0000 01000000 +// DWO-NOT: Contents of section +.section .foo.dwo +.long .Ltmp2-.Ltmp1 diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index 605885239edb..05edb9f4386e 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -97,6 +97,7 @@ struct AssemblerInvocation { llvm::DebugCompressionType CompressDebugSections = llvm::DebugCompressionType::None; std::string MainFileName; + std::string SplitDwarfFile; /// @} /// @name Frontend Options @@ -247,6 +248,7 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, } Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); Opts.OutputPath = Args.getLastArgValue(OPT_o); + Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); if (Arg *A = Args.getLastArg(OPT_filetype)) { StringRef Name = A->getValue(); unsigned OutputType = StringSwitch(Name) @@ -282,22 +284,17 @@ bool AssemblerInvocation::CreateFromArgs(AssemblerInvocation &Opts, } static std::unique_ptr -getOutputStream(AssemblerInvocation &Opts, DiagnosticsEngine &Diags, - bool Binary) { - if (Opts.OutputPath.empty()) - Opts.OutputPath = "-"; - +getOutputStream(StringRef Path, DiagnosticsEngine &Diags, bool Binary) { // Make sure that the Out file gets unlinked from the disk if we get a // SIGINT. - if (Opts.OutputPath != "-") - sys::RemoveFileOnSignal(Opts.OutputPath); + if (Path != "-") + sys::RemoveFileOnSignal(Path); std::error_code EC; auto Out = llvm::make_unique( - Opts.OutputPath, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text)); + Path, EC, (Binary ? sys::fs::F_None : sys::fs::F_Text)); if (EC) { - Diags.Report(diag::err_fe_unable_to_open_output) << Opts.OutputPath - << EC.message(); + Diags.Report(diag::err_fe_unable_to_open_output) << Path << EC.message(); return nullptr; } @@ -342,9 +339,15 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, MAI->setRelaxELFRelocations(Opts.RelaxELFRelocations); bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; - std::unique_ptr FDOS = getOutputStream(Opts, Diags, IsBinary); + if (Opts.OutputPath.empty()) + Opts.OutputPath = "-"; + std::unique_ptr FDOS = + getOutputStream(Opts.OutputPath, Diags, IsBinary); if (!FDOS) return true; + std::unique_ptr DwoOS; + if (!Opts.SplitDwarfFile.empty()) + DwoOS = getOutputStream(Opts.SplitDwarfFile, Diags, IsBinary); // FIXME: This is not pretty. MCContext has a ptr to MCObjectFileInfo and // MCObjectFileInfo needs a MCContext reference in order to initialize itself. @@ -427,7 +430,9 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, MCTargetOptions MCOptions; std::unique_ptr MAB( TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions)); - std::unique_ptr OW = MAB->createObjectWriter(*Out); + std::unique_ptr OW = + DwoOS ? MAB->createDwoObjectWriter(*Out, *DwoOS) + : MAB->createObjectWriter(*Out); Triple T(Opts.Triple); Str.reset(TheTarget->createMCObjectStreamer( @@ -476,8 +481,12 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, FDOS.reset(); // Delete output file if there were errors. - if (Failed && Opts.OutputPath != "-") - sys::fs::remove(Opts.OutputPath); + if (Failed) { + if (Opts.OutputPath != "-") + sys::fs::remove(Opts.OutputPath); + if (!Opts.SplitDwarfFile.empty() && Opts.SplitDwarfFile != "-") + sys::fs::remove(Opts.SplitDwarfFile); + } return Failed; }