blob: d43f00661de914fefaf57764407100b474389c89 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#ifdef TVM_GRAPH_EXECUTOR_NNAPI
#include "nnapi_builder.h"
#include <android/log.h>
#include <tvm/runtime/logging.h>
#include <algorithm>
#include <iterator>
#include <utility>
#include "../json/json_runtime.h"
#include "nnapi_ops.h"
namespace tvm {
namespace runtime {
namespace contrib {
WrappedANeuralNetworksOperandType::WrappedANeuralNetworksOperandType(
int32_t tensor_type, std::vector<uint32_t> dimensions, float scale, int32_t zero_point)
: dimensions_(dimensions) {
ty_.type = tensor_type;
if (dimensions_.empty()) {
ty_.dimensions = nullptr;
} else {
ty_.dimensions = dimensions_.data();
}
ty_.dimensionCount = dimensions_.size();
ty_.scale = scale;
ty_.zeroPoint = zero_point;
}
WrappedANeuralNetworksOperandType::WrappedANeuralNetworksOperandType(
const WrappedANeuralNetworksOperandType& other)
: dimensions_(other.dimensions_), ty_(other.ty_) {
if (dimensions_.empty()) {
ty_.dimensions = nullptr;
} else {
ty_.dimensions = dimensions_.data();
}
}
WrappedANeuralNetworksOperandType& WrappedANeuralNetworksOperandType::operator=(
const WrappedANeuralNetworksOperandType& other) {
WrappedANeuralNetworksOperandType temp(other);
std::swap(*this, temp);
return *this;
}
const ANeuralNetworksOperandType* WrappedANeuralNetworksOperandType::Get() const { return &ty_; }
NNAPIOperand::NNAPIOperand(uint32_t index, const DLTensor* tensor)
: index_(index), scalar_(false), dimensions_(tensor->shape, tensor->shape + tensor->ndim) {
if (dimensions_.size() == 0) {
dimensions_.push_back(1);
}
tensor_type_ = TensorTypeFromDLDataType(tensor->dtype);
scale_ = 0.0;
zero_point_ = 0;
}
NNAPIOperand::NNAPIOperand(uint32_t index, const int64_t* shape, int ndim, DLDataType dtype)
: index_(index), scalar_(false), dimensions_(shape, shape + ndim) {
if (dimensions_.size() == 0) {
dimensions_.push_back(1);
}
tensor_type_ = TensorTypeFromDLDataType(dtype);
scale_ = 0.0;
zero_point_ = 0;
}
NNAPIOperand::NNAPIOperand(uint32_t index, int32_t tensor_type, std::vector<int64_t> dimensions,
float scale, int32_t zero_point)
: index_(index),
scalar_(false),
tensor_type_(tensor_type),
dimensions_(dimensions),
scale_(scale),
zero_point_(zero_point) {
if (dimensions_.size() == 0) {
dimensions_.push_back(1);
}
}
NNAPIOperand NNAPIOperand::Scalar(uint32_t index, int32_t tensor_type,
std::vector<int64_t> dimensions, float scale,
int32_t zero_point) {
NNAPIOperand operand(index, tensor_type, dimensions, scale, zero_point);
operand.dimensions_.clear();
operand.scalar_ = true;
return operand;
}
void NNAPIOperand::SetDimensions(std::vector<int64_t> dimensions) { dimensions_ = dimensions; }
WrappedANeuralNetworksOperandType NNAPIOperand::GetOperandType() const {
std::vector<uint32_t> dimensions(dimensions_.begin(), dimensions_.end());
return WrappedANeuralNetworksOperandType(tensor_type_, dimensions, scale_, zero_point_);
}
uint32_t NNAPIOperand::GetOperandIndex() const { return index_; }
const std::vector<int64_t>& NNAPIOperand::GetDimensions() const { return dimensions_; }
const float NNAPIOperand::GetScale() const { return scale_; }
const int32_t NNAPIOperand::GetZeroPoint() const { return zero_point_; }
int32_t NNAPIOperand::GetTensorType() const { return tensor_type_; }
bool NNAPIOperand::IsDynamicShape() const {
return std::any_of(dimensions_.begin(), dimensions_.end(), [](int64_t dim) { return dim == -1; });
}
NNAPIModelBuilder::NNAPIModelBuilder() {
ICHECK_EQ(ANeuralNetworksModel_create(&model_), ANEURALNETWORKS_NO_ERROR);
}
NNAPIModelBuilder::~NNAPIModelBuilder() { ANeuralNetworksModel_free(model_); }
NNAPIOperand NNAPIModelBuilder::CreateOperandWithValue(const DLTensor& tensor) {
NNAPIOperand operand(next_operand_index_++, &tensor);
const size_t operand_data_size = GetDataSize(tensor);
ICHECK_EQ(ANeuralNetworksModel_addOperand(model_, operand.GetOperandType().Get()),
ANEURALNETWORKS_NO_ERROR);
ICHECK_EQ(ANeuralNetworksModel_setOperandValue(model_, operand.GetOperandIndex(), tensor.data,
operand_data_size),
ANEURALNETWORKS_NO_ERROR);
return operand;
}
NNAPIOperand NNAPIModelBuilder::CreateOperandWithValue(int32_t tensor_type,
std::vector<int64_t> dimensions, float scale,
int32_t zero_point, const void* buffer,
size_t size) {
NNAPIOperand operand(next_operand_index_++, tensor_type, dimensions, scale, zero_point);
ICHECK_EQ(ANeuralNetworksModel_addOperand(model_, operand.GetOperandType().Get()),
ANEURALNETWORKS_NO_ERROR);
ICHECK_EQ(ANeuralNetworksModel_setOperandValue(model_, operand.GetOperandIndex(), buffer, size),
ANEURALNETWORKS_NO_ERROR);
return operand;
}
NNAPIOperand NNAPIModelBuilder::CreateScalarOperandWithValue(OperandCode operand_code,
const void* buffer, size_t size) {
NNAPIOperand operand = NNAPIOperand::Scalar(next_operand_index_++, operand_code, {}, 0.0f, 0);
ICHECK_EQ(ANeuralNetworksModel_addOperand(model_, operand.GetOperandType().Get()),
ANEURALNETWORKS_NO_ERROR);
ICHECK_EQ(ANeuralNetworksModel_setOperandValue(model_, operand.GetOperandIndex(), buffer, size),
ANEURALNETWORKS_NO_ERROR);
return operand;
}
NNAPIOperand NNAPIModelBuilder::CreateOperand(const DLTensor& tensor) {
NNAPIOperand operand(next_operand_index_++, tensor.shape, tensor.ndim, tensor.dtype);
ICHECK_EQ(ANeuralNetworksModel_addOperand(model_, operand.GetOperandType().Get()),
ANEURALNETWORKS_NO_ERROR);
return operand;
}
NNAPIOperand NNAPIModelBuilder::CreateOperand(const int64_t* shape, int ndim, DLDataType dtype) {
NNAPIOperand operand(next_operand_index_++, shape, ndim, dtype);
ICHECK_EQ(ANeuralNetworksModel_addOperand(model_, operand.GetOperandType().Get()),
ANEURALNETWORKS_NO_ERROR);
return operand;
}
NNAPIOperand NNAPIModelBuilder::CreateOperand(int32_t tensor_type, std::vector<int64_t> dimensions,
float scale, int32_t zero_point) {
NNAPIOperand operand(next_operand_index_++, tensor_type, dimensions, scale, zero_point);
ICHECK_EQ(ANeuralNetworksModel_addOperand(model_, operand.GetOperandType().Get()),
ANEURALNETWORKS_NO_ERROR);
return operand;
}
void NNAPIModelBuilder::AddOperation(ANeuralNetworksOperationType operation,
const std::vector<uint32_t> input_indicies,
const std::vector<uint32_t> output_indicies) {
ICHECK_EQ(ANeuralNetworksModel_addOperation(model_, operation, input_indicies.size(),
input_indicies.data(), output_indicies.size(),
output_indicies.data()),
ANEURALNETWORKS_NO_ERROR);
}
void NNAPIModelBuilder::Finish(const std::vector<NNAPIOperand>& model_input_operands,
const std::vector<NNAPIOperand>& model_output_operands) {
const auto model_input_indices = ExtractOperandIndices(model_input_operands);
const auto model_output_indices = ExtractOperandIndices(model_output_operands);
ICHECK_EQ(ANeuralNetworksModel_identifyInputsAndOutputs(
model_, model_input_indices.size(), model_input_indices.data(),
model_output_indices.size(), model_output_indices.data()),
ANEURALNETWORKS_NO_ERROR);
ICHECK_EQ(ANeuralNetworksModel_finish(model_), ANEURALNETWORKS_NO_ERROR);
}
ANeuralNetworksCompilation* NNAPIModelBuilder::Compile() {
ANeuralNetworksCompilation* compilation;
ICHECK_EQ(ANeuralNetworksCompilation_create(model_, &compilation), ANEURALNETWORKS_NO_ERROR);
ICHECK_EQ(ANeuralNetworksCompilation_setPreference(compilation,
ANEURALNETWORKS_PREFER_FAST_SINGLE_ANSWER),
ANEURALNETWORKS_NO_ERROR);
ICHECK_EQ(ANeuralNetworksCompilation_finish(compilation), ANEURALNETWORKS_NO_ERROR);
return compilation;
}
int32_t TensorTypeFromDLDataType(DLDataType ty) {
if (ty.code == kDLInt) {
if (ty.bits == 32) {
return ANEURALNETWORKS_TENSOR_INT32;
} else {
ICHECK(false) << "Unsupported bit width " << ty.bits << " for NNAPI integer tensor";
}
} else if (ty.code == kDLUInt) {
if (ty.bits == 1) {
return ANEURALNETWORKS_TENSOR_BOOL8;
} else {
ICHECK(false) << "Unsupported bit width " << ty.bits << " for NNAPI unsigned integer tensor";
}
} else if (ty.code == kDLFloat) {
if (ty.bits == 32) {
return ANEURALNETWORKS_TENSOR_FLOAT32;
} else if (ty.bits == 16) {
return ANEURALNETWORKS_TENSOR_FLOAT16;
} else {
ICHECK(false) << "Unsupported bit width " << ty.bits << " for NNAPI integer tensor";
}
} else {
ICHECK(false) << "Unsupported DLDataTypeCode for NNAPI: " << ty.code;
}
}
std::vector<uint32_t> ExtractOperandIndices(const std::vector<NNAPIOperand>& operands) {
std::vector<uint32_t> indices;
indices.reserve(operands.size());
std::transform(operands.begin(), operands.end(), std::back_inserter(indices),
[](const NNAPIOperand& operand) { return operand.GetOperandIndex(); });
return indices;
}
} // namespace contrib
} // namespace runtime
} // namespace tvm
#endif // TVM_GRAPH_EXECUTOR_NNAPI