ExtensionTest.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  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 "WexTestClass.h"
  11. #include "HlslTestUtils.h"
  12. #include "DxcTestUtils.h"
  13. #include "dxc/Support/microcom.h"
  14. #include "dxc/dxcapi.internal.h"
  15. #include "dxc/HLSL/HLOperationLowerExtension.h"
  16. #include "dxc/HlslIntrinsicOp.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. struct Intrinsic {
  85. LPCWSTR hlslName;
  86. const char *dxilName;
  87. const char *strategy;
  88. HLSL_INTRINSIC hlsl;
  89. };
  90. const char * DEFAULT_NAME = "";
  91. // llvm::array_lengthof that returns a UINT instead of size_t
  92. template <class T, std::size_t N>
  93. UINT countof(T(&)[N]) { return static_cast<UINT>(N); }
  94. Intrinsic Intrinsics[] = {
  95. {L"test_fn", DEFAULT_NAME, "r", { 1, false, true, -1, countof(TestFnArgs), TestFnArgs }},
  96. {L"test_proc", DEFAULT_NAME, "r", { 2, false, true, -1, countof(TestProcArgs), TestProcArgs }},
  97. {L"test_poly", "test_poly.$o", "r", { 3, false, true, -1, countof(TestFnCustomArgs), TestFnCustomArgs }},
  98. {L"test_int", "test_int", "r", { 4, false, true, -1, countof(TestFnIntArgs), TestFnIntArgs}},
  99. {L"test_nolower", "test_nolower.$o", "n", { 5, false, true, -1, countof(TestFnNoLowerArgs), TestFnNoLowerArgs}},
  100. {L"test_pack_0", "test_pack_0.$o", "p", { 6, false, true, -1, countof(TestFnPack0), TestFnPack0}},
  101. {L"test_pack_1", "test_pack_1.$o", "p", { 7, false, true, -1, countof(TestFnPack1), TestFnPack1}},
  102. {L"test_pack_2", "test_pack_2.$o", "p", { 8, false, true, -1, countof(TestFnPack2), TestFnPack2}},
  103. {L"test_pack_3", "test_pack_3.$o", "p", { 9, false, true, -1, countof(TestFnPack3), TestFnPack3}},
  104. {L"test_pack_4", "test_pack_4.$o", "p", { 10, false, true, -1, countof(TestFnPack4), TestFnPack4}},
  105. {L"test_rand", "test_rand", "r", { 11, false, false,-1, countof(TestRand), TestRand}},
  106. // Make this intrinsic have the same opcode as an hlsl intrinsic with an unsigned
  107. // counterpart for testing purposes.
  108. {L"test_unsigned","test_unsigned", "n", { static_cast<unsigned>(hlsl::IntrinsicOp::IOP_min), false, true, -1, countof(TestUnsigned), TestUnsigned}},
  109. };
  110. Intrinsic BufferIntrinsics[] = {
  111. {L"MyBufferOp", "MyBufferOp", "m", { 12, false, true, -1, countof(TestMyBufferOp), TestMyBufferOp}},
  112. };
  113. class IntrinsicTable {
  114. public:
  115. IntrinsicTable(wchar_t *ns, Intrinsic *begin, Intrinsic *end)
  116. : m_namespace(ns), m_begin(begin), m_end(end)
  117. { }
  118. struct SearchResult {
  119. Intrinsic *intrinsic;
  120. uint64_t index;
  121. SearchResult() : SearchResult(nullptr, 0) {}
  122. SearchResult(Intrinsic *i, uint64_t n) : intrinsic(i), index(n) {}
  123. operator bool() { return intrinsic != nullptr; }
  124. };
  125. SearchResult Search(const wchar_t *name, std::ptrdiff_t startIndex) const {
  126. Intrinsic *begin = m_begin + startIndex;
  127. assert(std::distance(begin, m_end) >= 0);
  128. if (IsStar(name))
  129. return BuildResult(begin);
  130. Intrinsic *found = std::find_if(begin, m_end, [name](const Intrinsic &i) {
  131. return wcscmp(i.hlslName, name) == 0;
  132. });
  133. return BuildResult(found);
  134. }
  135. SearchResult Search(unsigned opcode) const {
  136. Intrinsic *begin = m_begin;
  137. assert(std::distance(begin, m_end) >= 0);
  138. Intrinsic *found = std::find_if(begin, m_end, [opcode](const Intrinsic &i) {
  139. return i.hlsl.Op == opcode;
  140. });
  141. return BuildResult(found);
  142. }
  143. bool MatchesNamespace(const wchar_t *ns) const {
  144. return wcscmp(m_namespace, ns) == 0;
  145. }
  146. private:
  147. const wchar_t *m_namespace;
  148. Intrinsic *m_begin;
  149. Intrinsic *m_end;
  150. bool IsStar(const wchar_t *name) const {
  151. return wcscmp(name, L"*") == 0;
  152. }
  153. SearchResult BuildResult(Intrinsic *found) const {
  154. if (found == m_end)
  155. return SearchResult{ nullptr, std::numeric_limits<uint64_t>::max() };
  156. return SearchResult{ found, static_cast<uint64_t>(std::distance(m_begin, found)) };
  157. }
  158. };
  159. class TestIntrinsicTable : public IDxcIntrinsicTable {
  160. private:
  161. DXC_MICROCOM_REF_FIELD(m_dwRef);
  162. std::vector<IntrinsicTable> m_tables;
  163. public:
  164. TestIntrinsicTable() : m_dwRef(0) {
  165. m_tables.push_back(IntrinsicTable(L"", std::begin(Intrinsics), std::end(Intrinsics)));
  166. m_tables.push_back(IntrinsicTable(L"Buffer", std::begin(BufferIntrinsics), std::end(BufferIntrinsics)));
  167. }
  168. DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
  169. __override HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) {
  170. return DoBasicQueryInterface<IDxcIntrinsicTable>(this, iid, ppvObject);
  171. }
  172. __override HRESULT STDMETHODCALLTYPE
  173. GetTableName(_Outptr_ LPCSTR *pTableName) {
  174. *pTableName = "test";
  175. return S_OK;
  176. }
  177. __override HRESULT STDMETHODCALLTYPE LookupIntrinsic(
  178. LPCWSTR typeName, LPCWSTR functionName, const HLSL_INTRINSIC **pIntrinsic,
  179. _Inout_ UINT64 *pLookupCookie) {
  180. if (typeName == nullptr)
  181. return E_FAIL;
  182. // Search for matching intrinsic name in matching namespace.
  183. IntrinsicTable::SearchResult result;
  184. for (const IntrinsicTable &table : m_tables) {
  185. if (table.MatchesNamespace(typeName)) {
  186. result = table.Search(functionName, *pLookupCookie);
  187. break;
  188. }
  189. }
  190. if (result) {
  191. *pIntrinsic = &result.intrinsic->hlsl;
  192. *pLookupCookie = result.index + 1;
  193. }
  194. else {
  195. *pIntrinsic = nullptr;
  196. *pLookupCookie = 0;
  197. }
  198. return result.intrinsic ? S_OK : E_FAIL;
  199. }
  200. __override HRESULT STDMETHODCALLTYPE
  201. GetLoweringStrategy(UINT opcode, _Outptr_ LPCSTR *pStrategy) {
  202. Intrinsic *intrinsic = FindByOpcode(opcode);
  203. if (!intrinsic)
  204. return E_FAIL;
  205. *pStrategy = intrinsic->strategy;
  206. return S_OK;
  207. }
  208. __override HRESULT STDMETHODCALLTYPE
  209. GetIntrinsicName(UINT opcode, _Outptr_ LPCSTR *pName) {
  210. Intrinsic *intrinsic = FindByOpcode(opcode);
  211. if (!intrinsic)
  212. return E_FAIL;
  213. *pName = intrinsic->dxilName;
  214. return S_OK;
  215. }
  216. Intrinsic *FindByOpcode(UINT opcode) {
  217. IntrinsicTable::SearchResult result;
  218. for (const IntrinsicTable &table : m_tables) {
  219. result = table.Search(opcode);
  220. if (result)
  221. break;
  222. }
  223. return result.intrinsic;
  224. }
  225. };
  226. // A class to test semantic define validation.
  227. // It takes a list of defines that when present should cause errors
  228. // and defines that should cause warnings. A more realistic validator
  229. // would look at the values and make sure (for example) they are
  230. // the correct type (integer, string, etc).
  231. class TestSemanticDefineValidator : public IDxcSemanticDefineValidator {
  232. private:
  233. DXC_MICROCOM_REF_FIELD(m_dwRef);
  234. std::vector<std::string> m_errorDefines;
  235. std::vector<std::string> m_warningDefines;
  236. public:
  237. TestSemanticDefineValidator(const std::vector<std::string> &errorDefines, const std::vector<std::string> &warningDefines)
  238. : m_dwRef(0)
  239. , m_errorDefines(errorDefines)
  240. , m_warningDefines(warningDefines)
  241. { }
  242. DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
  243. __override HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) {
  244. return DoBasicQueryInterface<IDxcSemanticDefineValidator>(this, iid, ppvObject);
  245. }
  246. virtual HRESULT STDMETHODCALLTYPE GetSemanticDefineWarningsAndErrors(LPCSTR pName, LPCSTR pValue, IDxcBlobEncoding **ppWarningBlob, IDxcBlobEncoding **ppErrorBlob) {
  247. if (!pName || !pValue || !ppWarningBlob || !ppErrorBlob)
  248. return E_FAIL;
  249. auto Check = [pName](const std::vector<std::string> &errors, IDxcBlobEncoding **blob) {
  250. if (std::find(errors.begin(), errors.end(), pName) != errors.end()) {
  251. dxc::DxcDllSupport dllSupport;
  252. VERIFY_SUCCEEDED(dllSupport.Initialize());
  253. std::string error("bad define: ");
  254. error.append(pName);
  255. Utf8ToBlob(dllSupport, error.c_str(), blob);
  256. }
  257. };
  258. Check(m_errorDefines, ppErrorBlob);
  259. Check(m_warningDefines, ppWarningBlob);
  260. return S_OK;
  261. }
  262. };
  263. static void CheckOperationFailed(IDxcOperationResult *pResult) {
  264. HRESULT status;
  265. VERIFY_SUCCEEDED(pResult->GetStatus(&status));
  266. VERIFY_FAILED(status);
  267. }
  268. static std::string GetCompileErrors(IDxcOperationResult *pResult) {
  269. CComPtr<IDxcBlobEncoding> pErrors;
  270. VERIFY_SUCCEEDED(pResult->GetErrorBuffer(&pErrors));
  271. if (!pErrors)
  272. return "";
  273. return BlobToUtf8(pErrors);
  274. }
  275. class Compiler {
  276. public:
  277. Compiler(dxc::DxcDllSupport &dll) : m_dllSupport(dll) {
  278. VERIFY_SUCCEEDED(m_dllSupport.Initialize());
  279. VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  280. VERIFY_SUCCEEDED(pCompiler.QueryInterface(&pLangExtensions));
  281. }
  282. void RegisterSemanticDefine(LPCWSTR define) {
  283. VERIFY_SUCCEEDED(pLangExtensions->RegisterSemanticDefine(define));
  284. }
  285. void RegisterSemanticDefineExclusion(LPCWSTR define) {
  286. VERIFY_SUCCEEDED(pLangExtensions->RegisterSemanticDefineExclusion(define));
  287. }
  288. void SetSemanticDefineValidator(IDxcSemanticDefineValidator *validator) {
  289. pTestSemanticDefineValidator = validator;
  290. VERIFY_SUCCEEDED(pLangExtensions->SetSemanticDefineValidator(pTestSemanticDefineValidator));
  291. }
  292. void SetSemanticDefineMetaDataName(const char *name) {
  293. VERIFY_SUCCEEDED(pLangExtensions->SetSemanticDefineMetaDataName("test.defs"));
  294. }
  295. void RegisterIntrinsicTable(IDxcIntrinsicTable *table) {
  296. pTestIntrinsicTable = table;
  297. VERIFY_SUCCEEDED(pLangExtensions->RegisterIntrinsicTable(pTestIntrinsicTable));
  298. }
  299. IDxcOperationResult *Compile(const char *program) {
  300. return Compile(program, {}, {});
  301. }
  302. IDxcOperationResult *Compile(const char *program, const std::vector<LPCWSTR> &arguments, const std::vector<DxcDefine> defs ) {
  303. Utf8ToBlob(m_dllSupport, program, &pCodeBlob);
  304. VERIFY_SUCCEEDED(pCompiler->Compile(pCodeBlob, L"hlsl.hlsl", L"main",
  305. L"ps_6_0",
  306. const_cast<LPCWSTR *>(arguments.data()), arguments.size(),
  307. defs.data(), defs.size(),
  308. nullptr, &pCompileResult));
  309. return pCompileResult;
  310. }
  311. std::string Disassemble() {
  312. CComPtr<IDxcBlob> pBlob;
  313. CheckOperationSucceeded(pCompileResult, &pBlob);
  314. return DisassembleProgram(m_dllSupport, pBlob);
  315. }
  316. dxc::DxcDllSupport &m_dllSupport;
  317. CComPtr<IDxcCompiler> pCompiler;
  318. CComPtr<IDxcLangExtensions> pLangExtensions;
  319. CComPtr<IDxcBlobEncoding> pCodeBlob;
  320. CComPtr<IDxcOperationResult> pCompileResult;
  321. CComPtr<IDxcSemanticDefineValidator> pTestSemanticDefineValidator;
  322. CComPtr<IDxcIntrinsicTable> pTestIntrinsicTable;
  323. };
  324. ///////////////////////////////////////////////////////////////////////////////
  325. // Extension unit tests.
  326. class ExtensionTest {
  327. public:
  328. BEGIN_TEST_CLASS(ExtensionTest)
  329. TEST_METHOD_PROPERTY(L"Priority", L"0")
  330. END_TEST_CLASS()
  331. dxc::DxcDllSupport m_dllSupport;
  332. TEST_METHOD(DefineWhenRegisteredThenPreserved);
  333. TEST_METHOD(DefineValidationError);
  334. TEST_METHOD(DefineValidationWarning);
  335. TEST_METHOD(DefineNoValidatorOk);
  336. TEST_METHOD(DefineFromMacro);
  337. TEST_METHOD(IntrinsicWhenAvailableThenUsed);
  338. TEST_METHOD(CustomIntrinsicName);
  339. TEST_METHOD(NoLowering);
  340. TEST_METHOD(PackedLowering);
  341. TEST_METHOD(ReplicateLoweringWhenOnlyVectorIsResult);
  342. TEST_METHOD(UnsignedOpcodeIsUnchanged);
  343. TEST_METHOD(ResourceExtensionIntrinsic);
  344. TEST_METHOD(NameLoweredWhenNoReplicationNeeded);
  345. };
  346. TEST_F(ExtensionTest, DefineWhenRegisteredThenPreserved) {
  347. Compiler c(m_dllSupport);
  348. c.RegisterSemanticDefine(L"FOO*");
  349. c.RegisterSemanticDefineExclusion(L"FOOBAR");
  350. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({ "FOOLALA" }, {}));
  351. c.SetSemanticDefineMetaDataName("test.defs");
  352. c.Compile(
  353. "#define FOOTBALL AWESOME\n"
  354. "#define FOOTLOOSE TOO\n"
  355. "#define FOOBAR 123\n"
  356. "#define FOOD\n"
  357. "#define FOO 1 2 3\n"
  358. "float4 main() : SV_Target {\n"
  359. " return 0;\n"
  360. "}\n",
  361. {L"/Vd"},
  362. { { L"FOODEF", L"1"} }
  363. );
  364. std::string disassembly = c.Disassemble();
  365. // Check for root named md node. It contains pointers to md nodes for each define.
  366. VERIFY_IS_TRUE(
  367. disassembly.npos !=
  368. disassembly.find("!test.defs"));
  369. // #define FOODEF 1
  370. VERIFY_IS_TRUE(
  371. disassembly.npos !=
  372. disassembly.find("!{!\"FOODEF\", !\"1\"}"));
  373. // #define FOOTBALL AWESOME
  374. VERIFY_IS_TRUE(
  375. disassembly.npos !=
  376. disassembly.find("!{!\"FOOTBALL\", !\"AWESOME\"}"));
  377. // #define FOOTLOOSE TOO
  378. VERIFY_IS_TRUE(
  379. disassembly.npos !=
  380. disassembly.find("!{!\"FOOTLOOSE\", !\"TOO\"}"));
  381. // #define FOOD
  382. VERIFY_IS_TRUE(
  383. disassembly.npos !=
  384. disassembly.find("!{!\"FOOD\", !\"\"}"));
  385. // #define FOO 1 2 3
  386. VERIFY_IS_TRUE(
  387. disassembly.npos !=
  388. disassembly.find("!{!\"FOO\", !\"1 2 3\"}"));
  389. // FOOBAR should be excluded.
  390. VERIFY_IS_TRUE(
  391. disassembly.npos ==
  392. disassembly.find("!{!\"FOOBAR\""));
  393. }
  394. TEST_F(ExtensionTest, DefineValidationError) {
  395. Compiler c(m_dllSupport);
  396. c.RegisterSemanticDefine(L"FOO*");
  397. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({ "FOO" }, {}));
  398. IDxcOperationResult *pCompileResult = c.Compile(
  399. "#define FOO 1\n"
  400. "float4 main() : SV_Target {\n"
  401. " return 0;\n"
  402. "}\n",
  403. {L"/Vd"}, {}
  404. );
  405. // Check that validation error causes compile failure.
  406. CheckOperationFailed(pCompileResult);
  407. std::string errors = GetCompileErrors(pCompileResult);
  408. // Check that the error message is for the validation failure.
  409. VERIFY_IS_TRUE(
  410. errors.npos !=
  411. errors.find("hlsl.hlsl:1:9: error: bad define: FOO"));
  412. }
  413. TEST_F(ExtensionTest, DefineValidationWarning) {
  414. Compiler c(m_dllSupport);
  415. c.RegisterSemanticDefine(L"FOO*");
  416. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({}, { "FOO" }));
  417. IDxcOperationResult *pCompileResult = c.Compile(
  418. "#define FOO 1\n"
  419. "float4 main() : SV_Target {\n"
  420. " return 0;\n"
  421. "}\n",
  422. { L"/Vd" }, {}
  423. );
  424. std::string errors = GetCompileErrors(pCompileResult);
  425. // Check that the error message is for the validation failure.
  426. VERIFY_IS_TRUE(
  427. errors.npos !=
  428. errors.find("hlsl.hlsl:1:9: warning: bad define: FOO"));
  429. // Check the define is still emitted.
  430. std::string disassembly = c.Disassemble();
  431. // Check for root named md node. It contains pointers to md nodes for each define.
  432. VERIFY_IS_TRUE(
  433. disassembly.npos !=
  434. disassembly.find("!hlsl.semdefs"));
  435. // #define FOO 1
  436. VERIFY_IS_TRUE(
  437. disassembly.npos !=
  438. disassembly.find("!{!\"FOO\", !\"1\"}"));
  439. }
  440. TEST_F(ExtensionTest, DefineNoValidatorOk) {
  441. Compiler c(m_dllSupport);
  442. c.RegisterSemanticDefine(L"FOO*");
  443. c.Compile(
  444. "#define FOO 1\n"
  445. "float4 main() : SV_Target {\n"
  446. " return 0;\n"
  447. "}\n",
  448. { L"/Vd" }, {}
  449. );
  450. std::string disassembly = c.Disassemble();
  451. // Check the define is emitted.
  452. // #define FOO 1
  453. VERIFY_IS_TRUE(
  454. disassembly.npos !=
  455. disassembly.find("!{!\"FOO\", !\"1\"}"));
  456. }
  457. TEST_F(ExtensionTest, DefineFromMacro) {
  458. Compiler c(m_dllSupport);
  459. c.RegisterSemanticDefine(L"FOO*");
  460. c.Compile(
  461. "#define BAR 1\n"
  462. "#define FOO BAR\n"
  463. "float4 main() : SV_Target {\n"
  464. " return 0;\n"
  465. "}\n",
  466. { L"/Vd" }, {}
  467. );
  468. std::string disassembly = c.Disassemble();
  469. // Check the define is emitted.
  470. // #define FOO 1
  471. VERIFY_IS_TRUE(
  472. disassembly.npos !=
  473. disassembly.find("!{!\"FOO\", !\"1\"}"));
  474. }
  475. TEST_F(ExtensionTest, IntrinsicWhenAvailableThenUsed) {
  476. Compiler c(m_dllSupport);
  477. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  478. c.Compile(
  479. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  480. " test_proc(v);\n"
  481. " float2 a = test_fn(v);\n"
  482. " int2 b = test_fn(i);\n"
  483. " return a + b;\n"
  484. "}\n",
  485. { L"/Vd" }, {}
  486. );
  487. std::string disassembly = c.Disassemble();
  488. // Things to call out:
  489. // - result is float, not a vector
  490. // - mangled name contains the 'test' and '.r' parts
  491. // - opcode is first i32 argument
  492. // - second argument is float, ie it got scalarized
  493. VERIFY_IS_TRUE(
  494. disassembly.npos !=
  495. disassembly.find("call void @\"test.\\01?test_proc@hlsl@@YAXV?$vector@M$01@@@Z.r\"(i32 2, float"));
  496. VERIFY_IS_TRUE(
  497. disassembly.npos !=
  498. disassembly.find("call float @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@M$01@@V2@@Z.r\"(i32 1, float"));
  499. VERIFY_IS_TRUE(
  500. disassembly.npos !=
  501. disassembly.find("call i32 @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@H$01@@V2@@Z.r\"(i32 1, i32"));
  502. // - attributes are added to the declaration (the # at the end of the decl)
  503. // TODO: would be nice to check for the actual attribute (e.g. readonly)
  504. VERIFY_IS_TRUE(
  505. disassembly.npos !=
  506. disassembly.find("declare float @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@M$01@@V2@@Z.r\"(i32, float) #"));
  507. }
  508. TEST_F(ExtensionTest, CustomIntrinsicName) {
  509. Compiler c(m_dllSupport);
  510. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  511. c.Compile(
  512. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  513. " float2 a = test_poly(v);\n"
  514. " int2 b = test_poly(i);\n"
  515. " int2 c = test_int(i);\n"
  516. " return a + b + c;\n"
  517. "}\n",
  518. { L"/Vd" }, {}
  519. );
  520. std::string disassembly = c.Disassemble();
  521. // - custom name works for polymorphic function
  522. VERIFY_IS_TRUE(
  523. disassembly.npos !=
  524. disassembly.find("call float @test_poly.float(i32 3, float"));
  525. VERIFY_IS_TRUE(
  526. disassembly.npos !=
  527. disassembly.find("call i32 @test_poly.i32(i32 3, i32"));
  528. // - custom name works for non-polymorphic function
  529. VERIFY_IS_TRUE(
  530. disassembly.npos !=
  531. disassembly.find("call i32 @test_int(i32 4, i32"));
  532. }
  533. TEST_F(ExtensionTest, NoLowering) {
  534. Compiler c(m_dllSupport);
  535. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  536. c.Compile(
  537. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  538. " float2 a = test_nolower(v);\n"
  539. " float2 b = test_nolower(i);\n"
  540. " return a + b;\n"
  541. "}\n",
  542. { L"/Vd" }, {}
  543. );
  544. std::string disassembly = c.Disassemble();
  545. // - custom name works for non-lowered function
  546. // - non-lowered function has vector type as argument
  547. VERIFY_IS_TRUE(
  548. disassembly.npos !=
  549. disassembly.find("call <2 x float> @test_nolower.float(i32 5, <2 x float>"));
  550. VERIFY_IS_TRUE(
  551. disassembly.npos !=
  552. disassembly.find("call <2 x i32> @test_nolower.i32(i32 5, <2 x i32>"));
  553. }
  554. TEST_F(ExtensionTest, PackedLowering) {
  555. Compiler c(m_dllSupport);
  556. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  557. c.Compile(
  558. "float2 main(float2 v1 : V1, float2 v2 : V2, float3 v3 : V3) : SV_Target {\n"
  559. " test_pack_0(v1);\n"
  560. " int2 a = test_pack_1();\n"
  561. " float2 b = test_pack_2(v1, v2);\n"
  562. " float c = test_pack_3(v1);\n"
  563. " float2 d = test_pack_4(v3);\n"
  564. " return a + b + float2(c, c);\n"
  565. "}\n",
  566. { L"/Vd" }, {}
  567. );
  568. std::string disassembly = c.Disassemble();
  569. // - pack strategy changes vectors into structs
  570. VERIFY_IS_TRUE(
  571. disassembly.npos !=
  572. disassembly.find("call void @test_pack_0.float(i32 6, { float, float }"));
  573. VERIFY_IS_TRUE(
  574. disassembly.npos !=
  575. disassembly.find("call { float, float } @test_pack_1.float(i32 7)"));
  576. VERIFY_IS_TRUE(
  577. disassembly.npos !=
  578. disassembly.find("call { float, float } @test_pack_2.float(i32 8, { float, float }"));
  579. VERIFY_IS_TRUE(
  580. disassembly.npos !=
  581. disassembly.find("call float @test_pack_3.float(i32 9, { float, float }"));
  582. VERIFY_IS_TRUE(
  583. disassembly.npos !=
  584. disassembly.find("call { float, float } @test_pack_4.float(i32 10, { float, float, float }"));
  585. }
  586. TEST_F(ExtensionTest, ReplicateLoweringWhenOnlyVectorIsResult) {
  587. Compiler c(m_dllSupport);
  588. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  589. c.Compile(
  590. "float2 main(float2 v1 : V1, float2 v2 : V2, float3 v3 : V3) : SV_Target {\n"
  591. " return test_rand();\n"
  592. "}\n",
  593. { L"/Vd" }, {}
  594. );
  595. std::string disassembly = c.Disassemble();
  596. // - replicate strategy works for vector results
  597. VERIFY_IS_TRUE(
  598. disassembly.npos !=
  599. disassembly.find("call float @test_rand(i32 11)"));
  600. }
  601. TEST_F(ExtensionTest, UnsignedOpcodeIsUnchanged) {
  602. Compiler c(m_dllSupport);
  603. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  604. c.Compile(
  605. "uint main(uint v1 : V1) : SV_Target {\n"
  606. " return test_unsigned(v1);\n"
  607. "}\n",
  608. { L"/Vd" }, {}
  609. );
  610. std::string disassembly = c.Disassemble();
  611. // - opcode is unchanged when it matches an hlsl intrinsic with
  612. // an unsigned version.
  613. // Note that 113 is the current opcode for IOP_min. If that opcode
  614. // changes the test will need to be updated to reflect the new opcode.
  615. VERIFY_IS_TRUE(
  616. disassembly.npos !=
  617. disassembly.find("call i32 @test_unsigned(i32 113, "));
  618. }
  619. TEST_F(ExtensionTest, ResourceExtensionIntrinsic) {
  620. Compiler c(m_dllSupport);
  621. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  622. c.Compile(
  623. "Buffer<float2> buf;"
  624. "float2 main(uint2 v1 : V1) : SV_Target {\n"
  625. " return buf.MyBufferOp(uint2(1, 2));\n"
  626. "}\n",
  627. { L"/Vd" }, {}
  628. );
  629. std::string disassembly = c.Disassemble();
  630. // Things to check
  631. // - return type is translated to dx.types.ResRet
  632. // - buffer is translated to dx.types.Handle
  633. // - vector is exploded
  634. llvm::Regex regex("call %dx.types.ResRet.f32 @MyBufferOp\\(i32 12, %dx.types.Handle %.*, i32 1, i32 2\\)");
  635. std::string regexErrors;
  636. VERIFY_IS_TRUE(regex.isValid(regexErrors));
  637. VERIFY_IS_TRUE(regex.match(disassembly));
  638. }
  639. TEST_F(ExtensionTest, NameLoweredWhenNoReplicationNeeded) {
  640. Compiler c(m_dllSupport);
  641. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  642. c.Compile(
  643. "int main(int v1 : V1) : SV_Target {\n"
  644. " return test_int(v1);\n"
  645. "}\n",
  646. { L"/Vd" }, {}
  647. );
  648. std::string disassembly = c.Disassemble();
  649. // Make sure the name is still lowered even when no replication
  650. // is needed because a non-vector overload of the function
  651. // was used.
  652. VERIFY_IS_TRUE(
  653. disassembly.npos !=
  654. disassembly.find("call i32 @test_int("));
  655. }