xref: /aosp_15_r20/external/angle/third_party/spirv-tools/src/source/val/validate_adjacency.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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