Skip to content

Commit 402b54e

Browse files
committed
FileChecker improvements: VFS, %dxl, FileCheck -D, error reporting (#3576)
- VFS captures output files for duration of test, enabling: - %dxl test IDxcLinker - add -D to FileCheck args to supply defined variables - report failing RUN command when not consumed by FileCheck or XFail
1 parent 8f0184a commit 402b54e

2 files changed

Lines changed: 225 additions & 8 deletions

File tree

include/dxc/Test/DxcTestUtils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ class FileCheckForTest {
6060
int Run();
6161
};
6262

63+
// wstring because most uses need UTF-16: IDxcResult output names, include handler
64+
typedef std::map<std::wstring, CComPtr<IDxcBlob>> FileMap;
65+
6366
// The result of running a single command in a run pipeline
6467
struct FileRunCommandResult {
6568
CComPtr<IDxcOperationResult> OpResult; // The operation result, if any.
@@ -109,6 +112,7 @@ class FileRunCommandPart {
109112
std::string Command; // Command to run, eg %dxc
110113
std::string Arguments; // Arguments to command
111114
LPCWSTR CommandFileName; // File name replacement for %s
115+
FileMap *pVFS = nullptr; // Files in virtual file system
112116

113117
private:
114118
FileRunCommandResult RunFileChecker(const FileRunCommandResult *Prior, LPCWSTR dumpName = nullptr);
@@ -117,6 +121,7 @@ class FileRunCommandPart {
117121
FileRunCommandResult RunOpt(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior);
118122
FileRunCommandResult RunD3DReflect(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior);
119123
FileRunCommandResult RunDxr(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior);
124+
FileRunCommandResult RunLink(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior);
120125
FileRunCommandResult RunTee(const FileRunCommandResult *Prior);
121126
FileRunCommandResult RunXFail(const FileRunCommandResult *Prior);
122127
FileRunCommandResult RunDxilVer(dxc::DxcDllSupport& DllSupport, const FileRunCommandResult* Prior);

tools/clang/unittests/HLSLTestLib/FileCheckerTest.cpp

Lines changed: 220 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "dxc/dxctools.h"
4040
#include "dxc/Support/HLSLOptions.h"
4141
#include "dxc/Support/Unicode.h"
42+
#include "dxc/Support/microcom.h"
4243
#include "dxc/DxilContainer/DxilContainer.h"
4344
#include "dxc/Test/D3DReflectionDumper.h"
4445

@@ -107,6 +108,9 @@ FileRunCommandResult FileRunCommandPart::Run(dxc::DxcDllSupport &DllSupport, con
107108
else if (0 == _stricmp(Command.c_str(), "%dxr")) {
108109
return RunDxr(DllSupport, Prior);
109110
}
111+
else if (0 == _stricmp(Command.c_str(), "%dxl")) {
112+
return RunLink(DllSupport, Prior);
113+
}
110114
else if (pPluginToolsPaths != nullptr) {
111115
auto it = pPluginToolsPaths->find(Command.c_str());
112116
if (it != pPluginToolsPaths->end()) {
@@ -248,6 +252,85 @@ static HRESULT GetDxilBitcode(dxc::DxcDllSupport &DllSupport, IDxcBlob *pCompile
248252
return S_OK;
249253
}
250254

255+
// Simple virtual file system include handler for test, fall back to default include handler
256+
class IncludeHandlerVFSOverlayForTest : public IDxcIncludeHandler {
257+
private:
258+
DXC_MICROCOM_TM_REF_FIELDS()
259+
260+
public:
261+
DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL()
262+
DXC_MICROCOM_TM_CTOR(IncludeHandlerVFSOverlayForTest)
263+
264+
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
265+
return DoBasicQueryInterface<IDxcIncludeHandler>(this, iid, ppvObject);
266+
}
267+
268+
const FileMap *pVFS = nullptr;
269+
CComPtr<IDxcIncludeHandler> pInnerIncludeHandler;
270+
271+
HRESULT STDMETHODCALLTYPE LoadSource(
272+
_In_ LPCWSTR pFilename, // Candidate filename.
273+
_COM_Outptr_result_maybenull_ IDxcBlob **ppIncludeSource // Resultant source object for included file, nullptr if not found.
274+
) override {
275+
if (!ppIncludeSource)
276+
return E_INVALIDARG;
277+
*ppIncludeSource = nullptr;
278+
if (!pFilename)
279+
return E_INVALIDARG;
280+
try {
281+
if (pVFS) {
282+
auto it = pVFS->find(pFilename);
283+
if (it != pVFS->end()) {
284+
return it->second.QueryInterface(ppIncludeSource);
285+
}
286+
}
287+
if (pInnerIncludeHandler) {
288+
return pInnerIncludeHandler->LoadSource(pFilename, ppIncludeSource);
289+
}
290+
return E_FAIL;
291+
}
292+
CATCH_CPP_RETURN_HRESULT();
293+
}
294+
};
295+
296+
static IncludeHandlerVFSOverlayForTest *AllocVFSIncludeHandler(IUnknown *pUnkLibrary, const FileMap *pVFS) {
297+
CComPtr<IncludeHandlerVFSOverlayForTest> pVFSIncludeHandler = IncludeHandlerVFSOverlayForTest::Alloc(DxcGetThreadMallocNoRef());
298+
IFTBOOL(pVFSIncludeHandler, E_OUTOFMEMORY);
299+
if (pUnkLibrary) {
300+
CComPtr<IDxcIncludeHandler> pInnerIncludeHandler;
301+
CComPtr<IDxcUtils> pUtils;
302+
if (SUCCEEDED(pUnkLibrary->QueryInterface(IID_PPV_ARGS(&pUtils)))) {
303+
IFT(pUtils->CreateDefaultIncludeHandler(&pInnerIncludeHandler));
304+
} else {
305+
CComPtr<IDxcLibrary> pLibrary;
306+
if (SUCCEEDED(pUnkLibrary->QueryInterface(IID_PPV_ARGS(&pLibrary)))) {
307+
IFT(pLibrary->CreateIncludeHandler(&pInnerIncludeHandler));
308+
}
309+
}
310+
pVFSIncludeHandler->pInnerIncludeHandler = pInnerIncludeHandler;
311+
}
312+
pVFSIncludeHandler->pVFS = pVFS;
313+
return pVFSIncludeHandler.Detach();
314+
}
315+
316+
static void AddOutputsToFileMap(IUnknown *pUnkResult, FileMap *pVFS) {
317+
// If there is IDxcResult, save named output blobs to Files.
318+
if (pUnkResult && pVFS) {
319+
CComPtr<IDxcResult> pResult;
320+
if (SUCCEEDED(pUnkResult->QueryInterface(IID_PPV_ARGS(&pResult)))) {
321+
for (unsigned i = 0; i < pResult->GetNumOutputs(); i++) {
322+
CComPtr<IDxcBlob> pOutput;
323+
CComPtr<IDxcBlobUtf16> pOutputName;
324+
if (SUCCEEDED(pResult->GetOutput(pResult->GetOutputByIndex(i),
325+
IID_PPV_ARGS(&pOutput), &pOutputName)) &&
326+
pOutput && pOutputName && pOutputName->GetStringLength() > 0) {
327+
(*pVFS)[pOutputName->GetStringPointer()] = pOutput;
328+
}
329+
}
330+
}
331+
}
332+
}
333+
251334
static HRESULT CompileForHash(hlsl::options::DxcOpts &opts, LPCWSTR CommandFileName, dxc::DxcDllSupport &DllSupport, std::vector<LPCWSTR> &flags, IDxcBlob **ppHashBlob, std::string &output) {
252335
CComPtr<IDxcLibrary> pLibrary;
253336
CComPtr<IDxcCompiler> pCompiler;
@@ -466,13 +549,12 @@ FileRunCommandResult FileRunCommandPart::RunDxc(dxc::DxcDllSupport &DllSupport,
466549
CComPtr<IDxcBlobEncoding> pSource;
467550
CComPtr<IDxcBlobEncoding> pDisassembly;
468551
CComPtr<IDxcBlob> pCompiledBlob;
469-
CComPtr<IDxcIncludeHandler> pIncludeHandler;
470552

471553
HRESULT resultStatus;
472554

473555
IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
474556
IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
475-
IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
557+
CComPtr<IDxcIncludeHandler> pIncludeHandler = AllocVFSIncludeHandler(pLibrary, pVFS);
476558
IFT(DllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
477559
IFT(pCompiler->Compile(pSource, CommandFileName, entry.c_str(), profile.c_str(),
478560
flags.data(), flags.size(), nullptr, 0, pIncludeHandler, &pResult));
@@ -698,11 +780,10 @@ FileRunCommandResult FileRunCommandPart::RunDxr(dxc::DxcDllSupport &DllSupport,
698780
CComPtr<IDxcOperationResult> pResult;
699781
CComPtr<IDxcBlobEncoding> pSource;
700782
CComPtr<IDxcBlob> pResultBlob;
701-
CComPtr<IDxcIncludeHandler> pIncludeHandler;
702783

703784
IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
704785
IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
705-
IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
786+
CComPtr<IDxcIncludeHandler> pIncludeHandler = AllocVFSIncludeHandler(pLibrary, pVFS);
706787
IFT(DllSupport.CreateInstance(CLSID_DxcRewriter, &pRewriter));
707788
IFT(pRewriter->RewriteWithOptions(pSource, CommandFileName,
708789
flags.data(), flags.size(), nullptr, 0,
@@ -725,6 +806,107 @@ FileRunCommandResult FileRunCommandPart::RunDxr(dxc::DxcDllSupport &DllSupport,
725806
return result;
726807
}
727808

809+
FileRunCommandResult FileRunCommandPart::RunLink(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
810+
hlsl::options::MainArgs args;
811+
hlsl::options::DxcOpts opts;
812+
FileRunCommandResult readOptsResult = ReadOptsForDxc(args, opts,
813+
hlsl::options::HlslFlags::CoreOption);
814+
if (readOptsResult.ExitCode) return readOptsResult;
815+
816+
std::wstring entry =
817+
Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
818+
std::wstring profile =
819+
Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
820+
std::vector<LPCWSTR> flags;
821+
822+
// Skip targets that require a newer compiler or validator.
823+
// Some features may require newer compiler/validator than indicated by the
824+
// shader model, but these should use %dxilver explicitly.
825+
{
826+
unsigned RequiredDxilMajor = 1, RequiredDxilMinor = 0;
827+
llvm::StringRef stage;
828+
IFTBOOL(ParseTargetProfile(opts.TargetProfile, stage, RequiredDxilMajor, RequiredDxilMinor), E_INVALIDARG);
829+
if (RequiredDxilMinor != 0xF && stage.compare("rootsig") != 0) {
830+
// Convert stage to minimum dxil/validator version:
831+
RequiredDxilMajor = std::max(RequiredDxilMajor, (unsigned)6) - 5;
832+
FileRunCommandResult result = CheckDxilVer(DllSupport, RequiredDxilMajor, RequiredDxilMinor, !opts.DisableValidation);
833+
if (result.AbortPipeline) {
834+
return result;
835+
}
836+
}
837+
}
838+
839+
// For now, too many tests are sensitive to stripping the refleciton info
840+
// from the main module, so use this flag to prevent this until tests
841+
// can be updated.
842+
// That is, unless the test explicitly requests -Qstrip_reflect_from_dxil or -Qstrip_reflect
843+
if (!opts.StripReflectionFromDxil && !opts.StripReflection) {
844+
flags.push_back(L"-Qkeep_reflect_in_dxil");
845+
}
846+
847+
std::vector<std::wstring> argWStrings;
848+
CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
849+
for (const std::wstring &a : argWStrings)
850+
flags.push_back(a.data());
851+
852+
// Parse semicolon separated list of library names.
853+
llvm::StringRef optLibraries = opts.Args.getLastArgValue(hlsl::options::OPT_INPUT);
854+
auto libs_utf8 = strtok(optLibraries.str().c_str(), ";");
855+
std::vector<std::wstring> libs_utf16;
856+
for (auto name : libs_utf8)
857+
libs_utf16.emplace_back(Unicode::UTF8ToUTF16StringOrThrow(name.c_str()));
858+
std::vector<LPCWSTR> libNames;
859+
for (auto &name : libs_utf16)
860+
libNames.emplace_back(name.data());
861+
862+
CComPtr<IDxcLibrary> pLibrary;
863+
CComPtr<IDxcLinker> pLinker;
864+
CComPtr<IDxcCompiler> pCompiler;
865+
CComPtr<IDxcOperationResult> pResult;
866+
CComPtr<IDxcBlobEncoding> pDisassembly;
867+
CComPtr<IDxcBlob> pCompiledBlob;
868+
869+
HRESULT resultStatus;
870+
871+
IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
872+
CComPtr<IDxcIncludeHandler> pIncludeHandler = AllocVFSIncludeHandler(pLibrary, pVFS);
873+
IFT(DllSupport.CreateInstance(CLSID_DxcLinker, &pLinker));
874+
IFT(DllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
875+
876+
for (auto name : libNames) {
877+
CComPtr<IDxcBlob> pLibBlob;
878+
IFT(pIncludeHandler->LoadSource(name, &pLibBlob));
879+
IFT(pLinker->RegisterLibrary(name, pLibBlob));
880+
}
881+
882+
IFT(pLinker->Link(entry.c_str(), profile.c_str(), libNames.data(),
883+
libNames.size(), flags.data(), flags.size(), &pResult));
884+
IFT(pResult->GetStatus(&resultStatus));
885+
886+
FileRunCommandResult result = {};
887+
if (SUCCEEDED(resultStatus)) {
888+
IFT(pResult->GetResult(&pCompiledBlob));
889+
if (!opts.AstDump) {
890+
IFT(pCompiler->Disassemble(pCompiledBlob, &pDisassembly));
891+
result.StdOut = BlobToUtf8(pDisassembly);
892+
} else {
893+
result.StdOut = BlobToUtf8(pCompiledBlob);
894+
}
895+
CComPtr<IDxcBlobEncoding> pStdErr;
896+
IFT(pResult->GetErrorBuffer(&pStdErr));
897+
result.StdErr = BlobToUtf8(pStdErr);
898+
result.ExitCode = 0;
899+
}
900+
else {
901+
IFT(pResult->GetErrorBuffer(&pDisassembly));
902+
result.StdErr = BlobToUtf8(pDisassembly);
903+
result.ExitCode = resultStatus;
904+
}
905+
906+
result.OpResult = pResult;
907+
return result;
908+
}
909+
728910
FileRunCommandResult FileRunCommandPart::RunTee(const FileRunCommandResult *Prior) {
729911
if (Prior == nullptr) {
730912
return FileRunCommandResult::Error("tee requires a prior command");
@@ -938,6 +1120,8 @@ class FileRunTestResultImpl : public FileRunTestResult {
9381120
dxc::DxcDllSupport &m_support;
9391121
PluginToolsPaths *m_pPluginToolsPaths;
9401122
LPCWSTR m_dumpName = nullptr;
1123+
// keep track of virtual files for duration of this test (for all RUN lines)
1124+
FileMap Files;
9411125

9421126
void RunHashTestFromCommands(LPCSTR commands, LPCWSTR fileName) {
9431127
std::vector<FileRunCommandPart> parts;
@@ -967,13 +1151,38 @@ class FileRunTestResultImpl : public FileRunTestResult {
9671151
this->ErrorMessage = "FileCheck found no commands to run";
9681152
return;
9691153
}
970-
9711154
FileRunCommandResult result;
972-
FileRunCommandResult* previousResult = nullptr;
1155+
FileRunCommandResult *previousResult = nullptr;
1156+
FileRunCommandPart *pPrior = nullptr;
9731157
for (FileRunCommandPart & part : parts) {
1158+
int priorExitCode = result.ExitCode;
1159+
part.pVFS = &Files;
9741160
result = part.Run(m_support, previousResult, m_pPluginToolsPaths, dumpName);
1161+
1162+
// If there is IDxcResult, save named output blobs to Files.
1163+
AddOutputsToFileMap(result.OpResult, &Files);
1164+
1165+
// When current failing stage is FileCheck, print prior command,
1166+
// as well as FileCheck command that failed, to help identify
1167+
// failing commands in longer run chains.
1168+
if (result.ExitCode &&
1169+
(0 == _stricmp(part.Command.c_str(), "FileCheck") ||
1170+
0 == _stricmp(part.Command.c_str(), "%FileCheck"))) {
1171+
std::ostringstream oss;
1172+
if (pPrior) {
1173+
oss << "Prior (" << priorExitCode << "): "
1174+
<< pPrior->Command << pPrior->Arguments << endl;
1175+
}
1176+
oss << "Error (" << result.ExitCode << "): "
1177+
<< part.Command << part.Arguments << endl;
1178+
oss << result.StdErr;
1179+
result.StdErr = oss.str();
1180+
}
1181+
1182+
if (result.AbortPipeline)
1183+
break;
9751184
previousResult = &result;
976-
if (result.AbortPipeline) break;
1185+
pPrior = &part;
9771186
}
9781187

9791188
this->RunResult = result.ExitCode;
@@ -1001,7 +1210,10 @@ class FileRunTestResultImpl : public FileRunTestResult {
10011210
RunFileCheckFromCommands(cmd.c_str(), fileName, dumpName);
10021211
// If any of the RUN cmd fails then skip executing remaining cmds
10031212
// and report the error
1004-
if (this->RunResult != 0) break;
1213+
if (this->RunResult != 0) {
1214+
this->ErrorMessage = cmd + "\n" + this->ErrorMessage;
1215+
break;
1216+
}
10051217
runIdx += 1;
10061218
}
10071219
}

0 commit comments

Comments
 (0)