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 "ModelBuilder"
18 
19 #include "ModelBuilder.h"
20 
21 #include "CompilationBuilder.h"
22 #include "Utils.h"
23 #include "ValidateHal.h"
24 
25 #include <map>
26 #include <utility>
27 
28 namespace android {
29 namespace nn {
30 
31 // The maximum number of operands and operations that a model may have.
32 const uint32_t MAX_NUMBER_OF_OPERANDS = 0xFFFFFFFE;
33 const uint32_t MAX_NUMBER_OF_OPERATIONS = 0xFFFFFFFE;
34 
badState(const char * name)35 bool ModelBuilder::badState(const char* name) {
36     if (mCompletedModel) {
37         LOG(ERROR) << "ANeuralNetworksModel_" << name << " can't modify after model finished";
38         return true;
39     }
40     if (mInvalidModel) {
41         LOG(ERROR) << "ANeuralNetworksModel_" << name << " can't modify an invalid model";
42         return true;
43     }
44     return false;
45 }
46 
addOperand(const ANeuralNetworksOperandType & type)47 int ModelBuilder::addOperand(const ANeuralNetworksOperandType& type) {
48     if (badState("addOperand")) {
49         return ANEURALNETWORKS_BAD_STATE;
50     }
51 
52     int n = validateOperandType(type, "ANeuralNetworksModel_addOperand", true);
53     if (n != ANEURALNETWORKS_NO_ERROR) {
54         return n;
55     }
56     size_t idx = mOperands.size();
57     if (idx >= MAX_NUMBER_OF_OPERANDS) {
58         LOG(ERROR) << "ANeuralNetworksModel_addOperand exceed max operands";
59         return ANEURALNETWORKS_BAD_DATA;
60     }
61     mOperands.push_back({
62         .type = static_cast<OperandType>(type.type),
63         .dimensions = hidl_vec<uint32_t>(type.dimensions, type.dimensions + type.dimensionCount),
64         .numberOfConsumers = 0,
65         .scale = type.scale,
66         .zeroPoint = type.zeroPoint,
67         .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
68         .location = {.poolIndex = 0, .offset = 0, .length = 0},
69     });
70     return ANEURALNETWORKS_NO_ERROR;
71 }
72 
setOperandValue(uint32_t index,const void * buffer,size_t length)73 int ModelBuilder::setOperandValue(uint32_t index, const void* buffer, size_t length) {
74     VLOG(MODEL) << __func__ << " for operand " << index << " size " << length;
75     if (badState("setOperandValue")) {
76         return ANEURALNETWORKS_BAD_STATE;
77     }
78 
79     if (index >= operandCount()) {
80         LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting operand " << index << " of "
81                    << operandCount();
82         return ANEURALNETWORKS_BAD_DATA;
83     }
84     Operand& operand = mOperands[index];
85     if (buffer == nullptr) {
86         if (length) {
87             LOG(ERROR) << "ANeuralNetworksModel_setOperandValue buffer is nullptr but length is "
88                           "not 0";
89             return ANEURALNETWORKS_BAD_DATA;
90         }
91         operand.lifetime = OperandLifeTime::NO_VALUE;
92         // The location is unused and is set to zeros.
93         operand.location = {.poolIndex = 0,
94                             .offset = 0,
95                             .length = 0};
96     } else {
97         if (length > 0xFFFFFFFF) {
98             LOG(ERROR) << "ANeuralNetworksModel_setOperandValue value length of " << length
99                        << " exceeds max size";
100             return ANEURALNETWORKS_BAD_DATA;
101         }
102         uint32_t valueLength = static_cast<uint32_t>(length);
103         uint32_t neededLength = sizeOfData(operand.type, operand.dimensions);
104         if (operand.type != OperandType::OEM && neededLength != valueLength) {
105             LOG(ERROR) << "ANeuralNetworksModel_setOperandValue setting " << valueLength
106                        << " bytes when needing " << neededLength;
107             return ANEURALNETWORKS_BAD_DATA;
108         }
109         if (valueLength <= ANEURALNETWORKS_MAX_SIZE_OF_IMMEDIATELY_COPIED_VALUES) {
110             uint32_t existingSize = static_cast<uint32_t>(mSmallOperandValues.size());
111             uint32_t extraBytes = alignBytesNeeded(existingSize, valueLength);
112             mSmallOperandValues.resize(existingSize + extraBytes + valueLength);
113             operand.lifetime = OperandLifeTime::CONSTANT_COPY;
114             operand.location = {
115                 .poolIndex = 0, .offset = existingSize + extraBytes, .length = valueLength};
116             memcpy(&mSmallOperandValues[operand.location.offset], buffer, valueLength);
117             VLOG(MODEL) << "Copied small value to offset " << operand.location.offset;
118         } else {
119             VLOG(MODEL) << "Saving large value";
120             operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE;
121             // The values for poolIndex and offset will be set when the model is finished.
122             typedef decltype(operand.location.poolIndex) PoolIndexType;
123             typedef decltype(operand.location.offset) OffsetType;
124             operand.location = {.poolIndex = ~PoolIndexType(0), .offset = ~OffsetType(0),
125                                 .length = valueLength};
126             // We keep track of the buffers. We'll allocate the shared memory only
127             // once we know the total size, to avoid needless copies.
128             mLargeOperandValues.push_back(LargeValue{.operandIndex = index, .buffer = buffer});
129         }
130     }
131     return ANEURALNETWORKS_NO_ERROR;
132 }
133 
copyLargeValuesToSharedMemory()134 int ModelBuilder::copyLargeValuesToSharedMemory() {
135     VLOG(MODEL) << __func__ << " has " << mLargeOperandValues.size() << " values.";
136     if (!mLargeOperandValues.empty()) {
137         // Calculate the size of the shared memory needed for all the large values.
138         // Also sets the offset for each value within the memory.
139         size_t poolSize = 0;
140         for (LargeValue& l: mLargeOperandValues) {
141             Operand& operand = mOperands[l.operandIndex];
142             nnAssert(operand.lifetime == OperandLifeTime::CONSTANT_REFERENCE);
143             poolSize += alignBytesNeeded(poolSize, operand.location.length);
144             operand.location.offset = poolSize;
145             poolSize += operand.location.length;
146         }
147 
148         // Allocated the shared memory.
149         int n = mLargeValueMemory.create(poolSize);
150         if (n != ANEURALNETWORKS_NO_ERROR) {
151             return n;
152         }
153         uint8_t* memoryPointer = nullptr;
154         n = mLargeValueMemory.getPointer(&memoryPointer);
155         if (n != ANEURALNETWORKS_NO_ERROR) {
156             return n;
157         }
158         uint32_t poolIndex = mMemories.add(&mLargeValueMemory);
159         VLOG(MODEL) << "Allocated large value pool of size " << poolSize << " at index "
160                     << poolIndex;
161 
162         // Copy the values to this memory.
163         for (LargeValue& l: mLargeOperandValues) {
164             Operand& operand = mOperands[l.operandIndex];
165             operand.location.poolIndex = poolIndex;
166             memcpy(memoryPointer + operand.location.offset, l.buffer, operand.location.length);
167         }
168     }
169     return ANEURALNETWORKS_NO_ERROR;
170 }
171 
setOperandValueFromMemory(uint32_t index,const Memory * memory,uint32_t offset,size_t length)172 int ModelBuilder::setOperandValueFromMemory(uint32_t index, const Memory* memory, uint32_t offset,
173                                             size_t length) {
174     VLOG(MODEL) << __func__ << " for operand " << index << " offset " << offset << " size " << length;
175     if (badState("setOperandValueFromMemory")) {
176         return ANEURALNETWORKS_BAD_STATE;
177     }
178 
179     if (index >= operandCount()) {
180         LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting operand " << index
181                    << " of " << operandCount();
182         return ANEURALNETWORKS_BAD_DATA;
183     }
184     Operand& operand = mOperands[index];
185     uint32_t neededLength = sizeOfData(operand.type, operand.dimensions);
186     if (neededLength != length) {
187         LOG(ERROR) << "ANeuralNetworksModel_setOperandValueFromMemory setting " << length
188                    << " bytes when needing " << neededLength;
189         return ANEURALNETWORKS_BAD_DATA;
190     }
191     if (!memory->validateSize(offset, length)) {
192         return ANEURALNETWORKS_BAD_DATA;
193     }
194     operand.lifetime = OperandLifeTime::CONSTANT_REFERENCE;
195     operand.location = {
196                 .poolIndex = mMemories.add(memory), .offset = offset, .length = neededLength};
197     return ANEURALNETWORKS_NO_ERROR;
198 }
199 
addOperation(ANeuralNetworksOperationType type,uint32_t inputCount,const uint32_t * inputs,uint32_t outputCount,const uint32_t * outputs)200 int ModelBuilder::addOperation(ANeuralNetworksOperationType type, uint32_t inputCount,
201                                const uint32_t* inputs, uint32_t outputCount,
202                                const uint32_t* outputs) {
203     if (badState("addOperation")) {
204         return ANEURALNETWORKS_BAD_STATE;
205     }
206 
207     if (!validCode(kNumberOfOperationTypes, kNumberOfOperationTypesOEM, type)) {
208         LOG(ERROR) << "ANeuralNetworksModel_addOperation invalid operations type " << type;
209         return ANEURALNETWORKS_BAD_DATA;
210     }
211     int n = validateOperation(type, inputCount, inputs,
212                               outputCount, outputs, mOperands);
213     if (n != ANEURALNETWORKS_NO_ERROR) {
214         return n;
215     }
216 
217     uint32_t operationIndex = operationCount();
218     if (operationIndex >= MAX_NUMBER_OF_OPERATIONS) {
219         LOG(ERROR) << "ANeuralNetworksModel_addOperation exceed max operations";
220         return ANEURALNETWORKS_BAD_DATA;
221     }
222 
223     mOperations.push_back({
224         .type = static_cast<OperationType>(type),
225         .inputs = hidl_vec<uint32_t>(inputs, inputs + inputCount),
226         .outputs = hidl_vec<uint32_t>(outputs, outputs + outputCount),
227     });
228     for (uint32_t i : mOperations.back().inputs) {
229         mOperands[i].numberOfConsumers++;
230     }
231 
232     return ANEURALNETWORKS_NO_ERROR;
233 }
234 
identifyInputsAndOutputs(uint32_t inputCount,const uint32_t * inputs,uint32_t outputCount,const uint32_t * outputs)235 int ModelBuilder::identifyInputsAndOutputs(uint32_t inputCount, const uint32_t* inputs,
236                                       uint32_t outputCount, const uint32_t* outputs) {
237     if (badState("identifyInputsAndOutputs")) {
238         return ANEURALNETWORKS_BAD_STATE;
239     }
240 
241     int n = validateOperandList(inputCount, inputs, operandCount(),
242                                 "ANeuralNetworksModel_identifyInputsAndOutputs inputs");
243     if (n != ANEURALNETWORKS_NO_ERROR) {
244         return n;
245     }
246     n = validateOperandList(outputCount, outputs, operandCount(),
247                             "ANeuralNetworksModel_identifyInputsAndOutputs outputs");
248     if (n != ANEURALNETWORKS_NO_ERROR) {
249         return n;
250     }
251 
252     // Makes a copy of the index list, validates the arguments, and changes
253     // the lifetime info of the corresponding operand.
254     auto setArguments = [&](std::vector<uint32_t>* indexVector, uint32_t indexCount,
255                             const uint32_t* indexList, OperandLifeTime lifetime) -> bool {
256         indexVector->resize(indexCount);
257         for (uint32_t i = 0; i < indexCount; i++) {
258             const uint32_t operandIndex = indexList[i];
259             if (operandIndex >= mOperands.size()) {
260                 LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set input or output "
261                               "to be "
262                            << operandIndex << " as this exceeds the numbe of operands "
263                            << mOperands.size();
264                 return false;
265             }
266             (*indexVector)[i] = operandIndex;
267             Operand& operand = mOperands[operandIndex];
268             if (operand.lifetime != OperandLifeTime::TEMPORARY_VARIABLE) {
269                 LOG(ERROR) << "ANeuralNetworksModel_identifyInputsAndOutputs Can't set operand "
270                            << operandIndex
271                            << " to be an input or output.  Check that it's not a constant or "
272                               "already an input or output";
273                 return false;
274             }
275             operand.lifetime = lifetime;
276         }
277         return true;
278     };
279 
280     if (!setArguments(&mInputIndexes, inputCount, inputs, OperandLifeTime::MODEL_INPUT) ||
281         !setArguments(&mOutputIndexes, outputCount, outputs, OperandLifeTime::MODEL_OUTPUT)) {
282         return ANEURALNETWORKS_BAD_DATA;
283     }
284 
285     return ANEURALNETWORKS_NO_ERROR;
286 }
287 
relaxComputationFloat32toFloat16(bool allow)288 int ModelBuilder::relaxComputationFloat32toFloat16(bool allow) {
289     if (badState("relaxComputationFloat32toFloat16")) {
290         return ANEURALNETWORKS_BAD_STATE;
291     }
292 
293     mRelaxComputationFloat32toFloat16 = allow;
294 
295     return ANEURALNETWORKS_NO_ERROR;
296 }
297 
createCompilation(CompilationBuilder ** compilation)298 int ModelBuilder::createCompilation(CompilationBuilder** compilation) {
299     if (!mCompletedModel || mInvalidModel) {
300         LOG(ERROR) << "ANeuralNetworksCompilation_create passed an unfinished or invalid model";
301         *compilation = nullptr;
302         return ANEURALNETWORKS_BAD_STATE;
303     }
304     *compilation = new (std::nothrow) CompilationBuilder(this);
305     return (*compilation ? ANEURALNETWORKS_NO_ERROR : ANEURALNETWORKS_OUT_OF_MEMORY);
306 }
307 
finish()308 int ModelBuilder::finish() {
309     if (mCompletedModel) {
310         LOG(ERROR) << "ANeuralNetworksModel_finish called more than once";
311         return ANEURALNETWORKS_BAD_STATE;
312     }
313     if (mInvalidModel) {
314         LOG(ERROR) << "ANeuralNetworksModel_finish called on an invalid model";
315         return ANEURALNETWORKS_BAD_STATE;
316     }
317 
318     int n = copyLargeValuesToSharedMemory();
319     if (n != ANEURALNETWORKS_NO_ERROR) {
320         return n;
321     }
322 
323     // TODO: Modify validation so that it can be called without creating a HAL Model.
324     // NOTE: Must copyLargeValuesToSharedMemory() before validation; otherwise,
325     //       a CONSTANT_REFERENCE operand will not have correct .poolIndex, and
326     //       validation will not work properly.
327     Model modelForValidation;
328     setHidlModel(&modelForValidation);
329     if (!validateModel(modelForValidation)) {
330         LOG(ERROR) << "ANeuralNetworksModel_finish called on invalid model";
331         mInvalidModel = true;
332         return ANEURALNETWORKS_BAD_DATA;
333     }
334 
335     // We sort the operations so that they will be in the appropriate
336     // order for a single-threaded, op at a time execution.
337     // TODO: we don't need this if we always run the partitioner.
338     sortIntoRunOrder();
339     mCompletedModel = true;
340     return ANEURALNETWORKS_NO_ERROR;
341 }
342 
sortIntoRunOrder()343 void ModelBuilder::sortIntoRunOrder() {
344     // Tracks the operations that can be executed.
345     std::vector<uint32_t> opsReadyToRun;
346     std::vector<Operation> runOrder;
347 
348     // Tracks how many inputs are needed for each operation to be ready to run.
349     std::multimap<uint32_t, uint32_t> operandToOperations;
350     std::vector<uint32_t> unknownInputCount(operationCount());
351     for (uint32_t operationIndex = 0; operationIndex < operationCount(); operationIndex++) {
352         uint32_t& count = unknownInputCount[operationIndex];
353         count = 0;
354         for (uint32_t operandIndex : mOperations[operationIndex].inputs) {
355             auto lifetime = mOperands[operandIndex].lifetime;
356             if (lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
357                 lifetime == OperandLifeTime::MODEL_OUTPUT) {
358                 count++;
359                 operandToOperations.insert(
360                             std::pair<uint32_t, uint32_t>(operandIndex, operationIndex));
361             }
362         }
363         if (count == 0) {
364             opsReadyToRun.push_back(operationIndex);
365         }
366     }
367 
368     while (opsReadyToRun.size() > 0) {
369         // Execute the next op
370         int opIndex = opsReadyToRun.back();
371         opsReadyToRun.pop_back();
372         const Operation& operation = mOperations[opIndex];
373 
374         runOrder.push_back(mOperations[opIndex]);
375 
376         // Mark all its outputs as known.
377         for (uint32_t operandIndex : operation.outputs) {
378             auto range = operandToOperations.equal_range(operandIndex);
379             for (auto i = range.first; i != range.second; i++) {
380                 uint32_t& count = unknownInputCount[i->second];
381                 if (--count == 0) {
382                     opsReadyToRun.push_back(i->second);
383                 }
384             }
385         }
386     }
387     mOperations = runOrder;
388 }
389 
setHidlModel(Model * model) const390 void ModelBuilder::setHidlModel(Model* model) const {
391     model->operands = mOperands;
392     model->operations = mOperations;
393     model->inputIndexes = mInputIndexes;
394     model->outputIndexes = mOutputIndexes;
395     model->operandValues = mSmallOperandValues;
396     model->relaxComputationFloat32toFloat16 = mRelaxComputationFloat32toFloat16;
397 
398     uint32_t count = mMemories.size();
399     model->pools.resize(count);
400     for (uint32_t i = 0; i < count; i++) {
401         model->pools[i] = mMemories[i]->getHidlMemory();
402     }
403 }
404 
405 }  // namespace nn
406 }  // namespace android
407