OptimizerTest.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // OptimizerTest.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 for the optimizer API. //
  9. // //
  10. ///////////////////////////////////////////////////////////////////////////////
  11. #ifndef UNICODE
  12. #define UNICODE
  13. #endif
  14. #include <memory>
  15. #include <vector>
  16. #include <string>
  17. #include <map>
  18. #include <cassert>
  19. #include <sstream>
  20. #include <algorithm>
  21. #include "dxc/DxilContainer/DxilContainer.h"
  22. #include "dxc/Support/WinIncludes.h"
  23. #include "dxc/dxcapi.h"
  24. #include "dxc/Test/HLSLTestData.h"
  25. #include "dxc/Test/HlslTestUtils.h"
  26. #include "dxc/Test/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/microcom.h"
  31. #include "dxc/Support/HLSLOptions.h"
  32. #include "dxc/Support/Unicode.h"
  33. #include "llvm/Support/FileSystem.h"
  34. #include "llvm/Support/MSFileSystem.h"
  35. #include "llvm/Support/Path.h"
  36. #include "llvm/ADT/SmallString.h"
  37. #include "llvm/ADT/StringSwitch.h"
  38. using namespace std;
  39. using namespace hlsl_test;
  40. ///////////////////////////////////////////////////////////////////////////////
  41. // Optimizer test cases.
  42. #ifdef _WIN32
  43. class OptimizerTest {
  44. #else
  45. class OptimizerTest : public ::testing::Test {
  46. #endif
  47. public:
  48. BEGIN_TEST_CLASS(OptimizerTest)
  49. TEST_CLASS_PROPERTY(L"Parallel", L"true")
  50. TEST_METHOD_PROPERTY(L"Priority", L"0")
  51. END_TEST_CLASS()
  52. TEST_CLASS_SETUP(InitSupport);
  53. // Split just so we can run them with some degree of concurrency.
  54. TEST_METHOD(OptimizerWhenSlice0ThenOK)
  55. TEST_METHOD(OptimizerWhenSlice1ThenOK)
  56. TEST_METHOD(OptimizerWhenSlice2ThenOK)
  57. TEST_METHOD(OptimizerWhenSlice3ThenOK)
  58. TEST_METHOD(OptimizerWhenSliceWithIntermediateOptionsThenOK)
  59. void OptimizerWhenSliceNThenOK(int optLevel);
  60. void OptimizerWhenSliceNThenOK(int optLevel, LPCSTR pText, LPCWSTR pTarget, llvm::ArrayRef<LPCWSTR> args = {});
  61. dxc::DxcDllSupport m_dllSupport;
  62. VersionSupportInfo m_ver;
  63. HRESULT CreateCompiler(IDxcCompiler **ppResult) {
  64. return m_dllSupport.CreateInstance(CLSID_DxcCompiler, ppResult);
  65. }
  66. HRESULT CreateContainerBuilder(IDxcContainerBuilder **ppResult) {
  67. return m_dllSupport.CreateInstance(CLSID_DxcContainerBuilder, ppResult);
  68. }
  69. void VerifyOperationSucceeded(IDxcOperationResult *pResult) {
  70. HRESULT result;
  71. VERIFY_SUCCEEDED(pResult->GetStatus(&result));
  72. if (FAILED(result)) {
  73. CComPtr<IDxcBlobEncoding> pErrors;
  74. VERIFY_SUCCEEDED(pResult->GetErrorBuffer(&pErrors));
  75. CA2W errorsWide(BlobToUtf8(pErrors).c_str(), CP_UTF8);
  76. WEX::Logging::Log::Comment(errorsWide);
  77. }
  78. VERIFY_SUCCEEDED(result);
  79. }
  80. };
  81. bool OptimizerTest::InitSupport() {
  82. if (!m_dllSupport.IsEnabled()) {
  83. VERIFY_SUCCEEDED(m_dllSupport.Initialize());
  84. m_ver.Initialize(m_dllSupport);
  85. }
  86. return true;
  87. }
  88. TEST_F(OptimizerTest, OptimizerWhenSlice0ThenOK) { OptimizerWhenSliceNThenOK(0); }
  89. TEST_F(OptimizerTest, OptimizerWhenSlice1ThenOK) { OptimizerWhenSliceNThenOK(1); }
  90. TEST_F(OptimizerTest, OptimizerWhenSlice2ThenOK) { OptimizerWhenSliceNThenOK(2); }
  91. TEST_F(OptimizerTest, OptimizerWhenSlice3ThenOK) { OptimizerWhenSliceNThenOK(3); }
  92. TEST_F(OptimizerTest, OptimizerWhenSliceWithIntermediateOptionsThenOK) {
  93. // The program below working depends on the LegacyResourceReservation option being
  94. // carried through to the resource register allocator, even though it is not
  95. // preserved in the final shader.
  96. LPCSTR SampleProgram =
  97. "Texture2D tex0 : register(t0);\r\n"
  98. "Texture2D tex1;\r\n" // tex1 should get register t1
  99. "float4 main() : SV_Target {\r\n"
  100. " return tex1.Load((int3)0);\r\n"
  101. "}";
  102. OptimizerWhenSliceNThenOK(1, SampleProgram, L"ps_6_0", { L"-flegacy-resource-reservation" });
  103. }
  104. void OptimizerTest::OptimizerWhenSliceNThenOK(int optLevel) {
  105. LPCSTR SampleProgram =
  106. "Texture2D g_Tex;\r\n"
  107. "SamplerState g_Sampler;\r\n"
  108. "void unused() { }\r\n"
  109. "float4 main(float4 pos : SV_Position, float4 user : USER, bool b : B) : SV_Target {\r\n"
  110. " unused();\r\n"
  111. " if (b) user = g_Tex.Sample(g_Sampler, pos.xy);\r\n"
  112. " return user * pos;\r\n"
  113. "}";
  114. OptimizerWhenSliceNThenOK(optLevel, SampleProgram, L"ps_6_0",
  115. // Add -validator-version 1.4 to ensure it's not changed by DxcAssembler.
  116. {L"-validator-version", L"1.4"});
  117. }
  118. static bool IsPassMarkerFunction(LPCWSTR pName) {
  119. return 0 == _wcsicmp(pName, L"-opt-fn-passes");
  120. }
  121. static bool IsPassMarkerNotFunction(LPCWSTR pName) {
  122. return 0 == _wcsnicmp(pName, L"-opt-", 5) && !IsPassMarkerFunction(pName);
  123. }
  124. static void ExtractFunctionPasses(std::vector<LPCWSTR> &passes, std::vector<LPCWSTR> &functionPasses) {
  125. // Assumption: contiguous range
  126. typedef std::vector<LPCWSTR>::iterator it;
  127. it firstPass = std::find_if(passes.begin(), passes.end(), IsPassMarkerFunction);
  128. if (firstPass == passes.end()) return;
  129. it lastPass = std::find_if(firstPass, passes.end(), IsPassMarkerNotFunction);
  130. it cursor = firstPass;
  131. while (cursor != lastPass) {
  132. functionPasses.push_back(*cursor);
  133. ++cursor;
  134. }
  135. passes.erase(firstPass, lastPass);
  136. }
  137. void OptimizerTest::OptimizerWhenSliceNThenOK(int optLevel, LPCSTR pText, LPCWSTR pTarget, llvm::ArrayRef<LPCWSTR> args) {
  138. CComPtr<IDxcCompiler> pCompiler;
  139. CComPtr<IDxcOptimizer> pOptimizer;
  140. CComPtr<IDxcOperationResult> pResult;
  141. CComPtr<IDxcBlobEncoding> pSource;
  142. CComPtr<IDxcBlob> pProgram;
  143. CComPtr<IDxcBlob> pProgramModule;
  144. CComPtr<IDxcBlob> pProgramDisassemble;
  145. CComPtr<IDxcBlob> pHighLevelBlob;
  146. CComPtr<IDxcBlob> pOptDump;
  147. std::string passes;
  148. std::vector<LPCWSTR> passList;
  149. std::vector<LPCWSTR> prefixPassList;
  150. WEX::TestExecution::SetVerifyOutput verifySettings(WEX::TestExecution::VerifyOutputSettings::LogOnlyFailures);
  151. VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  152. VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcOptimizer, &pOptimizer));
  153. // Set up compilation args vector
  154. wchar_t OptArg[4] = L"/O0";
  155. OptArg[2] = L'0' + optLevel;
  156. Utf8ToBlob(m_dllSupport, pText, &pSource);
  157. std::vector<LPCWSTR> highLevelArgs = { L"/Vd", OptArg };
  158. highLevelArgs.insert(highLevelArgs.end(), args.begin(), args.end());
  159. // Create the target program with a single invocation.
  160. highLevelArgs.emplace_back(L"/Qkeep_reflect_in_dxil");
  161. VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"main", pTarget,
  162. highLevelArgs.data(), static_cast<UINT32>(highLevelArgs.size()), nullptr, 0, nullptr, &pResult));
  163. VerifyOperationSucceeded(pResult);
  164. VERIFY_SUCCEEDED(pResult->GetResult(&pProgram));
  165. pResult.Release();
  166. std::string originalAssembly = DisassembleProgram(m_dllSupport, pProgram);
  167. highLevelArgs.pop_back(); // Remove /keep_reflect_in_dxil
  168. // Get a list of passes for this configuration.
  169. highLevelArgs.emplace_back(L"/Odump");
  170. VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"main", pTarget,
  171. highLevelArgs.data(), static_cast<UINT32>(highLevelArgs.size()), nullptr, 0, nullptr, &pResult));
  172. VerifyOperationSucceeded(pResult);
  173. VERIFY_SUCCEEDED(pResult->GetResult(&pOptDump));
  174. pResult.Release();
  175. passes = BlobToUtf8(pOptDump);
  176. CA2W passesW(passes.c_str(), CP_UTF8);
  177. // Get the high-level compile of the program.
  178. highLevelArgs.pop_back(); // Remove /Odump
  179. highLevelArgs.emplace_back(L"/fcgl");
  180. VERIFY_SUCCEEDED(pCompiler->Compile(pSource, L"source.hlsl", L"main", pTarget,
  181. highLevelArgs.data(), static_cast<UINT32>(highLevelArgs.size()), nullptr, 0, nullptr, &pResult));
  182. VerifyOperationSucceeded(pResult);
  183. VERIFY_SUCCEEDED(pResult->GetResult(&pHighLevelBlob));
  184. pResult.Release();
  185. // Create a list of passes.
  186. SplitPassList(passesW.m_psz, passList);
  187. ExtractFunctionPasses(passList, prefixPassList);
  188. // For each point in between the passes ...
  189. for (size_t i = 0; i <= passList.size(); ++i) {
  190. size_t secondPassIdx = i;
  191. size_t firstPassCount = i;
  192. size_t secondPassCount = passList.size() - i;
  193. // If we find an -hlsl-passes-nopause, pause/resume will not work past this.
  194. if (i > 0 && 0 == wcscmp(L"-hlsl-passes-nopause", passList[i - 1])) {
  195. break;
  196. }
  197. CComPtr<IDxcBlob> pFirstModule;
  198. CComPtr<IDxcBlob> pSecondModule;
  199. CComPtr<IDxcBlob> pAssembledBlob;
  200. std::vector<LPCWSTR> firstPassList, secondPassList;
  201. firstPassList = prefixPassList;
  202. firstPassList.push_back(L"-opt-mod-passes");
  203. secondPassList = firstPassList;
  204. firstPassList.insert(firstPassList.end(), passList.begin(), passList.begin() + firstPassCount);
  205. firstPassList.push_back(L"-hlsl-passes-pause");
  206. secondPassList.push_back(L"-hlsl-passes-resume");
  207. secondPassList.insert(secondPassList.end(), passList.begin() + secondPassIdx, passList.begin() + secondPassIdx + secondPassCount);
  208. // Run a first pass.
  209. VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(pHighLevelBlob,
  210. firstPassList.data(), (UINT32)firstPassList.size(),
  211. &pFirstModule, nullptr));
  212. // Run a second pass.
  213. VERIFY_SUCCEEDED(pOptimizer->RunOptimizer(pFirstModule,
  214. secondPassList.data(), (UINT32)secondPassList.size(),
  215. &pSecondModule, nullptr));
  216. // Assembly it into a container so the disassembler shows equivalent data.
  217. AssembleToContainer(m_dllSupport, pSecondModule, &pAssembledBlob);
  218. // Verify we get the same results as in the full version.
  219. std::string assembly = DisassembleProgram(m_dllSupport, pAssembledBlob);
  220. if (0 != strcmp(assembly.c_str(), originalAssembly.c_str())) {
  221. LogCommentFmt(L"Difference found in disassembly in iteration %u when breaking before '%s'", i, (i == passList.size()) ? L"(full list)" : passList[i]);
  222. LogCommentFmt(L"Original assembly\r\n%S", originalAssembly.c_str());
  223. LogCommentFmt(L"\r\nReassembled assembly\r\n%S", assembly.c_str());
  224. VERIFY_FAIL();
  225. }
  226. }
  227. }