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