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