ExtensionTest.cpp 28 KB

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