ExtensionTest.cpp 35 KB

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