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