FileCheckerTest.cpp 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // FileCheckerTest.cpp //
  4. // Copyright (C) Microsoft Corporation. All rights reserved. //
  5. // This file is distributed under the University of Illinois Open Source //
  6. // License. See LICENSE.TXT for details. //
  7. // //
  8. // Provides tests that are based on FileChecker. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #ifndef UNICODE
  12. #define UNICODE
  13. #endif
  14. #include <memory>
  15. #include <vector>
  16. #include <string>
  17. #include <cctype>
  18. #include <cassert>
  19. #include <algorithm>
  20. #include "dxc/Support/WinIncludes.h"
  21. #include "dxc/dxcapi.h"
  22. #ifdef _WIN32
  23. #include <atlfile.h>
  24. #endif
  25. #include "dxc/Test/HlslTestData.h"
  26. #include "dxc/Test/HlslTestUtils.h"
  27. #include "dxc/Test/DxcTestUtils.h"
  28. #include "llvm/Support/raw_os_ostream.h"
  29. #include "llvm/Support/MD5.h"
  30. #include "llvm/Support/FileSystem.h"
  31. #include "llvm/Support/Path.h"
  32. #include "llvm/Support/raw_ostream.h"
  33. #include "dxc/Support/Global.h"
  34. #include "dxc/Support/dxcapi.use.h"
  35. #include "dxc/dxctools.h"
  36. #include "dxc/Support/HLSLOptions.h"
  37. #include "dxc/Support/Unicode.h"
  38. #include "dxc/Support/microcom.h"
  39. #include "dxc/DxilContainer/DxilContainer.h"
  40. #include "dxc/Test/D3DReflectionDumper.h"
  41. #include "d3d12shader.h"
  42. using namespace std;
  43. using namespace hlsl_test;
  44. using namespace refl_dump;
  45. FileRunCommandPart::FileRunCommandPart(const std::string &command, const std::string &arguments, LPCWSTR commandFileName) :
  46. Command(command), Arguments(arguments), CommandFileName(commandFileName) { }
  47. FileRunCommandResult FileRunCommandPart::RunHashTests(dxc::DxcDllSupport &DllSupport) {
  48. if (0 == _stricmp(Command.c_str(), "%dxc")) {
  49. return RunDxcHashTest(DllSupport);
  50. }
  51. else {
  52. return FileRunCommandResult::Success();
  53. }
  54. }
  55. FileRunCommandResult FileRunCommandPart::Run(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior,
  56. PluginToolsPaths *pPluginToolsPaths /*=nullptr*/,
  57. LPCWSTR dumpName /*=nullptr*/) {
  58. bool isFileCheck =
  59. 0 == _stricmp(Command.c_str(), "FileCheck") ||
  60. 0 == _stricmp(Command.c_str(), "%FileCheck");
  61. bool isXFail = 0 == _stricmp(Command.c_str(), "xfail");
  62. bool consumeErrors = isFileCheck || isXFail;
  63. // Stop the pipeline if on errors unless the command can consume them.
  64. if (Prior != nullptr && Prior->ExitCode && !consumeErrors) {
  65. FileRunCommandResult result = *Prior;
  66. result.AbortPipeline = true;
  67. return result;
  68. }
  69. // We would add support for 'not' and 'llc' here.
  70. if (isFileCheck) {
  71. return RunFileChecker(Prior, dumpName);
  72. }
  73. else if (isXFail) {
  74. return RunXFail(Prior);
  75. }
  76. else if (0 == _stricmp(Command.c_str(), "tee")) {
  77. return RunTee(Prior);
  78. }
  79. else if (0 == _stricmp(Command.c_str(), "fc")) {
  80. return RunFileCompareText(Prior);
  81. }
  82. else if (0 == _stricmp(Command.c_str(), "%dxilver")) {
  83. return RunDxilVer(DllSupport, Prior);
  84. }
  85. else if (0 == _stricmp(Command.c_str(), "%dxc")) {
  86. return RunDxc(DllSupport, Prior);
  87. }
  88. else if (0 == _stricmp(Command.c_str(), "%dxv")) {
  89. return RunDxv(DllSupport, Prior);
  90. }
  91. else if (0 == _stricmp(Command.c_str(), "%opt")) {
  92. return RunOpt(DllSupport, Prior);
  93. }
  94. else if (0 == _stricmp(Command.c_str(), "%D3DReflect")) {
  95. return RunD3DReflect(DllSupport, Prior);
  96. }
  97. else if (0 == _stricmp(Command.c_str(), "%dxr")) {
  98. return RunDxr(DllSupport, Prior);
  99. }
  100. else if (0 == _stricmp(Command.c_str(), "%dxl")) {
  101. return RunLink(DllSupport, Prior);
  102. }
  103. else if (pPluginToolsPaths != nullptr) {
  104. auto it = pPluginToolsPaths->find(Command.c_str());
  105. if (it != pPluginToolsPaths->end()) {
  106. return RunFromPath(it->second, Prior);
  107. }
  108. }
  109. FileRunCommandResult result {};
  110. result.ExitCode = 1;
  111. result.StdErr = "Unrecognized command ";
  112. result.StdErr += Command;
  113. return result;
  114. }
  115. FileRunCommandResult FileRunCommandPart::RunFileChecker(const FileRunCommandResult *Prior, LPCWSTR dumpName /*=nullptr*/) {
  116. if (!Prior) return FileRunCommandResult::Error("Prior command required to generate stdin");
  117. FileCheckForTest t;
  118. t.CheckFilename = CW2A(CommandFileName, CP_UTF8);
  119. t.InputForStdin = Prior->ExitCode ? Prior->StdErr : Prior->StdOut;
  120. // Parse command arguments
  121. static constexpr char checkPrefixStr[] = "-check-prefix=";
  122. static constexpr char checkPrefixesStr[] = "-check-prefixes=";
  123. static constexpr char defineStr[] = "-D";
  124. bool hasInputFilename = false;
  125. auto args = strtok(Arguments);
  126. for (const std::string& arg : args) {
  127. if (arg == "%s") hasInputFilename = true;
  128. else if (arg == "-input=stderr") {
  129. t.InputForStdin = Prior->StdErr;
  130. t.AllowEmptyInput = true;
  131. } else if (strstartswith(arg, checkPrefixStr))
  132. t.CheckPrefixes.emplace_back(arg.substr(sizeof(checkPrefixStr) - 1));
  133. else if (strstartswith(arg, checkPrefixesStr)) {
  134. auto prefixes = strtok(arg.substr(sizeof(checkPrefixesStr) - 1), ", ");
  135. for (auto &prefix : prefixes)
  136. t.CheckPrefixes.emplace_back(prefix);
  137. } else if (strstartswith(arg, defineStr)) {
  138. auto kv = strtok(arg.substr(sizeof(defineStr) - 1), "=");
  139. if (kv.size() != 2)
  140. return FileRunCommandResult::Error("Invalid argument");
  141. t.VariableTable[kv[0]] = kv[1];
  142. }
  143. else return FileRunCommandResult::Error("Invalid argument");
  144. }
  145. if (!hasInputFilename) return FileRunCommandResult::Error("Missing input filename");
  146. if (dumpName) {
  147. // Dump t.InputForStdin to file for comparison purposes
  148. CW2A dumpNameUtf8(dumpName, CP_UTF8);
  149. llvm::StringRef dumpPath(dumpNameUtf8.m_psz);
  150. llvm::sys::fs::create_directories(llvm::sys::path::parent_path(dumpPath), /*IgnoreExisting*/true);
  151. std::error_code ec;
  152. llvm::raw_fd_ostream os(dumpPath, ec, llvm::sys::fs::OpenFlags::F_Text);
  153. if (!ec) {
  154. os << t.InputForStdin;
  155. }
  156. }
  157. FileRunCommandResult result {};
  158. // Run
  159. result.ExitCode = t.Run();
  160. result.StdOut = t.test_outs;
  161. result.StdErr = t.test_errs;
  162. // Capture the input as well.
  163. if (result.ExitCode != 0 && Prior != nullptr) {
  164. result.StdErr += "\n<full input to FileCheck>\n";
  165. result.StdErr += t.InputForStdin;
  166. }
  167. return result;
  168. }
  169. FileRunCommandResult FileRunCommandPart::ReadOptsForDxc(
  170. hlsl::options::MainArgs &argStrings, hlsl::options::DxcOpts &Opts,
  171. unsigned flagsToInclude) {
  172. std::string args(strtrim(Arguments));
  173. const char *inputPos = strstr(args.c_str(), "%s");
  174. if (inputPos == nullptr)
  175. return FileRunCommandResult::Error("Only supported pattern includes input file as argument");
  176. args.erase(inputPos - args.c_str(), strlen("%s"));
  177. llvm::StringRef argsRef = args;
  178. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  179. argsRef.split(splitArgs, " ");
  180. argStrings = hlsl::options::MainArgs(splitArgs);
  181. std::string errorString;
  182. llvm::raw_string_ostream errorStream(errorString);
  183. int RunResult = ReadDxcOpts(hlsl::options::getHlslOptTable(), flagsToInclude,
  184. argStrings, Opts, errorStream);
  185. errorStream.flush();
  186. if (RunResult)
  187. return FileRunCommandResult::Error(RunResult, errorString);
  188. return FileRunCommandResult::Success("");
  189. }
  190. static HRESULT ReAssembleTo(dxc::DxcDllSupport &DllSupport, void *bitcode, UINT32 size, IDxcBlob **pBlob) {
  191. CComPtr<IDxcAssembler> pAssembler;
  192. CComPtr<IDxcLibrary> pLibrary;
  193. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  194. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  195. CComPtr<IDxcBlobEncoding> pInBlob;
  196. IFT(pLibrary->CreateBlobWithEncodingFromPinned(bitcode, size, 0, &pInBlob));
  197. CComPtr<IDxcOperationResult> pResult;
  198. pAssembler->AssembleToContainer(pInBlob, &pResult);
  199. HRESULT Result = 0;
  200. IFT(pResult->GetStatus(&Result));
  201. IFT(Result);
  202. IFT(pResult->GetResult(pBlob));
  203. return S_OK;
  204. }
  205. static HRESULT GetDxilBitcode(dxc::DxcDllSupport &DllSupport, IDxcBlob *pCompiledBlob, IDxcBlob **pBitcodeBlob) {
  206. CComPtr<IDxcContainerReflection> pReflection;
  207. CComPtr<IDxcLibrary> pLibrary;
  208. IFT(DllSupport.CreateInstance(CLSID_DxcContainerReflection, &pReflection));
  209. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  210. IFT(pReflection->Load(pCompiledBlob));
  211. UINT32 uIndex = 0;
  212. IFT(pReflection->FindFirstPartKind(hlsl::DFCC_DXIL, &uIndex));
  213. CComPtr<IDxcBlob> pPart;
  214. IFT(pReflection->GetPartContent(uIndex, &pPart));
  215. auto header = (hlsl::DxilProgramHeader*)pPart->GetBufferPointer();
  216. void *bitcode = (char *)&header->BitcodeHeader + header->BitcodeHeader.BitcodeOffset;
  217. UINT32 bitcode_size = header->BitcodeHeader.BitcodeSize;
  218. CComPtr<IDxcBlobEncoding> pBlob;
  219. IFT(pLibrary->CreateBlobWithEncodingFromPinned(bitcode, bitcode_size, 0, &pBlob));
  220. *pBitcodeBlob = pBlob.Detach();
  221. return S_OK;
  222. }
  223. // Simple virtual file system include handler for test, fall back to default include handler
  224. class IncludeHandlerVFSOverlayForTest : public IDxcIncludeHandler {
  225. private:
  226. DXC_MICROCOM_TM_REF_FIELDS()
  227. public:
  228. DXC_MICROCOM_TM_ADDREF_RELEASE_IMPL()
  229. DXC_MICROCOM_TM_CTOR(IncludeHandlerVFSOverlayForTest)
  230. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) override {
  231. return DoBasicQueryInterface<IDxcIncludeHandler>(this, iid, ppvObject);
  232. }
  233. const FileMap *pVFS = nullptr;
  234. CComPtr<IDxcIncludeHandler> pInnerIncludeHandler;
  235. HRESULT STDMETHODCALLTYPE LoadSource(
  236. _In_ LPCWSTR pFilename, // Candidate filename.
  237. _COM_Outptr_result_maybenull_ IDxcBlob **ppIncludeSource // Resultant source object for included file, nullptr if not found.
  238. ) override {
  239. if (!ppIncludeSource)
  240. return E_INVALIDARG;
  241. *ppIncludeSource = nullptr;
  242. if (!pFilename)
  243. return E_INVALIDARG;
  244. try {
  245. if (pVFS) {
  246. auto it = pVFS->find(pFilename);
  247. if (it != pVFS->end()) {
  248. return it->second.QueryInterface(ppIncludeSource);
  249. }
  250. }
  251. if (pInnerIncludeHandler) {
  252. return pInnerIncludeHandler->LoadSource(pFilename, ppIncludeSource);
  253. }
  254. return E_FAIL;
  255. }
  256. CATCH_CPP_RETURN_HRESULT();
  257. }
  258. };
  259. static IncludeHandlerVFSOverlayForTest *AllocVFSIncludeHandler(IUnknown *pUnkLibrary, const FileMap *pVFS) {
  260. CComPtr<IncludeHandlerVFSOverlayForTest> pVFSIncludeHandler = IncludeHandlerVFSOverlayForTest::Alloc(DxcGetThreadMallocNoRef());
  261. IFTBOOL(pVFSIncludeHandler, E_OUTOFMEMORY);
  262. if (pUnkLibrary) {
  263. CComPtr<IDxcIncludeHandler> pInnerIncludeHandler;
  264. CComPtr<IDxcUtils> pUtils;
  265. if (SUCCEEDED(pUnkLibrary->QueryInterface(IID_PPV_ARGS(&pUtils)))) {
  266. IFT(pUtils->CreateDefaultIncludeHandler(&pInnerIncludeHandler));
  267. } else {
  268. CComPtr<IDxcLibrary> pLibrary;
  269. if (SUCCEEDED(pUnkLibrary->QueryInterface(IID_PPV_ARGS(&pLibrary)))) {
  270. IFT(pLibrary->CreateIncludeHandler(&pInnerIncludeHandler));
  271. }
  272. }
  273. pVFSIncludeHandler->pInnerIncludeHandler = pInnerIncludeHandler;
  274. }
  275. pVFSIncludeHandler->pVFS = pVFS;
  276. return pVFSIncludeHandler.Detach();
  277. }
  278. static void AddOutputsToFileMap(IUnknown *pUnkResult, FileMap *pVFS) {
  279. // If there is IDxcResult, save named output blobs to Files.
  280. if (pUnkResult && pVFS) {
  281. CComPtr<IDxcResult> pResult;
  282. if (SUCCEEDED(pUnkResult->QueryInterface(IID_PPV_ARGS(&pResult)))) {
  283. for (unsigned i = 0; i < pResult->GetNumOutputs(); i++) {
  284. CComPtr<IDxcBlob> pOutput;
  285. CComPtr<IDxcBlobUtf16> pOutputName;
  286. if (SUCCEEDED(pResult->GetOutput(pResult->GetOutputByIndex(i),
  287. IID_PPV_ARGS(&pOutput), &pOutputName)) &&
  288. pOutput && pOutputName && pOutputName->GetStringLength() > 0) {
  289. (*pVFS)[pOutputName->GetStringPointer()] = pOutput;
  290. }
  291. }
  292. }
  293. }
  294. }
  295. static HRESULT CompileForHash(hlsl::options::DxcOpts &opts, LPCWSTR CommandFileName, dxc::DxcDllSupport &DllSupport, std::vector<LPCWSTR> &flags, IDxcBlob **ppHashBlob, std::string &output) {
  296. CComPtr<IDxcLibrary> pLibrary;
  297. CComPtr<IDxcCompiler> pCompiler;
  298. CComPtr<IDxcCompiler2> pCompiler2;
  299. CComPtr<IDxcOperationResult> pResult;
  300. CComPtr<IDxcBlobEncoding> pSource;
  301. CComPtr<IDxcBlob> pCompiledBlob;
  302. CComPtr<IDxcBlob> pCompiledName;
  303. CComPtr<IDxcIncludeHandler> pIncludeHandler;
  304. WCHAR *pDebugName = nullptr;
  305. CComPtr<IDxcBlob> pPDBBlob;
  306. std::wstring entry =
  307. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  308. std::wstring profile =
  309. Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
  310. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  311. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  312. IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
  313. IFT(DllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  314. IFT(pCompiler.QueryInterface(&pCompiler2));
  315. IFT(pCompiler2->CompileWithDebug(pSource, CommandFileName, entry.c_str(), profile.c_str(),
  316. flags.data(), flags.size(), nullptr, 0, pIncludeHandler, &pResult, &pDebugName, &pPDBBlob));
  317. HRESULT resultStatus = 0;
  318. IFT(pResult->GetStatus(&resultStatus));
  319. if (SUCCEEDED(resultStatus)) {
  320. IFT(pResult->GetResult(&pCompiledBlob));
  321. CComPtr<IDxcContainerReflection> pReflection;
  322. IFT(DllSupport.CreateInstance(CLSID_DxcContainerReflection, &pReflection));
  323. // If failed to load here, it's likely some non-compile operation thing. Just fail the hash generation.
  324. if (FAILED(pReflection->Load(pCompiledBlob)))
  325. return E_FAIL;
  326. *ppHashBlob = nullptr;
  327. UINT32 uHashIdx = 0;
  328. if (SUCCEEDED(pReflection->FindFirstPartKind(hlsl::DFCC_ShaderHash, &uHashIdx))) {
  329. CComPtr<IDxcBlob> pHashBlob;
  330. IFT(pReflection->GetPartContent(uHashIdx, &pHashBlob));
  331. *ppHashBlob = pHashBlob.Detach();
  332. }
  333. // Test that PDB is generated correctly.
  334. // This test needs to be done elsewhere later, ideally a fully
  335. // customizable test on all our test set with different compile options.
  336. if (pPDBBlob) {
  337. IFT(pReflection->Load(pPDBBlob));
  338. UINT32 uDebugInfoIndex = 0;
  339. IFT(pReflection->FindFirstPartKind(hlsl::DFCC_ShaderDebugInfoDXIL, &uDebugInfoIndex));
  340. }
  341. return S_OK;
  342. }
  343. else {
  344. CComPtr<IDxcBlobEncoding> pErrors;
  345. IFT(pResult->GetErrorBuffer(&pErrors));
  346. const char *errors = (char *)pErrors->GetBufferPointer();
  347. output = errors;
  348. return resultStatus;
  349. }
  350. }
  351. FileRunCommandResult FileRunCommandPart::RunDxcHashTest(dxc::DxcDllSupport &DllSupport) {
  352. hlsl::options::MainArgs args;
  353. hlsl::options::DxcOpts opts;
  354. ReadOptsForDxc(args, opts);
  355. std::vector<std::wstring> argWStrings;
  356. CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
  357. // Extract the vanilla flags for the test (i.e. no debug or ast-dump)
  358. std::vector<LPCWSTR> original_flags;
  359. bool skipNext = false;
  360. for (const std::wstring &a : argWStrings) {
  361. if (skipNext) {
  362. skipNext = false;
  363. continue;
  364. }
  365. if (a.find(L"ast-dump") != std::wstring::npos) continue;
  366. if (a.find(L"Zi") != std::wstring::npos) continue;
  367. std::wstring optValVer(L"validator-version");
  368. if (a.substr(1, optValVer.length()).compare(optValVer) == 0) {
  369. skipNext = a.length() == optValVer.length() + 1;
  370. continue;
  371. }
  372. original_flags.push_back(a.data());
  373. }
  374. std::string originalOutput;
  375. CComPtr<IDxcBlob> pOriginalHash;
  376. // If failed the original compilation, just pass the test. The original test was likely
  377. // testing for failure.
  378. if (FAILED(CompileForHash(opts, CommandFileName, DllSupport, original_flags, &pOriginalHash, originalOutput)))
  379. return FileRunCommandResult::Success();
  380. // Results of our compilations
  381. CComPtr<IDxcBlob> pHash1;
  382. std::string Output0;
  383. CComPtr<IDxcBlob> pHash0;
  384. std::string Output1;
  385. // Fail if -Qstrip_reflect failed the compilation
  386. std::vector<LPCWSTR> normal_flags = original_flags;
  387. normal_flags.push_back(L"-Qstrip_reflect");
  388. normal_flags.push_back(L"-Zsb");
  389. std::string StdErr;
  390. if (FAILED(CompileForHash(opts, CommandFileName, DllSupport, normal_flags, &pHash0, Output0))) {
  391. StdErr += "Adding Qstrip_reflect failed compilation.";
  392. StdErr += originalOutput;
  393. StdErr += Output0;
  394. return FileRunCommandResult::Error(StdErr);
  395. }
  396. // Fail if -Qstrip_reflect failed the compilation
  397. std::vector<LPCWSTR> dbg_flags = original_flags;
  398. dbg_flags.push_back(L"/Zi");
  399. dbg_flags.push_back(L"-Qstrip_reflect");
  400. dbg_flags.push_back(L"-Zsb");
  401. if (FAILED(CompileForHash(opts, CommandFileName, DllSupport, dbg_flags, &pHash1, Output1))) {
  402. return FileRunCommandResult::Error("Adding Qstrip_reflect and Zi failed compilation.");
  403. }
  404. if (pHash0->GetBufferSize() != pHash1->GetBufferSize() || 0 != memcmp(pHash0->GetBufferPointer(), pHash0->GetBufferPointer(), pHash1->GetBufferSize())) {
  405. StdErr = "Hashes do not match between normal and debug!!!\n";
  406. StdErr += Output0;
  407. StdErr += Output1;
  408. return FileRunCommandResult::Error(StdErr);
  409. }
  410. return FileRunCommandResult::Success();
  411. }
  412. static FileRunCommandResult CheckDxilVer(dxc::DxcDllSupport& DllSupport,
  413. unsigned RequiredDxilMajor,
  414. unsigned RequiredDxilMinor,
  415. bool bCheckValidator = true) {
  416. bool Supported = true;
  417. // If the following fails, we have Dxil 1.0 compiler
  418. unsigned DxilMajor = 1, DxilMinor = 0;
  419. GetVersion(DllSupport, CLSID_DxcCompiler, DxilMajor, DxilMinor);
  420. Supported &= hlsl::DXIL::CompareVersions(DxilMajor, DxilMinor, RequiredDxilMajor, RequiredDxilMinor) >= 0;
  421. if (bCheckValidator) {
  422. // If the following fails, we have validator 1.0
  423. unsigned ValMajor = 1, ValMinor = 0;
  424. GetVersion(DllSupport, CLSID_DxcValidator, ValMajor, ValMinor);
  425. Supported &= hlsl::DXIL::CompareVersions(ValMajor, ValMinor, RequiredDxilMajor, RequiredDxilMinor) >= 0;
  426. }
  427. if (!Supported) {
  428. FileRunCommandResult result {};
  429. result.StdErr = "Skipping test due to unsupported dxil version";
  430. result.ExitCode = 0; // Succeed the test
  431. result.AbortPipeline = true;
  432. return result;
  433. }
  434. return FileRunCommandResult::Success();
  435. }
  436. FileRunCommandResult FileRunCommandPart::RunDxc(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  437. // Support piping stdin from prior if needed.
  438. UNREFERENCED_PARAMETER(Prior);
  439. hlsl::options::MainArgs args;
  440. hlsl::options::DxcOpts opts;
  441. FileRunCommandResult readOptsResult = ReadOptsForDxc(args, opts);
  442. if (readOptsResult.ExitCode) return readOptsResult;
  443. std::wstring entry =
  444. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  445. std::wstring profile =
  446. Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
  447. std::vector<LPCWSTR> flags;
  448. if (opts.CodeGenHighLevel) {
  449. flags.push_back(L"-fcgl");
  450. }
  451. // Skip targets that require a newer compiler or validator.
  452. // Some features may require newer compiler/validator than indicated by the
  453. // shader model, but these should use %dxilver explicitly.
  454. {
  455. unsigned RequiredDxilMajor = 1, RequiredDxilMinor = 0;
  456. llvm::StringRef stage;
  457. IFTBOOL(ParseTargetProfile(opts.TargetProfile, stage, RequiredDxilMajor, RequiredDxilMinor), E_INVALIDARG);
  458. if (RequiredDxilMinor != 0xF && stage.compare("rootsig") != 0) {
  459. // Convert stage to minimum dxil/validator version:
  460. RequiredDxilMajor = std::max(RequiredDxilMajor, (unsigned)6) - 5;
  461. FileRunCommandResult result = CheckDxilVer(DllSupport, RequiredDxilMajor, RequiredDxilMinor, !opts.DisableValidation);
  462. if (result.AbortPipeline) {
  463. return result;
  464. }
  465. }
  466. }
  467. // For now, too many tests are sensitive to stripping the refleciton info
  468. // from the main module, so use this flag to prevent this until tests
  469. // can be updated.
  470. // That is, unless the test explicitly requests -Qstrip_reflect_from_dxil or -Qstrip_reflect
  471. if (!opts.StripReflectionFromDxil && !opts.StripReflection) {
  472. flags.push_back(L"-Qkeep_reflect_in_dxil");
  473. }
  474. std::vector<std::wstring> argWStrings;
  475. CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
  476. for (const std::wstring &a : argWStrings)
  477. flags.push_back(a.data());
  478. CComPtr<IDxcLibrary> pLibrary;
  479. CComPtr<IDxcCompiler> pCompiler;
  480. CComPtr<IDxcOperationResult> pResult;
  481. CComPtr<IDxcBlobEncoding> pSource;
  482. CComPtr<IDxcBlobEncoding> pDisassembly;
  483. CComPtr<IDxcBlob> pCompiledBlob;
  484. HRESULT resultStatus;
  485. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  486. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  487. CComPtr<IDxcIncludeHandler> pIncludeHandler = AllocVFSIncludeHandler(pLibrary, pVFS);
  488. IFT(DllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  489. IFT(pCompiler->Compile(pSource, CommandFileName, entry.c_str(), profile.c_str(),
  490. flags.data(), flags.size(), nullptr, 0, pIncludeHandler, &pResult));
  491. IFT(pResult->GetStatus(&resultStatus));
  492. FileRunCommandResult result = {};
  493. if (SUCCEEDED(resultStatus)) {
  494. IFT(pResult->GetResult(&pCompiledBlob));
  495. if (!opts.AstDump) {
  496. IFT(pCompiler->Disassemble(pCompiledBlob, &pDisassembly));
  497. result.StdOut = BlobToUtf8(pDisassembly);
  498. } else {
  499. result.StdOut = BlobToUtf8(pCompiledBlob);
  500. }
  501. CComPtr<IDxcBlobEncoding> pStdErr;
  502. IFT(pResult->GetErrorBuffer(&pStdErr));
  503. result.StdErr = BlobToUtf8(pStdErr);
  504. result.ExitCode = 0;
  505. }
  506. else {
  507. IFT(pResult->GetErrorBuffer(&pDisassembly));
  508. result.StdErr = BlobToUtf8(pDisassembly);
  509. result.ExitCode = resultStatus;
  510. }
  511. result.OpResult = pResult;
  512. return result;
  513. }
  514. FileRunCommandResult FileRunCommandPart::RunDxv(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  515. std::string args(strtrim(Arguments));
  516. const char *inputPos = strstr(args.c_str(), "%s");
  517. if (inputPos == nullptr) {
  518. return FileRunCommandResult::Error("Only supported pattern includes input file as argument");
  519. }
  520. args.erase(inputPos - args.c_str(), strlen("%s"));
  521. llvm::StringRef argsRef = args;
  522. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  523. argsRef.split(splitArgs, " ");
  524. IFTMSG(splitArgs.size()==1, "wrong arg num for dxv");
  525. CComPtr<IDxcLibrary> pLibrary;
  526. CComPtr<IDxcAssembler> pAssembler;
  527. CComPtr<IDxcValidator> pValidator;
  528. CComPtr<IDxcOperationResult> pResult;
  529. CComPtr<IDxcBlobEncoding> pSource;
  530. CComPtr<IDxcBlob> pContainerBlob;
  531. HRESULT resultStatus;
  532. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  533. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  534. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  535. IFT(pAssembler->AssembleToContainer(pSource, &pResult));
  536. IFT(pResult->GetStatus(&resultStatus));
  537. if (FAILED(resultStatus)) {
  538. CComPtr<IDxcBlobEncoding> pAssembleBlob;
  539. IFT(pResult->GetErrorBuffer(&pAssembleBlob));
  540. return FileRunCommandResult::Error(resultStatus, BlobToUtf8(pAssembleBlob));
  541. }
  542. IFT(pResult->GetResult(&pContainerBlob));
  543. IFT(DllSupport.CreateInstance(CLSID_DxcValidator, &pValidator));
  544. CComPtr<IDxcOperationResult> pValidationResult;
  545. IFT(pValidator->Validate(pContainerBlob, DxcValidatorFlags_InPlaceEdit,
  546. &pValidationResult));
  547. IFT(pValidationResult->GetStatus(&resultStatus));
  548. if (FAILED(resultStatus)) {
  549. CComPtr<IDxcBlobEncoding> pValidateBlob;
  550. IFT(pValidationResult->GetErrorBuffer(&pValidateBlob));
  551. return FileRunCommandResult::Success(BlobToUtf8(pValidateBlob));
  552. }
  553. return FileRunCommandResult::Success("");
  554. }
  555. FileRunCommandResult FileRunCommandPart::RunOpt(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  556. std::string args(strtrim(Arguments));
  557. const char *inputPos = strstr(args.c_str(), "%s");
  558. if (inputPos == nullptr && Prior == nullptr) {
  559. return FileRunCommandResult::Error("Only supported patterns are input file as argument or prior "
  560. "command with disassembly");
  561. }
  562. CComPtr<IDxcLibrary> pLibrary;
  563. CComPtr<IDxcOptimizer> pOptimizer;
  564. CComPtr<IDxcBlobEncoding> pSource;
  565. CComPtr<IDxcBlobEncoding> pOutputText;
  566. CComPtr<IDxcBlob> pOutputModule;
  567. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  568. IFT(DllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
  569. if (inputPos != nullptr) {
  570. args.erase(inputPos - args.c_str(), strlen("%s"));
  571. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  572. }
  573. else {
  574. assert(Prior != nullptr && "else early check should have returned");
  575. CComPtr<IDxcAssembler> pAssembler;
  576. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  577. IFT(pLibrary->CreateBlobWithEncodingFromPinned(
  578. Prior->StdOut.c_str(), Prior->StdOut.size(), CP_UTF8,
  579. &pSource));
  580. }
  581. args = strtrim(args);
  582. llvm::StringRef argsRef = args;
  583. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  584. argsRef.split(splitArgs, " ");
  585. std::vector<LPCWSTR> options;
  586. std::vector<std::wstring> optionStrings;
  587. for (llvm::StringRef S : splitArgs) {
  588. optionStrings.push_back(
  589. Unicode::UTF8ToUTF16StringOrThrow(strtrim(S.str()).c_str()));
  590. }
  591. // Add the options outside the above loop in case the vector is resized.
  592. for (const std::wstring& str : optionStrings)
  593. options.push_back(str.c_str());
  594. IFT(pOptimizer->RunOptimizer(pSource, options.data(), options.size(),
  595. &pOutputModule, &pOutputText));
  596. return FileRunCommandResult::Success(BlobToUtf8(pOutputText));
  597. }
  598. FileRunCommandResult FileRunCommandPart::RunD3DReflect(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  599. std::string args(strtrim(Arguments));
  600. if (args != "%s")
  601. return FileRunCommandResult::Error("Only supported pattern is a plain input file");
  602. if (!Prior)
  603. return FileRunCommandResult::Error("Prior command required to generate stdin");
  604. CComPtr<IDxcLibrary> pLibrary;
  605. CComPtr<IDxcBlobEncoding> pSource;
  606. CComPtr<IDxcAssembler> pAssembler;
  607. CComPtr<IDxcOperationResult> pResult;
  608. CComPtr<ID3D12ShaderReflection> pShaderReflection;
  609. CComPtr<ID3D12LibraryReflection> pLibraryReflection;
  610. CComPtr<IDxcContainerReflection> containerReflection;
  611. uint32_t partCount;
  612. CComPtr<IDxcBlob> pContainerBlob;
  613. HRESULT resultStatus;
  614. bool blobFound = false;
  615. std::ostringstream ss;
  616. D3DReflectionDumper dumper(ss);
  617. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  618. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  619. IFT(pLibrary->CreateBlobWithEncodingFromPinned(
  620. (LPBYTE)Prior->StdOut.c_str(), Prior->StdOut.size(), CP_UTF8,
  621. &pSource));
  622. IFT(pAssembler->AssembleToContainer(pSource, &pResult));
  623. IFT(pResult->GetStatus(&resultStatus));
  624. if (FAILED(resultStatus)) {
  625. CComPtr<IDxcBlobEncoding> pAssembleBlob;
  626. IFT(pResult->GetErrorBuffer(&pAssembleBlob));
  627. return FileRunCommandResult::Error(resultStatus, BlobToUtf8(pAssembleBlob));
  628. }
  629. IFT(pResult->GetResult(&pContainerBlob));
  630. VERIFY_SUCCEEDED(DllSupport.CreateInstance(CLSID_DxcContainerReflection, &containerReflection));
  631. VERIFY_SUCCEEDED(containerReflection->Load(pContainerBlob));
  632. VERIFY_SUCCEEDED(containerReflection->GetPartCount(&partCount));
  633. for (uint32_t i = 0; i < partCount; ++i) {
  634. uint32_t kind;
  635. VERIFY_SUCCEEDED(containerReflection->GetPartKind(i, &kind));
  636. if (kind == (uint32_t)hlsl::DxilFourCC::DFCC_DXIL) {
  637. blobFound = true;
  638. CComPtr<IDxcBlob> pPart;
  639. IFT(containerReflection->GetPartContent(i, &pPart));
  640. const hlsl::DxilProgramHeader *pProgramHeader =
  641. reinterpret_cast<const hlsl::DxilProgramHeader*>(pPart->GetBufferPointer());
  642. VERIFY_IS_TRUE(IsValidDxilProgramHeader(pProgramHeader, (uint32_t)pPart->GetBufferSize()));
  643. hlsl::DXIL::ShaderKind SK = hlsl::GetVersionShaderType(pProgramHeader->ProgramVersion);
  644. if (SK == hlsl::DXIL::ShaderKind::Library)
  645. VERIFY_SUCCEEDED(containerReflection->GetPartReflection(i, IID_PPV_ARGS(&pLibraryReflection)));
  646. else
  647. VERIFY_SUCCEEDED(containerReflection->GetPartReflection(i, IID_PPV_ARGS(&pShaderReflection)));
  648. break;
  649. }
  650. }
  651. if (!blobFound) {
  652. return FileRunCommandResult::Error("Unable to find DXIL part");
  653. } else if (pShaderReflection) {
  654. dumper.Dump(pShaderReflection);
  655. } else if (pLibraryReflection) {
  656. dumper.Dump(pLibraryReflection);
  657. }
  658. ss.flush();
  659. return FileRunCommandResult::Success(ss.str());
  660. }
  661. FileRunCommandResult FileRunCommandPart::RunDxr(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  662. // Support piping stdin from prior if needed.
  663. UNREFERENCED_PARAMETER(Prior);
  664. hlsl::options::MainArgs args;
  665. hlsl::options::DxcOpts opts;
  666. FileRunCommandResult readOptsResult = ReadOptsForDxc(args, opts,
  667. hlsl::options::HlslFlags::RewriteOption);
  668. if (readOptsResult.ExitCode) return readOptsResult;
  669. std::wstring entry =
  670. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  671. std::vector<LPCWSTR> flags;
  672. std::vector<std::wstring> argWStrings;
  673. CopyArgsToWStrings(opts.Args, hlsl::options::RewriteOption, argWStrings);
  674. for (const std::wstring &a : argWStrings)
  675. flags.push_back(a.data());
  676. CComPtr<IDxcLibrary> pLibrary;
  677. CComPtr<IDxcRewriter2> pRewriter;
  678. CComPtr<IDxcOperationResult> pResult;
  679. CComPtr<IDxcBlobEncoding> pSource;
  680. CComPtr<IDxcBlob> pResultBlob;
  681. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  682. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  683. CComPtr<IDxcIncludeHandler> pIncludeHandler = AllocVFSIncludeHandler(pLibrary, pVFS);
  684. IFT(DllSupport.CreateInstance(CLSID_DxcRewriter, &pRewriter));
  685. IFT(pRewriter->RewriteWithOptions(pSource, CommandFileName,
  686. flags.data(), flags.size(), nullptr, 0,
  687. pIncludeHandler, &pResult));
  688. HRESULT resultStatus;
  689. IFT(pResult->GetStatus(&resultStatus));
  690. FileRunCommandResult result = {};
  691. CComPtr<IDxcBlobEncoding> pStdErr;
  692. IFT(pResult->GetErrorBuffer(&pStdErr));
  693. result.StdErr = BlobToUtf8(pStdErr);
  694. result.ExitCode = resultStatus;
  695. if (SUCCEEDED(resultStatus)) {
  696. IFT(pResult->GetResult(&pResultBlob));
  697. result.StdOut = BlobToUtf8(pResultBlob);
  698. }
  699. result.OpResult = pResult;
  700. return result;
  701. }
  702. FileRunCommandResult FileRunCommandPart::RunLink(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  703. hlsl::options::MainArgs args;
  704. hlsl::options::DxcOpts opts;
  705. FileRunCommandResult readOptsResult = ReadOptsForDxc(args, opts,
  706. hlsl::options::HlslFlags::CoreOption);
  707. if (readOptsResult.ExitCode) return readOptsResult;
  708. std::wstring entry =
  709. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  710. std::wstring profile =
  711. Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
  712. std::vector<LPCWSTR> flags;
  713. // Skip targets that require a newer compiler or validator.
  714. // Some features may require newer compiler/validator than indicated by the
  715. // shader model, but these should use %dxilver explicitly.
  716. {
  717. unsigned RequiredDxilMajor = 1, RequiredDxilMinor = 0;
  718. llvm::StringRef stage;
  719. IFTBOOL(ParseTargetProfile(opts.TargetProfile, stage, RequiredDxilMajor, RequiredDxilMinor), E_INVALIDARG);
  720. if (RequiredDxilMinor != 0xF && stage.compare("rootsig") != 0) {
  721. // Convert stage to minimum dxil/validator version:
  722. RequiredDxilMajor = std::max(RequiredDxilMajor, (unsigned)6) - 5;
  723. FileRunCommandResult result = CheckDxilVer(DllSupport, RequiredDxilMajor, RequiredDxilMinor, !opts.DisableValidation);
  724. if (result.AbortPipeline) {
  725. return result;
  726. }
  727. }
  728. }
  729. // For now, too many tests are sensitive to stripping the refleciton info
  730. // from the main module, so use this flag to prevent this until tests
  731. // can be updated.
  732. // That is, unless the test explicitly requests -Qstrip_reflect_from_dxil or -Qstrip_reflect
  733. if (!opts.StripReflectionFromDxil && !opts.StripReflection) {
  734. flags.push_back(L"-Qkeep_reflect_in_dxil");
  735. }
  736. std::vector<std::wstring> argWStrings;
  737. CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
  738. for (const std::wstring &a : argWStrings)
  739. flags.push_back(a.data());
  740. // Parse semicolon separated list of library names.
  741. llvm::StringRef optLibraries = opts.Args.getLastArgValue(hlsl::options::OPT_INPUT);
  742. auto libs_utf8 = strtok(optLibraries.str().c_str(), ";");
  743. std::vector<std::wstring> libs_utf16;
  744. for (auto name : libs_utf8)
  745. libs_utf16.emplace_back(Unicode::UTF8ToUTF16StringOrThrow(name.c_str()));
  746. std::vector<LPCWSTR> libNames;
  747. for (auto &name : libs_utf16)
  748. libNames.emplace_back(name.data());
  749. CComPtr<IDxcLibrary> pLibrary;
  750. CComPtr<IDxcLinker> pLinker;
  751. CComPtr<IDxcCompiler> pCompiler;
  752. CComPtr<IDxcOperationResult> pResult;
  753. CComPtr<IDxcBlobEncoding> pDisassembly;
  754. CComPtr<IDxcBlob> pCompiledBlob;
  755. HRESULT resultStatus;
  756. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  757. CComPtr<IDxcIncludeHandler> pIncludeHandler = AllocVFSIncludeHandler(pLibrary, pVFS);
  758. IFT(DllSupport.CreateInstance(CLSID_DxcLinker, &pLinker));
  759. IFT(DllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  760. for (auto name : libNames) {
  761. CComPtr<IDxcBlob> pLibBlob;
  762. IFT(pIncludeHandler->LoadSource(name, &pLibBlob));
  763. IFT(pLinker->RegisterLibrary(name, pLibBlob));
  764. }
  765. IFT(pLinker->Link(entry.c_str(), profile.c_str(), libNames.data(),
  766. libNames.size(), flags.data(), flags.size(), &pResult));
  767. IFT(pResult->GetStatus(&resultStatus));
  768. FileRunCommandResult result = {};
  769. if (SUCCEEDED(resultStatus)) {
  770. IFT(pResult->GetResult(&pCompiledBlob));
  771. if (!opts.AstDump) {
  772. IFT(pCompiler->Disassemble(pCompiledBlob, &pDisassembly));
  773. result.StdOut = BlobToUtf8(pDisassembly);
  774. } else {
  775. result.StdOut = BlobToUtf8(pCompiledBlob);
  776. }
  777. CComPtr<IDxcBlobEncoding> pStdErr;
  778. IFT(pResult->GetErrorBuffer(&pStdErr));
  779. result.StdErr = BlobToUtf8(pStdErr);
  780. result.ExitCode = 0;
  781. }
  782. else {
  783. IFT(pResult->GetErrorBuffer(&pDisassembly));
  784. result.StdErr = BlobToUtf8(pDisassembly);
  785. result.ExitCode = resultStatus;
  786. }
  787. result.OpResult = pResult;
  788. return result;
  789. }
  790. FileRunCommandResult FileRunCommandPart::RunTee(const FileRunCommandResult *Prior) {
  791. if (Prior == nullptr) {
  792. return FileRunCommandResult::Error("tee requires a prior command");
  793. }
  794. // Ignore commands for now - simply log out through test framework.
  795. {
  796. CA2W outWide(Prior->StdOut.c_str(), CP_UTF8);
  797. WEX::Logging::Log::Comment(outWide.m_psz);
  798. }
  799. if (!Prior->StdErr.empty()) {
  800. CA2W errWide(Prior->StdErr.c_str(), CP_UTF8);
  801. WEX::Logging::Log::Comment(L"<stderr>");
  802. WEX::Logging::Log::Comment(errWide.m_psz);
  803. }
  804. return *Prior;
  805. }
  806. void FileRunCommandPart::SubstituteFilenameVars(std::string &args) {
  807. size_t pos;
  808. std::string baseFileName = CW2A(CommandFileName);
  809. if ((pos = baseFileName.find_last_of(".")) != std::string::npos) {
  810. baseFileName = baseFileName.substr(0, pos);
  811. }
  812. while ((pos = args.find("%t")) != std::string::npos) {
  813. args.replace(pos, 2, baseFileName.c_str());
  814. }
  815. while ((pos = args.find("%b")) != std::string::npos) {
  816. args.replace(pos, 2, baseFileName.c_str());
  817. }
  818. }
  819. #if _WIN32
  820. bool FileRunCommandPart::ReadFileContentToString(HANDLE hFile, std::string &str) {
  821. char buffer[1024];
  822. DWORD len;
  823. size_t size = ::GetFileSize(hFile, nullptr);
  824. if (size == INVALID_FILE_SIZE) {
  825. return false;
  826. }
  827. str.reserve(size);
  828. if (::SetFilePointer(hFile, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
  829. return false;
  830. }
  831. while (::ReadFile(hFile, buffer, sizeof(buffer), &len, nullptr) && len > 0) {
  832. str.append(buffer, len);
  833. }
  834. return true;
  835. }
  836. #endif
  837. FileRunCommandResult FileRunCommandPart::RunFileCompareText(const FileRunCommandResult *Prior) {
  838. if (Prior != nullptr) {
  839. return FileRunCommandResult::Error("prior command not supported");
  840. }
  841. FileRunCommandResult result;
  842. result.ExitCode = 1;
  843. // strip leading and trailing spaces and split
  844. std::string args(strtrim(Arguments));
  845. size_t pos;
  846. if ((pos = args.find_first_of(' ')) == std::string::npos) {
  847. return FileRunCommandResult::Error("RunFileCompareText expected 2 file arguments.");
  848. }
  849. std::string fileName1 = args.substr(0, pos);
  850. std::string fileName2 = strtrim(args.substr(pos + 1));
  851. // replace %t and %b with the command file name without extension
  852. SubstituteFilenameVars(fileName1);
  853. SubstituteFilenameVars(fileName2);
  854. // read file content and compare
  855. CA2W fileName1W(fileName1.c_str());
  856. CA2W fileName2W(fileName2.c_str());
  857. hlsl_test::LogCommentFmt(L"Comparing files %s and %s", fileName1W.m_psz, fileName2W.m_psz);
  858. std::ifstream ifs1(fileName1, std::ifstream::in);
  859. if (ifs1.fail()) {
  860. hlsl_test::LogCommentFmt(L"Failed to open %s", fileName1W.m_psz);
  861. return result;
  862. }
  863. std::string file1Content((std::istreambuf_iterator<char>(ifs1)), (std::istreambuf_iterator<char>()));
  864. std::ifstream ifs2(fileName2, std::ifstream::in);
  865. if (ifs2.fail()) {
  866. hlsl_test::LogCommentFmt(L"Failed to open %s", fileName2W.m_psz);
  867. return result;
  868. }
  869. std::string file2Content((std::istreambuf_iterator<char>(ifs2)), (std::istreambuf_iterator<char>()));
  870. if (file1Content.compare(file2Content) == 0) {
  871. hlsl_test::LogCommentFmt(L"No differences found.");
  872. result.ExitCode = 0;
  873. }
  874. else {
  875. hlsl_test::LogCommentFmt(L"Files are different!");
  876. }
  877. return result;
  878. }
  879. FileRunCommandResult FileRunCommandPart::RunXFail(const FileRunCommandResult *Prior) {
  880. if (Prior == nullptr)
  881. return FileRunCommandResult::Error("XFail requires a prior command");
  882. if (Prior->ExitCode == 0) {
  883. return FileRunCommandResult::Error("XFail expected a failure from previous command");
  884. } else {
  885. return FileRunCommandResult::Success("");
  886. }
  887. }
  888. FileRunCommandResult FileRunCommandPart::RunDxilVer(dxc::DxcDllSupport& DllSupport, const FileRunCommandResult* Prior) {
  889. Arguments = strtrim(Arguments);
  890. if (Arguments.size() != 3 || !std::isdigit(Arguments[0]) || Arguments[1] != '.' || !std::isdigit(Arguments[2])) {
  891. return FileRunCommandResult::Error("Invalid dxil version format");
  892. }
  893. unsigned RequiredDxilMajor = Arguments[0] - '0';
  894. unsigned RequiredDxilMinor = Arguments[2] - '0';
  895. return CheckDxilVer(DllSupport, RequiredDxilMajor, RequiredDxilMinor);
  896. }
  897. #ifndef _WIN32
  898. FileRunCommandResult FileRunCommandPart::RunFromPath(const std::string &toolPath, const FileRunCommandResult *Prior) {
  899. return FileRunCommandResult::Error("RunFromPath not supported");
  900. }
  901. #else //_WIN32
  902. FileRunCommandResult FileRunCommandPart::RunFromPath(const std::string &toolPath, const FileRunCommandResult *Prior) {
  903. if (Prior != nullptr) {
  904. return FileRunCommandResult::Error("prior command not supported");
  905. }
  906. std::string args = Arguments;
  907. // replace %s with command file name
  908. size_t pos;
  909. while ((pos = args.find("%s")) != std::string::npos) {
  910. args.replace(pos, 2, CW2A(CommandFileName));
  911. }
  912. // replace %t and %b with the command file name without extension
  913. SubstituteFilenameVars(args);
  914. // Run the tool via CreateProcess, redirect stdout and strerr to temporary files
  915. std::wstring stdOutFileName = std::wstring(CommandFileName) + L".tmp_stdout";
  916. std::wstring stdErrFileName = std::wstring(CommandFileName) + L".tmp_stderr";
  917. SECURITY_ATTRIBUTES sa;
  918. ZeroMemory(&sa, sizeof(sa));
  919. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  920. sa.bInheritHandle = true;
  921. HANDLE hStdOutFile = CreateFileW(stdOutFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa,
  922. CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
  923. IFT(hStdOutFile != INVALID_HANDLE_VALUE);
  924. HANDLE hStdErrFile = CreateFileW(stdErrFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa,
  925. CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
  926. IFT(hStdErrFile!= INVALID_HANDLE_VALUE);
  927. STARTUPINFOA si;
  928. ZeroMemory(&si, sizeof(si));
  929. si.cb = sizeof(si);
  930. si.hStdOutput = hStdOutFile;
  931. si.hStdError = hStdErrFile;
  932. si.dwFlags |= STARTF_USESTDHANDLES;
  933. PROCESS_INFORMATION pi;
  934. ZeroMemory(&pi, sizeof(pi));
  935. std::vector<char> args2(args.c_str(), args.c_str() + args.size() + 1); // args to CreateProcess cannot be const char *
  936. if (!CreateProcessA(toolPath.c_str(), args2.data(), nullptr, nullptr, true, 0, nullptr, nullptr, &si, &pi)) {
  937. return FileRunCommandResult::Error("CreateProcess failed.");
  938. }
  939. ::WaitForSingleObject(pi.hProcess, 10000); // 10s timeout
  940. // Get exit code of the process
  941. FileRunCommandResult result;
  942. DWORD exitCode;
  943. if (!::GetExitCodeProcess(pi.hProcess, &exitCode)) {
  944. result = FileRunCommandResult::Error("GetExitCodeProcess failed.");
  945. }
  946. else {
  947. result.ExitCode = exitCode;
  948. }
  949. // Close process and thread handles
  950. ::CloseHandle(pi.hProcess);
  951. ::CloseHandle(pi.hThread);
  952. // Read stdout and strerr output from temporary files
  953. if (!ReadFileContentToString(hStdOutFile, result.StdOut) ||
  954. !ReadFileContentToString(hStdErrFile, result.StdErr)) {
  955. result = FileRunCommandResult::Error("RunFromPaths failed.");
  956. }
  957. // Close temporary file handles - will delete the files
  958. IFT(::CloseHandle(hStdOutFile));
  959. IFT(::CloseHandle(hStdErrFile));
  960. return result;
  961. }
  962. #endif //_WIN32
  963. class FileRunTestResultImpl : public FileRunTestResult {
  964. dxc::DxcDllSupport &m_support;
  965. PluginToolsPaths *m_pPluginToolsPaths;
  966. LPCWSTR m_dumpName = nullptr;
  967. // keep track of virtual files for duration of this test (for all RUN lines)
  968. FileMap Files;
  969. void RunHashTestFromCommands(LPCSTR commands, LPCWSTR fileName) {
  970. std::vector<FileRunCommandPart> parts;
  971. ParseCommandParts(commands, fileName, parts);
  972. FileRunCommandResult result;
  973. bool ran = false;
  974. for (FileRunCommandPart & part : parts) {
  975. result = part.RunHashTests(m_support);
  976. ran = true;
  977. break;
  978. }
  979. if (ran) {
  980. this->RunResult = result.ExitCode;
  981. this->ErrorMessage = result.StdErr;
  982. }
  983. else {
  984. this->RunResult = 0;
  985. }
  986. }
  987. void RunFileCheckFromCommands(LPCSTR commands, LPCWSTR fileName, LPCWSTR dumpName = nullptr) {
  988. std::vector<FileRunCommandPart> parts;
  989. ParseCommandParts(commands, fileName, parts);
  990. if (parts.empty()) {
  991. this->RunResult = 1;
  992. this->ErrorMessage = "FileCheck found no commands to run";
  993. return;
  994. }
  995. FileRunCommandResult result;
  996. FileRunCommandResult *previousResult = nullptr;
  997. FileRunCommandPart *pPrior = nullptr;
  998. for (FileRunCommandPart & part : parts) {
  999. int priorExitCode = result.ExitCode;
  1000. part.pVFS = &Files;
  1001. result = part.Run(m_support, previousResult, m_pPluginToolsPaths, dumpName);
  1002. // If there is IDxcResult, save named output blobs to Files.
  1003. AddOutputsToFileMap(result.OpResult, &Files);
  1004. // When current failing stage is FileCheck, print prior command,
  1005. // as well as FileCheck command that failed, to help identify
  1006. // failing commands in longer run chains.
  1007. if (result.ExitCode &&
  1008. (0 == _stricmp(part.Command.c_str(), "FileCheck") ||
  1009. 0 == _stricmp(part.Command.c_str(), "%FileCheck"))) {
  1010. std::ostringstream oss;
  1011. if (pPrior) {
  1012. oss << "Prior (" << priorExitCode << "): "
  1013. << pPrior->Command << pPrior->Arguments << endl;
  1014. }
  1015. oss << "Error (" << result.ExitCode << "): "
  1016. << part.Command << part.Arguments << endl;
  1017. oss << result.StdErr;
  1018. result.StdErr = oss.str();
  1019. }
  1020. if (result.AbortPipeline)
  1021. break;
  1022. previousResult = &result;
  1023. pPrior = &part;
  1024. }
  1025. this->RunResult = result.ExitCode;
  1026. this->ErrorMessage = result.StdErr;
  1027. }
  1028. public:
  1029. FileRunTestResultImpl(dxc::DxcDllSupport &support, PluginToolsPaths *pPluginToolsPaths = nullptr,
  1030. LPCWSTR dumpName = nullptr)
  1031. : m_support(support), m_pPluginToolsPaths(pPluginToolsPaths), m_dumpName(dumpName) {}
  1032. void RunFileCheckFromFileCommands(LPCWSTR fileName) {
  1033. // Assume UTF-8 files.
  1034. auto cmds = GetRunLines(fileName);
  1035. // Iterate over all RUN lines
  1036. unsigned runIdx = 0;
  1037. for (auto &cmd : cmds) {
  1038. std::wstring dumpStr;
  1039. std::wstringstream os;
  1040. LPCWSTR dumpName = nullptr;
  1041. if (m_dumpName) {
  1042. os << m_dumpName << L"." << runIdx << L".txt";
  1043. dumpStr = os.str();
  1044. dumpName = dumpStr.c_str();
  1045. }
  1046. RunFileCheckFromCommands(cmd.c_str(), fileName, dumpName);
  1047. // If any of the RUN cmd fails then skip executing remaining cmds
  1048. // and report the error
  1049. if (this->RunResult != 0) {
  1050. this->ErrorMessage = cmd + "\n" + this->ErrorMessage;
  1051. break;
  1052. }
  1053. runIdx += 1;
  1054. }
  1055. }
  1056. void RunHashTestFromFileCommands(LPCWSTR fileName) {
  1057. // Assume UTF-8 files.
  1058. std::string commands(GetFirstLine(fileName));
  1059. return RunHashTestFromCommands(commands.c_str(), fileName);
  1060. }
  1061. };
  1062. FileRunTestResult FileRunTestResult::RunHashTestFromFileCommands(LPCWSTR fileName) {
  1063. dxc::DxcDllSupport dllSupport;
  1064. IFT(dllSupport.Initialize());
  1065. FileRunTestResultImpl result(dllSupport);
  1066. result.RunHashTestFromFileCommands(fileName);
  1067. return result;
  1068. }
  1069. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName,
  1070. PluginToolsPaths *pPluginToolsPaths /*=nullptr*/,
  1071. LPCWSTR dumpName /*=nullptr*/) {
  1072. dxc::DxcDllSupport dllSupport;
  1073. IFT(dllSupport.Initialize());
  1074. FileRunTestResultImpl result(dllSupport, pPluginToolsPaths, dumpName);
  1075. result.RunFileCheckFromFileCommands(fileName);
  1076. return result;
  1077. }
  1078. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName, dxc::DxcDllSupport &dllSupport,
  1079. PluginToolsPaths *pPluginToolsPaths /*=nullptr*/,
  1080. LPCWSTR dumpName /*=nullptr*/) {
  1081. FileRunTestResultImpl result(dllSupport, pPluginToolsPaths, dumpName);
  1082. result.RunFileCheckFromFileCommands(fileName);
  1083. return result;
  1084. }
  1085. void ParseCommandParts(LPCSTR commands, LPCWSTR fileName,
  1086. std::vector<FileRunCommandPart> &parts) {
  1087. // Barely enough parsing here.
  1088. commands = strstr(commands, "RUN: ");
  1089. if (!commands) {
  1090. return;
  1091. }
  1092. commands += strlen("RUN: ");
  1093. LPCSTR endCommands = strchr(commands, '\0');
  1094. while (commands != endCommands) {
  1095. LPCSTR nextStart;
  1096. LPCSTR thisEnd = strchr(commands, '|');
  1097. if (!thisEnd) {
  1098. nextStart = thisEnd = endCommands;
  1099. } else {
  1100. nextStart = thisEnd + 2;
  1101. }
  1102. LPCSTR commandEnd = strchr(commands, ' ');
  1103. if (!commandEnd)
  1104. commandEnd = endCommands;
  1105. parts.emplace_back(std::string(commands, commandEnd),
  1106. std::string(commandEnd, thisEnd), fileName);
  1107. commands = nextStart;
  1108. }
  1109. }
  1110. void ParseCommandPartsFromFile(LPCWSTR fileName,
  1111. std::vector<FileRunCommandPart> &parts) {
  1112. // Assume UTF-8 files.
  1113. std::string commands(GetFirstLine(fileName));
  1114. ParseCommandParts(commands.c_str(), fileName, parts);
  1115. }