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+
251334static 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+
728910FileRunCommandResult 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