FileCheckerTest.cpp 16 KB

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