Coverage Report

Created: 2026-09-28 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/spirv-tools/source/val/validate_logicals.cpp
Line
Count
Source
1
// Copyright (c) 2017 Google 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 logical SPIR-V instructions.
16
17
#include "source/opcode.h"
18
#include "source/val/instruction.h"
19
#include "source/val/validate.h"
20
#include "source/val/validation_state.h"
21
22
namespace spvtools {
23
namespace val {
24
25
26
spv_result_t ValidateAnyAll(ValidationState_t& _, const Instruction* inst) {
26
26
  const spv::Op opcode = inst->opcode();
27
26
  const uint32_t result_type = inst->type_id();
28
26
  if (!_.IsBoolScalarType(result_type))
29
4
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
30
4
           << "Expected bool scalar type as Result Type: "
31
4
           << spvOpcodeString(opcode);
32
33
22
  const uint32_t vector_type = _.GetOperandTypeId(inst, 2);
34
22
  if (!vector_type || !_.IsBoolVectorType(vector_type))
35
7
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
36
7
           << "Expected operand to be vector bool: " << spvOpcodeString(opcode);
37
15
  return SPV_SUCCESS;
38
22
}
39
40
spv_result_t ValidateSingleStatus(ValidationState_t& _,
41
2.97k
                                  const Instruction* inst) {
42
2.97k
  const spv::Op opcode = inst->opcode();
43
2.97k
  const uint32_t result_type = inst->type_id();
44
2.97k
  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
45
29
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
46
29
           << "Expected bool scalar or vector type as Result Type: "
47
29
           << spvOpcodeString(opcode);
48
49
2.94k
  const uint32_t operand_type = _.GetOperandTypeId(inst, 2);
50
2.94k
  if (!operand_type || (!_.IsFloatScalarType(operand_type) &&
51
7
                        !_.IsFloatVectorType(operand_type)))
52
7
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
53
7
           << "Expected operand to be scalar or vector float: "
54
7
           << spvOpcodeString(opcode);
55
56
2.93k
  if (_.GetDimension(result_type) != _.GetDimension(operand_type))
57
5
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
58
5
           << "Expected vector sizes of Result Type and the operand to be "
59
5
              "equal: "
60
5
           << spvOpcodeString(opcode);
61
62
2.93k
  return SPV_SUCCESS;
63
2.93k
}
64
65
spv_result_t ValidateFloatCompare(ValidationState_t& _,
66
20.0k
                                  const Instruction* inst) {
67
20.0k
  const spv::Op opcode = inst->opcode();
68
20.0k
  const uint32_t result_type = inst->type_id();
69
20.0k
  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
70
47
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
71
47
           << "Expected bool scalar or vector type as Result Type: "
72
47
           << spvOpcodeString(opcode);
73
74
20.0k
  const uint32_t left_operand_type = _.GetOperandTypeId(inst, 2);
75
20.0k
  if (!left_operand_type || (!_.IsFloatScalarType(left_operand_type) &&
76
31
                             !_.IsFloatVectorType(left_operand_type)))
77
29
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
78
29
           << "Expected operands to be scalar or vector float: "
79
29
           << spvOpcodeString(opcode);
80
81
20.0k
  if (_.GetDimension(result_type) != _.GetDimension(left_operand_type))
82
5
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
83
5
           << "Expected vector sizes of Result Type and the operands to be "
84
5
              "equal: "
85
5
           << spvOpcodeString(opcode);
86
87
20.0k
  if (left_operand_type != _.GetOperandTypeId(inst, 3))
88
9
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
89
9
           << "Expected left and right operands to have the same type: "
90
9
           << spvOpcodeString(opcode);
91
19.9k
  return SPV_SUCCESS;
92
20.0k
}
93
94
spv_result_t ValidateLogicalCompare(ValidationState_t& _,
95
                                    const Instruction* inst,
96
4.73k
                                    uint32_t operand_index = 2) {
97
4.73k
  const spv::Op opcode = inst->opcode();
98
4.73k
  const uint32_t result_type = inst->type_id();
99
4.73k
  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
100
35
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
101
35
           << "Expected bool scalar or vector type as Result Type: "
102
35
           << spvOpcodeString(opcode);
103
104
4.69k
  const uint32_t operand_1 = _.GetOperandTypeId(inst, operand_index);
105
4.69k
  const uint32_t operand_2 = _.GetOperandTypeId(inst, operand_index + 1);
106
4.69k
  if (result_type != operand_1 || result_type != operand_2)
107
23
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
108
23
           << "Expected both operands to be of Result Type: "
109
23
           << spvOpcodeString(opcode);
110
4.67k
  return SPV_SUCCESS;
111
4.69k
}
112
113
spv_result_t ValidateLogicalNot(ValidationState_t& _, const Instruction* inst,
114
6.93k
                                uint32_t operand_index = 2) {
115
6.93k
  const spv::Op opcode = inst->opcode();
116
6.93k
  const uint32_t result_type = inst->type_id();
117
6.93k
  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
118
16
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
119
16
           << "Expected bool scalar or vector type as Result Type: "
120
16
           << spvOpcodeString(opcode);
121
122
6.92k
  if (result_type != _.GetOperandTypeId(inst, operand_index))
123
8
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
124
8
           << "Expected operand to be of Result Type: "
125
8
           << spvOpcodeString(opcode);
126
6.91k
  return SPV_SUCCESS;
127
6.92k
}
128
129
spv_result_t ValidateSelect(ValidationState_t& _, const Instruction* inst,
130
5.70k
                            uint32_t operand_index = 2) {
131
5.70k
  const spv::Op opcode = inst->opcode();
132
5.70k
  const uint32_t result_type = inst->type_id();
133
5.70k
  uint32_t dimension = 1;
134
5.70k
  const Instruction* type_inst = _.FindDef(result_type);
135
5.70k
  assert(type_inst);
136
137
5.70k
  const auto composites = _.features().select_between_composites;
138
5.70k
  auto fail = [&_, composites, inst, opcode]() -> spv_result_t {
139
13
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
140
13
           << "Expected scalar or " << (composites ? "composite" : "vector")
141
13
           << " type as Result Type: " << spvOpcodeString(opcode);
142
13
  };
143
144
5.70k
  const spv::Op type_opcode = type_inst->opcode();
145
5.70k
  switch (type_opcode) {
146
0
    case spv::Op::OpTypeUntypedPointerKHR:
147
5
    case spv::Op::OpTypePointer: {
148
5
      if (_.addressing_model() == spv::AddressingModel::Logical &&
149
3
          !_.HasCapability(spv::Capability::VariablePointersStorageBuffer))
150
3
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
151
3
               << "Using pointers with OpSelect requires capability "
152
3
               << "VariablePointers or VariablePointersStorageBuffer";
153
2
      break;
154
5
    }
155
156
2
    case spv::Op::OpTypeSampledImage:
157
0
    case spv::Op::OpTypeImage:
158
2
    case spv::Op::OpTypeSampler: {
159
2
      if (!_.HasCapability(spv::Capability::BindlessTextureNV))
160
2
        return _.diag(SPV_ERROR_INVALID_DATA, inst)
161
2
               << "Using image/sampler with OpSelect requires capability "
162
2
               << "BindlessTextureNV";
163
0
      break;
164
2
    }
165
166
5
    case spv::Op::OpTypeVector: {
167
5
      dimension = type_inst->word(3);
168
5
      break;
169
2
    }
170
0
    case spv::Op::OpTypeVectorIdEXT: {
171
0
      dimension = _.GetDimension(result_type);
172
0
      break;
173
2
    }
174
175
65
    case spv::Op::OpTypeBool:
176
4.13k
    case spv::Op::OpTypeInt:
177
5.68k
    case spv::Op::OpTypeFloat: {
178
5.68k
      break;
179
4.13k
    }
180
181
    // Not RuntimeArray because of other rules.
182
3
    case spv::Op::OpTypeArray:
183
6
    case spv::Op::OpTypeMatrix:
184
9
    case spv::Op::OpTypeStruct: {
185
9
      if (!composites) return fail();
186
0
      break;
187
9
    }
188
189
4
    default:
190
4
      return fail();
191
5.70k
  }
192
193
5.69k
  const uint32_t condition_type = _.GetOperandTypeId(inst, operand_index);
194
5.69k
  const uint32_t left_type = _.GetOperandTypeId(inst, operand_index + 1);
195
5.69k
  const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 2);
196
197
5.69k
  if (!condition_type || (!_.IsBoolScalarType(condition_type) &&
198
10
                          !_.IsBoolVectorType(condition_type)))
199
15
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
200
15
           << "Expected bool scalar or vector type as condition: "
201
15
           << spvOpcodeString(opcode);
202
203
5.67k
  if (_.GetDimension(condition_type) != dimension) {
204
    // If the condition is a vector type, then the result must also be a
205
    // vector with matching dimensions. In SPIR-V 1.4, a scalar condition
206
    // can be used to select between vector types. |composites| is a
207
    // proxy for SPIR-V 1.4 functionality.
208
4
    if (!composites || _.IsBoolVectorType(condition_type)) {
209
4
      return _.diag(SPV_ERROR_INVALID_DATA, inst)
210
4
             << "Expected vector sizes of Result Type and the condition "
211
4
                "to be equal: "
212
4
             << spvOpcodeString(opcode);
213
4
    }
214
4
  }
215
216
5.67k
  if (result_type != left_type || result_type != right_type)
217
17
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
218
17
           << "Expected both objects to be of Result Type: "
219
17
           << spvOpcodeString(opcode);
220
5.65k
  return SPV_SUCCESS;
221
5.67k
}
222
223
spv_result_t ValidateIntCompare(ValidationState_t& _, const Instruction* inst,
224
77.9k
                                uint32_t operand_index = 2) {
225
77.9k
  const spv::Op opcode = inst->opcode();
226
77.9k
  const uint32_t result_type = inst->type_id();
227
77.9k
  if (!_.IsBoolScalarType(result_type) && !_.IsBoolVectorType(result_type))
228
66
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
229
66
           << "Expected bool scalar or vector type as Result Type: "
230
66
           << spvOpcodeString(opcode);
231
232
77.8k
  const uint32_t left_type = _.GetOperandTypeId(inst, operand_index);
233
77.8k
  const uint32_t right_type = _.GetOperandTypeId(inst, operand_index + 1);
234
235
77.8k
  if (!left_type ||
236
77.8k
      (!_.IsIntScalarType(left_type) && !_.IsIntVectorType(left_type)))
237
24
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
238
24
           << "Expected operands to be scalar or vector int: "
239
24
           << spvOpcodeString(opcode);
240
241
77.8k
  if (_.GetDimension(result_type) != _.GetDimension(left_type))
242
7
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
243
7
           << "Expected vector sizes of Result Type and the operands to be"
244
7
           << " equal: " << spvOpcodeString(opcode);
245
246
77.8k
  if (!right_type ||
247
77.8k
      (!_.IsIntScalarType(right_type) && !_.IsIntVectorType(right_type)))
248
8
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
249
8
           << "Expected operands to be scalar or vector int: "
250
8
           << spvOpcodeString(opcode);
251
252
77.8k
  if (_.GetDimension(result_type) != _.GetDimension(right_type))
253
4
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
254
4
           << "Expected vector sizes of Result Type and the operands to be"
255
4
           << " equal: " << spvOpcodeString(opcode);
256
257
77.8k
  if (_.GetBitWidth(left_type) != _.GetBitWidth(right_type))
258
1
    return _.diag(SPV_ERROR_INVALID_DATA, inst)
259
1
           << "Expected both operands to have the same component bit "
260
1
              "width: "
261
1
           << spvOpcodeString(opcode);
262
77.8k
  return SPV_SUCCESS;
263
77.8k
}
264
265
// Validates correctness of logical instructions.
266
14.3M
spv_result_t LogicalsPass(ValidationState_t& _, const Instruction* inst) {
267
14.3M
  switch (inst->opcode()) {
268
9
    case spv::Op::OpAny:
269
26
    case spv::Op::OpAll:
270
26
      return ValidateAnyAll(_, inst);
271
1.98k
    case spv::Op::OpIsNan:
272
2.96k
    case spv::Op::OpIsInf:
273
2.96k
    case spv::Op::OpIsFinite:
274
2.96k
    case spv::Op::OpIsNormal:
275
2.97k
    case spv::Op::OpSignBitSet:
276
2.97k
      return ValidateSingleStatus(_, inst);
277
485
    case spv::Op::OpFOrdEqual:
278
795
    case spv::Op::OpFUnordEqual:
279
970
    case spv::Op::OpFOrdNotEqual:
280
1.29k
    case spv::Op::OpFUnordNotEqual:
281
11.0k
    case spv::Op::OpFOrdLessThan:
282
11.3k
    case spv::Op::OpFUnordLessThan:
283
15.5k
    case spv::Op::OpFOrdGreaterThan:
284
15.9k
    case spv::Op::OpFUnordGreaterThan:
285
17.2k
    case spv::Op::OpFOrdLessThanEqual:
286
17.5k
    case spv::Op::OpFUnordLessThanEqual:
287
19.8k
    case spv::Op::OpFOrdGreaterThanEqual:
288
20.0k
    case spv::Op::OpFUnordGreaterThanEqual:
289
20.0k
    case spv::Op::OpLessOrGreater:
290
20.0k
    case spv::Op::OpOrdered:
291
20.0k
    case spv::Op::OpUnordered:
292
20.0k
      return ValidateFloatCompare(_, inst);
293
268
    case spv::Op::OpLogicalEqual:
294
659
    case spv::Op::OpLogicalNotEqual:
295
1.22k
    case spv::Op::OpLogicalOr:
296
4.71k
    case spv::Op::OpLogicalAnd:
297
4.71k
      return ValidateLogicalCompare(_, inst);
298
6.92k
    case spv::Op::OpLogicalNot:
299
6.92k
      return ValidateLogicalNot(_, inst);
300
5.70k
    case spv::Op::OpSelect:
301
5.70k
      return ValidateSelect(_, inst);
302
17.9k
    case spv::Op::OpIEqual:
303
24.3k
    case spv::Op::OpINotEqual:
304
26.6k
    case spv::Op::OpUGreaterThan:
305
28.0k
    case spv::Op::OpUGreaterThanEqual:
306
29.5k
    case spv::Op::OpULessThan:
307
30.9k
    case spv::Op::OpULessThanEqual:
308
39.6k
    case spv::Op::OpSGreaterThan:
309
43.6k
    case spv::Op::OpSGreaterThanEqual:
310
66.8k
    case spv::Op::OpSLessThan:
311
77.9k
    case spv::Op::OpSLessThanEqual:
312
77.9k
      return ValidateIntCompare(_, inst);
313
314
283
    case spv::Op::OpSpecConstantOp: {
315
283
      switch (inst->GetOperandAs<spv::Op>(2u)) {
316
3
        case spv::Op::OpLogicalEqual:
317
6
        case spv::Op::OpLogicalNotEqual:
318
9
        case spv::Op::OpLogicalOr:
319
15
        case spv::Op::OpLogicalAnd:
320
15
          return ValidateLogicalCompare(_, inst, 3);
321
11
        case spv::Op::OpLogicalNot:
322
11
          return ValidateLogicalNot(_, inst, 3);
323
0
        case spv::Op::OpSelect:
324
0
          return ValidateSelect(_, inst, 3);
325
3
        case spv::Op::OpIEqual:
326
6
        case spv::Op::OpINotEqual:
327
9
        case spv::Op::OpUGreaterThan:
328
12
        case spv::Op::OpUGreaterThanEqual:
329
17
        case spv::Op::OpULessThan:
330
20
        case spv::Op::OpULessThanEqual:
331
23
        case spv::Op::OpSGreaterThan:
332
26
        case spv::Op::OpSGreaterThanEqual:
333
29
        case spv::Op::OpSLessThan:
334
31
        case spv::Op::OpSLessThanEqual:
335
31
          return ValidateIntCompare(_, inst, 3);
336
226
        default:
337
226
          break;
338
283
      }
339
226
      break;
340
283
    }
341
342
14.2M
    default:
343
14.2M
      break;
344
14.3M
  }
345
346
14.2M
  return SPV_SUCCESS;
347
14.3M
}
348
349
}  // namespace val
350
}  // namespace spvtools