mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-18 04:53:06 +01:00
251 lines
9.2 KiB
C++
251 lines
9.2 KiB
C++
// 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
|