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