0f6a2a5d027d7309f54196056cf2401b13a821e0
[platform/core/ml/nnfw.git] / runtime / onert / frontend / base_loader / include / base_loader.h
1 /*
2  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
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 #ifndef __BASE_LOADER_BASE_LOADER_H__
18 #define __BASE_LOADER_BASE_LOADER_H__
19
20 #include "ir/Graph.h"
21 #include "ir/Shape.h"
22 #include "ir/Operations.Include.h"
23
24 #include "flatbuffers/flexbuffers.h"
25
26 #include <map>
27 #include <memory>
28 #include <fstream>
29 #include <limits>
30 #include <fcntl.h>
31 #include <sys/stat.h>
32 #include <sys/mman.h>
33 #include <unistd.h>
34 #include <util/logging.h>
35
36 namespace onert
37 {
38 namespace base_loader
39 {
40
41 template <typename LoaderDomain, typename SpecificLoader> class BaseLoader
42 {
43 protected:
44   using Verifier = typename LoaderDomain::Verifier;
45   using ActivationFunctionType = typename LoaderDomain::ActivationFunctionType;
46   using Buffer = typename LoaderDomain::Buffer;
47   using BuiltinOperator = typename LoaderDomain::BuiltinOperator;
48   using CustomOptionsFormat = typename LoaderDomain::CustomOptionsFormat;
49   using Model = typename LoaderDomain::Model;
50   using Operator = typename LoaderDomain::Operator;
51   using Padding = typename LoaderDomain::Padding;
52   using Pool2DOptions = typename LoaderDomain::Pool2DOptions;
53   using SubGraph = typename LoaderDomain::SubGraph;
54   using Tensor = typename LoaderDomain::Tensor;
55   using TensorType = typename LoaderDomain::TensorType;
56   using DimensionType = typename LoaderDomain::DimensionType;
57   using SparseIndexVector = typename LoaderDomain::SparseIndexVector;
58
59 protected:
60   bool isOptionalInputTensor(std::int32_t idx) { return idx == -1; }
61   virtual bool allowOptionalInputTensor(BuiltinOperator) = 0;
62
63 public:
64   /**
65    * @brief Construct a new Loader object
66    *
67    * @param graph reference on subgraphs
68    */
69   explicit BaseLoader(std::unique_ptr<ir::Subgraphs> &subgs)
70       : _base{nullptr}, _pagesize(getpagesize()), _fd(-1), _subgraphs(subgs), _model{nullptr}
71   {
72   }
73
74   /**
75    * @brief Load a model from file
76    *
77    * @param file_path
78    */
79   void loadFromFile(const char *file_path);
80   /**
81    * @brief Load a model from a buffer
82    *
83    * @param buffer buffer pointer
84    * @param size buffer size
85    */
86   void loadFromBuffer(uint8_t *buffer, size_t size);
87
88 protected:
89   ~BaseLoader() = default;
90   void loadModel();
91
92   // Helper functions
93   ir::Activation convertActivation(ActivationFunctionType type);
94   ir::DataType tensorTypeToDataType(TensorType type);
95   ir::OperandIndex tensorIdxToOperandIdx(int32_t tensorIdx);
96   void deallocateMmappedArea(uint8_t *ptr, size_t size);
97
98   // Create operands form tflite::Tensor
99   ir::OperandIndex loadOperand(const Tensor *tensor, ir::Graph &subg);
100   void loadOperationIO(const Operator *op, ir::OperandIndexSequence &inputs,
101                        ir::OperandIndexSequence &outputs);
102   // Create operations from Operator
103   void loadOperation(const Operator *op, ir::Graph &subg);
104   // Load Strides and Paddings from options to param
105   template <typename Param, typename OptionsType>
106   void loadStridesAndPaddings(Param &param, const OptionsType *options);
107   // Load Pool2D param
108   template <typename Param> void loadPool2D(Param &param, const Pool2DOptions *options);
109
110   // Operations
111   void loadConv2D(const Operator *op, ir::Graph &subg);
112   void loadDepthwiseConv2D(const Operator *op, ir::Graph &subg);
113   void loadTransposeConv(const Operator *op, ir::Graph &subg);
114   void loadAvgPool2D(const Operator *op, ir::Graph &subg);
115   void loadReshape(const Operator *op, ir::Graph &subg);
116   void loadSoftmax(const Operator *op, ir::Graph &subg);
117   void loadMaxPool2D(const Operator *op, ir::Graph &subg);
118   void loadConcatenation(const Operator *op, ir::Graph &subg);
119   void loadFill(const Operator *op, ir::Graph &subg);
120   void loadFC(const Operator *op, ir::Graph &subg);
121   void loadAdd(const Operator *op, ir::Graph &subg);
122   void loadSub(const Operator *op, ir::Graph &subg);
123   void loadMul(const Operator *op, ir::Graph &subg);
124   void loadDiv(const Operator *op, ir::Graph &subg);
125   void loadPack(const Operator *op, ir::Graph &subg);
126   void loadRelu(const Operator *op, ir::Graph &subg);
127   void loadRelu6(const Operator *op, ir::Graph &subg);
128   void loadResizeBilinear(const Operator *op, ir::Graph &subg);
129   void loadRsqrt(const Operator *op, ir::Graph &subg);
130   void loadSelect(const Operator *op, ir::Graph &subg);
131   void loadSqrt(const Operator *op, ir::Graph &subg);
132   void loadSquaredDifference(const Operator *op, ir::Graph &subg);
133   void loadTanh(const Operator *op, ir::Graph &subg);
134   void loadTranspose(const Operator *op, ir::Graph &subg);
135   void loadReduce(const Operator *op, ir::Graph &subg,
136                   ir::operation::Reduce::ReduceType reduce_type);
137   void loadReduceAll(const Operator *op, ir::Graph &subg);
138   void loadReverseV2(const Operator *op, ir::Graph &subg);
139   void loadPad(const Operator *op, ir::Graph &subg);
140   void loadLogistic(const Operator *op, ir::Graph &subg);
141   void loadExp(const Operator *op, ir::Graph &subg);
142   void loadExpandDims(const Operator *op, ir::Graph &subg);
143   void loadGather(const Operator *op, ir::Graph &subg);
144   void loadCustom(const Operator *op, ir::Graph &subg);
145   void loadSpaceToBatchND(const Operator *op, ir::Graph &subg);
146   void loadBatchMatMul(const Operator *op, ir::Graph &subg);
147   void loadBatchToSpaceND(const Operator *op, ir::Graph &subg);
148   void loadSqueeze(const Operator *op, ir::Graph &subg);
149   void loadPrelu(const Operator *op, ir::Graph &subg);
150   void loadSplit(const Operator *op, ir::Graph &subg);
151   void loadSplitV(const Operator *op, ir::Graph &subg);
152   void loadSlice(const Operator *op, ir::Graph &subg);
153   void loadStridedSlice(const Operator *op, ir::Graph &subg);
154   void loadUnpack(const Operator *op, ir::Graph &subg);
155   void loadMinimum(const Operator *op, ir::Graph &subg);
156   void loadMaximum(const Operator *op, ir::Graph &subg);
157   void loadCast(const Operator *op, ir::Graph &subg);
158   void loadComparison(const Operator *op, ir::Graph &subg);
159   void loadEinsum(const Operator *op, ir::Graph &subg);
160   void loadOneHot(const Operator *op, ir::Graph &subg);
161   void loadAbs(const Operator *op, ir::Graph &subg);
162   void loadCos(const Operator *op, ir::Graph &subg);
163   void loadSin(const Operator *op, ir::Graph &subg);
164   void loadShape(const Operator *op, ir::Graph &subg);
165   void loadIf(const Operator *op, ir::Graph &subg);
166   void loadWhile(const Operator *op, ir::Graph &subg);
167   void loadNeg(const Operator *op, ir::Graph &subg);
168   void loadLog(const Operator *op, ir::Graph &subg);
169   void loadArgMax(const Operator *op, ir::Graph &subg);
170   void loadRound(const Operator *op, ir::Graph &subg);
171   void loadPow(const Operator *op, ir::Graph &subg);
172   void loadLogicalNot(const Operator *op, ir::Graph &subg);
173   void loadZerosLike(const Operator *op, ir::Graph &subg);
174   void loadTile(const Operator *op, ir::Graph &subg);
175   void loadLogicalOr(const Operator *op, ir::Graph &subg);
176   void loadRange(const Operator *op, ir::Graph &subg);
177   void loadMatrixBandPart(const Operator *op, ir::Graph &subg);
178   void loadBroadcastTo(const Operator *op, ir::Graph &subg);
179   void loadFusedBatchNorm(const Operator *op, ir::Graph &subg);
180   void loadLogSoftmax(const Operator *op, ir::Graph &subg);
181   void loadQuantize(const Operator *op, ir::Graph &subg);
182   void loadSpaceToDepth(const Operator *op, ir::Graph &subg);
183   void loadStatelessRandomUniform(const Operator *op, ir::Graph &subg);
184
185 protected:
186   // Base address for mapped region for loading (if needed)
187   uint8_t *_base;
188   // Memory page size
189   int32_t _pagesize;
190   // loaded file description
191   int _fd;
192   // Reference on loadable subgraphs
193   std::unique_ptr<ir::Subgraphs> &_subgraphs;
194   const Model *_model;
195   // Maps Tensor indices to onert Operands.
196   std::vector<ir::OperandIndex> _tensor_to_operand;
197   // Verifier
198   std::unique_ptr<Verifier> _verifier;
199 };
200
201 template <typename LoaderDomain, typename SpecificLoader>
202 void BaseLoader<LoaderDomain, SpecificLoader>::BaseLoader::loadFromFile(const char *file_path)
203 {
204   _fd = open(file_path, O_RDONLY);
205   if (_fd < 0)
206   {
207     throw std::runtime_error("Failed to open file " + std::string(file_path));
208   }
209
210   struct stat file_stat;
211   if (fstat(_fd, &file_stat) != 0)
212   {
213     throw std::runtime_error("Fstat failed or file " + std::string(file_path) +
214                              " is not a regular file");
215   }
216   int size = file_stat.st_size;
217
218   // Map model file into memory region
219   _base = static_cast<uint8_t *>(mmap(NULL, size, PROT_READ, MAP_PRIVATE, _fd, 0));
220   if (_base == MAP_FAILED)
221   {
222     close(_fd);
223     throw std::runtime_error("mmap failed - " + std::string(strerror(errno)));
224   }
225
226   _verifier = std::make_unique<Verifier>(reinterpret_cast<const std::uint8_t *>(_base), size);
227
228   loadModel();
229
230   close(_fd);
231 }
232
233 template <typename LoaderDomain, typename SpecificLoader>
234 void BaseLoader<LoaderDomain, SpecificLoader>::BaseLoader::loadFromBuffer(uint8_t *buffer,
235                                                                           size_t size)
236 {
237   _base = buffer;
238   _verifier = std::make_unique<Verifier>(reinterpret_cast<const std::uint8_t *>(_base), size);
239   loadModel();
240 }
241
242 template <typename LoaderDomain, typename SpecificLoader>
243 ir::Activation BaseLoader<LoaderDomain, SpecificLoader>::BaseLoader::convertActivation(
244     const ActivationFunctionType type)
245 {
246   switch (type)
247   {
248     case ActivationFunctionType::ActivationFunctionType_NONE:
249       return ir::Activation::NONE;
250     case ActivationFunctionType::ActivationFunctionType_RELU:
251       return ir::Activation::RELU;
252     case ActivationFunctionType::ActivationFunctionType_RELU_N1_TO_1:
253       return ir::Activation::RELU1;
254     case ActivationFunctionType::ActivationFunctionType_RELU6:
255       return ir::Activation::RELU6;
256     case ActivationFunctionType::ActivationFunctionType_TANH:
257       return ir::Activation::TANH;
258     default:
259       throw std::runtime_error(std::string("Unsupported activation type: ")
260                                    .append(EnumNameActivationFunctionType(type)));
261   }
262 }
263
264 template <typename LoaderDomain, typename SpecificLoader>
265 ir::DataType
266 BaseLoader<LoaderDomain, SpecificLoader>::BaseLoader::tensorTypeToDataType(const TensorType type)
267 {
268   switch (type)
269   {
270     case TensorType::TensorType_FLOAT32:
271       return ir::DataType::FLOAT32;
272     case TensorType::TensorType_INT32:
273       return ir::DataType::INT32;
274     case TensorType::TensorType_BOOL:
275       return ir::DataType::BOOL8;
276     case TensorType::TensorType_UINT8:
277       return ir::DataType::QUANT_UINT8_ASYMM;
278     case TensorType::TensorType_INT8:
279       return ir::DataType::QUANT_INT8_SYMM;
280     case TensorType::TensorType_INT64:
281       return ir::DataType::INT64;
282     default:
283       throw std::runtime_error(
284           std::string("Unsupported tensor type: ").append(EnumNameTensorType(type)));
285   }
286 }
287
288 template <typename LoaderDomain, typename SpecificLoader>
289 ir::OperandIndex
290 BaseLoader<LoaderDomain, SpecificLoader>::BaseLoader::tensorIdxToOperandIdx(int32_t tensorIdx)
291 {
292   return isOptionalInputTensor(tensorIdx) ? ir::OperandIndex() : _tensor_to_operand[tensorIdx];
293 }
294
295 template <typename LoaderDomain, typename SpecificLoader>
296 void BaseLoader<LoaderDomain, SpecificLoader>::BaseLoader::deallocateMmappedArea(uint8_t *ptr,
297                                                                                  size_t size)
298 {
299   // Calculate offset from base address of mapped region
300   ptrdiff_t unaligned_offset_start = ptr - _base;
301   ptrdiff_t unaligned_offset_end = unaligned_offset_start + size;
302
303   // Calculated aligned offset from base address of mapped region
304   // munmap accepts memory address which is a multiple of the pagesize
305   ptrdiff_t aligned_offset_start =
306       ((unaligned_offset_start + (_pagesize - 1)) / _pagesize) * _pagesize;
307   ptrdiff_t aligned_offset_end = (unaligned_offset_end / _pagesize) * _pagesize;
308
309   ptrdiff_t area_size = aligned_offset_end - aligned_offset_start;
310   if (area_size > 0)
311   {
312     // Unmap mapped region for CachedData
313     if (munmap(_base + aligned_offset_start, area_size) == -1)
314     {
315       VERBOSE(BASE_LOADER) << "munmap failed" << std::endl;
316     }
317   }
318 }
319
320 /* Copied from tensorflow lite. Need to append copyright */
321 template <typename T> bool Copy(const T *data_ptr, std::vector<uint16_t> &arr)
322 {
323   if (data_ptr->values() == nullptr)
324   {
325     return false;
326   }
327
328   int size = data_ptr->values()->size();
329   arr.reserve(size);
330   for (int i = 0; i < size; i++)
331   {
332     arr.emplace_back(static_cast<uint16_t>(data_ptr->values()->Get(i)));
333   }
334   return true;
335 }
336
337 template <typename LoaderDomain, typename SpecificLoader>
338 ir::OperandIndex BaseLoader<LoaderDomain, SpecificLoader>::loadOperand(const Tensor *tensor,
339                                                                        ir::Graph &subg)
340 {
341   ir::Shape shape;
342   // Shape
343   const auto *tensor_shape = tensor->shape();
344   if (tensor_shape != nullptr)
345   {
346     for (const auto &dim : *tensor_shape)
347     {
348       shape.append(dim);
349     }
350   }
351
352   // Note for tensor->shape_signature()
353   // We don't handle shape signature
354   //    How we handle:
355   //       If shape_signature[k] == -1, we will use tensor->shape()[k] == 1
356   //       If app wants to change the input shape, call nnfw_apply_input_tensorinfo() can
357   //       be used.
358
359   // Type
360   ir::DataType data_type = tensorTypeToDataType(tensor->type());
361   // Quantization
362   auto q_params = tensor->quantization();
363   float scale = 0.0;
364   long zero_point = 0;
365   if (q_params != nullptr)
366   {
367     if (q_params->scale())
368     {
369       if (q_params->scale()->size() != 1)
370       {
371         throw std::runtime_error("Only 1 scale for a tensor is supported.");
372       }
373       scale = q_params->scale()->Get(0);
374     }
375
376     if (q_params->zero_point())
377     {
378       if (q_params->zero_point()->size() != 1)
379       {
380         throw std::runtime_error("Only 1 zero_point value for a tensor is supported.");
381       }
382       zero_point = q_params->zero_point()->Get(0);
383       // zero_point is long while TypeInfo.zero_point is defined as int32_t.
384       assert(zero_point >= std::numeric_limits<int32_t>::min());
385       assert(zero_point <= std::numeric_limits<int32_t>::max());
386     }
387     auto details = q_params->details_as_CustomQuantization();
388     if (details != nullptr)
389       throw std::runtime_error("Custom Quantization is not supported");
390   }
391   // Create TypeInfo
392   ir::TypeInfo type_info(data_type, scale, zero_point);
393   // Sparsity
394   auto src_sparsity = tensor->sparsity();
395   if (src_sparsity != nullptr)
396   {
397     std::vector<uint16_t> w1_segments;
398     std::vector<uint16_t> w1_indices;
399     // ignore traversal_order, block_map
400     // load metadata
401     const size_t dim_metadata_size = src_sparsity->dim_metadata()->size();
402     if (dim_metadata_size != 2)
403       throw std::runtime_error("sparse tensor is supported only for 2D");
404     const auto *src_metadata = src_sparsity->dim_metadata()->Get(0);
405     if (src_metadata->format() != DimensionType::DimensionType_DENSE)
406       throw std::runtime_error("sparse tensor dim[0] is not DENSE");
407     src_metadata = src_sparsity->dim_metadata()->Get(1);
408     if (src_metadata->format() != DimensionType::DimensionType_SPARSE_CSR)
409       throw std::runtime_error("sparse tensor dim[0] is not SPARSE_CSR");
410
411     auto ParseSparseIndexVector = [src_metadata, &w1_segments, &w1_indices]() {
412       if (src_metadata->array_segments() == nullptr || src_metadata->array_indices() == nullptr)
413         return false;
414       bool status = true;
415       switch (src_metadata->array_segments_type())
416       {
417         case SparseIndexVector::SparseIndexVector_Int32Vector:
418           status = Copy(src_metadata->array_segments_as_Int32Vector(), w1_segments);
419           break;
420         case SparseIndexVector::SparseIndexVector_Uint16Vector:
421           status = Copy(src_metadata->array_segments_as_Uint16Vector(), w1_segments);
422           break;
423         case SparseIndexVector::SparseIndexVector_Uint8Vector:
424           status = Copy(src_metadata->array_segments_as_Uint8Vector(), w1_segments);
425           break;
426         default:
427           return false;
428       }
429       if (status != true)
430         return false;
431       switch (src_metadata->array_indices_type())
432       {
433         case SparseIndexVector::SparseIndexVector_Int32Vector:
434           return Copy(src_metadata->array_indices_as_Int32Vector(), w1_indices);
435         case SparseIndexVector::SparseIndexVector_Uint16Vector:
436           return Copy(src_metadata->array_indices_as_Uint16Vector(), w1_indices);
437         case SparseIndexVector::SparseIndexVector_Uint8Vector:
438           return Copy(src_metadata->array_indices_as_Uint8Vector(), w1_indices);
439         default:
440           break;
441       }
442       return false;
443     };
444     if (ParseSparseIndexVector() == false)
445       throw std::runtime_error("Error during parsing sparsity index information");
446     type_info.sparse2DMetadata(std::move(w1_segments), std::move(w1_indices));
447   }
448   // Create operand
449   const auto operand_index = subg.addOperand(shape, type_info);
450
451   // Constant tensors are indicated by non-empty data.
452   const auto *data = _model->buffers()->Get(tensor->buffer())->data();
453   if (data != nullptr)
454   {
455     using std::ptrdiff_t;
456     std::unique_ptr<ir::Data> data_obj;
457     if (_fd == -1) // Model is from memory
458     {
459       data_obj = std::make_unique<ir::ExternalData>(data->data(), data->size());
460     }
461     else // Model is loaded(mmap'd) from a file
462     {
463       data_obj = std::make_unique<ir::CachedData>(data->data(), data->size());
464       deallocateMmappedArea(const_cast<uint8_t *>(data->data()), data->size());
465     }
466     subg.setOperandValue(operand_index, std::move(data_obj));
467   }
468
469   // Name unused
470   // auto name = tensor->name();
471   // Variablie
472   if (tensor->is_variable())
473     throw std::runtime_error("Variable tensor not supported!");
474
475   return operand_index;
476 }
477
478 template <typename LoaderDomain, typename SpecificLoader>
479 void BaseLoader<LoaderDomain, SpecificLoader>::loadOperationIO(const Operator *op,
480                                                                ir::OperandIndexSequence &inputs,
481                                                                ir::OperandIndexSequence &outputs)
482 {
483   for (const std::int32_t idx : *op->inputs())
484   {
485     // Optional tensors are not supported yet except for FULLY_CONNECTED and BCQ_FULLY_CONNECTED
486     auto check_optional_input = [&]() {
487       auto builtin_code = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
488       if (isOptionalInputTensor(idx) && !allowOptionalInputTensor(builtin_code))
489         throw std::runtime_error(
490             std::string("loader doesn't support optional input tensor yet for ")
491                 .append(EnumNameBuiltinOperator(builtin_code)));
492     };
493     check_optional_input();
494     inputs.append(tensorIdxToOperandIdx(idx));
495   }
496
497   for (const std::int32_t idx : *op->outputs())
498   {
499     outputs.append(tensorIdxToOperandIdx(idx));
500   }
501 }
502
503 template <typename LoaderDomain, typename SpecificLoader>
504 template <typename Param, typename OptionsType>
505 void BaseLoader<LoaderDomain, SpecificLoader>::loadStridesAndPaddings(Param &param,
506                                                                       const OptionsType *options)
507 {
508   // Strides
509   param.stride.vertical = options->stride_h();
510   param.stride.horizontal = options->stride_w();
511   // Paddings
512   if (options->padding() == Padding::Padding_SAME)
513     param.padding.type = ir::PaddingType::SAME;
514   if (options->padding() == Padding::Padding_VALID)
515     param.padding.type = ir::PaddingType::VALID;
516   // param paddings indexes unused
517 }
518
519 template <typename LoaderDomain, typename SpecificLoader>
520 template <typename Param>
521 void BaseLoader<LoaderDomain, SpecificLoader>::loadPool2D(Param &param,
522                                                           const Pool2DOptions *options)
523 {
524   // Strides and Paddings
525   loadStridesAndPaddings(param, options);
526   // Filter width and height
527   // Strides
528   param.kw = options->filter_width();
529   param.kh = options->filter_height();
530   // Activation
531   param.activation = convertActivation(options->fused_activation_function());
532 }
533
534 template <typename LoaderDomain, typename SpecificLoader>
535 void BaseLoader<LoaderDomain, SpecificLoader>::loadConv2D(const Operator *op, ir::Graph &subg)
536 {
537   ir::OperandIndexSequence inputs;
538   ir::OperandIndexSequence outputs;
539
540   loadOperationIO(op, inputs, outputs);
541
542   ir::operation::Conv2D::Param param;
543   const auto *options = op->builtin_options_as_Conv2DOptions();
544   param.activation = convertActivation(options->fused_activation_function());
545   loadStridesAndPaddings(param, options);
546   // Dilation h/w factor unused
547   std::unique_ptr<ir::Operation> new_op(new ir::operation::Conv2D(inputs, outputs, param));
548   subg.addOperation(std::move(new_op));
549 }
550
551 template <typename LoaderDomain, typename SpecificLoader>
552 void BaseLoader<LoaderDomain, SpecificLoader>::loadDepthwiseConv2D(const Operator *op,
553                                                                    ir::Graph &subg)
554 {
555   ir::OperandIndexSequence inputs;
556   ir::OperandIndexSequence outputs;
557
558   loadOperationIO(op, inputs, outputs);
559
560   ir::operation::DepthwiseConv2D::Param param;
561   const auto *options = op->builtin_options_as_DepthwiseConv2DOptions();
562   param.activation = convertActivation(options->fused_activation_function());
563   loadStridesAndPaddings(param, options);
564   // Multiplier
565   param.multiplier = options->depth_multiplier();
566   // Dilation h/w factor unused
567   std::unique_ptr<ir::Operation> new_op(new ir::operation::DepthwiseConv2D(inputs, outputs, param));
568   subg.addOperation(std::move(new_op));
569 }
570
571 template <typename LoaderDomain, typename SpecificLoader>
572 void BaseLoader<LoaderDomain, SpecificLoader>::loadTransposeConv(const Operator *op,
573                                                                  ir::Graph &subg)
574 {
575   ir::OperandIndexSequence inputs;
576   ir::OperandIndexSequence outputs;
577
578   loadOperationIO(op, inputs, outputs);
579
580   ir::operation::TransposeConv::Param param;
581   const auto *options = op->builtin_options_as_TransposeConvOptions();
582   loadStridesAndPaddings(param, options);
583   std::unique_ptr<ir::Operation> new_op(new ir::operation::TransposeConv(inputs, outputs, param));
584   subg.addOperation(std::move(new_op));
585 }
586
587 template <typename LoaderDomain, typename SpecificLoader>
588 void BaseLoader<LoaderDomain, SpecificLoader>::loadAvgPool2D(const Operator *op, ir::Graph &subg)
589 {
590   ir::OperandIndexSequence inputs;
591   ir::OperandIndexSequence outputs;
592
593   loadOperationIO(op, inputs, outputs);
594
595   ir::operation::AvgPool2D::Param param;
596   const auto *options = op->builtin_options_as_Pool2DOptions();
597
598   loadPool2D(param, options);
599
600   std::unique_ptr<ir::Operation> new_op(new ir::operation::AvgPool2D(inputs, outputs, param));
601   subg.addOperation(std::move(new_op));
602 }
603
604 template <typename LoaderDomain, typename SpecificLoader>
605 void BaseLoader<LoaderDomain, SpecificLoader>::loadReshape(const Operator *op, ir::Graph &subg)
606 {
607   ir::OperandIndexSequence inputs;
608   ir::OperandIndexSequence outputs;
609
610   loadOperationIO(op, inputs, outputs);
611
612   ir::operation::Reshape::Param param{};
613   const auto *options = op->builtin_options_as_ReshapeOptions();
614   if (options != nullptr)
615   {
616     const auto *new_shape = options->new_shape();
617     if (new_shape)
618     {
619       for (uint i = 0; i < new_shape->Length(); ++i)
620       {
621         param.new_shape.push_back(new_shape->Get(i));
622       }
623     }
624   }
625
626   std::unique_ptr<ir::Operation> new_op(new ir::operation::Reshape(inputs, outputs, param));
627   subg.addOperation(std::move(new_op));
628 }
629
630 template <typename LoaderDomain, typename SpecificLoader>
631 void BaseLoader<LoaderDomain, SpecificLoader>::loadSoftmax(const Operator *op, ir::Graph &subg)
632 {
633   ir::OperandIndexSequence inputs;
634   ir::OperandIndexSequence outputs;
635
636   loadOperationIO(op, inputs, outputs);
637
638   ir::operation::Softmax::Param param;
639   const auto *options = op->builtin_options_as_SoftmaxOptions();
640   // Beta
641   param.beta = options->beta();
642
643   std::unique_ptr<ir::Operation> new_op(new ir::operation::Softmax(inputs, outputs, param));
644   subg.addOperation(std::move(new_op));
645 }
646
647 template <typename LoaderDomain, typename SpecificLoader>
648 void BaseLoader<LoaderDomain, SpecificLoader>::loadMaxPool2D(const Operator *op, ir::Graph &subg)
649 {
650   ir::OperandIndexSequence inputs;
651   ir::OperandIndexSequence outputs;
652
653   loadOperationIO(op, inputs, outputs);
654
655   ir::operation::MaxPool2D::Param param;
656   const auto *options = op->builtin_options_as_Pool2DOptions();
657
658   loadPool2D(param, options);
659
660   std::unique_ptr<ir::Operation> new_op(new ir::operation::MaxPool2D(inputs, outputs, param));
661   subg.addOperation(std::move(new_op));
662 }
663
664 template <typename LoaderDomain, typename SpecificLoader>
665 void BaseLoader<LoaderDomain, SpecificLoader>::loadConcatenation(const Operator *op,
666                                                                  ir::Graph &subg)
667 {
668   ir::OperandIndexSequence inputs;
669   ir::OperandIndexSequence outputs;
670
671   loadOperationIO(op, inputs, outputs);
672
673   ir::operation::Concat::Param param;
674   const auto *options = op->builtin_options_as_ConcatenationOptions();
675   // Axis
676   param.axis = options->axis();
677   // activation unused
678
679   std::unique_ptr<ir::Operation> new_op(new ir::operation::Concat(inputs, outputs, param));
680   subg.addOperation(std::move(new_op));
681 }
682
683 template <typename LoaderDomain, typename SpecificLoader>
684 void BaseLoader<LoaderDomain, SpecificLoader>::loadFill(const Operator *op, ir::Graph &subg)
685 {
686   ir::OperandIndexSequence inputs;
687   ir::OperandIndexSequence outputs;
688
689   loadOperationIO(op, inputs, outputs);
690
691   std::unique_ptr<ir::Operation> new_op(new ir::operation::Fill(inputs, outputs));
692   subg.addOperation(std::move(new_op));
693 }
694
695 template <typename LoaderDomain, typename SpecificLoader>
696 void BaseLoader<LoaderDomain, SpecificLoader>::loadFC(const Operator *op, ir::Graph &subg)
697 {
698   ir::OperandIndexSequence inputs;
699   ir::OperandIndexSequence outputs;
700
701   loadOperationIO(op, inputs, outputs);
702
703   const auto &input_operand = subg.operands().at(inputs.at(ir::operation::FullyConnected::INPUT));
704   auto &weights_operand = subg.operands().at(inputs.at(ir::operation::FullyConnected::WEIGHT));
705   if (input_operand.typeInfo().type() == ir::DataType::FLOAT32 &&
706       weights_operand.typeInfo().type() == ir::DataType::QUANT_UINT8_ASYMM)
707   {
708     weights_operand.type(ir::DataType::QUANT_INT8_SYMM);
709   }
710
711   ir::operation::FullyConnected::Param param;
712   const auto *options = op->builtin_options_as_FullyConnectedOptions();
713
714   param.activation = convertActivation(options->fused_activation_function());
715   // weights_format unused
716
717   std::unique_ptr<ir::Operation> new_op(new ir::operation::FullyConnected(inputs, outputs, param));
718   subg.addOperation(std::move(new_op));
719 }
720
721 template <typename LoaderDomain, typename SpecificLoader>
722 void BaseLoader<LoaderDomain, SpecificLoader>::loadAdd(const Operator *op, ir::Graph &subg)
723 {
724   ir::OperandIndexSequence inputs;
725   ir::OperandIndexSequence outputs;
726
727   loadOperationIO(op, inputs, outputs);
728
729   ir::operation::Add::Param param;
730   const auto *options = op->builtin_options_as_AddOptions();
731
732   param.activation = convertActivation(options->fused_activation_function());
733
734   std::unique_ptr<ir::Operation> new_op(new ir::operation::Add(inputs, outputs, param));
735   subg.addOperation(std::move(new_op));
736 }
737
738 template <typename LoaderDomain, typename SpecificLoader>
739 void BaseLoader<LoaderDomain, SpecificLoader>::loadSub(const Operator *op, ir::Graph &subg)
740 {
741   ir::OperandIndexSequence inputs;
742   ir::OperandIndexSequence outputs;
743
744   loadOperationIO(op, inputs, outputs);
745
746   ir::operation::Sub::Param param;
747   const auto *options = op->builtin_options_as_SubOptions();
748
749   param.activation = convertActivation(options->fused_activation_function());
750
751   std::unique_ptr<ir::Operation> new_op(new ir::operation::Sub(inputs, outputs, param));
752   subg.addOperation(std::move(new_op));
753 }
754
755 template <typename LoaderDomain, typename SpecificLoader>
756 void BaseLoader<LoaderDomain, SpecificLoader>::loadMul(const Operator *op, ir::Graph &subg)
757 {
758   ir::OperandIndexSequence inputs;
759   ir::OperandIndexSequence outputs;
760
761   loadOperationIO(op, inputs, outputs);
762
763   ir::operation::Mul::Param param;
764   const auto *options = op->builtin_options_as_MulOptions();
765
766   param.activation = convertActivation(options->fused_activation_function());
767
768   std::unique_ptr<ir::Operation> new_op(new ir::operation::Mul(inputs, outputs, param));
769   subg.addOperation(std::move(new_op));
770 }
771
772 template <typename LoaderDomain, typename SpecificLoader>
773 void BaseLoader<LoaderDomain, SpecificLoader>::loadDiv(const Operator *op, ir::Graph &subg)
774 {
775   ir::OperandIndexSequence inputs;
776   ir::OperandIndexSequence outputs;
777
778   loadOperationIO(op, inputs, outputs);
779
780   ir::operation::Div::Param param;
781   const auto *options = op->builtin_options_as_DivOptions();
782
783   param.activation = convertActivation(options->fused_activation_function());
784
785   std::unique_ptr<ir::Operation> new_op(new ir::operation::Div(inputs, outputs, param));
786   subg.addOperation(std::move(new_op));
787 }
788
789 template <typename LoaderDomain, typename SpecificLoader>
790 void BaseLoader<LoaderDomain, SpecificLoader>::loadPack(const Operator *op, ir::Graph &subg)
791 {
792   // This runtime_error will be removed if the one of backend supports this operation
793   ir::OperandIndexSequence inputs;
794   ir::OperandIndexSequence outputs;
795
796   loadOperationIO(op, inputs, outputs);
797
798   ir::operation::Pack::Param param;
799   const auto *options = op->builtin_options_as_PackOptions();
800   param.num = options->values_count();
801   param.axis = options->axis();
802
803   std::unique_ptr<ir::Operation> new_op(new ir::operation::Pack(inputs, outputs, param));
804   subg.addOperation(std::move(new_op));
805 }
806
807 template <typename LoaderDomain, typename SpecificLoader>
808 void BaseLoader<LoaderDomain, SpecificLoader>::loadRelu(const Operator *op, ir::Graph &subg)
809 {
810   ir::OperandIndexSequence inputs;
811   ir::OperandIndexSequence outputs;
812
813   loadOperationIO(op, inputs, outputs);
814
815   std::unique_ptr<ir::Operation> new_op(new ir::operation::ReLU(inputs, outputs));
816   subg.addOperation(std::move(new_op));
817 }
818
819 template <typename LoaderDomain, typename SpecificLoader>
820 void BaseLoader<LoaderDomain, SpecificLoader>::loadRelu6(const Operator *op, ir::Graph &subg)
821 {
822   ir::OperandIndexSequence inputs;
823   ir::OperandIndexSequence outputs;
824
825   loadOperationIO(op, inputs, outputs);
826
827   std::unique_ptr<ir::Operation> new_op(new ir::operation::ReLU6(inputs, outputs));
828   subg.addOperation(std::move(new_op));
829 }
830
831 template <typename LoaderDomain, typename SpecificLoader>
832 void BaseLoader<LoaderDomain, SpecificLoader>::loadResizeBilinear(const Operator *op,
833                                                                   ir::Graph &subg)
834 {
835   ir::OperandIndexSequence inputs;
836   ir::OperandIndexSequence outputs;
837
838   loadOperationIO(op, inputs, outputs);
839   auto input = inputs.at(0);
840   auto size = inputs.at(1);
841
842   // FIXME Handle ResizeBilinearOptions.
843   if (!subg.operands().at(size).isConstant())
844     throw std::runtime_error("ResizeBilinear: non-constant 'size' is not supported.");
845
846   std::vector<std::int32_t> size_v = subg.operands().at(size).template asVector<std::int32_t>();
847
848   ir::operation::ResizeBilinear::Param param;
849   param.height_out = size_v[0];
850   param.width_out = size_v[1];
851   param.align_corners = op->builtin_options_as_ResizeBilinearOptions()->align_corners();
852   param.half_pixel_centers = op->builtin_options_as_ResizeBilinearOptions()->half_pixel_centers();
853
854   std::unique_ptr<ir::Operation> new_op(new ir::operation::ResizeBilinear({input}, outputs, param));
855   subg.addOperation(std::move(new_op));
856 }
857
858 template <typename LoaderDomain, typename SpecificLoader>
859 void BaseLoader<LoaderDomain, SpecificLoader>::loadRsqrt(const Operator *op, ir::Graph &subg)
860 {
861   ir::OperandIndexSequence inputs;
862   ir::OperandIndexSequence outputs;
863
864   loadOperationIO(op, inputs, outputs);
865
866   std::unique_ptr<ir::Operation> new_op(new ir::operation::RSQRT(inputs, outputs));
867   subg.addOperation(std::move(new_op));
868 }
869
870 template <typename LoaderDomain, typename SpecificLoader>
871 void BaseLoader<LoaderDomain, SpecificLoader>::loadSelect(const Operator *op, ir::Graph &subg)
872 {
873   ir::OperandIndexSequence inputs;
874   ir::OperandIndexSequence outputs;
875
876   loadOperationIO(op, inputs, outputs);
877
878   std::unique_ptr<ir::Operation> new_op(new ir::operation::Select(inputs, outputs));
879   subg.addOperation(std::move(new_op));
880 }
881
882 template <typename LoaderDomain, typename SpecificLoader>
883 void BaseLoader<LoaderDomain, SpecificLoader>::loadSqrt(const Operator *op, ir::Graph &subg)
884 {
885   ir::OperandIndexSequence inputs;
886   ir::OperandIndexSequence outputs;
887
888   loadOperationIO(op, inputs, outputs);
889
890   std::unique_ptr<ir::Operation> new_op(new ir::operation::SQRT(inputs, outputs));
891   subg.addOperation(std::move(new_op));
892 }
893
894 template <typename LoaderDomain, typename SpecificLoader>
895 void BaseLoader<LoaderDomain, SpecificLoader>::loadSquaredDifference(const Operator *op,
896                                                                      ir::Graph &subg)
897 {
898   ir::OperandIndexSequence inputs;
899   ir::OperandIndexSequence outputs;
900
901   loadOperationIO(op, inputs, outputs);
902
903   std::unique_ptr<ir::Operation> new_op(new ir::operation::SquaredDifference(inputs, outputs));
904   subg.addOperation(std::move(new_op));
905 }
906
907 template <typename LoaderDomain, typename SpecificLoader>
908 void BaseLoader<LoaderDomain, SpecificLoader>::loadTanh(const Operator *op, ir::Graph &subg)
909 {
910   ir::OperandIndexSequence inputs;
911   ir::OperandIndexSequence outputs;
912
913   loadOperationIO(op, inputs, outputs);
914
915   std::unique_ptr<ir::Operation> new_op(new ir::operation::Tanh(inputs, outputs));
916   subg.addOperation(std::move(new_op));
917 }
918
919 template <typename LoaderDomain, typename SpecificLoader>
920 void BaseLoader<LoaderDomain, SpecificLoader>::loadTranspose(const Operator *op, ir::Graph &subg)
921 {
922   ir::OperandIndexSequence inputs;
923   ir::OperandIndexSequence outputs;
924
925   loadOperationIO(op, inputs, outputs);
926   auto input = inputs.at(0);
927   auto perm = inputs.at(1);
928
929   if (!subg.operands().at(perm).isConstant())
930     throw std::runtime_error("Transpose: non-constant 'perm' is not supported.");
931
932   ir::operation::Transpose::Param param;
933   param.perm = subg.operands().at(perm).template asVector<int>();
934
935   std::unique_ptr<ir::Operation> new_op(new ir::operation::Transpose({input}, outputs, param));
936   subg.addOperation(std::move(new_op));
937 }
938
939 template <typename LoaderDomain, typename SpecificLoader>
940 void BaseLoader<LoaderDomain, SpecificLoader>::loadReduce(
941     const Operator *op, ir::Graph &subg, ir::operation::Reduce::ReduceType reduce_type)
942 {
943   ir::OperandIndexSequence inputs;
944   ir::OperandIndexSequence outputs;
945
946   loadOperationIO(op, inputs, outputs);
947
948   ir::operation::Reduce::Param param;
949   param.reduce_type = reduce_type;
950   param.keep_dims = op->builtin_options_as_ReducerOptions()->keep_dims();
951
952   std::unique_ptr<ir::Operation> new_op(new ir::operation::Reduce(inputs, outputs, param));
953   subg.addOperation(std::move(new_op));
954 }
955
956 template <typename LoaderDomain, typename SpecificLoader>
957 void BaseLoader<LoaderDomain, SpecificLoader>::loadReduceAll(const Operator *op, ir::Graph &subg)
958 {
959   ir::OperandIndexSequence inputs;
960   ir::OperandIndexSequence outputs;
961
962   loadOperationIO(op, inputs, outputs);
963
964   ir::operation::Reduce::Param param;
965   param.reduce_type = ir::operation::Reduce::ReduceType::ALL;
966   if (op->custom_options() == nullptr)
967   {
968     param.keep_dims = false;
969   }
970   else
971   {
972     size_t custom_op_data_size = op->custom_options()->size();
973     auto custom_op_data = op->custom_options()->Data();
974     auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
975     auto attr_map = data_root.AsMap();
976     param.keep_dims = attr_map["keep_dims"].AsBool();
977   }
978
979   std::unique_ptr<ir::Operation> new_op(new ir::operation::Reduce(inputs, outputs, param));
980   subg.addOperation(std::move(new_op));
981 }
982
983 template <typename LoaderDomain, typename SpecificLoader>
984 void BaseLoader<LoaderDomain, SpecificLoader>::loadReverseV2(const Operator *op, ir::Graph &subg)
985 {
986   ir::OperandIndexSequence inputs;
987   ir::OperandIndexSequence outputs;
988
989   loadOperationIO(op, inputs, outputs);
990
991   std::unique_ptr<ir::Operation> new_op(new ir::operation::Reverse(inputs, outputs));
992   subg.addOperation(std::move(new_op));
993 }
994
995 template <typename LoaderDomain, typename SpecificLoader>
996 void BaseLoader<LoaderDomain, SpecificLoader>::loadPad(const Operator *op, ir::Graph &subg)
997 {
998   ir::OperandIndexSequence inputs;
999   ir::OperandIndexSequence outputs;
1000
1001   loadOperationIO(op, inputs, outputs);
1002
1003   std::unique_ptr<ir::Operation> new_op(new ir::operation::Pad(inputs, outputs));
1004   subg.addOperation(std::move(new_op));
1005 }
1006
1007 template <typename LoaderDomain, typename SpecificLoader>
1008 void BaseLoader<LoaderDomain, SpecificLoader>::loadLogistic(const Operator *op, ir::Graph &subg)
1009 {
1010   ir::OperandIndexSequence inputs;
1011   ir::OperandIndexSequence outputs;
1012
1013   loadOperationIO(op, inputs, outputs);
1014
1015   std::unique_ptr<ir::Operation> new_op(new ir::operation::Logistic(inputs, outputs));
1016   subg.addOperation(std::move(new_op));
1017 }
1018
1019 template <typename LoaderDomain, typename SpecificLoader>
1020 void BaseLoader<LoaderDomain, SpecificLoader>::loadExp(const Operator *op, ir::Graph &subg)
1021 {
1022   ir::OperandIndexSequence inputs;
1023   ir::OperandIndexSequence outputs;
1024
1025   loadOperationIO(op, inputs, outputs);
1026
1027   std::unique_ptr<ir::Operation> new_op(new ir::operation::Exp(inputs, outputs));
1028   subg.addOperation(std::move(new_op));
1029 }
1030
1031 template <typename LoaderDomain, typename SpecificLoader>
1032 void BaseLoader<LoaderDomain, SpecificLoader>::loadExpandDims(const Operator *op, ir::Graph &subg)
1033 {
1034   ir::OperandIndexSequence inputs;
1035   ir::OperandIndexSequence outputs;
1036
1037   loadOperationIO(op, inputs, outputs);
1038
1039   std::unique_ptr<ir::Operation> new_op(new ir::operation::ExpandDims(inputs, outputs));
1040   subg.addOperation(std::move(new_op));
1041 }
1042
1043 template <typename LoaderDomain, typename SpecificLoader>
1044 void BaseLoader<LoaderDomain, SpecificLoader>::loadGather(const Operator *op, ir::Graph &subg)
1045 {
1046   ir::OperandIndexSequence inputs;
1047   ir::OperandIndexSequence outputs;
1048
1049   loadOperationIO(op, inputs, outputs);
1050   ir::operation::Gather::Param param;
1051   param.axis = op->builtin_options_as_GatherOptions()->axis();
1052
1053   std::unique_ptr<ir::Operation> new_op(new ir::operation::Gather(inputs, outputs, param));
1054   subg.addOperation(std::move(new_op));
1055 }
1056
1057 template <typename LoaderDomain, typename SpecificLoader>
1058 void BaseLoader<LoaderDomain, SpecificLoader>::loadSpaceToBatchND(const Operator *op,
1059                                                                   ir::Graph &subg)
1060 {
1061   ir::OperandIndexSequence inputs;
1062   ir::OperandIndexSequence outputs;
1063
1064   loadOperationIO(op, inputs, outputs);
1065
1066   std::unique_ptr<ir::Operation> new_op{new ir::operation::SpaceToBatchND{inputs, outputs}};
1067   subg.addOperation(std::move(new_op));
1068 }
1069
1070 template <typename LoaderDomain, typename SpecificLoader>
1071 void BaseLoader<LoaderDomain, SpecificLoader>::loadBatchMatMul(const Operator *op, ir::Graph &subg)
1072 {
1073   ir::OperandIndexSequence inputs;
1074   ir::OperandIndexSequence outputs;
1075
1076   loadOperationIO(op, inputs, outputs);
1077   ir::operation::BatchMatMul::Param param;
1078
1079   const auto builtin_op = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
1080
1081   switch (builtin_op)
1082   {
1083     case BuiltinOperator::BuiltinOperator_BATCH_MATMUL:
1084       param.adj_x = op->builtin_options_as_BatchMatMulOptions()->adjoint_lhs();
1085       param.adj_y = op->builtin_options_as_BatchMatMulOptions()->adjoint_rhs();
1086       break;
1087     case BuiltinOperator::BuiltinOperator_CUSTOM:
1088       if (op->custom_options() == nullptr)
1089       {
1090         param.adj_x = false;
1091         param.adj_y = false;
1092       }
1093       else
1094       {
1095         size_t custom_op_data_size = op->custom_options()->size();
1096         auto custom_op_data = op->custom_options()->Data();
1097         auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
1098         auto attr_map = data_root.AsMap();
1099         param.adj_x = attr_map["adj_x"].AsBool();
1100         param.adj_y = attr_map["adj_y"].AsBool();
1101       }
1102       break;
1103     default:
1104       throw std::runtime_error(
1105           std::string("Wrong loaded operation: ").append(EnumNameBuiltinOperator(builtin_op)) +
1106           " as " + EnumNameBuiltinOperator(BuiltinOperator::BuiltinOperator_BATCH_MATMUL));
1107   }
1108
1109   std::unique_ptr<ir::Operation> new_op{new ir::operation::BatchMatMul{inputs, outputs, param}};
1110   subg.addOperation(std::move(new_op));
1111 }
1112
1113 template <typename LoaderDomain, typename SpecificLoader>
1114 void BaseLoader<LoaderDomain, SpecificLoader>::loadBatchToSpaceND(const Operator *op,
1115                                                                   ir::Graph &subg)
1116 {
1117   ir::OperandIndexSequence inputs;
1118   ir::OperandIndexSequence outputs;
1119
1120   loadOperationIO(op, inputs, outputs);
1121
1122   std::unique_ptr<ir::Operation> new_op{new ir::operation::BatchToSpaceND{inputs, outputs}};
1123   subg.addOperation(std::move(new_op));
1124 }
1125
1126 template <typename LoaderDomain, typename SpecificLoader>
1127 void BaseLoader<LoaderDomain, SpecificLoader>::loadMatrixBandPart(const Operator *op,
1128                                                                   ir::Graph &subg)
1129 {
1130   ir::OperandIndexSequence inputs;
1131   ir::OperandIndexSequence outputs;
1132
1133   loadOperationIO(op, inputs, outputs);
1134
1135   std::unique_ptr<ir::Operation> new_op(new ir::operation::MatrixBandPart(inputs, outputs));
1136   subg.addOperation(std::move(new_op));
1137 }
1138
1139 template <typename LoaderDomain, typename SpecificLoader>
1140 void BaseLoader<LoaderDomain, SpecificLoader>::loadBroadcastTo(const Operator *op, ir::Graph &subg)
1141 {
1142   ir::OperandIndexSequence inputs;
1143   ir::OperandIndexSequence outputs;
1144
1145   loadOperationIO(op, inputs, outputs);
1146
1147   std::unique_ptr<ir::Operation> new_op(new ir::operation::BroadcastTo(inputs, outputs));
1148   subg.addOperation(std::move(new_op));
1149 }
1150 template <typename LoaderDomain, typename SpecificLoader>
1151 void BaseLoader<LoaderDomain, SpecificLoader>::loadSpaceToDepth(const Operator *op, ir::Graph &subg)
1152 {
1153   ir::OperandIndexSequence inputs;
1154   ir::OperandIndexSequence outputs;
1155   ir::operation::SpaceToDepth::Param param;
1156
1157   const auto *options = op->builtin_options_as_SpaceToDepthOptions();
1158
1159   param.block_size = options->block_size();
1160
1161   loadOperationIO(op, inputs, outputs);
1162
1163   std::unique_ptr<ir::Operation> new_op(new ir::operation::SpaceToDepth(inputs, outputs, param));
1164   subg.addOperation(std::move(new_op));
1165 }
1166
1167 template <typename LoaderDomain, typename SpecificLoader>
1168 void BaseLoader<LoaderDomain, SpecificLoader>::loadStatelessRandomUniform(const Operator *op,
1169                                                                           ir::Graph &subg)
1170 {
1171   ir::OperandIndexSequence inputs;
1172   ir::OperandIndexSequence outputs;
1173   loadOperationIO(op, inputs, outputs);
1174
1175   std::unique_ptr<ir::Operation> new_op(new ir::operation::StatelessRandomUniform(inputs, outputs));
1176   subg.addOperation(std::move(new_op));
1177 }
1178
1179 template <typename LoaderDomain, typename SpecificLoader>
1180 void BaseLoader<LoaderDomain, SpecificLoader>::loadCustom(const Operator *op, ir::Graph &subg)
1181 {
1182   ir::OperandIndexSequence inputs;
1183   ir::OperandIndexSequence outputs;
1184
1185   assert(op->custom_options_format() == CustomOptionsFormat::CustomOptionsFormat_FLEXBUFFERS &&
1186          "Unsupported custom operation options format");
1187
1188   auto *op_code = _model->operator_codes()->Get(op->opcode_index());
1189   auto custom_op_name = op_code->custom_code()->str();
1190
1191   enum class BuiltinOP
1192   {
1193     AddV2,
1194     ReduceAll,
1195     MatrixBandPart,
1196     BatchMatMul,
1197     Einsum,
1198     BroadcastTo,
1199     FusedBatchNorm,
1200     StatelessRandomUniform
1201   };
1202
1203   // Mapping from custom op name string to BuiltinOP enum
1204   std::map<std::string, BuiltinOP> builtin_map = {
1205       {"AddV2", BuiltinOP::AddV2},
1206       {"All", BuiltinOP::ReduceAll},
1207       {"MatrixBandPart", BuiltinOP::MatrixBandPart},
1208       {"BatchMatMulV2", BuiltinOP::BatchMatMul},
1209       {"Einsum", BuiltinOP::Einsum},
1210       {"FusedBatchNormV3", BuiltinOP::FusedBatchNorm},
1211       {"BroadcastTo", BuiltinOP::BroadcastTo},
1212       {"StatelessRandomUniform", BuiltinOP::StatelessRandomUniform},
1213   };
1214
1215   try
1216   {
1217     // Throw out_of_range if it is unknown custom op
1218     auto custom_op_id = builtin_map.at(custom_op_name);
1219     switch (custom_op_id)
1220     {
1221       case BuiltinOP::AddV2:
1222         loadAdd(op, subg);
1223         break;
1224       case BuiltinOP::ReduceAll:
1225         loadReduceAll(op, subg);
1226         break;
1227       case BuiltinOP::MatrixBandPart:
1228         loadMatrixBandPart(op, subg);
1229         break;
1230       case BuiltinOP::BatchMatMul:
1231         loadBatchMatMul(op, subg);
1232         break;
1233       case BuiltinOP::Einsum:
1234         loadEinsum(op, subg);
1235         break;
1236       case BuiltinOP::BroadcastTo:
1237         loadBroadcastTo(op, subg);
1238         break;
1239       case BuiltinOP::FusedBatchNorm:
1240         loadFusedBatchNorm(op, subg);
1241         break;
1242       case BuiltinOP::StatelessRandomUniform:
1243         loadStatelessRandomUniform(op, subg);
1244         break;
1245       default:
1246         throw std::runtime_error{
1247             "Loader: Custom OP map is defined but operation loader function is not defined"};
1248     }
1249
1250     return;
1251   }
1252   catch (...)
1253   {
1254     loadOperationIO(op, inputs, outputs);
1255
1256     auto constraint = ir::OperandConstraint::createExact(inputs.size());
1257
1258     size_t custom_op_data_size = op->custom_options()->size();
1259     auto custom_op_data = new char[custom_op_data_size];
1260     std::copy(op->custom_options()->begin(), op->custom_options()->end(), custom_op_data);
1261
1262     ir::operation::Custom::Userdata userdata{};
1263     userdata.data = custom_op_data;
1264     userdata.size = custom_op_data_size;
1265
1266     auto new_op = std::make_unique<ir::operation::Custom>(constraint, inputs, outputs,
1267                                                           custom_op_name, userdata);
1268
1269     subg.addOperation(std::move(new_op));
1270   }
1271 }
1272
1273 template <typename LoaderDomain, typename SpecificLoader>
1274 void BaseLoader<LoaderDomain, SpecificLoader>::loadSqueeze(const Operator *op, ir::Graph &subg)
1275 {
1276   ir::OperandIndexSequence inputs;
1277   ir::OperandIndexSequence outputs;
1278
1279   loadOperationIO(op, inputs, outputs);
1280
1281   ir::operation::Squeeze::Param param{};
1282   const auto *options = op->builtin_options_as_SqueezeOptions();
1283   const auto *dims = options->squeeze_dims();
1284   if (dims)
1285   {
1286     if (dims->Length() > sizeof(param.dims) / sizeof(param.dims[0]))
1287       throw std::runtime_error("Squeeze: 'param.ndims' is out of range.");
1288     param.ndim = dims->Length();
1289     for (int i = 0; i < param.ndim; ++i)
1290       param.dims[i] = dims->Get(i);
1291   }
1292
1293   std::unique_ptr<ir::Operation> new_op(new ir::operation::Squeeze(inputs, outputs, param));
1294   subg.addOperation(std::move(new_op));
1295 }
1296
1297 template <typename LoaderDomain, typename SpecificLoader>
1298 void BaseLoader<LoaderDomain, SpecificLoader>::loadPrelu(const Operator *op, ir::Graph &subg)
1299 {
1300   ir::OperandIndexSequence inputs;
1301   ir::OperandIndexSequence outputs;
1302
1303   loadOperationIO(op, inputs, outputs);
1304
1305   std::unique_ptr<ir::Operation> new_op(new ir::operation::PReLU(inputs, outputs));
1306   subg.addOperation(std::move(new_op));
1307 }
1308
1309 template <typename LoaderDomain, typename SpecificLoader>
1310 void BaseLoader<LoaderDomain, SpecificLoader>::loadSplit(const Operator *op, ir::Graph &subg)
1311 {
1312   ir::OperandIndexSequence inputs;
1313   ir::OperandIndexSequence outputs;
1314
1315   loadOperationIO(op, inputs, outputs);
1316   // Notice : input order is strange for tflite split
1317   auto input = inputs.at(1);
1318   auto axis = inputs.at(0);
1319
1320   // FIXME Handle SplitOptions.
1321   if (!subg.operands().at(axis).isConstant())
1322     throw std::runtime_error("Split: non-constant 'axis' is not supported.");
1323
1324   ir::operation::Split::Param param{};
1325   param.axis = subg.operands().at(axis).template asScalar<int>();
1326   const auto *options = op->builtin_options_as_SplitOptions();
1327   param.num_splits = options->num_splits();
1328
1329   std::unique_ptr<ir::Operation> new_op(new ir::operation::Split({input}, outputs, param));
1330   subg.addOperation(std::move(new_op));
1331 }
1332
1333 template <typename LoaderDomain, typename SpecificLoader>
1334 void BaseLoader<LoaderDomain, SpecificLoader>::loadSplitV(const Operator *op, ir::Graph &subg)
1335 {
1336   ir::OperandIndexSequence inputs;
1337   ir::OperandIndexSequence outputs;
1338
1339   loadOperationIO(op, inputs, outputs);
1340
1341   ir::operation::SplitV::Param param{};
1342
1343   const auto *options = op->builtin_options_as_SplitVOptions();
1344   param.num_splits = options->num_splits();
1345
1346   std::unique_ptr<ir::Operation> new_op(new ir::operation::SplitV(inputs, outputs, param));
1347   subg.addOperation(std::move(new_op));
1348 }
1349
1350 template <typename LoaderDomain, typename SpecificLoader>
1351 void BaseLoader<LoaderDomain, SpecificLoader>::loadSlice(const Operator *op, ir::Graph &subg)
1352 {
1353   ir::OperandIndexSequence inputs;
1354   ir::OperandIndexSequence outputs;
1355
1356   loadOperationIO(op, inputs, outputs);
1357
1358   std::unique_ptr<ir::Operation> new_op{new ir::operation::Slice{inputs, outputs}};
1359   subg.addOperation(std::move(new_op));
1360 }
1361
1362 template <typename LoaderDomain, typename SpecificLoader>
1363 void BaseLoader<LoaderDomain, SpecificLoader>::loadStridedSlice(const Operator *op, ir::Graph &subg)
1364 {
1365   ir::OperandIndexSequence inputs;
1366   ir::OperandIndexSequence outputs;
1367
1368   loadOperationIO(op, inputs, outputs);
1369
1370   ir::operation::StridedSlice::Param param;
1371
1372   const auto *options = op->builtin_options_as_StridedSliceOptions();
1373   param.begin_mask = options->begin_mask();
1374   param.end_mask = options->end_mask();
1375   param.shrink_axis_mask = options->shrink_axis_mask();
1376
1377   std::unique_ptr<ir::Operation> new_op{new ir::operation::StridedSlice{inputs, outputs, param}};
1378   subg.addOperation(std::move(new_op));
1379 }
1380
1381 template <typename LoaderDomain, typename SpecificLoader>
1382 void BaseLoader<LoaderDomain, SpecificLoader>::loadUnpack(const Operator *op, ir::Graph &subg)
1383 {
1384   ir::OperandIndexSequence inputs;
1385   ir::OperandIndexSequence outputs;
1386
1387   loadOperationIO(op, inputs, outputs);
1388
1389   ir::operation::Unpack::Param param;
1390   const auto *options = op->builtin_options_as_UnpackOptions();
1391   param.num = options->num();
1392   param.axis = options->axis();
1393
1394   std::unique_ptr<ir::Operation> new_op(new ir::operation::Unpack(inputs, outputs, param));
1395   subg.addOperation(std::move(new_op));
1396 }
1397
1398 template <typename LoaderDomain, typename SpecificLoader>
1399 void BaseLoader<LoaderDomain, SpecificLoader>::loadMinimum(const Operator *op, ir::Graph &subg)
1400 {
1401   ir::OperandIndexSequence inputs;
1402   ir::OperandIndexSequence outputs;
1403
1404   loadOperationIO(op, inputs, outputs);
1405
1406   std::unique_ptr<ir::Operation> new_op(new ir::operation::Min(inputs, outputs));
1407   subg.addOperation(std::move(new_op));
1408 }
1409
1410 template <typename LoaderDomain, typename SpecificLoader>
1411 void BaseLoader<LoaderDomain, SpecificLoader>::loadMaximum(const Operator *op, ir::Graph &subg)
1412 {
1413   ir::OperandIndexSequence inputs;
1414   ir::OperandIndexSequence outputs;
1415
1416   loadOperationIO(op, inputs, outputs);
1417
1418   std::unique_ptr<ir::Operation> new_op(new ir::operation::Max(inputs, outputs));
1419   subg.addOperation(std::move(new_op));
1420 }
1421
1422 template <typename LoaderDomain, typename SpecificLoader>
1423 void BaseLoader<LoaderDomain, SpecificLoader>::loadCast(const Operator *op, ir::Graph &subg)
1424 {
1425   ir::OperandIndexSequence inputs;
1426   ir::OperandIndexSequence outputs;
1427
1428   loadOperationIO(op, inputs, outputs);
1429
1430   auto qasymm8ToUint8 = [](ir::Operand &operand) {
1431     if (operand.typeInfo().type() == ir::DataType::QUANT_UINT8_ASYMM)
1432     {
1433       operand.type(ir::DataType::UINT8);
1434     }
1435   };
1436   qasymm8ToUint8(subg.operands().at(inputs.at(ir::operation::Cast::Input::INPUT)));
1437   qasymm8ToUint8(subg.operands().at(outputs.at(0)));
1438
1439   std::unique_ptr<ir::Operation> new_op(new ir::operation::Cast(inputs, outputs));
1440   subg.addOperation(std::move(new_op));
1441 }
1442
1443 template <typename LoaderDomain, typename SpecificLoader>
1444 void BaseLoader<LoaderDomain, SpecificLoader>::loadComparison(const Operator *op, ir::Graph &subg)
1445 {
1446   ir::OperandIndexSequence inputs;
1447   ir::OperandIndexSequence outputs;
1448
1449   loadOperationIO(op, inputs, outputs);
1450
1451   ir::operation::Comparison::Param param;
1452
1453   const auto builtin_op = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
1454
1455   switch (builtin_op)
1456   {
1457     case BuiltinOperator::BuiltinOperator_EQUAL:
1458       param.comparison_type = ir::operation::Comparison::ComparisonType::Equal;
1459       break;
1460     case BuiltinOperator::BuiltinOperator_NOT_EQUAL:
1461       param.comparison_type = ir::operation::Comparison::ComparisonType::NotEqual;
1462       break;
1463     case BuiltinOperator::BuiltinOperator_GREATER_EQUAL:
1464       param.comparison_type = ir::operation::Comparison::ComparisonType::GreaterEqual;
1465       break;
1466     case BuiltinOperator::BuiltinOperator_GREATER:
1467       param.comparison_type = ir::operation::Comparison::ComparisonType::Greater;
1468       break;
1469     case BuiltinOperator::BuiltinOperator_LESS_EQUAL:
1470       param.comparison_type = ir::operation::Comparison::ComparisonType::LessEqual;
1471       break;
1472     case BuiltinOperator::BuiltinOperator_LESS:
1473       param.comparison_type = ir::operation::Comparison::ComparisonType::Less;
1474       break;
1475     default:
1476       throw std::runtime_error(
1477           std::string("Unsupported operation: ").append(EnumNameBuiltinOperator(builtin_op)));
1478   }
1479
1480   std::unique_ptr<ir::Operation> new_op(new ir::operation::Comparison(inputs, outputs, param));
1481   subg.addOperation(std::move(new_op));
1482 }
1483
1484 template <typename LoaderDomain, typename SpecificLoader>
1485 void BaseLoader<LoaderDomain, SpecificLoader>::loadEinsum(const Operator *op, ir::Graph &subg)
1486 {
1487   ir::OperandIndexSequence inputs;
1488   ir::OperandIndexSequence outputs;
1489
1490   loadOperationIO(op, inputs, outputs);
1491   ir::operation::Einsum::Param param;
1492
1493   if (inputs.size() != 2)
1494   {
1495     throw std::runtime_error{"Einsum: NYI input - only support two inputs"};
1496   }
1497
1498   if (op->custom_options() == nullptr)
1499   {
1500     throw std::runtime_error{"Einsum: empty equation"};
1501   }
1502   else
1503   {
1504     size_t custom_op_data_size = op->custom_options()->size();
1505     auto custom_op_data = op->custom_options()->Data();
1506     auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
1507     auto attr_map = data_root.AsMap();
1508     param.equation = attr_map["equation"].ToString();
1509   }
1510
1511   std::unique_ptr<ir::Operation> new_op{new ir::operation::Einsum{inputs, outputs, param}};
1512   subg.addOperation(std::move(new_op));
1513 }
1514 template <typename LoaderDomain, typename SpecificLoader>
1515 void BaseLoader<LoaderDomain, SpecificLoader>::loadFusedBatchNorm(const Operator *op,
1516                                                                   ir::Graph &subg)
1517 {
1518   ir::OperandIndexSequence inputs;
1519   ir::OperandIndexSequence outputs;
1520
1521   loadOperationIO(op, inputs, outputs);
1522   ir::operation::FusedBatchNorm::Param param;
1523
1524   if (inputs.size() != 5)
1525   {
1526     throw std::runtime_error{"FusedBatchNorm: NYI input - only support five inputs"};
1527   }
1528
1529   if (op->custom_options() == nullptr)
1530   {
1531     throw std::runtime_error{"FusedBatchNorm: empty option"};
1532   }
1533   else
1534   {
1535     size_t custom_op_data_size = op->custom_options()->size();
1536     auto custom_op_data = op->custom_options()->Data();
1537     auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
1538     auto attr_map = data_root.AsMap();
1539     param.is_training = attr_map["is_training"].AsBool();
1540     param.epsilon = attr_map["epsilon"].AsFloat();
1541     param.data_format = attr_map["data_format"].ToString();
1542   }
1543
1544   std::unique_ptr<ir::Operation> new_op{new ir::operation::FusedBatchNorm{inputs, outputs, param}};
1545   subg.addOperation(std::move(new_op));
1546 }
1547
1548 template <typename LoaderDomain, typename SpecificLoader>
1549 void BaseLoader<LoaderDomain, SpecificLoader>::loadOneHot(const Operator *op, ir::Graph &subg)
1550 {
1551   if (op->inputs()->size() != 4 || op->outputs()->size() != 1)
1552     throw std::runtime_error("OneHot Op has wrong number of input or output tensors.");
1553
1554   // Set input and output tensors
1555   ir::OperandIndexSequence inputs, outputs;
1556   loadOperationIO(op, inputs, outputs);
1557
1558   // Set parameter
1559   const auto axis = op->builtin_options_as_OneHotOptions()->axis();
1560   std::unique_ptr<ir::Operation> new_op(new ir::operation::OneHot(inputs, outputs, {axis}));
1561   subg.addOperation(std::move(new_op));
1562 }
1563
1564 template <typename LoaderDomain, typename SpecificLoader>
1565 void BaseLoader<LoaderDomain, SpecificLoader>::loadAbs(const Operator *op, ir::Graph &subg)
1566 {
1567   ir::OperandIndexSequence inputs;
1568   ir::OperandIndexSequence outputs;
1569
1570   loadOperationIO(op, inputs, outputs);
1571
1572   std::unique_ptr<ir::Operation> new_op(new ir::operation::Abs(inputs, outputs));
1573   subg.addOperation(std::move(new_op));
1574 }
1575
1576 template <typename LoaderDomain, typename SpecificLoader>
1577 void BaseLoader<LoaderDomain, SpecificLoader>::loadCos(const Operator *op, ir::Graph &subg)
1578 {
1579   ir::OperandIndexSequence inputs;
1580   ir::OperandIndexSequence outputs;
1581
1582   loadOperationIO(op, inputs, outputs);
1583
1584   std::unique_ptr<ir::Operation> new_op(new ir::operation::Cos(inputs, outputs));
1585   subg.addOperation(std::move(new_op));
1586 }
1587
1588 template <typename LoaderDomain, typename SpecificLoader>
1589 void BaseLoader<LoaderDomain, SpecificLoader>::loadSin(const Operator *op, ir::Graph &subg)
1590 {
1591   ir::OperandIndexSequence inputs;
1592   ir::OperandIndexSequence outputs;
1593
1594   loadOperationIO(op, inputs, outputs);
1595
1596   std::unique_ptr<ir::Operation> new_op(new ir::operation::Sin(inputs, outputs));
1597   subg.addOperation(std::move(new_op));
1598 }
1599
1600 template <typename LoaderDomain, typename SpecificLoader>
1601 void BaseLoader<LoaderDomain, SpecificLoader>::loadShape(const Operator *op, ir::Graph &subg)
1602 {
1603   ir::OperandIndexSequence inputs;
1604   ir::OperandIndexSequence outputs;
1605
1606   loadOperationIO(op, inputs, outputs);
1607
1608   // ir::operation::Shape::Param param;
1609   // const auto *options = op->builtin_options_as_ShapeOptions();
1610   // param.out_type = tensorTypeToDataType(options->out_type());
1611
1612   std::unique_ptr<ir::Operation> new_op(new ir::operation::Shape(inputs, outputs /*, param*/));
1613   subg.addOperation(std::move(new_op));
1614 }
1615
1616 template <typename LoaderDomain, typename SpecificLoader>
1617 void BaseLoader<LoaderDomain, SpecificLoader>::loadIf(const Operator *op, ir::Graph &subg)
1618 {
1619   ir::OperandIndexSequence inputs;
1620   ir::OperandIndexSequence outputs;
1621
1622   loadOperationIO(op, inputs, outputs);
1623
1624   ir::operation::If::Param param;
1625   const auto *options = op->builtin_options_as_IfOptions();
1626   const uint32_t then_index = options->then_subgraph_index();
1627   const uint32_t else_index = options->else_subgraph_index();
1628   param.then_subg_index = ir::SubgraphIndex{then_index};
1629   param.else_subg_index = ir::SubgraphIndex{else_index};
1630
1631   std::unique_ptr<ir::Operation> new_op(new ir::operation::If(inputs, outputs, param));
1632   subg.addOperation(std::move(new_op));
1633 }
1634
1635 template <typename LoaderDomain, typename SpecificLoader>
1636 void BaseLoader<LoaderDomain, SpecificLoader>::loadWhile(const Operator *op, ir::Graph &subg)
1637 {
1638   ir::OperandIndexSequence inputs;
1639   ir::OperandIndexSequence outputs;
1640
1641   loadOperationIO(op, inputs, outputs);
1642
1643   ir::operation::While::Param param;
1644   const auto *options = op->builtin_options_as_WhileOptions();
1645   const uint32_t cond_index = options->cond_subgraph_index();
1646   const uint32_t body_index = options->body_subgraph_index();
1647   param.cond_subg_index = ir::SubgraphIndex{cond_index};
1648   param.body_subg_index = ir::SubgraphIndex{body_index};
1649
1650   std::unique_ptr<ir::Operation> new_op(new ir::operation::While(inputs, outputs, param));
1651   subg.addOperation(std::move(new_op));
1652 }
1653
1654 template <typename LoaderDomain, typename SpecificLoader>
1655 void BaseLoader<LoaderDomain, SpecificLoader>::loadNeg(const Operator *op, ir::Graph &subg)
1656 {
1657   ir::OperandIndexSequence inputs;
1658   ir::OperandIndexSequence outputs;
1659
1660   loadOperationIO(op, inputs, outputs);
1661
1662   std::unique_ptr<ir::Operation> new_op(new ir::operation::Neg(inputs, outputs));
1663   subg.addOperation(std::move(new_op));
1664 }
1665
1666 template <typename LoaderDomain, typename SpecificLoader>
1667 void BaseLoader<LoaderDomain, SpecificLoader>::loadArgMax(const Operator *op, ir::Graph &subg)
1668 {
1669   ir::OperandIndexSequence inputs;
1670   ir::OperandIndexSequence outputs;
1671
1672   loadOperationIO(op, inputs, outputs);
1673
1674   auto inputOperand = subg.operands().at(inputs.at(0));
1675   auto axisOperand = subg.operands().at(inputs.at(1));
1676
1677   if (!axisOperand.isConstant())
1678     throw std::runtime_error("ArgMax: non-constant 'axis' is not supported.");
1679   if (!(axisOperand.operandSize() == 4 && (axisOperand.typeInfo().type() == ir::DataType::INT32 ||
1680                                            axisOperand.typeInfo().type() == ir::DataType::INT64)))
1681     throw std::runtime_error("ArgMax: `axis` with an int32 or int64 element is only supported.");
1682
1683   ir::operation::ArgMax::Param param;
1684   param.axis = axisOperand.template asVector<int>()[0];
1685   const auto output_type = op->builtin_options_as_ArgMaxOptions()->output_type();
1686   switch (output_type)
1687   {
1688     case TensorType::TensorType_INT32:
1689     case TensorType::TensorType_INT64:
1690       break;
1691     default:
1692       throw std::runtime_error("ArgMax: `output_type` must be either int32 or int64.");
1693   }
1694   param.output_type = tensorTypeToDataType(output_type);
1695   std::unique_ptr<ir::Operation> new_op(new ir::operation::ArgMax(inputs, outputs, param));
1696   subg.addOperation(std::move(new_op));
1697 }
1698
1699 template <typename LoaderDomain, typename SpecificLoader>
1700 void BaseLoader<LoaderDomain, SpecificLoader>::loadLog(const Operator *op, ir::Graph &subg)
1701 {
1702   ir::OperandIndexSequence inputs;
1703   ir::OperandIndexSequence outputs;
1704
1705   loadOperationIO(op, inputs, outputs);
1706
1707   std::unique_ptr<ir::Operation> new_op(new ir::operation::Log(inputs, outputs));
1708   subg.addOperation(std::move(new_op));
1709 }
1710
1711 template <typename LoaderDomain, typename SpecificLoader>
1712 void BaseLoader<LoaderDomain, SpecificLoader>::loadRound(const Operator *op, ir::Graph &subg)
1713 {
1714   ir::OperandIndexSequence inputs;
1715   ir::OperandIndexSequence outputs;
1716
1717   loadOperationIO(op, inputs, outputs);
1718
1719   std::unique_ptr<ir::Operation> new_op(new ir::operation::Round(inputs, outputs));
1720   subg.addOperation(std::move(new_op));
1721 }
1722
1723 template <typename LoaderDomain, typename SpecificLoader>
1724 void BaseLoader<LoaderDomain, SpecificLoader>::loadPow(const Operator *op, ir::Graph &subg)
1725 {
1726   ir::OperandIndexSequence inputs;
1727   ir::OperandIndexSequence outputs;
1728
1729   loadOperationIO(op, inputs, outputs);
1730
1731   std::unique_ptr<ir::Operation> new_op(new ir::operation::Pow(inputs, outputs));
1732   subg.addOperation(std::move(new_op));
1733 }
1734
1735 template <typename LoaderDomain, typename SpecificLoader>
1736 void BaseLoader<LoaderDomain, SpecificLoader>::loadLogicalNot(const Operator *op, ir::Graph &subg)
1737 {
1738   ir::OperandIndexSequence inputs;
1739   ir::OperandIndexSequence outputs;
1740
1741   loadOperationIO(op, inputs, outputs);
1742
1743   std::unique_ptr<ir::Operation> new_op(new ir::operation::LogicalNot(inputs, outputs));
1744   subg.addOperation(std::move(new_op));
1745 }
1746
1747 template <typename LoaderDomain, typename SpecificLoader>
1748 void BaseLoader<LoaderDomain, SpecificLoader>::loadZerosLike(const Operator *op, ir::Graph &subg)
1749 {
1750   ir::OperandIndexSequence inputs;
1751   ir::OperandIndexSequence outputs;
1752
1753   loadOperationIO(op, inputs, outputs);
1754
1755   std::unique_ptr<ir::Operation> new_op(new ir::operation::ZerosLike(inputs, outputs));
1756
1757   subg.addOperation(std::move(new_op));
1758 }
1759
1760 template <typename LoaderDomain, typename SpecificLoader>
1761 void BaseLoader<LoaderDomain, SpecificLoader>::loadRange(const Operator *op, ir::Graph &subg)
1762 {
1763   ir::OperandIndexSequence inputs;
1764   ir::OperandIndexSequence outputs;
1765
1766   loadOperationIO(op, inputs, outputs);
1767
1768   std::unique_ptr<ir::Operation> new_op(new ir::operation::Range(inputs, outputs));
1769   subg.addOperation(std::move(new_op));
1770 }
1771
1772 template <typename LoaderDomain, typename SpecificLoader>
1773 void BaseLoader<LoaderDomain, SpecificLoader>::loadTile(const Operator *op, ir::Graph &subg)
1774 {
1775   ir::OperandIndexSequence inputs;
1776   ir::OperandIndexSequence outputs;
1777
1778   loadOperationIO(op, inputs, outputs);
1779
1780   auto multiples = inputs.at(ir::operation::Tile::MULTIPLES);
1781
1782   if (!subg.operands().at(multiples).isConstant())
1783     throw std::runtime_error("Tile: non-constant 'multiples' is not supported.");
1784
1785   std::unique_ptr<ir::Operation> new_op(new ir::operation::Tile(inputs, outputs));
1786   subg.addOperation(std::move(new_op));
1787 }
1788
1789 template <typename LoaderDomain, typename SpecificLoader>
1790 void BaseLoader<LoaderDomain, SpecificLoader>::loadLogicalOr(const Operator *op, ir::Graph &subg)
1791 {
1792   ir::OperandIndexSequence inputs;
1793   ir::OperandIndexSequence outputs;
1794
1795   loadOperationIO(op, inputs, outputs);
1796
1797   std::unique_ptr<ir::Operation> new_op(new ir::operation::LogicalOr(inputs, outputs));
1798   subg.addOperation(std::move(new_op));
1799 }
1800
1801 template <typename LoaderDomain, typename SpecificLoader>
1802 void BaseLoader<LoaderDomain, SpecificLoader>::loadLogSoftmax(const Operator *op, ir::Graph &subg)
1803 {
1804   ir::OperandIndexSequence inputs;
1805   ir::OperandIndexSequence outputs;
1806
1807   loadOperationIO(op, inputs, outputs);
1808
1809   ir::operation::LogSoftmax::Param param;
1810
1811   // In tflite, beta is fixed to 1.0 and axis is fixed to -1.
1812   param.beta = 1.0f;
1813   param.axis = -1;
1814
1815   std::unique_ptr<ir::Operation> new_op(new ir::operation::LogSoftmax(inputs, outputs, param));
1816   subg.addOperation(std::move(new_op));
1817 }
1818
1819 template <typename LoaderDomain, typename SpecificLoader>
1820 void BaseLoader<LoaderDomain, SpecificLoader>::loadQuantize(const Operator *op, ir::Graph &subg)
1821 {
1822   ir::OperandIndexSequence inputs;
1823   ir::OperandIndexSequence outputs;
1824
1825   loadOperationIO(op, inputs, outputs);
1826
1827   std::unique_ptr<ir::Operation> new_op(new ir::operation::Quantize(inputs, outputs));
1828   subg.addOperation(std::move(new_op));
1829 }
1830
1831 template <typename LoaderDomain, typename SpecificLoader>
1832 void BaseLoader<LoaderDomain, SpecificLoader>::loadOperation(const Operator *op, ir::Graph &subg)
1833 {
1834   const auto builtin_op = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
1835
1836   switch (builtin_op)
1837   {
1838     case BuiltinOperator::BuiltinOperator_CONV_2D:
1839       loadConv2D(op, subg);
1840       return;
1841     case BuiltinOperator::BuiltinOperator_AVERAGE_POOL_2D:
1842       loadAvgPool2D(op, subg);
1843       return;
1844     case BuiltinOperator::BuiltinOperator_DEPTHWISE_CONV_2D:
1845       loadDepthwiseConv2D(op, subg);
1846       return;
1847     case BuiltinOperator::BuiltinOperator_TRANSPOSE_CONV:
1848       loadTransposeConv(op, subg);
1849       return;
1850     case BuiltinOperator::BuiltinOperator_RESHAPE:
1851       loadReshape(op, subg);
1852       return;
1853     case BuiltinOperator::BuiltinOperator_SOFTMAX:
1854       loadSoftmax(op, subg);
1855       return;
1856     case BuiltinOperator::BuiltinOperator_MAX_POOL_2D:
1857       loadMaxPool2D(op, subg);
1858       return;
1859     case BuiltinOperator::BuiltinOperator_CONCATENATION:
1860       loadConcatenation(op, subg);
1861       return;
1862     case BuiltinOperator::BuiltinOperator_FULLY_CONNECTED:
1863       loadFC(op, subg);
1864       return;
1865     case BuiltinOperator::BuiltinOperator_ADD:
1866       loadAdd(op, subg);
1867       return;
1868     case BuiltinOperator::BuiltinOperator_SUB:
1869       loadSub(op, subg);
1870       return;
1871     case BuiltinOperator::BuiltinOperator_MUL:
1872       loadMul(op, subg);
1873       return;
1874     case BuiltinOperator::BuiltinOperator_DIV:
1875       loadDiv(op, subg);
1876       return;
1877     case BuiltinOperator::BuiltinOperator_PACK:
1878       loadPack(op, subg);
1879       return;
1880     case BuiltinOperator::BuiltinOperator_RELU:
1881       loadRelu(op, subg);
1882       return;
1883     case BuiltinOperator::BuiltinOperator_RELU6:
1884       loadRelu6(op, subg);
1885       return;
1886     case BuiltinOperator::BuiltinOperator_RESIZE_BILINEAR:
1887       loadResizeBilinear(op, subg);
1888       return;
1889     case BuiltinOperator::BuiltinOperator_RSQRT:
1890       loadRsqrt(op, subg);
1891       return;
1892     case BuiltinOperator::BuiltinOperator_SELECT:
1893       loadSelect(op, subg);
1894       return;
1895     case BuiltinOperator::BuiltinOperator_SELECT_V2:
1896       // Use same loader with BuiltinOperator_SELECT
1897       loadSelect(op, subg);
1898       return;
1899     case BuiltinOperator::BuiltinOperator_SQRT:
1900       loadSqrt(op, subg);
1901       return;
1902     case BuiltinOperator::BuiltinOperator_SQUARED_DIFFERENCE:
1903       loadSquaredDifference(op, subg);
1904       return;
1905     case BuiltinOperator::BuiltinOperator_TANH:
1906       loadTanh(op, subg);
1907       return;
1908     case BuiltinOperator::BuiltinOperator_TRANSPOSE:
1909       loadTranspose(op, subg);
1910       return;
1911     case BuiltinOperator::BuiltinOperator_MEAN:
1912       loadReduce(op, subg, ir::operation::Reduce::ReduceType::MEAN);
1913       return;
1914     case BuiltinOperator::BuiltinOperator_REDUCE_ANY:
1915       loadReduce(op, subg, ir::operation::Reduce::ReduceType::ANY);
1916       return;
1917     case BuiltinOperator::BuiltinOperator_REDUCE_MAX:
1918       loadReduce(op, subg, ir::operation::Reduce::ReduceType::MAX);
1919       return;
1920     case BuiltinOperator::BuiltinOperator_REVERSE_V2:
1921       loadReverseV2(op, subg);
1922       return;
1923     case BuiltinOperator::BuiltinOperator_PAD:
1924       loadPad(op, subg);
1925       return;
1926     case BuiltinOperator::BuiltinOperator_LOGISTIC:
1927       loadLogistic(op, subg);
1928       return;
1929     case BuiltinOperator::BuiltinOperator_EXP:
1930       loadExp(op, subg);
1931       return;
1932     case BuiltinOperator::BuiltinOperator_EXPAND_DIMS:
1933       loadExpandDims(op, subg);
1934       return;
1935     case BuiltinOperator::BuiltinOperator_GATHER:
1936       loadGather(op, subg);
1937       return;
1938     case BuiltinOperator::BuiltinOperator_SPACE_TO_BATCH_ND:
1939       loadSpaceToBatchND(op, subg);
1940       return;
1941     case BuiltinOperator::BuiltinOperator_BATCH_TO_SPACE_ND:
1942       loadBatchToSpaceND(op, subg);
1943       return;
1944     case BuiltinOperator::BuiltinOperator_SUM:
1945       loadReduce(op, subg, ir::operation::Reduce::ReduceType::SUM);
1946       return;
1947     case BuiltinOperator::BuiltinOperator_CUSTOM:
1948       loadCustom(op, subg);
1949       return;
1950     case BuiltinOperator::BuiltinOperator_SQUEEZE:
1951       loadSqueeze(op, subg);
1952       return;
1953     case BuiltinOperator::BuiltinOperator_PRELU:
1954       loadPrelu(op, subg);
1955       return;
1956     case BuiltinOperator::BuiltinOperator_SPLIT:
1957       loadSplit(op, subg);
1958       return;
1959     case BuiltinOperator::BuiltinOperator_SPLIT_V:
1960       loadSplitV(op, subg);
1961       return;
1962     case BuiltinOperator::BuiltinOperator_SLICE:
1963       loadSlice(op, subg);
1964       return;
1965     case BuiltinOperator::BuiltinOperator_STRIDED_SLICE:
1966       loadStridedSlice(op, subg);
1967       return;
1968     case BuiltinOperator::BuiltinOperator_UNPACK:
1969       loadUnpack(op, subg);
1970       return;
1971     case BuiltinOperator::BuiltinOperator_MINIMUM:
1972       loadMinimum(op, subg);
1973       return;
1974     case BuiltinOperator::BuiltinOperator_MAXIMUM:
1975       loadMaximum(op, subg);
1976       return;
1977     case BuiltinOperator::BuiltinOperator_CAST:
1978       loadCast(op, subg);
1979       return;
1980     case BuiltinOperator::BuiltinOperator_EQUAL:
1981     case BuiltinOperator::BuiltinOperator_NOT_EQUAL:
1982     case BuiltinOperator::BuiltinOperator_GREATER_EQUAL:
1983     case BuiltinOperator::BuiltinOperator_GREATER:
1984     case BuiltinOperator::BuiltinOperator_LESS_EQUAL:
1985     case BuiltinOperator::BuiltinOperator_LESS:
1986       loadComparison(op, subg);
1987       return;
1988     case BuiltinOperator::BuiltinOperator_ONE_HOT:
1989       loadOneHot(op, subg);
1990       return;
1991     case BuiltinOperator::BuiltinOperator_ABS:
1992       loadAbs(op, subg);
1993       return;
1994     case BuiltinOperator::BuiltinOperator_COS:
1995       loadCos(op, subg);
1996       return;
1997     case BuiltinOperator::BuiltinOperator_SIN:
1998       loadSin(op, subg);
1999       return;
2000     case BuiltinOperator::BuiltinOperator_SHAPE:
2001       loadShape(op, subg);
2002       return;
2003     case BuiltinOperator::BuiltinOperator_REDUCE_PROD:
2004       loadReduce(op, subg, ir::operation::Reduce::ReduceType::PROD);
2005       return;
2006     case BuiltinOperator::BuiltinOperator_IF:
2007       loadIf(op, subg);
2008       return;
2009     case BuiltinOperator::BuiltinOperator_WHILE:
2010       loadWhile(op, subg);
2011       return;
2012     case BuiltinOperator::BuiltinOperator_NEG:
2013       loadNeg(op, subg);
2014       return;
2015     case BuiltinOperator::BuiltinOperator_ARG_MAX:
2016       loadArgMax(op, subg);
2017       return;
2018     case BuiltinOperator::BuiltinOperator_LOG:
2019       loadLog(op, subg);
2020       return;
2021     case BuiltinOperator::BuiltinOperator_ROUND:
2022       loadRound(op, subg);
2023       return;
2024     case BuiltinOperator::BuiltinOperator_POW:
2025       loadPow(op, subg);
2026       return;
2027     case BuiltinOperator::BuiltinOperator_LOGICAL_NOT:
2028       loadLogicalNot(op, subg);
2029       return;
2030     case BuiltinOperator::BuiltinOperator_LOGICAL_OR:
2031       loadLogicalOr(op, subg);
2032       return;
2033     case BuiltinOperator::BuiltinOperator_FILL:
2034       loadFill(op, subg);
2035       return;
2036     case BuiltinOperator::BuiltinOperator_ZEROS_LIKE:
2037       loadZerosLike(op, subg);
2038       return;
2039     case BuiltinOperator::BuiltinOperator_TILE:
2040       loadTile(op, subg);
2041       return;
2042     case BuiltinOperator::BuiltinOperator_RANGE:
2043       loadRange(op, subg);
2044       return;
2045     case BuiltinOperator::BuiltinOperator_BATCH_MATMUL:
2046       loadBatchMatMul(op, subg);
2047       return;
2048     case BuiltinOperator::BuiltinOperator_LOG_SOFTMAX:
2049       loadLogSoftmax(op, subg);
2050       return;
2051     case BuiltinOperator::BuiltinOperator_QUANTIZE:
2052       loadQuantize(op, subg);
2053       return;
2054     case BuiltinOperator::BuiltinOperator_SPACE_TO_DEPTH:
2055       loadSpaceToDepth(op, subg);
2056       return;
2057     default:
2058       throw std::runtime_error(
2059           std::string("Unsupported operation: ").append(EnumNameBuiltinOperator(builtin_op)));
2060   }
2061 }
2062
2063 template <typename LoaderDomain, typename SpecificLoader>
2064 void BaseLoader<LoaderDomain, SpecificLoader>::loadModel()
2065 {
2066   LoaderDomain::VerifyModelBuffer(*_verifier.get());
2067   _model = LoaderDomain::GetModel(_base);
2068   // Version unused
2069   // const auto version = _model->version();
2070   // Description unused
2071   // const auto *description = _model->description();
2072   // Metabuffer unsued
2073   // const auto *metadata_buffer = _model->metadata_buffer();
2074   // Load subgraphs and map operations on subgraph
2075   const auto domain_subgraphs = _model->subgraphs();
2076   auto subgraphs = std::make_unique<ir::Subgraphs>();
2077   for (uint32_t subgraph_index = 0; subgraph_index < domain_subgraphs->size(); ++subgraph_index)
2078   {
2079     auto subg =
2080         static_cast<SpecificLoader *>(this)->loadSubgraph((*_model->subgraphs())[subgraph_index]);
2081     subgraphs->push(ir::SubgraphIndex{subgraph_index}, std::move(subg));
2082   }
2083   _subgraphs = std::move(subgraphs);
2084 }
2085
2086 } // namespace base_loader
2087 } // namespace onert
2088
2089 #endif //__BASE_LOADER_BASE_LOADER_H__