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