ExtensionTest.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  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 "dxc/Test/CompilationResult.h"
  10. #include "dxc/Test/HlslTestUtils.h"
  11. #include "dxc/Test/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. // $result = wave_proc(any_vector<any_cardinality> value)
  102. static const HLSL_INTRINSIC_ARGUMENT WaveProcArgs[] = {
  103. { "wave_proc", AR_QUAL_OUT, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C },
  104. { "value", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_NUMERIC, 1, IA_C }
  105. };
  106. // uint = Texutre1D.MyTextureOp(uint addr, uint offset)
  107. static const HLSL_INTRINSIC_ARGUMENT TestMyTexture1DOp_0[] = {
  108. { "MyTextureOp", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_UINT, 1, 1 },
  109. { "addr", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  110. { "offset", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  111. };
  112. // uint = Texutre1D.MyTextureOp(uint addr, uint offset, uint val)
  113. static const HLSL_INTRINSIC_ARGUMENT TestMyTexture1DOp_1[] = {
  114. { "MyTextureOp", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_UINT, 1, 1 },
  115. { "addr", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  116. { "offset", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  117. { "val", AR_QUAL_IN, 1, LITEMPLATE_SCALAR, 1, LICOMPTYPE_UINT, 1, 1},
  118. };
  119. // uint2 = Texture2D.MyTextureOp(uint2 addr, uint2 val)
  120. static const HLSL_INTRINSIC_ARGUMENT TestMyTexture2DOp[] = {
  121. { "MyTextureOp", AR_QUAL_OUT, 0, LITEMPLATE_VECTOR, 0, LICOMPTYPE_UINT, 1, 1 },
  122. { "addr", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_UINT, 1, 2},
  123. { "val", AR_QUAL_IN, 1, LITEMPLATE_VECTOR, 1, LICOMPTYPE_UINT, 1, 2},
  124. };
  125. // float = test_overload(float a, uint b, double c)
  126. static const HLSL_INTRINSIC_ARGUMENT TestOverloadArgs[] = {
  127. { "test_overload", AR_QUAL_OUT, 0, LITEMPLATE_SCALAR, 0, LICOMPTYPE_NUMERIC, 1, IA_C },
  128. { "a", AR_QUAL_IN, 1, LITEMPLATE_ANY, 1, LICOMPTYPE_FLOAT, 1, IA_C },
  129. { "b", AR_QUAL_IN, 2, LITEMPLATE_ANY, 2, LICOMPTYPE_UINT, 1, IA_C },
  130. { "c", AR_QUAL_IN, 3, LITEMPLATE_SCALAR, 3, LICOMPTYPE_DOUBLE, 1, IA_C },
  131. };
  132. struct Intrinsic {
  133. LPCWSTR hlslName;
  134. const char *dxilName;
  135. const char *strategy;
  136. HLSL_INTRINSIC hlsl;
  137. };
  138. const char * DEFAULT_NAME = "";
  139. // llvm::array_lengthof that returns a UINT instead of size_t
  140. template <class T, std::size_t N>
  141. UINT countof(T(&)[N]) { return static_cast<UINT>(N); }
  142. Intrinsic Intrinsics[] = {
  143. {L"test_fn", DEFAULT_NAME, "r", { 1, false, true, false, -1, countof(TestFnArgs), TestFnArgs }},
  144. {L"test_proc", DEFAULT_NAME, "r", { 2, false, false, false,-1, countof(TestProcArgs), TestProcArgs }},
  145. {L"test_poly", "test_poly.$o", "r", { 3, false, true, false, -1, countof(TestFnCustomArgs), TestFnCustomArgs }},
  146. {L"test_int", "test_int", "r", { 4, false, true, false, -1, countof(TestFnIntArgs), TestFnIntArgs}},
  147. {L"test_nolower", "test_nolower.$o", "n", { 5, false, true, false, -1, countof(TestFnNoLowerArgs), TestFnNoLowerArgs}},
  148. {L"test_pack_0", "test_pack_0.$o", "p", { 6, false, false, false,-1, countof(TestFnPack0), TestFnPack0}},
  149. {L"test_pack_1", "test_pack_1.$o", "p", { 7, false, true, false, -1, countof(TestFnPack1), TestFnPack1}},
  150. {L"test_pack_2", "test_pack_2.$o", "p", { 8, false, true, false, -1, countof(TestFnPack2), TestFnPack2}},
  151. {L"test_pack_3", "test_pack_3.$o", "p", { 9, false, true, false, -1, countof(TestFnPack3), TestFnPack3}},
  152. {L"test_pack_4", "test_pack_4.$o", "p", { 10, false, false, false,-1, countof(TestFnPack4), TestFnPack4}},
  153. {L"test_rand", "test_rand", "r", { 11, false, false, false,-1, countof(TestRand), TestRand}},
  154. {L"test_isinf", "test_isinf", "d", { 13, true, true, false, -1, countof(TestIsInf), TestIsInf}},
  155. {L"test_ibfe", "test_ibfe", "d", { 14, true, true, false, -1, countof(TestIBFE), TestIBFE}},
  156. // Make this intrinsic have the same opcode as an hlsl intrinsic with an unsigned
  157. // counterpart for testing purposes.
  158. {L"test_unsigned","test_unsigned", "n", { static_cast<unsigned>(hlsl::IntrinsicOp::IOP_min), false, true, false, -1, countof(TestUnsigned), TestUnsigned}},
  159. {L"wave_proc", DEFAULT_NAME, "r", { 16, false, true, true, -1, countof(WaveProcArgs), WaveProcArgs }},
  160. {L"test_o_1", "test_o_1.$o:1", "r", { 18, false, true, true, -1, countof(TestOverloadArgs), TestOverloadArgs }},
  161. {L"test_o_2", "test_o_2.$o:2", "r", { 19, false, true, true, -1, countof(TestOverloadArgs), TestOverloadArgs }},
  162. {L"test_o_3", "test_o_3.$o:3", "r", { 20, false, true, true, -1, countof(TestOverloadArgs), TestOverloadArgs }},
  163. };
  164. Intrinsic BufferIntrinsics[] = {
  165. {L"MyBufferOp", "MyBufferOp", "m", { 12, false, true, false, -1, countof(TestMyBufferOp), TestMyBufferOp}},
  166. };
  167. // Test adding a method to an object that normally has no methods (SamplerState will do).
  168. Intrinsic SamplerIntrinsics[] = {
  169. {L"MySamplerOp", "MySamplerOp", "m", { 15, false, true, false, -1, countof(TestMySamplerOp), TestMySamplerOp}},
  170. };
  171. // Define a lowering string to target a common dxil extension operation defined like this:
  172. //
  173. // @MyTextureOp(i32 opcode, %dx.types.Handle, i32 addr0, i32 addr1, i32 offset, i32 val0, i32 val1);
  174. //
  175. // This would produce the following lowerings (assuming the MyTextureOp opcode is 17)
  176. //
  177. // hlsl: Texture1D.MyTextureOp(a, b)
  178. // hl: @MyTextureOp(17, handle, a, b)
  179. // dxil: @MyTextureOp(17, handle, a, undef, b, undef, undef)
  180. //
  181. // hlsl: Texture1D.MyTextureOp(a, b, c)
  182. // hl: @MyTextureOp(17, handle, a, b, c)
  183. // dxil: @MyTextureOp(17, handle, a, undef, b, c, undef)
  184. //
  185. // hlsl: Texture2D.MyTextureOp(a, c)
  186. // hl: @MyTextureOp(17, handle, a, c)
  187. // dxil: @MyTextureOp(17, handle, a.x, a.y, undef, c.x, c.y)
  188. //
  189. static const char *MyTextureOp_LoweringInfo =
  190. "m:{"
  191. "\"default\" : \"0,1,2.0,2.1,3,4.0:?i32,4.1:?i32\","
  192. "\"Texture2D\" : \"0,1,2.0,2.1,-1:?i32,3.0,3.1\""
  193. "}";
  194. Intrinsic Texture1DIntrinsics[] = {
  195. {L"MyTextureOp", "MyTextureOp", MyTextureOp_LoweringInfo, { 17, false, true, false, -1, countof(TestMyTexture1DOp_0), TestMyTexture1DOp_0}},
  196. {L"MyTextureOp", "MyTextureOp", MyTextureOp_LoweringInfo, { 17, false, true, false, -1, countof(TestMyTexture1DOp_1), TestMyTexture1DOp_1}},
  197. };
  198. Intrinsic Texture2DIntrinsics[] = {
  199. {L"MyTextureOp", "MyTextureOp", MyTextureOp_LoweringInfo, { 17, false, true, false, -1, countof(TestMyTexture2DOp), TestMyTexture2DOp}},
  200. };
  201. class IntrinsicTable {
  202. public:
  203. IntrinsicTable(const wchar_t *ns, Intrinsic *begin, Intrinsic *end)
  204. : m_namespace(ns), m_begin(begin), m_end(end)
  205. { }
  206. struct SearchResult {
  207. Intrinsic *intrinsic;
  208. uint64_t index;
  209. SearchResult() : SearchResult(nullptr, 0) {}
  210. SearchResult(Intrinsic *i, uint64_t n) : intrinsic(i), index(n) {}
  211. operator bool() { return intrinsic != nullptr; }
  212. };
  213. SearchResult Search(const wchar_t *name, std::ptrdiff_t startIndex) const {
  214. Intrinsic *begin = m_begin + startIndex;
  215. assert(std::distance(begin, m_end) >= 0);
  216. if (IsStar(name))
  217. return BuildResult(begin);
  218. Intrinsic *found = std::find_if(begin, m_end, [name](const Intrinsic &i) {
  219. return wcscmp(i.hlslName, name) == 0;
  220. });
  221. return BuildResult(found);
  222. }
  223. SearchResult Search(unsigned opcode) const {
  224. Intrinsic *begin = m_begin;
  225. assert(std::distance(begin, m_end) >= 0);
  226. Intrinsic *found = std::find_if(begin, m_end, [opcode](const Intrinsic &i) {
  227. return i.hlsl.Op == opcode;
  228. });
  229. return BuildResult(found);
  230. }
  231. bool MatchesNamespace(const wchar_t *ns) const {
  232. return wcscmp(m_namespace, ns) == 0;
  233. }
  234. private:
  235. const wchar_t *m_namespace;
  236. Intrinsic *m_begin;
  237. Intrinsic *m_end;
  238. bool IsStar(const wchar_t *name) const {
  239. return wcscmp(name, L"*") == 0;
  240. }
  241. SearchResult BuildResult(Intrinsic *found) const {
  242. if (found == m_end)
  243. return SearchResult{ nullptr, std::numeric_limits<uint64_t>::max() };
  244. return SearchResult{ found, static_cast<uint64_t>(std::distance(m_begin, found)) };
  245. }
  246. };
  247. class TestIntrinsicTable : public IDxcIntrinsicTable {
  248. private:
  249. DXC_MICROCOM_REF_FIELD(m_dwRef)
  250. std::vector<IntrinsicTable> m_tables;
  251. public:
  252. TestIntrinsicTable() : m_dwRef(0) {
  253. m_tables.push_back(IntrinsicTable(L"", std::begin(Intrinsics), std::end(Intrinsics)));
  254. m_tables.push_back(IntrinsicTable(L"Buffer", std::begin(BufferIntrinsics), std::end(BufferIntrinsics)));
  255. m_tables.push_back(IntrinsicTable(L"SamplerState", std::begin(SamplerIntrinsics), std::end(SamplerIntrinsics)));
  256. m_tables.push_back(IntrinsicTable(L"Texture1D", std::begin(Texture1DIntrinsics), std::end(Texture1DIntrinsics)));
  257. m_tables.push_back(IntrinsicTable(L"Texture2D", std::begin(Texture2DIntrinsics), std::end(Texture2DIntrinsics)));
  258. }
  259. DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
  260. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) override {
  261. return DoBasicQueryInterface<IDxcIntrinsicTable>(this, iid, ppvObject);
  262. }
  263. HRESULT STDMETHODCALLTYPE
  264. GetTableName(_Outptr_ LPCSTR *pTableName) override {
  265. *pTableName = "test";
  266. return S_OK;
  267. }
  268. HRESULT STDMETHODCALLTYPE LookupIntrinsic(
  269. LPCWSTR typeName, LPCWSTR functionName, const HLSL_INTRINSIC **pIntrinsic,
  270. _Inout_ UINT64 *pLookupCookie) override {
  271. if (typeName == nullptr)
  272. return E_FAIL;
  273. // Search for matching intrinsic name in matching namespace.
  274. IntrinsicTable::SearchResult result;
  275. for (const IntrinsicTable &table : m_tables) {
  276. if (table.MatchesNamespace(typeName)) {
  277. result = table.Search(functionName, *pLookupCookie);
  278. break;
  279. }
  280. }
  281. if (result) {
  282. *pIntrinsic = &result.intrinsic->hlsl;
  283. *pLookupCookie = result.index + 1;
  284. }
  285. else {
  286. *pIntrinsic = nullptr;
  287. *pLookupCookie = 0;
  288. }
  289. return result.intrinsic ? S_OK : E_FAIL;
  290. }
  291. HRESULT STDMETHODCALLTYPE
  292. GetLoweringStrategy(UINT opcode, _Outptr_ LPCSTR *pStrategy) override {
  293. Intrinsic *intrinsic = FindByOpcode(opcode);
  294. if (!intrinsic)
  295. return E_FAIL;
  296. *pStrategy = intrinsic->strategy;
  297. return S_OK;
  298. }
  299. HRESULT STDMETHODCALLTYPE
  300. GetIntrinsicName(UINT opcode, _Outptr_ LPCSTR *pName) override {
  301. Intrinsic *intrinsic = FindByOpcode(opcode);
  302. if (!intrinsic)
  303. return E_FAIL;
  304. *pName = intrinsic->dxilName;
  305. return S_OK;
  306. }
  307. HRESULT STDMETHODCALLTYPE
  308. GetDxilOpCode(UINT opcode, _Outptr_ UINT *pDxilOpcode) override {
  309. if (opcode == 13) {
  310. *pDxilOpcode = static_cast<UINT>(hlsl::OP::OpCode::IsInf);
  311. return S_OK;
  312. }
  313. else if (opcode == 14) {
  314. *pDxilOpcode = static_cast<UINT>(hlsl::OP::OpCode::Ibfe);
  315. return S_OK;
  316. }
  317. return E_FAIL;
  318. }
  319. Intrinsic *FindByOpcode(UINT opcode) {
  320. IntrinsicTable::SearchResult result;
  321. for (const IntrinsicTable &table : m_tables) {
  322. result = table.Search(opcode);
  323. if (result)
  324. break;
  325. }
  326. return result.intrinsic;
  327. }
  328. };
  329. // A class to test semantic define validation.
  330. // It takes a list of defines that when present should cause errors
  331. // and defines that should cause warnings. A more realistic validator
  332. // would look at the values and make sure (for example) they are
  333. // the correct type (integer, string, etc).
  334. class TestSemanticDefineValidator : public IDxcSemanticDefineValidator {
  335. private:
  336. DXC_MICROCOM_REF_FIELD(m_dwRef)
  337. std::vector<std::string> m_errorDefines;
  338. std::vector<std::string> m_warningDefines;
  339. public:
  340. TestSemanticDefineValidator(const std::vector<std::string> &errorDefines, const std::vector<std::string> &warningDefines)
  341. : m_dwRef(0)
  342. , m_errorDefines(errorDefines)
  343. , m_warningDefines(warningDefines)
  344. { }
  345. DXC_MICROCOM_ADDREF_RELEASE_IMPL(m_dwRef)
  346. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject) override {
  347. return DoBasicQueryInterface<IDxcSemanticDefineValidator>(this, iid, ppvObject);
  348. }
  349. virtual HRESULT STDMETHODCALLTYPE GetSemanticDefineWarningsAndErrors(LPCSTR pName, LPCSTR pValue, IDxcBlobEncoding **ppWarningBlob, IDxcBlobEncoding **ppErrorBlob) override {
  350. if (!pName || !pValue || !ppWarningBlob || !ppErrorBlob)
  351. return E_FAIL;
  352. auto Check = [pName](const std::vector<std::string> &errors, IDxcBlobEncoding **blob) {
  353. if (std::find(errors.begin(), errors.end(), pName) != errors.end()) {
  354. dxc::DxcDllSupport dllSupport;
  355. VERIFY_SUCCEEDED(dllSupport.Initialize());
  356. std::string error("bad define: ");
  357. error.append(pName);
  358. Utf8ToBlob(dllSupport, error.c_str(), blob);
  359. }
  360. };
  361. Check(m_errorDefines, ppErrorBlob);
  362. Check(m_warningDefines, ppWarningBlob);
  363. return S_OK;
  364. }
  365. };
  366. static void CheckOperationFailed(IDxcOperationResult *pResult) {
  367. HRESULT status;
  368. VERIFY_SUCCEEDED(pResult->GetStatus(&status));
  369. VERIFY_FAILED(status);
  370. }
  371. static std::string GetCompileErrors(IDxcOperationResult *pResult) {
  372. CComPtr<IDxcBlobEncoding> pErrors;
  373. VERIFY_SUCCEEDED(pResult->GetErrorBuffer(&pErrors));
  374. if (!pErrors)
  375. return "";
  376. return BlobToUtf8(pErrors);
  377. }
  378. class Compiler {
  379. public:
  380. Compiler(dxc::DxcDllSupport &dll) : m_dllSupport(dll) {
  381. VERIFY_SUCCEEDED(m_dllSupport.Initialize());
  382. VERIFY_SUCCEEDED(m_dllSupport.CreateInstance(CLSID_DxcCompiler, &pCompiler));
  383. VERIFY_SUCCEEDED(pCompiler.QueryInterface(&pLangExtensions));
  384. }
  385. void RegisterSemanticDefine(LPCWSTR define) {
  386. VERIFY_SUCCEEDED(pLangExtensions->RegisterSemanticDefine(define));
  387. }
  388. void RegisterSemanticDefineExclusion(LPCWSTR define) {
  389. VERIFY_SUCCEEDED(pLangExtensions->RegisterSemanticDefineExclusion(define));
  390. }
  391. void SetSemanticDefineValidator(IDxcSemanticDefineValidator *validator) {
  392. pTestSemanticDefineValidator = validator;
  393. VERIFY_SUCCEEDED(pLangExtensions->SetSemanticDefineValidator(pTestSemanticDefineValidator));
  394. }
  395. void SetSemanticDefineMetaDataName(const char *name) {
  396. VERIFY_SUCCEEDED(pLangExtensions->SetSemanticDefineMetaDataName("test.defs"));
  397. }
  398. void SetTargetTriple(const char *name) {
  399. VERIFY_SUCCEEDED(
  400. pLangExtensions->SetTargetTriple(name));
  401. }
  402. void RegisterIntrinsicTable(IDxcIntrinsicTable *table) {
  403. pTestIntrinsicTable = table;
  404. VERIFY_SUCCEEDED(pLangExtensions->RegisterIntrinsicTable(pTestIntrinsicTable));
  405. }
  406. IDxcOperationResult *Compile(const char *program) {
  407. return Compile(program, {}, {});
  408. }
  409. IDxcOperationResult *Compile(const char *program, const std::vector<LPCWSTR> &arguments, const std::vector<DxcDefine> defs ) {
  410. Utf8ToBlob(m_dllSupport, program, &pCodeBlob);
  411. VERIFY_SUCCEEDED(pCompiler->Compile(pCodeBlob, L"hlsl.hlsl", L"main",
  412. L"ps_6_0",
  413. const_cast<LPCWSTR *>(arguments.data()), arguments.size(),
  414. defs.data(), defs.size(),
  415. nullptr, &pCompileResult));
  416. return pCompileResult;
  417. }
  418. std::string Disassemble() {
  419. CComPtr<IDxcBlob> pBlob;
  420. CheckOperationSucceeded(pCompileResult, &pBlob);
  421. return DisassembleProgram(m_dllSupport, pBlob);
  422. }
  423. dxc::DxcDllSupport &m_dllSupport;
  424. CComPtr<IDxcCompiler> pCompiler;
  425. CComPtr<IDxcLangExtensions2> pLangExtensions;
  426. CComPtr<IDxcBlobEncoding> pCodeBlob;
  427. CComPtr<IDxcOperationResult> pCompileResult;
  428. CComPtr<IDxcSemanticDefineValidator> pTestSemanticDefineValidator;
  429. CComPtr<IDxcIntrinsicTable> pTestIntrinsicTable;
  430. };
  431. ///////////////////////////////////////////////////////////////////////////////
  432. // Extension unit tests.
  433. #ifdef _WIN32
  434. class ExtensionTest {
  435. #else
  436. class ExtensionTest : public ::testing::Test {
  437. #endif
  438. public:
  439. BEGIN_TEST_CLASS(ExtensionTest)
  440. TEST_CLASS_PROPERTY(L"Parallel", L"true")
  441. TEST_METHOD_PROPERTY(L"Priority", L"0")
  442. END_TEST_CLASS()
  443. dxc::DxcDllSupport m_dllSupport;
  444. TEST_METHOD(DefineWhenRegisteredThenPreserved)
  445. TEST_METHOD(DefineValidationError)
  446. TEST_METHOD(DefineValidationWarning)
  447. TEST_METHOD(DefineNoValidatorOk)
  448. TEST_METHOD(DefineFromMacro)
  449. TEST_METHOD(DefineContradictionFail)
  450. TEST_METHOD(OptionFromDefineGVN)
  451. TEST_METHOD(OptionFromDefineStructurizeReturns)
  452. TEST_METHOD(TargetTriple)
  453. TEST_METHOD(IntrinsicWhenAvailableThenUsed)
  454. TEST_METHOD(CustomIntrinsicName)
  455. TEST_METHOD(NoLowering)
  456. TEST_METHOD(PackedLowering)
  457. TEST_METHOD(ReplicateLoweringWhenOnlyVectorIsResult)
  458. TEST_METHOD(UnsignedOpcodeIsUnchanged)
  459. TEST_METHOD(ResourceExtensionIntrinsic)
  460. TEST_METHOD(NameLoweredWhenNoReplicationNeeded)
  461. TEST_METHOD(DxilLoweringVector1)
  462. TEST_METHOD(DxilLoweringVector2)
  463. TEST_METHOD(DxilLoweringScalar)
  464. TEST_METHOD(SamplerExtensionIntrinsic)
  465. TEST_METHOD(WaveIntrinsic)
  466. TEST_METHOD(ResourceExtensionIntrinsicCustomLowering1)
  467. TEST_METHOD(ResourceExtensionIntrinsicCustomLowering2)
  468. TEST_METHOD(ResourceExtensionIntrinsicCustomLowering3)
  469. TEST_METHOD(CustomOverloadArg1)
  470. };
  471. TEST_F(ExtensionTest, DefineWhenRegisteredThenPreserved) {
  472. Compiler c(m_dllSupport);
  473. c.RegisterSemanticDefine(L"FOO*");
  474. c.RegisterSemanticDefineExclusion(L"FOOBAR");
  475. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({ "FOOLALA" }, {}));
  476. c.SetSemanticDefineMetaDataName("test.defs");
  477. c.Compile(
  478. "#define FOOTBALL AWESOME\n"
  479. "#define FOOTLOOSE TOO\n"
  480. "#define FOOBAR 123\n"
  481. "#define FOOD\n"
  482. "#define FOO 1 2 3\n"
  483. "float4 main() : SV_Target {\n"
  484. " return 0;\n"
  485. "}\n",
  486. {L"/Vd"},
  487. { { L"FOODEF", L"1"} }
  488. );
  489. std::string disassembly = c.Disassemble();
  490. // Check for root named md node. It contains pointers to md nodes for each define.
  491. VERIFY_IS_TRUE(
  492. disassembly.npos !=
  493. disassembly.find("!test.defs"));
  494. // #define FOODEF 1
  495. VERIFY_IS_TRUE(
  496. disassembly.npos !=
  497. disassembly.find("!{!\"FOODEF\", !\"1\"}"));
  498. // #define FOOTBALL AWESOME
  499. VERIFY_IS_TRUE(
  500. disassembly.npos !=
  501. disassembly.find("!{!\"FOOTBALL\", !\"AWESOME\"}"));
  502. // #define FOOTLOOSE TOO
  503. VERIFY_IS_TRUE(
  504. disassembly.npos !=
  505. disassembly.find("!{!\"FOOTLOOSE\", !\"TOO\"}"));
  506. // #define FOOD
  507. VERIFY_IS_TRUE(
  508. disassembly.npos !=
  509. disassembly.find("!{!\"FOOD\", !\"\"}"));
  510. // #define FOO 1 2 3
  511. VERIFY_IS_TRUE(
  512. disassembly.npos !=
  513. disassembly.find("!{!\"FOO\", !\"1 2 3\"}"));
  514. // FOOBAR should be excluded.
  515. VERIFY_IS_TRUE(
  516. disassembly.npos ==
  517. disassembly.find("!{!\"FOOBAR\""));
  518. }
  519. TEST_F(ExtensionTest, DefineValidationError) {
  520. Compiler c(m_dllSupport);
  521. c.RegisterSemanticDefine(L"FOO*");
  522. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({ "FOO" }, {}));
  523. IDxcOperationResult *pCompileResult = c.Compile(
  524. "#define FOO 1\n"
  525. "float4 main() : SV_Target {\n"
  526. " return 0;\n"
  527. "}\n",
  528. {L"/Vd"}, {}
  529. );
  530. // Check that validation error causes compile failure.
  531. CheckOperationFailed(pCompileResult);
  532. std::string errors = GetCompileErrors(pCompileResult);
  533. // Check that the error message is for the validation failure.
  534. VERIFY_IS_TRUE(
  535. errors.npos !=
  536. errors.find("hlsl.hlsl:1:9: error: bad define: FOO"));
  537. }
  538. TEST_F(ExtensionTest, DefineValidationWarning) {
  539. Compiler c(m_dllSupport);
  540. c.RegisterSemanticDefine(L"FOO*");
  541. c.SetSemanticDefineValidator(new TestSemanticDefineValidator({}, { "FOO" }));
  542. IDxcOperationResult *pCompileResult = c.Compile(
  543. "#define FOO 1\n"
  544. "float4 main() : SV_Target {\n"
  545. " return 0;\n"
  546. "}\n",
  547. { L"/Vd" }, {}
  548. );
  549. std::string errors = GetCompileErrors(pCompileResult);
  550. // Check that the error message is for the validation failure.
  551. VERIFY_IS_TRUE(
  552. errors.npos !=
  553. errors.find("hlsl.hlsl:1:9: warning: bad define: FOO"));
  554. // Check the define is still emitted.
  555. std::string disassembly = c.Disassemble();
  556. // Check for root named md node. It contains pointers to md nodes for each define.
  557. VERIFY_IS_TRUE(
  558. disassembly.npos !=
  559. disassembly.find("!hlsl.semdefs"));
  560. // #define FOO 1
  561. VERIFY_IS_TRUE(
  562. disassembly.npos !=
  563. disassembly.find("!{!\"FOO\", !\"1\"}"));
  564. }
  565. TEST_F(ExtensionTest, DefineNoValidatorOk) {
  566. Compiler c(m_dllSupport);
  567. c.RegisterSemanticDefine(L"FOO*");
  568. c.Compile(
  569. "#define FOO 1\n"
  570. "float4 main() : SV_Target {\n"
  571. " return 0;\n"
  572. "}\n",
  573. { L"/Vd" }, {}
  574. );
  575. std::string disassembly = c.Disassemble();
  576. // Check the define is emitted.
  577. // #define FOO 1
  578. VERIFY_IS_TRUE(
  579. disassembly.npos !=
  580. disassembly.find("!{!\"FOO\", !\"1\"}"));
  581. }
  582. TEST_F(ExtensionTest, DefineFromMacro) {
  583. Compiler c(m_dllSupport);
  584. c.RegisterSemanticDefine(L"FOO*");
  585. c.Compile(
  586. "#define BAR 1\n"
  587. "#define FOO BAR\n"
  588. "float4 main() : SV_Target {\n"
  589. " return 0;\n"
  590. "}\n",
  591. { L"/Vd" }, {}
  592. );
  593. std::string disassembly = c.Disassemble();
  594. // Check the define is emitted.
  595. // #define FOO 1
  596. VERIFY_IS_TRUE(
  597. disassembly.npos !=
  598. disassembly.find("!{!\"FOO\", !\"1\"}"));
  599. }
  600. // Test failure of contradictory optimization toggles
  601. TEST_F(ExtensionTest, DefineContradictionFail) {
  602. Compiler c(m_dllSupport);
  603. c.RegisterSemanticDefine(L"FOO*");
  604. IDxcOperationResult *pCompileResult = c.Compile(
  605. "#define FOO 1\n"
  606. "float4 main() : SV_Target {\n"
  607. " return 0;\n"
  608. "}\n",
  609. { L"/Vd", L"-opt-disable", L"whatever",
  610. L"-opt-enable", L"whatever" },
  611. {}
  612. );
  613. CheckOperationFailed(pCompileResult);
  614. std::string errors = GetCompileErrors(pCompileResult);
  615. // Check that the error message is for the option contradiction
  616. VERIFY_IS_TRUE(
  617. errors.npos !=
  618. errors.find("Contradictory use of -opt-disable and -opt-enable with \"whatever\""));
  619. Compiler c2(m_dllSupport);
  620. c2.RegisterSemanticDefine(L"FOO*");
  621. pCompileResult = c2.Compile(
  622. "#define FOO 1\n"
  623. "float4 main() : SV_Target {\n"
  624. " return 0;\n"
  625. "}\n",
  626. { L"/Vd", L"-opt-select", L"yook", L"butterUP",
  627. L"-opt-select", L"yook", L"butterdown" },
  628. {}
  629. );
  630. CheckOperationFailed(pCompileResult);
  631. errors = GetCompileErrors(pCompileResult);
  632. // Check that the error message is for the option contradiction
  633. VERIFY_IS_TRUE(
  634. errors.npos !=
  635. errors.find("Contradictory -opt-selects for \"yook\""));
  636. }
  637. // Test setting of codegen options from semantic defines
  638. TEST_F(ExtensionTest, OptionFromDefineGVN) {
  639. Compiler c(m_dllSupport);
  640. c.RegisterSemanticDefine(L"FOO*");
  641. c.Compile(
  642. "float4 main(float a : A) : SV_Target {\n"
  643. " float res = sin(a);\n"
  644. " return res + sin(a);\n"
  645. "}\n",
  646. { L"/Vd", L"-DFOO_DISABLE_GVN" },
  647. {}
  648. );
  649. std::string disassembly = c.Disassemble();
  650. // Verify that GVN is disabled by the presence
  651. // of the second sin(), which GVN would have removed
  652. llvm::Regex regex("call float @dx.op.unary.f32.*\n.*call float @dx.op.unary.f32");
  653. std::string regexErrors;
  654. VERIFY_IS_TRUE(regex.isValid(regexErrors));
  655. VERIFY_IS_TRUE(regex.match(disassembly));
  656. }
  657. // Test setting of codegen options from semantic defines
  658. TEST_F(ExtensionTest, OptionFromDefineStructurizeReturns) {
  659. Compiler c(m_dllSupport);
  660. c.RegisterSemanticDefine(L"FOO*");
  661. c.Compile(
  662. "int i;\n"
  663. "float main(float4 a:A) : SV_Target {\n"
  664. "float c = 0;\n"
  665. "if (i < 0) {\n"
  666. " if (a.w > 2)\n"
  667. " return -1;\n"
  668. " c += a.z;\n"
  669. "}\n"
  670. "return c;\n"
  671. "}\n",
  672. { L"/Vd", L"-fcgl", L"-DFOO_ENABLE_STRUCTURIZE_RETURNS" },
  673. {}
  674. );
  675. std::string disassembly = c.Disassemble();
  676. // Verify that structurize returns is enabled by the presence
  677. // of the associated annotation. Just a simple test to
  678. // verify that it's on. No need to go into detail here
  679. llvm::Regex regex("bReturned.* = alloca i1");
  680. std::string regexErrors;
  681. VERIFY_IS_TRUE(regex.isValid(regexErrors));
  682. VERIFY_IS_TRUE(regex.match(disassembly));
  683. }
  684. TEST_F(ExtensionTest, TargetTriple) {
  685. Compiler c(m_dllSupport);
  686. c.SetTargetTriple("dxil-ms-win32");
  687. c.Compile("float4 main() : SV_Target {\n"
  688. " return 0;\n"
  689. "}\n",
  690. {L"/Vd"}, {});
  691. std::string disassembly = c.Disassemble();
  692. // Check the triple is updated.
  693. VERIFY_IS_TRUE(disassembly.npos != disassembly.find("dxil-ms-win32"));
  694. }
  695. TEST_F(ExtensionTest, IntrinsicWhenAvailableThenUsed) {
  696. Compiler c(m_dllSupport);
  697. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  698. c.Compile(
  699. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  700. " test_proc(v);\n"
  701. " float2 a = test_fn(v);\n"
  702. " int2 b = test_fn(i);\n"
  703. " return a + b;\n"
  704. "}\n",
  705. { L"/Vd" }, {}
  706. );
  707. std::string disassembly = c.Disassemble();
  708. // Things to call out:
  709. // - result is float, not a vector
  710. // - mangled name contains the 'test' and '.r' parts
  711. // - opcode is first i32 argument
  712. // - second argument is float, ie it got scalarized
  713. VERIFY_IS_TRUE(
  714. disassembly.npos !=
  715. disassembly.find("call void @\"test.\\01?test_proc@hlsl@@YAXV?$vector@M$01@@@Z.r\"(i32 2, float"));
  716. VERIFY_IS_TRUE(
  717. disassembly.npos !=
  718. disassembly.find("call float @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@M$01@@V2@@Z.r\"(i32 1, float"));
  719. VERIFY_IS_TRUE(
  720. disassembly.npos !=
  721. disassembly.find("call i32 @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@H$01@@V2@@Z.r\"(i32 1, i32"));
  722. // - attributes are added to the declaration (the # at the end of the decl)
  723. // TODO: would be nice to check for the actual attribute (e.g. readonly)
  724. VERIFY_IS_TRUE(
  725. disassembly.npos !=
  726. disassembly.find("declare float @\"test.\\01?test_fn@hlsl@@YA?AV?$vector@M$01@@V2@@Z.r\"(i32, float) #"));
  727. }
  728. TEST_F(ExtensionTest, CustomIntrinsicName) {
  729. Compiler c(m_dllSupport);
  730. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  731. c.Compile(
  732. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  733. " float2 a = test_poly(v);\n"
  734. " int2 b = test_poly(i);\n"
  735. " int2 c = test_int(i);\n"
  736. " return a + b + c;\n"
  737. "}\n",
  738. { L"/Vd" }, {}
  739. );
  740. std::string disassembly = c.Disassemble();
  741. // - custom name works for polymorphic function
  742. VERIFY_IS_TRUE(
  743. disassembly.npos !=
  744. disassembly.find("call float @test_poly.float(i32 3, float"));
  745. VERIFY_IS_TRUE(
  746. disassembly.npos !=
  747. disassembly.find("call i32 @test_poly.i32(i32 3, i32"));
  748. // - custom name works for non-polymorphic function
  749. VERIFY_IS_TRUE(
  750. disassembly.npos !=
  751. disassembly.find("call i32 @test_int(i32 4, i32"));
  752. }
  753. TEST_F(ExtensionTest, NoLowering) {
  754. Compiler c(m_dllSupport);
  755. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  756. c.Compile(
  757. "float2 main(float2 v : V, int2 i : I) : SV_Target {\n"
  758. " float2 a = test_nolower(v);\n"
  759. " float2 b = test_nolower(i);\n"
  760. " return a + b;\n"
  761. "}\n",
  762. { L"/Vd" }, {}
  763. );
  764. std::string disassembly = c.Disassemble();
  765. // - custom name works for non-lowered function
  766. // - non-lowered function has vector type as argument
  767. VERIFY_IS_TRUE(
  768. disassembly.npos !=
  769. disassembly.find("call <2 x float> @test_nolower.float(i32 5, <2 x float>"));
  770. VERIFY_IS_TRUE(
  771. disassembly.npos !=
  772. disassembly.find("call <2 x i32> @test_nolower.i32(i32 5, <2 x i32>"));
  773. }
  774. TEST_F(ExtensionTest, PackedLowering) {
  775. Compiler c(m_dllSupport);
  776. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  777. c.Compile(
  778. "float2 main(float2 v1 : V1, float2 v2 : V2, float3 v3 : V3) : SV_Target {\n"
  779. " test_pack_0(v1);\n"
  780. " int2 a = test_pack_1();\n"
  781. " float2 b = test_pack_2(v1, v2);\n"
  782. " float c = test_pack_3(v1);\n"
  783. " float2 d = test_pack_4(v3);\n"
  784. " return a + b + float2(c, c);\n"
  785. "}\n",
  786. { L"/Vd" }, {}
  787. );
  788. std::string disassembly = c.Disassemble();
  789. // - pack strategy changes vectors into structs
  790. VERIFY_IS_TRUE(
  791. disassembly.npos !=
  792. disassembly.find("call void @test_pack_0.float(i32 6, { float, float }"));
  793. VERIFY_IS_TRUE(
  794. disassembly.npos !=
  795. disassembly.find("call { float, float } @test_pack_1.float(i32 7)"));
  796. VERIFY_IS_TRUE(
  797. disassembly.npos !=
  798. disassembly.find("call { float, float } @test_pack_2.float(i32 8, { float, float }"));
  799. VERIFY_IS_TRUE(
  800. disassembly.npos !=
  801. disassembly.find("call float @test_pack_3.float(i32 9, { float, float }"));
  802. VERIFY_IS_TRUE(
  803. disassembly.npos !=
  804. disassembly.find("call { float, float } @test_pack_4.float(i32 10, { float, float, float }"));
  805. }
  806. TEST_F(ExtensionTest, ReplicateLoweringWhenOnlyVectorIsResult) {
  807. Compiler c(m_dllSupport);
  808. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  809. c.Compile(
  810. "float2 main(float2 v1 : V1, float2 v2 : V2, float3 v3 : V3) : SV_Target {\n"
  811. " return test_rand();\n"
  812. "}\n",
  813. { L"/Vd" }, {}
  814. );
  815. std::string disassembly = c.Disassemble();
  816. // - replicate strategy works for vector results
  817. VERIFY_IS_TRUE(
  818. disassembly.npos !=
  819. disassembly.find("call float @test_rand(i32 11)"));
  820. }
  821. TEST_F(ExtensionTest, UnsignedOpcodeIsUnchanged) {
  822. Compiler c(m_dllSupport);
  823. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  824. c.Compile(
  825. "uint main(uint v1 : V1) : SV_Target {\n"
  826. " return test_unsigned(v1);\n"
  827. "}\n",
  828. { L"/Vd" }, {}
  829. );
  830. std::string disassembly = c.Disassemble();
  831. // - opcode is unchanged when it matches an hlsl intrinsic with
  832. // an unsigned version.
  833. // This should use the same value as IOP_min.
  834. std::string matchStr;
  835. std::ostringstream ss(matchStr);
  836. ss << "call i32 @test_unsigned(i32 "
  837. << (unsigned)hlsl::IntrinsicOp::IOP_min
  838. << ", ";
  839. VERIFY_IS_TRUE(
  840. disassembly.npos !=
  841. disassembly.find(ss.str()));
  842. }
  843. TEST_F(ExtensionTest, ResourceExtensionIntrinsic) {
  844. Compiler c(m_dllSupport);
  845. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  846. c.Compile(
  847. "Buffer<float2> buf;"
  848. "float2 main(uint2 v1 : V1) : SV_Target {\n"
  849. " return buf.MyBufferOp(uint2(1, 2));\n"
  850. "}\n",
  851. { L"/Vd" }, {}
  852. );
  853. std::string disassembly = c.Disassemble();
  854. // Things to check
  855. // - return type is translated to dx.types.ResRet
  856. // - buffer is translated to dx.types.Handle
  857. // - vector is exploded
  858. llvm::Regex regex("call %dx.types.ResRet.f32 @MyBufferOp\\(i32 12, %dx.types.Handle %.*, i32 1, i32 2\\)");
  859. std::string regexErrors;
  860. VERIFY_IS_TRUE(regex.isValid(regexErrors));
  861. VERIFY_IS_TRUE(regex.match(disassembly));
  862. }
  863. TEST_F(ExtensionTest, NameLoweredWhenNoReplicationNeeded) {
  864. Compiler c(m_dllSupport);
  865. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  866. c.Compile(
  867. "int main(int v1 : V1) : SV_Target {\n"
  868. " return test_int(v1);\n"
  869. "}\n",
  870. { L"/Vd" }, {}
  871. );
  872. std::string disassembly = c.Disassemble();
  873. // Make sure the name is still lowered even when no replication
  874. // is needed because a non-vector overload of the function
  875. // was used.
  876. VERIFY_IS_TRUE(
  877. disassembly.npos !=
  878. disassembly.find("call i32 @test_int("));
  879. }
  880. TEST_F(ExtensionTest, DxilLoweringVector1) {
  881. Compiler c(m_dllSupport);
  882. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  883. c.Compile(
  884. "int main(float v1 : V1) : SV_Target {\n"
  885. " return test_isinf(v1);\n"
  886. "}\n",
  887. { L"/Vd" }, {}
  888. );
  889. std::string disassembly = c.Disassemble();
  890. // Check that the extension was lowered to the correct dxil intrinsic.
  891. static_assert(9 == (unsigned)hlsl::OP::OpCode::IsInf, "isinf opcode changed?");
  892. VERIFY_IS_TRUE(
  893. disassembly.npos !=
  894. disassembly.find("call i1 @dx.op.isSpecialFloat.f32(i32 9"));
  895. }
  896. TEST_F(ExtensionTest, DxilLoweringVector2) {
  897. Compiler c(m_dllSupport);
  898. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  899. c.Compile(
  900. "int2 main(float2 v1 : V1) : SV_Target {\n"
  901. " return test_isinf(v1);\n"
  902. "}\n",
  903. { L"/Vd" }, {}
  904. );
  905. std::string disassembly = c.Disassemble();
  906. // Check that the extension was lowered to the correct dxil intrinsic.
  907. static_assert(9 == (unsigned)hlsl::OP::OpCode::IsInf, "isinf opcode changed?");
  908. VERIFY_IS_TRUE(
  909. disassembly.npos !=
  910. disassembly.find("call i1 @dx.op.isSpecialFloat.f32(i32 9"));
  911. }
  912. TEST_F(ExtensionTest, DxilLoweringScalar) {
  913. Compiler c(m_dllSupport);
  914. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  915. c.Compile(
  916. "int main(uint v1 : V1, uint v2 : V2, uint v3 : V3) : SV_Target {\n"
  917. " return test_ibfe(v1, v2, v3);\n"
  918. "}\n",
  919. { L"/Vd" }, {}
  920. );
  921. std::string disassembly = c.Disassemble();
  922. // Check that the extension was lowered to the correct dxil intrinsic.
  923. static_assert(51 == (unsigned)hlsl::OP::OpCode::Ibfe, "ibfe opcode changed?");
  924. VERIFY_IS_TRUE(
  925. disassembly.npos !=
  926. disassembly.find("call i32 @dx.op.tertiary.i32(i32 51"));
  927. }
  928. TEST_F(ExtensionTest, SamplerExtensionIntrinsic) {
  929. // Test adding methods to objects that don't have any methods normally,
  930. // and therefore have null default intrinsic table.
  931. Compiler c(m_dllSupport);
  932. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  933. auto result = c.Compile(
  934. "SamplerState samp;"
  935. "float2 main(uint2 v1 : V1) : SV_Target {\n"
  936. " return samp.MySamplerOp(uint2(1, 2));\n"
  937. "}\n",
  938. { L"/Vd" }, {}
  939. );
  940. CheckOperationResultMsgs(result, {}, true, false);
  941. std::string disassembly = c.Disassemble();
  942. // Things to check
  943. // - works when SamplerState normally has no methods
  944. // - return type is translated to dx.types.ResRet
  945. // - buffer is translated to dx.types.Handle
  946. // - vector is exploded
  947. LPCSTR expected[] = {
  948. "call %dx.types.ResRet.f32 @MySamplerOp\\(i32 15, %dx.types.Handle %.*, i32 1, i32 2\\)"
  949. };
  950. CheckMsgs(disassembly.c_str(), disassembly.length(), expected, 1, true);
  951. }
  952. TEST_F(ExtensionTest, WaveIntrinsic) {
  953. // Test wave-sensitive intrinsic in breaked loop
  954. Compiler c(m_dllSupport);
  955. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  956. c.Compile(
  957. "StructuredBuffer<int> buf[]: register(t2);"
  958. "float2 main(float2 a : A, int b : B) : SV_Target {"
  959. " int res = 0;"
  960. " float2 u = {0,0};"
  961. " for (;;) {"
  962. " u += wave_proc(a);"
  963. " if (a.x == u.x) {"
  964. " res += buf[b][(int)u.y];"
  965. " break;"
  966. " }"
  967. " }"
  968. " return res;"
  969. "}",
  970. { L"/Vd" }, {}
  971. );
  972. std::string disassembly = c.Disassemble();
  973. // Check that the wave op causes the break block to be retained
  974. VERIFY_IS_TRUE(
  975. disassembly.npos !=
  976. disassembly.find("@dx.break.cond = internal constant [1 x i32] zeroinitializer"));
  977. VERIFY_IS_TRUE(
  978. disassembly.npos !=
  979. disassembly.find("%1 = load i32, i32* getelementptr inbounds ([1 x i32], [1 x i32]* @dx.break.cond"));
  980. VERIFY_IS_TRUE(
  981. disassembly.npos !=
  982. disassembly.find("%2 = icmp eq i32 %1, 0"));
  983. VERIFY_IS_TRUE(
  984. disassembly.npos !=
  985. disassembly.find("call float @\"test.\\01?wave_proc@hlsl@@YA?AV?$vector@M$01@@V2@@Z.r\"(i32 16, float"));
  986. VERIFY_IS_TRUE(
  987. disassembly.npos !=
  988. disassembly.find("br i1 %2"));
  989. }
  990. TEST_F(ExtensionTest, ResourceExtensionIntrinsicCustomLowering1) {
  991. // Test adding methods to objects that don't have any methods normally,
  992. // and therefore have null default intrinsic table.
  993. Compiler c(m_dllSupport);
  994. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  995. auto result = c.Compile(
  996. "Texture1D tex1;"
  997. "float2 main() : SV_Target {\n"
  998. " return tex1.MyTextureOp(1,2,3);\n"
  999. "}\n",
  1000. { L"/Vd" }, {}
  1001. );
  1002. CheckOperationResultMsgs(result, {}, true, false);
  1003. std::string disassembly = c.Disassemble();
  1004. // Things to check
  1005. // @MyTextureOp(i32 opcode, %dx.types.Handle, i32 addr0, i32 addr1, i32 offset, i32 val0, i32 val1);
  1006. //
  1007. // hlsl: Texture1D.MyTextureOp(a, b, c)
  1008. // dxil: @MyTextureOp(17, handle, a, undef, b, c, undef)
  1009. //
  1010. LPCSTR expected[] = {
  1011. "call %dx.types.ResRet.i32 @MyTextureOp\\(i32 17, %dx.types.Handle %.*, i32 1, i32 undef, i32 2, i32 3, i32 undef\\)",
  1012. };
  1013. CheckMsgs(disassembly.c_str(), disassembly.length(), expected, 1, true);
  1014. }
  1015. TEST_F(ExtensionTest, ResourceExtensionIntrinsicCustomLowering2) {
  1016. // Test adding methods to objects that don't have any methods normally,
  1017. // and therefore have null default intrinsic table.
  1018. Compiler c(m_dllSupport);
  1019. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  1020. auto result = c.Compile(
  1021. "Texture2D tex2;"
  1022. "float2 main() : SV_Target {\n"
  1023. " return tex2.MyTextureOp(uint2(4,5), uint2(6,7));\n"
  1024. "}\n",
  1025. { L"/Vd" }, {}
  1026. );
  1027. CheckOperationResultMsgs(result, {}, true, false);
  1028. std::string disassembly = c.Disassemble();
  1029. // Things to check
  1030. // @MyTextureOp(i32 opcode, %dx.types.Handle, i32 addr0, i32 addr1, i32 offset, i32 val0, i32 val1);
  1031. //
  1032. // hlsl: Texture2D.MyTextureOp(a, c)
  1033. // dxil: @MyTextureOp(17, handle, a.x, a.y, undef, c.x, c.y)
  1034. LPCSTR expected[] = {
  1035. "call %dx.types.ResRet.i32 @MyTextureOp\\(i32 17, %dx.types.Handle %.*, i32 4, i32 5, i32 undef, i32 6, i32 7\\)",
  1036. };
  1037. CheckMsgs(disassembly.c_str(), disassembly.length(), expected, 1, true);
  1038. }
  1039. TEST_F(ExtensionTest, ResourceExtensionIntrinsicCustomLowering3) {
  1040. // Test adding methods to objects that don't have any methods normally,
  1041. // and therefore have null default intrinsic table.
  1042. Compiler c(m_dllSupport);
  1043. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  1044. auto result = c.Compile(
  1045. "Texture1D tex1;"
  1046. "float2 main() : SV_Target {\n"
  1047. " return tex1.MyTextureOp(1,2);\n"
  1048. "}\n",
  1049. { L"/Vd" }, {}
  1050. );
  1051. CheckOperationResultMsgs(result, {}, true, false);
  1052. std::string disassembly = c.Disassemble();
  1053. // Things to check
  1054. // @MyTextureOp(i32 opcode, %dx.types.Handle, i32 addr0, i32 addr1, i32 offset, i32 val0, i32 val1);
  1055. //
  1056. // hlsl: Texture1D.MyTextureOp(a, b)
  1057. // dxil: @MyTextureOp(17, handle, a, undef, b, undef, undef)
  1058. //
  1059. LPCSTR expected[] = {
  1060. "call %dx.types.ResRet.i32 @MyTextureOp\\(i32 17, %dx.types.Handle %.*, i32 1, i32 undef, i32 2, i32 undef, i32 undef\\)",
  1061. };
  1062. CheckMsgs(disassembly.c_str(), disassembly.length(), expected, 1, true);
  1063. }
  1064. TEST_F(ExtensionTest, CustomOverloadArg1) {
  1065. // Test that we pick the overload name based on the first arg.
  1066. Compiler c(m_dllSupport);
  1067. c.RegisterIntrinsicTable(new TestIntrinsicTable());
  1068. auto result = c.Compile(
  1069. "float main() : SV_Target {\n"
  1070. " float o1 = test_o_1(1.0f, 2u, 4.0);\n"
  1071. " float o2 = test_o_2(1.0f, 2u, 4.0);\n"
  1072. " float o3 = test_o_3(1.0f, 2u, 4.0);\n"
  1073. " return o1 + o2 + o3;\n"
  1074. "}\n",
  1075. { L"/Vd" }, {}
  1076. );
  1077. CheckOperationResultMsgs(result, {}, true, false);
  1078. std::string disassembly = c.Disassemble();
  1079. // The function name should match the first arg (float)
  1080. LPCSTR expected[] = {
  1081. "call float @test_o_1.float(i32 18, float 1.000000e+00, i32 2, double 4.000000e+00)",
  1082. "call float @test_o_2.i32(i32 18, float 1.000000e+00, i32 2, double 4.000000e+00)",
  1083. "call float @test_o_3.double(i32 18, float 1.000000e+00, i32 2, double 4.000000e+00)",
  1084. };
  1085. CheckMsgs(disassembly.c_str(), disassembly.length(), expected, 1, false);
  1086. }