Home | History | Annotate | Download | only in include
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #ifndef ANDROID_ML_NN_COMMON_UTILS_H
     18 #define ANDROID_ML_NN_COMMON_UTILS_H
     19 
     20 #include "HalInterfaces.h"
     21 #include "NeuralNetworks.h"
     22 #include "ValidateHal.h"
     23 
     24 #include <android-base/logging.h>
     25 #include <optional>
     26 #include <set>
     27 #include <vector>
     28 
     29 namespace android {
     30 namespace nn {
     31 
     32 // The number of data types (OperandCode) defined in NeuralNetworks.h.
     33 const int kNumberOfDataTypes = 14;
     34 
     35 // The number of operation types (OperationCode) defined in NeuralNetworks.h.
     36 const int kNumberOfOperationTypes = 95;
     37 
     38 // The number of execution preferences defined in NeuralNetworks.h.
     39 const int kNumberOfPreferences = 3;
     40 
     41 // The number of data types (OperandCode) defined in NeuralNetworksOEM.h.
     42 const int kNumberOfDataTypesOEM = 2;
     43 
     44 // The number of operation types (OperationCode) defined in NeuralNetworksOEM.h.
     45 const int kNumberOfOperationTypesOEM = 1;
     46 
     47 // The lowest number assigned to any OEM Code in NeuralNetworksOEM.h.
     48 const int kOEMCodeBase = 10000;
     49 
     50 /* IMPORTANT: if you change the following list, don't
     51  * forget to update the corresponding 'tags' table in
     52  * the initVlogMask() function implemented in Utils.cpp.
     53  */
     54 enum VLogFlags {
     55     MODEL = 0,
     56     COMPILATION,
     57     EXECUTION,
     58     CPUEXE,
     59     MANAGER,
     60     DRIVER
     61 };
     62 
     63 #define VLOG_IS_ON(TAG) \
     64     ((vLogMask & (1 << (TAG))) != 0)
     65 
     66 #define VLOG(TAG)         \
     67     if (LIKELY(!VLOG_IS_ON(TAG))) \
     68         ;                 \
     69     else                  \
     70         LOG(INFO)
     71 
     72 extern int vLogMask;
     73 void initVLogMask();
     74 
     75 #ifdef NN_DEBUGGABLE
     76 #define SHOW_IF_DEBUG(msg) msg
     77 #else
     78 #define SHOW_IF_DEBUG(msg) ""
     79 #endif
     80 
     81 // DEPRECATED(b/118737105). Use CHECK.
     82 #define nnAssert(v) CHECK(v)
     83 
     84 #define NN_RETURN_IF_ERROR(expr)                      \
     85     do {                                              \
     86         int _errorCode = (expr);                      \
     87         if (_errorCode != ANEURALNETWORKS_NO_ERROR) { \
     88             return _errorCode;                        \
     89         }                                             \
     90     } while (0)
     91 
     92 // The NN_RET_CHECK family of macros defined below is similar to the CHECK family defined in
     93 // system/core/base/include/android-base/logging.h
     94 //
     95 // The difference is that NN_RET_CHECK macros use LOG(ERROR) instead of LOG(FATAL)
     96 // and return false instead of aborting.
     97 
     98 // Logs an error and returns false. Append context using << after. For example:
     99 //
    100 //   NN_RET_CHECK_FAIL() << "Something went wrong";
    101 //
    102 // The containing function must return a bool.
    103 #define NN_RET_CHECK_FAIL()                   \
    104     return ::android::nn::FalseyErrorStream() \
    105            << "NN_RET_CHECK failed (" << __FILE__ << ":" << __LINE__ << "): "
    106 
    107 // Logs an error and returns false if condition is false. Extra logging can be appended using <<
    108 // after. For example:
    109 //
    110 //   NN_RET_CHECK(false) << "Something went wrong";
    111 //
    112 // The containing function must return a bool.
    113 #define NN_RET_CHECK(condition) \
    114     while (UNLIKELY(!(condition))) NN_RET_CHECK_FAIL() << #condition << " "
    115 
    116 // Helper for NN_CHECK_xx(x, y) macros.
    117 #define NN_RET_CHECK_OP(LHS, RHS, OP)                                                 \
    118     for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS);                \
    119          UNLIKELY(!(_values.lhs OP _values.rhs));                                     \
    120          /* empty */)                                                                 \
    121     NN_RET_CHECK_FAIL() << #LHS << " " << #OP << " " << #RHS << " (" << #LHS << " = " \
    122                         << _values.lhs << ", " << #RHS << " = " << _values.rhs << ") "
    123 
    124 // Logs an error and returns false if a condition between x and y does not hold. Extra logging can
    125 // be appended using << after. For example:
    126 //
    127 //   NN_RET_CHECK_EQ(a, b) << "Something went wrong";
    128 //
    129 // The values must implement the appropriate comparison operator as well as
    130 // `operator<<(std::ostream&, ...)`.
    131 // The containing function must return a bool.
    132 #define NN_RET_CHECK_EQ(x, y) NN_RET_CHECK_OP(x, y, ==)
    133 #define NN_RET_CHECK_NE(x, y) NN_RET_CHECK_OP(x, y, !=)
    134 #define NN_RET_CHECK_LE(x, y) NN_RET_CHECK_OP(x, y, <=)
    135 #define NN_RET_CHECK_LT(x, y) NN_RET_CHECK_OP(x, y, <)
    136 #define NN_RET_CHECK_GE(x, y) NN_RET_CHECK_OP(x, y, >=)
    137 #define NN_RET_CHECK_GT(x, y) NN_RET_CHECK_OP(x, y, >)
    138 
    139 // A wrapper around LOG(ERROR) that can be implicitly converted to bool (always evaluates to false).
    140 // Used to implement stream logging in NN_RET_CHECK.
    141 class FalseyErrorStream {
    142     DISALLOW_COPY_AND_ASSIGN(FalseyErrorStream);
    143 
    144    public:
    145     FalseyErrorStream() {}
    146 
    147     template <typename T>
    148     FalseyErrorStream& operator<<(const T& value) {
    149         mBuffer << value;
    150         return *this;
    151     }
    152 
    153     ~FalseyErrorStream() { LOG(ERROR) << mBuffer.str(); }
    154 
    155     operator bool() const { return false; }
    156 
    157    private:
    158     std::ostringstream mBuffer;
    159 };
    160 
    161 // Return a vector with one entry for each non extension OperandType, set to the
    162 // specified PerformanceInfo value.  The vector will be sorted by OperandType.
    163 hidl_vec<Capabilities::OperandPerformance> nonExtensionOperandPerformance(PerformanceInfo perf);
    164 
    165 // Update the vector entry corresponding to the specified OperandType with the
    166 // specified PerformanceInfo value.  The vector must already have an entry for
    167 // that OperandType, and must be sorted by OperandType.
    168 void update(hidl_vec<Capabilities::OperandPerformance>* operandPerformance, OperandType type,
    169             PerformanceInfo perf);
    170 
    171 // Look for a vector entry corresponding to the specified OperandType.  If
    172 // found, return the associated PerformanceInfo.  If not, return a pessimistic
    173 // PerformanceInfo (FLT_MAX).  The vector must be sorted by OperandType.
    174 PerformanceInfo lookup(const hidl_vec<Capabilities::OperandPerformance>& operandPerformance,
    175                        OperandType type);
    176 
    177 // Returns true if an operand type is an extension type.
    178 bool isExtensionOperandType(OperandType type);
    179 
    180 // Returns true if an operation type is an extension type.
    181 bool isExtensionOperationType(OperationType type);
    182 
    183 // Returns the amount of space needed to store a value of the specified
    184 // dimensions and type. For a tensor with unspecified rank or at least one
    185 // unspecified dimension, returns zero.
    186 //
    187 // Aborts if the specified type is an extension type.
    188 //
    189 // See also TypeManager::getSizeOfData(OperandType, const std::vector<uint32_t>&).
    190 uint32_t nonExtensionOperandSizeOfData(OperandType type, const std::vector<uint32_t>& dimensions);
    191 
    192 // Returns the amount of space needed to store a value of the dimensions and
    193 // type of this operand. For a tensor with unspecified rank or at least one
    194 // unspecified dimension, returns zero.
    195 //
    196 // Aborts if the specified type is an extension type.
    197 //
    198 // See also TypeManager::getSizeOfData(const Operand&).
    199 inline uint32_t nonExtensionOperandSizeOfData(const Operand& operand) {
    200     return nonExtensionOperandSizeOfData(operand.type, operand.dimensions);
    201 }
    202 
    203 // Returns true if a non-extension operand type is a scalar type.
    204 //
    205 // Aborts if the specified type is an extension type.
    206 //
    207 // See also TypeManager::isTensorType(OperandType).
    208 bool nonExtensionOperandTypeIsScalar(int type);
    209 
    210 // Returns the name of the operation type in ASCII.
    211 std::string getOperationName(OperationType opCode);
    212 
    213 // Returns the name of the operand type in ASCII.
    214 std::string getOperandTypeName(OperandType type);
    215 
    216 // Whether an operand of tensor type has unspecified dimensions.
    217 //
    218 // Undefined behavior if the operand type is a scalar type.
    219 bool tensorHasUnspecifiedDimensions(int type, const uint32_t* dim, uint32_t dimCount);
    220 bool tensorHasUnspecifiedDimensions(const Operand& operand);
    221 bool tensorHasUnspecifiedDimensions(const ANeuralNetworksOperandType* type);
    222 
    223 // Memory is unmapped.
    224 // Memory is reference counted by hidl_memory instances, and is deallocated
    225 // once there are no more references.
    226 hidl_memory allocateSharedMemory(int64_t size);
    227 
    228 // Returns the number of padding bytes needed to align data of the
    229 // specified length.  It aligns object of length:
    230 // 2, 3 on a 2 byte boundary,
    231 // 4+ on a 4 byte boundary.
    232 // We may want to have different alignments for tensors.
    233 // TODO: This is arbitrary, more a proof of concept.  We need
    234 // to determine what this should be.
    235 uint32_t alignBytesNeeded(uint32_t index, size_t length);
    236 
    237 // Does a detailed LOG(INFO) of the model
    238 void logModelToInfo(const V1_0::Model& model);
    239 void logModelToInfo(const V1_1::Model& model);
    240 void logModelToInfo(const V1_2::Model& model);
    241 
    242 inline std::string toString(uint32_t obj) {
    243     return std::to_string(obj);
    244 }
    245 
    246 template <typename Type>
    247 std::string toString(const std::vector<Type>& range) {
    248     std::string os = "[";
    249     for (size_t i = 0; i < range.size(); ++i) {
    250         os += (i == 0 ? "" : ", ") + toString(range[i]);
    251     }
    252     return os += "]";
    253 }
    254 
    255 inline std::string toString(HalVersion halVersion) {
    256     switch (halVersion) {
    257         case HalVersion::UNKNOWN:
    258             return "UNKNOWN HAL version";
    259         case HalVersion::V1_0:
    260             return "HAL version 1.0";
    261         case HalVersion::V1_1:
    262             return "HAL version 1.1";
    263         case HalVersion::V1_2:
    264             return "HAL version 1.2";
    265     }
    266 }
    267 
    268 inline bool validCode(uint32_t codeCount, uint32_t codeCountOEM, uint32_t code) {
    269     return (code < codeCount) || (code >= kOEMCodeBase && (code - kOEMCodeBase) < codeCountOEM);
    270 }
    271 
    272 bool validateOperandSymmPerChannelQuantParams(
    273         const Operand& halOperand, const ANeuralNetworksSymmPerChannelQuantParams& channelQuant,
    274         const char* tag);
    275 
    276 // Validates an operand type.
    277 //
    278 // extensionOperandTypeInfo must be nullptr iff the type is not an extension type.
    279 //
    280 // If allowPartial is true, the dimensions may be underspecified.
    281 int validateOperandType(const ANeuralNetworksOperandType& type,
    282                         const Extension::OperandTypeInformation* const extensionOperandTypeInfo,
    283                         const char* tag, bool allowPartial);
    284 int validateOperandList(uint32_t count, const uint32_t* list, uint32_t operandCount,
    285                         const char* tag);
    286 
    287 // Returns ANEURALNETWORKS_NO_ERROR if the corresponding operation is defined and can handle the
    288 // provided operand types in the given HAL version, otherwise returns ANEURALNETWORKS_BAD_DATA.
    289 int validateOperation(ANeuralNetworksOperationType opType, uint32_t inputCount,
    290                       const uint32_t* inputIndexes, uint32_t outputCount,
    291                       const uint32_t* outputIndexes, const std::vector<Operand>& operands,
    292                       HalVersion halVersion);
    293 
    294 inline size_t getSizeFromInts(int lower, int higher) {
    295     return (uint32_t)(lower) + ((uint64_t)(uint32_t)(higher) << 32);
    296 }
    297 
    298 // Convert ANEURALNETWORKS_* result code to ErrorStatus.
    299 // Not guaranteed to be a 1-to-1 mapping.
    300 ErrorStatus convertResultCodeToErrorStatus(int resultCode);
    301 
    302 // Convert ErrorStatus to ANEURALNETWORKS_* result code.
    303 // Not guaranteed to be a 1-to-1 mapping.
    304 int convertErrorStatusToResultCode(ErrorStatus status);
    305 
    306 // Versioning
    307 
    308 bool compliantWithV1_0(const V1_0::Capabilities& capabilities);
    309 bool compliantWithV1_0(const V1_1::Capabilities& capabilities);
    310 bool compliantWithV1_0(const V1_2::Capabilities& capabilities);
    311 bool compliantWithV1_1(const V1_0::Capabilities& capabilities);
    312 bool compliantWithV1_1(const V1_1::Capabilities& capabilities);
    313 bool compliantWithV1_1(const V1_2::Capabilities& capabilities);
    314 bool compliantWithV1_2(const V1_0::Capabilities& capabilities);
    315 bool compliantWithV1_2(const V1_1::Capabilities& capabilities);
    316 bool compliantWithV1_2(const V1_2::Capabilities& capabilities);
    317 
    318 bool compliantWithV1_0(const V1_2::Operand& operand);
    319 
    320 // If noncompliantOperations != nullptr, then
    321 //     precondition: noncompliantOperations->empty()
    322 //     postcondition: *noncompliantOperations consists of the indices of the noncompliant
    323 //                    operations; if the compliance check fails for some reason
    324 //                    other than a noncompliant operation,
    325 //                    *noncompliantOperations consists of the indices of all operations
    326 bool compliantWithV1_0(const V1_0::Model& model);
    327 bool compliantWithV1_0(const V1_1::Model& model);
    328 bool compliantWithV1_0(const V1_2::Model& model,
    329                        std::set<uint32_t>* noncompliantOperations = nullptr);
    330 bool compliantWithV1_1(const V1_0::Model& model);
    331 bool compliantWithV1_1(const V1_1::Model& model);
    332 bool compliantWithV1_1(const V1_2::Model& model,
    333                        std::set<uint32_t>* noncompliantOperations = nullptr);
    334 
    335 V1_0::Capabilities convertToV1_0(const V1_0::Capabilities& capabilities);
    336 V1_0::Capabilities convertToV1_0(const V1_1::Capabilities& capabilities);
    337 V1_0::Capabilities convertToV1_0(const V1_2::Capabilities& capabilities);
    338 V1_1::Capabilities convertToV1_1(const V1_0::Capabilities& capabilities);
    339 V1_1::Capabilities convertToV1_1(const V1_1::Capabilities& capabilities);
    340 V1_1::Capabilities convertToV1_1(const V1_2::Capabilities& capabilities);
    341 V1_2::Capabilities convertToV1_2(const V1_0::Capabilities& capabilities);
    342 V1_2::Capabilities convertToV1_2(const V1_1::Capabilities& capabilities);
    343 V1_2::Capabilities convertToV1_2(const V1_2::Capabilities& capabilities);
    344 
    345 V1_0::Model convertToV1_0(const V1_0::Model& model);
    346 V1_0::Model convertToV1_0(const V1_1::Model& model);
    347 V1_0::Model convertToV1_0(const V1_2::Model& model);
    348 V1_1::Model convertToV1_1(const V1_0::Model& model);
    349 V1_1::Model convertToV1_1(const V1_1::Model& model);
    350 V1_1::Model convertToV1_1(const V1_2::Model& model);
    351 V1_2::Model convertToV1_2(const V1_0::Model& model);
    352 V1_2::Model convertToV1_2(const V1_1::Model& model);
    353 V1_2::Model convertToV1_2(const V1_2::Model& model);
    354 
    355 // The IModelSlicer abstract class provides methods to create from an original
    356 // model a "slice" of that model consisting of the subset of operations that is
    357 // compliant with a particular HAL version, and a mechanism for mapping
    358 // operations from the slice back to operations of the original model.  The
    359 // slice is intended to be passed to getSupportedOperations*(), with the mapping
    360 // used to translate the results of that call from the slice's operations to the
    361 // original model's operations.  The slice has no other purpose (for example, it
    362 // is not guaranteed to have the same topology as a subgraph of the original
    363 // model).
    364 //
    365 // Note that the original model is not part of the ModelSlicer specification --
    366 // an instance of a class derived from ModelSlicer is responsible for knowing
    367 // the original model.  getSlice*() methods may be called multiple times on a
    368 // given instance; the intention is that the instance cache slices internally.
    369 //
    370 // The meaning of the return value of the getSlice*() methods is explained by
    371 // the following example:
    372 //
    373 //     IModelSlicer* slicer = ...;
    374 //     auto ret = slicer->getSliceV1_0();  // getSliceV1_1() is similar
    375 //     if (ret.has_value()) {
    376 //         const V1_0::Model model = ret->first;  // the slice
    377 //         auto mapper = ret->second;
    378 //         // mapper is a functor that takes an operation index in the
    379 //         // slice and returns the corresponding operation index in the
    380 //         // original model.  The functor must remain valid for the lifetime
    381 //         // of *slicer.
    382 //     } else {
    383 //         // Could not obtain a slice.  For example, perhaps none of the
    384 //         // original model's operations are compliant with V1_0.
    385 //     }
    386 //
    387 class IModelSlicer {
    388    public:
    389     virtual std::optional<std::pair<V1_0::Model, std::function<uint32_t(uint32_t)>>>
    390     getSliceV1_0() = 0;
    391     virtual std::optional<std::pair<V1_1::Model, std::function<uint32_t(uint32_t)>>>
    392     getSliceV1_1() = 0;
    393 
    394     virtual ~IModelSlicer() = default;
    395 };
    396 
    397 V1_0::OperationType uncheckedConvertToV1_0(V1_2::OperationType type);
    398 V1_1::OperationType uncheckedConvertToV1_1(V1_2::OperationType type);
    399 
    400 V1_0::Operand convertToV1_0(const V1_2::Operand& operand);
    401 
    402 V1_2::Operand convertToV1_2(const V1_0::Operand& operand);
    403 V1_2::Operand convertToV1_2(const V1_2::Operand& operand);
    404 
    405 hidl_vec<V1_2::Operand> convertToV1_2(const hidl_vec<V1_0::Operand>& operands);
    406 hidl_vec<V1_2::Operand> convertToV1_2(const hidl_vec<V1_2::Operand>& operands);
    407 
    408 #ifdef NN_DEBUGGABLE
    409 uint32_t getProp(const char* str, uint32_t defaultValue = 0);
    410 #endif  // NN_DEBUGGABLE
    411 
    412 }  // namespace nn
    413 }  // namespace android
    414 
    415 #endif  // ANDROID_ML_NN_COMMON_UTILS_H
    416