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