Build a FuncOp from a tflite SubGraph The op_names are a mapping from indexes into the TFLite operators array to the operator name MLIR expects (tfl.foo_op). The buffers are directly taken from the deserialized flatbuffer as we do not have the type information to interpret them until this point. The base_loc parameter is the location of the flatbuffer as a whole (usually a file). The is_entry_poin
| 724 | // ordered_output_arrays is not empty, then the imported mlir function will only |
| 725 | // return nodes in ordered_output_arrays in the same order. |
| 726 | StatusOr<FuncOp> ConvertSubgraph( |
| 727 | const tflite::SubGraphT& subgraph, llvm::StringRef name, |
| 728 | const std::vector<std::string>& op_names, |
| 729 | const std::vector<std::string>& func_names, |
| 730 | const std::vector<std::unique_ptr<tflite::BufferT>>& buffers, |
| 731 | Location base_loc, Builder builder, bool is_entry_point, |
| 732 | bool use_external_constant, |
| 733 | const std::vector<std::string>& ordered_input_arrays, |
| 734 | const std::vector<std::string>& ordered_output_arrays, |
| 735 | bool experimental_prune_unreachable_nodes_unconditionally) { |
| 736 | llvm::SmallVector<mlir::Type, 2> ret_types; |
| 737 | llvm::SmallVector<mlir::Type, 4> input_types; |
| 738 | |
| 739 | auto func_loc = mlir::NameLoc::get(builder.getIdentifier(name), base_loc); |
| 740 | |
| 741 | std::vector<int> func_inputs = subgraph.inputs; |
| 742 | if (is_entry_point && !ordered_input_arrays.empty()) { |
| 743 | if (!experimental_prune_unreachable_nodes_unconditionally) { |
| 744 | // TODO(b/149922113): Resolve input-arrays/pruning flags interaction. |
| 745 | return errors::InvalidArgument( |
| 746 | "input-arrays should be used with experimental pruning flag"); |
| 747 | } |
| 748 | TF_ASSIGN_OR_RETURN(func_inputs, |
| 749 | GetTensorIndices(subgraph, ordered_input_arrays)); |
| 750 | } |
| 751 | |
| 752 | // Add state variables to inputs. |
| 753 | absl::flat_hash_set<int32_t> input_index_set(func_inputs.begin(), |
| 754 | func_inputs.end()); |
| 755 | for (int i = 0; i < subgraph.tensors.size(); i++) { |
| 756 | auto& tensor = *subgraph.tensors.at(i); |
| 757 | if (tensor.is_variable && !input_index_set.contains(i)) { |
| 758 | func_inputs.emplace_back(i); |
| 759 | input_index_set.insert(i); |
| 760 | } |
| 761 | } |
| 762 | |
| 763 | for (auto input_or_variable : func_inputs) { |
| 764 | auto& tensor = *subgraph.tensors.at(input_or_variable); |
| 765 | // TODO(b/138222071) Graph inputs must have static shape per the exporter, |
| 766 | // but we cannot differentiate scalars from unranked tensors. |
| 767 | // Here we reverse the default assumption that shape = [] means unranked. |
| 768 | // when processing main() |
| 769 | auto type_or_err = GetTensorType(tensor, builder, |
| 770 | /*shapeless_are_scalars=*/is_entry_point, |
| 771 | /*is_constant=*/false); |
| 772 | if (!type_or_err.ok()) { |
| 773 | emitError(func_loc, "error reading argument types") |
| 774 | << type_or_err.status().ToString(); |
| 775 | return type_or_err.status(); |
| 776 | } |
| 777 | auto type = type_or_err.ConsumeValueOrDie(); |
| 778 | input_types.push_back(type); |
| 779 | } |
| 780 | |
| 781 | llvm::SmallVector<bool, 16> is_op_output(subgraph.tensors.size(), false); |
| 782 | for (auto& op : subgraph.operators) { |
| 783 | for (auto output : op->outputs) { |
no test coverage detected