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 SpvOpExtInst: |
58 | // If it is a debug info instruction, we do not change the status to |
59 | // allow debug info instructions before OpVariable in a function. |
60 | // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need |
61 | // to discuss the location of DebugScope, DebugNoScope, DebugDeclare, |
62 | // and DebugValue. |
63 | if (!spvExtInstIsDebugInfo(inst.ext_inst_type())) { |
64 | adjacency_status = PHI_AND_VAR_INVALID; |
65 | } |
66 | break; |
67 | case SpvOpPhi: |
68 | if (adjacency_status != PHI_VALID) { |
69 | return _.diag(SPV_ERROR_INVALID_DATA, &inst) |
70 | << "OpPhi must appear within a non-entry block before all " |
71 | << "non-OpPhi instructions " |
72 | << "(except for OpLine, which can be mixed with OpPhi)." ; |
73 | } |
74 | break; |
75 | case SpvOpLine: |
76 | case SpvOpNoLine: |
77 | break; |
78 | case SpvOpLoopMerge: |
79 | adjacency_status = PHI_AND_VAR_INVALID; |
80 | if (i != (instructions.size() - 1)) { |
81 | switch (instructions[i + 1].opcode()) { |
82 | case SpvOpBranch: |
83 | case SpvOpBranchConditional: |
84 | break; |
85 | default: |
86 | return _.diag(SPV_ERROR_INVALID_DATA, &inst) |
87 | << "OpLoopMerge must immediately precede either an " |
88 | << "OpBranch or OpBranchConditional instruction. " |
89 | << "OpLoopMerge must be the second-to-last instruction in " |
90 | << "its block." ; |
91 | } |
92 | } |
93 | break; |
94 | case SpvOpSelectionMerge: |
95 | adjacency_status = PHI_AND_VAR_INVALID; |
96 | if (i != (instructions.size() - 1)) { |
97 | switch (instructions[i + 1].opcode()) { |
98 | case SpvOpBranchConditional: |
99 | case SpvOpSwitch: |
100 | break; |
101 | default: |
102 | return _.diag(SPV_ERROR_INVALID_DATA, &inst) |
103 | << "OpSelectionMerge must immediately precede either an " |
104 | << "OpBranchConditional or OpSwitch instruction. " |
105 | << "OpSelectionMerge must be the second-to-last " |
106 | << "instruction in its block." ; |
107 | } |
108 | } |
109 | break; |
110 | case SpvOpVariable: |
111 | if (inst.GetOperandAs<SpvStorageClass>(2) == SpvStorageClassFunction && |
112 | adjacency_status != IN_ENTRY_BLOCK) { |
113 | return _.diag(SPV_ERROR_INVALID_DATA, &inst) |
114 | << "All OpVariable instructions in a function must be the " |
115 | "first instructions in the first block." ; |
116 | } |
117 | break; |
118 | default: |
119 | adjacency_status = PHI_AND_VAR_INVALID; |
120 | break; |
121 | } |
122 | } |
123 | |
124 | return SPV_SUCCESS; |
125 | } |
126 | |
127 | } // namespace val |
128 | } // namespace spvtools |
129 | |