| 563 | } |
| 564 | |
| 565 | TfLiteStatus Subgraph::AddNodeWithParameters( |
| 566 | const std::vector<int>& inputs, const std::vector<int>& outputs, |
| 567 | const std::vector<int>& intermediates, const char* init_data, |
| 568 | size_t init_data_size, void* builtin_data, |
| 569 | const TfLiteRegistration* registration, int* node_index) { |
| 570 | std::unique_ptr<void, decltype(free)*> builtin_data_deleter(builtin_data, |
| 571 | free); |
| 572 | if (state_ == kStateInvokableAndImmutable) { |
| 573 | ReportError("AddNodeWithParameters is disallowed when graph is immutable."); |
| 574 | return kTfLiteError; |
| 575 | } |
| 576 | state_ = kStateUninvokable; |
| 577 | |
| 578 | TF_LITE_ENSURE_OK(&context_, CheckTensorIndices("node inputs", inputs.data(), |
| 579 | inputs.size())); |
| 580 | TF_LITE_ENSURE_OK( |
| 581 | &context_, |
| 582 | CheckTensorIndices("node outputs", outputs.data(), outputs.size())); |
| 583 | |
| 584 | // For builtin ops, inputs and outputs must not overlap. Custom ops must do |
| 585 | // this check by themselves if they don't support overlapping tensors. This |
| 586 | // distinction is to allow custom ops to just forward a tensor, reusing it as |
| 587 | // both input and output. |
| 588 | if (builtin_data != nullptr) { |
| 589 | TF_LITE_ENSURE_OK(&context_, CheckInputAndOutputForOverlap( |
| 590 | inputs.data(), inputs.size(), |
| 591 | outputs.data(), outputs.size())); |
| 592 | } |
| 593 | |
| 594 | int new_node_index = nodes_and_registration_.size(); |
| 595 | if (node_index) *node_index = new_node_index; |
| 596 | nodes_and_registration_.resize(nodes_and_registration_.size() + 1); |
| 597 | auto& node_and_reg = nodes_and_registration_.back(); |
| 598 | TfLiteNode& node = node_and_reg.first; |
| 599 | if (node.inputs) TfLiteIntArrayFree(node.inputs); |
| 600 | if (node.outputs) TfLiteIntArrayFree(node.outputs); |
| 601 | if (node.intermediates) TfLiteIntArrayFree(node.intermediates); |
| 602 | if (node.temporaries) TfLiteIntArrayFree(node.temporaries); |
| 603 | |
| 604 | // NOTE, here we are not using move semantics yet, since our internal |
| 605 | // representation isn't std::vector, but in the future we would like to avoid |
| 606 | // copies, so we want the interface to take r-value references now. |
| 607 | node.inputs = ConvertVectorToTfLiteIntArray(inputs); |
| 608 | node.outputs = ConvertVectorToTfLiteIntArray(outputs); |
| 609 | node.intermediates = ConvertVectorToTfLiteIntArray(intermediates); |
| 610 | node.temporaries = TfLiteIntArrayCreate(0); |
| 611 | if (init_data) { |
| 612 | node.user_data = OpInit(*registration, init_data, init_data_size); |
| 613 | } else { |
| 614 | node.user_data = |
| 615 | OpInit(*registration, |
| 616 | reinterpret_cast<const char*>(builtin_data_deleter.get()), 0); |
| 617 | } |
| 618 | |
| 619 | node.builtin_data = builtin_data_deleter.release(); |
| 620 | // TODO(ycling): Filling `custom_initial_data` and `custom_initial_data_size` |
| 621 | // properly for nodes generated by ReplaceNodeSubsetsWithDelegateKernels. |
| 622 | |