FileCheckerTest.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  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 <cassert>
  18. #include <algorithm>
  19. #include "dxc/Support/WinIncludes.h"
  20. #include "dxc/dxcapi.h"
  21. #include <atlfile.h>
  22. #include "HLSLTestData.h"
  23. #include "WexTestClass.h"
  24. #include "HlslTestUtils.h"
  25. #include "DxcTestUtils.h"
  26. #include "llvm/Support/raw_os_ostream.h"
  27. #include "dxc/Support/Global.h"
  28. #include "dxc/Support/dxcapi.use.h"
  29. #include "dxc/Support/HLSLOptions.h"
  30. #include "dxc/Support/Unicode.h"
  31. using namespace std;
  32. using namespace hlsl_test;
  33. static std::string strltrim(std::string value) {
  34. return value.erase(0, value.find_first_not_of(" \t\r\n"));
  35. }
  36. static std::string strrtrim(std::string value) {
  37. size_t last = value.find_last_not_of(" \t\r\n");
  38. return last == string::npos ? value : value.substr(0, last + 1);
  39. }
  40. static std::string strtrim(const std::string &value) {
  41. return strltrim(strrtrim(value));
  42. }
  43. static string trim(string value) {
  44. size_t leading = value.find_first_not_of(' ');
  45. if (leading != std::string::npos) {
  46. value.erase(0, leading);
  47. }
  48. size_t trailing = value.find_last_not_of(' ');
  49. if (leading != std::string::npos) {
  50. value.erase(trailing + 1);
  51. }
  52. return value;
  53. }
  54. FileRunCommandPart::FileRunCommandPart(const std::string &command, const std::string &arguments, LPCWSTR commandFileName) :
  55. Command(command), Arguments(arguments), CommandFileName(commandFileName) { }
  56. FileRunCommandPart::FileRunCommandPart(FileRunCommandPart && other) :
  57. Command(std::move(other.Command)),
  58. CommandFileName(other.CommandFileName),
  59. Arguments(std::move(other.Arguments)),
  60. RunResult(other.RunResult),
  61. StdOut(std::move(other.StdOut)),
  62. StdErr(std::move(other.StdErr)) { }
  63. void FileRunCommandPart::Run(const FileRunCommandPart *Prior) {
  64. bool isFileCheck =
  65. 0 == _stricmp(Command.c_str(), "FileCheck") ||
  66. 0 == _stricmp(Command.c_str(), "%FileCheck");
  67. // For now, propagate errors.
  68. if (Prior && Prior->RunResult) {
  69. if (isFileCheck) {
  70. RunFileChecker(Prior);
  71. } else {
  72. StdErr = Prior->StdErr;
  73. RunResult = Prior->RunResult;
  74. }
  75. return;
  76. }
  77. // We would add support for 'not' and 'llc' here.
  78. if (isFileCheck) {
  79. RunFileChecker(Prior);
  80. }
  81. else if (0 == _stricmp(Command.c_str(), "StdErrCheck")) {
  82. RunStdErrChecker(Prior);
  83. }
  84. else if (0 == _stricmp(Command.c_str(), "tee")) {
  85. RunTee(Prior);
  86. }
  87. else if (0 == _stricmp(Command.c_str(), "%dxc")) {
  88. RunDxc(Prior);
  89. }
  90. else if (0 == _stricmp(Command.c_str(), "%dxv")) {
  91. RunDxv(Prior);
  92. }
  93. else if (0 == _stricmp(Command.c_str(), "%opt")) {
  94. RunOpt(Prior);
  95. }
  96. else {
  97. RunResult = 1;
  98. StdErr = "Unrecognized command ";
  99. StdErr += Command;
  100. }
  101. }
  102. void FileRunCommandPart::RunFileChecker(const FileRunCommandPart *Prior) {
  103. std::string args(strtrim(Arguments));
  104. if (args != "%s") {
  105. StdErr = "Only supported pattern is a plain input file";
  106. RunResult = 1;
  107. return;
  108. }
  109. if (!Prior) {
  110. StdErr = "Prior command required to generate stdin";
  111. RunResult = 1;
  112. return;
  113. }
  114. CW2A fileName(CommandFileName, CP_UTF8);
  115. FileCheckForTest t;
  116. t.CheckFilename = fileName;
  117. if (Prior->RunResult)
  118. t.InputForStdin = Prior->StdErr;
  119. else
  120. t.InputForStdin = Prior->StdOut;
  121. RunResult = t.Run();
  122. StdOut = t.test_outs;
  123. StdErr = t.test_errs;
  124. // Capture the input as well.
  125. if (RunResult != 0 && Prior != nullptr) {
  126. StdErr += "\n<full input to FileCheck>\n";
  127. StdErr += t.InputForStdin;
  128. }
  129. }
  130. void FileRunCommandPart::RunStdErrChecker(const FileRunCommandPart *Prior) {
  131. std::string args(strtrim(Arguments));
  132. if (args != "%s") {
  133. StdErr = "Only supported pattern is a plain input file";
  134. RunResult = 1;
  135. return;
  136. }
  137. if (!Prior) {
  138. StdErr = "Prior command required to generate stdin";
  139. RunResult = 1;
  140. return;
  141. }
  142. CW2A fileName(CommandFileName, CP_UTF8);
  143. FileCheckForTest t;
  144. t.CheckFilename = fileName;
  145. t.InputForStdin = Prior->StdErr;
  146. RunResult = t.Run();
  147. StdOut = t.test_outs;
  148. StdErr = t.test_errs;
  149. // Capture the input as well.
  150. if (RunResult != 0 && Prior != nullptr) {
  151. StdErr += "\n<full input to StdErrCheck>\n";
  152. StdErr += t.InputForStdin;
  153. }
  154. }
  155. void FileRunCommandPart::ReadOptsForDxc(hlsl::options::MainArgs &argStrings,
  156. hlsl::options::DxcOpts &Opts) {
  157. std::string args(strtrim(Arguments));
  158. const char *inputPos = strstr(args.c_str(), "%s");
  159. if (inputPos == nullptr) {
  160. StdErr = "Only supported pattern includes input file as argument";
  161. RunResult = 1;
  162. return;
  163. }
  164. args.erase(inputPos - args.c_str(), strlen("%s"));
  165. llvm::StringRef argsRef = args;
  166. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  167. argsRef.split(splitArgs, " ");
  168. argStrings = hlsl::options::MainArgs(splitArgs);
  169. std::string errorString;
  170. llvm::raw_string_ostream errorStream(errorString);
  171. RunResult = ReadDxcOpts(hlsl::options::getHlslOptTable(), /*flagsToInclude*/ 0,
  172. argStrings, Opts, errorStream);
  173. errorStream.flush();
  174. if (RunResult) {
  175. StdErr = errorString;
  176. }
  177. }
  178. void FileRunCommandPart::RunDxc(const FileRunCommandPart *Prior) {
  179. // Support piping stdin from prior if needed.
  180. UNREFERENCED_PARAMETER(Prior);
  181. hlsl::options::MainArgs args;
  182. hlsl::options::DxcOpts opts;
  183. ReadOptsForDxc(args, opts);
  184. std::wstring entry =
  185. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  186. std::wstring profile =
  187. Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
  188. std::vector<LPCWSTR> flags;
  189. if (opts.CodeGenHighLevel) {
  190. flags.push_back(L"-fcgl");
  191. }
  192. std::vector<std::wstring> argWStrings;
  193. CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
  194. for (const std::wstring &a : argWStrings)
  195. flags.push_back(a.data());
  196. CComPtr<IDxcLibrary> pLibrary;
  197. CComPtr<IDxcCompiler> pCompiler;
  198. CComPtr<IDxcOperationResult> pResult;
  199. CComPtr<IDxcBlobEncoding> pSource;
  200. CComPtr<IDxcBlobEncoding> pDisassembly;
  201. CComPtr<IDxcBlob> pCompiledBlob;
  202. CComPtr<IDxcIncludeHandler> pIncludeHandler;
  203. HRESULT resultStatus;
  204. if (RunResult) // opt parsing already failed
  205. return;
  206. IFT(DllSupport->CreateInstance(CLSID_DxcLibrary, &pLibrary));
  207. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  208. IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
  209. IFT(DllSupport->CreateInstance(CLSID_DxcCompiler, &pCompiler));
  210. IFT(pCompiler->Compile(pSource, CommandFileName, entry.c_str(), profile.c_str(),
  211. flags.data(), flags.size(), nullptr, 0, pIncludeHandler, &pResult));
  212. IFT(pResult->GetStatus(&resultStatus));
  213. if (SUCCEEDED(resultStatus)) {
  214. IFT(pResult->GetResult(&pCompiledBlob));
  215. if (!opts.AstDump) {
  216. IFT(pCompiler->Disassemble(pCompiledBlob, &pDisassembly));
  217. StdOut = BlobToUtf8(pDisassembly);
  218. } else {
  219. StdOut = BlobToUtf8(pCompiledBlob);
  220. }
  221. CComPtr<IDxcBlobEncoding> pStdErr;
  222. IFT(pResult->GetErrorBuffer(&pStdErr));
  223. StdErr = BlobToUtf8(pStdErr);
  224. RunResult = 0;
  225. }
  226. else {
  227. IFT(pResult->GetErrorBuffer(&pDisassembly));
  228. StdErr = BlobToUtf8(pDisassembly);
  229. RunResult = resultStatus;
  230. }
  231. OpResult = pResult;
  232. }
  233. void FileRunCommandPart::RunDxv(const FileRunCommandPart *Prior) {
  234. std::string args(strtrim(Arguments));
  235. const char *inputPos = strstr(args.c_str(), "%s");
  236. if (inputPos == nullptr) {
  237. StdErr = "Only supported pattern includes input file as argument";
  238. RunResult = 1;
  239. return;
  240. }
  241. args.erase(inputPos - args.c_str(), strlen("%s"));
  242. llvm::StringRef argsRef = args;
  243. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  244. argsRef.split(splitArgs, " ");
  245. IFTMSG(splitArgs.size()==1, "wrong arg num for dxv");
  246. CComPtr<IDxcLibrary> pLibrary;
  247. CComPtr<IDxcAssembler> pAssembler;
  248. CComPtr<IDxcValidator> pValidator;
  249. CComPtr<IDxcOperationResult> pResult;
  250. CComPtr<IDxcBlobEncoding> pSource;
  251. CComPtr<IDxcBlob> pContainerBlob;
  252. HRESULT resultStatus;
  253. IFT(DllSupport->CreateInstance(CLSID_DxcLibrary, &pLibrary));
  254. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  255. IFT(DllSupport->CreateInstance(CLSID_DxcAssembler, &pAssembler));
  256. IFT(pAssembler->AssembleToContainer(pSource, &pResult));
  257. IFT(pResult->GetStatus(&resultStatus));
  258. if (FAILED(resultStatus)) {
  259. CComPtr<IDxcBlobEncoding> pAssembleBlob;
  260. IFT(pResult->GetErrorBuffer(&pAssembleBlob));
  261. StdErr = BlobToUtf8(pAssembleBlob);
  262. RunResult = resultStatus;
  263. return;
  264. }
  265. IFT(pResult->GetResult(&pContainerBlob));
  266. IFT(DllSupport->CreateInstance(CLSID_DxcValidator, &pValidator));
  267. CComPtr<IDxcOperationResult> pValidationResult;
  268. IFT(pValidator->Validate(pContainerBlob, DxcValidatorFlags_InPlaceEdit,
  269. &pValidationResult));
  270. IFT(pValidationResult->GetStatus(&resultStatus));
  271. if (resultStatus) {
  272. CComPtr<IDxcBlobEncoding> pValidateBlob;
  273. IFT(pValidationResult->GetErrorBuffer(&pValidateBlob));
  274. StdOut = BlobToUtf8(pValidateBlob);
  275. }
  276. RunResult = 0;
  277. }
  278. void FileRunCommandPart::RunOpt(const FileRunCommandPart *Prior) {
  279. std::string args(strtrim(Arguments));
  280. const char *inputPos = strstr(args.c_str(), "%s");
  281. if (inputPos == nullptr && Prior == nullptr) {
  282. StdErr = "Only supported patterns are input file as argument or prior "
  283. "command with disassembly";
  284. RunResult = 1;
  285. return;
  286. }
  287. CComPtr<IDxcLibrary> pLibrary;
  288. CComPtr<IDxcOptimizer> pOptimizer;
  289. CComPtr<IDxcBlobEncoding> pSource;
  290. CComPtr<IDxcBlobEncoding> pOutputText;
  291. CComPtr<IDxcBlob> pOutputModule;
  292. IFT(DllSupport->CreateInstance(CLSID_DxcLibrary, &pLibrary));
  293. IFT(DllSupport->CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
  294. if (inputPos != nullptr) {
  295. args.erase(inputPos - args.c_str(), strlen("%s"));
  296. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  297. }
  298. else {
  299. assert(Prior != nullptr && "else early check should have returned");
  300. CComPtr<IDxcAssembler> pAssembler;
  301. IFT(DllSupport->CreateInstance(CLSID_DxcAssembler, &pAssembler));
  302. IFT(pLibrary->CreateBlobWithEncodingFromPinned(
  303. (LPBYTE)Prior->StdOut.c_str(), Prior->StdOut.size(), CP_UTF8,
  304. &pSource));
  305. }
  306. args = trim(args);
  307. llvm::StringRef argsRef = args;
  308. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  309. argsRef.split(splitArgs, " ");
  310. std::vector<LPCWSTR> options;
  311. std::vector<std::wstring> optionStrings;
  312. for (llvm::StringRef S : splitArgs) {
  313. optionStrings.push_back(
  314. Unicode::UTF8ToUTF16StringOrThrow(trim(S.str()).c_str()));
  315. }
  316. // Add the options outside the above loop in case the vector is resized.
  317. for (const std::wstring& str : optionStrings)
  318. options.push_back(str.c_str());
  319. IFT(pOptimizer->RunOptimizer(pSource, options.data(), options.size(),
  320. &pOutputModule, &pOutputText));
  321. StdOut = BlobToUtf8(pOutputText);
  322. RunResult = 0;
  323. }
  324. void FileRunCommandPart::RunTee(const FileRunCommandPart *Prior) {
  325. if (Prior == nullptr) {
  326. StdErr = "tee requires a prior command";
  327. RunResult = 1;
  328. return;
  329. }
  330. // Ignore commands for now - simply log out through test framework.
  331. {
  332. CA2W outWide(Prior->StdOut.c_str(), CP_UTF8);
  333. WEX::Logging::Log::Comment(outWide.m_psz);
  334. }
  335. if (!Prior->StdErr.empty()) {
  336. CA2W errWide(Prior->StdErr.c_str(), CP_UTF8);
  337. WEX::Logging::Log::Comment(L"<stderr>");
  338. WEX::Logging::Log::Comment(errWide.m_psz);
  339. }
  340. StdErr = Prior->StdErr;
  341. StdOut = Prior->StdOut;
  342. RunResult = Prior->RunResult;
  343. }
  344. class FileRunTestResultImpl : public FileRunTestResult {
  345. dxc::DxcDllSupport &m_support;
  346. void RunFileCheckFromCommands(LPCSTR commands, LPCWSTR fileName) {
  347. std::vector<FileRunCommandPart> parts;
  348. ParseCommandParts(commands, fileName, parts);
  349. FileRunCommandPart *prior = nullptr;
  350. for (FileRunCommandPart & part : parts) {
  351. part.DllSupport = &m_support;
  352. part.Run(prior);
  353. prior = &part;
  354. }
  355. if (prior == nullptr) {
  356. this->RunResult = 1;
  357. this->ErrorMessage = "FileCheck found no commands to run";
  358. }
  359. else {
  360. this->RunResult = prior->RunResult;
  361. this->ErrorMessage = prior->StdErr;
  362. }
  363. }
  364. public:
  365. FileRunTestResultImpl(dxc::DxcDllSupport &support) : m_support(support) {}
  366. void RunFileCheckFromFileCommands(LPCWSTR fileName) {
  367. // Assume UTF-8 files.
  368. std::string commands(GetFirstLine(fileName));
  369. return RunFileCheckFromCommands(commands.c_str(), fileName);
  370. }
  371. };
  372. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName) {
  373. dxc::DxcDllSupport dllSupport;
  374. IFT(dllSupport.Initialize());
  375. FileRunTestResultImpl result(dllSupport);
  376. result.RunFileCheckFromFileCommands(fileName);
  377. return result;
  378. }
  379. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName, dxc::DxcDllSupport &dllSupport) {
  380. FileRunTestResultImpl result(dllSupport);
  381. result.RunFileCheckFromFileCommands(fileName);
  382. return result;
  383. }
  384. void ParseCommandParts(LPCSTR commands, LPCWSTR fileName,
  385. std::vector<FileRunCommandPart> &parts) {
  386. // Barely enough parsing here.
  387. commands = strstr(commands, "RUN: ");
  388. if (!commands) {
  389. return;
  390. }
  391. commands += strlen("RUN: ");
  392. LPCSTR endCommands = strchr(commands, '\0');
  393. while (commands != endCommands) {
  394. LPCSTR nextStart;
  395. LPCSTR thisEnd = strchr(commands, '|');
  396. if (!thisEnd) {
  397. nextStart = thisEnd = endCommands;
  398. } else {
  399. nextStart = thisEnd + 2;
  400. }
  401. LPCSTR commandEnd = strchr(commands, ' ');
  402. if (!commandEnd)
  403. commandEnd = endCommands;
  404. parts.emplace_back(std::string(commands, commandEnd),
  405. std::string(commandEnd, thisEnd), fileName);
  406. commands = nextStart;
  407. }
  408. }
  409. void ParseCommandPartsFromFile(LPCWSTR fileName,
  410. std::vector<FileRunCommandPart> &parts) {
  411. // Assume UTF-8 files.
  412. std::string commands(GetFirstLine(fileName));
  413. ParseCommandParts(commands.c_str(), fileName, parts);
  414. }