| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- // Copyright (c) 2023-2025 Arm Ltd.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- // Validates correctness of tensor instructions.
- #include "source/opcode.h"
- #include "source/val/validate.h"
- #include "source/val/validation_state.h"
- namespace spvtools {
- namespace val {
- namespace {
- bool IsRankedTensor(ValidationState_t& _, uint32_t id) {
- auto inst = _.FindDef(id);
- if (!inst || inst->opcode() != spv::Op::OpTypeTensorARM ||
- inst->words().size() <= 3) {
- return false;
- }
- return true;
- }
- uint64_t GetTensorTypeRank(ValidationState_t& _, uint32_t id) {
- auto inst = _.FindDef(id);
- if (!inst || inst->opcode() != spv::Op::OpTypeTensorARM ||
- inst->words().size() <= 3) {
- return 0;
- }
- uint64_t rank = 0;
- if (!_.EvalConstantValUint64(inst->word(3), &rank)) {
- return 0;
- }
- return rank;
- }
- bool IsScalarTypeOrOrArrayOfScalarType(ValidationState_t& _, uint32_t id) {
- auto inst = _.FindDef(id);
- if (!inst) {
- return false;
- }
- return _.IsScalarType(id) || (inst->opcode() == spv::Op::OpTypeArray &&
- _.IsScalarType(inst->word(2)));
- }
- spv_result_t ValidateTensorRead(ValidationState_t& _, const Instruction* inst) {
- // Result Type must be a scalar type or array of scalar type.
- if (!IsScalarTypeOrOrArrayOfScalarType(_, inst->type_id())) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Result Type to be a scalar type or array of "
- "scalar type.";
- }
- // Tensor must be a Ranked Tensor.
- auto op_tensor = inst->word(3);
- auto inst_tensor = _.FindDef(op_tensor);
- if (!inst_tensor || !IsRankedTensor(_, inst_tensor->type_id())) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Tensor to be an OpTypeTensorARM whose Rank is "
- "specified";
- }
- // The scalar type must be the same as the Element Type of Tensor.
- if (_.GetComponentType(inst_tensor->type_id()) !=
- _.GetComponentType(inst->type_id())) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Result Type to be the same as the Element Type of "
- "Tensor.";
- }
- // Coordinates is an array whose Element Type must be an integer type and
- // whose Length must be equal to the Rank of Tensor.
- auto op_coord = inst->word(4);
- auto inst_coord = _.FindDef(op_coord);
- auto tensor_rank = GetTensorTypeRank(_, inst_tensor->type_id());
- if (!_.IsIntArrayType(inst_coord->type_id(), tensor_rank)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Coordinates to be an array whose Element Type is an "
- "integer type and whose Length is equal to the Rank of Tensor.";
- }
- // Validate Tensor Operands
- if (inst->words().size() > 5) {
- auto toperands = static_cast<spv::TensorOperandsMask>(inst->word(5));
- if ((toperands & spv::TensorOperandsMask::OutOfBoundsValueARM) !=
- spv::TensorOperandsMask::MaskNone) {
- if (inst->words().size() < 7) {
- return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "A value must be provided after the OutOfBoundsValueARM "
- "Tensor Operand.";
- }
- auto op_oobval = inst->word(6);
- auto inst_oobval = _.FindDef(op_oobval);
- if (_.GetComponentType(inst_tensor->type_id()) !=
- _.GetComponentType(inst_oobval->type_id())) {
- return _.diag(SPV_ERROR_INVALID_ID, inst)
- << "Expected the type of the OutOfBoundsValueARM value to be "
- "the same "
- "as the Element Type of Tensor.";
- }
- }
- if ((toperands & spv::TensorOperandsMask::MakeElementAvailableARM) !=
- spv::TensorOperandsMask::MaskNone) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "MakeElementAvailableARM cannot be used with OpTensorReadARM.";
- }
- if (((toperands & spv::TensorOperandsMask::MakeElementVisibleARM) !=
- spv::TensorOperandsMask::MaskNone) &&
- ((toperands & spv::TensorOperandsMask::NonPrivateElementARM) ==
- spv::TensorOperandsMask::MaskNone)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "MakeElementAvailableARM requires NonPrivateElementARM.";
- }
- }
- return SPV_SUCCESS;
- }
- spv_result_t ValidateTensorWrite(ValidationState_t& _,
- const Instruction* inst) {
- // Tensor must be a Ranked Tensor.
- auto op_tensor = inst->word(1);
- auto inst_tensor = _.FindDef(op_tensor);
- if (!IsRankedTensor(_, inst_tensor->type_id())) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Tensor to be an OpTypeTensorARM whose Rank is "
- "specified";
- }
- // Coordinates is an array whose Element Type must be an integer type and
- // whose Length must be equal to the Rank of Tensor.
- auto op_coord = inst->word(2);
- auto inst_coord = _.FindDef(op_coord);
- auto tensor_rank = GetTensorTypeRank(_, inst_tensor->type_id());
- if (!_.IsIntArrayType(inst_coord->type_id(), tensor_rank)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Coordinates to be an array whose Element Type is an "
- "integer type and whose Length is equal to the Rank of Tensor.";
- }
- // Object must be an object of scalar type or array of scalar type.
- // The scalar type must be the same as the Element Type of Tensor.
- auto op_object = inst->word(3);
- auto inst_object = _.FindDef(op_object);
- if (!IsScalarTypeOrOrArrayOfScalarType(_, inst_object->type_id()) ||
- (_.GetComponentType(inst_object->type_id()) !=
- _.GetComponentType(inst_tensor->type_id()))) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Object to be a scalar type or array of scalar "
- "type that is the same as the Element Type of Tensor.";
- }
- // Validate Tensor Operands
- if (inst->words().size() > 5) {
- auto toperands = static_cast<spv::TensorOperandsMask>(inst->word(4));
- if ((toperands & spv::TensorOperandsMask::OutOfBoundsValueARM) !=
- spv::TensorOperandsMask::MaskNone) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "OutOfBoundsValue Tensor Operand not allowed with "
- "OpTensorWriteARM.";
- }
- if ((toperands & spv::TensorOperandsMask::MakeElementVisibleARM) !=
- spv::TensorOperandsMask::MaskNone) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "MakeElementVisibleARM not allowed with OpTensorWriteARM.";
- }
- if (((toperands & spv::TensorOperandsMask::MakeElementAvailableARM) !=
- spv::TensorOperandsMask::MaskNone) &&
- ((toperands & spv::TensorOperandsMask::NonPrivateElementARM) ==
- spv::TensorOperandsMask::MaskNone)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "MakeElementAvailableARM requires NonPrivateElementARM.";
- }
- }
- return SPV_SUCCESS;
- }
- spv_result_t ValidateTensorQuerySize(ValidationState_t& _,
- const Instruction* inst) {
- // Check result type
- if (!_.IsIntScalarType(inst->type_id())) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Result Type to be an integer type scalar";
- }
- // Check Tensor operand
- auto op_tensor = inst->word(3);
- auto inst_tensor = _.FindDef(op_tensor);
- if (!inst_tensor || !IsRankedTensor(_, inst_tensor->type_id())) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Expected Tensor to be an OpTypeTensorARM whose Rank is "
- "specified";
- }
- // Check Dimension operand
- auto op_dim = inst->word(4);
- auto inst_dim = _.FindDef(op_dim);
- if (!spvOpcodeIsConstant(inst_dim->opcode()) ||
- !_.IsIntScalarType(inst_dim->type_id())) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Dimension must come from a constant instruction of scalar "
- "integer type.";
- }
- auto inst_tensor_type = _.FindDef(inst_tensor->type_id());
- auto op_tensor_rank = inst_tensor_type->word(3);
- uint64_t tensor_rank = 0;
- uint64_t dim;
- if (_.EvalConstantValUint64(op_tensor_rank, &tensor_rank) &&
- _.EvalConstantValUint64(op_dim, &dim) && (dim >= tensor_rank)) {
- return _.diag(SPV_ERROR_INVALID_DATA, inst)
- << "Dimension (" << dim << ") must be less than the Rank of Tensor ("
- << tensor_rank << ").";
- }
- return SPV_SUCCESS;
- }
- } // namespace
- // Validates correctness of tensor instructions.
- spv_result_t TensorPass(ValidationState_t& _, const Instruction* inst) {
- (void)_;
- const spv::Op opcode = inst->opcode();
- switch (opcode) {
- case spv::Op::OpTensorReadARM:
- return ValidateTensorRead(_, inst);
- case spv::Op::OpTensorWriteARM:
- return ValidateTensorWrite(_, inst);
- case spv::Op::OpTensorQuerySizeARM:
- return ValidateTensorQuerySize(_, inst);
- default:
- break;
- }
- return SPV_SUCCESS;
- }
- } // namespace val
- } // namespace spvtools
|