Coverage Report

Created: 2023-03-01 07:33

/src/spirv-tools/source/val/validate_id.cpp
Line
Count
Source (jump to first uncovered line)
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
40
2.86M
spv_result_t UpdateIdUse(ValidationState_t& _, const Instruction* inst) {
41
7.03M
  for (auto& operand : inst->operands()) {
42
7.03M
    const spv_operand_type_t& type = operand.type;
43
7.03M
    const uint32_t operand_id = inst->word(operand.offset);
44
7.03M
    if (spvIsIdType(type) && type != SPV_OPERAND_TYPE_RESULT_ID) {
45
4.37M
      if (auto def = _.FindDef(operand_id))
46
4.37M
        def->RegisterUse(inst, operand.offset);
47
4.37M
    }
48
7.03M
  }
49
50
2.86M
  return SPV_SUCCESS;
51
2.86M
}
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
61
13.3k
spv_result_t CheckIdDefinitionDominateUse(ValidationState_t& _) {
62
13.3k
  std::vector<const Instruction*> phi_instructions;
63
13.3k
  std::unordered_set<uint32_t> phi_ids;
64
2.17M
  for (const auto& inst : _.ordered_instructions()) {
65
2.17M
    if (inst.id() == 0) continue;
66
1.20M
    if (const Function* func = inst.function()) {
67
876k
      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
995k
        for (auto& use_index_pair : inst.uses()) {
71
995k
          const Instruction* use = use_index_pair.first;
72
995k
          if (const BasicBlock* use_block = use->block()) {
73
930k
            if (use_block->reachable() == false) continue;
74
898k
            if (use->opcode() == spv::Op::OpPhi) {
75
48.1k
              if (phi_ids.insert(use->id()).second) {
76
31.9k
                phi_instructions.push_back(use);
77
31.9k
              }
78
850k
            } else if (!block->dominates(*use->block())) {
79
55
              return _.diag(SPV_ERROR_INVALID_ID, use_block->label())
80
55
                     << "ID " << _.getIdName(inst.id()) << " defined in block "
81
55
                     << _.getIdName(block->id())
82
55
                     << " does not dominate its use in block "
83
55
                     << _.getIdName(use_block->id());
84
55
            }
85
898k
          }
86
995k
        }
87
641k
      } 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
448k
        for (auto use : inst.uses()) {
92
448k
          const Instruction* user = use.first;
93
448k
          if (user->function() && user->function() != func) {
94
12
            return _.diag(SPV_ERROR_INVALID_ID, _.FindDef(func->id()))
95
12
                   << "ID " << _.getIdName(inst.id()) << " used in function "
96
12
                   << _.getIdName(user->function()->id())
97
12
                   << " is used outside of it's defining function "
98
12
                   << _.getIdName(func->id());
99
12
          }
100
448k
        }
101
234k
      }
102
876k
    }
103
    // NOTE: Ids defined outside of functions must appear before they are used
104
    // This check is being performed in the IdPass function
105
1.20M
  }
106
107
  // Check all OpPhi parent blocks are dominated by the variable's defining
108
  // blocks
109
31.6k
  for (const Instruction* phi : phi_instructions) {
110
31.6k
    if (phi->block()->reachable() == false) continue;
111
97.0k
    for (size_t i = 3; i < phi->operands().size(); i += 2) {
112
65.3k
      const Instruction* variable = _.FindDef(phi->word(i));
113
65.3k
      const BasicBlock* parent =
114
65.3k
          phi->function()->GetBlock(phi->word(i + 1)).first;
115
65.3k
      if (variable->block() && parent->reachable() &&
116
65.3k
          !variable->block()->dominates(*parent)) {
117
16
        return _.diag(SPV_ERROR_INVALID_ID, phi)
118
16
               << "In OpPhi instruction " << _.getIdName(phi->id()) << ", ID "
119
16
               << _.getIdName(variable->id())
120
16
               << " definition does not dominate its parent "
121
16
               << _.getIdName(parent->id());
122
16
      }
123
65.3k
    }
124
31.6k
  }
125
126
13.2k
  return SPV_SUCCESS;
127
13.2k
}
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.
132
3.12M
spv_result_t IdPass(ValidationState_t& _, Instruction* inst) {
133
3.12M
  auto can_have_forward_declared_ids =
134
3.12M
      inst->opcode() == spv::Op::OpExtInst &&
135
3.12M
              spvExtInstIsDebugInfo(inst->ext_inst_type())
136
3.12M
          ? spvDbgInfoExtOperandCanBeForwardDeclaredFunction(
137
49
                inst->ext_inst_type(), inst->word(4))
138
3.12M
          : 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
3.12M
  uint32_t result_id = 0;
143
144
11.6M
  for (unsigned i = 0; i < inst->operands().size(); i++) {
145
8.53M
    const spv_parsed_operand_t& operand = inst->operand(i);
146
8.53M
    const spv_operand_type_t& type = operand.type;
147
    // We only care about Id operands, which are a single word.
148
8.53M
    const uint32_t operand_word = inst->word(operand.offset);
149
150
8.53M
    auto ret = SPV_ERROR_INTERNAL;
151
8.53M
    switch (type) {
152
1.78M
      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
1.78M
        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
1.78M
        ret = SPV_SUCCESS;
166
1.78M
        break;
167
4.36M
      case SPV_OPERAND_TYPE_ID:
168
4.36M
      case SPV_OPERAND_TYPE_MEMORY_SEMANTICS_ID:
169
4.36M
      case SPV_OPERAND_TYPE_SCOPE_ID:
170
4.36M
        if (const auto def = _.FindDef(operand_word)) {
171
2.71M
          const auto opcode = inst->opcode();
172
2.71M
          if (spvOpcodeGeneratesType(def->opcode()) &&
173
2.71M
              !spvOpcodeGeneratesType(opcode) && !spvOpcodeIsDebug(opcode) &&
174
2.71M
              !inst->IsDebugInfo() && !inst->IsNonSemantic() &&
175
2.71M
              !spvOpcodeIsDecoration(opcode) && opcode != spv::Op::OpFunction &&
176
2.71M
              opcode != spv::Op::OpCooperativeMatrixLengthNV &&
177
2.71M
              !(opcode == spv::Op::OpSpecConstantOp &&
178
40
                spv::Op(inst->word(3)) ==
179
37
                    spv::Op::OpCooperativeMatrixLengthNV)) {
180
37
            return _.diag(SPV_ERROR_INVALID_ID, inst)
181
37
                   << "Operand " << _.getIdName(operand_word)
182
37
                   << " cannot be a type";
183
2.71M
          } else if (def->type_id() == 0 && !spvOpcodeGeneratesType(opcode) &&
184
2.71M
                     !spvOpcodeIsDebug(opcode) && !inst->IsDebugInfo() &&
185
2.71M
                     !inst->IsNonSemantic() && !spvOpcodeIsDecoration(opcode) &&
186
2.71M
                     !spvOpcodeIsBranch(opcode) && opcode != spv::Op::OpPhi &&
187
2.71M
                     opcode != spv::Op::OpExtInst &&
188
2.71M
                     opcode != spv::Op::OpExtInstImport &&
189
2.71M
                     opcode != spv::Op::OpSelectionMerge &&
190
2.71M
                     opcode != spv::Op::OpLoopMerge &&
191
2.71M
                     opcode != spv::Op::OpFunction &&
192
2.71M
                     opcode != spv::Op::OpCooperativeMatrixLengthNV &&
193
2.71M
                     !(opcode == spv::Op::OpSpecConstantOp &&
194
31
                       spv::Op(inst->word(3)) ==
195
28
                           spv::Op::OpCooperativeMatrixLengthNV)) {
196
28
            return _.diag(SPV_ERROR_INVALID_ID, inst)
197
28
                   << "Operand " << _.getIdName(operand_word)
198
28
                   << " requires a type";
199
2.71M
          } else if (def->IsNonSemantic() && !inst->IsNonSemantic()) {
200
12
            return _.diag(SPV_ERROR_INVALID_ID, inst)
201
12
                   << "Operand " << _.getIdName(operand_word)
202
12
                   << " in semantic instruction cannot be a non-semantic "
203
12
                      "instruction";
204
2.71M
          } else {
205
2.71M
            ret = SPV_SUCCESS;
206
2.71M
          }
207
2.71M
        } else if (can_have_forward_declared_ids(i)) {
208
1.65M
          if (spvOpcodeGeneratesType(inst->opcode()) &&
209
1.65M
              !_.IsForwardPointer(operand_word)) {
210
461
            ret = _.diag(SPV_ERROR_INVALID_ID, inst)
211
461
                  << "Operand " << _.getIdName(operand_word)
212
461
                  << " requires a previous definition";
213
1.65M
          } else {
214
1.65M
            ret = _.ForwardDeclareId(operand_word);
215
1.65M
          }
216
1.65M
        } else {
217
351
          ret = _.diag(SPV_ERROR_INVALID_ID, inst)
218
351
                << "ID " << _.getIdName(operand_word)
219
351
                << " has not been defined";
220
351
        }
221
4.36M
        break;
222
4.36M
      case SPV_OPERAND_TYPE_TYPE_ID:
223
1.20M
        if (_.IsDefinedId(operand_word)) {
224
1.19M
          auto* def = _.FindDef(operand_word);
225
1.19M
          if (!spvOpcodeGeneratesType(def->opcode())) {
226
11
            ret = _.diag(SPV_ERROR_INVALID_ID, inst)
227
11
                  << "ID " << _.getIdName(operand_word) << " is not a type id";
228
1.19M
          } else {
229
1.19M
            ret = SPV_SUCCESS;
230
1.19M
          }
231
1.19M
        } else {
232
517
          ret = _.diag(SPV_ERROR_INVALID_ID, inst)
233
517
                << "ID " << _.getIdName(operand_word)
234
517
                << " has not been defined";
235
517
        }
236
1.20M
        break;
237
1.17M
      default:
238
1.17M
        ret = SPV_SUCCESS;
239
1.17M
        break;
240
8.53M
    }
241
8.53M
    if (SPV_SUCCESS != ret) return ret;
242
8.53M
  }
243
3.11M
  if (result_id) _.RemoveIfForwardDeclared(result_id);
244
245
3.11M
  return SPV_SUCCESS;
246
3.12M
}
247
248
}  // namespace val
249
}  // namespace spvtools