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/val/validate.h"
16
17 #include <algorithm>
18
19 #include "source/opcode.h"
20 #include "source/spirv_target_env.h"
21 #include "source/val/instruction.h"
22 #include "source/val/validation_state.h"
23
24 namespace spvtools {
25 namespace val {
26 namespace {
27
ValidateEntryPoint(ValidationState_t & _,const Instruction * inst)28 spv_result_t ValidateEntryPoint(ValidationState_t& _, const Instruction* inst) {
29 const auto entry_point_id = inst->GetOperandAs<uint32_t>(1);
30 auto entry_point = _.FindDef(entry_point_id);
31 if (!entry_point || SpvOpFunction != entry_point->opcode()) {
32 return _.diag(SPV_ERROR_INVALID_ID, inst)
33 << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
34 << "' is not a function.";
35 }
36 // don't check kernel function signatures
37 const SpvExecutionModel execution_model =
38 inst->GetOperandAs<SpvExecutionModel>(0);
39 if (execution_model != SpvExecutionModelKernel) {
40 // TODO: Check the entry point signature is void main(void), may be subject
41 // to change
42 const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3);
43 const auto entry_point_type = _.FindDef(entry_point_type_id);
44 if (!entry_point_type || 3 != entry_point_type->words().size()) {
45 return _.diag(SPV_ERROR_INVALID_ID, inst)
46 << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
47 << "'s function parameter count is not zero.";
48 }
49 }
50
51 auto return_type = _.FindDef(entry_point->type_id());
52 if (!return_type || SpvOpTypeVoid != return_type->opcode()) {
53 return _.diag(SPV_ERROR_INVALID_ID, inst)
54 << "OpEntryPoint Entry Point <id> '" << _.getIdName(entry_point_id)
55 << "'s function return type is not void.";
56 }
57
58 const auto* execution_modes = _.GetExecutionModes(entry_point_id);
59 if (_.HasCapability(SpvCapabilityShader)) {
60 switch (execution_model) {
61 case SpvExecutionModelFragment:
62 if (execution_modes &&
63 execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
64 execution_modes->count(SpvExecutionModeOriginLowerLeft)) {
65 return _.diag(SPV_ERROR_INVALID_DATA, inst)
66 << "Fragment execution model entry points can only specify "
67 "one of OriginUpperLeft or OriginLowerLeft execution "
68 "modes.";
69 }
70 if (!execution_modes ||
71 (!execution_modes->count(SpvExecutionModeOriginUpperLeft) &&
72 !execution_modes->count(SpvExecutionModeOriginLowerLeft))) {
73 return _.diag(SPV_ERROR_INVALID_DATA, inst)
74 << "Fragment execution model entry points require either an "
75 "OriginUpperLeft or OriginLowerLeft execution mode.";
76 }
77 if (execution_modes &&
78 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
79 [](const SpvExecutionMode& mode) {
80 switch (mode) {
81 case SpvExecutionModeDepthGreater:
82 case SpvExecutionModeDepthLess:
83 case SpvExecutionModeDepthUnchanged:
84 return true;
85 default:
86 return false;
87 }
88 })) {
89 return _.diag(SPV_ERROR_INVALID_DATA, inst)
90 << "Fragment execution model entry points can specify at most "
91 "one of DepthGreater, DepthLess or DepthUnchanged "
92 "execution modes.";
93 }
94 break;
95 case SpvExecutionModelTessellationControl:
96 case SpvExecutionModelTessellationEvaluation:
97 if (execution_modes &&
98 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
99 [](const SpvExecutionMode& mode) {
100 switch (mode) {
101 case SpvExecutionModeSpacingEqual:
102 case SpvExecutionModeSpacingFractionalEven:
103 case SpvExecutionModeSpacingFractionalOdd:
104 return true;
105 default:
106 return false;
107 }
108 })) {
109 return _.diag(SPV_ERROR_INVALID_DATA, inst)
110 << "Tessellation execution model entry points can specify at "
111 "most one of SpacingEqual, SpacingFractionalOdd or "
112 "SpacingFractionalEven execution modes.";
113 }
114 if (execution_modes &&
115 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
116 [](const SpvExecutionMode& mode) {
117 switch (mode) {
118 case SpvExecutionModeTriangles:
119 case SpvExecutionModeQuads:
120 case SpvExecutionModeIsolines:
121 return true;
122 default:
123 return false;
124 }
125 })) {
126 return _.diag(SPV_ERROR_INVALID_DATA, inst)
127 << "Tessellation execution model entry points can specify at "
128 "most one of Triangles, Quads or Isolines execution modes.";
129 }
130 if (execution_modes &&
131 1 < std::count_if(execution_modes->begin(), execution_modes->end(),
132 [](const SpvExecutionMode& mode) {
133 switch (mode) {
134 case SpvExecutionModeVertexOrderCw:
135 case SpvExecutionModeVertexOrderCcw:
136 return true;
137 default:
138 return false;
139 }
140 })) {
141 return _.diag(SPV_ERROR_INVALID_DATA, inst)
142 << "Tessellation execution model entry points can specify at "
143 "most one of VertexOrderCw or VertexOrderCcw execution "
144 "modes.";
145 }
146 break;
147 case SpvExecutionModelGeometry:
148 if (!execution_modes ||
149 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
150 [](const SpvExecutionMode& mode) {
151 switch (mode) {
152 case SpvExecutionModeInputPoints:
153 case SpvExecutionModeInputLines:
154 case SpvExecutionModeInputLinesAdjacency:
155 case SpvExecutionModeTriangles:
156 case SpvExecutionModeInputTrianglesAdjacency:
157 return true;
158 default:
159 return false;
160 }
161 })) {
162 return _.diag(SPV_ERROR_INVALID_DATA, inst)
163 << "Geometry execution model entry points must specify "
164 "exactly one of InputPoints, InputLines, "
165 "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "
166 "execution modes.";
167 }
168 if (!execution_modes ||
169 1 != std::count_if(execution_modes->begin(), execution_modes->end(),
170 [](const SpvExecutionMode& mode) {
171 switch (mode) {
172 case SpvExecutionModeOutputPoints:
173 case SpvExecutionModeOutputLineStrip:
174 case SpvExecutionModeOutputTriangleStrip:
175 return true;
176 default:
177 return false;
178 }
179 })) {
180 return _.diag(SPV_ERROR_INVALID_DATA, inst)
181 << "Geometry execution model entry points must specify "
182 "exactly one of OutputPoints, OutputLineStrip or "
183 "OutputTriangleStrip execution modes.";
184 }
185 break;
186 default:
187 break;
188 }
189 }
190
191 if (spvIsVulkanEnv(_.context()->target_env)) {
192 switch (execution_model) {
193 case SpvExecutionModelGLCompute:
194 if (!execution_modes ||
195 !execution_modes->count(SpvExecutionModeLocalSize)) {
196 bool ok = false;
197 for (auto& i : _.ordered_instructions()) {
198 if (i.opcode() == SpvOpDecorate) {
199 if (i.operands().size() > 2) {
200 if (i.GetOperandAs<SpvDecoration>(1) == SpvDecorationBuiltIn &&
201 i.GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) {
202 ok = true;
203 break;
204 }
205 }
206 }
207 }
208 if (!ok) {
209 return _.diag(SPV_ERROR_INVALID_DATA, inst)
210 << "In the Vulkan environment, GLCompute execution model "
211 "entry points require either the LocalSize execution "
212 "mode or an object decorated with WorkgroupSize must be "
213 "specified.";
214 }
215 }
216 break;
217 default:
218 break;
219 }
220 }
221
222 return SPV_SUCCESS;
223 }
224
ValidateExecutionMode(ValidationState_t & _,const Instruction * inst)225 spv_result_t ValidateExecutionMode(ValidationState_t& _,
226 const Instruction* inst) {
227 const auto entry_point_id = inst->GetOperandAs<uint32_t>(0);
228 const auto found = std::find(_.entry_points().cbegin(),
229 _.entry_points().cend(), entry_point_id);
230 if (found == _.entry_points().cend()) {
231 return _.diag(SPV_ERROR_INVALID_ID, inst)
232 << "OpExecutionMode Entry Point <id> '"
233 << _.getIdName(entry_point_id)
234 << "' is not the Entry Point "
235 "operand of an OpEntryPoint.";
236 }
237
238 const auto mode = inst->GetOperandAs<SpvExecutionMode>(1);
239 const auto* models = _.GetExecutionModels(entry_point_id);
240 switch (mode) {
241 case SpvExecutionModeInvocations:
242 case SpvExecutionModeInputPoints:
243 case SpvExecutionModeInputLines:
244 case SpvExecutionModeInputLinesAdjacency:
245 case SpvExecutionModeInputTrianglesAdjacency:
246 case SpvExecutionModeOutputLineStrip:
247 case SpvExecutionModeOutputTriangleStrip:
248 if (!std::all_of(models->begin(), models->end(),
249 [](const SpvExecutionModel& model) {
250 return model == SpvExecutionModelGeometry;
251 })) {
252 return _.diag(SPV_ERROR_INVALID_DATA, inst)
253 << "Execution mode can only be used with the Geometry execution "
254 "model.";
255 }
256 break;
257 case SpvExecutionModeOutputPoints:
258 if (!std::all_of(models->begin(), models->end(),
259 [&_](const SpvExecutionModel& model) {
260 switch (model) {
261 case SpvExecutionModelGeometry:
262 return true;
263 case SpvExecutionModelMeshNV:
264 return _.HasCapability(SpvCapabilityMeshShadingNV);
265 default:
266 return false;
267 }
268 })) {
269 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
270 return _.diag(SPV_ERROR_INVALID_DATA, inst)
271 << "Execution mode can only be used with the Geometry or "
272 "MeshNV execution model.";
273 } else {
274 return _.diag(SPV_ERROR_INVALID_DATA, inst)
275 << "Execution mode can only be used with the Geometry "
276 "execution "
277 "model.";
278 }
279 }
280 break;
281 case SpvExecutionModeSpacingEqual:
282 case SpvExecutionModeSpacingFractionalEven:
283 case SpvExecutionModeSpacingFractionalOdd:
284 case SpvExecutionModeVertexOrderCw:
285 case SpvExecutionModeVertexOrderCcw:
286 case SpvExecutionModePointMode:
287 case SpvExecutionModeQuads:
288 case SpvExecutionModeIsolines:
289 if (!std::all_of(
290 models->begin(), models->end(),
291 [](const SpvExecutionModel& model) {
292 return (model == SpvExecutionModelTessellationControl) ||
293 (model == SpvExecutionModelTessellationEvaluation);
294 })) {
295 return _.diag(SPV_ERROR_INVALID_DATA, inst)
296 << "Execution mode can only be used with a tessellation "
297 "execution model.";
298 }
299 break;
300 case SpvExecutionModeTriangles:
301 if (!std::all_of(models->begin(), models->end(),
302 [](const SpvExecutionModel& model) {
303 switch (model) {
304 case SpvExecutionModelGeometry:
305 case SpvExecutionModelTessellationControl:
306 case SpvExecutionModelTessellationEvaluation:
307 return true;
308 default:
309 return false;
310 }
311 })) {
312 return _.diag(SPV_ERROR_INVALID_DATA, inst)
313 << "Execution mode can only be used with a Geometry or "
314 "tessellation execution model.";
315 }
316 break;
317 case SpvExecutionModeOutputVertices:
318 if (!std::all_of(models->begin(), models->end(),
319 [&_](const SpvExecutionModel& model) {
320 switch (model) {
321 case SpvExecutionModelGeometry:
322 case SpvExecutionModelTessellationControl:
323 case SpvExecutionModelTessellationEvaluation:
324 return true;
325 case SpvExecutionModelMeshNV:
326 return _.HasCapability(SpvCapabilityMeshShadingNV);
327 default:
328 return false;
329 }
330 })) {
331 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
332 return _.diag(SPV_ERROR_INVALID_DATA, inst)
333 << "Execution mode can only be used with a Geometry, "
334 "tessellation or MeshNV execution model.";
335 } else {
336 return _.diag(SPV_ERROR_INVALID_DATA, inst)
337 << "Execution mode can only be used with a Geometry or "
338 "tessellation execution model.";
339 }
340 }
341 break;
342 case SpvExecutionModePixelCenterInteger:
343 case SpvExecutionModeOriginUpperLeft:
344 case SpvExecutionModeOriginLowerLeft:
345 case SpvExecutionModeEarlyFragmentTests:
346 case SpvExecutionModeDepthReplacing:
347 case SpvExecutionModeDepthLess:
348 case SpvExecutionModeDepthUnchanged:
349 if (!std::all_of(models->begin(), models->end(),
350 [](const SpvExecutionModel& model) {
351 return model == SpvExecutionModelFragment;
352 })) {
353 return _.diag(SPV_ERROR_INVALID_DATA, inst)
354 << "Execution mode can only be used with the Fragment execution "
355 "model.";
356 }
357 break;
358 case SpvExecutionModeLocalSizeHint:
359 case SpvExecutionModeVecTypeHint:
360 case SpvExecutionModeContractionOff:
361 case SpvExecutionModeLocalSizeHintId:
362 if (!std::all_of(models->begin(), models->end(),
363 [](const SpvExecutionModel& model) {
364 return model == SpvExecutionModelKernel;
365 })) {
366 return _.diag(SPV_ERROR_INVALID_DATA, inst)
367 << "Execution mode can only be used with the Kernel execution "
368 "model.";
369 }
370 break;
371 case SpvExecutionModeLocalSize:
372 case SpvExecutionModeLocalSizeId:
373 if (!std::all_of(models->begin(), models->end(),
374 [&_](const SpvExecutionModel& model) {
375 switch (model) {
376 case SpvExecutionModelKernel:
377 case SpvExecutionModelGLCompute:
378 return true;
379 case SpvExecutionModelTaskNV:
380 case SpvExecutionModelMeshNV:
381 return _.HasCapability(SpvCapabilityMeshShadingNV);
382 default:
383 return false;
384 }
385 })) {
386 if (_.HasCapability(SpvCapabilityMeshShadingNV)) {
387 return _.diag(SPV_ERROR_INVALID_DATA, inst)
388 << "Execution mode can only be used with a Kernel, GLCompute, "
389 "MeshNV, or TaskNV execution model.";
390 } else {
391 return _.diag(SPV_ERROR_INVALID_DATA, inst)
392 << "Execution mode can only be used with a Kernel or "
393 "GLCompute "
394 "execution model.";
395 }
396 }
397 default:
398 break;
399 }
400
401 if (spvIsVulkanEnv(_.context()->target_env)) {
402 if (mode == SpvExecutionModeOriginLowerLeft) {
403 return _.diag(SPV_ERROR_INVALID_DATA, inst)
404 << "In the Vulkan environment, the OriginLowerLeft execution mode "
405 "must not be used.";
406 }
407 if (mode == SpvExecutionModePixelCenterInteger) {
408 return _.diag(SPV_ERROR_INVALID_DATA, inst)
409 << "In the Vulkan environment, the PixelCenterInteger execution "
410 "mode must not be used.";
411 }
412 }
413
414 return SPV_SUCCESS;
415 }
416
417 } // namespace
418
ModeSettingPass(ValidationState_t & _,const Instruction * inst)419 spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) {
420 switch (inst->opcode()) {
421 case SpvOpEntryPoint:
422 if (auto error = ValidateEntryPoint(_, inst)) return error;
423 break;
424 case SpvOpExecutionMode:
425 case SpvOpExecutionModeId:
426 if (auto error = ValidateExecutionMode(_, inst)) return error;
427 break;
428 default:
429 break;
430 }
431 return SPV_SUCCESS;
432 }
433
434 } // namespace val
435 } // namespace spvtools
436