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