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 
IsValidWebGPUDecoration(uint32_t decoration)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 
LogStringForDecoration(uint32_t decoration)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.
DecorationTakesIdParameters(uint32_t type)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 
ValidateDecorate(ValidationState_t & _,const Instruction * inst)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 
ValidateDecorateId(ValidationState_t & _,const Instruction * inst)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 
ValidateMemberDecorate(ValidationState_t & _,const Instruction * inst)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 
ValidateDecorationGroup(ValidationState_t & _,const Instruction * inst)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 
ValidateGroupDecorate(ValidationState_t & _,const Instruction * inst)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 
ValidateGroupMemberDecorate(ValidationState_t & _,const Instruction * inst)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.
RegisterDecorations(ValidationState_t & _,const Instruction * inst)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 
AnnotationPass(ValidationState_t & _,const Instruction * inst)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