| // Copyright (c) 2018 LunarG Inc. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | // Validates correctness of the intra-block preconditions of SPIR-V | 
 | // instructions. | 
 |  | 
 | #include "source/val/validate.h" | 
 |  | 
 | #include <string> | 
 |  | 
 | #include "source/diagnostic.h" | 
 | #include "source/opcode.h" | 
 | #include "source/val/instruction.h" | 
 | #include "source/val/validation_state.h" | 
 |  | 
 | namespace spvtools { | 
 | namespace val { | 
 |  | 
 | enum { | 
 |   // Status right after meeting OpFunction. | 
 |   IN_NEW_FUNCTION, | 
 |   // Status right after meeting the entry block. | 
 |   IN_ENTRY_BLOCK, | 
 |   // Status right after meeting non-entry blocks. | 
 |   PHI_VALID, | 
 |   // Status right after meeting non-OpVariable instructions in the entry block | 
 |   // or non-OpPhi instructions in non-entry blocks, except OpLine. | 
 |   PHI_AND_VAR_INVALID, | 
 | }; | 
 |  | 
 | spv_result_t ValidateAdjacency(ValidationState_t& _) { | 
 |   const auto& instructions = _.ordered_instructions(); | 
 |   int adjacency_status = PHI_AND_VAR_INVALID; | 
 |  | 
 |   for (size_t i = 0; i < instructions.size(); ++i) { | 
 |     const auto& inst = instructions[i]; | 
 |     switch (inst.opcode()) { | 
 |       case SpvOpFunction: | 
 |       case SpvOpFunctionParameter: | 
 |         adjacency_status = IN_NEW_FUNCTION; | 
 |         break; | 
 |       case SpvOpLabel: | 
 |         adjacency_status = | 
 |             adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID; | 
 |         break; | 
 |       case SpvOpExtInst: | 
 |         // If it is a debug info instruction, we do not change the status to | 
 |         // allow debug info instructions before OpVariable in a function. | 
 |         // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need | 
 |         // to discuss the location of DebugScope, DebugNoScope, DebugDeclare, | 
 |         // and DebugValue. | 
 |         // NOTE: This does not apply to the non-semantic vulkan debug info. | 
 |         if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) || | 
 |             inst.ext_inst_type() == | 
 |                 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) { | 
 |           adjacency_status = PHI_AND_VAR_INVALID; | 
 |         } | 
 |         break; | 
 |       case SpvOpPhi: | 
 |         if (adjacency_status != PHI_VALID) { | 
 |           return _.diag(SPV_ERROR_INVALID_DATA, &inst) | 
 |                  << "OpPhi must appear within a non-entry block before all " | 
 |                  << "non-OpPhi instructions " | 
 |                  << "(except for OpLine, which can be mixed with OpPhi)."; | 
 |         } | 
 |         break; | 
 |       case SpvOpLine: | 
 |       case SpvOpNoLine: | 
 |         break; | 
 |       case SpvOpLoopMerge: | 
 |         adjacency_status = PHI_AND_VAR_INVALID; | 
 |         if (i != (instructions.size() - 1)) { | 
 |           switch (instructions[i + 1].opcode()) { | 
 |             case SpvOpBranch: | 
 |             case SpvOpBranchConditional: | 
 |               break; | 
 |             default: | 
 |               return _.diag(SPV_ERROR_INVALID_DATA, &inst) | 
 |                      << "OpLoopMerge must immediately precede either an " | 
 |                      << "OpBranch or OpBranchConditional instruction. " | 
 |                      << "OpLoopMerge must be the second-to-last instruction in " | 
 |                      << "its block."; | 
 |           } | 
 |         } | 
 |         break; | 
 |       case SpvOpSelectionMerge: | 
 |         adjacency_status = PHI_AND_VAR_INVALID; | 
 |         if (i != (instructions.size() - 1)) { | 
 |           switch (instructions[i + 1].opcode()) { | 
 |             case SpvOpBranchConditional: | 
 |             case SpvOpSwitch: | 
 |               break; | 
 |             default: | 
 |               return _.diag(SPV_ERROR_INVALID_DATA, &inst) | 
 |                      << "OpSelectionMerge must immediately precede either an " | 
 |                      << "OpBranchConditional or OpSwitch instruction. " | 
 |                      << "OpSelectionMerge must be the second-to-last " | 
 |                      << "instruction in its block."; | 
 |           } | 
 |         } | 
 |         break; | 
 |       case SpvOpVariable: | 
 |         if (inst.GetOperandAs<SpvStorageClass>(2) == SpvStorageClassFunction && | 
 |             adjacency_status != IN_ENTRY_BLOCK) { | 
 |           return _.diag(SPV_ERROR_INVALID_DATA, &inst) | 
 |                  << "All OpVariable instructions in a function must be the " | 
 |                     "first instructions in the first block."; | 
 |         } | 
 |         break; | 
 |       default: | 
 |         adjacency_status = PHI_AND_VAR_INVALID; | 
 |         break; | 
 |     } | 
 |   } | 
 |  | 
 |   return SPV_SUCCESS; | 
 | } | 
 |  | 
 | }  // namespace val | 
 | }  // namespace spvtools |