[Clang][HLSL][DebugInfo] Emit dx.source metadata nodes (#199689)
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. These 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 dxc 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.
This commit is contained in:
parent
8717831a8c
commit
63c9264c77
25 changed files with 433 additions and 7 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -268,6 +268,14 @@ public:
|
|||
/// if non-empty.
|
||||
std::string RecordCommandLine;
|
||||
|
||||
/// The string containing the commandline for the dx.source.args metadata,
|
||||
/// 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
|
||||
|
|
|
|||
|
|
@ -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'">;
|
||||
|
|
|
|||
|
|
@ -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'">;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -9723,6 +9723,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]>,
|
||||
|
|
|
|||
|
|
@ -26,9 +26,12 @@
|
|||
#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"
|
||||
#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,100 @@ 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 &CodeGenOpts = CGM.getCodeGenOpts();
|
||||
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) {
|
||||
SM.getDiagnostics().Report(diag::warn_hlsl_failed_to_embed_source)
|
||||
<< 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 (!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)));
|
||||
|
||||
SmallVector<llvm::Metadata *> Args;
|
||||
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));
|
||||
}
|
||||
|
||||
// Find array variable declaration from DeclRef expression
|
||||
static const ValueDecl *getArrayDecl(ASTContext &AST, const Expr *E) {
|
||||
E = E->IgnoreImpCasts();
|
||||
|
|
@ -749,6 +847,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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
10
clang/test/CodeGenHLSL/Inputs/a.hlsl
Normal file
10
clang/test/CodeGenHLSL/Inputs/a.hlsl
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef GUARD
|
||||
#define GUARD
|
||||
|
||||
#include "b.hlsl"
|
||||
|
||||
float helper1_add(float a, float b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
#endif
|
||||
3
clang/test/CodeGenHLSL/Inputs/b.hlsl
Normal file
3
clang/test/CodeGenHLSL/Inputs/b.hlsl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
float helper2_mul(float a, float b) {
|
||||
return a * b;
|
||||
}
|
||||
3
clang/test/CodeGenHLSL/SysInputs/c.hlsl
Normal file
3
clang/test/CodeGenHLSL/SysInputs/c.hlsl
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
float sys_helper(float a, float b) {
|
||||
return a * b;
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
12
clang/test/CodeGenHLSL/dx-source-metadata-disabled.hlsl
Normal file
12
clang/test/CodeGenHLSL/dx-source-metadata-disabled.hlsl
Normal file
|
|
@ -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;
|
||||
}
|
||||
21
clang/test/CodeGenHLSL/dx-source-metadata-includes.hlsl
Normal file
21
clang/test/CodeGenHLSL/dx-source-metadata-includes.hlsl
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// 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
|
||||
|
||||
// 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) {
|
||||
return a + b;
|
||||
}
|
||||
28
clang/test/CodeGenHLSL/dx-source-metadata.hlsl
Normal file
28
clang/test/CodeGenHLSL/dx-source-metadata.hlsl
Normal file
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
config.suffixes = [".c", ".hlsl"]
|
||||
config.excludes.add("SysInputs")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
135
clang/unittests/Driver/EscapedCommandLineTest.cpp
Normal file
135
clang/unittests/Driver/EscapedCommandLineTest.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in a new issue