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 28namespace android { 29namespace nn { 30 31// The maximum number of operands and operations that a model may have. 32const uint32_t MAX_NUMBER_OF_OPERANDS = 0xFFFFFFFE; 33const uint32_t MAX_NUMBER_OF_OPERATIONS = 0xFFFFFFFE; 34 35bool 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 47int 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 73int 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 134int 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 172int 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 200int 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 235int 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 288int 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 298int 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 308int 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 343void 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 390void 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