Home | History | Annotate | Download | only in val
      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