| /* | |
| * Copyright (C) 2017 The Android Open Source Project | |
| * | |
| * 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. | |
| */ | |
| #define LOG_TAG "Utils" | |
| #include "LegacyUtils.h" | |
| #include <android-base/logging.h> | |
| #include <android-base/properties.h> | |
| #include <errno.h> | |
| #include <nnapi/TypeUtils.h> | |
| #include <poll.h> | |
| #include <algorithm> | |
| #include <functional> | |
| #include <limits> | |
| #include <numeric> | |
| #include <string> | |
| #include <tuple> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <vector> | |
| #include "ControlFlow.h" | |
| #include "NeuralNetworks.h" | |
| #include "NeuralNetworksOEM.h" | |
| #include "OperationResolver.h" | |
| namespace android { | |
| namespace nn { | |
| std::ostream& operator<<(std::ostream& os, const HalVersion& halVersion) { | |
| switch (halVersion) { | |
| case HalVersion::UNKNOWN: | |
| return os << "UNKNOWN HAL version"; | |
| case HalVersion::V1_0: | |
| return os << "HAL version 1.0"; | |
| case HalVersion::V1_1: | |
| return os << "HAL version 1.1"; | |
| case HalVersion::V1_2: | |
| return os << "HAL version 1.2"; | |
| case HalVersion::V1_3: | |
| return os << "HAL version 1.3"; | |
| case HalVersion::AIDL_V1: | |
| return os << "HAL version AIDL_V1"; | |
| case HalVersion::AIDL_V2: | |
| return os << "HAL version AIDL_V2"; | |
| case HalVersion::AIDL_UNSTABLE: | |
| return os << "HAL uses unstable AIDL"; | |
| } | |
| return os << "HalVersion{" << static_cast<int32_t>(halVersion) << "}"; | |
| } | |
| Duration makeTimeoutDuration(uint64_t nanoseconds) { | |
| constexpr auto kMaxCount = Duration::max().count(); | |
| using CommonType = std::common_type_t<Duration::rep, uint64_t>; | |
| const auto count = std::min<CommonType>(kMaxCount, nanoseconds); | |
| return Duration{static_cast<Duration::rep>(count)}; | |
| } | |
| OptionalDuration makeTimeoutDuration(int64_t nanoseconds) { | |
| CHECK_GE(nanoseconds, -1); | |
| if (nanoseconds == -1) { | |
| return OptionalDuration{}; | |
| } | |
| return makeTimeoutDuration(static_cast<uint64_t>(nanoseconds)); | |
| } | |
| TimePoint makeDeadline(Duration duration) { | |
| constexpr auto kMaxTime = TimePoint::max(); | |
| const auto currentTime = Clock::now(); | |
| // If there would be an overflow, use the max value. | |
| if (duration > kMaxTime - currentTime) { | |
| return kMaxTime; | |
| } | |
| return currentTime + duration; | |
| } | |
| bool hasDeadlinePassed(const OptionalTimePoint& deadline) { | |
| if (!deadline.has_value()) { | |
| return false; | |
| } | |
| return Clock::now() >= *deadline; | |
| } | |
| static bool isExtensionOperandType(int32_t type) { | |
| return (static_cast<uint32_t>(type) >> kExtensionTypeBits) != 0; | |
| } | |
| static bool isExtensionOperationType(ANeuralNetworksOperationType type) { | |
| return (static_cast<uint32_t>(type) >> kExtensionTypeBits) != 0; | |
| } | |
| bool isExtensionOperandType(OperandType type) { | |
| return isExtensionOperandType(static_cast<int32_t>(type)); | |
| } | |
| bool isExtensionOperationType(OperationType type) { | |
| return isExtensionOperationType(static_cast<int32_t>(type)); | |
| } | |
| namespace { | |
| template <typename EntryType, uint32_t entryCount, uint32_t entryCountOEM> | |
| EntryType tableLookup(const EntryType (&table)[entryCount], | |
| const EntryType (&tableOEM)[entryCountOEM], uint32_t code) { | |
| if (code < entryCount) { | |
| return table[code]; | |
| } else if (code >= kOEMCodeBase && (code - kOEMCodeBase) < entryCountOEM) { | |
| return tableOEM[code - kOEMCodeBase]; | |
| } else { | |
| LOG(FATAL) << "tableLookup: bad code"; | |
| return EntryType(); | |
| } | |
| } | |
| static Version convert(HalVersion halVersion) { | |
| switch (halVersion) { | |
| case HalVersion::UNKNOWN: | |
| break; | |
| case HalVersion::V1_0: | |
| return kVersionFeatureLevel1; | |
| case HalVersion::V1_1: | |
| return kVersionFeatureLevel2; | |
| case HalVersion::V1_2: | |
| return kVersionFeatureLevel3; | |
| case HalVersion::V1_3: | |
| return kVersionFeatureLevel4; | |
| case HalVersion::AIDL_V1: | |
| return kVersionFeatureLevel5; | |
| case HalVersion::AIDL_V2: | |
| return kVersionFeatureLevel6; | |
| case HalVersion::AIDL_UNSTABLE: | |
| return kVersionFeatureLevel7; | |
| } | |
| LOG(FATAL) << "Cannot convert " << halVersion; | |
| return {}; | |
| } | |
| class OperationValidationContext : public IOperationValidationContext { | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(OperationValidationContext); | |
| public: | |
| OperationValidationContext(const char* operationName, uint32_t inputCount, | |
| const uint32_t* inputIndexes, uint32_t outputCount, | |
| const uint32_t* outputIndexes, const Operand* operands) | |
| : operationName(operationName), | |
| inputCount(inputCount), | |
| inputIndexes(inputIndexes), | |
| outputCount(outputCount), | |
| outputIndexes(outputIndexes), | |
| operands(operands) {} | |
| const char* getOperationName() const override; | |
| uint32_t getNumInputs() const override; | |
| OperandType getInputType(uint32_t index) const override; | |
| Shape getInputShape(uint32_t index) const override; | |
| const Operand::ExtraParams& getInputExtraParams(uint32_t index) const override; | |
| uint32_t getNumOutputs() const override; | |
| OperandType getOutputType(uint32_t index) const override; | |
| Shape getOutputShape(uint32_t index) const override; | |
| private: | |
| const Operand* getInputOperand(uint32_t index) const; | |
| const Operand* getOutputOperand(uint32_t index) const; | |
| const char* operationName; | |
| uint32_t inputCount; | |
| const uint32_t* inputIndexes; | |
| uint32_t outputCount; | |
| const uint32_t* outputIndexes; | |
| const Operand* operands; | |
| }; | |
| const char* OperationValidationContext::getOperationName() const { | |
| return operationName; | |
| } | |
| const Operand* OperationValidationContext::getInputOperand(uint32_t index) const { | |
| CHECK(index < static_cast<uint32_t>(inputCount)); | |
| return &operands[inputIndexes[index]]; | |
| } | |
| const Operand* OperationValidationContext::getOutputOperand(uint32_t index) const { | |
| CHECK(index < static_cast<uint32_t>(outputCount)); | |
| return &operands[outputIndexes[index]]; | |
| } | |
| uint32_t OperationValidationContext::getNumInputs() const { | |
| return inputCount; | |
| } | |
| uint32_t OperationValidationContext::getNumOutputs() const { | |
| return outputCount; | |
| } | |
| OperandType OperationValidationContext::getInputType(uint32_t index) const { | |
| return getInputOperand(index)->type; | |
| } | |
| Shape OperationValidationContext::getInputShape(uint32_t index) const { | |
| const Operand* operand = getInputOperand(index); | |
| return {operand->type, operand->dimensions, operand->scale, operand->zeroPoint, | |
| operand->extraParams}; | |
| } | |
| const Operand::ExtraParams& OperationValidationContext::getInputExtraParams(uint32_t index) const { | |
| return getInputOperand(index)->extraParams; | |
| } | |
| OperandType OperationValidationContext::getOutputType(uint32_t index) const { | |
| return getOutputOperand(index)->type; | |
| } | |
| Shape OperationValidationContext::getOutputShape(uint32_t index) const { | |
| const Operand* operand = getOutputOperand(index); | |
| return {operand->type, operand->dimensions, operand->scale, operand->zeroPoint, | |
| operand->extraParams}; | |
| } | |
| }; // anonymous namespace | |
| #define COUNT(X) (sizeof(X) / sizeof(X[0])) | |
| const uint32_t kSizeOfDataType[]{ | |
| 4, // ANEURALNETWORKS_FLOAT32 | |
| 4, // ANEURALNETWORKS_INT32 | |
| 4, // ANEURALNETWORKS_UINT32 | |
| 4, // ANEURALNETWORKS_TENSOR_FLOAT32 | |
| 4, // ANEURALNETWORKS_TENSOR_INT32 | |
| 1, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM | |
| 1, // ANEURALNETWORKS_BOOL | |
| 2, // ANEURALNETWORKS_TENSOR_QUANT16_SYMM | |
| 2, // ANEURALNETWORKS_TENSOR_FLOAT16 | |
| 1, // ANEURALNETWORKS_TENSOR_BOOL8 | |
| 2, // ANEURALNETWORKS_FLOAT16 | |
| 1, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL | |
| 2, // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM | |
| 1, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM | |
| 1, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED | |
| 0, // ANEURALNETWORKS_MODEL | |
| }; | |
| static_assert(COUNT(kSizeOfDataType) == kNumberOfDataTypes, "kSizeOfDataType is incorrect"); | |
| const bool kScalarDataType[]{ | |
| true, // ANEURALNETWORKS_FLOAT32 | |
| true, // ANEURALNETWORKS_INT32 | |
| true, // ANEURALNETWORKS_UINT32 | |
| false, // ANEURALNETWORKS_TENSOR_FLOAT32 | |
| false, // ANEURALNETWORKS_TENSOR_INT32 | |
| false, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM | |
| true, // ANEURALNETWORKS_BOOL | |
| false, // ANEURALNETWORKS_TENSOR_QUANT16_SYMM | |
| false, // ANEURALNETWORKS_TENSOR_FLOAT16 | |
| false, // ANEURALNETWORKS_TENSOR_BOOL8 | |
| true, // ANEURALNETWORKS_FLOAT16 | |
| false, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL | |
| false, // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM | |
| false, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM | |
| false, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED | |
| true, // ANEURALNETWORKS_MODEL | |
| }; | |
| static_assert(COUNT(kScalarDataType) == kNumberOfDataTypes, "kScalarDataType is incorrect"); | |
| const uint32_t kSizeOfDataTypeOEM[]{ | |
| 0, // ANEURALNETWORKS_OEM | |
| 1, // ANEURALNETWORKS_TENSOR_OEM_BYTE | |
| }; | |
| static_assert(COUNT(kSizeOfDataTypeOEM) == kNumberOfDataTypesOEM, | |
| "kSizeOfDataTypeOEM is incorrect"); | |
| const bool kScalarDataTypeOEM[]{ | |
| true, // ANEURALNETWORKS_OEM | |
| false, // ANEURALNETWORKS_TENSOR_OEM_BYTE | |
| }; | |
| static_assert(COUNT(kScalarDataTypeOEM) == kNumberOfDataTypesOEM, | |
| "kScalarDataTypeOEM is incorrect"); | |
| bool nonExtensionOperandTypeIsScalar(int type) { | |
| CHECK(!isExtensionOperandType(type)) << "Extension operand types are not supported"; | |
| return tableLookup(kScalarDataType, kScalarDataTypeOEM, type); | |
| } | |
| uint32_t nonExtensionOperandSizeOfData(OperandType type, const std::vector<uint32_t>& dimensions) { | |
| const size_t size = getNonExtensionSize(type, dimensions).value(); | |
| CHECK_LE(size, std::numeric_limits<uint32_t>::max()); | |
| return size; | |
| } | |
| // Returns a pair of {false, size} on success, {true, 0} if size overflows uint32_t. | |
| static std::pair<bool, uint32_t> sizeOfTensorDataHelper(uint32_t sizeOfElement, | |
| const std::vector<uint32_t>& dimensions) { | |
| if (dimensions.empty()) { | |
| return {false, 0}; | |
| } | |
| uint64_t size = static_cast<uint64_t>(sizeOfElement); | |
| constexpr uint64_t kMaxSize = static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()); | |
| for (uint32_t d : dimensions) { | |
| size *= d; | |
| if (size > kMaxSize) return {true, 0}; | |
| } | |
| return {false, static_cast<uint32_t>(size)}; | |
| } | |
| uint32_t sizeOfTensorData(uint32_t sizeOfElement, const std::vector<uint32_t>& dimensions) { | |
| const auto [overflow, size] = sizeOfTensorDataHelper(sizeOfElement, dimensions); | |
| CHECK(!overflow); | |
| return size; | |
| } | |
| bool nonExtensionOperandSizeOfDataOverflowsUInt32(OperandType type, | |
| const std::vector<uint32_t>& dimensions) { | |
| CHECK(!isExtension(type)) << "Size of extension operand data is unknown"; | |
| int n = static_cast<int>(type); | |
| uint32_t sizeOfElement = tableLookup(kSizeOfDataType, kSizeOfDataTypeOEM, n); | |
| return tableLookup(kScalarDataType, kScalarDataTypeOEM, n) | |
| ? false | |
| : sizeOfTensorDataOverflowsUInt32(sizeOfElement, dimensions); | |
| } | |
| bool sizeOfTensorDataOverflowsUInt32(uint32_t sizeOfElement, | |
| const std::vector<uint32_t>& dimensions) { | |
| return sizeOfTensorDataHelper(sizeOfElement, dimensions).first; | |
| } | |
| bool tensorHasUnspecifiedDimensions(int type, const uint32_t* dim, uint32_t dimCount) { | |
| if (!isExtensionOperandType(type)) { | |
| CHECK(!nonExtensionOperandTypeIsScalar(type)) | |
| << "A scalar type can never have unspecified dimensions"; | |
| } | |
| return dimCount == 0 || std::find(dim, dim + dimCount, 0) != (dim + dimCount); | |
| } | |
| bool tensorHasUnspecifiedDimensions(const ANeuralNetworksOperandType* type) { | |
| return tensorHasUnspecifiedDimensions(type->type, type->dimensions, type->dimensionCount); | |
| } | |
| uint32_t alignBytesNeeded(uint32_t index, size_t length) { | |
| uint32_t alignment = getAlignmentForLength(length); | |
| uint32_t pattern = alignment - 1; | |
| uint32_t extra = (~(index - 1)) & pattern; | |
| return extra; | |
| } | |
| void logModelToInfo(const Model& model) { | |
| LOG(INFO) << model; | |
| } | |
| static bool validateScalarDimensions(const ANeuralNetworksOperandType& type, const char* tag) { | |
| NN_RET_CHECK_EQ(type.dimensionCount, 0u) << tag << " invalid dimensions for scalar type"; | |
| NN_RET_CHECK(type.dimensions == nullptr) << tag << " invalid dimensions for scalar type"; | |
| return true; | |
| } | |
| static bool validateQuant8AsymmParams(const ANeuralNetworksOperandType& type, const char* tag) { | |
| NN_RET_CHECK(0 <= type.zeroPoint && type.zeroPoint <= 255) | |
| << tag << " invalid zeroPoint: " << type.zeroPoint; | |
| NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale"; | |
| return true; | |
| } | |
| static bool validateQuant8AsymmSignedParams(const ANeuralNetworksOperandType& type, | |
| const char* tag) { | |
| NN_RET_CHECK(-128 <= type.zeroPoint && type.zeroPoint <= 127) | |
| << tag << " invalid zeroPoint: " << type.zeroPoint; | |
| NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale"; | |
| return true; | |
| } | |
| static bool validateQuant8SymmParams(const ANeuralNetworksOperandType& type, const char* tag) { | |
| NN_RET_CHECK_EQ(type.zeroPoint, 0) << tag << " invalid zeroPoint: " << type.zeroPoint; | |
| NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale"; | |
| return true; | |
| } | |
| static bool validateQuant16AsymmParams(const ANeuralNetworksOperandType& type, const char* tag) { | |
| NN_RET_CHECK(0 <= type.zeroPoint && type.zeroPoint <= 65535) | |
| << tag << " invalid zeroPoint: " << type.zeroPoint; | |
| NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale"; | |
| return true; | |
| } | |
| static bool validateQuantSymmParams(const ANeuralNetworksOperandType& type, const char* tag) { | |
| NN_RET_CHECK_EQ(type.zeroPoint, 0) << tag << " zeroPoint is not zero"; | |
| NN_RET_CHECK_GT(type.scale, 0.f) << tag << " invalid scale"; | |
| return true; | |
| } | |
| static bool validateNoQuantParams(const ANeuralNetworksOperandType& type, const char* tag) { | |
| NN_RET_CHECK_EQ(type.zeroPoint, 0) << tag << " zeroPoint is not zero"; | |
| NN_RET_CHECK_EQ(type.scale, 0.f) << tag << " scale is not zero"; | |
| return true; | |
| } | |
| static bool validateTensorDimensions( | |
| const ANeuralNetworksOperandType& type, | |
| const Extension::OperandTypeInformation* const extensionOperandTypeInfo, const char* tag, | |
| bool allowPartial) { | |
| if (!allowPartial) { | |
| NN_RET_CHECK_GT(type.dimensionCount, 0u) << tag << " invalid operand dimensions"; | |
| } | |
| uint64_t size = | |
| isExtensionOperandType(type.type) | |
| ? extensionOperandTypeInfo->byteSize | |
| : tableLookup(kSizeOfDataType, kSizeOfDataTypeOEM, static_cast<int>(type.type)); | |
| constexpr uint64_t kMaxSize = std::numeric_limits<uint32_t>::max(); | |
| for (uint32_t i = 0; i < type.dimensionCount; i++) { | |
| if (!allowPartial) { | |
| NN_RET_CHECK_NE(type.dimensions[i], 0u) << tag << " invalid operand dimensions"; | |
| } | |
| if (type.dimensions[i] != 0) { | |
| size *= type.dimensions[i]; | |
| NN_RET_CHECK_LE(size, kMaxSize) << tag << " operand byte size exceeds " << kMaxSize; | |
| } | |
| } | |
| return true; | |
| } | |
| static bool validateOperandTypeHelper( | |
| const ANeuralNetworksOperandType& type, | |
| const Extension::OperandTypeInformation* const extensionOperandTypeInfo, const char* tag, | |
| bool allowPartial) { | |
| NN_RET_CHECK_EQ(type.dimensionCount == 0, type.dimensions == nullptr); | |
| if (isExtensionOperandType(type.type)) { | |
| NN_RET_CHECK(extensionOperandTypeInfo != nullptr); | |
| if (extensionOperandTypeInfo->isTensor) { | |
| NN_RET_CHECK( | |
| validateTensorDimensions(type, extensionOperandTypeInfo, tag, allowPartial)); | |
| } else { | |
| NN_RET_CHECK(validateScalarDimensions(type, tag)); | |
| } | |
| return validateNoQuantParams(type, tag); | |
| } | |
| NN_RET_CHECK(extensionOperandTypeInfo == nullptr); | |
| NN_RET_CHECK(validCode(kNumberOfDataTypes, kNumberOfDataTypesOEM, type.type)) | |
| << tag << " invalid OperandType: " << type.type; | |
| bool isScalar = tableLookup(kScalarDataType, kScalarDataTypeOEM, type.type); | |
| if (isScalar) { | |
| NN_RET_CHECK(validateScalarDimensions(type, tag)); | |
| if (type.type != ANEURALNETWORKS_OEM_SCALAR) { // Historically, we have allowed OEM types | |
| // to use quantization parameters. | |
| NN_RET_CHECK(validateNoQuantParams(type, tag)); | |
| } | |
| } else { | |
| NN_RET_CHECK(validateTensorDimensions(type, extensionOperandTypeInfo, tag, allowPartial)); | |
| if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM) { | |
| NN_RET_CHECK(validateQuant8AsymmParams(type, tag)); | |
| } else if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RET_CHECK(validateQuant8AsymmSignedParams(type, tag)); | |
| } else if (type.type == ANEURALNETWORKS_TENSOR_QUANT8_SYMM) { | |
| NN_RET_CHECK(validateQuant8SymmParams(type, tag)); | |
| } else if (type.type == ANEURALNETWORKS_TENSOR_QUANT16_ASYMM) { | |
| NN_RET_CHECK(validateQuant16AsymmParams(type, tag)); | |
| } else if (type.type == ANEURALNETWORKS_TENSOR_QUANT16_SYMM) { | |
| NN_RET_CHECK(validateQuantSymmParams(type, tag)); | |
| } else if (type.type == ANEURALNETWORKS_TENSOR_INT32) { | |
| // TODO(b/119869082): TENSOR_INT32 should not use quantization parameters. | |
| } else if (type.type == ANEURALNETWORKS_TENSOR_OEM_BYTE) { | |
| // Historically, we have allowed OEM types to use quantization parameters. | |
| } else { | |
| NN_RET_CHECK(validateNoQuantParams(type, tag)); | |
| } | |
| } | |
| return true; | |
| } | |
| int validateOperandType(const ANeuralNetworksOperandType& type, | |
| const Extension::OperandTypeInformation* const extensionOperandTypeInfo, | |
| const char* tag, bool allowPartial) { | |
| return validateOperandTypeHelper(type, extensionOperandTypeInfo, tag, allowPartial) | |
| ? ANEURALNETWORKS_NO_ERROR | |
| : ANEURALNETWORKS_BAD_DATA; | |
| } | |
| int validateOperandList(uint32_t count, const uint32_t* list, uint32_t operandCount, | |
| const char* tag) { | |
| for (uint32_t i = 0; i < count; i++) { | |
| if (list[i] >= operandCount) { | |
| LOG(ERROR) << tag << " invalid operand index at " << i << " = " << list[i] | |
| << ", operandCount " << operandCount; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| } | |
| return ANEURALNETWORKS_NO_ERROR; | |
| } | |
| int validateOperationOperandTypes(const std::vector<Operand>& operands, uint32_t inOperandCount, | |
| const uint32_t* inOperandIndexes, | |
| const std::vector<OperandType>& inExpectedTypes, | |
| uint32_t outOperandCount, const uint32_t* outOperandIndexes, | |
| const std::vector<OperandType>& outExpectedInTypes) { | |
| if (inOperandCount != static_cast<uint32_t>(inExpectedTypes.size()) || | |
| outOperandCount != static_cast<uint32_t>(outExpectedInTypes.size())) { | |
| LOG(ERROR) << "Wrong operand count: expected " << inExpectedTypes.size() << " inputs and " | |
| << outExpectedInTypes.size() << " outputs," | |
| << "got " << inOperandCount << " inputs and " << outOperandCount << " outputs"; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| for (uint32_t i = 0; i < inOperandCount; i++) { | |
| if (operands[inOperandIndexes[i]].type != inExpectedTypes[i]) { | |
| LOG(ERROR) << "Invalid input tensor type " << operands[inOperandIndexes[i]].type | |
| << " for input " << i << ", expected " << inExpectedTypes[i]; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| } | |
| for (uint32_t i = 0; i < outOperandCount; i++) { | |
| if (operands[outOperandIndexes[i]].type != outExpectedInTypes[i]) { | |
| LOG(ERROR) << "Invalid output tensor type " << operands[outOperandIndexes[i]].type | |
| << " for input " << i << ", expected " << outExpectedInTypes[i]; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| } | |
| return ANEURALNETWORKS_NO_ERROR; | |
| } | |
| static int validateHalVersion(ANeuralNetworksOperationType opType, HalVersion halVersion, | |
| HalVersion minSupportedHalVersion) { | |
| if (halVersion < minSupportedHalVersion) { | |
| LOG(ERROR) << "The given inputs and outputs for operation " << opType | |
| << " are only supported in " << minSupportedHalVersion | |
| << " and later (validating using " << halVersion << ")"; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| return ANEURALNETWORKS_NO_ERROR; | |
| } | |
| // Checks if two operands have the same types, ranks (if specified), dimensions | |
| // (if specified), scales, zeroPoints, and extraParams. | |
| static bool compatible(const Operand& a, const Operand& b) { | |
| NN_RET_CHECK(a.type == b.type) << a.type << " != " << b.type; | |
| if (a.dimensions.size() != 0 && b.dimensions.size() != 0) { | |
| NN_RET_CHECK_EQ(a.dimensions.size(), b.dimensions.size()) << "Incompatible dimensions"; | |
| for (uint32_t i = 0, n = a.dimensions.size(); i < n; ++i) { | |
| if (a.dimensions[i] != 0 && b.dimensions[i] != 0) { | |
| NN_RET_CHECK_EQ(a.dimensions[i], b.dimensions[i]) << "Incompatible dimensions"; | |
| } | |
| } | |
| } | |
| NN_RET_CHECK_EQ(a.scale, b.scale); | |
| NN_RET_CHECK_EQ(a.zeroPoint, b.zeroPoint); | |
| NN_RET_CHECK(a.extraParams == b.extraParams) << a.extraParams << " != " << b.extraParams; | |
| return true; | |
| } | |
| static bool validateConditionOperand(const Operand& operand) { | |
| NN_RET_CHECK(operand.type == OperandType::TENSOR_BOOL8) | |
| << "Unexpected condition operand type: " << operand.type; | |
| NN_RET_CHECK_EQ(operand.dimensions.size(), 1u) << "Condition operand must be a singleton"; | |
| NN_RET_CHECK_EQ(operand.dimensions[0], 1u) << "Condition operand must be a singleton"; | |
| return true; | |
| } | |
| static void checkSubgraphValidationHelper(const SubgraphValidationHelper& helper) { | |
| CHECK(helper.isValidSubgraphReference != nullptr); | |
| CHECK(helper.getSubgraphInputCount != nullptr); | |
| CHECK(helper.getSubgraphOutputCount != nullptr); | |
| CHECK(helper.getSubgraphInputOperand != nullptr); | |
| CHECK(helper.getSubgraphOutputOperand != nullptr); | |
| } | |
| static bool validateIfOperation(uint32_t inputCount, const uint32_t* inputs, uint32_t outputCount, | |
| const uint32_t* outputs, const std::vector<Operand>& operands, | |
| const SubgraphValidationHelper& helper) { | |
| namespace op = operation_if; | |
| checkSubgraphValidationHelper(helper); | |
| NN_RET_CHECK_GE(inputCount, 3u) << "ANEURALNETWORKS_IF must have at least 3 inputs"; | |
| NN_RET_CHECK_GE(outputCount, 1u) << "ANEURALNETWORKS_IF must have at least 1 output"; | |
| auto validateBranchOperand = [&](const Operand& branchModelOperand) -> bool { | |
| NN_RET_CHECK(helper.isValidSubgraphReference(branchModelOperand)) | |
| << "Operand is not a valid subgraph reference"; | |
| const uint32_t branchModelInputCount = helper.getSubgraphInputCount(branchModelOperand); | |
| const uint32_t branchModelOutputCount = helper.getSubgraphOutputCount(branchModelOperand); | |
| NN_RET_CHECK_EQ(inputCount, op::kFirstInput + branchModelInputCount); | |
| NN_RET_CHECK_EQ(outputCount, branchModelOutputCount); | |
| for (uint32_t i = 0; i < branchModelInputCount; ++i) { | |
| const Operand& innerOperand = *helper.getSubgraphInputOperand(branchModelOperand, i); | |
| const Operand& outerOperand = operands[inputs[op::kFirstInput + i]]; | |
| NN_RET_CHECK(compatible(innerOperand, outerOperand)); | |
| } | |
| for (uint32_t i = 0; i < branchModelOutputCount; ++i) { | |
| const Operand& innerOperand = *helper.getSubgraphOutputOperand(branchModelOperand, i); | |
| const Operand& outerOperand = operands[outputs[i]]; | |
| NN_RET_CHECK(compatible(innerOperand, outerOperand)); | |
| } | |
| return true; | |
| }; | |
| NN_RET_CHECK(validateConditionOperand(operands[inputs[op::kCondBoolOperand]])) | |
| << "Validation failed for IF condition operand"; | |
| NN_RET_CHECK(validateBranchOperand(operands[inputs[op::kThenModelOperand]])) | |
| << "Validation failed for IF then model"; | |
| NN_RET_CHECK(validateBranchOperand(operands[inputs[op::kElseModelOperand]])) | |
| << "Validation failed for IF else model"; | |
| return true; | |
| } | |
| static bool validateControlFlowOperandUnknownSize(const SubgraphValidationHelper& helper, | |
| const Operand& operand) { | |
| if (!helper.allowControlFlowOperationWithOperandOfUnknownSize && !isExtension(operand.type)) { | |
| NN_RET_CHECK_NE(nonExtensionOperandSizeOfData(operand.type, operand.dimensions), 0u); | |
| } | |
| return true; | |
| } | |
| static bool validateWhileOperation(uint32_t inputCount, const uint32_t* inputs, | |
| uint32_t outputCount, const uint32_t* outputs, | |
| const std::vector<Operand>& operands, | |
| const SubgraphValidationHelper& helper) { | |
| // Let the loop have | |
| // - m >= 1 input-output operands, | |
| // - k >= 0 state-only operands, and | |
| // - n >= 0 input-only operands. | |
| // Then | |
| // - the WHILE loop operation has (2 + m + k + n) inputs and m outputs. | |
| // - the condition model has (m + k + n) inputs and 1 output. | |
| // - the body model has (m + k + n) inputs and (m + k) outputs. | |
| namespace op = operation_while; | |
| checkSubgraphValidationHelper(helper); | |
| NN_RET_CHECK_GE(inputCount, 3u) << "ANEURALNETWORKS_WHILE must have at least 3 inputs"; | |
| NN_RET_CHECK_GE(outputCount, 1u) << "ANEURALNETWORKS_WHILE must have at least 1 output"; | |
| auto validateCondOperand = [&](const Operand& condModelOperand) -> bool { | |
| NN_RET_CHECK(helper.isValidSubgraphReference(condModelOperand)) | |
| << "Operand is not a valid subgraph reference"; | |
| const uint32_t condModelInputCount = helper.getSubgraphInputCount(condModelOperand); | |
| const uint32_t condModelOutputCount = helper.getSubgraphOutputCount(condModelOperand); | |
| NN_RET_CHECK_EQ(inputCount, op::kFirstInput + condModelInputCount); | |
| NN_RET_CHECK_EQ(condModelOutputCount, 1u); | |
| for (uint32_t i = 0; i < condModelInputCount; ++i) { | |
| const Operand& innerOperand = *helper.getSubgraphInputOperand(condModelOperand, i); | |
| const Operand& outerOperand = operands[inputs[op::kFirstInput + i]]; | |
| NN_RET_CHECK(compatible(innerOperand, outerOperand)); | |
| NN_RET_CHECK(validateControlFlowOperandUnknownSize(helper, innerOperand)); | |
| NN_RET_CHECK(validateControlFlowOperandUnknownSize(helper, outerOperand)); | |
| } | |
| NN_RET_CHECK( | |
| validateConditionOperand(*helper.getSubgraphOutputOperand(condModelOperand, 0))); | |
| return true; | |
| }; | |
| auto validateBodyOperand = [&](const Operand& bodyModelOperand) -> bool { | |
| NN_RET_CHECK(helper.isValidSubgraphReference(bodyModelOperand)) | |
| << "Operand is not a valid subgraph reference"; | |
| const uint32_t bodyModelInputCount = helper.getSubgraphInputCount(bodyModelOperand); | |
| const uint32_t bodyModelOutputCount = helper.getSubgraphOutputCount(bodyModelOperand); | |
| NN_RET_CHECK_EQ(inputCount, op::kFirstInput + bodyModelInputCount); | |
| NN_RET_CHECK_GE(bodyModelOutputCount, outputCount); | |
| NN_RET_CHECK_GE(bodyModelInputCount, bodyModelOutputCount); | |
| const uint32_t inputOutputCount = outputCount; | |
| const uint32_t stateOnlyCount = bodyModelOutputCount - inputOutputCount; | |
| const uint32_t inputOnlyCount = bodyModelInputCount - bodyModelOutputCount; | |
| for (uint32_t i = 0, n = inputOutputCount + stateOnlyCount + inputOnlyCount; i < n; ++i) { | |
| const Operand& innerOperand = *helper.getSubgraphInputOperand(bodyModelOperand, i); | |
| const Operand& outerOperand = operands[inputs[op::kFirstInput + i]]; | |
| NN_RET_CHECK(compatible(innerOperand, outerOperand)); | |
| NN_RET_CHECK(validateControlFlowOperandUnknownSize(helper, innerOperand)); | |
| NN_RET_CHECK(validateControlFlowOperandUnknownSize(helper, outerOperand)); | |
| } | |
| for (uint32_t i = 0; i < inputOutputCount; ++i) { | |
| const Operand& innerOperand = *helper.getSubgraphOutputOperand(bodyModelOperand, i); | |
| const Operand& outerOperand = operands[outputs[i]]; | |
| NN_RET_CHECK(compatible(innerOperand, outerOperand)); | |
| NN_RET_CHECK(validateControlFlowOperandUnknownSize(helper, outerOperand)); | |
| } | |
| for (uint32_t i = 0, n = inputOutputCount + stateOnlyCount; i < n; ++i) { | |
| const Operand& inputOperand = *helper.getSubgraphInputOperand(bodyModelOperand, i); | |
| const Operand& outputOperand = *helper.getSubgraphOutputOperand(bodyModelOperand, i); | |
| NN_RET_CHECK(compatible(inputOperand, outputOperand)); | |
| NN_RET_CHECK(validateControlFlowOperandUnknownSize(helper, outputOperand)); | |
| } | |
| return true; | |
| }; | |
| NN_RET_CHECK(validateCondOperand(operands[inputs[op::kCondModelOperand]])) | |
| << "Validation failed for WHILE condition model"; | |
| NN_RET_CHECK(validateBodyOperand(operands[inputs[op::kBodyModelOperand]])) | |
| << "Validation failed for WHILE body model"; | |
| return true; | |
| } | |
| int validateOperation(ANeuralNetworksOperationType opType, uint32_t inputCount, | |
| const uint32_t* inputIndexes, uint32_t outputCount, | |
| const uint32_t* outputIndexes, const std::vector<Operand>& operands, | |
| HalVersion halVersion, const SubgraphValidationHelper& helper) { | |
| NN_RETURN_IF_ERROR(validateOperandList(inputCount, inputIndexes, | |
| static_cast<uint32_t>(operands.size()), | |
| "ANeuralNetworksModel_addOperation inputs")); | |
| NN_RETURN_IF_ERROR(validateOperandList(outputCount, outputIndexes, | |
| static_cast<uint32_t>(operands.size()), | |
| "ANeuralNetworksModel_addOperation outputs")); | |
| if (isExtensionOperationType(opType)) { | |
| if (halVersion < HalVersion::V1_2) { | |
| LOG(ERROR) | |
| << "Extension operations are supported since HAL version 1.2, validating using " | |
| << halVersion; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| // There is no other validation we can do for an extension operation. | |
| return ANEURALNETWORKS_NO_ERROR; | |
| } | |
| auto logInvalidInOutNumber = [opType, inputCount, outputCount](int expIn, int expOut) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount << ", expected " << expIn | |
| << ") or output operands (" << outputCount << ", expected " << expOut | |
| << ") for operation " << opType; | |
| }; | |
| switch (opType) { | |
| case ANEURALNETWORKS_OEM_OPERATION: { | |
| return ANEURALNETWORKS_NO_ERROR; | |
| } | |
| case ANEURALNETWORKS_RESHAPE: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT32, OperandType::TENSOR_INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::TENSOR_INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED, | |
| OperandType::TENSOR_INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED}; | |
| } else if (inputType == OperandType::TENSOR_INT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::AIDL_V2)); | |
| inExpectedTypes = {OperandType::TENSOR_INT32, OperandType::TENSOR_INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_INT32}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| const auto inputRank = operands[inputIndexes[0]].dimensions.size(); | |
| if (inputRank > 4) { | |
| LOG(ERROR) << "Unsupported input tensor rank for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_DEPTH_TO_SPACE: { | |
| if ((inputCount != 3 && inputCount != 2) || outputCount != 1) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount | |
| << ", expected 3 or 2) or output operands (" << outputCount | |
| << ", expected 1) for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT32, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputCount == 3) { | |
| inExpectedTypes.push_back(OperandType::BOOL); | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_SPACE_TO_DEPTH: { | |
| if ((inputCount != 3 && inputCount != 2) || outputCount != 1) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount | |
| << ", expected 3 or 2) or output operands (" << outputCount | |
| << ", expected 1) for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT32, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| inExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputCount == 3) { | |
| inExpectedTypes.push_back(OperandType::BOOL); | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_EMBEDDING_LOOKUP: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[1]].type; | |
| if (inputType != OperandType::TENSOR_FLOAT16 && | |
| inputType != OperandType::TENSOR_FLOAT32 && | |
| inputType != OperandType::TENSOR_INT32 && | |
| inputType != OperandType::TENSOR_QUANT8_ASYMM && | |
| inputType != OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_INT32, inputType}; | |
| std::vector<OperandType> outExpectedTypes = {inputType}; | |
| if (inputType == OperandType::TENSOR_FLOAT16 || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else if (inputType == OperandType::TENSOR_INT32 || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_HASHTABLE_LOOKUP: { | |
| if (inputCount != 3 || outputCount != 2) { | |
| logInvalidInOutNumber(3, 2); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[2]].type; | |
| if (inputType != OperandType::TENSOR_FLOAT32 && | |
| inputType != OperandType::TENSOR_INT32 && | |
| inputType != OperandType::TENSOR_QUANT8_ASYMM) { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> inExpectedTypes = {OperandType::TENSOR_INT32, | |
| OperandType::TENSOR_INT32, inputType}; | |
| std::vector<OperandType> outExpectedTypes = {inputType, | |
| OperandType::TENSOR_QUANT8_ASYMM}; | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_LSH_PROJECTION: { | |
| if (inputCount != 4 || outputCount != 1) { | |
| logInvalidInOutNumber(4, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[1]].type; | |
| if (inputType != OperandType::TENSOR_FLOAT16 && | |
| inputType != OperandType::TENSOR_FLOAT32 && | |
| inputType != OperandType::TENSOR_INT32 && | |
| inputType != OperandType::TENSOR_QUANT8_ASYMM) { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto hashType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| if (hashType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT16, | |
| inputType, | |
| OperandType::TENSOR_FLOAT16, | |
| OperandType::INT32, | |
| }; | |
| } else if (hashType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT32, | |
| inputType, | |
| OperandType::TENSOR_FLOAT32, | |
| OperandType::INT32, | |
| }; | |
| } else { | |
| LOG(ERROR) << "Unsupported hash tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_INT32}; | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_BIDIRECTIONAL_SEQUENCE_LSTM: { | |
| const uint32_t kNumOutputs = 2; | |
| const uint32_t kNumOutputsMerged = 1; | |
| const uint32_t kNumOutputsWithState = 6; | |
| const uint32_t kNumOutputsMergedWithState = 5; | |
| if (inputCount != 61 || | |
| (outputCount != kNumOutputs && outputCount != kNumOutputsMerged && | |
| outputCount != kNumOutputsWithState && | |
| outputCount != kNumOutputsMergedWithState)) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount | |
| << ", expected 61) or output operands (" << outputCount | |
| << ", expected 1, 2, 5 or 6) for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> inExpectedTypes; | |
| auto inputType = operands[inputIndexes[0]].type; | |
| if (inputType != OperandType::TENSOR_FLOAT32 && | |
| inputType != OperandType::TENSOR_FLOAT16) { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| inExpectedTypes = {}; | |
| for (int i = 0; i < 48; ++i) { | |
| inExpectedTypes.push_back(inputType); | |
| } | |
| inExpectedTypes.push_back(OperandType::INT32); | |
| inExpectedTypes.push_back(inputType == OperandType::TENSOR_FLOAT32 | |
| ? OperandType::FLOAT32 | |
| : OperandType::FLOAT16); | |
| inExpectedTypes.push_back(inputType == OperandType::TENSOR_FLOAT32 | |
| ? OperandType::FLOAT32 | |
| : OperandType::FLOAT16); | |
| inExpectedTypes.push_back(OperandType::BOOL); | |
| inExpectedTypes.push_back(OperandType::BOOL); | |
| for (int i = 0; i < 8; ++i) { | |
| inExpectedTypes.push_back(inputType); | |
| } | |
| HalVersion minSupportedHalVersion = HalVersion::V1_2; | |
| if (outputCount == kNumOutputsWithState || outputCount == kNumOutputsMergedWithState) { | |
| minSupportedHalVersion = HalVersion::V1_3; | |
| } | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, minSupportedHalVersion)); | |
| std::vector<OperandType> outExpectedTypes(outputCount, inputType); | |
| auto status = validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| return status; | |
| } | |
| case ANEURALNETWORKS_LSTM: { | |
| if ((inputCount != 23 && inputCount != 27) || outputCount != 4) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount | |
| << ", expected 23 or 27) or output operands (" << outputCount | |
| << ", expected 4) for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| auto inputType = operands[inputIndexes[0]].type; | |
| if (inputType != OperandType::TENSOR_FLOAT32 && | |
| inputType != OperandType::TENSOR_FLOAT16) { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| inExpectedTypes = {inputType, inputType, inputType, inputType, inputType, | |
| inputType, inputType, inputType, inputType, inputType, | |
| inputType, inputType, inputType, inputType, inputType, | |
| inputType, inputType, inputType, inputType, inputType, | |
| OperandType::INT32}; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| inExpectedTypes.push_back(OperandType::FLOAT32); | |
| inExpectedTypes.push_back(OperandType::FLOAT32); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes.push_back(OperandType::FLOAT16); | |
| inExpectedTypes.push_back(OperandType::FLOAT16); | |
| } | |
| outExpectedTypes = {inputType, inputType, inputType, inputType}; | |
| if (inputCount == 23) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| for (int i = 0; i < 4; ++i) { | |
| inExpectedTypes.push_back(inputType); | |
| } | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_QUANTIZED_16BIT_LSTM: { | |
| if (inputCount != 15 || outputCount != 2) { | |
| logInvalidInOutNumber(15, 2); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| std::vector<OperandType> inExpectedTypes = { | |
| OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM, | |
| OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM, | |
| OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM, | |
| OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_QUANT8_ASYMM, | |
| OperandType::TENSOR_QUANT8_ASYMM, OperandType::TENSOR_INT32, | |
| OperandType::TENSOR_INT32, OperandType::TENSOR_INT32, | |
| OperandType::TENSOR_INT32, OperandType::TENSOR_QUANT16_SYMM, | |
| OperandType::TENSOR_QUANT8_ASYMM}; | |
| std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_QUANT16_SYMM, | |
| OperandType::TENSOR_QUANT8_ASYMM}; | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_RANDOM_MULTINOMIAL: { | |
| if (inputCount != 3 || outputCount != 1) { | |
| logInvalidInOutNumber(3, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| OperandType inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32 || | |
| inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| inputType, | |
| OperandType::INT32, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> outExpectedTypes = {OperandType::TENSOR_INT32}; | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_RNN: { | |
| if (inputCount != 6 || outputCount != 2) { | |
| logInvalidInOutNumber(6, 2); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| OperandType inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_FLOAT32, OperandType::INT32, | |
| }; | |
| outExpectedTypes = { | |
| OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_FLOAT32, | |
| }; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_FLOAT16, OperandType::INT32, | |
| }; | |
| outExpectedTypes = { | |
| OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_FLOAT16, | |
| }; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_SVDF: { | |
| if (inputCount != 7 || outputCount != 2) { | |
| logInvalidInOutNumber(7, 2); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| OperandType inputType = operands[inputIndexes[0]].type; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_0)); | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> inExpectedTypes = { | |
| inputType, inputType, inputType, inputType, | |
| inputType, OperandType::INT32, OperandType::INT32, | |
| }; | |
| std::vector<OperandType> outExpectedTypes = {inputType, inputType}; | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_BATCH_TO_SPACE_ND: { | |
| if ((inputCount != 3 && inputCount != 2) || outputCount != 1) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount | |
| << ", expected 3 or 2) or output operands (" << outputCount | |
| << ", expected 1) for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) { | |
| inExpectedTypes = { | |
| OperandType::TENSOR_QUANT8_ASYMM, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_QUANT8_ASYMM_SIGNED, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputCount == 3) { | |
| inExpectedTypes.push_back(OperandType::BOOL); | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_SPACE_TO_BATCH_ND: { | |
| if ((inputCount != 4 && inputCount != 3) || outputCount != 1) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount | |
| << ", expected 4 or 3) or output operands (" << outputCount | |
| << ", expected 1) for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_INT32, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_INT32, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) { | |
| if (operands[inputIndexes[0]].zeroPoint != 0) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| inExpectedTypes = { | |
| OperandType::TENSOR_QUANT8_ASYMM, | |
| OperandType::TENSOR_INT32, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_QUANT8_ASYMM_SIGNED, | |
| OperandType::TENSOR_INT32, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_QUANT8_ASYMM_SIGNED}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputCount == 4) { | |
| inExpectedTypes.push_back(OperandType::BOOL); | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_PAD: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| if (operands[inputIndexes[0]].zeroPoint == 0) { | |
| NN_RETURN_IF_ERROR( | |
| validateHalVersion(opType, halVersion, HalVersion::V1_1)); | |
| } else { | |
| NN_RETURN_IF_ERROR( | |
| validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| } | |
| inExpectedTypes = { | |
| inputType, | |
| OperandType::TENSOR_INT32, | |
| }; | |
| outExpectedTypes = {inputType}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| const auto inputRank = operands[inputIndexes[0]].dimensions.size(); | |
| if (inputRank > 4) { | |
| LOG(ERROR) << "Unsupported input tensor rank for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_PAD_V2: { | |
| if (inputCount != 3 || outputCount != 1) { | |
| logInvalidInOutNumber(3, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_INT32, | |
| OperandType::FLOAT32, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| inExpectedTypes = { | |
| OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_INT32, | |
| OperandType::FLOAT16, | |
| }; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| inExpectedTypes = { | |
| inputType, | |
| OperandType::TENSOR_INT32, | |
| OperandType::INT32, | |
| }; // TODO(b/116699425): Make it UINT8. | |
| outExpectedTypes = {inputType}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| const auto inputRank = operands[inputIndexes[0]].dimensions.size(); | |
| if (inputRank > 4) { | |
| LOG(ERROR) << "Unsupported input tensor rank for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_CAST: { | |
| if (inputCount != 1 || outputCount != 1) { | |
| logInvalidInOutNumber(1, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputOperand = operands[inputIndexes[0]]; | |
| auto outputOperand = operands[outputIndexes[0]]; | |
| auto inputType = inputOperand.type; | |
| auto outputType = outputOperand.type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if ((inputType == OperandType::TENSOR_FLOAT16 || | |
| inputType == OperandType::TENSOR_FLOAT32 || | |
| inputType == OperandType::TENSOR_INT32 || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM) && | |
| (outputType == OperandType::TENSOR_FLOAT16 || | |
| outputType == OperandType::TENSOR_FLOAT32 || | |
| outputType == OperandType::TENSOR_INT32 || | |
| outputType == OperandType::TENSOR_QUANT8_ASYMM)) { | |
| inExpectedTypes = {inputType}; | |
| outExpectedTypes = {outputType}; | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else if (inputType == OperandType::TENSOR_BOOL8 || | |
| inputType == OperandType::TENSOR_QUANT16_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT16_SYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED || | |
| inputType == OperandType::TENSOR_QUANT8_SYMM) { | |
| inExpectedTypes = {inputType}; | |
| outExpectedTypes = {inputType}; // Only identity CAST is supported. | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| LOG(ERROR) << "Unsupported data type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| // Validate that output shape is equal to input shape if dimensions | |
| // are already known. | |
| auto getNumberOfElements = [](const std::vector<uint32_t>& dims) { | |
| if (dims.size() == 0) { | |
| return 0; | |
| } | |
| return std::accumulate(dims.begin(), dims.end(), 1, std::multiplies<>()); | |
| }; | |
| if (inputOperand.dimensions.size() != 0 && outputOperand.dimensions.size() != 0 && | |
| getNumberOfElements(outputOperand.dimensions) != 0 && | |
| inputOperand.dimensions != outputOperand.dimensions) { | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_MEAN: { | |
| if (inputCount != 3 || outputCount != 1) { | |
| logInvalidInOutNumber(3, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| const auto inputRank = operands[inputIndexes[0]].dimensions.size(); | |
| if (inputRank > 4) { | |
| LOG(ERROR) << "Unsupported input tensor rank for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1)); | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_1)); | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> inExpectedTypes = {inputType, OperandType::TENSOR_INT32, | |
| OperandType::INT32}; | |
| std::vector<OperandType> outExpectedTypes = {inputType}; | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_ARGMAX: | |
| case ANEURALNETWORKS_ARGMIN: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT16 || | |
| inputType == OperandType::TENSOR_FLOAT32 || | |
| inputType == OperandType::TENSOR_INT32 || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| inExpectedTypes = {inputType, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_INT32}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_EXPAND_DIMS: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT16 || | |
| inputType == OperandType::TENSOR_FLOAT32 || | |
| inputType == OperandType::TENSOR_INT32 || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| inExpectedTypes = {inputType, OperandType::INT32}; | |
| outExpectedTypes = {inputType}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_SPLIT: { | |
| if (inputCount != 3) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount << ", expected 3)" | |
| << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| if (inputType != OperandType::TENSOR_FLOAT16 && | |
| inputType != OperandType::TENSOR_FLOAT32 && | |
| inputType != OperandType::TENSOR_INT32 && | |
| inputType != OperandType::TENSOR_QUANT8_ASYMM && | |
| inputType != OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| std::vector<OperandType> inExpectedTypes = {inputType, OperandType::INT32, | |
| OperandType::INT32}; | |
| std::vector<OperandType> outExpectedTypes(outputCount, inputType); | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_MAXIMUM: | |
| case ANEURALNETWORKS_MINIMUM: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| OperandType inputType = operands[inputIndexes[0]].type; | |
| if (inputType == OperandType::TENSOR_FLOAT16 || | |
| inputType == OperandType::TENSOR_FLOAT32 || | |
| inputType == OperandType::TENSOR_INT32 || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| inExpectedTypes = {inputType, inputType}; | |
| outExpectedTypes = {inputType}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_GROUPED_CONV_2D: { | |
| if ((inputCount != 12 && inputCount != 9) || outputCount != 1) { | |
| LOG(ERROR) << "Invalid number of input operands (" << inputCount | |
| << ", expected 12 or 9) or output operands (" << outputCount | |
| << ", expected 1) for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| auto filterType = operands[inputIndexes[1]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT32) { | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT32, OperandType::TENSOR_FLOAT32, | |
| OperandType::TENSOR_FLOAT32, OperandType::INT32, | |
| OperandType::INT32, OperandType::INT32, | |
| OperandType::INT32, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT32}; | |
| } else if (inputType == OperandType::TENSOR_FLOAT16) { | |
| inExpectedTypes = {OperandType::TENSOR_FLOAT16, OperandType::TENSOR_FLOAT16, | |
| OperandType::TENSOR_FLOAT16, OperandType::INT32, | |
| OperandType::INT32, OperandType::INT32, | |
| OperandType::INT32, OperandType::INT32}; | |
| outExpectedTypes = {OperandType::TENSOR_FLOAT16}; | |
| } else if (inputType == OperandType::TENSOR_QUANT8_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| if (filterType != inputType && | |
| filterType != OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) { | |
| LOG(ERROR) << "Unsupported filter tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (filterType == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL && | |
| std::get<Operand::SymmPerChannelQuantParams>( | |
| operands[inputIndexes[1]].extraParams) | |
| .channelDim != 0) { | |
| LOG(ERROR) << "Unsupported filter tensor channel dimension for operation " | |
| << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| inExpectedTypes = { | |
| inputType, filterType, OperandType::TENSOR_INT32, | |
| OperandType::INT32, OperandType::INT32, OperandType::INT32, | |
| OperandType::INT32, OperandType::INT32}; | |
| outExpectedTypes = {inputType}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputCount == 12) { | |
| std::vector<OperandType> explicitScalarTypes(3, OperandType::INT32); | |
| inExpectedTypes.insert(inExpectedTypes.end(), explicitScalarTypes.begin(), | |
| explicitScalarTypes.end()); | |
| } | |
| inExpectedTypes.push_back(OperandType::BOOL); | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_TILE: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT16 || | |
| inputType == OperandType::TENSOR_FLOAT32 || | |
| inputType == OperandType::TENSOR_INT32 || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM || | |
| inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| inExpectedTypes = {inputType, OperandType::TENSOR_INT32}; | |
| outExpectedTypes = {inputType}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_POW: { | |
| if (inputCount != 2 || outputCount != 1) { | |
| logInvalidInOutNumber(2, 1); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| auto inputType = operands[inputIndexes[0]].type; | |
| std::vector<OperandType> inExpectedTypes; | |
| std::vector<OperandType> outExpectedTypes; | |
| if (inputType == OperandType::TENSOR_FLOAT16 || | |
| inputType == OperandType::TENSOR_FLOAT32) { | |
| inExpectedTypes = {inputType, inputType}; | |
| outExpectedTypes = {inputType}; | |
| } else { | |
| LOG(ERROR) << "Unsupported input tensor type for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (inputType == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| } else { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_2)); | |
| } | |
| return validateOperationOperandTypes(operands, inputCount, inputIndexes, | |
| inExpectedTypes, outputCount, outputIndexes, | |
| outExpectedTypes); | |
| } | |
| case ANEURALNETWORKS_IF: { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| return validateIfOperation(inputCount, inputIndexes, outputCount, outputIndexes, | |
| operands, helper) | |
| ? ANEURALNETWORKS_NO_ERROR | |
| : ANEURALNETWORKS_BAD_DATA; | |
| } | |
| case ANEURALNETWORKS_WHILE: { | |
| NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); | |
| return validateWhileOperation(inputCount, inputIndexes, outputCount, outputIndexes, | |
| operands, helper) | |
| ? ANEURALNETWORKS_NO_ERROR | |
| : ANEURALNETWORKS_BAD_DATA; | |
| } | |
| default: { | |
| const OperationRegistration* operationRegistration = | |
| BuiltinOperationResolver::get()->findOperation( | |
| static_cast<OperationType>(opType)); | |
| if (operationRegistration == nullptr) { | |
| if (0 <= opType && opType < kNumberOfOperationTypes) { | |
| LOG(ERROR) << opType << " not registered"; | |
| } else { | |
| LOG(ERROR) << "Operation type " << opType << " out of the range [0, " | |
| << kNumberOfOperationTypes << ")"; | |
| } | |
| return ANEURALNETWORKS_UNEXPECTED_NULL; | |
| } | |
| if (operationRegistration->validate == nullptr) { | |
| LOG(ERROR) << "Incomplete operation registration: " << opType; | |
| return ANEURALNETWORKS_UNEXPECTED_NULL; | |
| } | |
| OperationValidationContext context(operationRegistration->name, inputCount, | |
| inputIndexes, outputCount, outputIndexes, | |
| operands.data()); | |
| const auto maybeVersion = operationRegistration->validate(&context); | |
| if (!maybeVersion.has_value()) { | |
| LOG(ERROR) << "Validation failed for operation " << opType << ": " | |
| << maybeVersion.error(); | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| if (!validateVersion(&context, convert(halVersion), maybeVersion.value())) { | |
| LOG(ERROR) << "Validation failed for operation " << opType; | |
| return ANEURALNETWORKS_BAD_DATA; | |
| } | |
| return ANEURALNETWORKS_NO_ERROR; | |
| } | |
| } | |
| } | |
| ErrorStatus convertResultCodeToErrorStatus(int resultCode) { | |
| switch (resultCode) { | |
| case ANEURALNETWORKS_NO_ERROR: | |
| return ErrorStatus::NONE; | |
| case ANEURALNETWORKS_BAD_DATA: | |
| case ANEURALNETWORKS_UNEXPECTED_NULL: | |
| return ErrorStatus::INVALID_ARGUMENT; | |
| case ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE: | |
| return ErrorStatus::OUTPUT_INSUFFICIENT_SIZE; | |
| case ANEURALNETWORKS_UNAVAILABLE_DEVICE: | |
| return ErrorStatus::DEVICE_UNAVAILABLE; | |
| case ANEURALNETWORKS_BAD_STATE: | |
| case ANEURALNETWORKS_INCOMPLETE: | |
| case ANEURALNETWORKS_OP_FAILED: | |
| case ANEURALNETWORKS_OUT_OF_MEMORY: | |
| case ANEURALNETWORKS_UNMAPPABLE: | |
| case ANEURALNETWORKS_DEAD_OBJECT: | |
| return ErrorStatus::GENERAL_FAILURE; | |
| case ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT: | |
| return ErrorStatus::MISSED_DEADLINE_TRANSIENT; | |
| case ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT: | |
| return ErrorStatus::MISSED_DEADLINE_PERSISTENT; | |
| case ANEURALNETWORKS_RESOURCE_EXHAUSTED_TRANSIENT: | |
| return ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT; | |
| case ANEURALNETWORKS_RESOURCE_EXHAUSTED_PERSISTENT: | |
| return ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT; | |
| } | |
| LOG(ERROR) << "Unknown result code " << resultCode << " mapped to ErrorStatus::GENERAL_FAILURE"; | |
| return ErrorStatus::GENERAL_FAILURE; | |
| } | |
| int convertErrorStatusToResultCode(ErrorStatus status) { | |
| switch (status) { | |
| case ErrorStatus::NONE: | |
| return ANEURALNETWORKS_NO_ERROR; | |
| case ErrorStatus::DEVICE_UNAVAILABLE: | |
| return ANEURALNETWORKS_UNAVAILABLE_DEVICE; | |
| case ErrorStatus::GENERAL_FAILURE: | |
| return ANEURALNETWORKS_OP_FAILED; | |
| case ErrorStatus::OUTPUT_INSUFFICIENT_SIZE: | |
| return ANEURALNETWORKS_OUTPUT_INSUFFICIENT_SIZE; | |
| case ErrorStatus::INVALID_ARGUMENT: | |
| return ANEURALNETWORKS_BAD_DATA; | |
| case ErrorStatus::MISSED_DEADLINE_TRANSIENT: | |
| return ANEURALNETWORKS_MISSED_DEADLINE_TRANSIENT; | |
| case ErrorStatus::MISSED_DEADLINE_PERSISTENT: | |
| return ANEURALNETWORKS_MISSED_DEADLINE_PERSISTENT; | |
| case ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT: | |
| return ANEURALNETWORKS_RESOURCE_EXHAUSTED_TRANSIENT; | |
| case ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT: | |
| return ANEURALNETWORKS_RESOURCE_EXHAUSTED_PERSISTENT; | |
| case ErrorStatus::DEAD_OBJECT: | |
| return ANEURALNETWORKS_DEAD_OBJECT; | |
| } | |
| LOG(ERROR) << "Unknown ErrorStatus " << status << " mapped to ANEURALNETWORKS_OP_FAILED"; | |
| return ANEURALNETWORKS_OP_FAILED; | |
| } | |
| std::tuple<int, std::vector<OutputShape>, Timing> getExecutionResult( | |
| ErrorStatus status, std::vector<OutputShape> outputShapes, Timing timing) { | |
| constexpr Timing kNoTiming = {}; | |
| const int n = convertErrorStatusToResultCode(status); | |
| if (status != ErrorStatus::NONE && status != ErrorStatus::OUTPUT_INSUFFICIENT_SIZE && | |
| !outputShapes.empty()) { | |
| LOG(ERROR) << "The driver returned OutputShapes when it shouldn't."; | |
| outputShapes.clear(); | |
| } | |
| if (status != ErrorStatus::NONE && timing != kNoTiming) { | |
| LOG(ERROR) << "The driver returned Timing when it shouldn't."; | |
| timing = kNoTiming; | |
| } | |
| return {n, std::move(outputShapes), timing}; | |
| } | |
| FenceState syncWait(int fd, int timeout) { | |
| // This implementation is directly based on the ::sync_wait() implementation. | |
| struct pollfd fds; | |
| int ret; | |
| if (fd < 0) { | |
| errno = EINVAL; | |
| return FenceState::UNKNOWN; | |
| } | |
| fds.fd = fd; | |
| fds.events = POLLIN; | |
| do { | |
| ret = poll(&fds, 1, timeout); | |
| if (ret > 0) { | |
| if (fds.revents & POLLNVAL) { | |
| errno = EINVAL; | |
| return FenceState::UNKNOWN; | |
| } | |
| if (fds.revents & POLLERR) { | |
| errno = EINVAL; | |
| return FenceState::ERROR; | |
| } | |
| return FenceState::SIGNALED; | |
| } else if (ret == 0) { | |
| errno = ETIME; | |
| return FenceState::ACTIVE; | |
| } | |
| } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); | |
| return FenceState::UNKNOWN; | |
| } | |
| #ifdef NN_DEBUGGABLE | |
| uint32_t getProp(const char* str, uint32_t defaultValue) { | |
| const std::string propStr = android::base::GetProperty(str, ""); | |
| if (propStr.size() > 0) { | |
| return std::stoi(propStr); | |
| } else { | |
| return defaultValue; | |
| } | |
| } | |
| #endif // NN_DEBUGGABLE | |
| } // namespace nn | |
| } // namespace android |