bca122050c92fb04fec5489bbe7f173c7c2981d0
[platform/core/ml/nnfw.git] / compiler / luci / export / src / CircleOperationExporter.cpp
1 /*
2  * Copyright (c) 2020 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 #include "CircleOperationExporter.h"
18 #include "CircleExporterUtils.h"
19 #include "Check.h"
20
21 #include <luci/IR/CircleNode.h>
22 #include <luci/IR/CircleNodes.h>
23 #include <luci/IR/CircleNodeVisitor.h>
24 #include <luci/Service/CircleShapeInference.h>
25 #include <luci/UserSettings.h>
26 #include <luci/Log.h>
27
28 #include <loco/IR/CanonicalNodeVisitor.h>
29 #include <oops/InternalExn.h>
30
31 #include <flatbuffers/flexbuffers.h>
32
33 using namespace flatbuffers;
34 using namespace circle;
35
36 namespace
37 {
38
39 using namespace luci;
40
41 class OperationExporter final : public luci::CircleNodeMutableVisitor<void>,
42                                 public loco::CanonicalNodeMutableVisitor<void>
43 {
44 public:
45   OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &m, SerializedGraphData &g)
46       : builder{fbb}, md{m}, gd{g}
47   {
48     // DO NOTHING
49   }
50
51 public:
52   void visit(luci::CircleAbs *) final;
53   void visit(luci::CircleAdd *) final;
54   void visit(luci::CircleAddN *) final;
55   void visit(luci::CircleArgMax *) final;
56   void visit(luci::CircleArgMin *) final;
57   void visit(luci::CircleAveragePool2D *) final;
58   void visit(luci::CircleBatchMatMul *) final;
59   void visit(luci::CircleBatchToSpaceND *) final;
60   void visit(luci::CircleCast *) final;
61   void visit(luci::CircleCeil *) final;
62   void visit(luci::CircleConcatenation *) final;
63   void visit(luci::CircleConst *) final{/* skip, everything is done in exportOpDefinedTensors */};
64   void visit(luci::CircleConv2D *) final;
65   void visit(luci::CircleCos *) final;
66   void visit(luci::CircleCustom *) final;
67   void visit(luci::CircleDepthToSpace *) final;
68   void visit(luci::CircleDepthwiseConv2D *) final;
69   void visit(luci::CircleDiv *) final;
70   void visit(luci::CircleElu *) final;
71   void visit(luci::CircleEqual *) final;
72   void visit(luci::CircleExp *) final;
73   void visit(luci::CircleExpandDims *) final;
74   void visit(luci::CircleFill *) final;
75   void visit(luci::CircleFloor *) final;
76   void visit(luci::CircleFloorDiv *) final;
77   void visit(luci::CircleFloorMod *) final;
78   void visit(luci::CircleFullyConnected *) final;
79   void visit(luci::CircleGather *) final;
80   void visit(luci::CircleGatherNd *) final;
81   void visit(luci::CircleGreater *) final;
82   void visit(luci::CircleGreaterEqual *) final;
83   void visit(luci::CircleIf *) final;
84   void visit(luci::CircleL2Normalize *) final;
85   void visit(luci::CircleL2Pool2D *) final;
86   void visit(luci::CircleLeakyRelu *) final;
87   void visit(luci::CircleLess *) final;
88   void visit(luci::CircleLessEqual *) final;
89   void visit(luci::CircleLocalResponseNormalization *) final;
90   void visit(luci::CircleLog *) final;
91   void visit(luci::CircleLogicalAnd *) final;
92   void visit(luci::CircleLogicalNot *) final;
93   void visit(luci::CircleLogicalOr *) final;
94   void visit(luci::CircleLogistic *) final;
95   void visit(luci::CircleLogSoftmax *) final;
96   void visit(luci::CircleMatrixDiag *) final;
97   void visit(luci::CircleMatrixSetDiag *) final;
98   void visit(luci::CircleMaximum *) final;
99   void visit(luci::CircleMaxPool2D *) final;
100   void visit(luci::CircleMean *) final;
101   void visit(luci::CircleMinimum *) final;
102   void visit(luci::CircleMirrorPad *) final;
103   void visit(luci::CircleMul *) final;
104   void visit(luci::CircleNeg *) final;
105   void visit(luci::CircleNonMaxSuppressionV4 *) final;
106   void visit(luci::CircleNotEqual *) final;
107   void visit(luci::CircleOneHot *) final;
108   void visit(luci::CirclePack *) final;
109   void visit(luci::CirclePad *) final;
110   void visit(luci::CirclePow *) final;
111   void visit(luci::CirclePRelu *) final;
112   void visit(luci::CircleRange *) final;
113   void visit(luci::CircleRank *) final;
114   void visit(luci::CircleReduceAny *) final;
115   void visit(luci::CircleReduceMax *) final;
116   void visit(luci::CircleReduceMin *) final;
117   void visit(luci::CircleReduceProd *) final;
118   void visit(luci::CircleRelu *) final;
119   void visit(luci::CircleRelu6 *) final;
120   void visit(luci::CircleReluN1To1 *) final;
121   void visit(luci::CircleReshape *) final;
122   void visit(luci::CircleResizeBilinear *) final;
123   void visit(luci::CircleResizeNearestNeighbor *) final;
124   void visit(luci::CircleReverseSequence *) final;
125   void visit(luci::CircleReverseV2 *) final;
126   void visit(luci::CircleRound *) final;
127   void visit(luci::CircleRsqrt *) final;
128   void visit(luci::CircleScatterNd *) final;
129   void visit(luci::CircleSegmentSum *) final;
130   void visit(luci::CircleSelect *) final;
131   void visit(luci::CircleSelectV2 *) final;
132   void visit(luci::CircleShape *) final;
133   void visit(luci::CircleSin *) final;
134   void visit(luci::CircleSlice *) final;
135   void visit(luci::CircleSoftmax *) final;
136   void visit(luci::CircleSpaceToBatchND *) final;
137   void visit(luci::CircleSpaceToDepth *) final;
138   void visit(luci::CircleSparseToDense *) final;
139   void visit(luci::CircleSplit *) final;
140   void visit(luci::CircleSplitV *) final;
141   void visit(luci::CircleSqrt *) final;
142   void visit(luci::CircleSquare *) final;
143   void visit(luci::CircleSquaredDifference *) final;
144   void visit(luci::CircleSqueeze *) final;
145   void visit(luci::CircleStridedSlice *) final;
146   void visit(luci::CircleSub *) final;
147   void visit(luci::CircleSum *) final;
148   void visit(luci::CircleTanh *) final;
149   void visit(luci::CircleTile *) final;
150   void visit(luci::CircleTopKV2 *) final;
151   void visit(luci::CircleTranspose *) final;
152   void visit(luci::CircleTransposeConv *) final;
153   void visit(luci::CircleUnique *) final;
154   void visit(luci::CircleUnpack *) final;
155   void visit(luci::CircleWhere *) final;
156   void visit(luci::CircleWhile *) final;
157   void visit(luci::CircleZerosLike *) final;
158   // Circle only
159   void visit(luci::CircleBCQFullyConnected *) final;
160   void visit(luci::CircleBCQGather *) final;
161   void visit(luci::CircleInstanceNorm *) final;
162   // Virtual
163   void visit(luci::CircleInput *) final {}
164   void visit(luci::CircleOutput *) final {}
165   void visit(luci::CircleOutputDummy *) final {}
166   void visit(luci::CircleOutputExclude *) final {}
167   // Virtual for multiple-outputs
168   void visit(luci::CircleCustomOut *) final {}
169   void visit(luci::CircleIfOut *) final {}
170   void visit(luci::CircleNonMaxSuppressionV4Out *) final {}
171   void visit(luci::CircleSplitOut *) final {}
172   void visit(luci::CircleSplitVOut *) final {}
173   void visit(luci::CircleTopKV2Out *) final {}
174   void visit(luci::CircleUniqueOut *) final {}
175   void visit(luci::CircleUnpackOut *) final {}
176   void visit(luci::CircleWhileOut *) final {}
177
178 private:
179   /**
180    * @brief Exports CircleMaxPool2D or CircleAveragePool2D
181    *
182    * @note  CirclePool2D should be one of CircleMaxPool2D or CircleAveragePool2D
183    */
184   template <class CirclePool2D>
185   void export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op);
186
187   /**
188    * @brief export simple nodes
189    */
190   void export_simple(loco::Node *node, circle::BuiltinOperator bop, circle::BuiltinOptions bot,
191                      flatbuffers::Offset<void> options_offset);
192
193   /**
194    * @brief export simple nodes having void options
195    */
196   void export_simple(loco::Node *node, circle::BuiltinOperator bop);
197
198 private:
199   FlatBufferBuilder &builder;
200   SerializedModelData &md;
201   SerializedGraphData &gd;
202 };
203
204 template <class CirclePool2D>
205 void OperationExporter::export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op)
206 {
207   LUCI_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D ||
208                   builtin_op == circle::BuiltinOperator_L2_POOL_2D ||
209                   builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D,
210               "Should be L2Pool, MaxPool or AvgPool");
211   LUCI_ASSERT(node->padding() != luci::Padding::UNDEFINED, "Padding is not set");
212
213   uint32_t op_idx = md.registerBuiltinOpcode(builtin_op, node->op_version());
214   std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
215   std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
216   auto inputs = builder.CreateVector(inputs_vec);
217   auto outputs = builder.CreateVector(outputs_vec);
218
219   circle::Padding padding = getOpPadding(node->padding());
220
221   auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
222                                      node->filter()->w(), node->filter()->h(),
223                                      to_circle_actfunc(node->fusedActivationFunction()));
224   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
225                                   circle::BuiltinOptions_Pool2DOptions, options.Union());
226   gd._operators.push_back(op_offset);
227 }
228
229 void OperationExporter::export_simple(loco::Node *node, circle::BuiltinOperator bop,
230                                       circle::BuiltinOptions bot,
231                                       flatbuffers::Offset<void> options_offset)
232 {
233   uint32_t op_idx =
234       md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version());
235   std::vector<int32_t> inputs_vec;
236   std::vector<int32_t> outputs_vec{get_tensor_index(node)};
237   for (uint32_t i = 0; i < node->arity(); ++i)
238     inputs_vec.push_back(get_tensor_index(node->arg(i)));
239   auto inputs = builder.CreateVector(inputs_vec);
240   auto outputs = builder.CreateVector(outputs_vec);
241   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, bot, options_offset);
242   gd._operators.push_back(op_offset);
243 }
244
245 void OperationExporter::export_simple(loco::Node *node, circle::BuiltinOperator bop)
246 {
247   uint32_t op_idx =
248       md.registerBuiltinOpcode(bop, loco::must_cast<luci::CircleNode *>(node)->op_version());
249   std::vector<int32_t> inputs_vec;
250   std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
251   for (uint32_t i = 0; i < node->arity(); ++i)
252     inputs_vec.push_back(get_tensor_index(node->arg(i)));
253   auto inputs = builder.CreateVector(inputs_vec);
254   auto outputs = builder.CreateVector(outputs_vec);
255   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
256   gd._operators.push_back(op_offset);
257 }
258
259 void OperationExporter::visit(luci::CircleAbs *node)
260 {
261   export_simple(node, circle::BuiltinOperator_ABS, circle::BuiltinOptions_AbsOptions,
262                 CreateAbsOptions(builder).Union());
263 }
264
265 void OperationExporter::visit(luci::CircleAdd *node)
266 {
267   export_simple(
268       node, circle::BuiltinOperator_ADD, circle::BuiltinOptions_AddOptions,
269       CreateAddOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
270 }
271
272 void OperationExporter::visit(luci::CircleAddN *node)
273 {
274   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ADD_N, node->op_version());
275   std::vector<int32_t> inputs_vec;
276   std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
277
278   for (uint32_t i = 0; i < node->arity(); ++i)
279     inputs_vec.push_back(get_tensor_index(node->inputs(i)));
280
281   auto inputs = builder.CreateVector(inputs_vec);
282   auto outputs = builder.CreateVector(outputs_vec);
283   auto options = CreateAddNOptions(builder);
284   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
285                                   circle::BuiltinOptions_AddNOptions, options.Union());
286   gd._operators.push_back(op_offset);
287 }
288
289 void OperationExporter::visit(luci::CircleArgMax *node)
290 {
291   export_simple(node, circle::BuiltinOperator_ARG_MAX, circle::BuiltinOptions_ArgMaxOptions,
292                 CreateArgMaxOptions(builder, to_circle_tensortype(node->output_type())).Union());
293 }
294
295 void OperationExporter::visit(luci::CircleArgMin *node)
296 {
297   export_simple(node, circle::BuiltinOperator_ARG_MIN, circle::BuiltinOptions_ArgMinOptions,
298                 CreateArgMinOptions(builder, to_circle_tensortype(node->output_type())).Union());
299 }
300
301 void OperationExporter::visit(luci::CircleAveragePool2D *node)
302 {
303   export_pool_2d<luci::CircleAveragePool2D>(node, circle::BuiltinOperator_AVERAGE_POOL_2D);
304 }
305
306 void OperationExporter::visit(luci::CircleBatchMatMul *node)
307 {
308   export_simple(node, circle::BuiltinOperator_BATCH_MATMUL,
309                 circle::BuiltinOptions_BatchMatMulOptions,
310                 CreateBatchMatMulOptions(builder, node->adj_x(), node->adj_y()).Union());
311 }
312
313 void OperationExporter::visit(luci::CircleCast *node)
314 {
315   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CAST, node->op_version());
316   std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
317   std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
318   auto inputs = builder.CreateVector(inputs_vec);
319   auto outputs = builder.CreateVector(outputs_vec);
320
321   flatbuffers::Offset<Operator> op_offset;
322   if (node->out_data_type() != loco::DataType::Unknown)
323   {
324     auto options = CreateCastOptions(builder, to_circle_tensortype(node->in_data_type()),
325                                      to_circle_tensortype(node->out_data_type()));
326     op_offset = CreateOperator(builder, op_idx, inputs, outputs, circle::BuiltinOptions_CastOptions,
327                                options.Union());
328   }
329   else
330   {
331     op_offset = CreateOperator(builder, op_idx, inputs, outputs);
332   }
333   gd._operators.push_back(op_offset);
334 }
335
336 void OperationExporter::visit(luci::CircleCeil *node)
337 {
338   export_simple(node, circle::BuiltinOperator_CEIL);
339 }
340
341 void OperationExporter::visit(luci::CircleConcatenation *node)
342 {
343   uint32_t op_idx =
344       md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION, node->op_version());
345   std::vector<int32_t> inputs_vec;
346   std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
347
348   for (uint32_t i = 0; i < node->numValues(); ++i)
349     inputs_vec.push_back(get_tensor_index(node->values(i)));
350
351   auto inputs = builder.CreateVector(inputs_vec);
352   auto outputs = builder.CreateVector(outputs_vec);
353   auto options = CreateConcatenationOptions(builder, node->axis(),
354                                             to_circle_actfunc(node->fusedActivationFunction()));
355   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
356                                   circle::BuiltinOptions_ConcatenationOptions, options.Union());
357   gd._operators.push_back(op_offset);
358 }
359
360 void OperationExporter::visit(luci::CircleBatchToSpaceND *node)
361 {
362   export_simple(node, circle::BuiltinOperator_BATCH_TO_SPACE_ND,
363                 circle::BuiltinOptions_BatchToSpaceNDOptions,
364                 CreateBatchToSpaceNDOptions(builder).Union());
365 }
366
367 void OperationExporter::visit(luci::CircleConv2D *node)
368 {
369   export_simple(node, circle::BuiltinOperator_CONV_2D, circle::BuiltinOptions_Conv2DOptions,
370                 CreateConv2DOptions(builder, getOpPadding(node->padding()), node->stride()->w(),
371                                     node->stride()->h(),
372                                     to_circle_actfunc(node->fusedActivationFunction()),
373                                     node->dilation()->w(), node->dilation()->h())
374                     .Union());
375 }
376
377 void OperationExporter::visit(luci::CircleCos *node)
378 {
379   export_simple(node, circle::BuiltinOperator_COS, circle::BuiltinOptions_CosOptions,
380                 CreateCosOptions(builder).Union());
381 }
382
383 void OperationExporter::visit(luci::CircleCustom *node)
384 {
385   auto custom_outputs = loco::succs(node);
386
387   uint32_t op_idx = md.registerCustomOpcode(node->custom_code());
388   std::vector<int32_t> inputs_vec;
389   std::vector<int32_t> outputs_vec;
390
391   for (uint32_t index = 0; index < node->numInputs(); index++)
392   {
393     inputs_vec.push_back(get_tensor_index(node->inputs(index)));
394   }
395   for (uint32_t index = 0; index < custom_outputs.size(); index++)
396   {
397     // store in order of index
398     bool found = false;
399     for (auto out : custom_outputs)
400     {
401       auto custom_out = loco::must_cast<luci::CircleCustomOut *>(out);
402       if (custom_out->index() == static_cast<int32_t>(index))
403       {
404         outputs_vec.push_back(get_tensor_index(custom_out));
405         found = true;
406         break;
407       }
408     }
409     if (!found)
410     {
411       INTERNAL_EXN("Invalid Custom output");
412     }
413   }
414
415   auto inputs = builder.CreateVector(inputs_vec);
416   auto outputs = builder.CreateVector(outputs_vec);
417   flatbuffers::Offset<flatbuffers::Vector<uint8_t>> circle_custom_options;
418   std::vector<uint8_t> custom_options_vec{node->custom_options().begin(),
419                                           node->custom_options().end()};
420   circle_custom_options = builder.CreateVector(custom_options_vec);
421   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs, circle::BuiltinOptions_NONE,
422                                   flatbuffers::Offset<void>(), circle_custom_options);
423   gd._operators.push_back(op_offset);
424 }
425
426 void OperationExporter::visit(luci::CircleDepthToSpace *node)
427 {
428   export_simple(node, circle::BuiltinOperator_DEPTH_TO_SPACE,
429                 circle::BuiltinOptions_DepthToSpaceOptions,
430                 CreateDepthToSpaceOptions(builder, node->block_size()).Union());
431 }
432
433 void OperationExporter::visit(luci::CircleDepthwiseConv2D *node)
434 {
435   export_simple(node, circle::BuiltinOperator_DEPTHWISE_CONV_2D,
436                 circle::BuiltinOptions_DepthwiseConv2DOptions,
437                 CreateDepthwiseConv2DOptions(builder, getOpPadding(node->padding()),
438                                              node->stride()->w(), node->stride()->h(),
439                                              node->depthMultiplier(),
440                                              to_circle_actfunc(node->fusedActivationFunction()),
441                                              node->dilation()->w(), node->dilation()->h())
442                     .Union());
443 }
444
445 void OperationExporter::visit(luci::CircleDiv *node)
446 {
447   export_simple(
448       node, circle::BuiltinOperator_DIV, circle::BuiltinOptions_DivOptions,
449       CreateDivOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
450 }
451
452 void OperationExporter::visit(luci::CircleElu *node)
453 {
454   export_simple(node, circle::BuiltinOperator_ELU);
455 }
456
457 void OperationExporter::visit(luci::CircleEqual *node)
458 {
459   export_simple(node, circle::BuiltinOperator_EQUAL, circle::BuiltinOptions_EqualOptions,
460                 CreateEqualOptions(builder).Union());
461 }
462
463 void OperationExporter::visit(luci::CircleExp *node)
464 {
465   export_simple(node, circle::BuiltinOperator_EXP, circle::BuiltinOptions_ExpOptions,
466                 CreateExpOptions(builder).Union());
467 }
468
469 void OperationExporter::visit(luci::CircleExpandDims *node)
470 {
471   export_simple(node, circle::BuiltinOperator_EXPAND_DIMS, circle::BuiltinOptions_ExpandDimsOptions,
472                 CreateExpandDimsOptions(builder).Union());
473 }
474
475 void OperationExporter::visit(luci::CircleFill *node)
476 {
477   export_simple(node, circle::BuiltinOperator_FILL, circle::BuiltinOptions_FillOptions,
478                 CreateFillOptions(builder).Union());
479 }
480
481 void OperationExporter::visit(luci::CircleFloor *node)
482 {
483   export_simple(node, circle::BuiltinOperator_FLOOR);
484 }
485
486 void OperationExporter::visit(luci::CircleFloorDiv *node)
487 {
488   export_simple(node, circle::BuiltinOperator_FLOOR_DIV, circle::BuiltinOptions_FloorDivOptions,
489                 CreateFloorDivOptions(builder).Union());
490 }
491
492 void OperationExporter::visit(luci::CircleFloorMod *node)
493 {
494   export_simple(node, circle::BuiltinOperator_FLOOR_MOD, circle::BuiltinOptions_FloorModOptions,
495                 CreateFloorModOptions(builder).Union());
496 }
497
498 void OperationExporter::visit(luci::CircleFullyConnected *node)
499 {
500   export_simple(
501       node, circle::BuiltinOperator_FULLY_CONNECTED, circle::BuiltinOptions_FullyConnectedOptions,
502       CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction()))
503           .Union());
504 }
505
506 void OperationExporter::visit(luci::CircleGather *node)
507 {
508   export_simple(node, circle::BuiltinOperator_GATHER, circle::BuiltinOptions_GatherOptions,
509                 CreateGatherOptions(builder, node->axis()).Union());
510 }
511
512 void OperationExporter::visit(luci::CircleGatherNd *node)
513 {
514   export_simple(node, circle::BuiltinOperator_GATHER_ND, circle::BuiltinOptions_GatherNdOptions,
515                 CreateGatherNdOptions(builder).Union());
516 }
517
518 void OperationExporter::visit(luci::CircleGreater *node)
519 {
520   export_simple(node, circle::BuiltinOperator_GREATER, circle::BuiltinOptions_GreaterOptions,
521                 CreateGreaterOptions(builder).Union());
522 }
523
524 void OperationExporter::visit(luci::CircleGreaterEqual *node)
525 {
526   export_simple(node, circle::BuiltinOperator_GREATER_EQUAL,
527                 circle::BuiltinOptions_GreaterEqualOptions,
528                 CreateGreaterEqualOptions(builder).Union());
529 }
530
531 void OperationExporter::visit(luci::CircleIf *node)
532 {
533   auto if_outs = loco::succs(node);
534   assert(if_outs.size() == node->output_count());
535
536   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_IF, node->op_version());
537   std::vector<int32_t> inputs_vec;
538   std::vector<int32_t> outputs_vec;
539
540   inputs_vec.push_back(get_tensor_index(node->cond()));
541   for (uint32_t idx = 0; idx < node->input_count(); ++idx)
542     inputs_vec.push_back(get_tensor_index(node->input(idx)));
543
544   for (uint32_t idx = 0; idx < node->output_count(); ++idx)
545   {
546     // store in order of index
547     bool found = false;
548     for (auto out : if_outs)
549     {
550       auto if_out = loco::must_cast<luci::CircleIfOut *>(out);
551       if (if_out->index() == static_cast<int32_t>(idx))
552       {
553         outputs_vec.push_back(get_tensor_index(if_out));
554         found = true;
555         break;
556       }
557     }
558     if (!found)
559     {
560       INTERNAL_EXN("Invalid CircleIf output");
561     }
562   }
563
564   auto inputs = builder.CreateVector(inputs_vec);
565   auto outputs = builder.CreateVector(outputs_vec);
566   auto options = CreateIfOptions(builder, node->then_branch(), node->else_branch());
567   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
568                                   circle::BuiltinOptions_IfOptions, options.Union());
569   gd._operators.push_back(op_offset);
570 }
571
572 void OperationExporter::visit(luci::CircleL2Normalize *node)
573 {
574   export_simple(
575       node, circle::BuiltinOperator_L2_NORMALIZATION, circle::BuiltinOptions_L2NormOptions,
576       CreateL2NormOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
577 }
578
579 void OperationExporter::visit(luci::CircleL2Pool2D *node)
580 {
581   export_pool_2d<luci::CircleL2Pool2D>(node, circle::BuiltinOperator_L2_POOL_2D);
582 }
583
584 void OperationExporter::visit(luci::CircleLeakyRelu *node)
585 {
586   export_simple(node, circle::BuiltinOperator_LEAKY_RELU, circle::BuiltinOptions_LeakyReluOptions,
587                 CreateLeakyReluOptions(builder, node->alpha()).Union());
588 }
589
590 void OperationExporter::visit(luci::CircleLess *node)
591 {
592   export_simple(node, circle::BuiltinOperator_LESS, circle::BuiltinOptions_LessOptions,
593                 CreateLessOptions(builder).Union());
594 }
595
596 void OperationExporter::visit(luci::CircleLessEqual *node)
597 {
598   export_simple(node, circle::BuiltinOperator_LESS_EQUAL, circle::BuiltinOptions_LessEqualOptions,
599                 CreateLessEqualOptions(builder).Union());
600 }
601
602 void OperationExporter::visit(luci::CircleLocalResponseNormalization *node)
603 {
604   export_simple(node, circle::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION,
605                 circle::BuiltinOptions_LocalResponseNormalizationOptions,
606                 CreateLocalResponseNormalizationOptions(builder, node->radius(), node->bias(),
607                                                         node->alpha(), node->beta())
608                     .Union());
609 }
610
611 void OperationExporter::visit(luci::CircleLog *node)
612 {
613   export_simple(node, circle::BuiltinOperator_LOG);
614 }
615
616 void OperationExporter::visit(luci::CircleLogicalAnd *node)
617 {
618   export_simple(node, circle::BuiltinOperator_LOGICAL_AND, circle::BuiltinOptions_LogicalAndOptions,
619                 CreateLogicalAndOptions(builder).Union());
620 }
621
622 void OperationExporter::visit(luci::CircleLogicalNot *node)
623 {
624   export_simple(node, circle::BuiltinOperator_LOGICAL_NOT, circle::BuiltinOptions_LogicalNotOptions,
625                 CreateLogicalNotOptions(builder).Union());
626 }
627
628 void OperationExporter::visit(luci::CircleLogicalOr *node)
629 {
630   export_simple(node, circle::BuiltinOperator_LOGICAL_OR, circle::BuiltinOptions_LogicalOrOptions,
631                 CreateLogicalOrOptions(builder).Union());
632 }
633
634 void OperationExporter::visit(luci::CircleLogistic *node)
635 {
636   export_simple(node, circle::BuiltinOperator_LOGISTIC);
637 }
638
639 void OperationExporter::visit(luci::CircleLogSoftmax *node)
640 {
641   export_simple(node, circle::BuiltinOperator_LOG_SOFTMAX, circle::BuiltinOptions_LogSoftmaxOptions,
642                 CreateLogSoftmaxOptions(builder).Union());
643 }
644
645 void OperationExporter::visit(luci::CircleMatrixDiag *node)
646 {
647   export_simple(node, circle::BuiltinOperator_MATRIX_DIAG, circle::BuiltinOptions_MatrixDiagOptions,
648                 CreateMatrixDiagOptions(builder).Union());
649 }
650
651 void OperationExporter::visit(luci::CircleMatrixSetDiag *node)
652 {
653   export_simple(node, circle::BuiltinOperator_MATRIX_SET_DIAG,
654                 circle::BuiltinOptions_MatrixSetDiagOptions,
655                 CreateMatrixSetDiagOptions(builder).Union());
656 }
657
658 void OperationExporter::visit(luci::CircleMaximum *node)
659 {
660   export_simple(node, circle::BuiltinOperator_MAXIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
661                 CreateMaximumMinimumOptions(builder).Union());
662 }
663
664 void OperationExporter::visit(luci::CircleMaxPool2D *node)
665 {
666   export_pool_2d<luci::CircleMaxPool2D>(node, circle::BuiltinOperator_MAX_POOL_2D);
667 }
668
669 void OperationExporter::visit(luci::CircleMean *node)
670 {
671   export_simple(node, circle::BuiltinOperator_MEAN, circle::BuiltinOptions_ReducerOptions,
672                 CreateReducerOptions(builder, node->keep_dims()).Union());
673 }
674
675 void OperationExporter::visit(luci::CircleMinimum *node)
676 {
677   export_simple(node, circle::BuiltinOperator_MINIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
678                 CreateMaximumMinimumOptions(builder).Union());
679 }
680
681 void OperationExporter::visit(luci::CircleMirrorPad *node)
682 {
683   export_simple(node, circle::BuiltinOperator_MIRROR_PAD, circle::BuiltinOptions_MirrorPadOptions,
684                 CreateMirrorPadOptions(builder, to_circle_mirrorpadmode(node->mode())).Union());
685 }
686
687 void OperationExporter::visit(luci::CircleMul *node)
688 {
689   export_simple(
690       node, circle::BuiltinOperator_MUL, circle::BuiltinOptions_MulOptions,
691       CreateMulOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
692 }
693
694 void OperationExporter::visit(luci::CircleNeg *node)
695 {
696   export_simple(node, circle::BuiltinOperator_NEG, circle::BuiltinOptions_NegOptions,
697                 CreateNegOptions(builder).Union());
698 }
699
700 void OperationExporter::visit(luci::CircleNonMaxSuppressionV4 *node)
701 {
702   auto nms_outs = loco::succs(node);
703   assert(nms_outs.size() == 2);
704
705   uint32_t op_idx =
706       md.registerBuiltinOpcode(circle::BuiltinOperator_NON_MAX_SUPPRESSION_V4, node->op_version());
707   std::vector<int32_t> inputs_vec{
708       get_tensor_index(node->boxes()),           get_tensor_index(node->scores()),
709       get_tensor_index(node->max_output_size()), get_tensor_index(node->iou_threshold()),
710       get_tensor_index(node->score_threshold()),
711   };
712   std::vector<int32_t> outputs_vec;
713
714   for (uint32_t idx = 0; idx < nms_outs.size(); ++idx)
715   {
716     // store in order of index
717     bool found = false;
718     for (auto out : nms_outs)
719     {
720       auto nms_out = loco::must_cast<luci::CircleNonMaxSuppressionV4Out *>(out);
721       if (nms_out->index() == static_cast<int32_t>(idx))
722       {
723         outputs_vec.push_back(get_tensor_index(nms_out));
724         found = true;
725         break;
726       }
727     }
728     if (!found)
729     {
730       INTERNAL_EXN("Invalid NonMaxSuppressionV4 output");
731     }
732   }
733
734   auto inputs = builder.CreateVector(inputs_vec);
735   auto outputs = builder.CreateVector(outputs_vec);
736   auto options = CreateNonMaxSuppressionV4Options(builder);
737   auto op_offset =
738       CreateOperator(builder, op_idx, inputs, outputs,
739                      circle::BuiltinOptions_NonMaxSuppressionV4Options, options.Union());
740   gd._operators.push_back(op_offset);
741 }
742
743 void OperationExporter::visit(luci::CircleNotEqual *node)
744 {
745   export_simple(node, circle::BuiltinOperator_NOT_EQUAL, circle::BuiltinOptions_NotEqualOptions,
746                 CreateNotEqualOptions(builder).Union());
747 }
748
749 void OperationExporter::visit(luci::CircleOneHot *node)
750 {
751   export_simple(node, circle::BuiltinOperator_ONE_HOT, circle::BuiltinOptions_OneHotOptions,
752                 CreateOneHotOptions(builder, node->axis()).Union());
753 }
754
755 void OperationExporter::visit(luci::CirclePack *node)
756 {
757   export_simple(node, circle::BuiltinOperator_PACK, circle::BuiltinOptions_PackOptions,
758                 CreatePackOptions(builder, node->values_count(), node->axis()).Union());
759 }
760
761 void OperationExporter::visit(luci::CirclePad *node)
762 {
763   export_simple(node, circle::BuiltinOperator_PAD, circle::BuiltinOptions_PadOptions,
764                 CreatePadOptions(builder).Union());
765 }
766
767 void OperationExporter::visit(luci::CirclePow *node)
768 {
769   export_simple(node, circle::BuiltinOperator_POW, circle::BuiltinOptions_PowOptions,
770                 CreatePowOptions(builder).Union());
771 }
772
773 void OperationExporter::visit(luci::CirclePRelu *node)
774 {
775   export_simple(node, circle::BuiltinOperator_PRELU);
776 }
777
778 void OperationExporter::visit(luci::CircleRange *node)
779 {
780   export_simple(node, circle::BuiltinOperator_RANGE, circle::BuiltinOptions_RangeOptions,
781                 CreateRangeOptions(builder).Union());
782 }
783
784 void OperationExporter::visit(luci::CircleRank *node)
785 {
786   export_simple(node, circle::BuiltinOperator_RANK, circle::BuiltinOptions_RankOptions,
787                 CreateRankOptions(builder).Union());
788 }
789
790 void OperationExporter::visit(luci::CircleReduceAny *node)
791 {
792   export_simple(node, circle::BuiltinOperator_REDUCE_ANY, circle::BuiltinOptions_ReducerOptions,
793                 CreateReducerOptions(builder, node->keep_dims()).Union());
794 }
795
796 void OperationExporter::visit(luci::CircleReduceMax *node)
797 {
798   export_simple(node, circle::BuiltinOperator_REDUCE_MAX, circle::BuiltinOptions_ReducerOptions,
799                 CreateReducerOptions(builder, node->keep_dims()).Union());
800 }
801
802 void OperationExporter::visit(luci::CircleReduceMin *node)
803 {
804   export_simple(node, circle::BuiltinOperator_REDUCE_MIN, circle::BuiltinOptions_ReducerOptions,
805                 CreateReducerOptions(builder, node->keep_dims()).Union());
806 }
807
808 void OperationExporter::visit(luci::CircleReduceProd *node)
809 {
810   export_simple(node, circle::BuiltinOperator_REDUCE_PROD, circle::BuiltinOptions_ReducerOptions,
811                 CreateReducerOptions(builder, node->keep_dims()).Union());
812 }
813
814 void OperationExporter::visit(luci::CircleRelu *node)
815 {
816   export_simple(node, circle::BuiltinOperator_RELU);
817 }
818
819 void OperationExporter::visit(luci::CircleRelu6 *node)
820 {
821   export_simple(node, circle::BuiltinOperator_RELU6);
822 }
823
824 void OperationExporter::visit(luci::CircleReluN1To1 *node)
825 {
826   export_simple(node, circle::BuiltinOperator_RELU_N1_TO_1);
827 }
828
829 void OperationExporter::visit(luci::CircleReshape *node)
830 {
831   auto new_shape = builder.CreateVector<int32_t>(
832       node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); });
833
834   export_simple(node, circle::BuiltinOperator_RESHAPE, circle::BuiltinOptions_ReshapeOptions,
835                 CreateReshapeOptions(builder, new_shape).Union());
836 }
837
838 void OperationExporter::visit(luci::CircleResizeBilinear *node)
839 {
840   export_simple(
841       node, circle::BuiltinOperator_RESIZE_BILINEAR, circle::BuiltinOptions_ResizeBilinearOptions,
842       CreateResizeBilinearOptions(builder, node->align_corners(), node->half_pixel_centers())
843           .Union());
844 }
845
846 void OperationExporter::visit(luci::CircleResizeNearestNeighbor *node)
847 {
848   export_simple(node, circle::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR,
849                 circle::BuiltinOptions_ResizeNearestNeighborOptions,
850                 CreateResizeNearestNeighborOptions(builder, node->align_corners()).Union());
851 }
852
853 void OperationExporter::visit(luci::CircleReverseSequence *node)
854 {
855   export_simple(
856       node, circle::BuiltinOperator_REVERSE_SEQUENCE, circle::BuiltinOptions_ReverseSequenceOptions,
857       CreateReverseSequenceOptions(builder, node->seq_axis(), node->batch_axis()).Union());
858 }
859
860 void OperationExporter::visit(luci::CircleReverseV2 *node)
861 {
862   uint32_t op_idx =
863       md.registerBuiltinOpcode(circle::BuiltinOperator_REVERSE_V2, node->op_version());
864   std::vector<int32_t> inputs_vec{get_tensor_index(node->tensor()), get_tensor_index(node->axis())};
865   std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
866   auto inputs = builder.CreateVector(inputs_vec);
867   auto outputs = builder.CreateVector(outputs_vec);
868   auto options = CreateReverseV2Options(builder);
869   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
870                                   circle::BuiltinOptions_ReverseSequenceOptions, options.Union());
871   gd._operators.push_back(op_offset);
872 }
873
874 void OperationExporter::visit(luci::CircleRound *node)
875 {
876   export_simple(node, circle::BuiltinOperator_ROUND);
877 }
878
879 void OperationExporter::visit(luci::CircleRsqrt *node)
880 {
881   export_simple(node, circle::BuiltinOperator_RSQRT);
882 }
883
884 void OperationExporter::visit(luci::CircleScatterNd *node)
885 {
886   export_simple(node, circle::BuiltinOperator_SCATTER_ND, circle::BuiltinOptions_ScatterNdOptions,
887                 CreateScatterNdOptions(builder).Union());
888 }
889
890 void OperationExporter::visit(luci::CircleSegmentSum *node)
891 {
892   export_simple(node, circle::BuiltinOperator_SEGMENT_SUM, circle::BuiltinOptions_SegmentSumOptions,
893                 CreateSegmentSumOptions(builder).Union());
894 }
895
896 void OperationExporter::visit(luci::CircleSelect *node)
897 {
898   export_simple(node, circle::BuiltinOperator_SELECT, circle::BuiltinOptions_SelectOptions,
899                 CreateSelectOptions(builder).Union());
900 }
901
902 void OperationExporter::visit(luci::CircleSelectV2 *node)
903 {
904   export_simple(node, circle::BuiltinOperator_SELECT_V2, circle::BuiltinOptions_SelectV2Options,
905                 CreateSelectV2Options(builder).Union());
906 }
907
908 void OperationExporter::visit(luci::CircleShape *node)
909 {
910   export_simple(node, circle::BuiltinOperator_SHAPE, circle::BuiltinOptions_ShapeOptions,
911                 CreateShapeOptions(builder, to_circle_tensortype(node->out_type())).Union());
912 }
913
914 void OperationExporter::visit(luci::CircleSin *node)
915 {
916   export_simple(node, circle::BuiltinOperator_SIN);
917 }
918
919 void OperationExporter::visit(luci::CircleSlice *node)
920 {
921   export_simple(node, circle::BuiltinOperator_SLICE, circle::BuiltinOptions_SliceOptions,
922                 CreateSliceOptions(builder).Union());
923 }
924
925 void OperationExporter::visit(luci::CircleSoftmax *node)
926 {
927   export_simple(node, circle::BuiltinOperator_SOFTMAX, circle::BuiltinOptions_SoftmaxOptions,
928                 CreateSoftmaxOptions(builder, node->beta()).Union());
929 }
930
931 void OperationExporter::visit(luci::CircleSpaceToBatchND *node)
932 {
933   export_simple(node, circle::BuiltinOperator_SPACE_TO_BATCH_ND,
934                 circle::BuiltinOptions_SpaceToBatchNDOptions,
935                 CreateSpaceToBatchNDOptions(builder).Union());
936 }
937
938 void OperationExporter::visit(luci::CircleSpaceToDepth *node)
939 {
940   export_simple(node, circle::BuiltinOperator_SPACE_TO_DEPTH,
941                 circle::BuiltinOptions_SpaceToDepthOptions,
942                 CreateSpaceToDepthOptions(builder, node->block_size()).Union());
943 }
944
945 void OperationExporter::visit(luci::CircleSparseToDense *node)
946 {
947   export_simple(node, circle::BuiltinOperator_SPARSE_TO_DENSE,
948                 circle::BuiltinOptions_SparseToDenseOptions,
949                 CreateSparseToDenseOptions(builder, node->validate_indices()).Union());
950 }
951
952 void OperationExporter::visit(luci::CircleSplit *node)
953 {
954   auto split_outs = loco::succs(node);
955   assert(int32_t(split_outs.size()) == node->num_split());
956
957   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT, node->op_version());
958   // NOTE BuiltinOperator_SPLIT input is placed at second position
959   std::vector<int32_t> inputs_vec{get_tensor_index(node->split_dim()),
960                                   get_tensor_index(node->input())};
961   std::vector<int32_t> outputs_vec;
962
963   for (int32_t index = 0; index < node->num_split(); index++)
964   {
965     // store in order of index
966     bool found = false;
967     for (auto out : split_outs)
968     {
969       auto split_out = loco::must_cast<luci::CircleSplitOut *>(out);
970       if (split_out->index() == index)
971       {
972         outputs_vec.push_back(get_tensor_index(split_out));
973         found = true;
974         break;
975       }
976     }
977     if (!found)
978     {
979       INTERNAL_EXN("Invalid Split output");
980     }
981   }
982
983   auto inputs = builder.CreateVector(inputs_vec);
984   auto outputs = builder.CreateVector(outputs_vec);
985   auto options = CreateSplitOptions(builder, node->num_split());
986   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
987                                   circle::BuiltinOptions_SplitOptions, options.Union());
988   gd._operators.push_back(op_offset);
989 }
990
991 void OperationExporter::visit(luci::CircleSplitV *node)
992 {
993   auto split_outs = loco::succs(node);
994   assert(int32_t(split_outs.size()) == node->num_split());
995
996   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SPLIT_V, node->op_version());
997   std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
998                                   get_tensor_index(node->size_splits()),
999                                   get_tensor_index(node->split_dim())};
1000   std::vector<int32_t> outputs_vec;
1001
1002   for (int32_t index = 0; index < node->num_split(); index++)
1003   {
1004     // store in order of index
1005     bool found = false;
1006     for (auto out : split_outs)
1007     {
1008       auto split_out = loco::must_cast<luci::CircleSplitVOut *>(out);
1009       if (split_out->index() == index)
1010       {
1011         outputs_vec.push_back(get_tensor_index(split_out));
1012         found = true;
1013         break;
1014       }
1015     }
1016     if (!found)
1017     {
1018       INTERNAL_EXN("Invalid SplitV output");
1019     }
1020   }
1021
1022   auto inputs = builder.CreateVector(inputs_vec);
1023   auto outputs = builder.CreateVector(outputs_vec);
1024   auto options = CreateSplitVOptions(builder, node->num_split());
1025   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
1026                                   circle::BuiltinOptions_SplitVOptions, options.Union());
1027   gd._operators.push_back(op_offset);
1028 }
1029
1030 void OperationExporter::visit(luci::CircleSqrt *node)
1031 {
1032   export_simple(node, circle::BuiltinOperator_SQRT);
1033 }
1034
1035 void OperationExporter::visit(luci::CircleSquare *node)
1036 {
1037   export_simple(node, circle::BuiltinOperator_SQUARE, circle::BuiltinOptions_SquareOptions,
1038                 CreateSquareOptions(builder).Union());
1039 }
1040
1041 void OperationExporter::visit(luci::CircleSquaredDifference *node)
1042 {
1043   export_simple(node, circle::BuiltinOperator_SQUARED_DIFFERENCE,
1044                 circle::BuiltinOptions_SquaredDifferenceOptions,
1045                 CreateSquaredDifferenceOptions(builder).Union());
1046 }
1047
1048 void OperationExporter::visit(luci::CircleSqueeze *node)
1049 {
1050   auto squeeze_dims = builder.CreateVector<int32_t>(node->squeeze_dims());
1051   export_simple(node, circle::BuiltinOperator_SQUEEZE, circle::BuiltinOptions_SqueezeOptions,
1052                 CreateSqueezeOptions(builder, squeeze_dims).Union());
1053 }
1054
1055 void OperationExporter::visit(luci::CircleStridedSlice *node)
1056 {
1057   export_simple(node, circle::BuiltinOperator_STRIDED_SLICE,
1058                 circle::BuiltinOptions_StridedSliceOptions,
1059                 CreateStridedSliceOptions(builder, node->begin_mask(), node->end_mask(),
1060                                           node->ellipsis_mask(), node->new_axis_mask(),
1061                                           node->shrink_axis_mask())
1062                     .Union());
1063 }
1064
1065 void OperationExporter::visit(luci::CircleSub *node)
1066 {
1067   export_simple(
1068       node, circle::BuiltinOperator_SUB, circle::BuiltinOptions_SubOptions,
1069       CreateSubOptions(builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
1070 }
1071
1072 void OperationExporter::visit(luci::CircleSum *node)
1073 {
1074   export_simple(node, circle::BuiltinOperator_SUM, circle::BuiltinOptions_ReducerOptions,
1075                 CreateReducerOptions(builder, node->keep_dims()).Union());
1076 }
1077
1078 void OperationExporter::visit(luci::CircleTanh *node)
1079 {
1080   export_simple(node, circle::BuiltinOperator_TANH);
1081 }
1082
1083 void OperationExporter::visit(luci::CircleTile *node)
1084 {
1085   export_simple(node, circle::BuiltinOperator_TILE, circle::BuiltinOptions_TileOptions,
1086                 CreateTileOptions(builder).Union());
1087 }
1088
1089 void OperationExporter::visit(luci::CircleTopKV2 *node)
1090 {
1091   auto topkv2_outs = loco::succs(node);
1092   int outs_count = int32_t(topkv2_outs.size());
1093   assert(outs_count == 2);
1094
1095   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TOPK_V2, node->op_version());
1096   std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->k())};
1097   std::vector<int32_t> outputs_vec;
1098
1099   for (int32_t index = 0; index < outs_count; index++)
1100   {
1101     // store in order of index
1102     bool found = false;
1103     for (auto out : topkv2_outs)
1104     {
1105       auto topkv2_out = loco::must_cast<luci::CircleTopKV2Out *>(out);
1106       if (topkv2_out->index() == index)
1107       {
1108         outputs_vec.push_back(get_tensor_index(topkv2_out));
1109         found = true;
1110         break;
1111       }
1112     }
1113     if (!found)
1114     {
1115       INTERNAL_EXN("Invalid TopKV2 output");
1116     }
1117   }
1118
1119   auto inputs = builder.CreateVector(inputs_vec);
1120   auto outputs = builder.CreateVector(outputs_vec);
1121   auto options = CreateTopKV2Options(builder);
1122   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
1123                                   circle::BuiltinOptions_TopKV2Options, options.Union());
1124   gd._operators.push_back(op_offset);
1125 }
1126
1127 void OperationExporter::visit(luci::CircleTranspose *node)
1128 {
1129   export_simple(node, circle::BuiltinOperator_TRANSPOSE, circle::BuiltinOptions_TransposeOptions,
1130                 CreateTransposeOptions(builder).Union());
1131 }
1132
1133 void OperationExporter::visit(luci::CircleTransposeConv *node)
1134 {
1135   export_simple(node, circle::BuiltinOperator_TRANSPOSE_CONV,
1136                 circle::BuiltinOptions_TransposeConvOptions,
1137                 CreateTransposeConvOptions(builder, getOpPadding(node->padding()),
1138                                            node->stride()->w(), node->stride()->h())
1139                     .Union());
1140 }
1141
1142 void OperationExporter::visit(luci::CircleUnique *node)
1143 {
1144   auto unique_outs = loco::succs(node);
1145   assert(int32_t(unique_outs.size()) == 2);
1146   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_UNIQUE, node->op_version());
1147
1148   std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
1149   std::vector<int32_t> outputs_vec;
1150
1151   for (int32_t index = 0; index < 2; index++)
1152   {
1153     // store in order of index
1154     bool found = false;
1155     for (auto out : unique_outs)
1156     {
1157       auto unique_out = loco::must_cast<luci::CircleUniqueOut *>(out);
1158       if (unique_out->index() == index)
1159       {
1160         outputs_vec.push_back(get_tensor_index(unique_out));
1161         found = true;
1162         break;
1163       }
1164     }
1165     if (!found)
1166     {
1167       INTERNAL_EXN("Invalid Unique output");
1168     }
1169   }
1170
1171   auto inputs = builder.CreateVector(inputs_vec);
1172   auto outputs = builder.CreateVector(outputs_vec);
1173   auto options = CreateUniqueOptions(builder, to_circle_tensortype(node->idx_out_type()));
1174   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
1175                                   circle::BuiltinOptions_UniqueOptions, options.Union());
1176   gd._operators.push_back(op_offset);
1177 }
1178
1179 void OperationExporter::visit(luci::CircleUnpack *node)
1180 {
1181   LOGGER(l);
1182   auto settings = luci::UserSettings::settings();
1183
1184   auto unpack_outs = loco::succs(node);
1185   // NOTE real models may not use all of the outputs
1186   if (static_cast<int32_t>(unpack_outs.size()) != node->num())
1187   {
1188     if (settings->get(luci::UserSettings::Key::DisableValidation))
1189     {
1190       WARN(l) << "Warning: export Unpack(" << node->name() << ") 'num' not same as outputs";
1191     }
1192     else
1193       assert(false);
1194   }
1195
1196   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_UNPACK, node->op_version());
1197   std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
1198   std::vector<int32_t> outputs_vec;
1199
1200   for (int32_t index = 0; index < node->num(); index++)
1201   {
1202     // store in order of index
1203     bool found = false;
1204     for (auto out : unpack_outs)
1205     {
1206       auto unpack_out = loco::must_cast<luci::CircleUnpackOut *>(out);
1207       if (unpack_out->index() == index)
1208       {
1209         outputs_vec.push_back(get_tensor_index(unpack_out));
1210         found = true;
1211         break;
1212       }
1213     }
1214     // NOTE real models may not use all of the outputs
1215     if (!found)
1216     {
1217       if (settings->get(luci::UserSettings::Key::DisableValidation))
1218       {
1219         WARN(l) << "Warning: export Unpack(" << node->name() << ") output " << index << " not used";
1220       }
1221       else
1222         assert(false);
1223     }
1224   }
1225
1226   auto inputs = builder.CreateVector(inputs_vec);
1227   auto outputs = builder.CreateVector(outputs_vec);
1228   auto options = CreateUnpackOptions(builder, node->num(), node->axis());
1229   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
1230                                   circle::BuiltinOptions_UnpackOptions, options.Union());
1231   gd._operators.push_back(op_offset);
1232 }
1233
1234 void OperationExporter::visit(luci::CircleWhere *node)
1235 {
1236   export_simple(node, circle::BuiltinOperator_WHERE, circle::BuiltinOptions_WhereOptions,
1237                 CreateWhereOptions(builder).Union());
1238 }
1239
1240 void OperationExporter::visit(luci::CircleWhile *node)
1241 {
1242   auto while_outs = loco::succs(node);
1243   assert(while_outs.size() == node->output_count());
1244
1245   uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_WHILE, node->op_version());
1246   std::vector<int32_t> inputs_vec;
1247   std::vector<int32_t> outputs_vec;
1248
1249   for (uint32_t idx = 0; idx < node->input_count(); ++idx)
1250     inputs_vec.push_back(get_tensor_index(node->input(idx)));
1251
1252   for (uint32_t idx = 0; idx < node->output_count(); ++idx)
1253   {
1254     // store in order of index
1255     bool found = false;
1256     for (auto out : while_outs)
1257     {
1258       auto while_out = loco::must_cast<luci::CircleWhileOut *>(out);
1259       if (while_out->index() == static_cast<int32_t>(idx))
1260       {
1261         outputs_vec.push_back(get_tensor_index(while_out));
1262         found = true;
1263         break;
1264       }
1265     }
1266     if (!found)
1267     {
1268       INTERNAL_EXN("Invalid CircleWhile output");
1269     }
1270   }
1271
1272   auto inputs = builder.CreateVector(inputs_vec);
1273   auto outputs = builder.CreateVector(outputs_vec);
1274   auto options = CreateWhileOptions(builder, node->cond_branch(), node->body_branch());
1275   auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
1276                                   circle::BuiltinOptions_WhileOptions, options.Union());
1277   gd._operators.push_back(op_offset);
1278 }
1279
1280 void OperationExporter::visit(luci::CircleZerosLike *node)
1281 {
1282   export_simple(node, circle::BuiltinOperator_ZEROS_LIKE, circle::BuiltinOptions_ZerosLikeOptions,
1283                 CreateZerosLikeOptions(builder).Union());
1284 }
1285
1286 void OperationExporter::visit(luci::CircleBCQFullyConnected *node)
1287 {
1288   export_simple(node, circle::BuiltinOperator_BCQ_FULLY_CONNECTED,
1289                 circle::BuiltinOptions_BCQFullyConnectedOptions,
1290                 CreateBCQFullyConnectedOptions(builder, node->weights_hidden_size(),
1291                                                to_circle_actfunc(node->fusedActivationFunction()))
1292                     .Union());
1293 }
1294
1295 void OperationExporter::visit(luci::CircleBCQGather *node)
1296 {
1297   export_simple(node, circle::BuiltinOperator_BCQ_GATHER, circle::BuiltinOptions_BCQGatherOptions,
1298                 CreateBCQGatherOptions(builder, node->input_hidden_size(), node->axis()).Union());
1299 }
1300
1301 void OperationExporter::visit(luci::CircleInstanceNorm *node)
1302 {
1303   export_simple(node, circle::BuiltinOperator_INSTANCE_NORM,
1304                 circle::BuiltinOptions_InstanceNormOptions,
1305                 CreateInstanceNormOptions(builder, node->epsilon(),
1306                                           to_circle_actfunc(node->fusedActivationFunction()))
1307                     .Union());
1308 }
1309
1310 void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md,
1311                 SerializedGraphData &gd)
1312 {
1313   if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
1314   {
1315     OperationExporter exporter{builder, md, gd};
1316     circle_node->accept(&exporter);
1317   }
1318   else
1319   {
1320     INTERNAL_EXN("Node with unsupported dialect found");
1321   }
1322 }
1323
1324 } // namespace
1325
1326 namespace luci
1327 {
1328
1329 void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md,
1330                  SerializedGraphData &gd)
1331 {
1332   for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
1333   {
1334     exportNode(node, builder, md, gd);
1335   }
1336 }
1337
1338 } // namespace luci