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 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 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 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