• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2015-2016 The Khronos Group 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 #include "source/val/validate.h"
16 
17 #include <cassert>
18 
19 #include <algorithm>
20 #include <iostream>
21 #include <iterator>
22 #include <stack>
23 #include <string>
24 #include <unordered_set>
25 #include <utility>
26 #include <vector>
27 
28 #include "source/diagnostic.h"
29 #include "source/instruction.h"
30 #include "source/opcode.h"
31 #include "source/operand.h"
32 #include "source/spirv_validator_options.h"
33 #include "source/val/function.h"
34 #include "source/val/validation_state.h"
35 #include "spirv-tools/libspirv.h"
36 
37 namespace spvtools {
38 namespace val {
39 
UpdateIdUse(ValidationState_t & _,const Instruction * inst)40 spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst) {
41   for (auto& operand : inst->operands()) {
42     const spv_operand_type_t& type = operand.type;
43     const uint32_t operand_id = inst->word(operand.offset);
44     if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) {
45       if (auto def = _.FindDef(operand_id))
46         def->RegisterUse(inst, operand.offset);
47     }
48   }
49 
50   return SPV_SUCCESS;
51 }
52 
53 /// This function checks all ID definitions dominate their use in the CFG.
54 ///
55 /// This function will iterate over all ID definitions that are defined in the
56 /// functions of a module and make sure that the definitions appear in a
57 /// block that dominates their use.
58 ///
59 /// NOTE: This function does NOT check module scoped functions which are
60 /// checked during the initial binary parse in the IdPass below
CheckIdDefinitionDominateUse(ValidationState_t & _)61 spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _) {
62   std::vector<const Instruction*> phi_instructions;
63   std::unordered_set<uint32_t> phi_ids;
64   for (const auto& inst : _.ordered_instructions()) {
65     if (inst.id() == 0) continue;
66     if (const Function* func = inst.function()) {
67       if (const BasicBlock* block = inst.block()) {
68         // If the Id is defined within a block then make sure all references to
69         // that Id appear in a blocks that are dominated by the defining block
70         for (auto& use_index_pair : inst.uses()) {
71           const Instruction* use = use_index_pair.first;
72           if (const BasicBlock* use_block = use->block()) {
73             if (use_block->reachable() == false) continue;
74             if (use->opcode() == SpvOpPhi) {
75               if (phi_ids.insert(use->id()).second) {
76                 phi_instructions.push_back(use);
77               }
78             } else if (!block->dominates(*use->block())) {
79               return _.diag(SPV_ERROR_INVALID_ID, use_block->label())
80                      << "ID " << _.getIdName(inst.id()) << " defined in block "
81                      << _.getIdName(block->id())
82                      << " does not dominate its use in block "
83                      << _.getIdName(use_block->id());
84             }
85           }
86         }
87       } else {
88         // If the Ids defined within a function but not in a block(i.e. function
89         // parameters, block ids), then make sure all references to that Id
90         // appear within the same function
91         for (auto use : inst.uses()) {
92           const Instruction* user = use.first;
93           if (user->function() && user->function() != func) {
94             return _.diag(SPV_ERROR_INVALID_ID, _.FindDef(func->id()))
95                    << "ID " << _.getIdName(inst.id()) << " used in function "
96                    << _.getIdName(user->function()->id())
97                    << " is used outside of it's defining function "
98                    << _.getIdName(func->id());
99           }
100         }
101       }
102     }
103     // NOTE: Ids defined outside of functions must appear before they are used
104     // This check is being performed in the IdPass function
105   }
106 
107   // Check all OpPhi parent blocks are dominated by the variable's defining
108   // blocks
109   for (const Instruction* phi : phi_instructions) {
110     if (phi->block()->reachable() == false) continue;
111     for (size_t i = 3; i < phi->operands().size(); i += 2) {
112       const Instruction* variable = _.FindDef(phi->word(i));
113       const BasicBlock* parent =
114           phi->function()->GetBlock(phi->word(i + 1)).first;
115       if (variable->block() && parent->reachable() &&
116           !variable->block()->dominates(*parent)) {
117         return _.diag(SPV_ERROR_INVALID_ID, phi)
118                << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID "
119                << _.getIdName(variable->id())
120                << " definition does not dominate its parent "
121                << _.getIdName(parent->id());
122       }
123     }
124   }
125 
126   return SPV_SUCCESS;
127 }
128 
129 // Performs SSA validation on the IDs of an instruction. The
130 // can_have_forward_declared_ids  functor should return true if the
131 // instruction operand's ID can be forward referenced.
IdPass(ValidationState_t & _,Instruction * inst)132 spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
133   auto can_have_forward_declared_ids =
134       inst->opcode() == SpvOpExtInst &&
135               spvExtInstIsDebugInfo(inst->ext_inst_type())
136           ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
137                 inst->ext_inst_type(), inst->word(4))
138           : spvOperandCanBeForwardDeclaredFunction(inst->opcode());
139 
140   // Keep track of a result id defined by this instruction.  0 means it
141   // does not define an id.
142   uint32_t result_id = 0;
143 
144   for (unsigned i = 0; i < inst->operands().size(); i++) {
145     const spv_parsed_operand_t& operand = inst->operand(i);
146     const spv_operand_type_t& type = operand.type;
147     // We only care about Id operands, which are a single word.
148     const uint32_t operand_word = inst->word(operand.offset);
149 
150     auto ret = SPV_ERROR_INTERNAL;
151     switch (type) {
152       case SPV_OPERAND_TYPE_RESULT_ID:
153         // NOTE: Multiple Id definitions are being checked by the binary parser.
154         //
155         // Defer undefined-forward-reference removal until after we've analyzed
156         // the remaining operands to this instruction.  Deferral only matters
157         // for OpPhi since it's the only case where it defines its own forward
158         // reference.  Other instructions that can have forward references
159         // either don't define a value or the forward reference is to a function
160         // Id (and hence defined outside of a function body).
161         result_id = operand_word;
162         // NOTE: The result Id is added (in RegisterInstruction) *after* all of
163         // the other Ids have been checked to avoid premature use in the same
164         // instruction.
165         ret = SPV_SUCCESS;
166         break;
167       case SPV_OPERAND_TYPE_ID:
168       case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
169       case SPV_OPERAND_TYPE_SCOPE_ID:
170         if (const auto def = _.FindDef(operand_word)) {
171           const auto opcode = inst->opcode();
172           if (spvOpcodeGeneratesType(def->opcode()) &&
173               !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
174               !inst->IsDebugInfo() && !inst->IsNonSemantic() &&
175               !spvOpcodeIsDecoration(opcode) && opcode != SpvOpFunction &&
176               opcode != SpvOpCooperativeMatrixLengthNV &&
177               !(opcode == SpvOpSpecConstantOp &&
178                 inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
179             return _.diag(SPV_ERROR_INVALID_ID, inst)
180                    << "Operand " << _.getIdName(operand_word)
181                    << " cannot be a type";
182           } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
183                      !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() &&
184                      !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
185                      !spvOpcodeIsBranch(opcode) && opcode != SpvOpPhi &&
186                      opcode != SpvOpExtInst && opcode != SpvOpExtInstImport &&
187                      opcode != SpvOpSelectionMerge &&
188                      opcode != SpvOpLoopMerge && opcode != SpvOpFunction &&
189                      opcode != SpvOpCooperativeMatrixLengthNV &&
190                      !(opcode == SpvOpSpecConstantOp &&
191                        inst->word(3) == SpvOpCooperativeMatrixLengthNV)) {
192             return _.diag(SPV_ERROR_INVALID_ID, inst)
193                    << "Operand " << _.getIdName(operand_word)
194                    << " requires a type";
195           } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) {
196             return _.diag(SPV_ERROR_INVALID_ID, inst)
197                    << "Operand " << _.getIdName(operand_word)
198                    << " in semantic instruction cannot be a non-semantic "
199                       "instruction";
200           } else {
201             ret = SPV_SUCCESS;
202           }
203         } else if (can_have_forward_declared_ids(i)) {
204           if (spvOpcodeGeneratesType(inst->opcode()) &&
205               !_.IsForwardPointer(operand_word)) {
206             ret = _.diag(SPV_ERROR_INVALID_ID, inst)
207                   << "Operand " << _.getIdName(operand_word)
208                   << " requires a previous definition";
209           } else {
210             ret = _.ForwardDeclareId(operand_word);
211           }
212         } else {
213           ret = _.diag(SPV_ERROR_INVALID_ID, inst)
214                 << "ID " << _.getIdName(operand_word)
215                 << " has not been defined";
216         }
217         break;
218       case SPV_OPERAND_TYPE_TYPE_ID:
219         if (_.IsDefinedId(operand_word)) {
220           auto* def = _.FindDef(operand_word);
221           if (!spvOpcodeGeneratesType(def->opcode())) {
222             ret = _.diag(SPV_ERROR_INVALID_ID, inst)
223                   << "ID " << _.getIdName(operand_word) << " is not a type id";
224           } else {
225             ret = SPV_SUCCESS;
226           }
227         } else {
228           ret = _.diag(SPV_ERROR_INVALID_ID, inst)
229                 << "ID " << _.getIdName(operand_word)
230                 << " has not been defined";
231         }
232         break;
233       default:
234         ret = SPV_SUCCESS;
235         break;
236     }
237     if (SPV_SUCCESS != ret) return ret;
238   }
239   if (result_id) _.RemoveIfForwardDeclared(result_id);
240 
241   return SPV_SUCCESS;
242 }
243 
244 }  // namespace val
245 }  // namespace spvtools
246