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
21namespace spvtools {
22namespace val {
23namespace {
24
25bool 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
55std::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.
189bool 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
202spv_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
231spv_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
243spv_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
275spv_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
300spv_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
328spv_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.
372spv_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
446spv_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