FileCheckerTest.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  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 "dxc/Support/Global.h"
  31. #include "dxc/Support/dxcapi.use.h"
  32. #include "dxc/Support/HLSLOptions.h"
  33. #include "dxc/Support/Unicode.h"
  34. #include "dxc/DxilContainer/DxilContainer.h"
  35. #include "dxc/Test/D3DReflectionDumper.h"
  36. #include "d3d12shader.h"
  37. using namespace std;
  38. using namespace hlsl_test;
  39. FileRunCommandPart::FileRunCommandPart(const std::string &command, const std::string &arguments, LPCWSTR commandFileName) :
  40. Command(command), Arguments(arguments), CommandFileName(commandFileName) { }
  41. FileRunCommandResult FileRunCommandPart::RunHashTests(dxc::DxcDllSupport &DllSupport) {
  42. if (0 == _stricmp(Command.c_str(), "%dxc")) {
  43. return RunDxcHashTest(DllSupport);
  44. }
  45. else {
  46. return FileRunCommandResult::Success();
  47. }
  48. }
  49. FileRunCommandResult FileRunCommandPart::Run(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior,
  50. PluginToolsPaths *pPluginToolsPaths /*=nullptr*/) {
  51. bool isFileCheck =
  52. 0 == _stricmp(Command.c_str(), "FileCheck") ||
  53. 0 == _stricmp(Command.c_str(), "%FileCheck");
  54. bool isXFail = 0 == _stricmp(Command.c_str(), "xfail");
  55. bool consumeErrors = isFileCheck || isXFail;
  56. // Stop the pipeline if on errors unless the command can consume them.
  57. if (Prior != nullptr && Prior->ExitCode && !consumeErrors) {
  58. FileRunCommandResult result = *Prior;
  59. result.AbortPipeline = true;
  60. return result;
  61. }
  62. // We would add support for 'not' and 'llc' here.
  63. if (isFileCheck) {
  64. return RunFileChecker(Prior);
  65. }
  66. else if (isXFail) {
  67. return RunXFail(Prior);
  68. }
  69. else if (0 == _stricmp(Command.c_str(), "tee")) {
  70. return RunTee(Prior);
  71. }
  72. else if (0 == _stricmp(Command.c_str(), "fc")) {
  73. return RunFileCompareText(Prior);
  74. }
  75. else if (0 == _stricmp(Command.c_str(), "%dxilver")) {
  76. return RunDxilVer(DllSupport, Prior);
  77. }
  78. else if (0 == _stricmp(Command.c_str(), "%dxc")) {
  79. return RunDxc(DllSupport, Prior);
  80. }
  81. else if (0 == _stricmp(Command.c_str(), "%dxv")) {
  82. return RunDxv(DllSupport, Prior);
  83. }
  84. else if (0 == _stricmp(Command.c_str(), "%opt")) {
  85. return RunOpt(DllSupport, Prior);
  86. }
  87. else if (0 == _stricmp(Command.c_str(), "%D3DReflect")) {
  88. return RunD3DReflect(DllSupport, Prior);
  89. }
  90. else if (pPluginToolsPaths != nullptr) {
  91. auto it = pPluginToolsPaths->find(Command.c_str());
  92. if (it != pPluginToolsPaths->end()) {
  93. return RunFromPath(it->second, Prior);
  94. }
  95. }
  96. FileRunCommandResult result {};
  97. result.ExitCode = 1;
  98. result.StdErr = "Unrecognized command ";
  99. result.StdErr += Command;
  100. return result;
  101. }
  102. FileRunCommandResult FileRunCommandPart::RunFileChecker(const FileRunCommandResult *Prior) {
  103. if (!Prior) return FileRunCommandResult::Error("Prior command required to generate stdin");
  104. FileCheckForTest t;
  105. t.CheckFilename = CW2A(CommandFileName, CP_UTF8);
  106. t.InputForStdin = Prior->ExitCode ? Prior->StdErr : Prior->StdOut;
  107. // Parse command arguments
  108. static constexpr char checkPrefixStr[] = "-check-prefix=";
  109. static constexpr char checkPrefixesStr[] = "-check-prefixes=";
  110. bool hasInputFilename = false;
  111. for (const std::string& arg : strtok(Arguments)) {
  112. if (arg == "%s") hasInputFilename = true;
  113. else if (arg == "-input=stderr") t.InputForStdin = Prior->StdErr;
  114. else if (strstartswith(arg, checkPrefixStr))
  115. t.CheckPrefixes.emplace_back(arg.substr(sizeof(checkPrefixStr) - 1));
  116. else if (strstartswith(arg, checkPrefixesStr)) {
  117. auto prefixes = strtok(arg.substr(sizeof(checkPrefixesStr) - 1), ", ");
  118. for (auto &prefix : prefixes)
  119. t.CheckPrefixes.emplace_back(prefix);
  120. }
  121. else return FileRunCommandResult::Error("Invalid argument");
  122. }
  123. if (!hasInputFilename) return FileRunCommandResult::Error("Missing input filename");
  124. FileRunCommandResult result {};
  125. // Run
  126. result.ExitCode = t.Run();
  127. result.StdOut = t.test_outs;
  128. result.StdErr = t.test_errs;
  129. // Capture the input as well.
  130. if (result.ExitCode != 0 && Prior != nullptr) {
  131. result.StdErr += "\n<full input to FileCheck>\n";
  132. result.StdErr += t.InputForStdin;
  133. }
  134. return result;
  135. }
  136. FileRunCommandResult FileRunCommandPart::ReadOptsForDxc(
  137. hlsl::options::MainArgs &argStrings, hlsl::options::DxcOpts &Opts) {
  138. std::string args(strtrim(Arguments));
  139. const char *inputPos = strstr(args.c_str(), "%s");
  140. if (inputPos == nullptr)
  141. return FileRunCommandResult::Error("Only supported pattern includes input file as argument");
  142. args.erase(inputPos - args.c_str(), strlen("%s"));
  143. llvm::StringRef argsRef = args;
  144. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  145. argsRef.split(splitArgs, " ");
  146. argStrings = hlsl::options::MainArgs(splitArgs);
  147. std::string errorString;
  148. llvm::raw_string_ostream errorStream(errorString);
  149. int RunResult = ReadDxcOpts(hlsl::options::getHlslOptTable(), /*flagsToInclude*/ 0,
  150. argStrings, Opts, errorStream);
  151. errorStream.flush();
  152. if (RunResult)
  153. return FileRunCommandResult::Error(RunResult, errorString);
  154. return FileRunCommandResult::Success("");
  155. }
  156. static HRESULT ReAssembleTo(dxc::DxcDllSupport &DllSupport, void *bitcode, UINT32 size, IDxcBlob **pBlob) {
  157. CComPtr<IDxcAssembler> pAssembler;
  158. CComPtr<IDxcLibrary> pLibrary;
  159. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  160. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  161. CComPtr<IDxcBlobEncoding> pInBlob;
  162. IFT(pLibrary->CreateBlobWithEncodingFromPinned(bitcode, size, 0, &pInBlob));
  163. CComPtr<IDxcOperationResult> pResult;
  164. pAssembler->AssembleToContainer(pInBlob, &pResult);
  165. HRESULT Result = 0;
  166. IFT(pResult->GetStatus(&Result));
  167. IFT(Result);
  168. IFT(pResult->GetResult(pBlob));
  169. return S_OK;
  170. }
  171. static HRESULT GetDxilBitcode(dxc::DxcDllSupport &DllSupport, IDxcBlob *pCompiledBlob, IDxcBlob **pBitcodeBlob) {
  172. CComPtr<IDxcContainerReflection> pReflection;
  173. CComPtr<IDxcLibrary> pLibrary;
  174. IFT(DllSupport.CreateInstance(CLSID_DxcContainerReflection, &pReflection));
  175. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  176. IFT(pReflection->Load(pCompiledBlob));
  177. UINT32 uIndex = 0;
  178. IFT(pReflection->FindFirstPartKind(hlsl::DFCC_DXIL, &uIndex));
  179. CComPtr<IDxcBlob> pPart;
  180. IFT(pReflection->GetPartContent(uIndex, &pPart));
  181. auto header = (hlsl::DxilProgramHeader*)pPart->GetBufferPointer();
  182. void *bitcode = (char *)&header->BitcodeHeader + header->BitcodeHeader.BitcodeOffset;
  183. UINT32 bitcode_size = header->BitcodeHeader.BitcodeSize;
  184. CComPtr<IDxcBlobEncoding> pBlob;
  185. IFT(pLibrary->CreateBlobWithEncodingFromPinned(bitcode, bitcode_size, 0, &pBlob));
  186. *pBitcodeBlob = pBlob.Detach();
  187. return S_OK;
  188. }
  189. static HRESULT CompileForHash(hlsl::options::DxcOpts &opts, LPCWSTR CommandFileName, dxc::DxcDllSupport &DllSupport, std::vector<LPCWSTR> &flags, IDxcBlob **ppHashBlob, std::string &output) {
  190. CComPtr<IDxcLibrary> pLibrary;
  191. CComPtr<IDxcCompiler> pCompiler;
  192. CComPtr<IDxcCompiler2> pCompiler2;
  193. CComPtr<IDxcOperationResult> pResult;
  194. CComPtr<IDxcBlobEncoding> pSource;
  195. CComPtr<IDxcBlob> pCompiledBlob;
  196. CComPtr<IDxcBlob> pCompiledName;
  197. CComPtr<IDxcIncludeHandler> pIncludeHandler;
  198. WCHAR *pDebugName = nullptr;
  199. CComPtr<IDxcBlob> pPDBBlob;
  200. std::wstring entry =
  201. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  202. std::wstring profile =
  203. Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
  204. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  205. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  206. IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
  207. IFT(DllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  208. IFT(pCompiler.QueryInterface(&pCompiler2));
  209. IFT(pCompiler2->CompileWithDebug(pSource, CommandFileName, entry.c_str(), profile.c_str(),
  210. flags.data(), flags.size(), nullptr, 0, pIncludeHandler, &pResult, &pDebugName, &pPDBBlob));
  211. HRESULT resultStatus = 0;
  212. IFT(pResult->GetStatus(&resultStatus));
  213. if (SUCCEEDED(resultStatus)) {
  214. IFT(pResult->GetResult(&pCompiledBlob));
  215. CComPtr<IDxcContainerReflection> pReflection;
  216. IFT(DllSupport.CreateInstance(CLSID_DxcContainerReflection, &pReflection));
  217. // If failed to load here, it's likely some non-compile operation thing. Just fail the hash generation.
  218. if (FAILED(pReflection->Load(pCompiledBlob)))
  219. return E_FAIL;
  220. *ppHashBlob = nullptr;
  221. UINT32 uHashIdx = 0;
  222. if (SUCCEEDED(pReflection->FindFirstPartKind(hlsl::DFCC_ShaderHash, &uHashIdx))) {
  223. CComPtr<IDxcBlob> pHashBlob;
  224. IFT(pReflection->GetPartContent(uHashIdx, &pHashBlob));
  225. *ppHashBlob = pHashBlob.Detach();
  226. }
  227. // Test that PDB is generated correctly.
  228. // This test needs to be done elsewhere later, ideally a fully
  229. // customizable test on all our test set with different compile options.
  230. if (pPDBBlob) {
  231. IFT(pReflection->Load(pPDBBlob));
  232. UINT32 uDebugInfoIndex = 0;
  233. IFT(pReflection->FindFirstPartKind(hlsl::DFCC_ShaderDebugInfoDXIL, &uDebugInfoIndex));
  234. }
  235. return S_OK;
  236. }
  237. else {
  238. CComPtr<IDxcBlobEncoding> pErrors;
  239. IFT(pResult->GetErrorBuffer(&pErrors));
  240. const char *errors = (char *)pErrors->GetBufferPointer();
  241. output = errors;
  242. return resultStatus;
  243. }
  244. }
  245. FileRunCommandResult FileRunCommandPart::RunDxcHashTest(dxc::DxcDllSupport &DllSupport) {
  246. hlsl::options::MainArgs args;
  247. hlsl::options::DxcOpts opts;
  248. ReadOptsForDxc(args, opts);
  249. std::vector<std::wstring> argWStrings;
  250. CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
  251. // Extract the vanilla flags for the test (i.e. no debug or ast-dump)
  252. std::vector<LPCWSTR> original_flags;
  253. for (const std::wstring &a : argWStrings) {
  254. if (a.find(L"ast-dump") != std::wstring::npos) continue;
  255. if (a.find(L"Zi") != std::wstring::npos) continue;
  256. original_flags.push_back(a.data());
  257. }
  258. std::string originalOutput;
  259. CComPtr<IDxcBlob> pOriginalHash;
  260. // If failed the original compilation, just pass the test. The original test was likely
  261. // testing for failure.
  262. if (FAILED(CompileForHash(opts, CommandFileName, DllSupport, original_flags, &pOriginalHash, originalOutput)))
  263. return FileRunCommandResult::Success();
  264. // Results of our compilations
  265. CComPtr<IDxcBlob> pHash1;
  266. std::string Output0;
  267. CComPtr<IDxcBlob> pHash0;
  268. std::string Output1;
  269. // Fail if -Qstrip_reflect failed the compilation
  270. std::vector<LPCWSTR> normal_flags = original_flags;
  271. normal_flags.push_back(L"-Qstrip_reflect");
  272. normal_flags.push_back(L"-Zsb");
  273. std::string StdErr;
  274. if (FAILED(CompileForHash(opts, CommandFileName, DllSupport, normal_flags, &pHash0, Output0))) {
  275. StdErr += "Adding Qstrip_reflect failed compilation.";
  276. StdErr += originalOutput;
  277. StdErr += Output0;
  278. return FileRunCommandResult::Error(StdErr);
  279. }
  280. // Fail if -Qstrip_reflect failed the compilation
  281. std::vector<LPCWSTR> dbg_flags = original_flags;
  282. dbg_flags.push_back(L"/Zi");
  283. dbg_flags.push_back(L"-Qstrip_reflect");
  284. dbg_flags.push_back(L"-Zsb");
  285. if (FAILED(CompileForHash(opts, CommandFileName, DllSupport, dbg_flags, &pHash1, Output1))) {
  286. return FileRunCommandResult::Error("Adding Qstrip_reflect and Zi failed compilation.");
  287. }
  288. if (pHash0->GetBufferSize() != pHash1->GetBufferSize() || 0 != memcmp(pHash0->GetBufferPointer(), pHash0->GetBufferPointer(), pHash1->GetBufferSize())) {
  289. StdErr = "Hashes do not match between normal and debug!!!\n";
  290. StdErr += Output0;
  291. StdErr += Output1;
  292. return FileRunCommandResult::Error(StdErr);
  293. }
  294. return FileRunCommandResult::Success();
  295. }
  296. static FileRunCommandResult CheckDxilVer(dxc::DxcDllSupport& DllSupport,
  297. unsigned RequiredDxilMajor,
  298. unsigned RequiredDxilMinor,
  299. bool bCheckValidator = true) {
  300. bool Supported = true;
  301. // If the following fails, we have Dxil 1.0 compiler
  302. unsigned DxilMajor = 1, DxilMinor = 0;
  303. GetVersion(DllSupport, CLSID_DxcCompiler, DxilMajor, DxilMinor);
  304. Supported &= hlsl::DXIL::CompareVersions(DxilMajor, DxilMinor, RequiredDxilMajor, RequiredDxilMinor) >= 0;
  305. if (bCheckValidator) {
  306. // If the following fails, we have validator 1.0
  307. unsigned ValMajor = 1, ValMinor = 0;
  308. GetVersion(DllSupport, CLSID_DxcValidator, ValMajor, ValMinor);
  309. Supported &= hlsl::DXIL::CompareVersions(ValMajor, ValMinor, RequiredDxilMajor, RequiredDxilMinor) >= 0;
  310. }
  311. if (!Supported) {
  312. FileRunCommandResult result {};
  313. result.StdErr = "Skipping test due to unsupported dxil version";
  314. result.ExitCode = 0; // Succeed the test
  315. result.AbortPipeline = true;
  316. return result;
  317. }
  318. return FileRunCommandResult::Success();
  319. }
  320. FileRunCommandResult FileRunCommandPart::RunDxc(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  321. // Support piping stdin from prior if needed.
  322. UNREFERENCED_PARAMETER(Prior);
  323. hlsl::options::MainArgs args;
  324. hlsl::options::DxcOpts opts;
  325. FileRunCommandResult readOptsResult = ReadOptsForDxc(args, opts);
  326. if (readOptsResult.ExitCode) return readOptsResult;
  327. std::wstring entry =
  328. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  329. std::wstring profile =
  330. Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
  331. std::vector<LPCWSTR> flags;
  332. if (opts.CodeGenHighLevel) {
  333. flags.push_back(L"-fcgl");
  334. }
  335. // Skip targets that require a newer compiler or validator.
  336. // Some features may require newer compiler/validator than indicated by the
  337. // shader model, but these should use %dxilver explicitly.
  338. {
  339. unsigned RequiredDxilMajor = 1, RequiredDxilMinor = 0;
  340. llvm::StringRef stage;
  341. IFTBOOL(ParseTargetProfile(opts.TargetProfile, stage, RequiredDxilMajor, RequiredDxilMinor), E_INVALIDARG);
  342. if (RequiredDxilMinor != 0xF && stage.compare("rootsig") != 0) {
  343. // Convert stage to minimum dxil/validator version:
  344. RequiredDxilMajor = std::max(RequiredDxilMajor, (unsigned)6) - 5;
  345. FileRunCommandResult result = CheckDxilVer(DllSupport, RequiredDxilMajor, RequiredDxilMinor, !opts.DisableValidation);
  346. if (result.AbortPipeline) {
  347. return result;
  348. }
  349. }
  350. }
  351. // For now, too many tests are sensitive to stripping the refleciton info
  352. // from the main module, so use this flag to prevent this until tests
  353. // can be updated.
  354. // That is, unless the test explicitly requests -Qstrip_reflect_from_dxil or -Qstrip_reflect
  355. if (!opts.StripReflectionFromDxil && !opts.StripReflection) {
  356. flags.push_back(L"-Qkeep_reflect_in_dxil");
  357. }
  358. std::vector<std::wstring> argWStrings;
  359. CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
  360. for (const std::wstring &a : argWStrings)
  361. flags.push_back(a.data());
  362. CComPtr<IDxcLibrary> pLibrary;
  363. CComPtr<IDxcCompiler> pCompiler;
  364. CComPtr<IDxcOperationResult> pResult;
  365. CComPtr<IDxcBlobEncoding> pSource;
  366. CComPtr<IDxcBlobEncoding> pDisassembly;
  367. CComPtr<IDxcBlob> pCompiledBlob;
  368. CComPtr<IDxcIncludeHandler> pIncludeHandler;
  369. HRESULT resultStatus;
  370. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  371. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  372. IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
  373. IFT(DllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  374. IFT(pCompiler->Compile(pSource, CommandFileName, entry.c_str(), profile.c_str(),
  375. flags.data(), flags.size(), nullptr, 0, pIncludeHandler, &pResult));
  376. IFT(pResult->GetStatus(&resultStatus));
  377. FileRunCommandResult result = {};
  378. if (SUCCEEDED(resultStatus)) {
  379. IFT(pResult->GetResult(&pCompiledBlob));
  380. if (!opts.AstDump) {
  381. IFT(pCompiler->Disassemble(pCompiledBlob, &pDisassembly));
  382. result.StdOut = BlobToUtf8(pDisassembly);
  383. } else {
  384. result.StdOut = BlobToUtf8(pCompiledBlob);
  385. }
  386. CComPtr<IDxcBlobEncoding> pStdErr;
  387. IFT(pResult->GetErrorBuffer(&pStdErr));
  388. result.StdErr = BlobToUtf8(pStdErr);
  389. result.ExitCode = 0;
  390. }
  391. else {
  392. IFT(pResult->GetErrorBuffer(&pDisassembly));
  393. result.StdErr = BlobToUtf8(pDisassembly);
  394. result.ExitCode = resultStatus;
  395. }
  396. result.OpResult = pResult;
  397. return result;
  398. }
  399. FileRunCommandResult FileRunCommandPart::RunDxv(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  400. std::string args(strtrim(Arguments));
  401. const char *inputPos = strstr(args.c_str(), "%s");
  402. if (inputPos == nullptr) {
  403. return FileRunCommandResult::Error("Only supported pattern includes input file as argument");
  404. }
  405. args.erase(inputPos - args.c_str(), strlen("%s"));
  406. llvm::StringRef argsRef = args;
  407. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  408. argsRef.split(splitArgs, " ");
  409. IFTMSG(splitArgs.size()==1, "wrong arg num for dxv");
  410. CComPtr<IDxcLibrary> pLibrary;
  411. CComPtr<IDxcAssembler> pAssembler;
  412. CComPtr<IDxcValidator> pValidator;
  413. CComPtr<IDxcOperationResult> pResult;
  414. CComPtr<IDxcBlobEncoding> pSource;
  415. CComPtr<IDxcBlob> pContainerBlob;
  416. HRESULT resultStatus;
  417. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  418. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  419. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  420. IFT(pAssembler->AssembleToContainer(pSource, &pResult));
  421. IFT(pResult->GetStatus(&resultStatus));
  422. if (FAILED(resultStatus)) {
  423. CComPtr<IDxcBlobEncoding> pAssembleBlob;
  424. IFT(pResult->GetErrorBuffer(&pAssembleBlob));
  425. return FileRunCommandResult::Error(resultStatus, BlobToUtf8(pAssembleBlob));
  426. }
  427. IFT(pResult->GetResult(&pContainerBlob));
  428. IFT(DllSupport.CreateInstance(CLSID_DxcValidator, &pValidator));
  429. CComPtr<IDxcOperationResult> pValidationResult;
  430. IFT(pValidator->Validate(pContainerBlob, DxcValidatorFlags_InPlaceEdit,
  431. &pValidationResult));
  432. IFT(pValidationResult->GetStatus(&resultStatus));
  433. if (FAILED(resultStatus)) {
  434. CComPtr<IDxcBlobEncoding> pValidateBlob;
  435. IFT(pValidationResult->GetErrorBuffer(&pValidateBlob));
  436. return FileRunCommandResult::Success(BlobToUtf8(pValidateBlob));
  437. }
  438. return FileRunCommandResult::Success("");
  439. }
  440. FileRunCommandResult FileRunCommandPart::RunOpt(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  441. std::string args(strtrim(Arguments));
  442. const char *inputPos = strstr(args.c_str(), "%s");
  443. if (inputPos == nullptr && Prior == nullptr) {
  444. return FileRunCommandResult::Error("Only supported patterns are input file as argument or prior "
  445. "command with disassembly");
  446. }
  447. CComPtr<IDxcLibrary> pLibrary;
  448. CComPtr<IDxcOptimizer> pOptimizer;
  449. CComPtr<IDxcBlobEncoding> pSource;
  450. CComPtr<IDxcBlobEncoding> pOutputText;
  451. CComPtr<IDxcBlob> pOutputModule;
  452. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  453. IFT(DllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
  454. if (inputPos != nullptr) {
  455. args.erase(inputPos - args.c_str(), strlen("%s"));
  456. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  457. }
  458. else {
  459. assert(Prior != nullptr && "else early check should have returned");
  460. CComPtr<IDxcAssembler> pAssembler;
  461. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  462. IFT(pLibrary->CreateBlobWithEncodingFromPinned(
  463. Prior->StdOut.c_str(), Prior->StdOut.size(), CP_UTF8,
  464. &pSource));
  465. }
  466. args = strtrim(args);
  467. llvm::StringRef argsRef = args;
  468. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  469. argsRef.split(splitArgs, " ");
  470. std::vector<LPCWSTR> options;
  471. std::vector<std::wstring> optionStrings;
  472. for (llvm::StringRef S : splitArgs) {
  473. optionStrings.push_back(
  474. Unicode::UTF8ToUTF16StringOrThrow(strtrim(S.str()).c_str()));
  475. }
  476. // Add the options outside the above loop in case the vector is resized.
  477. for (const std::wstring& str : optionStrings)
  478. options.push_back(str.c_str());
  479. IFT(pOptimizer->RunOptimizer(pSource, options.data(), options.size(),
  480. &pOutputModule, &pOutputText));
  481. return FileRunCommandResult::Success(BlobToUtf8(pOutputText));
  482. }
  483. FileRunCommandResult FileRunCommandPart::RunD3DReflect(dxc::DxcDllSupport &DllSupport, const FileRunCommandResult *Prior) {
  484. std::string args(strtrim(Arguments));
  485. if (args != "%s")
  486. return FileRunCommandResult::Error("Only supported pattern is a plain input file");
  487. if (!Prior)
  488. return FileRunCommandResult::Error("Prior command required to generate stdin");
  489. CComPtr<IDxcLibrary> pLibrary;
  490. CComPtr<IDxcBlobEncoding> pSource;
  491. CComPtr<IDxcAssembler> pAssembler;
  492. CComPtr<IDxcOperationResult> pResult;
  493. CComPtr<ID3D12ShaderReflection> pShaderReflection;
  494. CComPtr<ID3D12LibraryReflection> pLibraryReflection;
  495. CComPtr<IDxcContainerReflection> containerReflection;
  496. uint32_t partCount;
  497. CComPtr<IDxcBlob> pContainerBlob;
  498. HRESULT resultStatus;
  499. bool blobFound = false;
  500. std::ostringstream ss;
  501. D3DReflectionDumper dumper(ss);
  502. IFT(DllSupport.CreateInstance(CLSID_DxcLibrary, &pLibrary));
  503. IFT(DllSupport.CreateInstance(CLSID_DxcAssembler, &pAssembler));
  504. IFT(pLibrary->CreateBlobWithEncodingFromPinned(
  505. (LPBYTE)Prior->StdOut.c_str(), Prior->StdOut.size(), CP_UTF8,
  506. &pSource));
  507. IFT(pAssembler->AssembleToContainer(pSource, &pResult));
  508. IFT(pResult->GetStatus(&resultStatus));
  509. if (FAILED(resultStatus)) {
  510. CComPtr<IDxcBlobEncoding> pAssembleBlob;
  511. IFT(pResult->GetErrorBuffer(&pAssembleBlob));
  512. return FileRunCommandResult::Error(resultStatus, BlobToUtf8(pAssembleBlob));
  513. }
  514. IFT(pResult->GetResult(&pContainerBlob));
  515. VERIFY_SUCCEEDED(DllSupport.CreateInstance(CLSID_DxcContainerReflection, &containerReflection));
  516. VERIFY_SUCCEEDED(containerReflection->Load(pContainerBlob));
  517. VERIFY_SUCCEEDED(containerReflection->GetPartCount(&partCount));
  518. for (uint32_t i = 0; i < partCount; ++i) {
  519. uint32_t kind;
  520. VERIFY_SUCCEEDED(containerReflection->GetPartKind(i, &kind));
  521. if (kind == (uint32_t)hlsl::DxilFourCC::DFCC_DXIL) {
  522. blobFound = true;
  523. CComPtr<IDxcBlob> pPart;
  524. IFT(containerReflection->GetPartContent(i, &pPart));
  525. const hlsl::DxilProgramHeader *pProgramHeader =
  526. reinterpret_cast<const hlsl::DxilProgramHeader*>(pPart->GetBufferPointer());
  527. VERIFY_IS_TRUE(IsValidDxilProgramHeader(pProgramHeader, (uint32_t)pPart->GetBufferSize()));
  528. hlsl::DXIL::ShaderKind SK = hlsl::GetVersionShaderType(pProgramHeader->ProgramVersion);
  529. if (SK == hlsl::DXIL::ShaderKind::Library)
  530. VERIFY_SUCCEEDED(containerReflection->GetPartReflection(i, IID_PPV_ARGS(&pLibraryReflection)));
  531. else
  532. VERIFY_SUCCEEDED(containerReflection->GetPartReflection(i, IID_PPV_ARGS(&pShaderReflection)));
  533. break;
  534. }
  535. }
  536. if (!blobFound) {
  537. return FileRunCommandResult::Error("Unable to find DXIL part");
  538. } else if (pShaderReflection) {
  539. dumper.Dump(pShaderReflection);
  540. } else if (pLibraryReflection) {
  541. dumper.Dump(pLibraryReflection);
  542. }
  543. ss.flush();
  544. return FileRunCommandResult::Success(ss.str());
  545. }
  546. FileRunCommandResult FileRunCommandPart::RunTee(const FileRunCommandResult *Prior) {
  547. if (Prior == nullptr) {
  548. return FileRunCommandResult::Error("tee requires a prior command");
  549. }
  550. // Ignore commands for now - simply log out through test framework.
  551. {
  552. CA2W outWide(Prior->StdOut.c_str(), CP_UTF8);
  553. WEX::Logging::Log::Comment(outWide.m_psz);
  554. }
  555. if (!Prior->StdErr.empty()) {
  556. CA2W errWide(Prior->StdErr.c_str(), CP_UTF8);
  557. WEX::Logging::Log::Comment(L"<stderr>");
  558. WEX::Logging::Log::Comment(errWide.m_psz);
  559. }
  560. return *Prior;
  561. }
  562. void FileRunCommandPart::SubstituteFilenameVars(std::string &args) {
  563. size_t pos;
  564. std::string baseFileName = CW2A(CommandFileName);
  565. if ((pos = baseFileName.find_last_of(".")) != std::string::npos) {
  566. baseFileName = baseFileName.substr(0, pos);
  567. }
  568. while ((pos = args.find("%t")) != std::string::npos) {
  569. args.replace(pos, 2, baseFileName.c_str());
  570. }
  571. while ((pos = args.find("%b")) != std::string::npos) {
  572. args.replace(pos, 2, baseFileName.c_str());
  573. }
  574. }
  575. #if _WIN32
  576. bool FileRunCommandPart::ReadFileContentToString(HANDLE hFile, std::string &str) {
  577. char buffer[1024];
  578. DWORD len;
  579. size_t size = ::GetFileSize(hFile, nullptr);
  580. if (size == INVALID_FILE_SIZE) {
  581. return false;
  582. }
  583. str.reserve(size);
  584. if (::SetFilePointer(hFile, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) {
  585. return false;
  586. }
  587. while (::ReadFile(hFile, buffer, sizeof(buffer), &len, nullptr) && len > 0) {
  588. str.append(buffer, len);
  589. }
  590. return true;
  591. }
  592. #endif
  593. FileRunCommandResult FileRunCommandPart::RunFileCompareText(const FileRunCommandResult *Prior) {
  594. if (Prior != nullptr) {
  595. return FileRunCommandResult::Error("prior command not supported");
  596. }
  597. FileRunCommandResult result;
  598. result.ExitCode = 1;
  599. // strip leading and trailing spaces and split
  600. std::string args(strtrim(Arguments));
  601. size_t pos;
  602. if ((pos = args.find_first_of(' ')) == std::string::npos) {
  603. return FileRunCommandResult::Error("RunFileCompareText expected 2 file arguments.");
  604. }
  605. std::string fileName1 = args.substr(0, pos);
  606. std::string fileName2 = strtrim(args.substr(pos + 1));
  607. // replace %t and %b with the command file name without extension
  608. SubstituteFilenameVars(fileName1);
  609. SubstituteFilenameVars(fileName2);
  610. // read file content and compare
  611. CA2W fileName1W(fileName1.c_str());
  612. CA2W fileName2W(fileName2.c_str());
  613. hlsl_test::LogCommentFmt(L"Comparing files %s and %s", fileName1W.m_psz, fileName2W.m_psz);
  614. std::ifstream ifs1(fileName1, std::ifstream::in);
  615. if (ifs1.fail()) {
  616. hlsl_test::LogCommentFmt(L"Failed to open %s", fileName1W.m_psz);
  617. return result;
  618. }
  619. std::string file1Content((std::istreambuf_iterator<char>(ifs1)), (std::istreambuf_iterator<char>()));
  620. std::ifstream ifs2(fileName2, std::ifstream::in);
  621. if (ifs2.fail()) {
  622. hlsl_test::LogCommentFmt(L"Failed to open %s", fileName2W.m_psz);
  623. return result;
  624. }
  625. std::string file2Content((std::istreambuf_iterator<char>(ifs2)), (std::istreambuf_iterator<char>()));
  626. if (file1Content.compare(file2Content) == 0) {
  627. hlsl_test::LogCommentFmt(L"No differences found.");
  628. result.ExitCode = 0;
  629. }
  630. else {
  631. hlsl_test::LogCommentFmt(L"Files are different!");
  632. }
  633. return result;
  634. }
  635. FileRunCommandResult FileRunCommandPart::RunXFail(const FileRunCommandResult *Prior) {
  636. if (Prior == nullptr)
  637. return FileRunCommandResult::Error("XFail requires a prior command");
  638. if (Prior->ExitCode == 0) {
  639. return FileRunCommandResult::Error("XFail expected a failure from previous command");
  640. } else {
  641. return FileRunCommandResult::Success("");
  642. }
  643. }
  644. FileRunCommandResult FileRunCommandPart::RunDxilVer(dxc::DxcDllSupport& DllSupport, const FileRunCommandResult* Prior) {
  645. Arguments = strtrim(Arguments);
  646. if (Arguments.size() != 3 || !std::isdigit(Arguments[0]) || Arguments[1] != '.' || !std::isdigit(Arguments[2])) {
  647. return FileRunCommandResult::Error("Invalid dxil version format");
  648. }
  649. unsigned RequiredDxilMajor = Arguments[0] - '0';
  650. unsigned RequiredDxilMinor = Arguments[2] - '0';
  651. return CheckDxilVer(DllSupport, RequiredDxilMajor, RequiredDxilMinor);
  652. }
  653. #ifndef _WIN32
  654. FileRunCommandResult FileRunCommandPart::RunFromPath(const std::string &toolPath, const FileRunCommandResult *Prior) {
  655. return FileRunCommandResult::Error("RunFromPath not supported");
  656. }
  657. #else //_WIN32
  658. FileRunCommandResult FileRunCommandPart::RunFromPath(const std::string &toolPath, const FileRunCommandResult *Prior) {
  659. if (Prior != nullptr) {
  660. return FileRunCommandResult::Error("prior command not supported");
  661. }
  662. std::string args = Arguments;
  663. // replace %s with command file name
  664. size_t pos;
  665. while ((pos = args.find("%s")) != std::string::npos) {
  666. args.replace(pos, 2, CW2A(CommandFileName));
  667. }
  668. // replace %t and %b with the command file name without extension
  669. SubstituteFilenameVars(args);
  670. // Run the tool via CreateProcess, redirect stdout and strerr to temporary files
  671. std::wstring stdOutFileName = std::wstring(CommandFileName) + L".tmp_stdout";
  672. std::wstring stdErrFileName = std::wstring(CommandFileName) + L".tmp_stderr";
  673. SECURITY_ATTRIBUTES sa;
  674. ZeroMemory(&sa, sizeof(sa));
  675. sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  676. sa.bInheritHandle = true;
  677. HANDLE hStdOutFile = CreateFileW(stdOutFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa,
  678. CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
  679. IFT(hStdOutFile != INVALID_HANDLE_VALUE);
  680. HANDLE hStdErrFile = CreateFileW(stdErrFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, &sa,
  681. CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
  682. IFT(hStdErrFile!= INVALID_HANDLE_VALUE);
  683. STARTUPINFOA si;
  684. ZeroMemory(&si, sizeof(si));
  685. si.cb = sizeof(si);
  686. si.hStdOutput = hStdOutFile;
  687. si.hStdError = hStdErrFile;
  688. si.dwFlags |= STARTF_USESTDHANDLES;
  689. PROCESS_INFORMATION pi;
  690. ZeroMemory(&pi, sizeof(pi));
  691. std::vector<char> args2(args.c_str(), args.c_str() + args.size() + 1); // args to CreateProcess cannot be const char *
  692. if (!CreateProcessA(toolPath.c_str(), args2.data(), nullptr, nullptr, true, 0, nullptr, nullptr, &si, &pi)) {
  693. return FileRunCommandResult::Error("CreateProcess failed.");
  694. }
  695. ::WaitForSingleObject(pi.hProcess, 10000); // 10s timeout
  696. // Get exit code of the process
  697. FileRunCommandResult result;
  698. DWORD exitCode;
  699. if (!::GetExitCodeProcess(pi.hProcess, &exitCode)) {
  700. result = FileRunCommandResult::Error("GetExitCodeProcess failed.");
  701. }
  702. else {
  703. result.ExitCode = exitCode;
  704. }
  705. // Close process and thread handles
  706. ::CloseHandle(pi.hProcess);
  707. ::CloseHandle(pi.hThread);
  708. // Read stdout and strerr output from temporary files
  709. if (!ReadFileContentToString(hStdOutFile, result.StdOut) ||
  710. !ReadFileContentToString(hStdErrFile, result.StdErr)) {
  711. result = FileRunCommandResult::Error("RunFromPaths failed.");
  712. }
  713. // Close temporary file handles - will delete the files
  714. IFT(::CloseHandle(hStdOutFile));
  715. IFT(::CloseHandle(hStdErrFile));
  716. return result;
  717. }
  718. #endif //_WIN32
  719. class FileRunTestResultImpl : public FileRunTestResult {
  720. dxc::DxcDllSupport &m_support;
  721. PluginToolsPaths *m_pPluginToolsPaths;
  722. void RunHashTestFromCommands(LPCSTR commands, LPCWSTR fileName) {
  723. std::vector<FileRunCommandPart> parts;
  724. ParseCommandParts(commands, fileName, parts);
  725. FileRunCommandResult result;
  726. bool ran = false;
  727. for (FileRunCommandPart & part : parts) {
  728. result = part.RunHashTests(m_support);
  729. ran = true;
  730. break;
  731. }
  732. if (ran) {
  733. this->RunResult = result.ExitCode;
  734. this->ErrorMessage = result.StdErr;
  735. }
  736. else {
  737. this->RunResult = 0;
  738. }
  739. }
  740. void RunFileCheckFromCommands(LPCSTR commands, LPCWSTR fileName) {
  741. std::vector<FileRunCommandPart> parts;
  742. ParseCommandParts(commands, fileName, parts);
  743. if (parts.empty()) {
  744. this->RunResult = 1;
  745. this->ErrorMessage = "FileCheck found no commands to run";
  746. return;
  747. }
  748. FileRunCommandResult result;
  749. FileRunCommandResult* previousResult = nullptr;
  750. for (FileRunCommandPart & part : parts) {
  751. result = part.Run(m_support, previousResult, m_pPluginToolsPaths);
  752. previousResult = &result;
  753. if (result.AbortPipeline) break;
  754. }
  755. this->RunResult = result.ExitCode;
  756. this->ErrorMessage = result.StdErr;
  757. }
  758. public:
  759. FileRunTestResultImpl(dxc::DxcDllSupport &support, PluginToolsPaths *pPluginToolsPaths = nullptr)
  760. : m_support(support), m_pPluginToolsPaths(pPluginToolsPaths) {}
  761. void RunFileCheckFromFileCommands(LPCWSTR fileName) {
  762. // Assume UTF-8 files.
  763. auto cmds = GetRunLines(fileName);
  764. // Iterate over all RUN lines
  765. for (auto &cmd : cmds) {
  766. RunFileCheckFromCommands(cmd.c_str(), fileName);
  767. // If any of the RUN cmd fails then skip executing remaining cmds
  768. // and report the error
  769. if (this->RunResult != 0) break;
  770. }
  771. }
  772. void RunHashTestFromFileCommands(LPCWSTR fileName) {
  773. // Assume UTF-8 files.
  774. std::string commands(GetFirstLine(fileName));
  775. return RunHashTestFromCommands(commands.c_str(), fileName);
  776. }
  777. };
  778. FileRunTestResult FileRunTestResult::RunHashTestFromFileCommands(LPCWSTR fileName) {
  779. dxc::DxcDllSupport dllSupport;
  780. IFT(dllSupport.Initialize());
  781. FileRunTestResultImpl result(dllSupport);
  782. result.RunHashTestFromFileCommands(fileName);
  783. return result;
  784. }
  785. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName,
  786. PluginToolsPaths *pPluginToolsPaths /*=nullptr*/) {
  787. dxc::DxcDllSupport dllSupport;
  788. IFT(dllSupport.Initialize());
  789. FileRunTestResultImpl result(dllSupport, pPluginToolsPaths);
  790. result.RunFileCheckFromFileCommands(fileName);
  791. return result;
  792. }
  793. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName, dxc::DxcDllSupport &dllSupport,
  794. PluginToolsPaths *pPluginToolsPaths /*=nullptr*/) {
  795. FileRunTestResultImpl result(dllSupport, pPluginToolsPaths);
  796. result.RunFileCheckFromFileCommands(fileName);
  797. return result;
  798. }
  799. void ParseCommandParts(LPCSTR commands, LPCWSTR fileName,
  800. std::vector<FileRunCommandPart> &parts) {
  801. // Barely enough parsing here.
  802. commands = strstr(commands, "RUN: ");
  803. if (!commands) {
  804. return;
  805. }
  806. commands += strlen("RUN: ");
  807. LPCSTR endCommands = strchr(commands, '\0');
  808. while (commands != endCommands) {
  809. LPCSTR nextStart;
  810. LPCSTR thisEnd = strchr(commands, '|');
  811. if (!thisEnd) {
  812. nextStart = thisEnd = endCommands;
  813. } else {
  814. nextStart = thisEnd + 2;
  815. }
  816. LPCSTR commandEnd = strchr(commands, ' ');
  817. if (!commandEnd)
  818. commandEnd = endCommands;
  819. parts.emplace_back(std::string(commands, commandEnd),
  820. std::string(commandEnd, thisEnd), fileName);
  821. commands = nextStart;
  822. }
  823. }
  824. void ParseCommandPartsFromFile(LPCWSTR fileName,
  825. std::vector<FileRunCommandPart> &parts) {
  826. // Assume UTF-8 files.
  827. std::string commands(GetFirstLine(fileName));
  828. ParseCommandParts(commands.c_str(), fileName, parts);
  829. }