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 #define LOG_TAG "CompilationBuilder"
18 
19 #include "CompilationBuilder.h"
20 
21 #include <LegacyUtils.h>
22 #include <nnapi/IBurst.h>
23 #include <nnapi/SharedMemory.h>
24 #include <nnapi/Types.h>
25 
26 #include <algorithm>
27 #include <limits>
28 #include <memory>
29 #include <string>
30 #include <utility>
31 #include <vector>
32 
33 #include "BurstBuilder.h"
34 #include "ExecutionBuilder.h"
35 #include "ExecutionPlan.h"
36 #include "Manager.h"
37 #include "ModelBuilder.h"
38 #include "TypeManager.h"
39 
40 namespace android {
41 namespace nn {
42 
CompilationBuilder(const ModelBuilder * model,const std::vector<std::shared_ptr<Device>> & devices,bool explicitDeviceList)43 CompilationBuilder::CompilationBuilder(const ModelBuilder* model,
44                                        const std::vector<std::shared_ptr<Device>>& devices,
45                                        bool explicitDeviceList)
46     : mModel(model),
47       mPartitioning(explicitDeviceList ? DeviceManager::kPartitioningWithoutFallback
48                                        : DeviceManager::get()->getPartitioning()),
49       mDevices(devices),
50       mExplicitDeviceList(explicitDeviceList) {
51     VLOG(COMPILATION) << "CompilationBuilder::CompilationBuilder";
52 }
53 
finish()54 int CompilationBuilder::finish() {
55     if (mFinished) {
56         LOG(ERROR) << "ANeuralNetworksCompilation_finish called more than once";
57         return ANEURALNETWORKS_BAD_STATE;
58     }
59     // TODO validate the rest
60 
61     // Init telemetry info, start measuring compilation time
62     mTelemetryInfo = TelemetryInfo{};
63     const auto scopedTimeNanoMeasurer = TimeNanoMeasurer(&mTelemetryInfo->compilationTimeNanos);
64 
65     const auto deadline = makeDeadline(mTimeoutDuration);
66 
67     mFinished = true;
68     if (mIsCacheInfoProvided) {
69         mPlan.setCaching(&mCacheInfo, mToken);
70     }
71     if (mPartitioning) {
72         int n = mModel->partitionTheWork(mDevices, mPreference, mPriority, deadline, &mPlan,
73                                          mMetadata, mFailPartitioning);
74         switch (n) {
75             case ANEURALNETWORKS_NO_ERROR:
76                 return n;
77             case ANEURALNETWORKS_UNEXPECTED_NULL:
78             case ANEURALNETWORKS_BAD_DATA:
79                 // The two error codes above should only be used for errors in the user's
80                 // request. In case of a user error, we won't try any fallback.
81                 // TODO: Document this in NeuralNetworks.h and in the HAL. Make sure
82                 // driver writers know which code they can return.
83                 return n;
84             default:
85                 // The error might be recoverable. Return the error only if falling back
86                 // is not allowed.
87                 if (!DeviceManager::partitioningAllowsFallback(mPartitioning)) {
88                     return n;
89                 }
90                 if (mModel->hasOEMOperation()) {
91                     LOG(ERROR) << "Cannot fall back to CPU because of an OEM operation";
92                     return n;
93                 }
94                 if (mModel->hasExtensionOperation()) {
95                     LOG(ERROR) << "Cannot fall back to CPU because of an extension operation";
96                     return n;
97                 }
98                 break;
99         }
100     }
101 
102     // Fallback to CPU
103     mTelemetryInfo->fallbackToCpuFromError = true;
104     VLOG(COMPILATION) << "CompilationBuilder::finish with CPU fallback";
105     mPlan.reset();
106     mPlan.becomeSingleStep(DeviceManager::getCpuDevice(), mModel);
107     return mPlan.finish(mPreference, mPriority, deadline, mMetadata, ANEURALNETWORKS_NO_ERROR);
108 }
109 
setPreference(int32_t preference)110 int CompilationBuilder::setPreference(int32_t preference) {
111     if (mFinished) {
112         LOG(ERROR) << "ANeuralNetworksCompilation_setPreference can't modify after compilation "
113                       "finished";
114         return ANEURALNETWORKS_BAD_STATE;
115     }
116     if (preference >= kNumberOfPreferences) {
117         LOG(ERROR) << "ANeuralNetworksCompilation_setPreference invalid preference " << preference;
118         return ANEURALNETWORKS_BAD_DATA;
119     }
120 
121     mPreference = preference;
122     return ANEURALNETWORKS_NO_ERROR;
123 }
124 
setCaching(const std::string & cacheDir,const uint8_t * token)125 int CompilationBuilder::setCaching(const std::string& cacheDir, const uint8_t* token) {
126     if (mFinished) {
127         LOG(ERROR)
128                 << "ANeuralNetworksCompilation_setCaching can't modify after compilation finished";
129         return ANEURALNETWORKS_BAD_STATE;
130     }
131     std::string path = cacheDir;
132     // Make sure the cache dir can concat with the filename.
133     if (!path.empty() && path.back() != '/') {
134         path.push_back('/');
135     }
136     mCacheInfo.variant = std::move(path);
137     std::copy(token, token + ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, mToken);
138     mIsCacheInfoProvided = true;
139     return ANEURALNETWORKS_NO_ERROR;
140 }
141 
createCacheHandle(int fd)142 static GeneralResult<SharedHandle> createCacheHandle(int fd) {
143     base::unique_fd duplicatedFd = NN_TRY(dupFd(fd));
144     return std::make_shared<const Handle>(std::move(duplicatedFd));
145 }
146 
createCacheHandleVec(const int * fds,uint32_t numFds)147 static GeneralResult<std::vector<SharedHandle>> createCacheHandleVec(const int* fds,
148                                                                      uint32_t numFds) {
149     std::vector<SharedHandle> handles;
150     handles.reserve(numFds);
151     for (uint32_t i = 0; i < numFds; i++) {
152         handles.push_back(NN_TRY(createCacheHandle(fds[i])));
153     }
154     return handles;
155 }
156 
setCachingFromFds(const int * modelCacheFds,const uint32_t numModelCacheFiles,const int * dataCacheFds,const uint32_t numDataCacheFiles,const uint8_t * token)157 int CompilationBuilder::setCachingFromFds(const int* modelCacheFds,
158                                           const uint32_t numModelCacheFiles,
159                                           const int* dataCacheFds, const uint32_t numDataCacheFiles,
160                                           const uint8_t* token) {
161     if (mFinished) {
162         LOG(ERROR) << "SL_ANeuralNetworksCompilation_setCachingFromFds can't modify after "
163                       "compilation finished";
164         return ANEURALNETWORKS_BAD_STATE;
165     }
166     auto modelCache = createCacheHandleVec(modelCacheFds, numModelCacheFiles);
167     if (!modelCache.has_value()) {
168         LOG(ERROR) << "SL_ANeuralNetworksCompilation_setCachingFromFds can't duplicate model cache "
169                       "fds: "
170                    << modelCache.error().message;
171         return ANEURALNETWORKS_BAD_DATA;
172     }
173     auto dataCache = createCacheHandleVec(dataCacheFds, numDataCacheFiles);
174     if (!dataCache.has_value()) {
175         LOG(ERROR) << "SL_ANeuralNetworksCompilation_setCachingFromFds can't duplicate data cache "
176                       "fds: "
177                    << dataCache.error().message;
178         return ANEURALNETWORKS_BAD_DATA;
179     }
180     mCacheInfo.variant = CacheHandles{
181             .modelCache = std::move(modelCache).value(),
182             .dataCache = std::move(dataCache).value(),
183     };
184     std::copy(token, token + ANEURALNETWORKS_BYTE_SIZE_OF_CACHE_TOKEN, mToken);
185     mIsCacheInfoProvided = true;
186     return ANEURALNETWORKS_NO_ERROR;
187 }
188 
setPriority(int32_t priority)189 int CompilationBuilder::setPriority(int32_t priority) {
190     if (mFinished) {
191         LOG(ERROR) << "ANeuralNetworksCompilation_setPriority can't modify after compilation "
192                       "finished";
193         return ANEURALNETWORKS_BAD_STATE;
194     }
195     if (priority != ANEURALNETWORKS_PRIORITY_LOW && priority != ANEURALNETWORKS_PRIORITY_MEDIUM &&
196         priority != ANEURALNETWORKS_PRIORITY_HIGH) {
197         LOG(ERROR) << "ANeuralNetworksCompilation_setPriority invalid priority " << priority;
198         return ANEURALNETWORKS_BAD_DATA;
199     }
200 
201     mPriority = priority;
202     return ANEURALNETWORKS_NO_ERROR;
203 }
204 
setTimeoutDuration(uint64_t duration)205 int CompilationBuilder::setTimeoutDuration(uint64_t duration) {
206     if (mFinished) {
207         LOG(ERROR) << "ANeuralNetworksCompilation_setTimeout can't modify after compilation "
208                       "finished";
209         return ANEURALNETWORKS_BAD_STATE;
210     }
211     if (!mExplicitDeviceList || (mDevices.size() != 1)) {
212         LOG(ERROR) << "ANeuralNetworksCompilation_setTimeout called on an "
213                       "ANeuralNetworksCompilation that was not created by "
214                       "ANeuralNetworksCompilation_createForDevices with numDevices = 1";
215         return ANEURALNETWORKS_BAD_DATA;
216     }
217     if (duration > 0) {
218         mTimeoutDuration = duration;
219     } else {
220         mTimeoutDuration.reset();
221     }
222     return ANEURALNETWORKS_NO_ERROR;
223 }
224 
addExtensionAttribute(const char * extensionName,uint16_t attributeCodeWithinExtension,const void * data,size_t length)225 int CompilationBuilder::addExtensionAttribute(const char* extensionName,
226                                               uint16_t attributeCodeWithinExtension,
227                                               const void* data, size_t length) {
228     if (mFinished) {
229         LOG(ERROR) << "ANeuralNetworksCompilation_addExtensionAttribute can't modify after "
230                       "compilation finished";
231         return ANEURALNETWORKS_BAD_STATE;
232     }
233     if (!mExplicitDeviceList || (mDevices.size() != 1)) {
234         LOG(ERROR) << "ANeuralNetworksCompilation_addExtensionAttribute called on an "
235                       "ANeuralNetworksCompilation that was not created by "
236                       "ANeuralNetworksCompilation_createForDevices with numDevices = 1";
237         return ANEURALNETWORKS_BAD_DATA;
238     }
239     int32_t attributeToken = 0;
240     if (!TypeManager::get()->getExtensionType(extensionName, attributeCodeWithinExtension,
241                                               &attributeToken)) {
242         return ANEURALNETWORKS_BAD_DATA;
243     }
244     if (std::find_if(mMetadata.begin(), mMetadata.end(), [attributeToken](const auto& entry) {
245             return attributeToken == entry.token;
246         }) != mMetadata.end()) {
247         LOG(ERROR) << "ANeuralNetworksCompilation_addExtensionAttribute called more than once for "
248                       "the same attribute";
249         return ANEURALNETWORKS_BAD_DATA;
250     }
251     const uint8_t* dataPtr = reinterpret_cast<const uint8_t*>(data);
252     mMetadata.push_back({attributeToken, std::vector<uint8_t>(dataPtr, dataPtr + length)});
253     return ANEURALNETWORKS_NO_ERROR;
254 }
255 
forTest_setPartitioning(uint32_t partitioning)256 int CompilationBuilder::forTest_setPartitioning(uint32_t partitioning) {
257     if (mFinished) {
258         LOG(ERROR) << "CompilationBuilder::forTest_setPartitioning can't modify after compilation "
259                       "finished";
260         return ANEURALNETWORKS_BAD_STATE;
261     }
262 
263     mPartitioning = partitioning;
264     return ANEURALNETWORKS_NO_ERROR;
265 }
266 
forTest_failPartitioning(int fail)267 int CompilationBuilder::forTest_failPartitioning(int fail) {
268     if (mFinished) {
269         LOG(ERROR) << "CompilationBuilder::forTest_failPartitioning can't modify after compilation "
270                       "finished";
271         return ANEURALNETWORKS_BAD_STATE;
272     }
273 
274     mFailPartitioning = fail;
275     return ANEURALNETWORKS_NO_ERROR;
276 }
277 
getPreferredMemoryAlignmentForInput(uint32_t index,uint32_t * alignment) const278 int CompilationBuilder::getPreferredMemoryAlignmentForInput(uint32_t index,
279                                                             uint32_t* alignment) const {
280     CHECK(alignment != nullptr);
281     if (!mFinished) {
282         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput passed an "
283                       "unfinished compilation";
284         return ANEURALNETWORKS_BAD_STATE;
285     }
286     if (!mPlan.isValid()) {
287         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput passed an "
288                       "invalid compilation";
289         return ANEURALNETWORKS_BAD_STATE;
290     }
291     if (index >= mModel->inputCount()) {
292         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForInput passed an "
293                       "invalid input index "
294                    << index;
295         return ANEURALNETWORKS_BAD_DATA;
296     }
297     *alignment = mPlan.getMemoryPreference(IOType::INPUT, index).alignment;
298     return ANEURALNETWORKS_NO_ERROR;
299 }
300 
getPreferredMemoryPaddingForInput(uint32_t index,uint32_t * padding) const301 int CompilationBuilder::getPreferredMemoryPaddingForInput(uint32_t index, uint32_t* padding) const {
302     CHECK(padding != nullptr);
303     if (!mFinished) {
304         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput passed an "
305                       "unfinished compilation";
306         return ANEURALNETWORKS_BAD_STATE;
307     }
308     if (!mPlan.isValid()) {
309         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput passed an "
310                       "invalid compilation";
311         return ANEURALNETWORKS_BAD_STATE;
312     }
313     if (index >= mModel->inputCount()) {
314         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForInput passed an "
315                       "invalid input index "
316                    << index;
317         return ANEURALNETWORKS_BAD_DATA;
318     }
319     *padding = mPlan.getMemoryPreference(IOType::INPUT, index).padding;
320     return ANEURALNETWORKS_NO_ERROR;
321 }
322 
getPreferredMemoryAlignmentForOutput(uint32_t index,uint32_t * alignment) const323 int CompilationBuilder::getPreferredMemoryAlignmentForOutput(uint32_t index,
324                                                              uint32_t* alignment) const {
325     CHECK(alignment != nullptr);
326     if (!mFinished) {
327         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput passed an "
328                       "unfinished compilation";
329         return ANEURALNETWORKS_BAD_STATE;
330     }
331     if (!mPlan.isValid()) {
332         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput passed an "
333                       "invalid compilation";
334         return ANEURALNETWORKS_BAD_STATE;
335     }
336     if (index >= mModel->outputCount()) {
337         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryAlignmentForOutput passed an "
338                       "invalid output index "
339                    << index;
340         return ANEURALNETWORKS_BAD_DATA;
341     }
342     *alignment = mPlan.getMemoryPreference(IOType::OUTPUT, index).alignment;
343     return ANEURALNETWORKS_NO_ERROR;
344 }
345 
getPreferredMemoryPaddingForOutput(uint32_t index,uint32_t * padding) const346 int CompilationBuilder::getPreferredMemoryPaddingForOutput(uint32_t index,
347                                                            uint32_t* padding) const {
348     CHECK(padding != nullptr);
349     if (!mFinished) {
350         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput passed an "
351                       "unfinished compilation";
352         return ANEURALNETWORKS_BAD_STATE;
353     }
354     if (!mPlan.isValid()) {
355         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput passed an "
356                       "invalid compilation";
357         return ANEURALNETWORKS_BAD_STATE;
358     }
359     if (index >= mModel->outputCount()) {
360         LOG(ERROR) << "ANeuralNetworksCompilation_getPreferredMemoryPaddingForOutput passed an "
361                       "invalid output index "
362                    << index;
363         return ANEURALNETWORKS_BAD_DATA;
364     }
365     *padding = mPlan.getMemoryPreference(IOType::OUTPUT, index).padding;
366     return ANEURALNETWORKS_NO_ERROR;
367 }
368 
createExecution(ExecutionBuilder ** execution)369 int CompilationBuilder::createExecution(ExecutionBuilder** execution) {
370     if (!mFinished) {
371         LOG(ERROR) << "ANeuralNetworksExecution_create passed an unfinished compilation";
372         *execution = nullptr;
373         return ANEURALNETWORKS_BAD_STATE;
374     }
375     if (!mPlan.isValid()) {
376         LOG(ERROR) << "ANeuralNetworksExecution_create passed an invalid compilation";
377         *execution = nullptr;
378         return ANEURALNETWORKS_BAD_STATE;
379     }
380     if (mPlan.isSimple()) {
381         *execution = new (std::nothrow) SimpleExecutionBuilder(this);
382     } else {
383         *execution = new (std::nothrow) CompoundExecutionBuilder(this);
384     }
385     return (*execution ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
386 }
387 
createBurst(BurstBuilder ** burst)388 int CompilationBuilder::createBurst(BurstBuilder** burst) {
389     if (!mFinished) {
390         LOG(ERROR) << "ANeuralNetworksBurst_create passed an unfinished compilation";
391         *burst = nullptr;
392         return ANEURALNETWORKS_BAD_STATE;
393     }
394     if (!mPlan.isValid()) {
395         LOG(ERROR) << "ANeuralNetworksBurst_create passed an invalid compilation";
396         *burst = nullptr;
397         return ANEURALNETWORKS_BAD_STATE;
398     }
399     std::vector<SharedBurst> burstControllers = mPlan.makeBursts();
400     *burst = new (std::nothrow) BurstBuilder(this, std::move(burstControllers));
401     return (*burst ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
402 }
403 
forEachStepRoleOfInput(uint32_t index,const StepRoleCallback & callback) const404 int CompilationBuilder::forEachStepRoleOfInput(uint32_t index,
405                                                const StepRoleCallback& callback) const {
406     if (!mFinished) {
407         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an unfinished compilation";
408         return ANEURALNETWORKS_BAD_STATE;
409     }
410     if (!mPlan.isValid()) {
411         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an invalid compilation";
412         return ANEURALNETWORKS_BAD_STATE;
413     }
414     if (index >= mModel->inputCount()) {
415         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addInputRole passed an invalid input index "
416                    << index;
417         return ANEURALNETWORKS_BAD_DATA;
418     }
419     mPlan.forEachStepRoleOfInput(index, callback);
420     return ANEURALNETWORKS_NO_ERROR;
421 }
422 
forEachStepRoleOfOutput(uint32_t index,const StepRoleCallback & callback) const423 int CompilationBuilder::forEachStepRoleOfOutput(uint32_t index,
424                                                 const StepRoleCallback& callback) const {
425     if (!mFinished) {
426         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an unfinished compilation";
427         return ANEURALNETWORKS_BAD_STATE;
428     }
429     if (!mPlan.isValid()) {
430         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an invalid compilation";
431         return ANEURALNETWORKS_BAD_STATE;
432     }
433     if (index >= mModel->outputCount()) {
434         LOG(ERROR) << "ANeuralNetworksMemoryDesc_addOutputRole passed an invalid output index "
435                    << index;
436         return ANEURALNETWORKS_BAD_DATA;
437     }
438     mPlan.forEachStepRoleOfOutput(index, callback);
439     return ANEURALNETWORKS_NO_ERROR;
440 }
441 
442 }  // namespace nn
443 }  // namespace android
444