val_webgpu_test.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // Copyright (c) 2018 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. // Validation tests for WebGPU env specific checks
  15. #include <string>
  16. #include "gmock/gmock.h"
  17. #include "test/val/val_fixtures.h"
  18. namespace spvtools {
  19. namespace val {
  20. namespace {
  21. using testing::HasSubstr;
  22. using ValidateWebGPU = spvtest::ValidateBase<bool>;
  23. TEST_F(ValidateWebGPU, OpUndefIsDisallowed) {
  24. std::string spirv = R"(
  25. OpCapability Shader
  26. OpCapability VulkanMemoryModelKHR
  27. OpExtension "SPV_KHR_vulkan_memory_model"
  28. OpMemoryModel Logical VulkanKHR
  29. OpEntryPoint Vertex %func "shader"
  30. %float = OpTypeFloat 32
  31. %1 = OpUndef %float
  32. %void = OpTypeVoid
  33. %void_f = OpTypeFunction %void
  34. %func = OpFunction %void None %void_f
  35. %label = OpLabel
  36. OpReturn
  37. OpFunctionEnd
  38. )";
  39. CompileSuccessfully(spirv);
  40. // Control case: OpUndef is allowed in SPIR-V 1.3
  41. EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
  42. // Control case: OpUndef is disallowed in the WebGPU env
  43. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  44. EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
  45. }
  46. TEST_F(ValidateWebGPU, OpNameIsDisallowed) {
  47. std::string spirv = R"(
  48. OpCapability Shader
  49. OpCapability VulkanMemoryModelKHR
  50. OpExtension "SPV_KHR_vulkan_memory_model"
  51. OpMemoryModel Logical VulkanKHR
  52. OpName %1 "foo"
  53. %1 = OpTypeFloat 32
  54. )";
  55. CompileSuccessfully(spirv);
  56. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  57. EXPECT_THAT(getDiagnosticString(),
  58. HasSubstr("Debugging instructions are not allowed in the WebGPU "
  59. "execution environment.\n OpName %foo \"foo\"\n"));
  60. }
  61. TEST_F(ValidateWebGPU, OpMemberNameIsDisallowed) {
  62. std::string spirv = R"(
  63. OpCapability Shader
  64. OpCapability VulkanMemoryModelKHR
  65. OpExtension "SPV_KHR_vulkan_memory_model"
  66. OpMemoryModel Logical VulkanKHR
  67. OpMemberName %2 0 "foo"
  68. %1 = OpTypeFloat 32
  69. %2 = OpTypeStruct %1
  70. )";
  71. CompileSuccessfully(spirv);
  72. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  73. EXPECT_THAT(getDiagnosticString(),
  74. HasSubstr("Debugging instructions are not allowed in the WebGPU "
  75. "execution environment.\n OpMemberName %_struct_1 0 "
  76. "\"foo\"\n"));
  77. }
  78. TEST_F(ValidateWebGPU, OpSourceIsDisallowed) {
  79. std::string spirv = R"(
  80. OpCapability Shader
  81. OpCapability VulkanMemoryModelKHR
  82. OpExtension "SPV_KHR_vulkan_memory_model"
  83. OpMemoryModel Logical VulkanKHR
  84. OpSource GLSL 450
  85. )";
  86. CompileSuccessfully(spirv);
  87. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  88. EXPECT_THAT(getDiagnosticString(),
  89. HasSubstr("Debugging instructions are not allowed in the WebGPU "
  90. "execution environment.\n OpSource GLSL 450\n"));
  91. }
  92. // OpSourceContinued does not have a test case, because it requires being
  93. // preceded by OpSource, which will cause a validation error.
  94. TEST_F(ValidateWebGPU, OpSourceExtensionIsDisallowed) {
  95. std::string spirv = R"(
  96. OpCapability Shader
  97. OpCapability VulkanMemoryModelKHR
  98. OpExtension "SPV_KHR_vulkan_memory_model"
  99. OpMemoryModel Logical VulkanKHR
  100. OpSourceExtension "bar"
  101. )";
  102. CompileSuccessfully(spirv);
  103. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  104. EXPECT_THAT(getDiagnosticString(),
  105. HasSubstr("Debugging instructions are not allowed in the WebGPU "
  106. "execution environment.\n OpSourceExtension "
  107. "\"bar\"\n"));
  108. }
  109. TEST_F(ValidateWebGPU, OpStringIsDisallowed) {
  110. std::string spirv = R"(
  111. OpCapability Shader
  112. OpCapability VulkanMemoryModelKHR
  113. OpExtension "SPV_KHR_vulkan_memory_model"
  114. OpMemoryModel Logical VulkanKHR
  115. %1 = OpString "foo"
  116. )";
  117. CompileSuccessfully(spirv);
  118. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  119. EXPECT_THAT(getDiagnosticString(),
  120. HasSubstr("Debugging instructions are not allowed in the WebGPU "
  121. "execution environment.\n %1 = OpString \"foo\"\n"));
  122. }
  123. // OpLine does not have a test case, because it requires being preceded by
  124. // OpString, which will cause a validation error.
  125. TEST_F(ValidateWebGPU, OpNoLineDisallowed) {
  126. std::string spirv = R"(
  127. OpCapability Shader
  128. OpCapability VulkanMemoryModelKHR
  129. OpExtension "SPV_KHR_vulkan_memory_model"
  130. OpMemoryModel Logical VulkanKHR
  131. OpNoLine
  132. )";
  133. CompileSuccessfully(spirv);
  134. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  135. EXPECT_THAT(getDiagnosticString(),
  136. HasSubstr("Debugging instructions are not allowed in the WebGPU "
  137. "execution environment.\n OpNoLine\n"));
  138. }
  139. TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
  140. std::string spirv = R"(
  141. OpCapability Shader
  142. OpCapability VulkanMemoryModelKHR
  143. OpExtension "SPV_KHR_vulkan_memory_model"
  144. OpMemoryModel Logical VulkanKHR
  145. OpEntryPoint Vertex %func "shader"
  146. %void = OpTypeVoid
  147. %void_f = OpTypeFunction %void
  148. %func = OpFunction %void None %void_f
  149. %label = OpLabel
  150. OpReturn
  151. OpFunctionEnd
  152. )";
  153. CompileSuccessfully(spirv);
  154. EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
  155. }
  156. TEST_F(ValidateWebGPU, NonLogicalAddressingModelBad) {
  157. std::string spirv = R"(
  158. OpCapability Shader
  159. OpCapability VulkanMemoryModelKHR
  160. OpExtension "SPV_KHR_vulkan_memory_model"
  161. OpMemoryModel Physical32 VulkanKHR
  162. )";
  163. CompileSuccessfully(spirv);
  164. EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
  165. EXPECT_THAT(getDiagnosticString(),
  166. HasSubstr("Addressing model must be Logical for WebGPU "
  167. "environment.\n OpMemoryModel Physical32 "
  168. "Vulkan\n"));
  169. }
  170. TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelBad) {
  171. std::string spirv = R"(
  172. OpCapability Shader
  173. OpMemoryModel Logical GLSL450
  174. OpNoLine
  175. )";
  176. CompileSuccessfully(spirv);
  177. EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
  178. EXPECT_THAT(getDiagnosticString(),
  179. HasSubstr("Memory model must be VulkanKHR for WebGPU "
  180. "environment.\n OpMemoryModel Logical GLSL450\n"));
  181. }
  182. TEST_F(ValidateWebGPU, WhitelistedExtendedInstructionsImportGood) {
  183. std::string spirv = R"(
  184. OpCapability Shader
  185. OpCapability VulkanMemoryModelKHR
  186. OpExtension "SPV_KHR_vulkan_memory_model"
  187. %1 = OpExtInstImport "GLSL.std.450"
  188. OpMemoryModel Logical VulkanKHR
  189. OpEntryPoint Vertex %func "shader"
  190. %void = OpTypeVoid
  191. %void_f = OpTypeFunction %void
  192. %func = OpFunction %void None %void_f
  193. %label = OpLabel
  194. OpReturn
  195. OpFunctionEnd
  196. )";
  197. CompileSuccessfully(spirv);
  198. EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
  199. }
  200. TEST_F(ValidateWebGPU, NonWhitelistedExtendedInstructionsImportBad) {
  201. std::string spirv = R"(
  202. OpCapability Shader
  203. OpCapability VulkanMemoryModelKHR
  204. OpExtension "SPV_KHR_vulkan_memory_model"
  205. %1 = OpExtInstImport "OpenCL.std"
  206. OpMemoryModel Logical VulkanKHR
  207. )";
  208. CompileSuccessfully(spirv);
  209. EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
  210. EXPECT_THAT(getDiagnosticString(),
  211. HasSubstr("For WebGPU, the only valid parameter to "
  212. "OpExtInstImport is \"GLSL.std.450\".\n %1 = "
  213. "OpExtInstImport \"OpenCL.std\"\n"));
  214. }
  215. TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) {
  216. std::string spirv = R"(
  217. OpCapability Shader
  218. OpCapability VulkanMemoryModelKHR
  219. OpExtension "SPV_KHR_8bit_storage"
  220. OpExtension "SPV_KHR_vulkan_memory_model"
  221. OpMemoryModel Logical VulkanKHR
  222. )";
  223. CompileSuccessfully(spirv);
  224. EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
  225. EXPECT_THAT(getDiagnosticString(),
  226. HasSubstr("For WebGPU, the only valid parameter to OpExtension "
  227. "is \"SPV_KHR_vulkan_memory_model\".\n OpExtension "
  228. "\"SPV_KHR_8bit_storage\"\n"));
  229. }
  230. spv_binary GenerateTrivialBinary(bool need_little_endian) {
  231. // Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all
  232. // the required boilerplate and a trivial entry point function.
  233. static const uint8_t binary_bytes[] = {
  234. // clang-format off
  235. 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00,
  236. 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
  237. 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00,
  238. 0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F,
  239. 0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72,
  240. 0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00,
  241. 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00,
  242. 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64,
  243. 0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
  244. 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  245. 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  246. 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
  247. 0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
  248. // clang-format on
  249. };
  250. static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t);
  251. std::unique_ptr<spv_binary_t> result(new spv_binary_t);
  252. if (!result) return nullptr;
  253. result->wordCount = word_count;
  254. result->code = new uint32_t[word_count];
  255. if (!result->code) return nullptr;
  256. if (need_little_endian) {
  257. memcpy(result->code, binary_bytes, sizeof(binary_bytes));
  258. } else {
  259. uint8_t* code_bytes = reinterpret_cast<uint8_t*>(result->code);
  260. for (size_t word = 0; word < word_count; ++word) {
  261. code_bytes[4 * word] = binary_bytes[4 * word + 3];
  262. code_bytes[4 * word + 1] = binary_bytes[4 * word + 2];
  263. code_bytes[4 * word + 2] = binary_bytes[4 * word + 1];
  264. code_bytes[4 * word + 3] = binary_bytes[4 * word];
  265. }
  266. }
  267. return result.release();
  268. }
  269. TEST_F(ValidateWebGPU, LittleEndianGood) {
  270. DestroyBinary();
  271. binary_ = GenerateTrivialBinary(true);
  272. EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
  273. }
  274. TEST_F(ValidateWebGPU, BigEndianBad) {
  275. DestroyBinary();
  276. binary_ = GenerateTrivialBinary(false);
  277. EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
  278. EXPECT_THAT(getDiagnosticString(),
  279. HasSubstr("WebGPU requires SPIR-V to be little endian."));
  280. }
  281. } // namespace
  282. } // namespace val
  283. } // namespace spvtools