1 | // Copyright (c) 2018 Google LLC. |
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 | #include "source/opcode.h" |
16 | #include "source/spirv_target_env.h" |
17 | #include "source/val/instruction.h" |
18 | #include "source/val/validate.h" |
19 | #include "source/val/validation_state.h" |
20 | |
21 | namespace spvtools { |
22 | namespace val { |
23 | namespace { |
24 | |
25 | bool IsValidWebGPUDecoration(uint32_t decoration) { |
26 | switch (decoration) { |
27 | case SpvDecorationSpecId: |
28 | case SpvDecorationBlock: |
29 | case SpvDecorationRowMajor: |
30 | case SpvDecorationColMajor: |
31 | case SpvDecorationArrayStride: |
32 | case SpvDecorationMatrixStride: |
33 | case SpvDecorationBuiltIn: |
34 | case SpvDecorationNoPerspective: |
35 | case SpvDecorationFlat: |
36 | case SpvDecorationCentroid: |
37 | case SpvDecorationRestrict: |
38 | case SpvDecorationAliased: |
39 | case SpvDecorationNonWritable: |
40 | case SpvDecorationNonReadable: |
41 | case SpvDecorationUniform: |
42 | case SpvDecorationLocation: |
43 | case SpvDecorationComponent: |
44 | case SpvDecorationIndex: |
45 | case SpvDecorationBinding: |
46 | case SpvDecorationDescriptorSet: |
47 | case SpvDecorationOffset: |
48 | case SpvDecorationNoContraction: |
49 | return true; |
50 | default: |
51 | return false; |
52 | } |
53 | } |
54 | |
55 | std::string LogStringForDecoration(uint32_t decoration) { |
56 | switch (decoration) { |
57 | case SpvDecorationRelaxedPrecision: |
58 | return "RelaxedPrecision" ; |
59 | case SpvDecorationSpecId: |
60 | return "SpecId" ; |
61 | case SpvDecorationBlock: |
62 | return "Block" ; |
63 | case SpvDecorationBufferBlock: |
64 | return "BufferBlock" ; |
65 | case SpvDecorationRowMajor: |
66 | return "RowMajor" ; |
67 | case SpvDecorationColMajor: |
68 | return "ColMajor" ; |
69 | case SpvDecorationArrayStride: |
70 | return "ArrayStride" ; |
71 | case SpvDecorationMatrixStride: |
72 | return "MatrixStride" ; |
73 | case SpvDecorationGLSLShared: |
74 | return "GLSLShared" ; |
75 | case SpvDecorationGLSLPacked: |
76 | return "GLSLPacked" ; |
77 | case SpvDecorationCPacked: |
78 | return "CPacked" ; |
79 | case SpvDecorationBuiltIn: |
80 | return "BuiltIn" ; |
81 | case SpvDecorationNoPerspective: |
82 | return "NoPerspective" ; |
83 | case SpvDecorationFlat: |
84 | return "Flat" ; |
85 | case SpvDecorationPatch: |
86 | return "Patch" ; |
87 | case SpvDecorationCentroid: |
88 | return "Centroid" ; |
89 | case SpvDecorationSample: |
90 | return "Sample" ; |
91 | case SpvDecorationInvariant: |
92 | return "Invariant" ; |
93 | case SpvDecorationRestrict: |
94 | return "Restrict" ; |
95 | case SpvDecorationAliased: |
96 | return "Aliased" ; |
97 | case SpvDecorationVolatile: |
98 | return "Volatile" ; |
99 | case SpvDecorationConstant: |
100 | return "Constant" ; |
101 | case SpvDecorationCoherent: |
102 | return "Coherent" ; |
103 | case SpvDecorationNonWritable: |
104 | return "NonWritable" ; |
105 | case SpvDecorationNonReadable: |
106 | return "NonReadable" ; |
107 | case SpvDecorationUniform: |
108 | return "Uniform" ; |
109 | case SpvDecorationSaturatedConversion: |
110 | return "SaturatedConversion" ; |
111 | case SpvDecorationStream: |
112 | return "Stream" ; |
113 | case SpvDecorationLocation: |
114 | return "Location" ; |
115 | case SpvDecorationComponent: |
116 | return "Component" ; |
117 | case SpvDecorationIndex: |
118 | return "Index" ; |
119 | case SpvDecorationBinding: |
120 | return "Binding" ; |
121 | case SpvDecorationDescriptorSet: |
122 | return "DescriptorSet" ; |
123 | case SpvDecorationOffset: |
124 | return "Offset" ; |
125 | case SpvDecorationXfbBuffer: |
126 | return "XfbBuffer" ; |
127 | case SpvDecorationXfbStride: |
128 | return "XfbStride" ; |
129 | case SpvDecorationFuncParamAttr: |
130 | return "FuncParamAttr" ; |
131 | case SpvDecorationFPRoundingMode: |
132 | return "FPRoundingMode" ; |
133 | case SpvDecorationFPFastMathMode: |
134 | return "FPFastMathMode" ; |
135 | case SpvDecorationLinkageAttributes: |
136 | return "LinkageAttributes" ; |
137 | case SpvDecorationNoContraction: |
138 | return "NoContraction" ; |
139 | case SpvDecorationInputAttachmentIndex: |
140 | return "InputAttachmentIndex" ; |
141 | case SpvDecorationAlignment: |
142 | return "Alignment" ; |
143 | case SpvDecorationMaxByteOffset: |
144 | return "MaxByteOffset" ; |
145 | case SpvDecorationAlignmentId: |
146 | return "AlignmentId" ; |
147 | case SpvDecorationMaxByteOffsetId: |
148 | return "MaxByteOffsetId" ; |
149 | case SpvDecorationNoSignedWrap: |
150 | return "NoSignedWrap" ; |
151 | case SpvDecorationNoUnsignedWrap: |
152 | return "NoUnsignedWrap" ; |
153 | case SpvDecorationExplicitInterpAMD: |
154 | return "ExplicitInterpAMD" ; |
155 | case SpvDecorationOverrideCoverageNV: |
156 | return "OverrideCoverageNV" ; |
157 | case SpvDecorationPassthroughNV: |
158 | return "PassthroughNV" ; |
159 | case SpvDecorationViewportRelativeNV: |
160 | return "ViewportRelativeNV" ; |
161 | case SpvDecorationSecondaryViewportRelativeNV: |
162 | return "SecondaryViewportRelativeNV" ; |
163 | case SpvDecorationPerPrimitiveNV: |
164 | return "PerPrimitiveNV" ; |
165 | case SpvDecorationPerViewNV: |
166 | return "PerViewNV" ; |
167 | case SpvDecorationPerTaskNV: |
168 | return "PerTaskNV" ; |
169 | case SpvDecorationPerVertexNV: |
170 | return "PerVertexNV" ; |
171 | case SpvDecorationNonUniformEXT: |
172 | return "NonUniformEXT" ; |
173 | case SpvDecorationRestrictPointerEXT: |
174 | return "RestrictPointerEXT" ; |
175 | case SpvDecorationAliasedPointerEXT: |
176 | return "AliasedPointerEXT" ; |
177 | case SpvDecorationHlslCounterBufferGOOGLE: |
178 | return "HlslCounterBufferGOOGLE" ; |
179 | case SpvDecorationHlslSemanticGOOGLE: |
180 | return "HlslSemanticGOOGLE" ; |
181 | default: |
182 | break; |
183 | } |
184 | return "Unknown" ; |
185 | } |
186 | |
187 | // Returns true if the decoration takes ID parameters. |
188 | // TODO(dneto): This can be generated from the grammar. |
189 | bool DecorationTakesIdParameters(uint32_t type) { |
190 | switch (static_cast<SpvDecoration>(type)) { |
191 | case SpvDecorationUniformId: |
192 | case SpvDecorationAlignmentId: |
193 | case SpvDecorationMaxByteOffsetId: |
194 | case SpvDecorationHlslCounterBufferGOOGLE: |
195 | return true; |
196 | default: |
197 | break; |
198 | } |
199 | return false; |
200 | } |
201 | |
202 | spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) { |
203 | const auto decoration = inst->GetOperandAs<uint32_t>(1); |
204 | if (decoration == SpvDecorationSpecId) { |
205 | const auto target_id = inst->GetOperandAs<uint32_t>(0); |
206 | const auto target = _.FindDef(target_id); |
207 | if (!target || !spvOpcodeIsScalarSpecConstant(target->opcode())) { |
208 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
209 | << "OpDecorate SpecId decoration target <id> '" |
210 | << _.getIdName(target_id) |
211 | << "' is not a scalar specialization constant." ; |
212 | } |
213 | } |
214 | |
215 | if (spvIsWebGPUEnv(_.context()->target_env) && |
216 | !IsValidWebGPUDecoration(decoration)) { |
217 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
218 | << "OpDecorate decoration '" << LogStringForDecoration(decoration) |
219 | << "' is not valid for the WebGPU execution environment." ; |
220 | } |
221 | |
222 | if (DecorationTakesIdParameters(decoration)) { |
223 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
224 | << "Decorations taking ID parameters may not be used with " |
225 | "OpDecorateId" ; |
226 | } |
227 | // TODO: Add validations for all decorations. |
228 | return SPV_SUCCESS; |
229 | } |
230 | |
231 | spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) { |
232 | const auto decoration = inst->GetOperandAs<uint32_t>(1); |
233 | if (!DecorationTakesIdParameters(decoration)) { |
234 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
235 | << "Decorations that don't take ID parameters may not be used with " |
236 | "OpDecorateId" ; |
237 | } |
238 | // TODO: Add validations for these decorations. |
239 | // UniformId is covered elsewhere. |
240 | return SPV_SUCCESS; |
241 | } |
242 | |
243 | spv_result_t ValidateMemberDecorate(ValidationState_t& _, |
244 | const Instruction* inst) { |
245 | const auto struct_type_id = inst->GetOperandAs<uint32_t>(0); |
246 | const auto struct_type = _.FindDef(struct_type_id); |
247 | if (!struct_type || SpvOpTypeStruct != struct_type->opcode()) { |
248 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
249 | << "OpMemberDecorate Structure type <id> '" |
250 | << _.getIdName(struct_type_id) << "' is not a struct type." ; |
251 | } |
252 | const auto member = inst->GetOperandAs<uint32_t>(1); |
253 | const auto member_count = |
254 | static_cast<uint32_t>(struct_type->words().size() - 2); |
255 | if (member_count <= member) { |
256 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
257 | << "Index " << member |
258 | << " provided in OpMemberDecorate for struct <id> " |
259 | << _.getIdName(struct_type_id) |
260 | << " is out of bounds. The structure has " << member_count |
261 | << " members. Largest valid index is " << member_count - 1 << "." ; |
262 | } |
263 | |
264 | const auto decoration = inst->GetOperandAs<uint32_t>(2); |
265 | if (spvIsWebGPUEnv(_.context()->target_env) && |
266 | !IsValidWebGPUDecoration(decoration)) { |
267 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
268 | << "OpMemberDecorate decoration '" << _.getIdName(decoration) |
269 | << "' is not valid for the WebGPU execution environment." ; |
270 | } |
271 | |
272 | return SPV_SUCCESS; |
273 | } |
274 | |
275 | spv_result_t ValidateDecorationGroup(ValidationState_t& _, |
276 | const Instruction* inst) { |
277 | if (spvIsWebGPUEnv(_.context()->target_env)) { |
278 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
279 | << "OpDecorationGroup is not allowed in the WebGPU execution " |
280 | << "environment." ; |
281 | } |
282 | |
283 | const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0); |
284 | const auto decoration_group = _.FindDef(decoration_group_id); |
285 | for (auto pair : decoration_group->uses()) { |
286 | auto use = pair.first; |
287 | if (use->opcode() != SpvOpDecorate && use->opcode() != SpvOpGroupDecorate && |
288 | use->opcode() != SpvOpGroupMemberDecorate && |
289 | use->opcode() != SpvOpName && use->opcode() != SpvOpDecorateId && |
290 | !use->IsNonSemantic()) { |
291 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
292 | << "Result id of OpDecorationGroup can only " |
293 | << "be targeted by OpName, OpGroupDecorate, " |
294 | << "OpDecorate, OpDecorateId, and OpGroupMemberDecorate" ; |
295 | } |
296 | } |
297 | return SPV_SUCCESS; |
298 | } |
299 | |
300 | spv_result_t ValidateGroupDecorate(ValidationState_t& _, |
301 | const Instruction* inst) { |
302 | if (spvIsWebGPUEnv(_.context()->target_env)) { |
303 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
304 | << "OpGroupDecorate is not allowed in the WebGPU execution " |
305 | << "environment." ; |
306 | } |
307 | |
308 | const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0); |
309 | auto decoration_group = _.FindDef(decoration_group_id); |
310 | if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { |
311 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
312 | << "OpGroupDecorate Decoration group <id> '" |
313 | << _.getIdName(decoration_group_id) |
314 | << "' is not a decoration group." ; |
315 | } |
316 | for (unsigned i = 1; i < inst->operands().size(); ++i) { |
317 | auto target_id = inst->GetOperandAs<uint32_t>(i); |
318 | auto target = _.FindDef(target_id); |
319 | if (!target || target->opcode() == SpvOpDecorationGroup) { |
320 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
321 | << "OpGroupDecorate may not target OpDecorationGroup <id> '" |
322 | << _.getIdName(target_id) << "'" ; |
323 | } |
324 | } |
325 | return SPV_SUCCESS; |
326 | } |
327 | |
328 | spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _, |
329 | const Instruction* inst) { |
330 | if (spvIsWebGPUEnv(_.context()->target_env)) { |
331 | return _.diag(SPV_ERROR_INVALID_BINARY, inst) |
332 | << "OpGroupMemberDecorate is not allowed in the WebGPU execution " |
333 | << "environment." ; |
334 | } |
335 | |
336 | const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0); |
337 | const auto decoration_group = _.FindDef(decoration_group_id); |
338 | if (!decoration_group || SpvOpDecorationGroup != decoration_group->opcode()) { |
339 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
340 | << "OpGroupMemberDecorate Decoration group <id> '" |
341 | << _.getIdName(decoration_group_id) |
342 | << "' is not a decoration group." ; |
343 | } |
344 | // Grammar checks ensures that the number of arguments to this instruction |
345 | // is an odd number: 1 decoration group + (id,literal) pairs. |
346 | for (size_t i = 1; i + 1 < inst->operands().size(); i += 2) { |
347 | const uint32_t struct_id = inst->GetOperandAs<uint32_t>(i); |
348 | const uint32_t index = inst->GetOperandAs<uint32_t>(i + 1); |
349 | auto struct_instr = _.FindDef(struct_id); |
350 | if (!struct_instr || SpvOpTypeStruct != struct_instr->opcode()) { |
351 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
352 | << "OpGroupMemberDecorate Structure type <id> '" |
353 | << _.getIdName(struct_id) << "' is not a struct type." ; |
354 | } |
355 | const uint32_t num_struct_members = |
356 | static_cast<uint32_t>(struct_instr->words().size() - 2); |
357 | if (index >= num_struct_members) { |
358 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
359 | << "Index " << index |
360 | << " provided in OpGroupMemberDecorate for struct <id> " |
361 | << _.getIdName(struct_id) |
362 | << " is out of bounds. The structure has " << num_struct_members |
363 | << " members. Largest valid index is " << num_struct_members - 1 |
364 | << "." ; |
365 | } |
366 | } |
367 | return SPV_SUCCESS; |
368 | } |
369 | |
370 | // Registers necessary decoration(s) for the appropriate IDs based on the |
371 | // instruction. |
372 | spv_result_t RegisterDecorations(ValidationState_t& _, |
373 | const Instruction* inst) { |
374 | switch (inst->opcode()) { |
375 | case SpvOpDecorate: |
376 | case SpvOpDecorateId: { |
377 | const uint32_t target_id = inst->word(1); |
378 | const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(2)); |
379 | std::vector<uint32_t> dec_params; |
380 | if (inst->words().size() > 3) { |
381 | dec_params.insert(dec_params.end(), inst->words().begin() + 3, |
382 | inst->words().end()); |
383 | } |
384 | _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params)); |
385 | break; |
386 | } |
387 | case SpvOpMemberDecorate: { |
388 | const uint32_t struct_id = inst->word(1); |
389 | const uint32_t index = inst->word(2); |
390 | const SpvDecoration dec_type = static_cast<SpvDecoration>(inst->word(3)); |
391 | std::vector<uint32_t> dec_params; |
392 | if (inst->words().size() > 4) { |
393 | dec_params.insert(dec_params.end(), inst->words().begin() + 4, |
394 | inst->words().end()); |
395 | } |
396 | _.RegisterDecorationForId(struct_id, |
397 | Decoration(dec_type, dec_params, index)); |
398 | break; |
399 | } |
400 | case SpvOpDecorationGroup: { |
401 | // We don't need to do anything right now. Assigning decorations to groups |
402 | // will be taken care of via OpGroupDecorate. |
403 | break; |
404 | } |
405 | case SpvOpGroupDecorate: { |
406 | // Word 1 is the group <id>. All subsequent words are target <id>s that |
407 | // are going to be decorated with the decorations. |
408 | const uint32_t decoration_group_id = inst->word(1); |
409 | std::vector<Decoration>& group_decorations = |
410 | _.id_decorations(decoration_group_id); |
411 | for (size_t i = 2; i < inst->words().size(); ++i) { |
412 | const uint32_t target_id = inst->word(i); |
413 | _.RegisterDecorationsForId(target_id, group_decorations.begin(), |
414 | group_decorations.end()); |
415 | } |
416 | break; |
417 | } |
418 | case SpvOpGroupMemberDecorate: { |
419 | // Word 1 is the Decoration Group <id> followed by (struct<id>,literal) |
420 | // pairs. All decorations of the group should be applied to all the struct |
421 | // members that are specified in the instructions. |
422 | const uint32_t decoration_group_id = inst->word(1); |
423 | std::vector<Decoration>& group_decorations = |
424 | _.id_decorations(decoration_group_id); |
425 | // Grammar checks ensures that the number of arguments to this instruction |
426 | // is an odd number: 1 decoration group + (id,literal) pairs. |
427 | for (size_t i = 2; i + 1 < inst->words().size(); i = i + 2) { |
428 | const uint32_t struct_id = inst->word(i); |
429 | const uint32_t index = inst->word(i + 1); |
430 | // ID validation phase ensures this is in fact a struct instruction and |
431 | // that the index is not out of bound. |
432 | _.RegisterDecorationsForStructMember(struct_id, index, |
433 | group_decorations.begin(), |
434 | group_decorations.end()); |
435 | } |
436 | break; |
437 | } |
438 | default: |
439 | break; |
440 | } |
441 | return SPV_SUCCESS; |
442 | } |
443 | |
444 | } // namespace |
445 | |
446 | spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) { |
447 | switch (inst->opcode()) { |
448 | case SpvOpDecorate: |
449 | if (auto error = ValidateDecorate(_, inst)) return error; |
450 | break; |
451 | case SpvOpDecorateId: |
452 | if (auto error = ValidateDecorateId(_, inst)) return error; |
453 | break; |
454 | // TODO(dneto): SpvOpDecorateStringGOOGLE |
455 | // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253 |
456 | case SpvOpMemberDecorate: |
457 | if (auto error = ValidateMemberDecorate(_, inst)) return error; |
458 | break; |
459 | case SpvOpDecorationGroup: |
460 | if (auto error = ValidateDecorationGroup(_, inst)) return error; |
461 | break; |
462 | case SpvOpGroupDecorate: |
463 | if (auto error = ValidateGroupDecorate(_, inst)) return error; |
464 | break; |
465 | case SpvOpGroupMemberDecorate: |
466 | if (auto error = ValidateGroupMemberDecorate(_, inst)) return error; |
467 | break; |
468 | default: |
469 | break; |
470 | } |
471 | |
472 | // In order to validate decoration rules, we need to know all the decorations |
473 | // that are applied to any given <id>. |
474 | RegisterDecorations(_, inst); |
475 | |
476 | return SPV_SUCCESS; |
477 | } |
478 | |
479 | } // namespace val |
480 | } // namespace spvtools |
481 | |