https://github.com/dzhidzhoev updated 
https://github.com/llvm/llvm-project/pull/199689

>From 8e39032caf4c4469d033fd0c3452057632141cfa Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <[email protected]>
Date: Mon, 20 Apr 2026 01:39:58 +0200
Subject: [PATCH] [Clang][HLSL][DebugInfo] Emit dx.source metadata nodes

Add HLSL debug metadata emission for DXIL by embedding non-system source
files (including included headers) into dx.source.contents, recording the
main file name and user defines, and serializing the dxc-style command
line into dx.source.args.

This metadata nodes are needed for emitting SRCI part of a debug DXContainer in 
llc.

Introduce -fdx-record-command-line option to cc1 to carry the escaped driver 
command
line into cc1.
Introduce -fdx-no-source-metadata CodeGen option to cc1, to be able to
disable dx.source metadata nodes emission. This comes in handy for
writing debug info tests, so that CHECK-NOT lines do not match themselves in
LLVM IR. For example, without this option, in 
`clang/test/CodeGenHLSL/debug/source-language.hlsl`,
CHECK-V4-NOT line gets a match on the line with dx.source.contents node.
---
 clang/include/clang/Basic/CodeGenOptions.def  |   3 +
 clang/include/clang/Basic/CodeGenOptions.h    |   4 +
 clang/include/clang/Driver/CommonArgs.h       |   4 +-
 clang/include/clang/Options/OptionUtils.h     |   4 +
 clang/include/clang/Options/Options.td        |  13 ++
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 111 ++++++++++++++
 clang/lib/CodeGen/CMakeLists.txt              |   1 +
 clang/lib/Driver/ToolChains/Clang.cpp         |   8 +-
 clang/lib/Driver/ToolChains/CommonArgs.cpp    |   7 +-
 clang/lib/Driver/ToolChains/Flang.cpp         |   4 +-
 clang/lib/Options/OptionUtils.cpp             |  32 +++++
 clang/test/CodeGenHLSL/Inputs/a.hlsl          |  10 ++
 clang/test/CodeGenHLSL/Inputs/b.hlsl          |   3 +
 clang/test/CodeGenHLSL/SysInputs/c.hlsl       |   3 +
 .../CodeGenHLSL/debug/source-language.hlsl    |   4 +
 .../dx-source-metadata-disabled.hlsl          |  12 ++
 .../dx-source-metadata-includes.hlsl          |  21 +++
 ...urce-metadata-mailformed-command-line.hlsl |  10 ++
 .../test/CodeGenHLSL/dx-source-metadata.hlsl  |  28 ++++
 clang/test/CodeGenHLSL/lit.local.cfg          |   1 +
 clang/test/Driver/dxc_debug.hlsl              |   5 +-
 clang/unittests/Driver/CMakeLists.txt         |   2 +
 .../Driver/EscapedCommandLineTest.cpp         | 135 ++++++++++++++++++
 23 files changed, 418 insertions(+), 7 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/Inputs/a.hlsl
 create mode 100644 clang/test/CodeGenHLSL/Inputs/b.hlsl
 create mode 100644 clang/test/CodeGenHLSL/SysInputs/c.hlsl
 create mode 100644 clang/test/CodeGenHLSL/dx-source-metadata-disabled.hlsl
 create mode 100644 clang/test/CodeGenHLSL/dx-source-metadata-includes.hlsl
 create mode 100644 
clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl
 create mode 100644 clang/test/CodeGenHLSL/dx-source-metadata.hlsl
 create mode 100644 clang/unittests/Driver/EscapedCommandLineTest.cpp

diff --git a/clang/include/clang/Basic/CodeGenOptions.def 
b/clang/include/clang/Basic/CodeGenOptions.def
index aa36de6edecbf..b121e7cb46601 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -520,6 +520,9 @@ CODEGENOPT(ResMayAlias, 1, 0, Benign)
 /// Assume that all resources are bound if enabled
 CODEGENOPT(AllResourcesBound, 1, 0, Benign)
 
+/// Do not embed dx.source.* metadata in HLSL modules.
+CODEGENOPT(DisableDXSourceMetadata, 1, 0, Benign)
+
 /// Controls how unwind v2 (epilog) information should be generated for x64
 /// Windows.
 ENUM_CODEGENOPT(WinX64EHUnwindV2, WinX64EHUnwindV2Mode,
diff --git a/clang/include/clang/Basic/CodeGenOptions.h 
b/clang/include/clang/Basic/CodeGenOptions.h
index e43112b4bb98b..33991827eb061 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -268,6 +268,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
   /// if non-empty.
   std::string RecordCommandLine;
 
+  /// The string containing the commandline for the dx.source.args metadata,
+  /// if non-empty.
+  std::string HLSLRecordCommandLine;
+
   llvm::SmallVector<std::pair<std::string, std::string>, 0> DebugPrefixMap;
 
   /// Prefix replacement map for source-based code coverage to remap source
diff --git a/clang/include/clang/Driver/CommonArgs.h 
b/clang/include/clang/Driver/CommonArgs.h
index 0af1b89425227..51ae3b13b5550 100644
--- a/clang/include/clang/Driver/CommonArgs.h
+++ b/clang/include/clang/Driver/CommonArgs.h
@@ -275,8 +275,8 @@ const char *renderEscapedCommandLine(const ToolChain &TC,
 /// line options that were passed.
 bool shouldRecordCommandLine(const ToolChain &TC,
                              const llvm::opt::ArgList &Args,
-                             bool &FRecordCommandLine,
-                             bool &GRecordCommandLine);
+                             bool &FRecordCommandLine, bool 
&GRecordCommandLine,
+                             bool &DXRecordCommandLine);
 
 void renderGlobalISelOptions(const Driver &D, const llvm::opt::ArgList &Args,
                              llvm::opt::ArgStringList &CmdArgs,
diff --git a/clang/include/clang/Options/OptionUtils.h 
b/clang/include/clang/Options/OptionUtils.h
index 02c9c27554db1..4305cdd134bf4 100644
--- a/clang/include/clang/Options/OptionUtils.h
+++ b/clang/include/clang/Options/OptionUtils.h
@@ -77,6 +77,10 @@ std::string GetResourcesPath(StringRef BinaryPath);
 /// executable), for finding the builtin compiler path.
 std::string GetResourcesPath(const char *Argv0, void *MainAddr);
 
+/// Parse a space-separated command line with escaped spaces and backslashes.
+llvm::Expected<llvm::SmallVector<llvm::SmallString<8>>>
+parseEscapedCommandLine(const char *CommandLine);
+
 } // namespace clang
 
 #endif // LLVM_CLANG_OPTIONS_OPTIONUTILS_H
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 753e3ac1b74a5..f448a986038cb 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -9670,6 +9670,19 @@ def dxc_rootsig_define :
   Alias<fdx_rootsignature_define>,
   Group<dxc_Group>,
   Visibility<[DXCOption]>;
+def dxc_record_command_line
+    : Separate<["-"], "fdx-record-command-line">,
+      Group<dxc_Group>,
+      Visibility<[CC1Option]>,
+      MarshallingInfoString<CodeGenOpts<"HLSLRecordCommandLine">>,
+      HelpText<
+          "Command line arguments to embed in the dx.source.args metadata">;
+def dxc_no_source_metadata
+    : Flag<["-"], "fdx-no-source-metadata">,
+      Group<dxc_Group>,
+      Visibility<[CC1Option]>,
+      MarshallingInfoFlag<CodeGenOpts<"DisableDXSourceMetadata">>,
+      HelpText<"Do not embed source info metadata in HLSL modules">;
 def hlsl_entrypoint : Option<["-"], "hlsl-entry", KIND_SEPARATE>,
                       Group<dxc_Group>,
                       Visibility<[ClangOption, CC1Option]>,
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 98d65715d45b9..a21b96f4acb7c 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -27,8 +27,11 @@
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/DiagnosticFrontend.h"
+#include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TargetOptions.h"
+#include "clang/Options/OptionUtils.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
@@ -45,6 +48,7 @@
 #include "llvm/Support/Alignment.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Path.h"
 #include <cstdint>
 #include <optional>
 
@@ -189,6 +193,109 @@ findAssociatedResourceDeclForStruct(ASTContext &AST, 
const MemberExpr *ME) {
   return nullptr;
 }
 
+void addSourceInfo(CodeGenModule &CGM, llvm::Module &M) {
+  auto &SM = CGM.getContext().getSourceManager();
+  auto &Macros = CGM.getPreprocessorOpts().Macros;
+  auto &Ctx = M.getContext();
+
+  // Names and content of shader source code files.
+  llvm::NamedMDNode *DXContents =
+      M.getOrInsertNamedMetadata("dx.source.contents");
+  auto addFile = [&](const std::pair<StringRef, StringRef> &NameContent) {
+    llvm::MDTuple *FileInfo =
+        llvm::MDNode::get(Ctx, {llvm::MDString::get(Ctx, NameContent.first),
+                                llvm::MDString::get(Ctx, NameContent.second)});
+    DXContents->addOperand(FileInfo);
+  };
+
+  bool Invalid = false;
+  const SrcMgr::SLocEntry *MainLocEntry =
+      &SM.getSLocEntry(SM.getMainFileID(), &Invalid);
+  assert(!Invalid && "Main file SLocEntry must not be invalid!");
+  const SrcMgr::ContentCache &MainCCEntry =
+      MainLocEntry->getFile().getContentCache();
+
+  SmallVector<std::pair<std::string, StringRef>> Files;
+  std::optional<SmallString<256>> MainFileName;
+  Files.reserve(SM.local_sloc_entry_size());
+  for (unsigned I : llvm::seq(SM.local_sloc_entry_size())) {
+    const SrcMgr::SLocEntry &LocEntry = SM.getLocalSLocEntry(I);
+    if (!LocEntry.isFile())
+      continue;
+
+    const SrcMgr::FileInfo &FInfo = LocEntry.getFile();
+    if (isSystem(FInfo.getFileCharacteristic()))
+      continue;
+
+    const SrcMgr::ContentCache &CCEntry = FInfo.getContentCache();
+    OptionalFileEntryRef FEntry = CCEntry.OrigEntry;
+    if (!FEntry)
+      continue;
+
+    llvm::SmallString<256> Path = FEntry->getName();
+    llvm::sys::path::native(Path);
+    std::optional<llvm::MemoryBufferRef> Buffer = CCEntry.getBufferOrNone(
+        SM.getDiagnostics(), SM.getFileManager(), SourceLocation());
+    if (!Buffer) {
+      unsigned ID = SM.getDiagnostics().getCustomDiagID(
+          clang::DiagnosticsEngine::Warning,
+          "failed to embed source for \"%0\" into dx.source.contents");
+      SM.getDiagnostics().Report(ID) << Path;
+      continue;
+    }
+
+    if (&MainCCEntry != &CCEntry) {
+      Files.emplace_back(Path, Buffer->getBuffer());
+    } else {
+      // Main file should be at first position.
+      addFile(std::make_pair(Path, Buffer->getBuffer()));
+      MainFileName.emplace(Path);
+    }
+  }
+  assert(MainFileName && "Main file not found.");
+
+  // Files other that main one should be sorted by name.
+  llvm::sort(Files);
+#ifndef NDEBUG
+  for (unsigned I = 1; I < Files.size(); ++I)
+    assert((Files[I - 1].first != Files[I].first) &&
+           "duplicate files in dx.source.contents");
+#endif
+  llvm::for_each(Files, addFile);
+
+  SmallVector<llvm::Metadata *> Defines;
+  Defines.reserve(Macros.size());
+  for (const auto &Macro : Macros) {
+    // Ignore undefs.
+    if (!Macro.second)
+      Defines.emplace_back(llvm::MDString::get(Ctx, Macro.first));
+  }
+  M.getOrInsertNamedMetadata("dx.source.defines")
+      ->addOperand(llvm::MDNode::get(Ctx, Defines));
+
+  if (!CGM.getCodeGenOpts().MainFileName.empty())
+    llvm::sys::path::native(CGM.getCodeGenOpts().MainFileName, *MainFileName);
+  M.getOrInsertNamedMetadata("dx.source.mainFileName")
+      ->addOperand(
+          llvm::MDNode::get(Ctx, llvm::MDString::get(Ctx, *MainFileName)));
+
+  auto ParsedArgs = clang::parseEscapedCommandLine(
+      CGM.getCodeGenOpts().HLSLRecordCommandLine.c_str());
+  if (!ParsedArgs) {
+    unsigned DiagID = CGM.getDiags().getCustomDiagID(
+        DiagnosticsEngine::Error, "invalid escaped command line: %0");
+    CGM.getDiags().Report(DiagID) << llvm::toString(ParsedArgs.takeError());
+    return;
+  }
+  SmallVector<llvm::Metadata *> Args;
+  Args.reserve(ParsedArgs->size());
+  if (!ParsedArgs->empty())
+    for (const auto &Arg : llvm::drop_begin(*ParsedArgs))
+      Args.push_back(llvm::MDString::get(Ctx, Arg));
+  M.getOrInsertNamedMetadata("dx.source.args")
+      ->addOperand(llvm::MDNode::get(Ctx, Args));
+}
+
 // Find array variable declaration from DeclRef expression
 static const ValueDecl *getArrayDecl(ASTContext &AST, const Expr *E) {
   E = E->IgnoreImpCasts();
@@ -576,6 +683,10 @@ void CGHLSLRuntime::finishCodeGen() {
   Triple T(M.getTargetTriple());
   if (T.getArch() == Triple::ArchType::dxil)
     addDxilValVersion(TargetOpts.DxilValidatorVersion, M);
+  if (!CodeGenOpts.DisableDXSourceMetadata &&
+      CodeGenOpts.getDebugInfo() >=
+          llvm::codegenoptions::DebugInfoKind::DebugInfoConstructor)
+    addSourceInfo(CGM, M);
   if (CodeGenOpts.ResMayAlias)
     M.setModuleFlag(llvm::Module::ModFlagBehavior::Error, "dx.resmayalias", 1);
   if (CodeGenOpts.AllResourcesBound)
diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 117438c616ab5..54498de279f42 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -173,5 +173,6 @@ add_clang_library(clangCodeGen
   clangBasic
   clangFrontend
   clangLex
+  clangOptions
   clangSerialization
   )
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 8d8e00bbaf7d0..1372798b45cb9 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8058,7 +8058,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
   // By default, -gno-record-gcc-switches is set on and no recording.
   auto GRecordSwitches = false;
   auto FRecordSwitches = false;
-  if (shouldRecordCommandLine(TC, Args, FRecordSwitches, GRecordSwitches)) {
+  bool DXRecordSwitches = false;
+  if (shouldRecordCommandLine(TC, Args, FRecordSwitches, GRecordSwitches,
+                              DXRecordSwitches)) {
     auto FlagsArgString = renderEscapedCommandLine(TC, Args);
     if (TC.UseDwarfDebugFlags() || GRecordSwitches) {
       CmdArgs.push_back("-dwarf-debug-flags");
@@ -8068,6 +8070,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
       CmdArgs.push_back("-record-command-line");
       CmdArgs.push_back(FlagsArgString);
     }
+    if (DXRecordSwitches) {
+      CmdArgs.push_back("-fdx-record-command-line");
+      CmdArgs.push_back(FlagsArgString);
+    }
   }
 
   // Host-side offloading compilation receives all device-side outputs. Include
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp 
b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 2ef7c1506e18d..dae0c67ad69c9 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -3338,7 +3338,8 @@ const char *tools::renderEscapedCommandLine(const 
ToolChain &TC,
 bool tools::shouldRecordCommandLine(const ToolChain &TC,
                                     const llvm::opt::ArgList &Args,
                                     bool &FRecordCommandLine,
-                                    bool &GRecordCommandLine) {
+                                    bool &GRecordCommandLine,
+                                    bool &DXRecordCommandLine) {
   const Driver &D = TC.getDriver();
   const llvm::Triple &Triple = TC.getEffectiveTriple();
   const std::string &TripleStr = Triple.getTriple();
@@ -3349,13 +3350,15 @@ bool tools::shouldRecordCommandLine(const ToolChain &TC,
   GRecordCommandLine =
       Args.hasFlag(options::OPT_grecord_command_line,
                    options::OPT_gno_record_command_line, false);
+  DXRecordCommandLine = Triple.isDXIL() && Args.hasArg(options::OPT_g_Flag);
   if (FRecordCommandLine && !Triple.isOSBinFormatELF() &&
       !Triple.isOSBinFormatXCOFF() && !Triple.isOSBinFormatMachO())
     D.Diag(diag::err_drv_unsupported_opt_for_target)
         << 
Args.getLastArg(options::OPT_frecord_command_line)->getAsString(Args)
         << TripleStr;
 
-  return FRecordCommandLine || TC.UseDwarfDebugFlags() || GRecordCommandLine;
+  return FRecordCommandLine || TC.UseDwarfDebugFlags() || GRecordCommandLine ||
+         DXRecordCommandLine;
 }
 
 void tools::renderGlobalISelOptions(const Driver &D, const ArgList &Args,
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index 4c722a2e021eb..3620961b4c3b1 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -1284,7 +1284,9 @@ void Flang::ConstructJob(Compilation &C, const JobAction 
&JA,
 
   bool FRecordCmdLine = false;
   bool GRecordCmdLine = false;
-  if (shouldRecordCommandLine(TC, Args, FRecordCmdLine, GRecordCmdLine)) {
+  bool DXRecordCmdLine = false;
+  if (shouldRecordCommandLine(TC, Args, FRecordCmdLine, GRecordCmdLine,
+                              DXRecordCmdLine)) {
     const char *CmdLine = renderEscapedCommandLine(TC, Args);
     if (FRecordCmdLine) {
       CmdArgs.push_back("-record-command-line");
diff --git a/clang/lib/Options/OptionUtils.cpp 
b/clang/lib/Options/OptionUtils.cpp
index e5aefa012f679..77f89552e852a 100644
--- a/clang/lib/Options/OptionUtils.cpp
+++ b/clang/lib/Options/OptionUtils.cpp
@@ -244,3 +244,35 @@ std::string clang::GetResourcesPath(const char *Argv0, 
void *MainAddr) {
       llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
   return GetResourcesPath(ClangExecutable);
 }
+
+static bool isSpaceOrNull(char c) { return !c || c == ' '; }
+
+static Expected<const char *> unescapeUntilSpace(const char *Arg,
+                                                 SmallVectorImpl<char> &Res) {
+  for (; !isSpaceOrNull(*Arg); ++Arg) {
+    if (*Arg == '\\') {
+      ++Arg;
+      if (*Arg != '\\' && *Arg != ' ')
+        return llvm::createStringError(
+            llvm::inconvertibleErrorCode(),
+            "only escaped backslashes and spaces are supported");
+    }
+    Res.push_back(*Arg);
+  }
+  return Arg;
+}
+
+Expected<SmallVector<SmallString<8>>>
+clang::parseEscapedCommandLine(const char *CommandLine) {
+  SmallVector<SmallString<8>> Res;
+  while (*CommandLine) {
+    Expected<const char *> ArgEnd =
+        unescapeUntilSpace(CommandLine, Res.emplace_back());
+    if (!ArgEnd)
+      return ArgEnd.takeError();
+    CommandLine = *ArgEnd;
+    if (*CommandLine == ' ')
+      ++CommandLine;
+  }
+  return Res;
+}
diff --git a/clang/test/CodeGenHLSL/Inputs/a.hlsl 
b/clang/test/CodeGenHLSL/Inputs/a.hlsl
new file mode 100644
index 0000000000000..9c132672dea82
--- /dev/null
+++ b/clang/test/CodeGenHLSL/Inputs/a.hlsl
@@ -0,0 +1,10 @@
+#ifndef GUARD
+#define GUARD
+
+#include "b.hlsl"
+
+float helper1_add(float a, float b) {
+  return a + b;
+}
+
+#endif
diff --git a/clang/test/CodeGenHLSL/Inputs/b.hlsl 
b/clang/test/CodeGenHLSL/Inputs/b.hlsl
new file mode 100644
index 0000000000000..798909fa50569
--- /dev/null
+++ b/clang/test/CodeGenHLSL/Inputs/b.hlsl
@@ -0,0 +1,3 @@
+float helper2_mul(float a, float b) {
+  return a * b;
+}
diff --git a/clang/test/CodeGenHLSL/SysInputs/c.hlsl 
b/clang/test/CodeGenHLSL/SysInputs/c.hlsl
new file mode 100644
index 0000000000000..30223007e1920
--- /dev/null
+++ b/clang/test/CodeGenHLSL/SysInputs/c.hlsl
@@ -0,0 +1,3 @@
+float sys_helper(float a, float b) {
+  return a * b;
+}
diff --git a/clang/test/CodeGenHLSL/debug/source-language.hlsl 
b/clang/test/CodeGenHLSL/debug/source-language.hlsl
index f1e63d17724ee..83099b288853f 100644
--- a/clang/test/CodeGenHLSL/debug/source-language.hlsl
+++ b/clang/test/CodeGenHLSL/debug/source-language.hlsl
@@ -5,24 +5,28 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -x hlsl -emit-llvm \
 // RUN:   -disable-llvm-passes -hlsl-entry main \
 // RUN:   -debug-info-kind=standalone -dwarf-version=4 -o - %s \
+// RUN:   -fdx-no-source-metadata \
 // RUN:   | FileCheck %s --check-prefix=CHECK-V4
 
 // SPIR-V target, DWARFv4
 // RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -x hlsl -emit-llvm \
 // RUN:   -disable-llvm-passes -hlsl-entry main \
 // RUN:   -debug-info-kind=standalone -dwarf-version=4 -o - %s \
+// RUN:   -fdx-no-source-metadata \
 // RUN:   | FileCheck %s --check-prefix=CHECK-V4
 
 // DXIL target, DWARFv6
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -x hlsl -emit-llvm \
 // RUN:   -disable-llvm-passes -hlsl-entry main \
 // RUN:   -debug-info-kind=standalone -dwarf-version=6 -o - %s \
+// RUN:   -fdx-no-source-metadata \
 // RUN:   | FileCheck %s --check-prefix=CHECK-V6
 
 // SPIR-V target, DWARFv6
 // RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -x hlsl -emit-llvm \
 // RUN:   -disable-llvm-passes -hlsl-entry main \
 // RUN:   -debug-info-kind=standalone -dwarf-version=6 -o - %s \
+// RUN:   -fdx-no-source-metadata \
 // RUN:   | FileCheck %s --check-prefix=CHECK-V6
 
 // CHECK-V4: !DICompileUnit(language: DW_LANG_HLSL,
diff --git a/clang/test/CodeGenHLSL/dx-source-metadata-disabled.hlsl 
b/clang/test/CodeGenHLSL/dx-source-metadata-disabled.hlsl
new file mode 100644
index 0000000000000..aa26a49feeef6
--- /dev/null
+++ b/clang/test/CodeGenHLSL/dx-source-metadata-disabled.hlsl
@@ -0,0 +1,12 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm \
+// RUN:   -debug-info-kind=constructor -fdx-no-source-metadata -o - \
+// RUN:   -disable-llvm-passes %s | FileCheck %s
+
+// CHECK-NOT: !dx.source.contents
+// CHECK-NOT: !dx.source.defines
+// CHECK-NOT: !dx.source.mainFileName
+// CHECK-NOT: !dx.source.args
+
+float foo(float a, float b) {
+  return a + b;
+}
diff --git a/clang/test/CodeGenHLSL/dx-source-metadata-includes.hlsl 
b/clang/test/CodeGenHLSL/dx-source-metadata-includes.hlsl
new file mode 100644
index 0000000000000..65861f5a50fdc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/dx-source-metadata-includes.hlsl
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm \
+// RUN:   -debug-info-kind=constructor -I %S/Inputs -isystem %S/SysInputs \
+// RUN:   -o - %s | FileCheck %s
+
+#include "a.hlsl"
+#include "a.hlsl"
+#include <c.hlsl>
+
+// The main file appears first in dx.source.contents; included files are
+// appended in sorted order afterwards. Duplicate and system includes are 
ignored.
+
+// CHECK: !dx.source.contents = !{![[MAIN:[0-9]+]], ![[H1:[0-9]+]], 
![[H2:[0-9]+]]}
+// CHECK: !dx.source.mainFileName = !{![[MAIN_FILE_NAME:[0-9]+]]}
+// CHECK: ![[MAIN]] = !{!"{{.*[\\/]dx-source-metadata-includes.hlsl}}", 
!"{{.*}}"}
+// CHECK: ![[H1]] = !{!"{{.*[\\/]a.hlsl}}", !"{{.*}}"}
+// CHECK: ![[H2]] = !{!"{{.*[\\/]b.hlsl}}", !"{{.*}}"}
+// CHECK: ![[MAIN_FILE_NAME]] = 
!{!"{{.*[\\/]dx-source-metadata-includes.hlsl}}"}
+
+float foo(float a, float b) {
+  return helper1_add(a, b) + helper2_mul(a, b) + sys_helper(a, b);
+}
diff --git 
a/clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl 
b/clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl
new file mode 100644
index 0000000000000..b6557503ab118
--- /dev/null
+++ b/clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl
@@ -0,0 +1,10 @@
+// RUN: not %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl 
-emit-llvm \
+// RUN:   -debug-info-kind=constructor \
+// RUN:   -fdx-record-command-line "clang_dxc \\" \
+// RUN:   -o - %s 2>&1 | FileCheck %s
+
+// CHECK: error: invalid escaped command line: only escaped backslashes and 
spaces are supported
+
+float foo(float a, float b) {
+  return a + b;
+}
diff --git a/clang/test/CodeGenHLSL/dx-source-metadata.hlsl 
b/clang/test/CodeGenHLSL/dx-source-metadata.hlsl
new file mode 100644
index 0000000000000..c523f36cc69b6
--- /dev/null
+++ b/clang/test/CodeGenHLSL/dx-source-metadata.hlsl
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm \
+// RUN:   -debug-info-kind=constructor -DUSER_DEF0=42 -DUSER_DEF1=43 
-UUSER_DEF1 \
+// RUN:   -fdx-record-command-line "clang_dxc -g -Tlib_6_3 -DUSER_DEF0=42 
-DUSER_DEF1=43 C:\\\\dx-source-metadata.hlsl" \
+// RUN:   -o - %s | FileCheck %s
+
+// RUN: cat %s | %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl 
-emit-llvm \
+// RUN:   -debug-info-kind=constructor \
+// RUN:   -fdx-record-command-line "clang_dxc -g -Tlib_6_3 
C:\\\\dx-source-metadata.hlsl" \
+// RUN:   -o - | FileCheck %s --check-prefix=CHECK-FROM-PIPE
+
+// CHECK: !dx.source.contents = !{![[CONTENTS:[0-9]+]]}
+// CHECK: !dx.source.defines = !{![[DEFINES:[0-9]+]]}
+// CHECK: !dx.source.mainFileName = !{![[MAIN:[0-9]+]]}
+// CHECK: !dx.source.args = !{![[ARGS:[0-9]+]]}
+
+// CHECK: ![[CONTENTS]] = !{!"{{.*[\\/]dx-source-metadata.hlsl}}", !"{{.*}}"}
+// CHECK: ![[DEFINES]] = !{!"USER_DEF0=42", !"USER_DEF1=43"}
+// CHECK: ![[MAIN]] = !{!"{{.*[\\/]dx-source-metadata.hlsl}}"}
+// CHECK: ![[ARGS]] = !{!"-g", !"-Tlib_6_3", !"-DUSER_DEF0=42", 
!"-DUSER_DEF1=43", !"C:\\dx-source-metadata.hlsl"}
+
+// CHECK-FROM-PIPE: !dx.source.contents = !{![[CONTENTS:[0-9]+]]}
+// CHECK-FROM-PIPE: !dx.source.mainFileName = !{![[MAIN:[0-9]+]]}
+// CHECK-FROM-PIPE: ![[CONTENTS]] = !{!"<stdin>", !"{{.*}}"}
+// CHECK-FROM-PIPE: ![[MAIN]] = !{!"<stdin>"}
+
+float foo(float a, float b) {
+  return a + b;
+}
diff --git a/clang/test/CodeGenHLSL/lit.local.cfg 
b/clang/test/CodeGenHLSL/lit.local.cfg
index 0604d1a83dc68..378a42883c9df 100644
--- a/clang/test/CodeGenHLSL/lit.local.cfg
+++ b/clang/test/CodeGenHLSL/lit.local.cfg
@@ -1 +1,2 @@
 config.suffixes = [".c", ".hlsl"]
+config.excludes.add("SysInputs")
diff --git a/clang/test/Driver/dxc_debug.hlsl b/clang/test/Driver/dxc_debug.hlsl
index 0eab32168982b..d250e95a0bfae 100644
--- a/clang/test/Driver/dxc_debug.hlsl
+++ b/clang/test/Driver/dxc_debug.hlsl
@@ -1,4 +1,4 @@
-// RUN: %clang_dxc -Tlib_6_7 -### -g %s 2>&1 | FileCheck %s
+// RUN: %clang_dxc -Tlib_6_7 -### -g %s 2>&1 | FileCheck %s 
--check-prefix=CHECK,CHECK-CMD
 // RUN: %clang_dxc -Tlib_6_7 -### /Zi %s 2>&1 | FileCheck %s
 // RUN: %clang_dxc -Tlib_6_7 -### /Zi /Qembed_debug %s 2>&1 | FileCheck %s
 // RUN: %clang_dxc -Tlib_6_7 -### -Zi %s 2>&1 | FileCheck %s
@@ -13,3 +13,6 @@
 // CHECK-SAME: "-debug-info-kind=constructor"
 // Make sure dwarf-version is 4.
 // CHECK-DWARF-SAME: -dwarf-version=4
+// Make sure dxc command line arguments are passed to clang invocation.
+// CHECK-SAME: -fdx-record-command-line
+// CHECK-CMD-SAME: --driver-mode=dxc -T lib_6_7 -### -g {{.*}}dxc_debug.hlsl
diff --git a/clang/unittests/Driver/CMakeLists.txt 
b/clang/unittests/Driver/CMakeLists.txt
index fa0e87c3318df..fadaacf511291 100644
--- a/clang/unittests/Driver/CMakeLists.txt
+++ b/clang/unittests/Driver/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_clang_unittest(ClangDriverTests
   DistroTest.cpp
   DXCModeTest.cpp
+  EscapedCommandLineTest.cpp
   GCCVersionTest.cpp
   ToolChainTest.cpp
   ModuleCacheTest.cpp
@@ -11,6 +12,7 @@ add_clang_unittest(ClangDriverTests
   clangDriver
   clangBasic
   clangFrontend # For TextDiagnosticPrinter.
+  clangOptions
   clangSerialization
   LLVM_COMPONENTS
   ${LLVM_TARGETS_TO_BUILD}
diff --git a/clang/unittests/Driver/EscapedCommandLineTest.cpp 
b/clang/unittests/Driver/EscapedCommandLineTest.cpp
new file mode 100644
index 0000000000000..bf4cffdf742d0
--- /dev/null
+++ b/clang/unittests/Driver/EscapedCommandLineTest.cpp
@@ -0,0 +1,135 @@
+//===- unittests/Driver/EscapedCommandLineTest.cpp 
------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Unit tests for -record-command-line.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Driver/CommonArgs.h"
+#include "clang/Options/OptionUtils.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/iterator_range.h"
+#include "gtest/gtest.h"
+
+using namespace clang::driver::tools;
+using namespace llvm;
+
+using ArgStr = SmallString<8>;
+using ArgVec = SmallVector<ArgStr>;
+
+static ArgStr escape(const char *Arg) {
+  ArgStr Res;
+  escapeSpacesAndBackslashes(Arg, Res);
+  return Res;
+}
+
+TEST(EscapedCommandLineTest, EscapeEmpty) { EXPECT_EQ(escape(""), ""); }
+
+TEST(EscapedCommandLineTest, EscapeNoSpecialChars) {
+  EXPECT_EQ(escape("hello"), "hello");
+  EXPECT_EQ(escape("-Tlib_6_3"), "-Tlib_6_3");
+}
+
+TEST(EscapedCommandLineTest, EscapeSpace) {
+  EXPECT_EQ(escape("foo bar"), "foo\\ bar");
+  EXPECT_EQ(escape(" leading"), "\\ leading");
+  EXPECT_EQ(escape("trailing "), "trailing\\ ");
+}
+
+TEST(EscapedCommandLineTest, EscapeBackslash) {
+  EXPECT_EQ(escape("a\\b"), "a\\\\b");
+}
+
+TEST(EscapedCommandLineTest, EscapeSpaceAndBackslash) {
+  EXPECT_EQ(escape("a\\ b"), "a\\\\\\ b");
+}
+
+static ArgVec parse(const char *CommandLine) {
+  ArgVec Res;
+  auto ParsedArgs = clang::parseEscapedCommandLine(CommandLine);
+  if (!ParsedArgs) {
+    ADD_FAILURE() << llvm::toString(ParsedArgs.takeError());
+    return Res;
+  }
+  for (const auto &Arg : *ParsedArgs)
+    Res.emplace_back(Arg.begin(), Arg.end());
+  return Res;
+}
+
+TEST(EscapedCommandLineTest, ParseEmpty) { EXPECT_TRUE(parse("").empty()); }
+
+TEST(EscapedCommandLineTest, ParseSingleArg) {
+  EXPECT_EQ(parse("hello"), ArgVec({StringRef("hello")}));
+}
+
+TEST(EscapedCommandLineTest, ParseMultipleArgs) {
+  auto Args = parse("clang -Tlib_6_3 foo.hlsl");
+  ASSERT_EQ(Args.size(), 3u);
+  EXPECT_EQ(Args[0], "clang");
+  EXPECT_EQ(Args[1], "-Tlib_6_3");
+  EXPECT_EQ(Args[2], "foo.hlsl");
+}
+
+TEST(EscapedCommandLineTest, ParseEscapedSpace) {
+  auto Args = parse("foo\\ bar baz");
+  ASSERT_EQ(Args.size(), 2u);
+  EXPECT_EQ(Args[0], "foo bar");
+  EXPECT_EQ(Args[1], "baz");
+}
+
+TEST(EscapedCommandLineTest, ParseEscapedBackslash) {
+  auto Args = parse("a\\\\b");
+  ASSERT_EQ(Args.size(), 1u);
+  EXPECT_EQ(Args[0], "a\\b");
+}
+
+TEST(EscapedCommandLineTest, ParseInvalidEscape) {
+  auto Args = clang::parseEscapedCommandLine("clang \\");
+  ASSERT_FALSE(Args);
+  EXPECT_EQ(llvm::toString(Args.takeError()),
+            "only escaped backslashes and spaces are supported");
+}
+
+static ArgVec roundTrip(ArgVec Args) {
+  SmallString<256> Joined;
+  escapeSpacesAndBackslashes(Args.begin()->c_str(), Joined);
+  for (auto &Arg : llvm::make_range(Args.begin() + 1, Args.end())) {
+    Joined += " ";
+    escapeSpacesAndBackslashes(Arg.c_str(), Joined);
+  }
+  return parse(Joined.c_str());
+}
+
+TEST(EscapedCommandLineTest, RoundTripSimple) {
+  ArgVec Args;
+  Args.emplace_back("clang");
+  Args.emplace_back("-O2");
+  Args.emplace_back("foo.cpp");
+  EXPECT_EQ(roundTrip(Args), Args);
+}
+
+TEST(EscapedCommandLineTest, RoundTripArgWithSpace) {
+  ArgVec Args;
+  Args.emplace_back("clang");
+  Args.emplace_back("path with spaces/file.cpp");
+  EXPECT_EQ(roundTrip(Args), Args);
+}
+
+TEST(EscapedCommandLineTest, RoundTripArgWithBackslash) {
+  ArgVec Args;
+  Args.emplace_back("clang");
+  Args.emplace_back("C:\\path\\file.cpp");
+  EXPECT_EQ(roundTrip(Args), Args);
+}
+
+TEST(EscapedCommandLineTest, RoundTripArgWithSpaceAndBackslash) {
+  ArgVec Args;
+  Args.emplace_back("clang");
+  Args.emplace_back("C:\\path with space\\file.cpp");
+  EXPECT_EQ(roundTrip(Args), Args);
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to