| 38 | namespace { |
| 39 | |
| 40 | spv_result_t ValidatePhi(ValidationState_t& _, const Instruction* inst) { |
| 41 | auto block = inst->block(); |
| 42 | size_t num_in_ops = inst->words().size() - 3; |
| 43 | if (num_in_ops % 2 != 0) { |
| 44 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
| 45 | << "OpPhi does not have an equal number of incoming values and " |
| 46 | "basic blocks."; |
| 47 | } |
| 48 | |
| 49 | if (_.IsVoidType(inst->type_id())) { |
| 50 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
| 51 | << "OpPhi must not have void result type"; |
| 52 | } |
| 53 | if (_.IsPointerType(inst->type_id()) && |
| 54 | _.addressing_model() == spv::AddressingModel::Logical) { |
| 55 | if (!_.features().variable_pointers) { |
| 56 | return _.diag(SPV_ERROR_INVALID_DATA, inst) |
| 57 | << "Using pointers with OpPhi requires capability " |
| 58 | << "VariablePointers or VariablePointersStorageBuffer"; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | const Instruction* type_inst = _.FindDef(inst->type_id()); |
| 63 | assert(type_inst); |
| 64 | const spv::Op type_opcode = type_inst->opcode(); |
| 65 | |
| 66 | if (!_.options()->before_hlsl_legalization && |
| 67 | !_.HasCapability(spv::Capability::BindlessTextureNV)) { |
| 68 | if (type_opcode == spv::Op::OpTypeSampledImage || |
| 69 | (_.HasCapability(spv::Capability::Shader) && |
| 70 | (type_opcode == spv::Op::OpTypeImage || |
| 71 | type_opcode == spv::Op::OpTypeSampler))) { |
| 72 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
| 73 | << "Result type cannot be Op" << spvOpcodeString(type_opcode); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | // Create a uniqued vector of predecessor ids for comparison against |
| 78 | // incoming values. OpBranchConditional %cond %label %label produces two |
| 79 | // predecessors in the CFG. |
| 80 | std::vector<uint32_t> pred_ids; |
| 81 | std::transform(block->predecessors()->begin(), block->predecessors()->end(), |
| 82 | std::back_inserter(pred_ids), |
| 83 | [](const BasicBlock* b) { return b->id(); }); |
| 84 | std::sort(pred_ids.begin(), pred_ids.end()); |
| 85 | pred_ids.erase(std::unique(pred_ids.begin(), pred_ids.end()), pred_ids.end()); |
| 86 | |
| 87 | size_t num_edges = num_in_ops / 2; |
| 88 | if (num_edges != pred_ids.size()) { |
| 89 | return _.diag(SPV_ERROR_INVALID_ID, inst) |
| 90 | << "OpPhi's number of incoming blocks (" << num_edges |
| 91 | << ") does not match block's predecessor count (" |
| 92 | << block->predecessors()->size() << ")."; |
| 93 | } |
| 94 | |
| 95 | std::unordered_set<uint32_t> observed_predecessors; |
| 96 | |
| 97 | for (size_t i = 3; i < inst->words().size(); ++i) { |
no test coverage detected