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