| 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 |  | 
| 37 |   // Only check the shader execution models | 
| 38 |   const SpvExecutionModel execution_model = | 
| 39 |       inst->GetOperandAs<SpvExecutionModel>(0); | 
| 40 |   if (execution_model != SpvExecutionModelKernel) { | 
| 41 |     const auto entry_point_type_id = entry_point->GetOperandAs<uint32_t>(3); | 
| 42 |     const auto entry_point_type = _.FindDef(entry_point_type_id); | 
| 43 |     if (!entry_point_type || 3 != entry_point_type->words().size()) { | 
| 44 |       return _.diag(SPV_ERROR_INVALID_ID, inst) | 
| 45 |              << "OpEntryPoint Entry Point <id> '"  << _.getIdName(entry_point_id) | 
| 46 |              << "'s function parameter count is not zero." ; | 
| 47 |     } | 
| 48 |   } | 
| 49 |  | 
| 50 |   auto return_type = _.FindDef(entry_point->type_id()); | 
| 51 |   if (!return_type || SpvOpTypeVoid != return_type->opcode()) { | 
| 52 |     return _.diag(SPV_ERROR_INVALID_ID, inst) | 
| 53 |            << "OpEntryPoint Entry Point <id> '"  << _.getIdName(entry_point_id) | 
| 54 |            << "'s function return type is not void." ; | 
| 55 |   } | 
| 56 |  | 
| 57 |   const auto* execution_modes = _.GetExecutionModes(entry_point_id); | 
| 58 |   if (_.HasCapability(SpvCapabilityShader)) { | 
| 59 |     switch (execution_model) { | 
| 60 |       case SpvExecutionModelFragment: | 
| 61 |         if (execution_modes && | 
| 62 |             execution_modes->count(SpvExecutionModeOriginUpperLeft) && | 
| 63 |             execution_modes->count(SpvExecutionModeOriginLowerLeft)) { | 
| 64 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 65 |                  << "Fragment execution model entry points can only specify "  | 
| 66 |                     "one of OriginUpperLeft or OriginLowerLeft execution "  | 
| 67 |                     "modes." ; | 
| 68 |         } | 
| 69 |         if (!execution_modes || | 
| 70 |             (!execution_modes->count(SpvExecutionModeOriginUpperLeft) && | 
| 71 |              !execution_modes->count(SpvExecutionModeOriginLowerLeft))) { | 
| 72 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 73 |                  << "Fragment execution model entry points require either an "  | 
| 74 |                     "OriginUpperLeft or OriginLowerLeft execution mode." ; | 
| 75 |         } | 
| 76 |         if (execution_modes && | 
| 77 |             1 < std::count_if(execution_modes->begin(), execution_modes->end(), | 
| 78 |                               [](const SpvExecutionMode& mode) { | 
| 79 |                                 switch (mode) { | 
| 80 |                                   case SpvExecutionModeDepthGreater: | 
| 81 |                                   case SpvExecutionModeDepthLess: | 
| 82 |                                   case SpvExecutionModeDepthUnchanged: | 
| 83 |                                     return true; | 
| 84 |                                   default: | 
| 85 |                                     return false; | 
| 86 |                                 } | 
| 87 |                               })) { | 
| 88 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 89 |                  << "Fragment execution model entry points can specify at most "  | 
| 90 |                     "one of DepthGreater, DepthLess or DepthUnchanged "  | 
| 91 |                     "execution modes." ; | 
| 92 |         } | 
| 93 |         if (execution_modes && | 
| 94 |             1 < std::count_if( | 
| 95 |                     execution_modes->begin(), execution_modes->end(), | 
| 96 |                     [](const SpvExecutionMode& mode) { | 
| 97 |                       switch (mode) { | 
| 98 |                         case SpvExecutionModePixelInterlockOrderedEXT: | 
| 99 |                         case SpvExecutionModePixelInterlockUnorderedEXT: | 
| 100 |                         case SpvExecutionModeSampleInterlockOrderedEXT: | 
| 101 |                         case SpvExecutionModeSampleInterlockUnorderedEXT: | 
| 102 |                         case SpvExecutionModeShadingRateInterlockOrderedEXT: | 
| 103 |                         case SpvExecutionModeShadingRateInterlockUnorderedEXT: | 
| 104 |                           return true; | 
| 105 |                         default: | 
| 106 |                           return false; | 
| 107 |                       } | 
| 108 |                     })) { | 
| 109 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 110 |                  << "Fragment execution model entry points can specify at most "  | 
| 111 |                     "one fragment shader interlock execution mode." ; | 
| 112 |         } | 
| 113 |         break; | 
| 114 |       case SpvExecutionModelTessellationControl: | 
| 115 |       case SpvExecutionModelTessellationEvaluation: | 
| 116 |         if (execution_modes && | 
| 117 |             1 < std::count_if(execution_modes->begin(), execution_modes->end(), | 
| 118 |                               [](const SpvExecutionMode& mode) { | 
| 119 |                                 switch (mode) { | 
| 120 |                                   case SpvExecutionModeSpacingEqual: | 
| 121 |                                   case SpvExecutionModeSpacingFractionalEven: | 
| 122 |                                   case SpvExecutionModeSpacingFractionalOdd: | 
| 123 |                                     return true; | 
| 124 |                                   default: | 
| 125 |                                     return false; | 
| 126 |                                 } | 
| 127 |                               })) { | 
| 128 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 129 |                  << "Tessellation execution model entry points can specify at "  | 
| 130 |                     "most one of SpacingEqual, SpacingFractionalOdd or "  | 
| 131 |                     "SpacingFractionalEven execution modes." ; | 
| 132 |         } | 
| 133 |         if (execution_modes && | 
| 134 |             1 < std::count_if(execution_modes->begin(), execution_modes->end(), | 
| 135 |                               [](const SpvExecutionMode& mode) { | 
| 136 |                                 switch (mode) { | 
| 137 |                                   case SpvExecutionModeTriangles: | 
| 138 |                                   case SpvExecutionModeQuads: | 
| 139 |                                   case SpvExecutionModeIsolines: | 
| 140 |                                     return true; | 
| 141 |                                   default: | 
| 142 |                                     return false; | 
| 143 |                                 } | 
| 144 |                               })) { | 
| 145 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 146 |                  << "Tessellation execution model entry points can specify at "  | 
| 147 |                     "most one of Triangles, Quads or Isolines execution modes." ; | 
| 148 |         } | 
| 149 |         if (execution_modes && | 
| 150 |             1 < std::count_if(execution_modes->begin(), execution_modes->end(), | 
| 151 |                               [](const SpvExecutionMode& mode) { | 
| 152 |                                 switch (mode) { | 
| 153 |                                   case SpvExecutionModeVertexOrderCw: | 
| 154 |                                   case SpvExecutionModeVertexOrderCcw: | 
| 155 |                                     return true; | 
| 156 |                                   default: | 
| 157 |                                     return false; | 
| 158 |                                 } | 
| 159 |                               })) { | 
| 160 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 161 |                  << "Tessellation execution model entry points can specify at "  | 
| 162 |                     "most one of VertexOrderCw or VertexOrderCcw execution "  | 
| 163 |                     "modes." ; | 
| 164 |         } | 
| 165 |         break; | 
| 166 |       case SpvExecutionModelGeometry: | 
| 167 |         if (!execution_modes || | 
| 168 |             1 != std::count_if(execution_modes->begin(), execution_modes->end(), | 
| 169 |                                [](const SpvExecutionMode& mode) { | 
| 170 |                                  switch (mode) { | 
| 171 |                                    case SpvExecutionModeInputPoints: | 
| 172 |                                    case SpvExecutionModeInputLines: | 
| 173 |                                    case SpvExecutionModeInputLinesAdjacency: | 
| 174 |                                    case SpvExecutionModeTriangles: | 
| 175 |                                    case SpvExecutionModeInputTrianglesAdjacency: | 
| 176 |                                      return true; | 
| 177 |                                    default: | 
| 178 |                                      return false; | 
| 179 |                                  } | 
| 180 |                                })) { | 
| 181 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 182 |                  << "Geometry execution model entry points must specify "  | 
| 183 |                     "exactly one of InputPoints, InputLines, "  | 
| 184 |                     "InputLinesAdjacency, Triangles or InputTrianglesAdjacency "  | 
| 185 |                     "execution modes." ; | 
| 186 |         } | 
| 187 |         if (!execution_modes || | 
| 188 |             1 != std::count_if(execution_modes->begin(), execution_modes->end(), | 
| 189 |                                [](const SpvExecutionMode& mode) { | 
| 190 |                                  switch (mode) { | 
| 191 |                                    case SpvExecutionModeOutputPoints: | 
| 192 |                                    case SpvExecutionModeOutputLineStrip: | 
| 193 |                                    case SpvExecutionModeOutputTriangleStrip: | 
| 194 |                                      return true; | 
| 195 |                                    default: | 
| 196 |                                      return false; | 
| 197 |                                  } | 
| 198 |                                })) { | 
| 199 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 200 |                  << "Geometry execution model entry points must specify "  | 
| 201 |                     "exactly one of OutputPoints, OutputLineStrip or "  | 
| 202 |                     "OutputTriangleStrip execution modes." ; | 
| 203 |         } | 
| 204 |         break; | 
| 205 |       default: | 
| 206 |         break; | 
| 207 |     } | 
| 208 |   } | 
| 209 |  | 
| 210 |   if (spvIsVulkanEnv(_.context()->target_env)) { | 
| 211 |     switch (execution_model) { | 
| 212 |       case SpvExecutionModelGLCompute: | 
| 213 |         if (!execution_modes || | 
| 214 |             !execution_modes->count(SpvExecutionModeLocalSize)) { | 
| 215 |           bool ok = false; | 
| 216 |           for (auto& i : _.ordered_instructions()) { | 
| 217 |             if (i.opcode() == SpvOpDecorate) { | 
| 218 |               if (i.operands().size() > 2) { | 
| 219 |                 if (i.GetOperandAs<SpvDecoration>(1) == SpvDecorationBuiltIn && | 
| 220 |                     i.GetOperandAs<SpvBuiltIn>(2) == SpvBuiltInWorkgroupSize) { | 
| 221 |                   ok = true; | 
| 222 |                   break; | 
| 223 |                 } | 
| 224 |               } | 
| 225 |             } | 
| 226 |           } | 
| 227 |           if (!ok) { | 
| 228 |             return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 229 |                    << "In the Vulkan environment, GLCompute execution model "  | 
| 230 |                       "entry points require either the LocalSize execution "  | 
| 231 |                       "mode or an object decorated with WorkgroupSize must be "  | 
| 232 |                       "specified." ; | 
| 233 |           } | 
| 234 |         } | 
| 235 |         break; | 
| 236 |       default: | 
| 237 |         break; | 
| 238 |     } | 
| 239 |   } | 
| 240 |  | 
| 241 |   return SPV_SUCCESS; | 
| 242 | } | 
| 243 |  | 
| 244 | spv_result_t ValidateExecutionMode(ValidationState_t& _, | 
| 245 |                                    const Instruction* inst) { | 
| 246 |   const auto entry_point_id = inst->GetOperandAs<uint32_t>(0); | 
| 247 |   const auto found = std::find(_.entry_points().cbegin(), | 
| 248 |                                _.entry_points().cend(), entry_point_id); | 
| 249 |   if (found == _.entry_points().cend()) { | 
| 250 |     return _.diag(SPV_ERROR_INVALID_ID, inst) | 
| 251 |            << "OpExecutionMode Entry Point <id> '"  | 
| 252 |            << _.getIdName(entry_point_id) | 
| 253 |            << "' is not the Entry Point "  | 
| 254 |               "operand of an OpEntryPoint." ; | 
| 255 |   } | 
| 256 |  | 
| 257 |   const auto mode = inst->GetOperandAs<SpvExecutionMode>(1); | 
| 258 |   if (inst->opcode() == SpvOpExecutionModeId) { | 
| 259 |     size_t operand_count = inst->operands().size(); | 
| 260 |     for (size_t i = 2; i < operand_count; ++i) { | 
| 261 |       const auto operand_id = inst->GetOperandAs<uint32_t>(2); | 
| 262 |       const auto* operand_inst = _.FindDef(operand_id); | 
| 263 |       if (mode == SpvExecutionModeSubgroupsPerWorkgroupId || | 
| 264 |           mode == SpvExecutionModeLocalSizeHintId || | 
| 265 |           mode == SpvExecutionModeLocalSizeId) { | 
| 266 |         if (!spvOpcodeIsConstant(operand_inst->opcode())) { | 
| 267 |           return _.diag(SPV_ERROR_INVALID_ID, inst) | 
| 268 |                  << "For OpExecutionModeId all Extra Operand ids must be "  | 
| 269 |                     "constant "  | 
| 270 |                     "instructions." ; | 
| 271 |         } | 
| 272 |       } else { | 
| 273 |         return _.diag(SPV_ERROR_INVALID_ID, inst) | 
| 274 |                << "OpExecutionModeId is only valid when the Mode operand is an "  | 
| 275 |                   "execution mode that takes Extra Operands that are id "  | 
| 276 |                   "operands." ; | 
| 277 |       } | 
| 278 |     } | 
| 279 |   } else if (mode == SpvExecutionModeSubgroupsPerWorkgroupId || | 
| 280 |              mode == SpvExecutionModeLocalSizeHintId || | 
| 281 |              mode == SpvExecutionModeLocalSizeId) { | 
| 282 |     return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 283 |            << "OpExecutionMode is only valid when the Mode operand is an "  | 
| 284 |               "execution mode that takes no Extra Operands, or takes Extra "  | 
| 285 |               "Operands that are not id operands." ; | 
| 286 |   } | 
| 287 |  | 
| 288 |   const auto* models = _.GetExecutionModels(entry_point_id); | 
| 289 |   switch (mode) { | 
| 290 |     case SpvExecutionModeInvocations: | 
| 291 |     case SpvExecutionModeInputPoints: | 
| 292 |     case SpvExecutionModeInputLines: | 
| 293 |     case SpvExecutionModeInputLinesAdjacency: | 
| 294 |     case SpvExecutionModeInputTrianglesAdjacency: | 
| 295 |     case SpvExecutionModeOutputLineStrip: | 
| 296 |     case SpvExecutionModeOutputTriangleStrip: | 
| 297 |       if (!std::all_of(models->begin(), models->end(), | 
| 298 |                        [](const SpvExecutionModel& model) { | 
| 299 |                          return model == SpvExecutionModelGeometry; | 
| 300 |                        })) { | 
| 301 |         return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 302 |                << "Execution mode can only be used with the Geometry execution "  | 
| 303 |                   "model." ; | 
| 304 |       } | 
| 305 |       break; | 
| 306 |     case SpvExecutionModeOutputPoints: | 
| 307 |       if (!std::all_of(models->begin(), models->end(), | 
| 308 |                        [&_](const SpvExecutionModel& model) { | 
| 309 |                          switch (model) { | 
| 310 |                            case SpvExecutionModelGeometry: | 
| 311 |                              return true; | 
| 312 |                            case SpvExecutionModelMeshNV: | 
| 313 |                              return _.HasCapability(SpvCapabilityMeshShadingNV); | 
| 314 |                            default: | 
| 315 |                              return false; | 
| 316 |                          } | 
| 317 |                        })) { | 
| 318 |         if (_.HasCapability(SpvCapabilityMeshShadingNV)) { | 
| 319 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 320 |                  << "Execution mode can only be used with the Geometry or "  | 
| 321 |                     "MeshNV execution model." ; | 
| 322 |         } else { | 
| 323 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 324 |                  << "Execution mode can only be used with the Geometry "  | 
| 325 |                     "execution "  | 
| 326 |                     "model." ; | 
| 327 |         } | 
| 328 |       } | 
| 329 |       break; | 
| 330 |     case SpvExecutionModeSpacingEqual: | 
| 331 |     case SpvExecutionModeSpacingFractionalEven: | 
| 332 |     case SpvExecutionModeSpacingFractionalOdd: | 
| 333 |     case SpvExecutionModeVertexOrderCw: | 
| 334 |     case SpvExecutionModeVertexOrderCcw: | 
| 335 |     case SpvExecutionModePointMode: | 
| 336 |     case SpvExecutionModeQuads: | 
| 337 |     case SpvExecutionModeIsolines: | 
| 338 |       if (!std::all_of( | 
| 339 |               models->begin(), models->end(), | 
| 340 |               [](const SpvExecutionModel& model) { | 
| 341 |                 return (model == SpvExecutionModelTessellationControl) || | 
| 342 |                        (model == SpvExecutionModelTessellationEvaluation); | 
| 343 |               })) { | 
| 344 |         return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 345 |                << "Execution mode can only be used with a tessellation "  | 
| 346 |                   "execution model." ; | 
| 347 |       } | 
| 348 |       break; | 
| 349 |     case SpvExecutionModeTriangles: | 
| 350 |       if (!std::all_of(models->begin(), models->end(), | 
| 351 |                        [](const SpvExecutionModel& model) { | 
| 352 |                          switch (model) { | 
| 353 |                            case SpvExecutionModelGeometry: | 
| 354 |                            case SpvExecutionModelTessellationControl: | 
| 355 |                            case SpvExecutionModelTessellationEvaluation: | 
| 356 |                              return true; | 
| 357 |                            default: | 
| 358 |                              return false; | 
| 359 |                          } | 
| 360 |                        })) { | 
| 361 |         return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 362 |                << "Execution mode can only be used with a Geometry or "  | 
| 363 |                   "tessellation execution model." ; | 
| 364 |       } | 
| 365 |       break; | 
| 366 |     case SpvExecutionModeOutputVertices: | 
| 367 |       if (!std::all_of(models->begin(), models->end(), | 
| 368 |                        [&_](const SpvExecutionModel& model) { | 
| 369 |                          switch (model) { | 
| 370 |                            case SpvExecutionModelGeometry: | 
| 371 |                            case SpvExecutionModelTessellationControl: | 
| 372 |                            case SpvExecutionModelTessellationEvaluation: | 
| 373 |                              return true; | 
| 374 |                            case SpvExecutionModelMeshNV: | 
| 375 |                              return _.HasCapability(SpvCapabilityMeshShadingNV); | 
| 376 |                            default: | 
| 377 |                              return false; | 
| 378 |                          } | 
| 379 |                        })) { | 
| 380 |         if (_.HasCapability(SpvCapabilityMeshShadingNV)) { | 
| 381 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 382 |                  << "Execution mode can only be used with a Geometry, "  | 
| 383 |                     "tessellation or MeshNV execution model." ; | 
| 384 |         } else { | 
| 385 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 386 |                  << "Execution mode can only be used with a Geometry or "  | 
| 387 |                     "tessellation execution model." ; | 
| 388 |         } | 
| 389 |       } | 
| 390 |       break; | 
| 391 |     case SpvExecutionModePixelCenterInteger: | 
| 392 |     case SpvExecutionModeOriginUpperLeft: | 
| 393 |     case SpvExecutionModeOriginLowerLeft: | 
| 394 |     case SpvExecutionModeEarlyFragmentTests: | 
| 395 |     case SpvExecutionModeDepthReplacing: | 
| 396 |     case SpvExecutionModeDepthGreater: | 
| 397 |     case SpvExecutionModeDepthLess: | 
| 398 |     case SpvExecutionModeDepthUnchanged: | 
| 399 |     case SpvExecutionModePixelInterlockOrderedEXT: | 
| 400 |     case SpvExecutionModePixelInterlockUnorderedEXT: | 
| 401 |     case SpvExecutionModeSampleInterlockOrderedEXT: | 
| 402 |     case SpvExecutionModeSampleInterlockUnorderedEXT: | 
| 403 |     case SpvExecutionModeShadingRateInterlockOrderedEXT: | 
| 404 |     case SpvExecutionModeShadingRateInterlockUnorderedEXT: | 
| 405 |       if (!std::all_of(models->begin(), models->end(), | 
| 406 |                        [](const SpvExecutionModel& model) { | 
| 407 |                          return model == SpvExecutionModelFragment; | 
| 408 |                        })) { | 
| 409 |         return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 410 |                << "Execution mode can only be used with the Fragment execution "  | 
| 411 |                   "model." ; | 
| 412 |       } | 
| 413 |       break; | 
| 414 |     case SpvExecutionModeLocalSizeHint: | 
| 415 |     case SpvExecutionModeVecTypeHint: | 
| 416 |     case SpvExecutionModeContractionOff: | 
| 417 |     case SpvExecutionModeLocalSizeHintId: | 
| 418 |       if (!std::all_of(models->begin(), models->end(), | 
| 419 |                        [](const SpvExecutionModel& model) { | 
| 420 |                          return model == SpvExecutionModelKernel; | 
| 421 |                        })) { | 
| 422 |         return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 423 |                << "Execution mode can only be used with the Kernel execution "  | 
| 424 |                   "model." ; | 
| 425 |       } | 
| 426 |       break; | 
| 427 |     case SpvExecutionModeLocalSize: | 
| 428 |     case SpvExecutionModeLocalSizeId: | 
| 429 |       if (!std::all_of(models->begin(), models->end(), | 
| 430 |                        [&_](const SpvExecutionModel& model) { | 
| 431 |                          switch (model) { | 
| 432 |                            case SpvExecutionModelKernel: | 
| 433 |                            case SpvExecutionModelGLCompute: | 
| 434 |                              return true; | 
| 435 |                            case SpvExecutionModelTaskNV: | 
| 436 |                            case SpvExecutionModelMeshNV: | 
| 437 |                              return _.HasCapability(SpvCapabilityMeshShadingNV); | 
| 438 |                            default: | 
| 439 |                              return false; | 
| 440 |                          } | 
| 441 |                        })) { | 
| 442 |         if (_.HasCapability(SpvCapabilityMeshShadingNV)) { | 
| 443 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 444 |                  << "Execution mode can only be used with a Kernel, GLCompute, "  | 
| 445 |                     "MeshNV, or TaskNV execution model." ; | 
| 446 |         } else { | 
| 447 |           return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 448 |                  << "Execution mode can only be used with a Kernel or "  | 
| 449 |                     "GLCompute "  | 
| 450 |                     "execution model." ; | 
| 451 |         } | 
| 452 |       } | 
| 453 |     default: | 
| 454 |       break; | 
| 455 |   } | 
| 456 |  | 
| 457 |   if (spvIsVulkanEnv(_.context()->target_env)) { | 
| 458 |     if (mode == SpvExecutionModeOriginLowerLeft) { | 
| 459 |       return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 460 |              << "In the Vulkan environment, the OriginLowerLeft execution mode "  | 
| 461 |                 "must not be used." ; | 
| 462 |     } | 
| 463 |     if (mode == SpvExecutionModePixelCenterInteger) { | 
| 464 |       return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 465 |              << "In the Vulkan environment, the PixelCenterInteger execution "  | 
| 466 |                 "mode must not be used." ; | 
| 467 |     } | 
| 468 |   } | 
| 469 |  | 
| 470 |   if (spvIsWebGPUEnv(_.context()->target_env)) { | 
| 471 |     if (mode != SpvExecutionModeOriginUpperLeft && | 
| 472 |         mode != SpvExecutionModeDepthReplacing && | 
| 473 |         mode != SpvExecutionModeDepthGreater && | 
| 474 |         mode != SpvExecutionModeDepthLess && | 
| 475 |         mode != SpvExecutionModeDepthUnchanged && | 
| 476 |         mode != SpvExecutionModeLocalSize && | 
| 477 |         mode != SpvExecutionModeLocalSizeHint) { | 
| 478 |       return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 479 |              << "Execution mode must be one of OriginUpperLeft, "  | 
| 480 |                 "DepthReplacing, DepthGreater, DepthLess, DepthUnchanged, "  | 
| 481 |                 "LocalSize, or LocalSizeHint for WebGPU environment." ; | 
| 482 |     } | 
| 483 |   } | 
| 484 |  | 
| 485 |   return SPV_SUCCESS; | 
| 486 | } | 
| 487 |  | 
| 488 | spv_result_t ValidateMemoryModel(ValidationState_t& _, | 
| 489 |                                  const Instruction* inst) { | 
| 490 |   // Already produced an error if multiple memory model instructions are | 
| 491 |   // present. | 
| 492 |   if (_.memory_model() != SpvMemoryModelVulkanKHR && | 
| 493 |       _.HasCapability(SpvCapabilityVulkanMemoryModelKHR)) { | 
| 494 |     return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 495 |            << "VulkanMemoryModelKHR capability must only be specified if "  | 
| 496 |               "the VulkanKHR memory model is used." ; | 
| 497 |   } | 
| 498 |  | 
| 499 |   if (spvIsWebGPUEnv(_.context()->target_env)) { | 
| 500 |     if (_.addressing_model() != SpvAddressingModelLogical) { | 
| 501 |       return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 502 |              << "Addressing model must be Logical for WebGPU environment." ; | 
| 503 |     } | 
| 504 |     if (_.memory_model() != SpvMemoryModelVulkanKHR) { | 
| 505 |       return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 506 |              << "Memory model must be VulkanKHR for WebGPU environment." ; | 
| 507 |     } | 
| 508 |   } | 
| 509 |  | 
| 510 |   if (spvIsOpenCLEnv(_.context()->target_env)) { | 
| 511 |     if ((_.addressing_model() != SpvAddressingModelPhysical32) && | 
| 512 |         (_.addressing_model() != SpvAddressingModelPhysical64)) { | 
| 513 |       return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 514 |              << "Addressing model must be Physical32 or Physical64 "  | 
| 515 |              << "in the OpenCL environment." ; | 
| 516 |     } | 
| 517 |     if (_.memory_model() != SpvMemoryModelOpenCL) { | 
| 518 |       return _.diag(SPV_ERROR_INVALID_DATA, inst) | 
| 519 |              << "Memory model must be OpenCL in the OpenCL environment." ; | 
| 520 |     } | 
| 521 |   } | 
| 522 |  | 
| 523 |   return SPV_SUCCESS; | 
| 524 | } | 
| 525 |  | 
| 526 | }  // namespace | 
| 527 |  | 
| 528 | spv_result_t ModeSettingPass(ValidationState_t& _, const Instruction* inst) { | 
| 529 |   switch (inst->opcode()) { | 
| 530 |     case SpvOpEntryPoint: | 
| 531 |       if (auto error = ValidateEntryPoint(_, inst)) return error; | 
| 532 |       break; | 
| 533 |     case SpvOpExecutionMode: | 
| 534 |     case SpvOpExecutionModeId: | 
| 535 |       if (auto error = ValidateExecutionMode(_, inst)) return error; | 
| 536 |       break; | 
| 537 |     case SpvOpMemoryModel: | 
| 538 |       if (auto error = ValidateMemoryModel(_, inst)) return error; | 
| 539 |       break; | 
| 540 |     default: | 
| 541 |       break; | 
| 542 |   } | 
| 543 |   return SPV_SUCCESS; | 
| 544 | } | 
| 545 |  | 
| 546 | }  // namespace val | 
| 547 | }  // namespace spvtools | 
| 548 |  |