ExtensionTest.cpp 41 KB

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