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:
FalseyErrorStream()145 FalseyErrorStream() {}
146
147 template <typename T>
148 FalseyErrorStream& operator<<(const T& value) {
149 mBuffer << value;
150 return *this;
151 }
152
~FalseyErrorStream()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&).
nonExtensionOperandSizeOfData(const Operand & 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
toString(uint32_t obj)242 inline std::string toString(uint32_t obj) {
243 return std::to_string(obj);
244 }
245
246 template <typename Type>
toString(const std::vector<Type> & range)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
toString(HalVersion halVersion)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
validCode(uint32_t codeCount,uint32_t codeCountOEM,uint32_t code)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
getSizeFromInts(int lower,int higher)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