diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -316,6 +316,10 @@ /// coverage pass should actually not be instrumented. std::vector SanitizeCoverageBlacklistFiles; + /// Executable and command-line used to create a given CompilerInvocation. + const char *BuildTool = nullptr; + ArrayRef CommandLineArgs; + public: // Define accessors/mutators for code generation options of enumeration type. #define CODEGENOPT(Name, Bits, Default) diff --git a/clang/include/clang/Frontend/CompilerInvocation.h b/clang/include/clang/Frontend/CompilerInvocation.h --- a/clang/include/clang/Frontend/CompilerInvocation.h +++ b/clang/include/clang/Frontend/CompilerInvocation.h @@ -155,7 +155,8 @@ /// \param [out] Res - The resulting invocation. static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef CommandLineArgs, - DiagnosticsEngine &Diags); + DiagnosticsEngine &Diags, + const char *BuildTool = nullptr); /// Get the directory where the compiler headers /// reside, relative to the compiler binary (found by the passed in diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -519,6 +519,8 @@ Entry.Group == frontend::IncludeDirGroup::System)) Options.MCOptions.IASSearchPaths.push_back( Entry.IgnoreSysRoot ? Entry.Path : HSOpts.Sysroot + Entry.Path); + Options.MCOptions.BuildTool = CodeGenOpts.BuildTool; + Options.MCOptions.CommandLineArgs = CodeGenOpts.CommandLineArgs; } static Optional getGCOVOptions(const CodeGenOptions &CodeGenOpts) { if (CodeGenOpts.DisableGCov) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3608,7 +3608,8 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, ArrayRef CommandLineArgs, - DiagnosticsEngine &Diags) { + DiagnosticsEngine &Diags, + const char *BuildTool) { bool Success = true; // Parse the arguments. @@ -3728,6 +3729,11 @@ Res.getCodeGenOpts().FineGrainedBitfieldAccesses = false; Diags.Report(diag::warn_drv_fine_grained_bitfield_accesses_ignored); } + + // Store the command-line for using in CodeView backend. + Res.getCodeGenOpts().BuildTool = BuildTool; + Res.getCodeGenOpts().CommandLineArgs = CommandLineArgs; + return Success; } diff --git a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp --- a/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp +++ b/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -93,7 +93,7 @@ if (CC1Args) *CC1Args = {CCArgs.begin(), CCArgs.end()}; auto CI = std::make_unique(); - if (!CompilerInvocation::CreateFromArgs(*CI, CCArgs, *Diags) && + if (!CompilerInvocation::CreateFromArgs(*CI, CCArgs, *Diags, Args[0]) && !ShouldRecoverOnErorrs) return nullptr; return CI; diff --git a/clang/test/CodeGen/debug-info-codeview-buildinfo.c b/clang/test/CodeGen/debug-info-codeview-buildinfo.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/debug-info-codeview-buildinfo.c @@ -0,0 +1,17 @@ +// RUN: %clang_cl /c /Z7 %s /Fo%t.obj +// RUN: llvm-pdbutil dump --types %t.obj | FileCheck %s + +int main() { return 42; } + +// CHECK: Types (.debug$T) +// CHECK: ============================================================ +// CHECK: 0x[[PWD:.+]] | LF_STRING_ID [size = {{.+}}] ID: , String: [[PWDVAL:.+]] +// CHECK: 0x[[FILEPATH:.+]] | LF_STRING_ID [size = {{.+}}] ID: , String: [[FILEPATHVAL:.+[\\/]debug-info-codeview-buildinfo.c]] +// CHECK: 0x[[TOOL:.+]] | LF_STRING_ID [size = {{.+}}] ID: , String: [[TOOLVAL:.+[\\/]clang.*]] +// CHECK: 0x[[CMDLINE:.+]] | LF_STRING_ID [size = {{.+}}] ID: , String: -cc1 +// CHECK: 0x{{.+}} | LF_BUILDINFO [size = {{.+}}] +// CHECK: 0x[[PWD]]: `[[PWDVAL]]` +// CHECK: 0x[[TOOL]]: `[[TOOLVAL]]` +// CHECK: 0x[[FILEPATH]]: `[[FILEPATHVAL]]` +// CHECK: : `` +// CHECK: 0x[[CMDLINE]]: `-cc1 diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -181,7 +181,7 @@ return 0; } -int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { +int cc1_main(ArrayRef Argv, void *MainAddr) { ensureSufficientStack(); std::unique_ptr Clang(new CompilerInstance()); @@ -203,12 +203,11 @@ IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); - bool Success = - CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, Diags); - + bool Success = CompilerInvocation::CreateFromArgs( + Clang->getInvocation(), Argv.slice(1), Diags, Argv[0]); if (Clang->getFrontendOpts().TimeTrace) { llvm::timeTraceProfilerInitialize( - Clang->getFrontendOpts().TimeTraceGranularity, Argv0); + Clang->getFrontendOpts().TimeTraceGranularity, Argv[0]); } // --print-supported-cpus takes priority over the actual compilation. if (Clang->getFrontendOpts().PrintSupportedCPUs) @@ -218,7 +217,7 @@ if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && Clang->getHeaderSearchOpts().ResourceDir.empty()) Clang->getHeaderSearchOpts().ResourceDir = - CompilerInvocation::GetResourcesPath(Argv0, MainAddr); + CompilerInvocation::GetResourcesPath(Argv[0], MainAddr); // Create the actual diagnostics engine. Clang->createDiagnostics(); diff --git a/clang/tools/driver/driver.cpp b/clang/tools/driver/driver.cpp --- a/clang/tools/driver/driver.cpp +++ b/clang/tools/driver/driver.cpp @@ -203,8 +203,7 @@ } } -extern int cc1_main(ArrayRef Argv, const char *Argv0, - void *MainAddr); +extern int cc1_main(ArrayRef Argv, void *MainAddr); extern int cc1as_main(ArrayRef Argv, const char *Argv0, void *MainAddr); extern int cc1gen_reproducer_main(ArrayRef Argv, @@ -327,7 +326,7 @@ StringRef Tool = ArgV[1]; void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath; if (Tool == "-cc1") - return cc1_main(makeArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); + return cc1_main(makeArrayRef(ArgV), GetExecutablePathVP); if (Tool == "-cc1as") return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0], GetExecutablePathVP); diff --git a/llvm/include/llvm/MC/MCTargetOptions.h b/llvm/include/llvm/MC/MCTargetOptions.h --- a/llvm/include/llvm/MC/MCTargetOptions.h +++ b/llvm/include/llvm/MC/MCTargetOptions.h @@ -9,6 +9,7 @@ #ifndef LLVM_MC_MCTARGETOPTIONS_H #define LLVM_MC_MCTARGETOPTIONS_H +#include "llvm/ADT/ArrayRef.h" #include #include @@ -59,6 +60,9 @@ std::string AssemblyLanguage; std::string SplitDwarfFile; + const char *BuildTool = nullptr; + ArrayRef CommandLineArgs; + /// Additional paths to search for `.include` directives when using the /// integrated assembler. std::vector IASSearchPaths; diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp --- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp @@ -831,6 +831,31 @@ return TypeTable.writeLeafType(SIR); } +static std::string renderCommandLine(ArrayRef CommandLineArgs, + StringRef MainFile) { + std::string FlatCmdLine; + SmallString<128> TempArg; + auto maybeQuote = [&TempArg](const char *Arg) -> StringRef { + const bool Escape = + StringRef(Arg).find_first_of(" \"\\$") != StringRef::npos; + if (!Escape) + return Arg; + TempArg.clear(); + (Twine("\"") + Arg + "\"").toVector(TempArg); + return TempArg; + }; + for (auto &Arg : CommandLineArgs) { + if (Arg == nullptr) + break; + // The command-line shall not contain the file to compile. + if (Arg == MainFile) + continue; + FlatCmdLine += maybeQuote(Arg); + FlatCmdLine += " "; + } + return FlatCmdLine; +} + void CodeViewDebug::emitBuildInfo() { // First, make LF_BUILDINFO. It's a sequence of strings with various bits of // build info. The known prefix is: @@ -851,6 +876,13 @@ getStringIdTypeIdx(TypeTable, MainSourceFile->getDirectory()); BuildInfoArgs[BuildInfoRecord::SourceFile] = getStringIdTypeIdx(TypeTable, MainSourceFile->getFilename()); + if (!StringRef(Asm->TM.Options.MCOptions.BuildTool).empty()) { + BuildInfoArgs[BuildInfoRecord::BuildTool] = + getStringIdTypeIdx(TypeTable, Asm->TM.Options.MCOptions.BuildTool); + BuildInfoArgs[BuildInfoRecord::CommandLine] = getStringIdTypeIdx( + TypeTable, renderCommandLine(Asm->TM.Options.MCOptions.CommandLineArgs, + MainSourceFile->getFilename())); + } // FIXME: Path to compiler and command line. PDB is intentionally blank unless // we implement /Zi type servers. BuildInfoRecord BIR(BuildInfoArgs);