FileCheckerTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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. IFT(ReadDxcOpts(hlsl::options::getHlslOptTable(), /*flagsToInclude*/ 0,
  172. argStrings, Opts, errorStream));
  173. }
  174. void FileRunCommandPart::RunDxc(const FileRunCommandPart *Prior) {
  175. // Support piping stdin from prior if needed.
  176. UNREFERENCED_PARAMETER(Prior);
  177. hlsl::options::MainArgs args;
  178. hlsl::options::DxcOpts opts;
  179. ReadOptsForDxc(args, opts);
  180. std::wstring entry =
  181. Unicode::UTF8ToUTF16StringOrThrow(opts.EntryPoint.str().c_str());
  182. std::wstring profile =
  183. Unicode::UTF8ToUTF16StringOrThrow(opts.TargetProfile.str().c_str());
  184. std::vector<LPCWSTR> flags;
  185. if (opts.CodeGenHighLevel) {
  186. flags.push_back(L"-fcgl");
  187. }
  188. std::vector<std::wstring> argWStrings;
  189. CopyArgsToWStrings(opts.Args, hlsl::options::CoreOption, argWStrings);
  190. for (const std::wstring &a : argWStrings)
  191. flags.push_back(a.data());
  192. CComPtr<IDxcLibrary> pLibrary;
  193. CComPtr<IDxcCompiler> pCompiler;
  194. CComPtr<IDxcOperationResult> pResult;
  195. CComPtr<IDxcBlobEncoding> pSource;
  196. CComPtr<IDxcBlobEncoding> pDisassembly;
  197. CComPtr<IDxcBlob> pCompiledBlob;
  198. CComPtr<IDxcIncludeHandler> pIncludeHandler;
  199. HRESULT resultStatus;
  200. IFT(DllSupport->CreateInstance(CLSID_DxcLibrary, &pLibrary));
  201. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  202. IFT(pLibrary->CreateIncludeHandler(&pIncludeHandler));
  203. IFT(DllSupport->CreateInstance(CLSID_DxcCompiler, &pCompiler));
  204. IFT(pCompiler->Compile(pSource, CommandFileName, entry.c_str(), profile.c_str(),
  205. flags.data(), flags.size(), nullptr, 0, pIncludeHandler, &pResult));
  206. IFT(pResult->GetStatus(&resultStatus));
  207. if (SUCCEEDED(resultStatus)) {
  208. IFT(pResult->GetResult(&pCompiledBlob));
  209. if (!opts.AstDump) {
  210. IFT(pCompiler->Disassemble(pCompiledBlob, &pDisassembly));
  211. StdOut = BlobToUtf8(pDisassembly);
  212. } else {
  213. StdOut = BlobToUtf8(pCompiledBlob);
  214. }
  215. CComPtr<IDxcBlobEncoding> pStdErr;
  216. IFT(pResult->GetErrorBuffer(&pStdErr));
  217. StdErr = BlobToUtf8(pStdErr);
  218. RunResult = 0;
  219. }
  220. else {
  221. IFT(pResult->GetErrorBuffer(&pDisassembly));
  222. StdErr = BlobToUtf8(pDisassembly);
  223. RunResult = resultStatus;
  224. }
  225. OpResult = pResult;
  226. }
  227. void FileRunCommandPart::RunDxv(const FileRunCommandPart *Prior) {
  228. std::string args(strtrim(Arguments));
  229. const char *inputPos = strstr(args.c_str(), "%s");
  230. if (inputPos == nullptr) {
  231. StdErr = "Only supported pattern includes input file as argument";
  232. RunResult = 1;
  233. return;
  234. }
  235. args.erase(inputPos - args.c_str(), strlen("%s"));
  236. llvm::StringRef argsRef = args;
  237. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  238. argsRef.split(splitArgs, " ");
  239. IFTMSG(splitArgs.size()==1, "wrong arg num for dxv");
  240. CComPtr<IDxcLibrary> pLibrary;
  241. CComPtr<IDxcAssembler> pAssembler;
  242. CComPtr<IDxcValidator> pValidator;
  243. CComPtr<IDxcOperationResult> pResult;
  244. CComPtr<IDxcBlobEncoding> pSource;
  245. CComPtr<IDxcBlob> pContainerBlob;
  246. HRESULT resultStatus;
  247. IFT(DllSupport->CreateInstance(CLSID_DxcLibrary, &pLibrary));
  248. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  249. IFT(DllSupport->CreateInstance(CLSID_DxcAssembler, &pAssembler));
  250. IFT(pAssembler->AssembleToContainer(pSource, &pResult));
  251. IFT(pResult->GetStatus(&resultStatus));
  252. if (FAILED(resultStatus)) {
  253. CComPtr<IDxcBlobEncoding> pAssembleBlob;
  254. IFT(pResult->GetErrorBuffer(&pAssembleBlob));
  255. StdErr = BlobToUtf8(pAssembleBlob);
  256. RunResult = resultStatus;
  257. return;
  258. }
  259. IFT(pResult->GetResult(&pContainerBlob));
  260. IFT(DllSupport->CreateInstance(CLSID_DxcValidator, &pValidator));
  261. CComPtr<IDxcOperationResult> pValidationResult;
  262. IFT(pValidator->Validate(pContainerBlob, DxcValidatorFlags_InPlaceEdit,
  263. &pValidationResult));
  264. IFT(pValidationResult->GetStatus(&resultStatus));
  265. if (resultStatus) {
  266. CComPtr<IDxcBlobEncoding> pValidateBlob;
  267. IFT(pValidationResult->GetErrorBuffer(&pValidateBlob));
  268. StdOut = BlobToUtf8(pValidateBlob);
  269. }
  270. RunResult = 0;
  271. }
  272. void FileRunCommandPart::RunOpt(const FileRunCommandPart *Prior) {
  273. std::string args(strtrim(Arguments));
  274. const char *inputPos = strstr(args.c_str(), "%s");
  275. if (inputPos == nullptr && Prior == nullptr) {
  276. StdErr = "Only supported patterns are input file as argument or prior "
  277. "command with disassembly";
  278. RunResult = 1;
  279. return;
  280. }
  281. CComPtr<IDxcLibrary> pLibrary;
  282. CComPtr<IDxcOptimizer> pOptimizer;
  283. CComPtr<IDxcBlobEncoding> pSource;
  284. CComPtr<IDxcBlobEncoding> pOutputText;
  285. CComPtr<IDxcBlob> pOutputModule;
  286. IFT(DllSupport->CreateInstance(CLSID_DxcLibrary, &pLibrary));
  287. IFT(DllSupport->CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
  288. if (inputPos != nullptr) {
  289. args.erase(inputPos - args.c_str(), strlen("%s"));
  290. IFT(pLibrary->CreateBlobFromFile(CommandFileName, nullptr, &pSource));
  291. }
  292. else {
  293. assert(Prior != nullptr && "else early check should have returned");
  294. CComPtr<IDxcAssembler> pAssembler;
  295. IFT(DllSupport->CreateInstance(CLSID_DxcAssembler, &pAssembler));
  296. IFT(pLibrary->CreateBlobWithEncodingFromPinned(
  297. (LPBYTE)Prior->StdOut.c_str(), Prior->StdOut.size(), CP_UTF8,
  298. &pSource));
  299. }
  300. args = trim(args);
  301. llvm::StringRef argsRef = args;
  302. llvm::SmallVector<llvm::StringRef, 8> splitArgs;
  303. argsRef.split(splitArgs, " ");
  304. std::vector<LPCWSTR> options;
  305. std::vector<std::wstring> optionStrings;
  306. for (llvm::StringRef S : splitArgs) {
  307. optionStrings.push_back(
  308. Unicode::UTF8ToUTF16StringOrThrow(trim(S.str()).c_str()));
  309. }
  310. // Add the options outside the above loop in case the vector is resized.
  311. for (const std::wstring& str : optionStrings)
  312. options.push_back(str.c_str());
  313. IFT(pOptimizer->RunOptimizer(pSource, options.data(), options.size(),
  314. &pOutputModule, &pOutputText));
  315. StdOut = BlobToUtf8(pOutputText);
  316. RunResult = 0;
  317. }
  318. void FileRunCommandPart::RunTee(const FileRunCommandPart *Prior) {
  319. if (Prior == nullptr) {
  320. StdErr = "tee requires a prior command";
  321. RunResult = 1;
  322. return;
  323. }
  324. // Ignore commands for now - simply log out through test framework.
  325. {
  326. CA2W outWide(Prior->StdOut.c_str(), CP_UTF8);
  327. WEX::Logging::Log::Comment(outWide.m_psz);
  328. }
  329. if (!Prior->StdErr.empty()) {
  330. CA2W errWide(Prior->StdErr.c_str(), CP_UTF8);
  331. WEX::Logging::Log::Comment(L"<stderr>");
  332. WEX::Logging::Log::Comment(errWide.m_psz);
  333. }
  334. StdErr = Prior->StdErr;
  335. StdOut = Prior->StdOut;
  336. RunResult = Prior->RunResult;
  337. }
  338. class FileRunTestResultImpl : public FileRunTestResult {
  339. dxc::DxcDllSupport &m_support;
  340. void RunFileCheckFromCommands(LPCSTR commands, LPCWSTR fileName) {
  341. std::vector<FileRunCommandPart> parts;
  342. ParseCommandParts(commands, fileName, parts);
  343. FileRunCommandPart *prior = nullptr;
  344. for (FileRunCommandPart & part : parts) {
  345. part.DllSupport = &m_support;
  346. part.Run(prior);
  347. prior = &part;
  348. }
  349. if (prior == nullptr) {
  350. this->RunResult = 1;
  351. this->ErrorMessage = "FileCheck found no commands to run";
  352. }
  353. else {
  354. this->RunResult = prior->RunResult;
  355. this->ErrorMessage = prior->StdErr;
  356. }
  357. }
  358. public:
  359. FileRunTestResultImpl(dxc::DxcDllSupport &support) : m_support(support) {}
  360. void RunFileCheckFromFileCommands(LPCWSTR fileName) {
  361. // Assume UTF-8 files.
  362. std::string commands(GetFirstLine(fileName));
  363. return RunFileCheckFromCommands(commands.c_str(), fileName);
  364. }
  365. };
  366. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName) {
  367. dxc::DxcDllSupport dllSupport;
  368. IFT(dllSupport.Initialize());
  369. FileRunTestResultImpl result(dllSupport);
  370. result.RunFileCheckFromFileCommands(fileName);
  371. return result;
  372. }
  373. FileRunTestResult FileRunTestResult::RunFromFileCommands(LPCWSTR fileName, dxc::DxcDllSupport &dllSupport) {
  374. FileRunTestResultImpl result(dllSupport);
  375. result.RunFileCheckFromFileCommands(fileName);
  376. return result;
  377. }
  378. void ParseCommandParts(LPCSTR commands, LPCWSTR fileName,
  379. std::vector<FileRunCommandPart> &parts) {
  380. // Barely enough parsing here.
  381. commands = strstr(commands, "RUN: ");
  382. if (!commands) {
  383. return;
  384. }
  385. commands += strlen("RUN: ");
  386. LPCSTR endCommands = strchr(commands, '\0');
  387. while (commands != endCommands) {
  388. LPCSTR nextStart;
  389. LPCSTR thisEnd = strchr(commands, '|');
  390. if (!thisEnd) {
  391. nextStart = thisEnd = endCommands;
  392. } else {
  393. nextStart = thisEnd + 2;
  394. }
  395. LPCSTR commandEnd = strchr(commands, ' ');
  396. if (!commandEnd)
  397. commandEnd = endCommands;
  398. parts.emplace_back(std::string(commands, commandEnd),
  399. std::string(commandEnd, thisEnd), fileName);
  400. commands = nextStart;
  401. }
  402. }
  403. void ParseCommandPartsFromFile(LPCWSTR fileName,
  404. std::vector<FileRunCommandPart> &parts) {
  405. // Assume UTF-8 files.
  406. std::string commands(GetFirstLine(fileName));
  407. ParseCommandParts(commands.c_str(), fileName, parts);
  408. }