ExtensionTest.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // //
  3. // Copyright (C) Microsoft Corporation. All rights reserved. //
  4. // ExtensionTest.cpp //
  5. // //
  6. // Provides tests for the language extension APIs. //
  7. // //
  8. ///////////////////////////////////////////////////////////////////////////////
  9. #include "CompilationResult.h"
  10. #include "HlslTestUtils.h"
  11. #include "DxcTestUtils.h"
  12. #include "dxc/Support/microcom.h"
  13. #include "dxc/dxcapi.internal.h"
  14. #include "dxc/HLSL/HLOperationLowerExtension.h"
  15. #include "dxc/HlslIntrinsicOp.h"
  16. #include "dxc/DXIL/DxilOperations.h"
  17. #include "llvm/Support/Regex.h"
  18. ///////////////////////////////////////////////////////////////////////////////
  19. // Support for test intrinsics.
  20. // $result = test_fn(any_vector<any_cardinality> value)
  21. static const HLSL_INTRINSIC_ARGUMENT TestFnArgs[] = {
  22. { "test_fn", AR_QUAL_OUT, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C },
  23. { "value", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C }
  24. };
  25. // void test_proc(any_vector<any_cardinality> value)
  26. static const HLSL_INTRINSIC_ARGUMENT TestProcArgs[] = {
  27. { "test_proc", 0, 0, LITEMPLATE_VOID, 0, LICOMPTYPE_VOID, 0, 0 },
  28. { "value", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C }
  29. };
  30. // $result = test_poly(any_vector<any_cardinality> value)
  31. static const HLSL_INTRINSIC_ARGUMENT TestFnCustomArgs[] = {
  32. { "test_poly", AR_QUAL_OUT, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C },
  33. { "value", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C }
  34. };
  35. // $result = test_int(int<any_cardinality> value)
  36. static const HLSL_INTRINSIC_ARGUMENT TestFnIntArgs[] = {
  37. { "test_int", AR_QUAL_OUT, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_INT, 1, IA_C },
  38. { "value", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_INT, 1, IA_C }
  39. };
  40. // $result = test_nolower(any_vector<any_cardinality> value)
  41. static const HLSL_INTRINSIC_ARGUMENT TestFnNoLowerArgs[] = {
  42. { "test_nolower", AR_QUAL_OUT, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C },
  43. { "value", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C }
  44. };
  45. // void test_pack_0(any_vector<any_cardinality> value)
  46. static const HLSL_INTRINSIC_ARGUMENT TestFnPack0[] = {
  47. { "test_pack_0", 0, 0, LITEMPLATE_VOID, 0, LICOMPTYPE_VOID, 0, 0 },
  48. { "value", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C }
  49. };
  50. // $result = test_pack_1()
  51. static const HLSL_INTRINSIC_ARGUMENT TestFnPack1[] = {
  52. { "test_pack_1", AR_QUAL_OUT, 0, LITEMPLATE_VECTOR, 0, LICOMPTYPE_FLOAT, 1, 2 },
  53. };
  54. // $result = test_pack_2(any_vector<any_cardinality> value1, any_vector<any_cardinality> value2)
  55. static const HLSL_INTRINSIC_ARGUMENT TestFnPack2[] = {
  56. { "test_pack_2", AR_QUAL_OUT, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C },
  57. { "value1", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C },
  58. { "value2", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C },
  59. };
  60. // $scalar = test_pack_3(any_vector<any_cardinality> value)
  61. static const HLSL_INTRINSIC_ARGUMENT TestFnPack3[] = {
  62. { "test_pack_3", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_FLOAT, 1, 1 },
  63. { "value1", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_FLOAT, 1, 2},
  64. };
  65. // float<2> = test_pack_4(float<3> value)
  66. static const HLSL_INTRINSIC_ARGUMENT TestFnPack4[] = {
  67. { "test_pack_4", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_FLOAT, 1, 2 },
  68. { "value", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_FLOAT, 1, 3},
  69. };
  70. // float<2> = test_rand()
  71. static const HLSL_INTRINSIC_ARGUMENT TestRand[] = {
  72. { "test_rand", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_FLOAT, 1, 2 },
  73. };
  74. // uint = test_rand(uint x)
  75. static const HLSL_INTRINSIC_ARGUMENT TestUnsigned[] = {
  76. { "test_unsigned", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_UINT, 1, 1 },
  77. { "x", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_UINT, 1, 1},
  78. };
  79. // float2 = MyBufferOp(uint2 addr)
  80. static const HLSL_INTRINSIC_ARGUMENT TestMyBufferOp[] = {
  81. { "MyBufferOp", AR_QUAL_OUT, 0, LITEMPLATE_VECTOR, 0, LICOMPTYPE_FLOAT, 1, 2 },
  82. { "addr", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_UINT, 1, 2},
  83. };
  84. // bool<> = test_isinf(float<> x)
  85. static const HLSL_INTRINSIC_ARGUMENT TestIsInf[] = {
  86. { "test_isinf", AR_QUAL_OUT, 0, LITEMPLATE_VECTOR, 0, LICOMPTYPE_BOOL, 1, IA_C },
  87. { "x", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_FLOAT, 1, IA_C},
  88. };
  89. // int = test_ibfe(uint width, uint offset, uint val)
  90. static const HLSL_INTRINSIC_ARGUMENT TestIBFE[] = {
  91. { "test_ibfe", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_INT, 1, 1 },
  92. { "width", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  93. { "offset", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  94. { "val", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  95. };
  96. // float2 = MySamplerOp(uint2 addr)
  97. static const HLSL_INTRINSIC_ARGUMENT TestMySamplerOp[] = {
  98. { "MySamplerOp", AR_QUAL_OUT, 0, LITEMPLATE_VECTOR, 0, LICOMPTYPE_FLOAT, 1, 2 },
  99. { "addr", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_UINT, 1, 2},
  100. };
  101. struct Intrinsic {
  102. LPCWSTR hlslName;
  103. const char *dxilName;
  104. const char *strategy;
  105. HLSL_INTRINSIC hlsl;
  106. };
  107. const char * DEFAULT_NAME = "";
  108. // llvm::array_lengthof that returns a UINT instead of size_t
  109. template <class T, std::size_t N>
  110. UINT countof(T(&)[N]) { return static_cast<UINT>(N); }
  111. Intrinsic Intrinsics[] = {
  112. {L"test_fn", DEFAULT_NAME, "r", { 1, false, true, -1, countof(TestFnArgs), TestFnArgs }},
  113. {L"test_proc", DEFAULT_NAME, "r", { 2, false, false,-1, countof(TestProcArgs), TestProcArgs }},
  114. {L"test_poly", "test_poly.$o", "r", { 3, false, true, -1, countof(TestFnCustomArgs), TestFnCustomArgs }},
  115. {L"test_int", "test_int", "r", { 4, false, true, -1, countof(TestFnIntArgs), TestFnIntArgs}},
  116. {L"test_nolower", "test_nolower.$o", "n", { 5, false, true, -1, countof(TestFnNoLowerArgs), TestFnNoLowerArgs}},
  117. {L"test_pack_0", "test_pack_0.$o", "p", { 6, false, false,-1, countof(TestFnPack0), TestFnPack0}},
  118. {L"test_pack_1", "test_pack_1.$o", "p", { 7, false, true, -1, countof(TestFnPack1), TestFnPack1}},
  119. {L"test_pack_2", "test_pack_2.$o", "p", { 8, false, true, -1, countof(TestFnPack2), TestFnPack2}},
  120. {L"test_pack_3", "test_pack_3.$o", "p", { 9, false, true, -1, countof(TestFnPack3), TestFnPack3}},
  121. {L"test_pack_4", "test_pack_4.$o", "p", { 10, false, false,-1, countof(TestFnPack4), TestFnPack4}},
  122. {L"test_rand", "test_rand", "r", { 11, false, false,-1, countof(TestRand), TestRand}},
  123. {L"test_isinf", "test_isinf", "d", { 13, true, true, -1, countof(TestIsInf), TestIsInf}},
  124. {L"test_ibfe", "test_ibfe", "d", { 14, true, true, -1, countof(TestIBFE), TestIBFE}},
  125. // Make this intrinsic have the same opcode as an hlsl intrinsic with an unsigned
  126. // counterpart for testing purposes.
  127. {L"test_unsigned","test_unsigned", "n", { static_cast<unsigned>(hlsl::IntrinsicOp::IOP_min), false, true, -1, countof(TestUnsigned), TestUnsigned}},
  128. };
  129. Intrinsic BufferIntrinsics[] = {
  130. {L"MyBufferOp", "MyBufferOp", "m", { 12, false, true, -1, countof(TestMyBufferOp), TestMyBufferOp}},
  131. };
  132. // Test adding a method to an object that normally has no methods (SamplerState will do).
  133. Intrinsic SamplerIntrinsics[] = {
  134. {L"MySamplerOp", "MySamplerOp", "m", { 15, false, true, -1, countof(TestMySamplerOp), TestMySamplerOp}},
  135. };
  136. class IntrinsicTable {
  137. public:
  138. IntrinsicTable(const wchar_t *ns, Intrinsic *begin, Intrinsic *end)
  139. : m_namespace(ns), m_begin(begin), m_end(end)
  140. { }
  141. struct SearchResult {
  142. Intrinsic *intrinsic;
  143. uint64_t index;
  144. SearchResult() : SearchResult(nullptr, 0) {}
  145. SearchResult(Intrinsic *i, uint64_t n) : intrinsic(i), index(n) {}
  146. operator bool() { return intrinsic != nullptr; }
  147. };
  148. SearchResult Search(const wchar_t *name, std::ptrdiff_t startIndex) const {
  149. Intrinsic *begin = m_begin + startIndex;
  150. assert(std::distance(begin, m_end) >= 0);
  151. if (IsStar(name))
  152. return BuildResult(begin);
  153. Intrinsic *found = std::find_if(begin, m_end, [name](const Intrinsic &i) {
  154. return wcscmp(i.hlslName, name) == 0;
  155. });
  156. return BuildResult(found);
  157. }
  158. SearchResult Search(unsigned opcode) const {
  159. Intrinsic *begin = m_begin;
  160. assert(std::distance(begin, m_end) >= 0);
  161. Intrinsic *found = std::find_if(begin, m_end, [opcode](const Intrinsic &i) {
  162. return i.hlsl.Op == opcode;
  163. });
  164. return BuildResult(found);
  165. }
  166. bool MatchesNamespace(const wchar_t *ns) const {
  167. return wcscmp(m_namespace, ns) == 0;
  168. }
  169. private:
  170. const wchar_t *m_namespace;
  171. Intrinsic *m_begin;
  172. Intrinsic *m_end;
  173. bool IsStar(const wchar_t *name) const {
  174. return wcscmp(name, L"*") == 0;
  175. }
  176. SearchResult BuildResult(Intrinsic *found) const {
  177. if (found == m_end)
  178. return SearchResult{ nullptr, std::numeric_limits<uint64_t>::max() };
  179. return SearchResult{ found, static_cast<uint64_t>(std::distance(m_begin, found)) };
  180. }
  181. };
  182. class TestIntrinsicTable : public IDxcIntrinsicTable {
  183. private:
  184. DXC_MICROCOM_REF_FIELD(m_dwRef)
  185. std::vector<IntrinsicTable> m_tables;
  186. public:
  187. TestIntrinsicTable() : m_dwRef(0) {
  188. m_tables.push_back(IntrinsicTable(L"", std::begin(Intrinsics), std::end(Intrinsics)));
  189. m_tables.push_back(IntrinsicTable(L"Buffer", std::begin(BufferIntrinsics), std::end(BufferIntrinsics)));
  190. m_tables.push_back(IntrinsicTable(L"SamplerState", std::begin(SamplerIntrinsics), std::end(SamplerIntrinsics)));
  191. }
  192. DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
  193. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) override {
  194. return DoBasicQueryInterface<IDxcIntrinsicTable>(this, iid, ppvObject);
  195. }
  196. HRESULT STDMETHODCALLTYPE
  197. GetTableName(_Outptr_ LPCSTR *pTableName) override {
  198. *pTableName = "test";
  199. return S_OK;
  200. }
  201. HRESULT STDMETHODCALLTYPE LookupIntrinsic(
  202. LPCWSTR typeName, LPCWSTR functionName, const HLSL_INTRINSIC **pIntrinsic,
  203. _Inout_ UINT64 *pLookupCookie) override {
  204. if (typeName == nullptr)
  205. return E_FAIL;
  206. // Search for matching intrinsic name in matching namespace.
  207. IntrinsicTable::SearchResult result;
  208. for (const IntrinsicTable &table : m_tables) {
  209. if (table.MatchesNamespace(typeName)) {
  210. result = table.Search(functionName, *pLookupCookie);
  211. break;
  212. }
  213. }
  214. if (result) {
  215. *pIntrinsic = &result.intrinsic->hlsl;
  216. *pLookupCookie = result.index + 1;
  217. }
  218. else {
  219. *pIntrinsic = nullptr;
  220. *pLookupCookie = 0;
  221. }
  222. return result.intrinsic ? S_OK : E_FAIL;
  223. }
  224. HRESULT STDMETHODCALLTYPE
  225. GetLoweringStrategy(UINT opcode, _Outptr_ LPCSTR *pStrategy) override {
  226. Intrinsic *intrinsic = FindByOpcode(opcode);
  227. if (!intrinsic)
  228. return E_FAIL;
  229. *pStrategy = intrinsic->strategy;
  230. return S_OK;
  231. }
  232. HRESULT STDMETHODCALLTYPE
  233. GetIntrinsicName(UINT opcode, _Outptr_ LPCSTR *pName) override {
  234. Intrinsic *intrinsic = FindByOpcode(opcode);
  235. if (!intrinsic)
  236. return E_FAIL;
  237. *pName = intrinsic->dxilName;
  238. return S_OK;
  239. }
  240. HRESULT STDMETHODCALLTYPE
  241. GetDxilOpCode(UINT opcode, _Outptr_ UINT *pDxilOpcode) override {
  242. if (opcode == 13) {
  243. *pDxilOpcode = static_cast<UINT>(hlsl::OP::OpCode::IsInf);
  244. return S_OK;
  245. }
  246. else if (opcode == 14) {
  247. *pDxilOpcode = static_cast<UINT>(hlsl::OP::OpCode::Ibfe);
  248. return S_OK;
  249. }
  250. return E_FAIL;
  251. }
  252. Intrinsic *FindByOpcode(UINT opcode) {
  253. IntrinsicTable::SearchResult result;
  254. for (const IntrinsicTable &table : m_tables) {
  255. result = table.Search(opcode);
  256. if (result)
  257. break;
  258. }
  259. return result.intrinsic;
  260. }
  261. };
  262. // A class to test semantic define validation.
  263. // It takes a list of defines that when present should cause errors
  264. // and defines that should cause warnings. A more realistic validator
  265. // would look at the values and make sure (for example) they are
  266. // the correct type (integer, string, etc).
  267. class TestSemanticDefineValidator : public IDxcSemanticDefineValidator {
  268. private:
  269. DXC_MICROCOM_REF_FIELD(m_dwRef)
  270. std::vector<std::string> m_errorDefines;
  271. std::vector<std::string> m_warningDefines;
  272. public:
  273. TestSemanticDefineValidator(const std::vector<std::string> &errorDefines, const std::vector<std::string> &warningDefines)
  274. : m_dwRef(0)
  275. , m_errorDefines(errorDefines)
  276. , m_warningDefines(warningDefines)
  277. { }
  278. DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
  279. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) override {
  280. return DoBasicQueryInterface<IDxcSemanticDefineValidator>(this, iid, ppvObject);
  281. }
  282. virtual HRESULT STDMETHODCALLTYPE GetSemanticDefineWarningsAndErrors(LPCSTR pName, LPCSTR pValue, IDxcBlobEncoding **ppWarningBlob, IDxcBlobEncoding **ppErrorBlob) override {
  283. if (!pName || !pValue || !ppWarningBlob || !ppErrorBlob)
  284. return E_FAIL;
  285. auto Check = [pName](const std::vector<std::string> &errors, IDxcBlobEncoding **blob) {
  286. if (std::find(errors.begin(), errors.end(), pName) != errors.end()) {
  287. dxc::DxcDllSupport dllSupport;
  288. VERIFY_SUCCEEDED(dllSupport.Initialize());
  289. std::string error("bad define: ");
  290. error.append(pName);
  291. Utf8ToBlob(dllSupport, error.c_str(), blob);
  292. }
  293. };
  294. Check(m_errorDefines, ppErrorBlob);
  295. Check(m_warningDefines, ppWarningBlob);
  296. return S_OK;
  297. }
  298. };
  299. static void CheckOperationFailed(IDxcOperationResult *pResult) {
  300. HRESULT status;
  301. VERIFY_SUCCEEDED(pResult->GetStatus(&status));
  302. VERIFY_FAILED(status);
  303. }
  304. static std::string GetCompileErrors(IDxcOperationResult *pResult) {
  305. CComPtr<IDxcBlobEncoding> pErrors;
  306. VERIFY_SUCCEEDED(pResult->GetErrorBuffer(&pErrors));
  307. if (!pErrors)
  308. return "";
  309. return BlobToUtf8(pErrors);
  310. }
  311. class Compiler {
  312. public:
  313. Compiler(dxc::DxcDllSupport &dll) : m_dllSupport(dll) {
  314. VERIFY_SUCCEEDED(m_dllSupport.Initialize());
  315. VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  316. VERIFY_SUCCEEDED(pCompiler.QueryInterface(&pLangExtensions));
  317. }
  318. void RegisterSemanticDefine(LPCWSTR define) {
  319. VERIFY_SUCCEEDED(pLangExtensions->RegisterSemanticDefine(define));
  320. }
  321. void RegisterSemanticDefineExclusion(LPCWSTR define) {
  322. VERIFY_SUCCEEDED(pLangExtensions->RegisterSemanticDefineExclusion(define));
  323. }
  324. void SetSemanticDefineValidator(IDxcSemanticDefineValidator *validator) {
  325. pTestSemanticDefineValidator = validator;
  326. VERIFY_SUCCEEDED(pLangExtensions->SetSemanticDefineValidator(pTestSemanticDefineValidator));
  327. }
  328. void SetSemanticDefineMetaDataName(const char *name) {
  329. VERIFY_SUCCEEDED(pLangExtensions->SetSemanticDefineMetaDataName("test.defs"));
  330. }
  331. void RegisterIntrinsicTable(IDxcIntrinsicTable *table) {
  332. pTestIntrinsicTable = table;
  333. VERIFY_SUCCEEDED(pLangExtensions->RegisterIntrinsicTable(pTestIntrinsicTable));
  334. }
  335. IDxcOperationResult *Compile(const char *program) {
  336. return Compile(program, {}, {});
  337. }
  338. IDxcOperationResult *Compile(const char *program, const std::vector<LPCWSTR> &arguments, const std::vector<DxcDefine> defs ) {
  339. Utf8ToBlob(m_dllSupport, program, &pCodeBlob);
  340. VERIFY_SUCCEEDED(pCompiler->Compile(pCodeBlob, L"hlsl.hlsl", L"main",
  341. L"ps_6_0",
  342. const_cast<LPCWSTR *>(arguments.data()), arguments.size(),
  343. defs.data(), defs.size(),
  344. nullptr, &pCompileResult));
  345. return pCompileResult;
  346. }
  347. std::string Disassemble() {
  348. CComPtr<IDxcBlob> pBlob;
  349. CheckOperationSucceeded(pCompileResult, &pBlob);
  350. return DisassembleProgram(m_dllSupport, pBlob);
  351. }
  352. dxc::DxcDllSupport &m_dllSupport;
  353. CComPtr<IDxcCompiler> pCompiler;
  354. CComPtr<IDxcLangExtensions> pLangExtensions;
  355. CComPtr<IDxcBlobEncoding> pCodeBlob;
  356. CComPtr<IDxcOperationResult> pCompileResult;
  357. CComPtr<IDxcSemanticDefineValidator> pTestSemanticDefineValidator;
  358. CComPtr<IDxcIntrinsicTable> pTestIntrinsicTable;
  359. };
  360. ///////////////////////////////////////////////////////////////////////////////
  361. // Extension unit tests.
  362. #ifdef _WIN32
  363. class ExtensionTest {
  364. #else
  365. class ExtensionTest : public ::testing::Test {
  366. #endif
  367. public:
  368. BEGIN_TEST_CLASS(ExtensionTest)
  369. TEST_CLASS_PROPERTY(L"Parallel", L"true")
  370. TEST_METHOD_PROPERTY(L"Priority", L"0")
  371. END_TEST_CLASS()
  372. dxc::DxcDllSupport m_dllSupport;
  373. TEST_METHOD(DefineWhenRegisteredThenPreserved)
  374. TEST_METHOD(DefineValidationError)
  375. TEST_METHOD(DefineValidationWarning)
  376. TEST_METHOD(DefineNoValidatorOk)
  377. TEST_METHOD(DefineFromMacro)
  378. TEST_METHOD(IntrinsicWhenAvailableThenUsed)
  379. TEST_METHOD(CustomIntrinsicName)
  380. TEST_METHOD(NoLowering)
  381. TEST_METHOD(PackedLowering)
  382. TEST_METHOD(ReplicateLoweringWhenOnlyVectorIsResult)
  383. TEST_METHOD(UnsignedOpcodeIsUnchanged)
  384. TEST_METHOD(ResourceExtensionIntrinsic)
  385. TEST_METHOD(NameLoweredWhenNoReplicationNeeded)
  386. TEST_METHOD(DxilLoweringVector1)
  387. TEST_METHOD(DxilLoweringVector2)
  388. TEST_METHOD(DxilLoweringScalar)
  389. TEST_METHOD(SamplerExtensionIntrinsic)
  390. };
  391. TEST_F(ExtensionTest, DefineWhenRegisteredThenPreserved) {
  392. Compiler c(m_dllSupport);
  393. c.RegisterSemanticDefine(L"FOO*");
  394. c.RegisterSemanticDefineExclusion(L"FOOBAR");
  395. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({ "FOOLALA" }, {}));
  396. c.SetSemanticDefineMetaDataName("test.defs");
  397. c.Compile(
  398. "#define FOOTBALL AWESOME\n"
  399. "#define FOOTLOOSE TOO\n"
  400. "#define FOOBAR 123\n"
  401. "#define FOOD\n"
  402. "#define FOO 1 2 3\n"
  403. "float4 main() : SV_Target {\n"
  404. " return 0;\n"
  405. "}\n",
  406. {L"/Vd"},
  407. { { L"FOODEF", L"1"} }
  408. );
  409. std::string disassembly = c.Disassemble();
  410. // Check for root named md node. It contains pointers to md nodes for each define.
  411. VERIFY_IS_TRUE(
  412. disassembly.npos !=
  413. disassembly.find("!test.defs"));
  414. // #define FOODEF 1
  415. VERIFY_IS_TRUE(
  416. disassembly.npos !=
  417. disassembly.find("!{!\"FOODEF\", !\"1\"}"));
  418. // #define FOOTBALL AWESOME
  419. VERIFY_IS_TRUE(
  420. disassembly.npos !=
  421. disassembly.find("!{!\"FOOTBALL\", !\"AWESOME\"}"));
  422. // #define FOOTLOOSE TOO
  423. VERIFY_IS_TRUE(
  424. disassembly.npos !=
  425. disassembly.find("!{!\"FOOTLOOSE\", !\"TOO\"}"));
  426. // #define FOOD
  427. VERIFY_IS_TRUE(
  428. disassembly.npos !=
  429. disassembly.find("!{!\"FOOD\", !\"\"}"));
  430. // #define FOO 1 2 3
  431. VERIFY_IS_TRUE(
  432. disassembly.npos !=
  433. disassembly.find("!{!\"FOO\", !\"1 2 3\"}"));
  434. // FOOBAR should be excluded.
  435. VERIFY_IS_TRUE(
  436. disassembly.npos ==
  437. disassembly.find("!{!\"FOOBAR\""));
  438. }
  439. TEST_F(ExtensionTest, DefineValidationError) {
  440. Compiler c(m_dllSupport);
  441. c.RegisterSemanticDefine(L"FOO*");
  442. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({ "FOO" }, {}));
  443. IDxcOperationResult *pCompileResult = c.Compile(
  444. "#define FOO 1\n"
  445. "float4 main() : SV_Target {\n"
  446. " return 0;\n"
  447. "}\n",
  448. {L"/Vd"}, {}
  449. );
  450. // Check that validation error causes compile failure.
  451. CheckOperationFailed(pCompileResult);
  452. std::string errors = GetCompileErrors(pCompileResult);
  453. // Check that the error message is for the validation failure.
  454. VERIFY_IS_TRUE(
  455. errors.npos !=
  456. errors.find("hlsl.hlsl:1:9: error: bad define: FOO"));
  457. }
  458. TEST_F(ExtensionTest, DefineValidationWarning) {
  459. Compiler c(m_dllSupport);
  460. c.RegisterSemanticDefine(L"FOO*");
  461. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({}, { "FOO" }));
  462. IDxcOperationResult *pCompileResult = c.Compile(
  463. "#define FOO 1\n"
  464. "float4 main() : SV_Target {\n"
  465. " return 0;\n"
  466. "}\n",
  467. { L"/Vd" }, {}
  468. );
  469. std::string errors = GetCompileErrors(pCompileResult);
  470. // Check that the error message is for the validation failure.
  471. VERIFY_IS_TRUE(
  472. errors.npos !=
  473. errors.find("hlsl.hlsl:1:9: warning: bad define: FOO"));
  474. // Check the define is still emitted.
  475. std::string disassembly = c.Disassemble();
  476. // Check for root named md node. It contains pointers to md nodes for each define.
  477. VERIFY_IS_TRUE(
  478. disassembly.npos !=
  479. disassembly.find("!hlsl.semdefs"));
  480. // #define FOO 1
  481. VERIFY_IS_TRUE(
  482. disassembly.npos !=
  483. disassembly.find("!{!\"FOO\", !\"1\"}"));
  484. }
  485. TEST_F(ExtensionTest, DefineNoValidatorOk) {
  486. Compiler c(m_dllSupport);
  487. c.RegisterSemanticDefine(L"FOO*");
  488. c.Compile(
  489. "#define FOO 1\n"
  490. "float4 main() : SV_Target {\n"
  491. " return 0;\n"
  492. "}\n",
  493. { L"/Vd" }, {}
  494. );
  495. std::string disassembly = c.Disassemble();
  496. // Check the define is emitted.
  497. // #define FOO 1
  498. VERIFY_IS_TRUE(
  499. disassembly.npos !=
  500. disassembly.find("!{!\"FOO\", !\"1\"}"));
  501. }
  502. TEST_F(ExtensionTest, DefineFromMacro) {
  503. Compiler c(m_dllSupport);
  504. c.RegisterSemanticDefine(L"FOO*");
  505. c.Compile(
  506. "#define BAR 1\n"
  507. "#define FOO BAR\n"
  508. "float4 main() : SV_Target {\n"
  509. " return 0;\n"
  510. "}\n",
  511. { L"/Vd" }, {}
  512. );
  513. std::string disassembly = c.Disassemble();
  514. // Check the define is emitted.
  515. // #define FOO 1
  516. VERIFY_IS_TRUE(
  517. disassembly.npos !=
  518. disassembly.find("!{!\"FOO\", !\"1\"}"));
  519. }
  520. TEST_F(ExtensionTest, IntrinsicWhenAvailableThenUsed) {
  521. Compiler c(m_dllSupport);
  522. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  523. c.Compile(
  524. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  525. " test_proc(v);\n"
  526. " float2 a = test_fn(v);\n"
  527. " int2 b = test_fn(i);\n"
  528. " return a + b;\n"
  529. "}\n",
  530. { L"/Vd" }, {}
  531. );
  532. std::string disassembly = c.Disassemble();
  533. // Things to call out:
  534. // - result is float, not a vector
  535. // - mangled name contains the 'test' and '.r' parts
  536. // - opcode is first i32 argument
  537. // - second argument is float, ie it got scalarized
  538. VERIFY_IS_TRUE(
  539. disassembly.npos !=
  540. disassembly.find("call void @\"test.\\01?test_proc@hlsl@@YAXV?$vector@M$01@@@Z.r\"(i32 2, float"));
  541. VERIFY_IS_TRUE(
  542. disassembly.npos !=
  543. disassembly.find("call float @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@M$01@@V2@@Z.r\"(i32 1, float"));
  544. VERIFY_IS_TRUE(
  545. disassembly.npos !=
  546. disassembly.find("call i32 @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@H$01@@V2@@Z.r\"(i32 1, i32"));
  547. // - attributes are added to the declaration (the # at the end of the decl)
  548. // TODO: would be nice to check for the actual attribute (e.g. readonly)
  549. VERIFY_IS_TRUE(
  550. disassembly.npos !=
  551. disassembly.find("declare float @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@M$01@@V2@@Z.r\"(i32, float) #"));
  552. }
  553. TEST_F(ExtensionTest, CustomIntrinsicName) {
  554. Compiler c(m_dllSupport);
  555. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  556. c.Compile(
  557. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  558. " float2 a = test_poly(v);\n"
  559. " int2 b = test_poly(i);\n"
  560. " int2 c = test_int(i);\n"
  561. " return a + b + c;\n"
  562. "}\n",
  563. { L"/Vd" }, {}
  564. );
  565. std::string disassembly = c.Disassemble();
  566. // - custom name works for polymorphic function
  567. VERIFY_IS_TRUE(
  568. disassembly.npos !=
  569. disassembly.find("call float @test_poly.float(i32 3, float"));
  570. VERIFY_IS_TRUE(
  571. disassembly.npos !=
  572. disassembly.find("call i32 @test_poly.i32(i32 3, i32"));
  573. // - custom name works for non-polymorphic function
  574. VERIFY_IS_TRUE(
  575. disassembly.npos !=
  576. disassembly.find("call i32 @test_int(i32 4, i32"));
  577. }
  578. TEST_F(ExtensionTest, NoLowering) {
  579. Compiler c(m_dllSupport);
  580. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  581. c.Compile(
  582. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  583. " float2 a = test_nolower(v);\n"
  584. " float2 b = test_nolower(i);\n"
  585. " return a + b;\n"
  586. "}\n",
  587. { L"/Vd" }, {}
  588. );
  589. std::string disassembly = c.Disassemble();
  590. // - custom name works for non-lowered function
  591. // - non-lowered function has vector type as argument
  592. VERIFY_IS_TRUE(
  593. disassembly.npos !=
  594. disassembly.find("call <2 x float> @test_nolower.float(i32 5, <2 x float>"));
  595. VERIFY_IS_TRUE(
  596. disassembly.npos !=
  597. disassembly.find("call <2 x i32> @test_nolower.i32(i32 5, <2 x i32>"));
  598. }
  599. TEST_F(ExtensionTest, PackedLowering) {
  600. Compiler c(m_dllSupport);
  601. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  602. c.Compile(
  603. "float2 main(float2 v1 : V1, float2 v2 : V2, float3 v3 : V3) : SV_Target {\n"
  604. " test_pack_0(v1);\n"
  605. " int2 a = test_pack_1();\n"
  606. " float2 b = test_pack_2(v1, v2);\n"
  607. " float c = test_pack_3(v1);\n"
  608. " float2 d = test_pack_4(v3);\n"
  609. " return a + b + float2(c, c);\n"
  610. "}\n",
  611. { L"/Vd" }, {}
  612. );
  613. std::string disassembly = c.Disassemble();
  614. // - pack strategy changes vectors into structs
  615. VERIFY_IS_TRUE(
  616. disassembly.npos !=
  617. disassembly.find("call void @test_pack_0.float(i32 6, { float, float }"));
  618. VERIFY_IS_TRUE(
  619. disassembly.npos !=
  620. disassembly.find("call { float, float } @test_pack_1.float(i32 7)"));
  621. VERIFY_IS_TRUE(
  622. disassembly.npos !=
  623. disassembly.find("call { float, float } @test_pack_2.float(i32 8, { float, float }"));
  624. VERIFY_IS_TRUE(
  625. disassembly.npos !=
  626. disassembly.find("call float @test_pack_3.float(i32 9, { float, float }"));
  627. VERIFY_IS_TRUE(
  628. disassembly.npos !=
  629. disassembly.find("call { float, float } @test_pack_4.float(i32 10, { float, float, float }"));
  630. }
  631. TEST_F(ExtensionTest, ReplicateLoweringWhenOnlyVectorIsResult) {
  632. Compiler c(m_dllSupport);
  633. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  634. c.Compile(
  635. "float2 main(float2 v1 : V1, float2 v2 : V2, float3 v3 : V3) : SV_Target {\n"
  636. " return test_rand();\n"
  637. "}\n",
  638. { L"/Vd" }, {}
  639. );
  640. std::string disassembly = c.Disassemble();
  641. // - replicate strategy works for vector results
  642. VERIFY_IS_TRUE(
  643. disassembly.npos !=
  644. disassembly.find("call float @test_rand(i32 11)"));
  645. }
  646. TEST_F(ExtensionTest, UnsignedOpcodeIsUnchanged) {
  647. Compiler c(m_dllSupport);
  648. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  649. c.Compile(
  650. "uint main(uint v1 : V1) : SV_Target {\n"
  651. " return test_unsigned(v1);\n"
  652. "}\n",
  653. { L"/Vd" }, {}
  654. );
  655. std::string disassembly = c.Disassemble();
  656. // - opcode is unchanged when it matches an hlsl intrinsic with
  657. // an unsigned version.
  658. // This should use the same value as IOP_min.
  659. std::string matchStr;
  660. std::ostringstream ss(matchStr);
  661. ss << "call i32 @test_unsigned(i32 "
  662. << (unsigned)hlsl::IntrinsicOp::IOP_min
  663. << ", ";
  664. VERIFY_IS_TRUE(
  665. disassembly.npos !=
  666. disassembly.find(ss.str()));
  667. }
  668. TEST_F(ExtensionTest, ResourceExtensionIntrinsic) {
  669. Compiler c(m_dllSupport);
  670. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  671. c.Compile(
  672. "Buffer<float2> buf;"
  673. "float2 main(uint2 v1 : V1) : SV_Target {\n"
  674. " return buf.MyBufferOp(uint2(1, 2));\n"
  675. "}\n",
  676. { L"/Vd" }, {}
  677. );
  678. std::string disassembly = c.Disassemble();
  679. // Things to check
  680. // - return type is translated to dx.types.ResRet
  681. // - buffer is translated to dx.types.Handle
  682. // - vector is exploded
  683. llvm::Regex regex("call %dx.types.ResRet.f32 @MyBufferOp\\(i32 12, %dx.types.Handle %.*, i32 1, i32 2\\)");
  684. std::string regexErrors;
  685. VERIFY_IS_TRUE(regex.isValid(regexErrors));
  686. VERIFY_IS_TRUE(regex.match(disassembly));
  687. }
  688. TEST_F(ExtensionTest, NameLoweredWhenNoReplicationNeeded) {
  689. Compiler c(m_dllSupport);
  690. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  691. c.Compile(
  692. "int main(int v1 : V1) : SV_Target {\n"
  693. " return test_int(v1);\n"
  694. "}\n",
  695. { L"/Vd" }, {}
  696. );
  697. std::string disassembly = c.Disassemble();
  698. // Make sure the name is still lowered even when no replication
  699. // is needed because a non-vector overload of the function
  700. // was used.
  701. VERIFY_IS_TRUE(
  702. disassembly.npos !=
  703. disassembly.find("call i32 @test_int("));
  704. }
  705. TEST_F(ExtensionTest, DxilLoweringVector1) {
  706. Compiler c(m_dllSupport);
  707. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  708. c.Compile(
  709. "int main(float v1 : V1) : SV_Target {\n"
  710. " return test_isinf(v1);\n"
  711. "}\n",
  712. { L"/Vd" }, {}
  713. );
  714. std::string disassembly = c.Disassemble();
  715. // Check that the extension was lowered to the correct dxil intrinsic.
  716. static_assert(9 == (unsigned)hlsl::OP::OpCode::IsInf, "isinf opcode changed?");
  717. VERIFY_IS_TRUE(
  718. disassembly.npos !=
  719. disassembly.find("call i1 @dx.op.isSpecialFloat.f32(i32 9"));
  720. }
  721. TEST_F(ExtensionTest, DxilLoweringVector2) {
  722. Compiler c(m_dllSupport);
  723. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  724. c.Compile(
  725. "int2 main(float2 v1 : V1) : SV_Target {\n"
  726. " return test_isinf(v1);\n"
  727. "}\n",
  728. { L"/Vd" }, {}
  729. );
  730. std::string disassembly = c.Disassemble();
  731. // Check that the extension was lowered to the correct dxil intrinsic.
  732. static_assert(9 == (unsigned)hlsl::OP::OpCode::IsInf, "isinf opcode changed?");
  733. VERIFY_IS_TRUE(
  734. disassembly.npos !=
  735. disassembly.find("call i1 @dx.op.isSpecialFloat.f32(i32 9"));
  736. }
  737. TEST_F(ExtensionTest, DxilLoweringScalar) {
  738. Compiler c(m_dllSupport);
  739. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  740. c.Compile(
  741. "int main(uint v1 : V1, uint v2 : V2, uint v3 : V3) : SV_Target {\n"
  742. " return test_ibfe(v1, v2, v3);\n"
  743. "}\n",
  744. { L"/Vd" }, {}
  745. );
  746. std::string disassembly = c.Disassemble();
  747. // Check that the extension was lowered to the correct dxil intrinsic.
  748. static_assert(51 == (unsigned)hlsl::OP::OpCode::Ibfe, "ibfe opcode changed?");
  749. VERIFY_IS_TRUE(
  750. disassembly.npos !=
  751. disassembly.find("call i32 @dx.op.tertiary.i32(i32 51"));
  752. }
  753. TEST_F(ExtensionTest, SamplerExtensionIntrinsic) {
  754. // Test adding methods to objects that don't have any methods normally,
  755. // and therefore have null default intrinsic table.
  756. Compiler c(m_dllSupport);
  757. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  758. auto result = c.Compile(
  759. "SamplerState samp;"
  760. "float2 main(uint2 v1 : V1) : SV_Target {\n"
  761. " return samp.MySamplerOp(uint2(1, 2));\n"
  762. "}\n",
  763. { L"/Vd" }, {}
  764. );
  765. CheckOperationResultMsgs(result, {}, true, false);
  766. std::string disassembly = c.Disassemble();
  767. // Things to check
  768. // - works when SamplerState normally has no methods
  769. // - return type is translated to dx.types.ResRet
  770. // - buffer is translated to dx.types.Handle
  771. // - vector is exploded
  772. LPCSTR expected[] = {
  773. "call %dx.types.ResRet.f32 @MySamplerOp\\(i32 15, %dx.types.Handle %.*, i32 1, i32 2\\)"
  774. };
  775. CheckMsgs(disassembly.c_str(), disassembly.length(), expected, 1, true);
  776. }