aganea created this revision.
aganea added reviewers: hans, amccarth, thakis, mstorsjo, akhuang.
Herald added subscribers: llvm-commits, cfe-commits, hiraditya.
Herald added projects: clang, LLVM.

This patch adds some missing information to the `LF_BUILDINFO` which allows for 
rebuilding a .CPP without any external dependency but the .OBJ itself (other 
than the compiler).

Some external tools that we are using (Recode <https://www.indefiant.com/>, 
Live++ <https://liveplusplus.tech/>) are extracting the information to 
reproduce a build without any knowledge of the build system. The `LF_BUILDINFO` 
therefore stores a full path to the compiler, the PWD, a relative or absolute 
path to the TU, and the full CC1 command line. The command line needs to be 
freestanding (not depend on any environment variables). In the same way, MSVC 
doesn't store the provided command-line, but an expanded version (somehow their 
equivalent of CC1) which is also freestanding.

For more information see PR36198 and D43002 <https://reviews.llvm.org/D43002>.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80833

Files:
  clang/include/clang/Basic/CodeGenOptions.h
  clang/include/clang/Frontend/CompilerInvocation.h
  clang/lib/CodeGen/BackendUtil.cpp
  clang/lib/Frontend/CompilerInvocation.cpp
  clang/lib/Frontend/CreateInvocationFromCommandLine.cpp
  clang/test/CodeGen/debug-info-codeview-buildinfo.c
  clang/tools/driver/cc1_main.cpp
  clang/tools/driver/driver.cpp
  llvm/include/llvm/MC/MCTargetOptions.h
  llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp

Index: llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
===================================================================
--- llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -831,6 +831,31 @@
   return TypeTable.writeLeafType(SIR);
 }
 
+static std::string renderCommandLine(ArrayRef<const char *> 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);
Index: llvm/include/llvm/MC/MCTargetOptions.h
===================================================================
--- llvm/include/llvm/MC/MCTargetOptions.h
+++ 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 <string>
 #include <vector>
 
@@ -59,6 +60,9 @@
   std::string AssemblyLanguage;
   std::string SplitDwarfFile;
 
+  const char *BuildTool = nullptr;
+  ArrayRef<const char *> CommandLineArgs;
+
   /// Additional paths to search for `.include` directives when using the
   /// integrated assembler.
   std::vector<std::string> IASSearchPaths;
Index: clang/tools/driver/driver.cpp
===================================================================
--- clang/tools/driver/driver.cpp
+++ clang/tools/driver/driver.cpp
@@ -203,8 +203,7 @@
   }
 }
 
-extern int cc1_main(ArrayRef<const char *> Argv, const char *Argv0,
-                    void *MainAddr);
+extern int cc1_main(ArrayRef<const char *> Argv, void *MainAddr);
 extern int cc1as_main(ArrayRef<const char *> Argv, const char *Argv0,
                       void *MainAddr);
 extern int cc1gen_reproducer_main(ArrayRef<const char *> 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);
Index: clang/tools/driver/cc1_main.cpp
===================================================================
--- clang/tools/driver/cc1_main.cpp
+++ clang/tools/driver/cc1_main.cpp
@@ -181,7 +181,7 @@
   return 0;
 }
 
-int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
+int cc1_main(ArrayRef<const char *> Argv, void *MainAddr) {
   ensureSufficientStack();
 
   std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());
@@ -203,12 +203,11 @@
   IntrusiveRefCntPtr<DiagnosticOptions> 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();
Index: clang/test/CodeGen/debug-info-codeview-buildinfo.c
===================================================================
--- /dev/null
+++ 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: <no type>, String: [[PWDVAL:.+]]
+// CHECK: 0x[[FILEPATH:.+]] | LF_STRING_ID [size = {{.+}}] ID: <no type>, String: [[FILEPATHVAL:.+[\\/]debug-info-codeview-buildinfo.c]]
+// CHECK: 0x[[TOOL:.+]] | LF_STRING_ID [size = {{.+}}] ID: <no type>, String: [[TOOLVAL:.+[\\/]clang.*]]
+// CHECK: 0x[[CMDLINE:.+]] | LF_STRING_ID [size = {{.+}}] ID: <no type>, String: -cc1
+// CHECK: 0x{{.+}} | LF_BUILDINFO [size = {{.+}}]
+// CHECK:          0x[[PWD]]: `[[PWDVAL]]`
+// CHECK:          0x[[TOOL]]: `[[TOOLVAL]]`
+// CHECK:          0x[[FILEPATH]]: `[[FILEPATHVAL]]`
+// CHECK:          <no type>: ``
+// CHECK:          0x[[CMDLINE]]: `-cc1
Index: clang/lib/Frontend/CreateInvocationFromCommandLine.cpp
===================================================================
--- clang/lib/Frontend/CreateInvocationFromCommandLine.cpp
+++ clang/lib/Frontend/CreateInvocationFromCommandLine.cpp
@@ -93,7 +93,7 @@
   if (CC1Args)
     *CC1Args = {CCArgs.begin(), CCArgs.end()};
   auto CI = std::make_unique<CompilerInvocation>();
-  if (!CompilerInvocation::CreateFromArgs(*CI, CCArgs, *Diags) &&
+  if (!CompilerInvocation::CreateFromArgs(*CI, CCArgs, *Diags, Args[0]) &&
       !ShouldRecoverOnErorrs)
     return nullptr;
   return CI;
Index: clang/lib/Frontend/CompilerInvocation.cpp
===================================================================
--- clang/lib/Frontend/CompilerInvocation.cpp
+++ clang/lib/Frontend/CompilerInvocation.cpp
@@ -3608,7 +3608,8 @@
 
 bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
                                         ArrayRef<const char *> 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;
 }
 
Index: clang/lib/CodeGen/BackendUtil.cpp
===================================================================
--- clang/lib/CodeGen/BackendUtil.cpp
+++ 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<GCOVOptions> getGCOVOptions(const CodeGenOptions &CodeGenOpts) {
   if (CodeGenOpts.DisableGCov)
Index: clang/include/clang/Frontend/CompilerInvocation.h
===================================================================
--- clang/include/clang/Frontend/CompilerInvocation.h
+++ clang/include/clang/Frontend/CompilerInvocation.h
@@ -155,7 +155,8 @@
   /// \param [out] Res - The resulting invocation.
   static bool CreateFromArgs(CompilerInvocation &Res,
                              ArrayRef<const char *> 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
Index: clang/include/clang/Basic/CodeGenOptions.h
===================================================================
--- clang/include/clang/Basic/CodeGenOptions.h
+++ clang/include/clang/Basic/CodeGenOptions.h
@@ -316,6 +316,10 @@
   /// coverage pass should actually not be instrumented.
   std::vector<std::string> SanitizeCoverageBlacklistFiles;
 
+  /// Executable and command-line used to create a given CompilerInvocation.
+  const char *BuildTool = nullptr;
+  ArrayRef<const char *> CommandLineArgs;
+
 public:
   // Define accessors/mutators for code generation options of enumeration type.
 #define CODEGENOPT(Name, Bits, Default)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to