cpp_interface_test.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. // Copyright (c) 2016 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <string>
  15. #include <utility>
  16. #include <vector>
  17. #include "gmock/gmock.h"
  18. #include "gtest/gtest.h"
  19. #include "spirv-tools/optimizer.hpp"
  20. #include "spirv/1.1/spirv.h"
  21. namespace spvtools {
  22. namespace {
  23. using ::testing::ContainerEq;
  24. using ::testing::HasSubstr;
  25. // Return a string that contains the minimum instructions needed to form
  26. // a valid module. Other instructions can be appended to this string.
  27. std::string Header() {
  28. return R"(OpCapability Shader
  29. OpCapability Linkage
  30. OpMemoryModel Logical GLSL450
  31. )";
  32. }
  33. // When we assemble with a target environment of SPIR-V 1.1, we expect
  34. // the following in the module header version word.
  35. const uint32_t kExpectedSpvVersion = 0x10100;
  36. TEST(CppInterface, SuccessfulRoundTrip) {
  37. const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  38. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  39. std::vector<uint32_t> binary;
  40. EXPECT_TRUE(t.Assemble(input_text, &binary));
  41. EXPECT_TRUE(binary.size() > 5u);
  42. EXPECT_EQ(SpvMagicNumber, binary[0]);
  43. EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  44. // This cannot pass validation since %1 is not defined.
  45. t.SetMessageConsumer([](spv_message_level_t level, const char* source,
  46. const spv_position_t& position, const char* message) {
  47. EXPECT_EQ(SPV_MSG_ERROR, level);
  48. EXPECT_STREQ("input", source);
  49. EXPECT_EQ(0u, position.line);
  50. EXPECT_EQ(0u, position.column);
  51. EXPECT_EQ(1u, position.index);
  52. EXPECT_STREQ("ID 1[%1] has not been defined\n %2 = OpSizeOf %1 %3\n",
  53. message);
  54. });
  55. EXPECT_FALSE(t.Validate(binary));
  56. std::string output_text;
  57. EXPECT_TRUE(t.Disassemble(binary, &output_text));
  58. EXPECT_EQ(input_text, output_text);
  59. }
  60. TEST(CppInterface, AssembleEmptyModule) {
  61. std::vector<uint32_t> binary(10, 42);
  62. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  63. EXPECT_TRUE(t.Assemble("", &binary));
  64. // We only have the header.
  65. EXPECT_EQ(5u, binary.size());
  66. EXPECT_EQ(SpvMagicNumber, binary[0]);
  67. EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  68. }
  69. TEST(CppInterface, AssembleOverloads) {
  70. const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  71. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  72. {
  73. std::vector<uint32_t> binary;
  74. EXPECT_TRUE(t.Assemble(input_text, &binary));
  75. EXPECT_TRUE(binary.size() > 5u);
  76. EXPECT_EQ(SpvMagicNumber, binary[0]);
  77. EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  78. }
  79. {
  80. std::vector<uint32_t> binary;
  81. EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size(), &binary));
  82. EXPECT_TRUE(binary.size() > 5u);
  83. EXPECT_EQ(SpvMagicNumber, binary[0]);
  84. EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  85. }
  86. { // Ignore the last newline.
  87. std::vector<uint32_t> binary;
  88. EXPECT_TRUE(t.Assemble(input_text.data(), input_text.size() - 1, &binary));
  89. EXPECT_TRUE(binary.size() > 5u);
  90. EXPECT_EQ(SpvMagicNumber, binary[0]);
  91. EXPECT_EQ(kExpectedSpvVersion, binary[1]);
  92. }
  93. }
  94. TEST(CppInterface, DisassembleEmptyModule) {
  95. std::string text(10, 'x');
  96. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  97. int invocation_count = 0;
  98. t.SetMessageConsumer(
  99. [&invocation_count](spv_message_level_t level, const char* source,
  100. const spv_position_t& position, const char* message) {
  101. ++invocation_count;
  102. EXPECT_EQ(SPV_MSG_ERROR, level);
  103. EXPECT_STREQ("input", source);
  104. EXPECT_EQ(0u, position.line);
  105. EXPECT_EQ(0u, position.column);
  106. EXPECT_EQ(0u, position.index);
  107. EXPECT_STREQ("Missing module.", message);
  108. });
  109. EXPECT_FALSE(t.Disassemble({}, &text));
  110. EXPECT_EQ("xxxxxxxxxx", text); // The original string is unmodified.
  111. EXPECT_EQ(1, invocation_count);
  112. }
  113. TEST(CppInterface, DisassembleOverloads) {
  114. const std::string input_text = "%2 = OpSizeOf %1 %3\n";
  115. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  116. std::vector<uint32_t> binary;
  117. EXPECT_TRUE(t.Assemble(input_text, &binary));
  118. {
  119. std::string output_text;
  120. EXPECT_TRUE(t.Disassemble(binary, &output_text));
  121. EXPECT_EQ(input_text, output_text);
  122. }
  123. {
  124. std::string output_text;
  125. EXPECT_TRUE(t.Disassemble(binary.data(), binary.size(), &output_text));
  126. EXPECT_EQ(input_text, output_text);
  127. }
  128. }
  129. TEST(CppInterface, SuccessfulValidation) {
  130. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  131. int invocation_count = 0;
  132. t.SetMessageConsumer([&invocation_count](spv_message_level_t, const char*,
  133. const spv_position_t&, const char*) {
  134. ++invocation_count;
  135. });
  136. std::vector<uint32_t> binary;
  137. EXPECT_TRUE(t.Assemble(Header(), &binary));
  138. EXPECT_TRUE(t.Validate(binary));
  139. EXPECT_EQ(0, invocation_count);
  140. }
  141. TEST(CppInterface, ValidateOverloads) {
  142. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  143. std::vector<uint32_t> binary;
  144. EXPECT_TRUE(t.Assemble(Header(), &binary));
  145. { EXPECT_TRUE(t.Validate(binary)); }
  146. { EXPECT_TRUE(t.Validate(binary.data(), binary.size())); }
  147. }
  148. TEST(CppInterface, ValidateEmptyModule) {
  149. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  150. int invocation_count = 0;
  151. t.SetMessageConsumer(
  152. [&invocation_count](spv_message_level_t level, const char* source,
  153. const spv_position_t& position, const char* message) {
  154. ++invocation_count;
  155. EXPECT_EQ(SPV_MSG_ERROR, level);
  156. EXPECT_STREQ("input", source);
  157. EXPECT_EQ(0u, position.line);
  158. EXPECT_EQ(0u, position.column);
  159. EXPECT_EQ(0u, position.index);
  160. EXPECT_STREQ("Invalid SPIR-V magic number.", message);
  161. });
  162. EXPECT_FALSE(t.Validate({}));
  163. EXPECT_EQ(1, invocation_count);
  164. }
  165. // Returns the assembly for a SPIR-V module with a struct declaration
  166. // with the given number of members.
  167. std::string MakeModuleHavingStruct(int num_members) {
  168. std::stringstream os;
  169. os << Header();
  170. os << R"(%1 = OpTypeInt 32 0
  171. %2 = OpTypeStruct)";
  172. for (int i = 0; i < num_members; i++) os << " %1";
  173. return os.str();
  174. }
  175. TEST(CppInterface, ValidateWithOptionsPass) {
  176. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  177. std::vector<uint32_t> binary;
  178. EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
  179. const ValidatorOptions opts;
  180. EXPECT_TRUE(t.Validate(binary.data(), binary.size(), opts));
  181. }
  182. TEST(CppInterface, ValidateWithOptionsFail) {
  183. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  184. std::vector<uint32_t> binary;
  185. EXPECT_TRUE(t.Assemble(MakeModuleHavingStruct(10), &binary));
  186. ValidatorOptions opts;
  187. opts.SetUniversalLimit(spv_validator_limit_max_struct_members, 9);
  188. std::stringstream os;
  189. t.SetMessageConsumer([&os](spv_message_level_t, const char*,
  190. const spv_position_t&,
  191. const char* message) { os << message; });
  192. EXPECT_FALSE(t.Validate(binary.data(), binary.size(), opts));
  193. EXPECT_THAT(
  194. os.str(),
  195. HasSubstr(
  196. "Number of OpTypeStruct members (10) has exceeded the limit (9)"));
  197. }
  198. // Checks that after running the given optimizer |opt| on the given |original|
  199. // source code, we can get the given |optimized| source code.
  200. void CheckOptimization(const std::string& original,
  201. const std::string& optimized, const Optimizer& opt) {
  202. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  203. std::vector<uint32_t> original_binary;
  204. ASSERT_TRUE(t.Assemble(original, &original_binary));
  205. std::vector<uint32_t> optimized_binary;
  206. EXPECT_TRUE(opt.Run(original_binary.data(), original_binary.size(),
  207. &optimized_binary));
  208. std::string optimized_text;
  209. EXPECT_TRUE(t.Disassemble(optimized_binary, &optimized_text));
  210. EXPECT_EQ(optimized, optimized_text);
  211. }
  212. TEST(CppInterface, OptimizeEmptyModule) {
  213. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  214. std::vector<uint32_t> binary;
  215. EXPECT_TRUE(t.Assemble("", &binary));
  216. Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  217. o.RegisterPass(CreateStripDebugInfoPass());
  218. // Fails to validate.
  219. EXPECT_FALSE(o.Run(binary.data(), binary.size(), &binary));
  220. }
  221. TEST(CppInterface, OptimizeModifiedModule) {
  222. Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  223. o.RegisterPass(CreateStripDebugInfoPass());
  224. CheckOptimization(Header() + "OpSource GLSL 450", Header(), o);
  225. }
  226. TEST(CppInterface, OptimizeMulitplePasses) {
  227. std::string original_text = Header() +
  228. "OpSource GLSL 450 "
  229. "OpDecorate %true SpecId 1 "
  230. "%bool = OpTypeBool "
  231. "%true = OpSpecConstantTrue %bool";
  232. Optimizer o(SPV_ENV_UNIVERSAL_1_1);
  233. o.RegisterPass(CreateStripDebugInfoPass())
  234. .RegisterPass(CreateFreezeSpecConstantValuePass());
  235. std::string expected_text = Header() +
  236. "%bool = OpTypeBool\n"
  237. "%true = OpConstantTrue %bool\n";
  238. CheckOptimization(original_text, expected_text, o);
  239. }
  240. TEST(CppInterface, OptimizeDoNothingWithPassToken) {
  241. CreateFreezeSpecConstantValuePass();
  242. auto token = CreateUnifyConstantPass();
  243. }
  244. TEST(CppInterface, OptimizeReassignPassToken) {
  245. auto token = CreateNullPass();
  246. token = CreateStripDebugInfoPass();
  247. CheckOptimization(
  248. Header() + "OpSource GLSL 450", Header(),
  249. Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token)));
  250. }
  251. TEST(CppInterface, OptimizeMoveConstructPassToken) {
  252. auto token1 = CreateStripDebugInfoPass();
  253. Optimizer::PassToken token2(std::move(token1));
  254. CheckOptimization(
  255. Header() + "OpSource GLSL 450", Header(),
  256. Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
  257. }
  258. TEST(CppInterface, OptimizeMoveAssignPassToken) {
  259. auto token1 = CreateStripDebugInfoPass();
  260. auto token2 = CreateNullPass();
  261. token2 = std::move(token1);
  262. CheckOptimization(
  263. Header() + "OpSource GLSL 450", Header(),
  264. Optimizer(SPV_ENV_UNIVERSAL_1_1).RegisterPass(std::move(token2)));
  265. }
  266. TEST(CppInterface, OptimizeSameAddressForOriginalOptimizedBinary) {
  267. SpirvTools t(SPV_ENV_UNIVERSAL_1_1);
  268. std::vector<uint32_t> binary;
  269. ASSERT_TRUE(t.Assemble(Header() + "OpSource GLSL 450", &binary));
  270. EXPECT_TRUE(Optimizer(SPV_ENV_UNIVERSAL_1_1)
  271. .RegisterPass(CreateStripDebugInfoPass())
  272. .Run(binary.data(), binary.size(), &binary));
  273. std::string optimized_text;
  274. EXPECT_TRUE(t.Disassemble(binary, &optimized_text));
  275. EXPECT_EQ(Header(), optimized_text);
  276. }
  277. // TODO(antiagainst): tests for SetMessageConsumer().
  278. } // namespace
  279. } // namespace spvtools