/* * 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 "LegacyHalUtils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "CpuExecutor.h" #include "NeuralNetworks.h" #include "ValidateHal.h" namespace android { namespace nn { constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {.execTime = FLT_MAX, .powerUsage = FLT_MAX}; template static Type handleError(GeneralResult result) { CHECK(result.has_value()) << "Unhandled error (" << result.error().code << "): " << result.error().message; return std::move(result).value(); } LegacyOptionalTimePoint makeDeadline(const V1_3::OptionalTimePoint& timePoint) { using Disc = V1_3::OptionalTimePoint::hidl_discriminator; if (timePoint.getDiscriminator() == Disc::none) { return LegacyOptionalTimePoint{}; } const uint64_t count = timePoint.nanosecondsSinceEpoch(); return LegacyTimePoint{LegacyDuration{count}}; } LegacyOptionalTimePoint makeDeadline(const V1_3::OptionalTimeoutDuration& optionalDuration) { if (optionalDuration.getDiscriminator() == V1_3::OptionalTimeoutDuration::hidl_discriminator::none) { return LegacyOptionalTimePoint{}; } const auto duration = LegacyDuration{optionalDuration.nanoseconds()}; constexpr auto kMaxTime = LegacyTimePoint::max(); const auto currentTime = LegacyClock::now(); // If there would be an overflow, use the max value. if (duration > kMaxTime - currentTime) { return kMaxTime; } return currentTime + duration; } bool hasDeadlinePassed(const LegacyOptionalTimePoint& deadline) { if (!deadline.has_value()) { return false; } return LegacyClock::now() >= *deadline; } bool isExtensionOperandType(V1_3::OperandType type) { return isExtensionOperandType(static_cast(type)); } bool isExtensionOperationType(V1_3::OperationType type) { return isExtensionOperationType(static_cast(type)); } std::string getOperandTypeName(V1_3::OperandType type) { return toString(type); } std::string getOperationName(V1_3::OperationType type) { return toString(type); } uint32_t nonExtensionOperandSizeOfData(V1_3::OperandType type, const std::vector& dimensions) { return nonExtensionOperandSizeOfData(uncheckedConvert(type), dimensions); } bool nonExtensionOperandSizeOfDataOverflowsUInt32(V1_3::OperandType type, const std::vector& dimensions) { return nonExtensionOperandSizeOfDataOverflowsUInt32(uncheckedConvert(type), dimensions); } bool tensorHasUnspecifiedDimensions(V1_3::OperandType type, const std::vector& dimensions) { return tensorHasUnspecifiedDimensions(static_cast(type), dimensions.data(), dimensions.size()); } bool tensorHasUnspecifiedDimensions(const V1_3::Operand& operand) { return tensorHasUnspecifiedDimensions(static_cast(operand.type), operand.dimensions.data(), operand.dimensions.size()); } void logModelToInfo(const V1_0::Model& model) { LOG(INFO) << "V1_0::Model start"; LOG(INFO) << "operands" << toString(model.operands); LOG(INFO) << "operations" << toString(model.operations); LOG(INFO) << "inputIndexes" << toString(model.inputIndexes); LOG(INFO) << "outputIndexes" << toString(model.outputIndexes); LOG(INFO) << "operandValues size" << model.operandValues.size(); LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools)); } void logModelToInfo(const V1_1::Model& model) { LOG(INFO) << "V1_1::Model start"; LOG(INFO) << "operands" << toString(model.operands); LOG(INFO) << "operations" << toString(model.operations); LOG(INFO) << "inputIndexes" << toString(model.inputIndexes); LOG(INFO) << "outputIndexes" << toString(model.outputIndexes); LOG(INFO) << "operandValues size " << model.operandValues.size(); LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools)); } void logModelToInfo(const V1_2::Model& model) { LOG(INFO) << "V1_2::Model start"; LOG(INFO) << "operands" << toString(model.operands); LOG(INFO) << "operations" << toString(model.operations); LOG(INFO) << "inputIndexes" << toString(model.inputIndexes); LOG(INFO) << "outputIndexes" << toString(model.outputIndexes); LOG(INFO) << "operandValues size" << model.operandValues.size(); LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools)); LOG(INFO) << "relaxComputationFloat32toFloat16" << model.relaxComputationFloat32toFloat16; LOG(INFO) << "extensionNameToPrefix" << toString(model.extensionNameToPrefix); } static void logSubgraphToInfo(std::string label, const V1_3::Subgraph& subgraph) { LOG(INFO) << label << ".operands" << toString(subgraph.operands); LOG(INFO) << label << ".operations" << toString(subgraph.operations); LOG(INFO) << label << ".inputIndexes" << toString(subgraph.inputIndexes); LOG(INFO) << label << ".outputIndexes" << toString(subgraph.outputIndexes); } void logModelToInfo(const V1_3::Model& model) { LOG(INFO) << "V1_3::Model start"; logSubgraphToInfo("main", model.main); for (uint32_t i = 0, n = model.referenced.size(); i < n; ++i) { logSubgraphToInfo("referenced[" + std::to_string(i) + "]", model.referenced[i]); } LOG(INFO) << "operandValues size " << model.operandValues.size(); LOG(INFO) << "pools" << SHOW_IF_DEBUG(toString(model.pools)); LOG(INFO) << "relaxComputationFloat32toFloat16 " << model.relaxComputationFloat32toFloat16; LOG(INFO) << "extensionNameToPrefix" << toString(model.extensionNameToPrefix); } bool validateOperandSymmPerChannelQuantParams( const V1_3::Operand& halOperand, const ANeuralNetworksSymmPerChannelQuantParams& channelQuant, const char* tag) { if (halOperand.type != V1_3::OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) { return false; } NN_RET_CHECK_LT(channelQuant.channelDim, halOperand.dimensions.size()) << tag; NN_RET_CHECK(channelQuant.scales != nullptr) << tag; NN_RET_CHECK_EQ(channelQuant.scaleCount, halOperand.dimensions[channelQuant.channelDim]) << tag; NN_RET_CHECK_NE(halOperand.dimensions[channelQuant.channelDim], 0u) << tag << " channel dimension " << channelQuant.channelDim << " is underspecified"; for (uint32_t i = 0; i < halOperand.dimensions[channelQuant.channelDim]; i++) { NN_RET_CHECK_GT(channelQuant.scales[i], 0.0f) << tag << " invalid scaleArray[" << i << "]"; } return true; } 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; } static inline int validateOperation(ANeuralNetworksOperationType opType, uint32_t inputCount, const uint32_t* inputIndexes, uint32_t outputCount, const uint32_t* outputIndexes, const std::vector& operands, HalVersion halVersion) { if (opType == ANEURALNETWORKS_IF || opType == ANEURALNETWORKS_WHILE) { NN_RETURN_IF_ERROR(validateHalVersion(opType, halVersion, HalVersion::V1_3)); LOG(ERROR) << "This validateOperation() overload does not support control flow"; return ANEURALNETWORKS_BAD_DATA; } return validateOperation(opType, inputCount, inputIndexes, outputCount, outputIndexes, operands, halVersion, {}); } V1_3::ErrorStatus convertResultCodeToHalErrorStatus(int resultCode) { return convertToV1_3(convertResultCodeToErrorStatus(resultCode)); } int convertErrorStatusToResultCode(V1_3::ErrorStatus status) { return convertErrorStatusToResultCode(uncheckedConvert(status)); } std::tuple, Timing> getExecutionResult( V1_3::ErrorStatus status, const hardware::hidl_vec& outputShapes, const V1_2::Timing& timing) { return getExecutionResult(uncheckedConvert(status), uncheckedConvert(outputShapes), uncheckedConvert(timing)); } // Capabilities::operandPerformance utilities. // The field Capabilities::operandPerformance is a vector sorted by the field // Capabilities::OperandPerformance::type. template hardware::hidl_vec> nonExtensionOperandPerformance( V1_0::PerformanceInfo perf) { using OpPerf = VersionedOperandPerformance; // Note: range presents enumerators in declaration order, not in numerical order. static constexpr hardware::hidl_enum_range> kOperandTypeRange; std::vector ret; ret.reserve(kOperandTypeRange.end() - kOperandTypeRange.begin()); for (VersionedOperandType type : kOperandTypeRange) { if (static_cast(type) != V1_3::OperandType::SUBGRAPH) { ret.push_back(OpPerf{type, perf}); } } std::sort(ret.begin(), ret.end(), [](const OpPerf& a, const OpPerf& b) { return a.type < b.type; }); return ret; } template hardware::hidl_vec nonExtensionOperandPerformance(V1_0::PerformanceInfo perf); template hardware::hidl_vec nonExtensionOperandPerformance(V1_0::PerformanceInfo perf); template void update(hardware::hidl_vec>* operandPerformance, VersionedOperandType type, V1_0::PerformanceInfo perf) { CHECK(operandPerformance != nullptr); const auto it = std::lower_bound(operandPerformance->begin(), operandPerformance->end(), type, [](const VersionedOperandPerformance& perf, VersionedOperandType type) { return perf.type < type; }); CHECK(it != operandPerformance->end()) << toString(type) << " not in " << toString(*operandPerformance); it->info = perf; } void update(hardware::hidl_vec* operandPerformance, V1_2::OperandType type, V1_0::PerformanceInfo perf) { update(operandPerformance, type, perf); } void update(hardware::hidl_vec* operandPerformance, V1_3::OperandType type, V1_0::PerformanceInfo perf) { update(operandPerformance, type, perf); } template V1_0::PerformanceInfo lookup( const hardware::hidl_vec>& operandPerformance, VersionedOperandType type) { const auto it = std::lower_bound(operandPerformance.begin(), operandPerformance.end(), type, [](const VersionedOperandPerformance& perf, VersionedOperandType type) { return static_cast(perf.type) < static_cast(type); }); if (it == operandPerformance.end()) { LOG(WARNING) << "No PerformanceInfo for " << toString(type); return kNoPerformanceInfo; } else { return it->info; } } V1_0::PerformanceInfo lookup( const hardware::hidl_vec& operandPerformance, V1_2::OperandType type) { return lookup(operandPerformance, type); } V1_0::PerformanceInfo lookup( const hardware::hidl_vec& operandPerformance, V1_3::OperandType type) { CHECK(type != V1_3::OperandType::SUBGRAPH) << "Use Capabilities::ifPerformance or Capabilities::whilePerformance"; return lookup(operandPerformance, type); } bool setRunTimePoolInfosFromHidlMemories(std::vector* poolInfos, const hardware::hidl_vec& pools) { return setRunTimePoolInfosFromCanonicalMemories(poolInfos, uncheckedConvert(pools)); } // Versioning // In Android P, most data types are treated as having the same performance as TENSOR_QUANT8_ASYMM. // This array must be in sorted order. static const V1_3::OperandType kQuantized8PerformanceConsistentWithP[] = { V1_3::OperandType::INT32, V1_3::OperandType::UINT32, V1_3::OperandType::TENSOR_INT32, V1_3::OperandType::OEM, V1_3::OperandType::TENSOR_OEM_BYTE}; static bool isQuantized8PerformanceConsistentWithP(const V1_2::Capabilities& capabilities) { const V1_0::PerformanceInfo quantized8Performance = lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_QUANT8_ASYMM); return std::all_of(std::begin(kQuantized8PerformanceConsistentWithP), std::end(kQuantized8PerformanceConsistentWithP), [quantized8Performance, &capabilities](V1_3::OperandType type) { return quantized8Performance == lookup(capabilities.operandPerformance, static_cast(type)); }); } static bool isQuantized8PerformanceConsistentWithP(const V1_3::Capabilities& capabilities) { const V1_0::PerformanceInfo quantized8Performance = lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_QUANT8_ASYMM); return std::all_of(std::begin(kQuantized8PerformanceConsistentWithP), std::end(kQuantized8PerformanceConsistentWithP), [quantized8Performance, &capabilities](V1_3::OperandType type) { return quantized8Performance == lookup(capabilities.operandPerformance, type); }); } static hardware::hidl_vec makeQuantized8PerformanceConsistentWithP(V1_0::PerformanceInfo quantized8Performance) { hardware::hidl_vec ret( std::size(kQuantized8PerformanceConsistentWithP)); std::transform(std::begin(kQuantized8PerformanceConsistentWithP), std::end(kQuantized8PerformanceConsistentWithP), ret.begin(), [quantized8Performance]( V1_3::OperandType type) -> V1_2::Capabilities::OperandPerformance { return {static_cast(type), quantized8Performance}; }); return ret; } bool compliantWithV1_0(const V1_0::Capabilities&) { return true; } bool compliantWithV1_0(const V1_1::Capabilities& capabilities) { return capabilities.relaxedFloat32toFloat16Performance == capabilities.float32Performance; } bool compliantWithV1_0(const V1_2::Capabilities& capabilities) { const V1_0::PerformanceInfo perfTensorFloat32 = lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32); const V1_0::PerformanceInfo perfFloat32 = lookup(capabilities.operandPerformance, V1_2::OperandType::FLOAT32); if (perfTensorFloat32 != perfFloat32 || perfTensorFloat32 != capabilities.relaxedFloat32toFloat16PerformanceTensor || perfFloat32 != capabilities.relaxedFloat32toFloat16PerformanceScalar) { return false; } return isQuantized8PerformanceConsistentWithP(capabilities); } bool compliantWithV1_0(const V1_3::Capabilities& capabilities) { const V1_0::PerformanceInfo perfTensorFloat32 = lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32); const V1_0::PerformanceInfo perfFloat32 = lookup(capabilities.operandPerformance, V1_3::OperandType::FLOAT32); if (perfTensorFloat32 != perfFloat32 || perfTensorFloat32 != capabilities.relaxedFloat32toFloat16PerformanceTensor || perfFloat32 != capabilities.relaxedFloat32toFloat16PerformanceScalar) { return false; } return isQuantized8PerformanceConsistentWithP(capabilities); } bool compliantWithV1_1(const V1_0::Capabilities&) { return true; } bool compliantWithV1_1(const V1_1::Capabilities&) { return true; } bool compliantWithV1_1(const V1_2::Capabilities& capabilities) { if ((capabilities.relaxedFloat32toFloat16PerformanceTensor != capabilities.relaxedFloat32toFloat16PerformanceScalar) || (lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32) != lookup(capabilities.operandPerformance, V1_2::OperandType::FLOAT32))) { return false; } return isQuantized8PerformanceConsistentWithP(capabilities); } bool compliantWithV1_1(const V1_3::Capabilities& capabilities) { if ((capabilities.relaxedFloat32toFloat16PerformanceTensor != capabilities.relaxedFloat32toFloat16PerformanceScalar) || (lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32) != lookup(capabilities.operandPerformance, V1_3::OperandType::FLOAT32))) { return false; } return isQuantized8PerformanceConsistentWithP(capabilities); } bool compliantWithV1_2(const V1_0::Capabilities&) { return true; } bool compliantWithV1_2(const V1_1::Capabilities&) { return true; } bool compliantWithV1_2(const V1_2::Capabilities&) { return true; } bool compliantWithV1_2(const V1_3::Capabilities&) { return true; } bool compliantWithV1_3(const V1_0::Capabilities&) { return true; } bool compliantWithV1_3(const V1_1::Capabilities&) { return true; } bool compliantWithV1_3(const V1_2::Capabilities&) { return true; } bool compliantWithV1_3(const V1_3::Capabilities&) { return true; } V1_0::ErrorStatus convertToV1_0(V1_0::ErrorStatus status) { return status; } V1_0::ErrorStatus convertToV1_0(V1_3::ErrorStatus status) { switch (status) { case V1_3::ErrorStatus::NONE: return V1_0::ErrorStatus::NONE; case V1_3::ErrorStatus::DEVICE_UNAVAILABLE: return V1_0::ErrorStatus::DEVICE_UNAVAILABLE; case V1_3::ErrorStatus::GENERAL_FAILURE: return V1_0::ErrorStatus::GENERAL_FAILURE; case V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE: return V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE; case V1_3::ErrorStatus::INVALID_ARGUMENT: return V1_0::ErrorStatus::INVALID_ARGUMENT; case V1_3::ErrorStatus::MISSED_DEADLINE_TRANSIENT: return V1_0::ErrorStatus::GENERAL_FAILURE; case V1_3::ErrorStatus::MISSED_DEADLINE_PERSISTENT: return V1_0::ErrorStatus::GENERAL_FAILURE; case V1_3::ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT: return V1_0::ErrorStatus::GENERAL_FAILURE; case V1_3::ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT: return V1_0::ErrorStatus::GENERAL_FAILURE; } LOG(ERROR) << "Unknown ErrorStatus: " << toString(status) << " mapped to GENERAL_FAILURE"; return V1_0::ErrorStatus::GENERAL_FAILURE; } V1_3::ErrorStatus convertToV1_3(V1_0::ErrorStatus status) { return static_cast(status); } V1_3::ErrorStatus convertToV1_3(V1_3::ErrorStatus status) { return status; } static V1_0::OperationType uncheckedConvertToV1_0(V1_1::OperationType type) { return static_cast(type); } static V1_0::OperationType uncheckedConvertToV1_0(V1_2::OperationType type) { return static_cast(type); } V1_0::OperationType uncheckedConvertToV1_0(V1_3::OperationType type) { return static_cast(type); } static V1_1::OperationType convertToV1_1(V1_0::OperationType type) { return static_cast(type); } static V1_1::OperationType uncheckedConvertToV1_1(V1_2::OperationType type) { return static_cast(type); } V1_1::OperationType uncheckedConvertToV1_1(V1_3::OperationType type) { return static_cast(type); } static V1_2::OperationType convertToV1_2(V1_0::OperationType type) { return static_cast(type); } static V1_2::OperationType convertToV1_2(V1_1::OperationType type) { return static_cast(type); } V1_2::OperationType uncheckedConvertToV1_2(V1_3::OperationType type) { return static_cast(type); } static V1_3::OperationType convertToV1_3(V1_0::OperationType type) { return static_cast(type); } static V1_3::OperationType convertToV1_3(V1_1::OperationType type) { return static_cast(type); } static V1_3::OperationType convertToV1_3(V1_2::OperationType type) { return static_cast(type); } V1_0::Capabilities convertToV1_0(const V1_0::Capabilities& capabilities) { return capabilities; } V1_0::Capabilities convertToV1_0(const V1_1::Capabilities& capabilities) { if (!compliantWithV1_0(capabilities)) { LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities) << " from V1_1::Capabilities to V1_0::Capabilities"; } return {.float32Performance = capabilities.float32Performance, .quantized8Performance = capabilities.quantized8Performance}; } V1_0::Capabilities convertToV1_0(const V1_2::Capabilities& capabilities) { if (!compliantWithV1_0(capabilities)) { LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities) << " from V1_2::Capabilities to V1_0::Capabilities"; } return {.float32Performance = lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32), .quantized8Performance = lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_QUANT8_ASYMM)}; } V1_0::Capabilities convertToV1_0(const V1_3::Capabilities& capabilities) { if (!compliantWithV1_0(capabilities)) { LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities) << " from V1_3::Capabilities to V1_0::Capabilities"; } return {.float32Performance = lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32), .quantized8Performance = lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_QUANT8_ASYMM)}; } V1_1::Capabilities convertToV1_1(const V1_0::Capabilities& capabilities) { return {.float32Performance = capabilities.float32Performance, .quantized8Performance = capabilities.quantized8Performance, .relaxedFloat32toFloat16Performance = capabilities.float32Performance}; } V1_1::Capabilities convertToV1_1(const V1_1::Capabilities& capabilities) { return capabilities; } V1_1::Capabilities convertToV1_1(const V1_2::Capabilities& capabilities) { if (!compliantWithV1_1(capabilities)) { LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities) << " from V1_2::Capabilities to V1_1::Capabilities"; } return {.float32Performance = lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_FLOAT32), .quantized8Performance = lookup(capabilities.operandPerformance, V1_2::OperandType::TENSOR_QUANT8_ASYMM), .relaxedFloat32toFloat16Performance = capabilities.relaxedFloat32toFloat16PerformanceTensor}; } V1_1::Capabilities convertToV1_1(const V1_3::Capabilities& capabilities) { if (!compliantWithV1_1(capabilities)) { LOG(ERROR) << "Upcasting non-compliant capabilities " << toString(capabilities) << " from V1_3::Capabilities to V1_1::Capabilities"; } return {.float32Performance = lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_FLOAT32), .quantized8Performance = lookup(capabilities.operandPerformance, V1_3::OperandType::TENSOR_QUANT8_ASYMM), .relaxedFloat32toFloat16Performance = capabilities.relaxedFloat32toFloat16PerformanceTensor}; } V1_2::Capabilities convertToV1_2(const V1_0::Capabilities& capabilities) { V1_2::Capabilities ret = { .relaxedFloat32toFloat16PerformanceScalar = capabilities.float32Performance, .relaxedFloat32toFloat16PerformanceTensor = capabilities.float32Performance, .operandPerformance = makeQuantized8PerformanceConsistentWithP(capabilities.quantized8Performance)}; auto& opPerf = ret.operandPerformance; opPerf.resize(opPerf.size() + 2); opPerf[opPerf.size() - 2] = {V1_2::OperandType::TENSOR_FLOAT32, capabilities.float32Performance}; opPerf[opPerf.size() - 1] = {V1_2::OperandType::FLOAT32, capabilities.float32Performance}; using OperandPerformance = V1_2::Capabilities::OperandPerformance; std::sort(opPerf.begin(), opPerf.end(), [](const OperandPerformance& a, const OperandPerformance& b) { return a.type < b.type; }); return ret; } V1_2::Capabilities convertToV1_2(const V1_1::Capabilities& capabilities) { V1_2::Capabilities ret = {.relaxedFloat32toFloat16PerformanceScalar = capabilities.relaxedFloat32toFloat16Performance, .relaxedFloat32toFloat16PerformanceTensor = capabilities.relaxedFloat32toFloat16Performance, .operandPerformance = makeQuantized8PerformanceConsistentWithP( capabilities.quantized8Performance)}; auto& opPerf = ret.operandPerformance; opPerf.resize(opPerf.size() + 2); opPerf[opPerf.size() - 2] = {V1_2::OperandType::TENSOR_FLOAT32, capabilities.float32Performance}; opPerf[opPerf.size() - 1] = {V1_2::OperandType::FLOAT32, capabilities.float32Performance}; using OperandPerformance = V1_2::Capabilities::OperandPerformance; std::sort(opPerf.begin(), opPerf.end(), [](const OperandPerformance& a, const OperandPerformance& b) { return a.type < b.type; }); return ret; } V1_2::Capabilities convertToV1_2(const V1_2::Capabilities& capabilities) { return capabilities; } V1_2::Capabilities convertToV1_2(const V1_3::Capabilities& capabilities) { V1_2::Capabilities ret = { .relaxedFloat32toFloat16PerformanceScalar = capabilities.relaxedFloat32toFloat16PerformanceScalar, .relaxedFloat32toFloat16PerformanceTensor = capabilities.relaxedFloat32toFloat16PerformanceTensor, }; const auto& inputOpPerf = capabilities.operandPerformance; hardware::hidl_vec opPerfSupported; opPerfSupported.resize(inputOpPerf.size()); auto last = std::copy_if(inputOpPerf.begin(), inputOpPerf.end(), opPerfSupported.begin(), [](V1_3::Capabilities::OperandPerformance opPerf) { return validOperandType(static_cast(opPerf.type)); }); opPerfSupported.resize(std::distance(opPerfSupported.begin(), last)); auto& convertedOpPerf = ret.operandPerformance; convertedOpPerf.resize(opPerfSupported.size()); std::transform(opPerfSupported.begin(), opPerfSupported.end(), convertedOpPerf.begin(), [](V1_3::Capabilities::OperandPerformance opPerf) { return V1_2::Capabilities::OperandPerformance{ static_cast(opPerf.type), opPerf.info}; }); return ret; } V1_3::Capabilities convertToV1_3(const V1_0::Capabilities& capabilities) { return convertToV1_3(convertToV1_2(capabilities)); } V1_3::Capabilities convertToV1_3(const V1_1::Capabilities& capabilities) { return convertToV1_3(convertToV1_2(capabilities)); } V1_3::Capabilities convertToV1_3(const V1_2::Capabilities& capabilities) { V1_3::Capabilities ret = { .relaxedFloat32toFloat16PerformanceScalar = capabilities.relaxedFloat32toFloat16PerformanceScalar, .relaxedFloat32toFloat16PerformanceTensor = capabilities.relaxedFloat32toFloat16PerformanceTensor, .ifPerformance = kNoPerformanceInfo, .whilePerformance = kNoPerformanceInfo, }; auto& opPerf = ret.operandPerformance; opPerf.resize(capabilities.operandPerformance.size()); std::transform(capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(), opPerf.begin(), [](V1_2::Capabilities::OperandPerformance opPerf) { return V1_3::Capabilities::OperandPerformance{ static_cast(opPerf.type), opPerf.info}; }); return ret; } V1_3::Capabilities convertToV1_3(const V1_3::Capabilities& capabilities) { return capabilities; } static V1_0::Operation uncheckedConvertToV1_0(const V1_1::Operation& operation) { return {.type = uncheckedConvertToV1_0(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_1::Operation convertToV1_1(const V1_0::Operation& operation) { return {.type = convertToV1_1(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static hardware::hidl_vec uncheckedConvertToV1_0( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform( operations.begin(), operations.end(), result.begin(), [](const V1_1::Operation& operation) { return uncheckedConvertToV1_0(operation); }); return result; } static hardware::hidl_vec convertToV1_1( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform(operations.begin(), operations.end(), result.begin(), [](const V1_0::Operation& operation) { return convertToV1_1(operation); }); return result; } bool compliantWithV1_0(const V1_3::Operand& operand) { return validOperandType(static_cast(operand.type)) && (nonExtensionOperandTypeIsScalar(static_cast(operand.type)) || operand.dimensions.size() != 0) && compliantWithV1_0(operand.lifetime); } bool compliantWithV1_2(const V1_3::Operand& operand) { return validOperandType(static_cast(operand.type)) && compliantWithV1_0(operand.lifetime); } bool compliantWithV1_3(const V1_3::Operand& /*operand*/) { return true; } bool compliantWithAidl(const V1_3::Operand& operand) { if (static_cast>(operand.type) > std::numeric_limits::max()) { return false; } if (operand.location.poolIndex > std::numeric_limits::max()) { return false; } if (operand.extraParams.getDiscriminator() == V1_2::Operand::ExtraParams::hidl_discriminator::channelQuant && operand.extraParams.channelQuant().channelDim > std::numeric_limits::max()) { return false; } for (auto dim : operand.dimensions) { if (dim > std::numeric_limits::max()) { return false; } } return true; } static bool compliantWith(HalVersion version, const V1_3::Model& model, std::set* noncompliantOperations) { // A boolean vector indicating whether each pool is compliant with the target HAL version. std::vector isPoolCompliant(model.pools.size(), false); std::transform( model.pools.begin(), model.pools.end(), isPoolCompliant.begin(), [version](const hardware::hidl_memory& pool) { return validatePool(pool, version); }); // A boolean vector indicating whether each operand is compliant with the target HAL version. std::vector isOperandCompliant(model.main.operands.size(), false); std::transform(model.main.operands.begin(), model.main.operands.end(), isOperandCompliant.begin(), [&isPoolCompliant, version](const V1_3::Operand& op) { bool is_operand_compliant = false; switch (version) { case HalVersion::UNKNOWN: is_operand_compliant = false; break; case HalVersion::V1_0: is_operand_compliant = compliantWithV1_0(op); break; case HalVersion::V1_1: // There is no V1_1::Operand -- both V1_0::Model // and V1_1::Model use V1_0::Operand. is_operand_compliant = compliantWithV1_0(op); break; case HalVersion::V1_2: is_operand_compliant = compliantWithV1_2(op); break; case HalVersion::V1_3: is_operand_compliant = compliantWithV1_3(op); break; case HalVersion::AIDL_UNSTABLE: is_operand_compliant = compliantWithAidl(op); break; } return is_operand_compliant && !(op.lifetime == V1_3::OperandLifeTime::CONSTANT_REFERENCE && !isPoolCompliant[op.location.poolIndex]); }); auto allOperandsCompliant = [&isOperandCompliant](const hardware::hidl_vec& indices) { return std::all_of( indices.begin(), indices.end(), [&isOperandCompliant](const uint32_t ind) { return isOperandCompliant[ind]; }); }; auto localValidateOperation = [&model, version, &allOperandsCompliant](const V1_3::Operation& op) { if (!allOperandsCompliant(op.inputs) || !allOperandsCompliant(op.outputs)) return false; int error = validateOperation(static_cast(op.type), op.inputs.size(), op.inputs.size() > 0 ? op.inputs.data() : nullptr, op.outputs.size(), op.outputs.size() > 0 ? op.outputs.data() : nullptr, uncheckedConvert(model.main.operands), version); return error == ANEURALNETWORKS_NO_ERROR; }; if (noncompliantOperations) { CHECK(noncompliantOperations->empty()); for (uint32_t idx = 0; idx < model.main.operations.size(); ++idx) { if (!localValidateOperation(model.main.operations[idx])) { noncompliantOperations->insert(idx); } } return noncompliantOperations->empty(); } else { return std::all_of(model.main.operations.begin(), model.main.operations.end(), localValidateOperation); } } bool compliantWithV1_0(const V1_0::Model& /*model*/) { return true; } bool compliantWithV1_0(const V1_1::Model& model) { // In addition to new enumeration values being introduced in V1_1::Model, a // new flag was introduced to indicate whether or not float32 data can be // calculated using float16 units. This 'relaxComputationFloat32toFloat16' // flag is not relevant in whether a V1_1::Model is compliant with a // V1_0::Model because all 1.0 drivers require strict calculation by default // in the P NN runtime. Even if fp16 calculations are allowed, they can // still be computed by a strict fp32 driver. auto operands = uncheckedConvert(convertToV1_3(model.operands)); return std::all_of(model.operations.begin(), model.operations.end(), [&operands](const V1_1::Operation& op) { int error = validateOperation( static_cast(op.type), op.inputs.size(), op.inputs.size() > 0 ? op.inputs.data() : nullptr, op.outputs.size(), op.outputs.size() > 0 ? op.outputs.data() : nullptr, operands, HalVersion::V1_0); return error == ANEURALNETWORKS_NO_ERROR; }); } bool compliantWithV1_0(const V1_2::Model& model, std::set* noncompliantOperations) { return compliantWith(HalVersion::V1_0, convertToV1_3(model), noncompliantOperations); } bool compliantWithV1_0(const V1_3::Model& model, std::set* noncompliantOperations) { return compliantWith(HalVersion::V1_0, model, noncompliantOperations); } bool compliantWithV1_1(const V1_0::Model&) { return true; } bool compliantWithV1_1(const V1_1::Model&) { return true; } bool compliantWithV1_1(const V1_2::Model& model, std::set* noncompliantOperations) { return compliantWith(HalVersion::V1_1, convertToV1_3(model), noncompliantOperations); } bool compliantWithV1_1(const V1_3::Model& model, std::set* noncompliantOperations) { return compliantWith(HalVersion::V1_1, model, noncompliantOperations); } bool compliantWithV1_2(const V1_0::Model&) { return true; } bool compliantWithV1_2(const V1_1::Model&) { return true; } bool compliantWithV1_2(const V1_2::Model&, std::set* /*noncompliantOperations*/) { return true; } bool compliantWithV1_2(const V1_3::Model& model, std::set* noncompliantOperations) { return compliantWith(HalVersion::V1_2, model, noncompliantOperations); } static V1_0::Operation uncheckedConvertToV1_0(const V1_2::Operation& operation) { return {.type = uncheckedConvertToV1_0(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_0::Operation uncheckedConvertToV1_0(const V1_3::Operation& operation) { return {.type = uncheckedConvertToV1_0(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_1::Operation uncheckedConvertToV1_1(const V1_2::Operation& operation) { return {.type = uncheckedConvertToV1_1(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_1::Operation uncheckedConvertToV1_1(const V1_3::Operation& operation) { return {.type = uncheckedConvertToV1_1(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_2::Operation convertToV1_2(const V1_0::Operation& operation) { return {.type = convertToV1_2(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_2::Operation convertToV1_2(const V1_1::Operation& operation) { return {.type = convertToV1_2(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_2::Operation uncheckedConvertToV1_2(const V1_3::Operation& operation) { return {.type = uncheckedConvertToV1_2(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_3::Operation convertToV1_3(const V1_0::Operation& operation) { return {.type = convertToV1_3(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_3::Operation convertToV1_3(const V1_1::Operation& operation) { return {.type = convertToV1_3(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static V1_3::Operation convertToV1_3(const V1_2::Operation& operation) { return {.type = convertToV1_3(operation.type), .inputs = operation.inputs, .outputs = operation.outputs}; } static hardware::hidl_vec uncheckedConvertToV1_0( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform( operations.begin(), operations.end(), result.begin(), [](const V1_3::Operation& operation) { return uncheckedConvertToV1_0(operation); }); return result; } static hardware::hidl_vec uncheckedConvertToV1_0( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform( operations.begin(), operations.end(), result.begin(), [](const V1_2::Operation& operation) { return uncheckedConvertToV1_0(operation); }); return result; } static hardware::hidl_vec uncheckedConvertToV1_2( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform( operations.begin(), operations.end(), result.begin(), [](const V1_3::Operation& operation) { return uncheckedConvertToV1_2(operation); }); return result; } static hardware::hidl_vec uncheckedConvertToV1_1( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform( operations.begin(), operations.end(), result.begin(), [](const V1_2::Operation& operation) { return uncheckedConvertToV1_1(operation); }); return result; } static hardware::hidl_vec uncheckedConvertToV1_1( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform( operations.begin(), operations.end(), result.begin(), [](const V1_3::Operation& operation) { return uncheckedConvertToV1_1(operation); }); return result; } static hardware::hidl_vec convertToV1_2( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform(operations.begin(), operations.end(), result.begin(), [](const V1_0::Operation& operation) { return convertToV1_2(operation); }); return result; } static hardware::hidl_vec convertToV1_2( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform(operations.begin(), operations.end(), result.begin(), [](const V1_1::Operation& operation) { return convertToV1_2(operation); }); return result; } static hardware::hidl_vec convertToV1_3( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform(operations.begin(), operations.end(), result.begin(), [](const V1_0::Operation& operation) { return convertToV1_3(operation); }); return result; } static hardware::hidl_vec convertToV1_3( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform(operations.begin(), operations.end(), result.begin(), [](const V1_1::Operation& operation) { return convertToV1_3(operation); }); return result; } static hardware::hidl_vec convertToV1_3( const hardware::hidl_vec& operations) { hardware::hidl_vec result(operations.size()); std::transform(operations.begin(), operations.end(), result.begin(), [](const V1_2::Operation& operation) { return convertToV1_3(operation); }); return result; } static bool compliantWithV1_0(const V1_2::OperandType& operandType) { return validOperandType(static_cast(operandType)); } static bool compliantWithV1_0(const V1_3::OperandType& operandType) { return validOperandType(static_cast(operandType)); } static bool compliantWithV1_2(const V1_3::OperandType& operandType) { return validOperandType(static_cast(operandType)); } V1_0::OperandType convertToV1_0(const V1_2::OperandType& operandType) { if (!compliantWithV1_0(operandType)) { LOG(ERROR) << "Upcasting non-compliant operand type " << toString(operandType) << " from V1_2::OperandType to V1_0::OperandType"; } return static_cast(operandType); } V1_2::OperandType convertToV1_2(const V1_0::OperandType& operandType) { return static_cast(operandType); } V1_2::OperandType convertToV1_2(const V1_3::OperandType& operandType) { if (!compliantWithV1_2(operandType)) { LOG(ERROR) << "Upcasting non-compliant operand type " << toString(operandType) << " from V1_3::OperandType to V1_2::OperandType"; } return static_cast(operandType); } V1_0::OperandType convertToV1_0(const V1_3::OperandType& operandType) { if (!compliantWithV1_0(operandType)) { LOG(ERROR) << "Upcasting non-compliant operand type " << toString(operandType) << " from V1_3::Operand to V1_0::Operand"; } return static_cast(operandType); } bool compliantWithV1_0(V1_0::OperandLifeTime /*lifetime*/) { return true; } bool compliantWithV1_0(V1_3::OperandLifeTime lifetime) { return lifetime != V1_3::OperandLifeTime::SUBGRAPH; } bool compliantWithV1_3(V1_0::OperandLifeTime /*lifetime*/) { return true; } bool compliantWithV1_3(V1_3::OperandLifeTime /*lifetime*/) { return true; } V1_0::OperandLifeTime convertToV1_0(V1_0::OperandLifeTime lifetime) { return lifetime; } V1_0::OperandLifeTime convertToV1_0(V1_3::OperandLifeTime lifetime) { if (!compliantWithV1_0(lifetime)) { LOG(ERROR) << "Upcasting non-compliant lifetime " << toString(lifetime) << " from V1_3 to V1_0"; } return static_cast(lifetime); } V1_3::OperandLifeTime convertToV1_3(V1_0::OperandLifeTime lifetime) { return static_cast(lifetime); } V1_3::OperandLifeTime convertToV1_3(V1_3::OperandLifeTime lifetime) { return lifetime; } V1_0::Operand convertToV1_0(const V1_2::Operand& operand) { return {.type = convertToV1_0(operand.type), .dimensions = operand.dimensions, .numberOfConsumers = operand.numberOfConsumers, .scale = operand.scale, .zeroPoint = operand.zeroPoint, .lifetime = convertToV1_0(operand.lifetime), .location = operand.location}; } V1_0::Operand convertToV1_0(const V1_3::Operand& operand) { return {.type = convertToV1_0(operand.type), .dimensions = operand.dimensions, .numberOfConsumers = operand.numberOfConsumers, .scale = operand.scale, .zeroPoint = operand.zeroPoint, .lifetime = convertToV1_0(operand.lifetime), .location = operand.location}; } V1_2::Operand convertToV1_2(const V1_0::Operand& operand) { return {.type = convertToV1_2(operand.type), .dimensions = operand.dimensions, .numberOfConsumers = operand.numberOfConsumers, .scale = operand.scale, .zeroPoint = operand.zeroPoint, .lifetime = operand.lifetime, .location = operand.location}; } V1_2::Operand convertToV1_2(const V1_3::Operand& operand) { return {.type = convertToV1_2(operand.type), .dimensions = operand.dimensions, .numberOfConsumers = operand.numberOfConsumers, .scale = operand.scale, .zeroPoint = operand.zeroPoint, .lifetime = static_cast(operand.lifetime), .location = operand.location, .extraParams = operand.extraParams}; } V1_3::Operand convertToV1_3(const V1_0::Operand& operand) { return {.type = static_cast(operand.type), .dimensions = operand.dimensions, .numberOfConsumers = operand.numberOfConsumers, .scale = operand.scale, .zeroPoint = operand.zeroPoint, .lifetime = convertToV1_3(operand.lifetime), .location = operand.location}; } V1_3::Operand convertToV1_3(const V1_2::Operand& operand) { return {.type = static_cast(operand.type), .dimensions = operand.dimensions, .numberOfConsumers = operand.numberOfConsumers, .scale = operand.scale, .zeroPoint = operand.zeroPoint, .lifetime = convertToV1_3(operand.lifetime), .location = operand.location, .extraParams = operand.extraParams}; } V1_3::Operand convertToV1_3(const V1_3::Operand& operand) { return operand; } hardware::hidl_vec convertToV1_0(const hardware::hidl_vec& operands) { return operands; } hardware::hidl_vec convertToV1_0(const hardware::hidl_vec& operands) { hardware::hidl_vec result(operands.size()); std::transform(operands.begin(), operands.end(), result.begin(), [](const V1_2::Operand& operand) { return convertToV1_0(operand); }); return result; } hardware::hidl_vec convertToV1_0(const hardware::hidl_vec& operands) { hardware::hidl_vec result(operands.size()); std::transform(operands.begin(), operands.end(), result.begin(), [](const V1_3::Operand& operand) { return convertToV1_0(operand); }); return result; } hardware::hidl_vec convertToV1_2(const hardware::hidl_vec& operands) { hardware::hidl_vec result(operands.size()); std::transform(operands.begin(), operands.end(), result.begin(), [](const V1_0::Operand& operand) { return convertToV1_2(operand); }); return result; } hardware::hidl_vec convertToV1_2(const hardware::hidl_vec& operands) { return operands; } hardware::hidl_vec convertToV1_2(const hardware::hidl_vec& operands) { hardware::hidl_vec result(operands.size()); std::transform(operands.begin(), operands.end(), result.begin(), [](const V1_3::Operand& operand) { return convertToV1_2(operand); }); return result; } hardware::hidl_vec convertToV1_3(const hardware::hidl_vec& operands) { hardware::hidl_vec result(operands.size()); std::transform(operands.begin(), operands.end(), result.begin(), [](const V1_0::Operand& operand) { return convertToV1_3(operand); }); return result; } hardware::hidl_vec convertToV1_3(const hardware::hidl_vec& operands) { hardware::hidl_vec result(operands.size()); std::transform(operands.begin(), operands.end(), result.begin(), [](const V1_2::Operand& operand) { return convertToV1_3(operand); }); return result; } hardware::hidl_vec convertToV1_3(const hardware::hidl_vec& operands) { return operands; } V1_0::Model convertToV1_0(const V1_0::Model& model) { return model; } V1_0::Model convertToV1_0(const V1_1::Model& model) { if (!compliantWithV1_0(model)) { LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model)) << " from V1_1::Model to V1_0::Model"; } return {.operands = model.operands, .operations = uncheckedConvertToV1_0(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes, .operandValues = model.operandValues, .pools = model.pools}; } V1_0::Model convertToV1_0(const V1_2::Model& model) { if (!compliantWithV1_0(model)) { LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model)) << " from V1_2::Model to V1_0::Model"; } return {.operands = convertToV1_0(model.operands), .operations = uncheckedConvertToV1_0(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes, .operandValues = model.operandValues, .pools = model.pools}; } V1_0::Model convertToV1_0(const V1_3::Model& model) { if (!compliantWithV1_0(model)) { LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model)) << " from V1_3::Model to V1_0::Model"; } return {.operands = convertToV1_0(model.main.operands), .operations = uncheckedConvertToV1_0(model.main.operations), .inputIndexes = model.main.inputIndexes, .outputIndexes = model.main.outputIndexes, .operandValues = model.operandValues, .pools = model.pools}; } V1_1::Model convertToV1_1(const V1_0::Model& model) { return {.operands = model.operands, .operations = convertToV1_1(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = false}; } V1_1::Model convertToV1_1(const V1_1::Model& model) { return model; } V1_1::Model convertToV1_1(const V1_2::Model& model) { if (!compliantWithV1_1(model)) { LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model)) << " from V1_2::Model to V1_1::Model"; } return {.operands = convertToV1_0(model.operands), // Operands in 1.1 and 1.0 are identical. .operations = uncheckedConvertToV1_1(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16}; } V1_1::Model convertToV1_1(const V1_3::Model& model) { if (!compliantWithV1_1(model)) { LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model)) << " from V1_3::Model to V1_1::Model"; } return {// Operands in 1.1 and 1.0 are identical. .operands = convertToV1_0(model.main.operands), .operations = uncheckedConvertToV1_1(model.main.operations), .inputIndexes = model.main.inputIndexes, .outputIndexes = model.main.outputIndexes, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16}; } V1_2::Model convertToV1_2(const V1_0::Model& model) { return {.operands = convertToV1_2(model.operands), .operations = convertToV1_2(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = false}; } V1_2::Model convertToV1_2(const V1_1::Model& model) { return {.operands = convertToV1_2(model.operands), .operations = convertToV1_2(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16}; } V1_2::Model convertToV1_2(const V1_2::Model& model) { return model; } V1_2::Model convertToV1_2(const V1_3::Model& model) { if (!compliantWithV1_2(model)) { LOG(ERROR) << "Upcasting non-compliant model " << SHOW_IF_DEBUG(toString(model)) << " from V1_3::Model to V1_2::Model"; } return {.operands = convertToV1_2(model.main.operands), .operations = uncheckedConvertToV1_2(model.main.operations), .inputIndexes = model.main.inputIndexes, .outputIndexes = model.main.outputIndexes, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16, .extensionNameToPrefix = model.extensionNameToPrefix}; } V1_3::Model convertToV1_3(const V1_0::Model& model) { return {.main = {.operands = convertToV1_3(model.operands), .operations = convertToV1_3(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes}, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = false}; } V1_3::Model convertToV1_3(const V1_1::Model& model) { return {.main = {.operands = convertToV1_3(model.operands), .operations = convertToV1_3(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes}, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16}; } V1_3::Model convertToV1_3(const V1_2::Model& model) { return {.main = {.operands = convertToV1_3(model.operands), .operations = convertToV1_3(model.operations), .inputIndexes = model.inputIndexes, .outputIndexes = model.outputIndexes}, .operandValues = model.operandValues, .pools = model.pools, .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16, .extensionNameToPrefix = model.extensionNameToPrefix}; } V1_3::Model convertToV1_3(const V1_3::Model& model) { return model; } bool compliantWithV1_0(const V1_0::Request& /*request*/) { return true; } bool compliantWithV1_0(const V1_3::Request& request) { return std::all_of(request.pools.begin(), request.pools.end(), [](const auto& pool) { if (pool.getDiscriminator() != V1_3::Request::MemoryPool::hidl_discriminator::hidlMemory) { return false; } const auto& name = pool.hidlMemory().name(); return name == "ashmem" || name == "mmap_fd"; }); } bool compliantWithV1_2(const V1_3::Request& request) { return std::all_of(request.pools.begin(), request.pools.end(), [](const auto& pool) { if (pool.getDiscriminator() != V1_3::Request::MemoryPool::hidl_discriminator::hidlMemory) { return false; } const auto& name = pool.hidlMemory().name(); return name == "ashmem" || name == "mmap_fd" || name == "hardware_buffer_blob" || name == "hardware_buffer"; }); } static hardware::hidl_memory convertToV1_0(const V1_3::Request::MemoryPool& pool) { switch (pool.getDiscriminator()) { case V1_3::Request::MemoryPool::hidl_discriminator::hidlMemory: return pool.hidlMemory(); case V1_3::Request::MemoryPool::hidl_discriminator::token: return hardware::hidl_memory{}; } } static V1_3::Request::MemoryPool convertToV1_3(const hardware::hidl_memory& pool) { V1_3::Request::MemoryPool ret; ret.hidlMemory(pool); return ret; } V1_0::Request convertToV1_0(const V1_0::Request& request) { return request; } static V1_0::Request uncheckedConvertToV1_0(const V1_3::Request& request) { hardware::hidl_vec pools(request.pools.size()); std::transform(request.pools.begin(), request.pools.end(), pools.begin(), [](const auto& pool) { return convertToV1_0(pool); }); return {.inputs = request.inputs, .outputs = request.outputs, .pools = std::move(pools)}; } V1_0::Request convertToV1_0(const V1_3::Request& request) { if (!compliantWithV1_0(request)) { LOG(ERROR) << "Upcasting non-compliant request " << SHOW_IF_DEBUG(toString(request)) << " from V1_3::Request to V1_0::Request of version 1.0"; } return uncheckedConvertToV1_0(request); } V1_0::Request convertToV1_2(const V1_3::Request& request) { if (!compliantWithV1_2(request)) { LOG(ERROR) << "Upcasting non-compliant request " << SHOW_IF_DEBUG(toString(request)) << " from V1_3::Request to V1_0::Request of version 1.2"; } return uncheckedConvertToV1_0(request); } V1_3::Request convertToV1_3(const V1_0::Request& request) { hardware::hidl_vec pools(request.pools.size()); std::transform(request.pools.begin(), request.pools.end(), pools.begin(), [](const auto& pool) { return convertToV1_3(pool); }); return {.inputs = request.inputs, .outputs = request.outputs, .pools = std::move(pools)}; } V1_3::Request convertToV1_3(const V1_3::Request& request) { return request; } ErrorStatus uncheckedConvert(V1_0::ErrorStatus status) { return handleError(convert(status)); } ErrorStatus uncheckedConvert(V1_3::ErrorStatus status) { return handleError(convert(status)); } OperandType uncheckedConvert(V1_3::OperandType operandType) { return handleError(unvalidatedConvert(operandType)); } OperationType uncheckedConvert(V1_3::OperationType operandType) { return handleError(unvalidatedConvert(operandType)); } Operand::LifeTime uncheckedConvert(V1_3::OperandLifeTime lifetime) { return handleError(unvalidatedConvert(lifetime)); } MeasureTiming uncheckedConvert(V1_2::MeasureTiming measure) { return handleError(convert(measure)); } DataLocation uncheckedConvert(const V1_0::DataLocation& location) { return handleError(unvalidatedConvert(location)); } Operand uncheckedConvert(const V1_3::Operand& operand) { return handleError(unvalidatedConvert(operand)); } Operand::ExtraParams uncheckedConvert(const V1_2::Operand::ExtraParams& params) { return handleError(unvalidatedConvert(params)); } Operand::SymmPerChannelQuantParams uncheckedConvert(const V1_2::SymmPerChannelQuantParams& params) { return handleError(unvalidatedConvert(params)); } Operand::ExtensionParams uncheckedConvert(const hardware::hidl_vec& params) { return params; } Operation uncheckedConvert(const V1_3::Operation& operation) { return handleError(unvalidatedConvert(operation)); } template static std::vector convertVec(const hardware::hidl_vec& items) { std::vector result; result.reserve(items.size()); std::transform(items.begin(), items.end(), std::back_inserter(result), [](const HalType& item) { return uncheckedConvert(item); }); return result; } Model uncheckedConvert(const V1_3::Model& model) { return handleError(convert(model)); } Model::Subgraph uncheckedConvert(const V1_3::Subgraph& subgraph) { return handleError(unvalidatedConvert(subgraph)); } Model::ExtensionNameAndPrefix uncheckedConvert(const V1_2::Model::ExtensionNameAndPrefix& x) { return handleError(unvalidatedConvert(x)); } Request uncheckedConvert(const V1_3::Request& request) { return handleError(convert(request)); } Request::Argument uncheckedConvert(const V1_0::RequestArgument& requestArgument) { return handleError(unvalidatedConvert(requestArgument)); } Request::MemoryPool uncheckedConvert(const V1_3::Request::MemoryPool& memoryPool) { return handleError(unvalidatedConvert(memoryPool)); } OutputShape uncheckedConvert(const V1_2::OutputShape& outputShape) { return handleError(unvalidatedConvert(outputShape)); } std::vector uncheckedConvert( const hardware::hidl_vec& outputShapes) { return convertVec(outputShapes); } Capabilities uncheckedConvert(const V1_3::Capabilities& capabilities) { return handleError(convert(capabilities)); } Capabilities::OperandPerformance uncheckedConvert( const V1_3::Capabilities::OperandPerformance& operandPerformance) { return handleError(unvalidatedConvert(operandPerformance)); } Capabilities::PerformanceInfo uncheckedConvert(const V1_0::PerformanceInfo& performanceInfo) { return handleError(unvalidatedConvert(performanceInfo)); } Extension uncheckedConvert(const V1_2::Extension& extension) { return handleError(unvalidatedConvert(extension)); } std::vector uncheckedConvert(const hardware::hidl_vec& extensions) { return convertVec(extensions); } Extension::OperandTypeInformation uncheckedConvert( const V1_2::Extension::OperandTypeInformation& info) { return handleError(unvalidatedConvert(info)); } OptionalDuration uncheckedConvert(const V1_3::OptionalTimeoutDuration& timeoutDuration) { return handleError(convert(timeoutDuration)); } Timing uncheckedConvert(const V1_2::Timing& timing) { return handleError(convert(timing)); } V1_0::ErrorStatus convertToV1_0(ErrorStatus status) { return static_cast(static_cast(status)); } V1_3::ErrorStatus convertToV1_3(ErrorStatus status) { return handleError(V1_3::utils::convert(status)); } V1_3::OperandType convertToV1_3(OperandType operandType) { return handleError(V1_3::utils::unvalidatedConvert(operandType)); } V1_3::OperationType convertToV1_3(OperationType operandType) { return handleError(V1_3::utils::unvalidatedConvert(operandType)); } V1_3::OperandLifeTime convertToV1_3(Operand::LifeTime lifetime) { return handleError(V1_3::utils::unvalidatedConvert(lifetime)); } V1_1::ExecutionPreference convertToV1_1(ExecutionPreference preference) { return handleError(V1_1::utils::convert(preference)); } V1_3::Priority convertToV1_3(Priority priority) { return handleError(V1_3::utils::convert(priority)); } V1_2::MeasureTiming convertToV1_2(MeasureTiming measure) { return handleError(V1_2::utils::convert(measure)); } V1_0::DataLocation convertToV1_0(const DataLocation& location) { return handleError(V1_0::utils::unvalidatedConvert(location)); } V1_3::Operand convertToV1_3(const Operand& operand) { return handleError(V1_3::utils::unvalidatedConvert(operand)); } V1_2::Operand::ExtraParams convertToV1_2(const Operand::ExtraParams& params) { return handleError(V1_2::utils::unvalidatedConvert(params)); } V1_2::SymmPerChannelQuantParams convertToV1_2(const Operand::SymmPerChannelQuantParams& params) { return handleError(V1_2::utils::unvalidatedConvert(params)); } hardware::hidl_vec uncheckedConvert(const Operand::ExtensionParams& params) { return params; } V1_3::Operation convertToV1_3(const Operation& operation) { return handleError(V1_3::utils::unvalidatedConvert(operation)); } template static hardware::hidl_vec convertVecToV1_0(const std::vector& items) { hardware::hidl_vec result(items.size()); std::transform(items.begin(), items.end(), result.begin(), [](const CanonicalType& item) { return convertToV1_0(item); }); return result; } template static hardware::hidl_vec convertVecToV1_2(const std::vector& items) { hardware::hidl_vec result(items.size()); std::transform(items.begin(), items.end(), result.begin(), [](const CanonicalType& item) { return convertToV1_2(item); }); return result; } template static hardware::hidl_vec convertVecToV1_3(const std::vector& items) { hardware::hidl_vec result(items.size()); std::transform(items.begin(), items.end(), result.begin(), [](const CanonicalType& item) { return convertToV1_3(item); }); return result; } V1_2::OutputShape convertToV1_2(const OutputShape& outputShape) { return handleError(V1_2::utils::unvalidatedConvert(outputShape)); } hardware::hidl_vec convertToV1_2(const std::vector& outputShapes) { return convertVecToV1_2(outputShapes); } V1_3::Model convertToV1_3(const Model& model) { return handleError(V1_3::utils::convert(model)); } V1_3::Subgraph convertToV1_3(const Model::Subgraph& subgraph) { return handleError(V1_3::utils::unvalidatedConvert(subgraph)); } V1_2::Model::ExtensionNameAndPrefix convertToV1_2(const Model::ExtensionNameAndPrefix& x) { return handleError(V1_2::utils::unvalidatedConvert(x)); } V1_3::Request convertToV1_3(const Request& request) { return handleError(V1_3::utils::convert(request)); } V1_0::RequestArgument convertToV1_0(const Request::Argument& requestArgument) { return handleError(V1_0::utils::unvalidatedConvert(requestArgument)); } V1_3::Request::MemoryPool convertToV1_3(const Request::MemoryPool& memoryPool) { return handleError(V1_3::utils::unvalidatedConvert(memoryPool)); } std::vector uncheckedConvert( const hardware::hidl_vec& memoryPools) { return convertVec(memoryPools); } V1_3::OptionalTimePoint convertToV1_3(const OptionalTimePoint& timePoint) { return handleError(V1_3::utils::convert(timePoint)); } V1_3::OptionalTimeoutDuration convertToV1_3(const OptionalDuration& timeoutDuration) { return handleError(V1_3::utils::convert(timeoutDuration)); } V1_2::Timing convertToV1_2(const Timing& timing) { return handleError(V1_2::utils::convert(timing)); } V1_3::BufferRole convertToV1_3(const BufferRole& bufferRole) { return handleError(V1_3::utils::unvalidatedConvert(bufferRole)); } hardware::hidl_vec convertToV1_3(const std::vector& bufferRoles) { return convertVecToV1_3(bufferRoles); } hardware::hidl_vec convertToV1_0(const Model::OperandValues& operandValues) { return handleError(V1_0::utils::unvalidatedConvert(operandValues)); } hardware::hidl_memory convertToV1_0(const SharedMemory& memory) { return handleError(V1_0::utils::unvalidatedConvert(memory)); } SharedMemory uncheckedConvert(const hardware::hidl_memory& memory) { return handleError(convert(memory)); } hardware::hidl_vec convertToV1_0(const std::vector& memories) { return convertVecToV1_0(memories); } std::vector uncheckedConvert( const hardware::hidl_vec& memories) { return convertVec(memories); } std::vector uncheckedConvert(const hardware::hidl_vec& subgraphs) { return convertVec(subgraphs); } std::vector uncheckedConvert(const hardware::hidl_vec& operands) { return convertVec(operands); } } // namespace nn } // namespace android