• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
15 // Validation tests for WebGPU env specific checks
16 
17 #include <string>
18 
19 #include "gmock/gmock.h"
20 #include "test/val/val_fixtures.h"
21 
22 namespace spvtools {
23 namespace val {
24 namespace {
25 
26 using testing::HasSubstr;
27 
28 using ValidateWebGPU = spvtest::ValidateBase<bool>;
29 
TEST_F(ValidateWebGPU,OpUndefIsDisallowed)30 TEST_F(ValidateWebGPU, OpUndefIsDisallowed) {
31   std::string spirv = R"(
32           OpCapability Shader
33           OpCapability VulkanMemoryModelKHR
34           OpExtension "SPV_KHR_vulkan_memory_model"
35           OpMemoryModel Logical VulkanKHR
36           OpEntryPoint Vertex %func "shader"
37 %float  = OpTypeFloat 32
38 %1      = OpUndef %float
39 %void   = OpTypeVoid
40 %void_f = OpTypeFunction %void
41 %func   = OpFunction %void None %void_f
42 %label  = OpLabel
43           OpReturn
44           OpFunctionEnd
45 )";
46 
47   CompileSuccessfully(spirv);
48 
49   // Control case: OpUndef is allowed in SPIR-V 1.3
50   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_UNIVERSAL_1_3));
51 
52   // Control case: OpUndef is disallowed in the WebGPU env
53   EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
54   EXPECT_THAT(getDiagnosticString(), HasSubstr("OpUndef is disallowed"));
55 }
56 
TEST_F(ValidateWebGPU,OpNameIsAllowed)57 TEST_F(ValidateWebGPU, OpNameIsAllowed) {
58   std::string spirv = R"(
59             OpCapability Shader
60             OpCapability VulkanMemoryModelKHR
61             OpExtension "SPV_KHR_vulkan_memory_model"
62             OpMemoryModel Logical VulkanKHR
63             OpEntryPoint Vertex %func "shader"
64             OpName %1 "foo"
65        %1 = OpTypeFloat 32
66   %void   = OpTypeVoid
67   %void_f = OpTypeFunction %void
68   %func   = OpFunction %void None %void_f
69   %label  = OpLabel
70             OpReturn
71             OpFunctionEnd
72 )";
73 
74   CompileSuccessfully(spirv);
75   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
76 }
77 
TEST_F(ValidateWebGPU,OpMemberNameIsAllowed)78 TEST_F(ValidateWebGPU, OpMemberNameIsAllowed) {
79   std::string spirv = R"(
80             OpCapability Shader
81             OpCapability VulkanMemoryModelKHR
82             OpExtension "SPV_KHR_vulkan_memory_model"
83             OpMemoryModel Logical VulkanKHR
84             OpEntryPoint Vertex %func "shader"
85             OpMemberName %2 0 "foo"
86        %1 = OpTypeFloat 32
87        %2 = OpTypeStruct %1
88   %void   = OpTypeVoid
89   %void_f = OpTypeFunction %void
90   %func   = OpFunction %void None %void_f
91   %label  = OpLabel
92             OpReturn
93             OpFunctionEnd
94 
95 )";
96 
97   CompileSuccessfully(spirv);
98   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
99 }
100 
TEST_F(ValidateWebGPU,OpSourceIsAllowed)101 TEST_F(ValidateWebGPU, OpSourceIsAllowed) {
102   std::string spirv = R"(
103             OpCapability Shader
104             OpCapability VulkanMemoryModelKHR
105             OpExtension "SPV_KHR_vulkan_memory_model"
106             OpMemoryModel Logical VulkanKHR
107             OpEntryPoint Vertex %func "shader"
108             OpSource GLSL 450
109   %void   = OpTypeVoid
110   %void_f = OpTypeFunction %void
111   %func   = OpFunction %void None %void_f
112   %label  = OpLabel
113             OpReturn
114             OpFunctionEnd
115 )";
116 
117   CompileSuccessfully(spirv);
118   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
119 }
120 
TEST_F(ValidateWebGPU,OpSourceContinuedIsAllowed)121 TEST_F(ValidateWebGPU, OpSourceContinuedIsAllowed) {
122   std::string spirv = R"(
123             OpCapability Shader
124             OpCapability VulkanMemoryModelKHR
125             OpExtension "SPV_KHR_vulkan_memory_model"
126             OpMemoryModel Logical VulkanKHR
127             OpEntryPoint Vertex %func "shader"
128             OpSource GLSL 450
129             OpSourceContinued "I am a happy shader! Yay! ;"
130   %void   = OpTypeVoid
131   %void_f = OpTypeFunction %void
132   %func   = OpFunction %void None %void_f
133   %label  = OpLabel
134             OpReturn
135             OpFunctionEnd
136 )";
137 
138   CompileSuccessfully(spirv);
139   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
140 }
141 
TEST_F(ValidateWebGPU,OpSourceExtensionIsAllowed)142 TEST_F(ValidateWebGPU, OpSourceExtensionIsAllowed) {
143   std::string spirv = R"(
144             OpCapability Shader
145             OpCapability VulkanMemoryModelKHR
146             OpExtension "SPV_KHR_vulkan_memory_model"
147             OpMemoryModel Logical VulkanKHR
148             OpEntryPoint Vertex %func "shader"
149             OpSourceExtension "bar"
150   %void   = OpTypeVoid
151   %void_f = OpTypeFunction %void
152   %func   = OpFunction %void None %void_f
153   %label  = OpLabel
154             OpReturn
155             OpFunctionEnd
156 )";
157 
158   CompileSuccessfully(spirv);
159   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
160 }
161 
TEST_F(ValidateWebGPU,OpStringIsAllowed)162 TEST_F(ValidateWebGPU, OpStringIsAllowed) {
163   std::string spirv = R"(
164             OpCapability Shader
165             OpCapability VulkanMemoryModelKHR
166             OpExtension "SPV_KHR_vulkan_memory_model"
167             OpMemoryModel Logical VulkanKHR
168             OpEntryPoint Vertex %func "shader"
169        %1 = OpString "foo"
170   %void   = OpTypeVoid
171   %void_f = OpTypeFunction %void
172   %func   = OpFunction %void None %void_f
173   %label  = OpLabel
174             OpReturn
175             OpFunctionEnd
176 )";
177 
178   CompileSuccessfully(spirv);
179   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
180 }
181 
TEST_F(ValidateWebGPU,OpLineIsAllowed)182 TEST_F(ValidateWebGPU, OpLineIsAllowed) {
183   std::string spirv = R"(
184             OpCapability Shader
185             OpCapability VulkanMemoryModelKHR
186             OpExtension "SPV_KHR_vulkan_memory_model"
187             OpMemoryModel Logical VulkanKHR
188             OpEntryPoint Vertex %func "shader"
189        %1 = OpString "minimal.vert"
190             OpLine %1 1 1
191   %void   = OpTypeVoid
192   %void_f = OpTypeFunction %void
193   %func   = OpFunction %void None %void_f
194   %label  = OpLabel
195             OpReturn
196             OpFunctionEnd
197 )";
198 
199   CompileSuccessfully(spirv);
200   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
201 }
202 
TEST_F(ValidateWebGPU,OpNoLineIsAllowed)203 TEST_F(ValidateWebGPU, OpNoLineIsAllowed) {
204   std::string spirv = R"(
205             OpCapability Shader
206             OpCapability VulkanMemoryModelKHR
207             OpExtension "SPV_KHR_vulkan_memory_model"
208             OpMemoryModel Logical VulkanKHR
209             OpEntryPoint Vertex %func "shader"
210             OpNoLine
211   %void   = OpTypeVoid
212   %void_f = OpTypeFunction %void
213   %func   = OpFunction %void None %void_f
214   %label  = OpLabel
215             OpReturn
216             OpFunctionEnd
217 )";
218 
219   CompileSuccessfully(spirv);
220   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
221 }
222 
TEST_F(ValidateWebGPU,LogicalAddressingVulkanKHRMemoryGood)223 TEST_F(ValidateWebGPU, LogicalAddressingVulkanKHRMemoryGood) {
224   std::string spirv = R"(
225           OpCapability Shader
226           OpCapability VulkanMemoryModelKHR
227           OpExtension "SPV_KHR_vulkan_memory_model"
228           OpMemoryModel Logical VulkanKHR
229           OpEntryPoint Vertex %func "shader"
230 %void   = OpTypeVoid
231 %void_f = OpTypeFunction %void
232 %func   = OpFunction %void None %void_f
233 %label  = OpLabel
234           OpReturn
235           OpFunctionEnd
236 )";
237 
238   CompileSuccessfully(spirv);
239   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
240 }
241 
TEST_F(ValidateWebGPU,NonVulkanKHRMemoryModelBad)242 TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelBad) {
243   std::string spirv = R"(
244      OpCapability Shader
245      OpMemoryModel Logical GLSL450
246      OpNoLine
247 )";
248 
249   CompileSuccessfully(spirv);
250 
251   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
252   EXPECT_THAT(getDiagnosticString(),
253               HasSubstr("Memory model must be VulkanKHR for WebGPU "
254                         "environment.\n  OpMemoryModel Logical GLSL450\n"));
255 }
256 
TEST_F(ValidateWebGPU,WhitelistedExtendedInstructionsImportGood)257 TEST_F(ValidateWebGPU, WhitelistedExtendedInstructionsImportGood) {
258   std::string spirv = R"(
259           OpCapability Shader
260           OpCapability VulkanMemoryModelKHR
261           OpExtension "SPV_KHR_vulkan_memory_model"
262 %1      = OpExtInstImport "GLSL.std.450"
263           OpMemoryModel Logical VulkanKHR
264           OpEntryPoint Vertex %func "shader"
265 %void   = OpTypeVoid
266 %void_f = OpTypeFunction %void
267 %func   = OpFunction %void None %void_f
268 %label  = OpLabel
269           OpReturn
270           OpFunctionEnd
271 )";
272 
273   CompileSuccessfully(spirv);
274 
275   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
276 }
277 
TEST_F(ValidateWebGPU,NonWhitelistedExtendedInstructionsImportBad)278 TEST_F(ValidateWebGPU, NonWhitelistedExtendedInstructionsImportBad) {
279   std::string spirv = R"(
280      OpCapability Shader
281      OpCapability VulkanMemoryModelKHR
282      OpExtension "SPV_KHR_vulkan_memory_model"
283 %1 = OpExtInstImport "OpenCL.std"
284      OpMemoryModel Logical VulkanKHR
285 )";
286 
287   CompileSuccessfully(spirv);
288 
289   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
290   EXPECT_THAT(getDiagnosticString(),
291               HasSubstr("For WebGPU, the only valid parameter to "
292                         "OpExtInstImport is \"GLSL.std.450\".\n  %1 = "
293                         "OpExtInstImport \"OpenCL.std\"\n"));
294 }
295 
TEST_F(ValidateWebGPU,NonVulkanKHRMemoryModelExtensionBad)296 TEST_F(ValidateWebGPU, NonVulkanKHRMemoryModelExtensionBad) {
297   std::string spirv = R"(
298      OpCapability Shader
299      OpCapability VulkanMemoryModelKHR
300      OpExtension "SPV_KHR_8bit_storage"
301      OpExtension "SPV_KHR_vulkan_memory_model"
302      OpMemoryModel Logical VulkanKHR
303 )";
304 
305   CompileSuccessfully(spirv);
306 
307   EXPECT_EQ(SPV_ERROR_INVALID_DATA, ValidateInstructions(SPV_ENV_WEBGPU_0));
308   EXPECT_THAT(getDiagnosticString(),
309               HasSubstr("For WebGPU, the only valid parameter to OpExtension "
310                         "is \"SPV_KHR_vulkan_memory_model\".\n  OpExtension "
311                         "\"SPV_KHR_8bit_storage\"\n"));
312 }
313 
GenerateTrivialBinary(bool need_little_endian)314 spv_binary GenerateTrivialBinary(bool need_little_endian) {
315   // Smallest possible valid WebGPU SPIR-V binary in little endian. Contains all
316   // the required boilerplate and a trivial entry point function.
317   static const uint8_t binary_bytes[] = {
318       // clang-format off
319     0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00,
320     0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00,
321     0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0xE1, 0x14, 0x00, 0x00,
322     0x0A, 0x00, 0x08, 0x00, 0x53, 0x50, 0x56, 0x5F, 0x4B, 0x48, 0x52, 0x5F,
323     0x76, 0x75, 0x6C, 0x6B, 0x61, 0x6E, 0x5F, 0x6D, 0x65, 0x6D, 0x6F, 0x72,
324     0x79, 0x5F, 0x6D, 0x6F, 0x64, 0x65, 0x6C, 0x00, 0x0E, 0x00, 0x03, 0x00,
325     0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x05, 0x00,
326     0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64,
327     0x65, 0x72, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00,
328     0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
329     0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
330     0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00,
331     0x04, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00
332       // clang-format on
333   };
334   static const size_t word_count = sizeof(binary_bytes) / sizeof(uint32_t);
335   std::unique_ptr<spv_binary_t> result(new spv_binary_t);
336   if (!result) return nullptr;
337 
338   result->wordCount = word_count;
339   result->code = new uint32_t[word_count];
340   if (!result->code) return nullptr;
341 
342   if (need_little_endian) {
343     memcpy(result->code, binary_bytes, sizeof(binary_bytes));
344   } else {
345     uint8_t* code_bytes = reinterpret_cast<uint8_t*>(result->code);
346     for (size_t word = 0; word < word_count; ++word) {
347       code_bytes[4 * word] = binary_bytes[4 * word + 3];
348       code_bytes[4 * word + 1] = binary_bytes[4 * word + 2];
349       code_bytes[4 * word + 2] = binary_bytes[4 * word + 1];
350       code_bytes[4 * word + 3] = binary_bytes[4 * word];
351     }
352   }
353 
354   return result.release();
355 }
356 
TEST_F(ValidateWebGPU,LittleEndianGood)357 TEST_F(ValidateWebGPU, LittleEndianGood) {
358   DestroyBinary();
359   binary_ = GenerateTrivialBinary(true);
360   EXPECT_EQ(SPV_SUCCESS, ValidateInstructions(SPV_ENV_WEBGPU_0));
361 }
362 
TEST_F(ValidateWebGPU,BigEndianBad)363 TEST_F(ValidateWebGPU, BigEndianBad) {
364   DestroyBinary();
365   binary_ = GenerateTrivialBinary(false);
366   EXPECT_EQ(SPV_ERROR_INVALID_BINARY, ValidateInstructions(SPV_ENV_WEBGPU_0));
367   EXPECT_THAT(getDiagnosticString(),
368               HasSubstr("WebGPU requires SPIR-V to be little endian."));
369 }
370 
371 }  // namespace
372 }  // namespace val
373 }  // namespace spvtools
374