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