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 <string>
19
20 #include "source/val/instruction.h"
21 #include "source/val/validate.h"
22 #include "source/val/validation_state.h"
23
24 namespace spvtools {
25 namespace val {
26
27 enum {
28 // Status right after meeting OpFunction.
29 IN_NEW_FUNCTION,
30 // Status right after meeting the entry block.
31 IN_ENTRY_BLOCK,
32 // Status right after meeting non-entry blocks.
33 PHI_VALID,
34 // Status right after meeting non-OpVariable instructions in the entry block
35 // or non-OpPhi instructions in non-entry blocks, except OpLine.
36 PHI_AND_VAR_INVALID,
37 };
38
ValidateAdjacency(ValidationState_t & _)39 spv_result_t ValidateAdjacency(ValidationState_t& _) {
40 const auto& instructions = _.ordered_instructions();
41 int adjacency_status = PHI_AND_VAR_INVALID;
42
43 for (size_t i = 0; i < instructions.size(); ++i) {
44 const auto& inst = instructions[i];
45 switch (inst.opcode()) {
46 case spv::Op::OpFunction:
47 case spv::Op::OpFunctionParameter:
48 adjacency_status = IN_NEW_FUNCTION;
49 break;
50 case spv::Op::OpLabel:
51 adjacency_status =
52 adjacency_status == IN_NEW_FUNCTION ? IN_ENTRY_BLOCK : PHI_VALID;
53 break;
54 case spv::Op::OpExtInst:
55 case spv::Op::OpExtInstWithForwardRefsKHR:
56 // If it is a debug info instruction, we do not change the status to
57 // allow debug info instructions before OpVariable in a function.
58 // TODO(https://gitlab.khronos.org/spirv/SPIR-V/issues/533): We need
59 // to discuss the location of DebugScope, DebugNoScope, DebugDeclare,
60 // and DebugValue.
61 // NOTE: This does not apply to the non-semantic vulkan debug info.
62 if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) ||
63 inst.ext_inst_type() ==
64 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
65 adjacency_status = PHI_AND_VAR_INVALID;
66 }
67 break;
68 case spv::Op::OpPhi:
69 if (adjacency_status != PHI_VALID) {
70 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
71 << "OpPhi must appear within a non-entry block before all "
72 << "non-OpPhi instructions "
73 << "(except for OpLine, which can be mixed with OpPhi).";
74 }
75 break;
76 case spv::Op::OpLine:
77 case spv::Op::OpNoLine:
78 break;
79 case spv::Op::OpLoopMerge:
80 adjacency_status = PHI_AND_VAR_INVALID;
81 if (i != (instructions.size() - 1)) {
82 switch (instructions[i + 1].opcode()) {
83 case spv::Op::OpBranch:
84 case spv::Op::OpBranchConditional:
85 break;
86 default:
87 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
88 << "OpLoopMerge must immediately precede either an "
89 << "OpBranch or OpBranchConditional instruction. "
90 << "OpLoopMerge must be the second-to-last instruction in "
91 << "its block.";
92 }
93 }
94 break;
95 case spv::Op::OpSelectionMerge:
96 adjacency_status = PHI_AND_VAR_INVALID;
97 if (i != (instructions.size() - 1)) {
98 switch (instructions[i + 1].opcode()) {
99 case spv::Op::OpBranchConditional:
100 case spv::Op::OpSwitch:
101 break;
102 default:
103 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
104 << "OpSelectionMerge must immediately precede either an "
105 << "OpBranchConditional or OpSwitch instruction. "
106 << "OpSelectionMerge must be the second-to-last "
107 << "instruction in its block.";
108 }
109 }
110 break;
111 case spv::Op::OpVariable:
112 if (inst.GetOperandAs<spv::StorageClass>(2) ==
113 spv::StorageClass::Function &&
114 adjacency_status != IN_ENTRY_BLOCK) {
115 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
116 << "All OpVariable instructions in a function must be the "
117 "first instructions in the first block.";
118 }
119 break;
120 case spv::Op::OpUntypedVariableKHR:
121 if (inst.GetOperandAs<spv::StorageClass>(2) ==
122 spv::StorageClass::Function &&
123 adjacency_status != IN_ENTRY_BLOCK) {
124 return _.diag(SPV_ERROR_INVALID_DATA, &inst)
125 << "All OpUntypedVariableKHR instructions in a function must "
126 "be the first instructions in the first block.";
127 }
128 break;
129 default:
130 adjacency_status = PHI_AND_VAR_INVALID;
131 break;
132 }
133 }
134
135 return SPV_SUCCESS;
136 }
137
138 } // namespace val
139 } // namespace spvtools
140