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