Home | History | Annotate | Download | only in val
      1 // Copyright (c) 2018 LunarG Inc.
      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 // Validates correctness of the intra-block preconditions of SPIR-V
     16 // instructions.
     17 
     18 #include "source/val/validate.h"
     19 
     20 #include <string>
     21 
     22 #include "source/diagnostic.h"
     23 #include "source/opcode.h"
     24 #include "source/val/instruction.h"
     25 #include "source/val/validation_state.h"
     26 
     27 namespace spvtools {
     28 namespace val {
     29 
     30 enum {
     31   // Status right after meeting OpFunction.
     32   IN_NEW_FUNCTION,
     33   // Status right after meeting the entry block.
     34   IN_ENTRY_BLOCK,
     35   // Status right after meeting non-entry blocks.
     36   PHI_VALID,
     37   // Status right after meeting non-OpVariable instructions in the entry block
     38   // or non-OpPhi instructions in non-entry blocks, except OpLine.
     39   PHI_AND_VAR_INVALID,
     40 };
     41 
     42 spv_result_t ValidateAdjacency(ValidationState_t& _) {
     43   const auto& instructions = _.ordered_instructions();
     44   int adjacency_status = PHI_AND_VAR_INVALID;
     45 
     46   for (size_t i = 0; i < instructions.size(); ++i) {
     47     const auto& inst = instructions[i];
     48     switch (inst.opcode()) {
     49       case SpvOpFunction:
     50       case SpvOpFunctionParameter:
     51         adjacency_status = IN_NEW_FUNCTION;
     52         break;
     53       case SpvOpLabel:
     54         adjacency_status =
     55             adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID;
     56         break;
     57       case SpvOpPhi:
     58         if (adjacency_status != PHI_VALID) {
     59           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
     60                  << "OpPhi must appear within a non-entry block before all "
     61                  << "non-OpPhi instructions "
     62                  << "(except for OpLine, which can be mixed with OpPhi).";
     63         }
     64         break;
     65       case SpvOpLine:
     66       case SpvOpNoLine:
     67         break;
     68       case SpvOpLoopMerge:
     69         adjacency_status = PHI_AND_VAR_INVALID;
     70         if (i != (instructions.size() - 1)) {
     71           switch (instructions[i + 1].opcode()) {
     72             case SpvOpBranch:
     73             case SpvOpBranchConditional:
     74               break;
     75             default:
     76               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
     77                      << "OpLoopMerge must immediately precede either an "
     78                      << "OpBranch or OpBranchConditional instruction. "
     79                      << "OpLoopMerge must be the second-to-last instruction in "
     80                      << "its block.";
     81           }
     82         }
     83         break;
     84       case SpvOpSelectionMerge:
     85         adjacency_status = PHI_AND_VAR_INVALID;
     86         if (i != (instructions.size() - 1)) {
     87           switch (instructions[i + 1].opcode()) {
     88             case SpvOpBranchConditional:
     89             case SpvOpSwitch:
     90               break;
     91             default:
     92               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
     93                      << "OpSelectionMerge must immediately precede either an "
     94                      << "OpBranchConditional or OpSwitch instruction. "
     95                      << "OpSelectionMerge must be the second-to-last "
     96                      << "instruction in its block.";
     97           }
     98         }
     99         break;
    100       case SpvOpVariable:
    101         if (inst.GetOperandAs<SpvStorageClass>(2) == SpvStorageClassFunction &&
    102             adjacency_status != IN_ENTRY_BLOCK) {
    103           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
    104                  << "All OpVariable instructions in a function must be the "
    105                     "first instructions in the first block.";
    106         }
    107         break;
    108       default:
    109         adjacency_status = PHI_AND_VAR_INVALID;
    110         break;
    111     }
    112   }
    113 
    114   return SPV_SUCCESS;
    115 }
    116 
    117 }  // namespace val
    118 }  // namespace spvtools
    119