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 // Returns true if the decoration takes ID parameters.
26 // TODO(dneto): This can be generated from the grammar.
DecorationTakesIdParameters(spv::Decoration type)27 bool DecorationTakesIdParameters(spv::Decoration type) {
28 switch (type) {
29 case spv::Decoration::UniformId:
30 case spv::Decoration::AlignmentId:
31 case spv::Decoration::MaxByteOffsetId:
32 case spv::Decoration::HlslCounterBufferGOOGLE:
33 return true;
34 default:
35 break;
36 }
37 return false;
38 }
39
IsMemberDecorationOnly(spv::Decoration dec)40 bool IsMemberDecorationOnly(spv::Decoration dec) {
41 switch (dec) {
42 case spv::Decoration::RowMajor:
43 case spv::Decoration::ColMajor:
44 case spv::Decoration::MatrixStride:
45 // SPIR-V spec bug? Offset is generated on variables when dealing with
46 // transform feedback.
47 // case spv::Decoration::Offset:
48 return true;
49 default:
50 break;
51 }
52 return false;
53 }
54
IsNotMemberDecoration(spv::Decoration dec)55 bool IsNotMemberDecoration(spv::Decoration dec) {
56 switch (dec) {
57 case spv::Decoration::SpecId:
58 case spv::Decoration::Block:
59 case spv::Decoration::BufferBlock:
60 case spv::Decoration::ArrayStride:
61 case spv::Decoration::GLSLShared:
62 case spv::Decoration::GLSLPacked:
63 case spv::Decoration::CPacked:
64 // TODO: https://github.com/KhronosGroup/glslang/issues/703:
65 // glslang applies Restrict to structure members.
66 // case spv::Decoration::Restrict:
67 case spv::Decoration::Aliased:
68 case spv::Decoration::Constant:
69 case spv::Decoration::Uniform:
70 case spv::Decoration::UniformId:
71 case spv::Decoration::SaturatedConversion:
72 case spv::Decoration::Index:
73 case spv::Decoration::Binding:
74 case spv::Decoration::DescriptorSet:
75 case spv::Decoration::FuncParamAttr:
76 case spv::Decoration::FPRoundingMode:
77 case spv::Decoration::FPFastMathMode:
78 case spv::Decoration::LinkageAttributes:
79 case spv::Decoration::NoContraction:
80 case spv::Decoration::InputAttachmentIndex:
81 case spv::Decoration::Alignment:
82 case spv::Decoration::MaxByteOffset:
83 case spv::Decoration::AlignmentId:
84 case spv::Decoration::MaxByteOffsetId:
85 case spv::Decoration::NoSignedWrap:
86 case spv::Decoration::NoUnsignedWrap:
87 case spv::Decoration::NonUniform:
88 case spv::Decoration::RestrictPointer:
89 case spv::Decoration::AliasedPointer:
90 case spv::Decoration::CounterBuffer:
91 return true;
92 default:
93 break;
94 }
95 return false;
96 }
97
ValidateDecorationTarget(ValidationState_t & _,spv::Decoration dec,const Instruction * inst,const Instruction * target)98 spv_result_t ValidateDecorationTarget(ValidationState_t& _, spv::Decoration dec,
99 const Instruction* inst,
100 const Instruction* target) {
101 auto fail = [&_, dec, inst, target](uint32_t vuid) -> DiagnosticStream {
102 DiagnosticStream ds = std::move(
103 _.diag(SPV_ERROR_INVALID_ID, inst)
104 << _.VkErrorID(vuid) << _.SpvDecorationString(dec)
105 << " decoration on target <id> " << _.getIdName(target->id()) << " ");
106 return ds;
107 };
108 switch (dec) {
109 case spv::Decoration::SpecId:
110 if (!spvOpcodeIsScalarSpecConstant(target->opcode())) {
111 return fail(0) << "must be a scalar specialization constant";
112 }
113 break;
114 case spv::Decoration::Block:
115 case spv::Decoration::BufferBlock:
116 case spv::Decoration::GLSLShared:
117 case spv::Decoration::GLSLPacked:
118 case spv::Decoration::CPacked:
119 if (target->opcode() != spv::Op::OpTypeStruct) {
120 return fail(0) << "must be a structure type";
121 }
122 break;
123 case spv::Decoration::ArrayStride:
124 if (target->opcode() != spv::Op::OpTypeArray &&
125 target->opcode() != spv::Op::OpTypeRuntimeArray &&
126 target->opcode() != spv::Op::OpTypePointer) {
127 return fail(0) << "must be an array or pointer type";
128 }
129 break;
130 case spv::Decoration::BuiltIn:
131 if (target->opcode() != spv::Op::OpVariable &&
132 !spvOpcodeIsConstant(target->opcode())) {
133 return _.diag(SPV_ERROR_INVALID_DATA, inst)
134 << "BuiltIns can only target variables, structure members or "
135 "constants";
136 }
137 if (_.HasCapability(spv::Capability::Shader) &&
138 inst->GetOperandAs<spv::BuiltIn>(2) == spv::BuiltIn::WorkgroupSize) {
139 if (!spvOpcodeIsConstant(target->opcode())) {
140 return fail(0) << "must be a constant for WorkgroupSize";
141 }
142 } else if (target->opcode() != spv::Op::OpVariable) {
143 return fail(0) << "must be a variable";
144 }
145 break;
146 case spv::Decoration::NoPerspective:
147 case spv::Decoration::Flat:
148 case spv::Decoration::Patch:
149 case spv::Decoration::Centroid:
150 case spv::Decoration::Sample:
151 case spv::Decoration::Restrict:
152 case spv::Decoration::Aliased:
153 case spv::Decoration::Volatile:
154 case spv::Decoration::Coherent:
155 case spv::Decoration::NonWritable:
156 case spv::Decoration::NonReadable:
157 case spv::Decoration::XfbBuffer:
158 case spv::Decoration::XfbStride:
159 case spv::Decoration::Component:
160 case spv::Decoration::Stream:
161 case spv::Decoration::RestrictPointer:
162 case spv::Decoration::AliasedPointer:
163 if (target->opcode() != spv::Op::OpVariable &&
164 target->opcode() != spv::Op::OpFunctionParameter &&
165 target->opcode() != spv::Op::OpRawAccessChainNV) {
166 return fail(0) << "must be a memory object declaration";
167 }
168 if (_.GetIdOpcode(target->type_id()) != spv::Op::OpTypePointer) {
169 return fail(0) << "must be a pointer type";
170 }
171 break;
172 case spv::Decoration::Invariant:
173 case spv::Decoration::Constant:
174 case spv::Decoration::Location:
175 case spv::Decoration::Index:
176 case spv::Decoration::Binding:
177 case spv::Decoration::DescriptorSet:
178 case spv::Decoration::InputAttachmentIndex:
179 if (target->opcode() != spv::Op::OpVariable) {
180 return fail(0) << "must be a variable";
181 }
182 break;
183 default:
184 break;
185 }
186
187 if (spvIsVulkanEnv(_.context()->target_env)) {
188 // The following were all checked as pointer types above.
189 spv::StorageClass sc = spv::StorageClass::Uniform;
190 const auto type = _.FindDef(target->type_id());
191 if (type && type->operands().size() > 2) {
192 sc = type->GetOperandAs<spv::StorageClass>(1);
193 }
194 switch (dec) {
195 case spv::Decoration::Location:
196 case spv::Decoration::Component:
197 // Location is used for input, output, tile image, and ray tracing
198 // stages.
199 if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output &&
200 sc != spv::StorageClass::RayPayloadKHR &&
201 sc != spv::StorageClass::IncomingRayPayloadKHR &&
202 sc != spv::StorageClass::HitAttributeKHR &&
203 sc != spv::StorageClass::CallableDataKHR &&
204 sc != spv::StorageClass::IncomingCallableDataKHR &&
205 sc != spv::StorageClass::ShaderRecordBufferKHR &&
206 sc != spv::StorageClass::HitObjectAttributeNV &&
207 sc != spv::StorageClass::TileImageEXT) {
208 return _.diag(SPV_ERROR_INVALID_ID, target)
209 << _.VkErrorID(6672) << _.SpvDecorationString(dec)
210 << " decoration must not be applied to this storage class";
211 }
212 break;
213 case spv::Decoration::Index:
214 // Langauge from SPIR-V definition of Index
215 if (sc != spv::StorageClass::Output) {
216 return fail(0) << "must be in the Output storage class";
217 }
218 break;
219 case spv::Decoration::Binding:
220 case spv::Decoration::DescriptorSet:
221 if (sc != spv::StorageClass::StorageBuffer &&
222 sc != spv::StorageClass::Uniform &&
223 sc != spv::StorageClass::UniformConstant) {
224 return fail(6491) << "must be in the StorageBuffer, Uniform, or "
225 "UniformConstant storage class";
226 }
227 break;
228 case spv::Decoration::InputAttachmentIndex:
229 if (sc != spv::StorageClass::UniformConstant) {
230 return fail(6678) << "must be in the UniformConstant storage class";
231 }
232 break;
233 case spv::Decoration::Flat:
234 case spv::Decoration::NoPerspective:
235 case spv::Decoration::Centroid:
236 case spv::Decoration::Sample:
237 if (sc != spv::StorageClass::Input && sc != spv::StorageClass::Output) {
238 return fail(4670) << "storage class must be Input or Output";
239 }
240 break;
241 case spv::Decoration::PerVertexKHR:
242 if (sc != spv::StorageClass::Input) {
243 return fail(6777) << "storage class must be Input";
244 }
245 break;
246 default:
247 break;
248 }
249 }
250 return SPV_SUCCESS;
251 }
252
ValidateDecorate(ValidationState_t & _,const Instruction * inst)253 spv_result_t ValidateDecorate(ValidationState_t& _, const Instruction* inst) {
254 const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
255 const auto target_id = inst->GetOperandAs<uint32_t>(0);
256 const auto target = _.FindDef(target_id);
257 if (!target) {
258 return _.diag(SPV_ERROR_INVALID_ID, inst) << "target is not defined";
259 }
260
261 if (spvIsVulkanEnv(_.context()->target_env)) {
262 if ((decoration == spv::Decoration::GLSLShared) ||
263 (decoration == spv::Decoration::GLSLPacked)) {
264 return _.diag(SPV_ERROR_INVALID_ID, inst)
265 << _.VkErrorID(4669) << "OpDecorate decoration '"
266 << _.SpvDecorationString(decoration)
267 << "' is not valid for the Vulkan execution environment.";
268 }
269 }
270
271 if (decoration == spv::Decoration::FPFastMathMode) {
272 if (_.HasDecoration(target_id, spv::Decoration::NoContraction)) {
273 return _.diag(SPV_ERROR_INVALID_ID, inst)
274 << "FPFastMathMode and NoContraction cannot decorate the same "
275 "target";
276 }
277 auto mask = inst->GetOperandAs<spv::FPFastMathModeMask>(2);
278 if ((mask & spv::FPFastMathModeMask::AllowTransform) !=
279 spv::FPFastMathModeMask::MaskNone &&
280 ((mask & (spv::FPFastMathModeMask::AllowContract |
281 spv::FPFastMathModeMask::AllowReassoc)) !=
282 (spv::FPFastMathModeMask::AllowContract |
283 spv::FPFastMathModeMask::AllowReassoc))) {
284 return _.diag(SPV_ERROR_INVALID_DATA, inst)
285 << "AllowReassoc and AllowContract must be specified when "
286 "AllowTransform is specified";
287 }
288 }
289
290 // This is checked from both sides since we register decorations as we go.
291 if (decoration == spv::Decoration::NoContraction) {
292 if (_.HasDecoration(target_id, spv::Decoration::FPFastMathMode)) {
293 return _.diag(SPV_ERROR_INVALID_ID, inst)
294 << "FPFastMathMode and NoContraction cannot decorate the same "
295 "target";
296 }
297 }
298
299 if (DecorationTakesIdParameters(decoration)) {
300 return _.diag(SPV_ERROR_INVALID_ID, inst)
301 << "Decorations taking ID parameters may not be used with "
302 "OpDecorateId";
303 }
304
305 if (target->opcode() != spv::Op::OpDecorationGroup) {
306 if (IsMemberDecorationOnly(decoration)) {
307 return _.diag(SPV_ERROR_INVALID_ID, inst)
308 << _.SpvDecorationString(decoration)
309 << " can only be applied to structure members";
310 }
311
312 if (auto error = ValidateDecorationTarget(_, decoration, inst, target)) {
313 return error;
314 }
315 }
316
317 // TODO: Add validations for all decorations.
318 return SPV_SUCCESS;
319 }
320
ValidateDecorateId(ValidationState_t & _,const Instruction * inst)321 spv_result_t ValidateDecorateId(ValidationState_t& _, const Instruction* inst) {
322 const auto decoration = inst->GetOperandAs<spv::Decoration>(1);
323 if (!DecorationTakesIdParameters(decoration)) {
324 return _.diag(SPV_ERROR_INVALID_ID, inst)
325 << "Decorations that don't take ID parameters may not be used with "
326 "OpDecorateId";
327 }
328
329 // No member decorations take id parameters, so we don't bother checking if
330 // we are using a member only decoration here.
331
332 // TODO: Add validations for these decorations.
333 // UniformId is covered elsewhere.
334 return SPV_SUCCESS;
335 }
336
ValidateMemberDecorate(ValidationState_t & _,const Instruction * inst)337 spv_result_t ValidateMemberDecorate(ValidationState_t& _,
338 const Instruction* inst) {
339 const auto struct_type_id = inst->GetOperandAs<uint32_t>(0);
340 const auto struct_type = _.FindDef(struct_type_id);
341 if (!struct_type || spv::Op::OpTypeStruct != struct_type->opcode()) {
342 return _.diag(SPV_ERROR_INVALID_ID, inst)
343 << "OpMemberDecorate Structure type <id> "
344 << _.getIdName(struct_type_id) << " is not a struct type.";
345 }
346 const auto member = inst->GetOperandAs<uint32_t>(1);
347 const auto member_count =
348 static_cast<uint32_t>(struct_type->words().size() - 2);
349 if (member_count <= member) {
350 return _.diag(SPV_ERROR_INVALID_ID, inst)
351 << "Index " << member
352 << " provided in OpMemberDecorate for struct <id> "
353 << _.getIdName(struct_type_id)
354 << " is out of bounds. The structure has " << member_count
355 << " members. Largest valid index is " << member_count - 1 << ".";
356 }
357
358 const auto decoration = inst->GetOperandAs<spv::Decoration>(2);
359 if (IsNotMemberDecoration(decoration)) {
360 return _.diag(SPV_ERROR_INVALID_ID, inst)
361 << _.SpvDecorationString(decoration)
362 << " cannot be applied to structure members";
363 }
364
365 return SPV_SUCCESS;
366 }
367
ValidateDecorationGroup(ValidationState_t & _,const Instruction * inst)368 spv_result_t ValidateDecorationGroup(ValidationState_t& _,
369 const Instruction* inst) {
370 const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
371 const auto decoration_group = _.FindDef(decoration_group_id);
372 for (auto pair : decoration_group->uses()) {
373 auto use = pair.first;
374 if (use->opcode() != spv::Op::OpDecorate &&
375 use->opcode() != spv::Op::OpGroupDecorate &&
376 use->opcode() != spv::Op::OpGroupMemberDecorate &&
377 use->opcode() != spv::Op::OpName &&
378 use->opcode() != spv::Op::OpDecorateId && !use->IsNonSemantic()) {
379 return _.diag(SPV_ERROR_INVALID_ID, inst)
380 << "Result id of OpDecorationGroup can only "
381 << "be targeted by OpName, OpGroupDecorate, "
382 << "OpDecorate, OpDecorateId, and OpGroupMemberDecorate";
383 }
384 }
385 return SPV_SUCCESS;
386 }
387
ValidateGroupDecorate(ValidationState_t & _,const Instruction * inst)388 spv_result_t ValidateGroupDecorate(ValidationState_t& _,
389 const Instruction* inst) {
390 const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
391 auto decoration_group = _.FindDef(decoration_group_id);
392 if (!decoration_group ||
393 spv::Op::OpDecorationGroup != decoration_group->opcode()) {
394 return _.diag(SPV_ERROR_INVALID_ID, inst)
395 << "OpGroupDecorate Decoration group <id> "
396 << _.getIdName(decoration_group_id) << " is not a decoration group.";
397 }
398 for (unsigned i = 1; i < inst->operands().size(); ++i) {
399 auto target_id = inst->GetOperandAs<uint32_t>(i);
400 auto target = _.FindDef(target_id);
401 if (!target || target->opcode() == spv::Op::OpDecorationGroup) {
402 return _.diag(SPV_ERROR_INVALID_ID, inst)
403 << "OpGroupDecorate may not target OpDecorationGroup <id> "
404 << _.getIdName(target_id);
405 }
406 }
407 return SPV_SUCCESS;
408 }
409
ValidateGroupMemberDecorate(ValidationState_t & _,const Instruction * inst)410 spv_result_t ValidateGroupMemberDecorate(ValidationState_t& _,
411 const Instruction* inst) {
412 const auto decoration_group_id = inst->GetOperandAs<uint32_t>(0);
413 const auto decoration_group = _.FindDef(decoration_group_id);
414 if (!decoration_group ||
415 spv::Op::OpDecorationGroup != decoration_group->opcode()) {
416 return _.diag(SPV_ERROR_INVALID_ID, inst)
417 << "OpGroupMemberDecorate Decoration group <id> "
418 << _.getIdName(decoration_group_id) << " is not a decoration group.";
419 }
420 // Grammar checks ensures that the number of arguments to this instruction
421 // is an odd number: 1 decoration group + (id,literal) pairs.
422 for (size_t i = 1; i + 1 < inst->operands().size(); i += 2) {
423 const uint32_t struct_id = inst->GetOperandAs<uint32_t>(i);
424 const uint32_t index = inst->GetOperandAs<uint32_t>(i + 1);
425 auto struct_instr = _.FindDef(struct_id);
426 if (!struct_instr || spv::Op::OpTypeStruct != struct_instr->opcode()) {
427 return _.diag(SPV_ERROR_INVALID_ID, inst)
428 << "OpGroupMemberDecorate Structure type <id> "
429 << _.getIdName(struct_id) << " is not a struct type.";
430 }
431 const uint32_t num_struct_members =
432 static_cast<uint32_t>(struct_instr->words().size() - 2);
433 if (index >= num_struct_members) {
434 return _.diag(SPV_ERROR_INVALID_ID, inst)
435 << "Index " << index
436 << " provided in OpGroupMemberDecorate for struct <id> "
437 << _.getIdName(struct_id)
438 << " is out of bounds. The structure has " << num_struct_members
439 << " members. Largest valid index is " << num_struct_members - 1
440 << ".";
441 }
442 }
443 return SPV_SUCCESS;
444 }
445
446 // Registers necessary decoration(s) for the appropriate IDs based on the
447 // instruction.
RegisterDecorations(ValidationState_t & _,const Instruction * inst)448 spv_result_t RegisterDecorations(ValidationState_t& _,
449 const Instruction* inst) {
450 switch (inst->opcode()) {
451 case spv::Op::OpDecorate:
452 case spv::Op::OpDecorateId: {
453 const uint32_t target_id = inst->word(1);
454 const spv::Decoration dec_type =
455 static_cast<spv::Decoration>(inst->word(2));
456 std::vector<uint32_t> dec_params;
457 if (inst->words().size() > 3) {
458 dec_params.insert(dec_params.end(), inst->words().begin() + 3,
459 inst->words().end());
460 }
461 _.RegisterDecorationForId(target_id, Decoration(dec_type, dec_params));
462 break;
463 }
464 case spv::Op::OpMemberDecorate: {
465 const uint32_t struct_id = inst->word(1);
466 const uint32_t index = inst->word(2);
467 const spv::Decoration dec_type =
468 static_cast<spv::Decoration>(inst->word(3));
469 std::vector<uint32_t> dec_params;
470 if (inst->words().size() > 4) {
471 dec_params.insert(dec_params.end(), inst->words().begin() + 4,
472 inst->words().end());
473 }
474 _.RegisterDecorationForId(struct_id,
475 Decoration(dec_type, dec_params, index));
476 break;
477 }
478 case spv::Op::OpDecorationGroup: {
479 // We don't need to do anything right now. Assigning decorations to groups
480 // will be taken care of via OpGroupDecorate.
481 break;
482 }
483 case spv::Op::OpGroupDecorate: {
484 // Word 1 is the group <id>. All subsequent words are target <id>s that
485 // are going to be decorated with the decorations.
486 const uint32_t decoration_group_id = inst->word(1);
487 std::set<Decoration>& group_decorations =
488 _.id_decorations(decoration_group_id);
489 for (size_t i = 2; i < inst->words().size(); ++i) {
490 const uint32_t target_id = inst->word(i);
491 _.RegisterDecorationsForId(target_id, group_decorations.begin(),
492 group_decorations.end());
493 }
494 break;
495 }
496 case spv::Op::OpGroupMemberDecorate: {
497 // Word 1 is the Decoration Group <id> followed by (struct<id>,literal)
498 // pairs. All decorations of the group should be applied to all the struct
499 // members that are specified in the instructions.
500 const uint32_t decoration_group_id = inst->word(1);
501 std::set<Decoration>& group_decorations =
502 _.id_decorations(decoration_group_id);
503 // Grammar checks ensures that the number of arguments to this instruction
504 // is an odd number: 1 decoration group + (id,literal) pairs.
505 for (size_t i = 2; i + 1 < inst->words().size(); i = i + 2) {
506 const uint32_t struct_id = inst->word(i);
507 const uint32_t index = inst->word(i + 1);
508 // ID validation phase ensures this is in fact a struct instruction and
509 // that the index is not out of bound.
510 _.RegisterDecorationsForStructMember(struct_id, index,
511 group_decorations.begin(),
512 group_decorations.end());
513 }
514 break;
515 }
516 default:
517 break;
518 }
519 return SPV_SUCCESS;
520 }
521
522 } // namespace
523
AnnotationPass(ValidationState_t & _,const Instruction * inst)524 spv_result_t AnnotationPass(ValidationState_t& _, const Instruction* inst) {
525 switch (inst->opcode()) {
526 case spv::Op::OpDecorate:
527 if (auto error = ValidateDecorate(_, inst)) return error;
528 break;
529 case spv::Op::OpDecorateId:
530 if (auto error = ValidateDecorateId(_, inst)) return error;
531 break;
532 // TODO(dneto): spv::Op::OpDecorateStringGOOGLE
533 // See https://github.com/KhronosGroup/SPIRV-Tools/issues/2253
534 case spv::Op::OpMemberDecorate:
535 if (auto error = ValidateMemberDecorate(_, inst)) return error;
536 break;
537 case spv::Op::OpDecorationGroup:
538 if (auto error = ValidateDecorationGroup(_, inst)) return error;
539 break;
540 case spv::Op::OpGroupDecorate:
541 if (auto error = ValidateGroupDecorate(_, inst)) return error;
542 break;
543 case spv::Op::OpGroupMemberDecorate:
544 if (auto error = ValidateGroupMemberDecorate(_, inst)) return error;
545 break;
546 default:
547 break;
548 }
549
550 // In order to validate decoration rules, we need to know all the decorations
551 // that are applied to any given <id>.
552 RegisterDecorations(_, inst);
553
554 return SPV_SUCCESS;
555 }
556
557 } // namespace val
558 } // namespace spvtools
559