• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ValidateAdjacency(ValidationState_t & _)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         // NOTE: This does not apply to the non-semantic vulkan debug info.
64         if (!spvExtInstIsDebugInfo(inst.ext_inst_type()) ||
65             inst.ext_inst_type() ==
66                 SPV_EXT_INST_TYPE_NONSEMANTIC_SHADER_DEBUGINFO_100) {
67           adjacency_status = PHI_AND_VAR_INVALID;
68         }
69         break;
70       case SpvOpPhi:
71         if (adjacency_status != PHI_VALID) {
72           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
73                  << "OpPhi must appear within a non-entry block before all "
74                  << "non-OpPhi instructions "
75                  << "(except for OpLine, which can be mixed with OpPhi).";
76         }
77         break;
78       case SpvOpLine:
79       case SpvOpNoLine:
80         break;
81       case SpvOpLoopMerge:
82         adjacency_status = PHI_AND_VAR_INVALID;
83         if (i != (instructions.size() - 1)) {
84           switch (instructions[i + 1].opcode()) {
85             case SpvOpBranch:
86             case SpvOpBranchConditional:
87               break;
88             default:
89               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
90                      << "OpLoopMerge must immediately precede either an "
91                      << "OpBranch or OpBranchConditional instruction. "
92                      << "OpLoopMerge must be the second-to-last instruction in "
93                      << "its block.";
94           }
95         }
96         break;
97       case SpvOpSelectionMerge:
98         adjacency_status = PHI_AND_VAR_INVALID;
99         if (i != (instructions.size() - 1)) {
100           switch (instructions[i + 1].opcode()) {
101             case SpvOpBranchConditional:
102             case SpvOpSwitch:
103               break;
104             default:
105               return _.diag(SPV_ERROR_INVALID_DATA, &inst)
106                      << "OpSelectionMerge must immediately precede either an "
107                      << "OpBranchConditional or OpSwitch instruction. "
108                      << "OpSelectionMerge must be the second-to-last "
109                      << "instruction in its block.";
110           }
111         }
112         break;
113       case SpvOpVariable:
114         if (inst.GetOperandAs<SpvStorageClass>(2) == SpvStorageClassFunction &&
115             adjacency_status != IN_ENTRY_BLOCK) {
116           return _.diag(SPV_ERROR_INVALID_DATA, &inst)
117                  << "All OpVariable instructions in a function must be the "
118                     "first instructions in the first block.";
119         }
120         break;
121       default:
122         adjacency_status = PHI_AND_VAR_INVALID;
123         break;
124     }
125   }
126 
127   return SPV_SUCCESS;
128 }
129 
130 }  // namespace val
131 }  // namespace spvtools
132