[clang] [Clang][HLSL][DebugInfo] Emit dx.source metadata nodes (PR #199689)
Vladislav Dzhidzhoev via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 15 04:11:24 PDT 2026
https://github.com/dzhidzhoev updated https://github.com/llvm/llvm-project/pull/199689
>From e7234d0a54ec697c5bfea7f75164b44c9d8c4a1c Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Mon, 20 Apr 2026 01:39:58 +0200
Subject: [PATCH 1/3] [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 073daeeb4b87a..5f3baf771ff96 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -521,6 +521,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 the x64 Windows unwind info version.
ENUM_CODEGENOPT(WinX64EHUnwind, WinX64EHUnwindMode, 3,
WinX64EHUnwindMode::Default, Benign)
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 835ea728e9c56..ac801f6e598f1 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 cdadb824a8ac3..14a83ff7beae4 100644
--- a/clang/include/clang/Driver/CommonArgs.h
+++ b/clang/include/clang/Driver/CommonArgs.h
@@ -280,8 +280,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 b4447fcc04120..7af5f3fc6532f 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -9719,6 +9719,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 98e7b70d46672..784d4ae4e92f3 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 "llvm/Transforms/Utils/ModuleUtils.h"
#include <cstdint>
#include <optional>
@@ -190,6 +194,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();
@@ -749,6 +856,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 75b2f5826f863..94f3351a8b102 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -174,5 +174,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 c2ac478d84929..312e8f8c69f0a 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8091,7 +8091,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");
@@ -8101,6 +8103,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 2267d74ee7d58..48724746d9330 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -3350,7 +3350,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();
@@ -3361,13 +3362,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 fcbbcbcc7018b..629f56d6cf4a4 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -1299,7 +1299,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);
+}
>From cfa480eb8ec9d3e9da05500d95f6cb36faee8536 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Mon, 8 Jun 2026 21:39:44 +0200
Subject: [PATCH 2/3] Do not use custom diagnostics
---
clang/include/clang/Basic/DiagnosticDriverKinds.td | 2 ++
clang/include/clang/Basic/DiagnosticFrontendKinds.td | 3 +++
clang/lib/CodeGen/CGHLSLRuntime.cpp | 12 +++++-------
3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index 8046db82d9ea8..6aba0622be193 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -352,6 +352,8 @@ def err_drv_too_many_actions: Error<
"only one action option is allowed. Got %0">;
def err_drv_invalid_value : Error<"invalid value '%1' in '%0'">;
def err_drv_invalid_int_value : Error<"invalid integral value '%1' in '%0'">;
+def err_drv_invalid_escaped_command_line
+ : Error<"invalid escaped command line: %0">;
def err_drv_invalid_value_with_suggestion : Error<
"invalid value '%1' in '%0', expected one of: %2">;
def err_drv_alignment_not_power_of_two : Error<"alignment is not a power of 2 in '%0'">;
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 322ce2c3a75fd..058449ef47a46 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -458,6 +458,9 @@ def warn_hlsl_langstd_minimal :
Warning<"support for HLSL language version %0 is incomplete, "
"recommend using %1 instead">,
InGroup<HLSLDXCCompat>;
+def warn_hlsl_failed_to_embed_source
+ : Warning<"failed to embed source for '%0' into dx.source.contents">,
+ InGroup<HLSLDXCCompat>;
def err_data_layout_mismatch : Error<"backend data layout '%0' does not match "
"expected target description '%1'">;
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 784d4ae4e92f3..00b87845e1732 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -26,6 +26,7 @@
#include "clang/AST/HLSLResource.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
+#include "clang/Basic/DiagnosticDriver.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetOptions.h"
@@ -238,10 +239,8 @@ void addSourceInfo(CodeGenModule &CGM, llvm::Module &M) {
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;
+ SM.getDiagnostics().Report(diag::warn_hlsl_failed_to_embed_source)
+ << Path;
continue;
}
@@ -283,9 +282,8 @@ void addSourceInfo(CodeGenModule &CGM, llvm::Module &M) {
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());
+ CGM.getDiags().Report(diag::err_drv_invalid_escaped_command_line)
+ << llvm::toString(ParsedArgs.takeError());
return;
}
SmallVector<llvm::Metadata *> Args;
>From 5957fc6e0d77bd9dd7b6ee69fc244b44fec548c4 Mon Sep 17 00:00:00 2001
From: Vladislav Dzhidzhoev <vdzhidzhoev at accesssoftek.com>
Date: Tue, 9 Jun 2026 01:21:22 +0200
Subject: [PATCH 3/3] Parse recorded command line in Frontend. Remove
dependency of CodeGen from Options
---
clang/include/clang/Basic/CodeGenOptions.h | 4 ++++
clang/lib/CodeGen/CGHLSLRuntime.cpp | 19 ++++++-------------
clang/lib/CodeGen/CMakeLists.txt | 1 -
clang/lib/Frontend/CompilerInvocation.cpp | 10 ++++++++++
...urce-metadata-mailformed-command-line.hlsl | 6 ++++++
5 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index ac801f6e598f1..97d68877467fd 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -272,6 +272,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// if non-empty.
std::string HLSLRecordCommandLine;
+ /// The vector contains parsed commandline for the dx.source.args metadata,
+ /// if parsing was successful.
+ llvm::SmallVector<llvm::SmallString<8>> HLSLParsedCommandLine;
+
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/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 00b87845e1732..40b29559c2a1c 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -30,7 +30,6 @@
#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"
@@ -198,6 +197,7 @@ findAssociatedResourceDeclForStruct(ASTContext &AST, const MemberExpr *ME) {
void addSourceInfo(CodeGenModule &CGM, llvm::Module &M) {
auto &SM = CGM.getContext().getSourceManager();
auto &Macros = CGM.getPreprocessorOpts().Macros;
+ auto &CodeGenOpts = CGM.getCodeGenOpts();
auto &Ctx = M.getContext();
// Names and content of shader source code files.
@@ -273,23 +273,16 @@ void addSourceInfo(CodeGenModule &CGM, llvm::Module &M) {
M.getOrInsertNamedMetadata("dx.source.defines")
->addOperand(llvm::MDNode::get(Ctx, Defines));
- if (!CGM.getCodeGenOpts().MainFileName.empty())
- llvm::sys::path::native(CGM.getCodeGenOpts().MainFileName, *MainFileName);
+ if (!CodeGenOpts.MainFileName.empty())
+ llvm::sys::path::native(CodeGenOpts.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) {
- CGM.getDiags().Report(diag::err_drv_invalid_escaped_command_line)
- << 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.reserve(CodeGenOpts.HLSLParsedCommandLine.size());
+ if (!CodeGenOpts.HLSLParsedCommandLine.empty())
+ for (const auto &Arg : llvm::drop_begin(CodeGenOpts.HLSLParsedCommandLine))
Args.push_back(llvm::MDString::get(Ctx, Arg));
M.getOrInsertNamedMetadata("dx.source.args")
->addOperand(llvm::MDNode::get(Ctx, Args));
diff --git a/clang/lib/CodeGen/CMakeLists.txt b/clang/lib/CodeGen/CMakeLists.txt
index 94f3351a8b102..75b2f5826f863 100644
--- a/clang/lib/CodeGen/CMakeLists.txt
+++ b/clang/lib/CodeGen/CMakeLists.txt
@@ -174,6 +174,5 @@ add_clang_library(clangCodeGen
clangBasic
clangFrontend
clangLex
- clangOptions
clangSerialization
)
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 47cdcad377d06..c564748ddabc1 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2327,6 +2327,16 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
Opts.StaticClosure = Args.hasArg(options::OPT_static_libclosure);
+ if (!Opts.HLSLRecordCommandLine.empty()) {
+ auto ParsedArgs =
+ clang::parseEscapedCommandLine(Opts.HLSLRecordCommandLine.c_str());
+ if (!ParsedArgs)
+ Diags.Report(diag::err_drv_invalid_escaped_command_line)
+ << llvm::toString(ParsedArgs.takeError());
+ else
+ Opts.HLSLParsedCommandLine = std::move(*ParsedArgs);
+ }
+
return Diags.getNumErrors() == NumErrorsBefore;
}
diff --git a/clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl b/clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl
index b6557503ab118..8d964aaf573b4 100644
--- a/clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl
+++ b/clang/test/CodeGenHLSL/dx-source-metadata-mailformed-command-line.hlsl
@@ -3,6 +3,12 @@
// RUN: -fdx-record-command-line "clang_dxc \\" \
// RUN: -o - %s 2>&1 | FileCheck %s
+// 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: -fsyntax-only \
+// 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) {
More information about the cfe-commits
mailing list