2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "CircleOperationExporter.h"
18 #include "CircleExporterUtils.h"
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>
29 #include <loco/IR/CanonicalNodeVisitor.h>
30 #include <oops/InternalExn.h>
32 #include <flatbuffers/flexbuffers.h>
34 using namespace flatbuffers;
35 using namespace circle;
44 FlatBufferBuilder &builder;
45 SerializedModelData &md;
46 SerializedGraphData &gd;
50 * @brief Exports CircleMaxPool2D or CircleAveragePool2D
52 * @note CirclePool2D should be one of CircleMaxPool2D or CircleAveragePool2D
54 template <class CirclePool2D>
55 void export_pool_2d(ExportContext &ctx, CirclePool2D *node, circle::BuiltinOperator builtin_op)
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");
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);
69 circle::Padding padding = getOpPadding(node->padding());
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);
80 * @brief export simple nodes
82 void export_node(ExportContext &ctx, loco::Node *node, circle::BuiltinOperator bop,
83 circle::BuiltinOptions bot, flatbuffers::Offset<void> options_offset)
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);
98 * @brief export simple nodes having void options
100 void export_node(ExportContext &ctx, loco::Node *node, circle::BuiltinOperator bop)
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);
114 void export_node(ExportContext &ctx, luci::CircleAddN *node)
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))};
120 for (uint32_t i = 0; i < node->arity(); ++i)
121 inputs_vec.push_back(get_tensor_index(node->inputs(i)));
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);
131 void export_node(ExportContext &ctx, luci::CircleCast *node)
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);
139 flatbuffers::Offset<Operator> op_offset;
140 if (node->out_data_type() != loco::DataType::Unknown)
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());
149 op_offset = CreateOperator(ctx.builder, op_idx, inputs, outputs);
151 ctx.gd._operators.push_back(op_offset);
154 void export_node(ExportContext &ctx, luci::CircleConcatenation *node)
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))};
161 for (uint32_t i = 0; i < node->numValues(); ++i)
162 inputs_vec.push_back(get_tensor_index(node->values(i)));
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);
173 void export_node(ExportContext &ctx, luci::CircleCustom *node)
175 auto custom_outputs = loco::succs(node);
176 assert(custom_outputs.size() == node->numOutputs());
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;
182 for (uint32_t index = 0; index < node->numInputs(); index++)
184 inputs_vec.push_back(get_tensor_index(node->inputs(index)));
186 for (uint32_t index = 0; index < custom_outputs.size(); index++)
188 // store in order of index
190 for (auto out : custom_outputs)
192 auto custom_out = loco::must_cast<luci::CircleCustomOut *>(out);
193 if (custom_out->index() == static_cast<int32_t>(index))
195 outputs_vec.push_back(get_tensor_index(custom_out));
202 INTERNAL_EXN("Invalid Custom output");
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);
217 void export_node(ExportContext &ctx, luci::CircleIf *node)
219 auto if_outs = loco::succs(node);
220 assert(if_outs.size() == node->output_count());
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;
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)));
230 for (uint32_t idx = 0; idx < node->output_count(); ++idx)
232 // store in order of index
234 for (auto out : if_outs)
236 auto if_out = loco::must_cast<luci::CircleIfOut *>(out);
237 if (if_out->index() == static_cast<int32_t>(idx))
239 outputs_vec.push_back(get_tensor_index(if_out));
246 INTERNAL_EXN("Invalid CircleIf output");
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);
258 void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV4 *node)
260 auto nms_outs = loco::succs(node);
261 assert(nms_outs.size() == 2);
263 uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_NON_MAX_SUPPRESSION_V4,
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()),
270 std::vector<int32_t> outputs_vec;
272 for (uint32_t idx = 0; idx < nms_outs.size(); ++idx)
274 // store in order of index
276 for (auto out : nms_outs)
278 auto nms_out = loco::must_cast<luci::CircleNonMaxSuppressionV4Out *>(out);
279 if (nms_out->index() == static_cast<int32_t>(idx))
281 outputs_vec.push_back(get_tensor_index(nms_out));
288 INTERNAL_EXN("Invalid NonMaxSuppressionV4 output");
292 auto inputs = ctx.builder.CreateVector(inputs_vec);
293 auto outputs = ctx.builder.CreateVector(outputs_vec);
294 auto options = CreateNonMaxSuppressionV4Options(ctx.builder);
296 CreateOperator(ctx.builder, op_idx, inputs, outputs,
297 circle::BuiltinOptions_NonMaxSuppressionV4Options, options.Union());
298 ctx.gd._operators.push_back(op_offset);
301 void export_node(ExportContext &ctx, luci::CircleNonMaxSuppressionV5 *node)
303 auto nms_outs = loco::succs(node);
304 assert(nms_outs.size() == 3);
306 uint32_t op_idx = ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_NON_MAX_SUPPRESSION_V5,
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()),
313 std::vector<int32_t> outputs_vec;
315 for (uint32_t idx = 0; idx < nms_outs.size(); ++idx)
317 // store in order of index
319 for (auto out : nms_outs)
321 auto nms_out = loco::must_cast<luci::CircleNonMaxSuppressionV5Out *>(out);
322 if (nms_out->index() == static_cast<int32_t>(idx))
324 outputs_vec.push_back(get_tensor_index(nms_out));
331 INTERNAL_EXN("Invalid NonMaxSuppressionV5 output");
335 auto inputs = ctx.builder.CreateVector(inputs_vec);
336 auto outputs = ctx.builder.CreateVector(outputs_vec);
337 auto options = CreateNonMaxSuppressionV5Options(ctx.builder);
339 CreateOperator(ctx.builder, op_idx, inputs, outputs,
340 circle::BuiltinOptions_NonMaxSuppressionV5Options, options.Union());
341 ctx.gd._operators.push_back(op_offset);
344 void export_node(ExportContext &ctx, luci::CircleReverseV2 *node)
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);
358 void export_node(ExportContext &ctx, luci::CircleSplit *node)
360 auto split_outs = loco::succs(node);
361 assert(int32_t(split_outs.size()) == node->num_split());
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;
369 for (int32_t index = 0; index < node->num_split(); index++)
371 // store in order of index
373 for (auto out : split_outs)
375 auto split_out = loco::must_cast<luci::CircleSplitOut *>(out);
376 if (split_out->index() == index)
378 outputs_vec.push_back(get_tensor_index(split_out));
385 INTERNAL_EXN("Invalid Split output");
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);
397 void export_node(ExportContext &ctx, luci::CircleSplitV *node)
399 auto split_outs = loco::succs(node);
400 assert(int32_t(split_outs.size()) == node->num_split());
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;
409 for (int32_t index = 0; index < node->num_split(); index++)
411 // store in order of index
413 for (auto out : split_outs)
415 auto split_out = loco::must_cast<luci::CircleSplitVOut *>(out);
416 if (split_out->index() == index)
418 outputs_vec.push_back(get_tensor_index(split_out));
425 INTERNAL_EXN("Invalid SplitV output");
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);
437 void export_node(ExportContext &ctx, luci::CircleTopKV2 *node)
439 auto topkv2_outs = loco::succs(node);
440 int outs_count = int32_t(topkv2_outs.size());
441 assert(outs_count == 2);
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;
448 for (int32_t index = 0; index < outs_count; index++)
450 // store in order of index
452 for (auto out : topkv2_outs)
454 auto topkv2_out = loco::must_cast<luci::CircleTopKV2Out *>(out);
455 if (topkv2_out->index() == index)
457 outputs_vec.push_back(get_tensor_index(topkv2_out));
464 INTERNAL_EXN("Invalid TopKV2 output");
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);
476 void export_node(ExportContext &ctx, luci::CircleUnique *node)
478 auto unique_outs = loco::succs(node);
479 assert(int32_t(unique_outs.size()) == 2);
481 ctx.md.registerBuiltinOpcode(circle::BuiltinOperator_UNIQUE, node->op_version());
483 std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
484 std::vector<int32_t> outputs_vec;
486 for (int32_t index = 0; index < 2; index++)
488 // store in order of index
490 for (auto out : unique_outs)
492 auto unique_out = loco::must_cast<luci::CircleUniqueOut *>(out);
493 if (unique_out->index() == index)
495 outputs_vec.push_back(get_tensor_index(unique_out));
502 INTERNAL_EXN("Invalid Unique output");
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);
514 void export_node(ExportContext &ctx, luci::CircleUnpack *node)
517 auto settings = luci::UserSettings::settings();
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())
523 if (settings->get(luci::UserSettings::Key::DisableValidation))
525 WARN(l) << "Warning: export Unpack(" << node->name() << ") 'num' not same as outputs";
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;
536 for (int32_t index = 0; index < node->num(); index++)
538 // store in order of index
540 for (auto out : unpack_outs)
542 auto unpack_out = loco::must_cast<luci::CircleUnpackOut *>(out);
543 if (unpack_out->index() == index)
545 outputs_vec.push_back(get_tensor_index(unpack_out));
550 // NOTE real models may not use all of the outputs
553 if (settings->get(luci::UserSettings::Key::DisableValidation))
555 WARN(l) << "Warning: export Unpack(" << node->name() << ") output " << index << " not used";
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);
570 void export_node(ExportContext &ctx, luci::CircleWhile *node)
572 auto while_outs = loco::succs(node);
573 assert(while_outs.size() == node->output_count());
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;
579 for (uint32_t idx = 0; idx < node->input_count(); ++idx)
580 inputs_vec.push_back(get_tensor_index(node->input(idx)));
582 for (uint32_t idx = 0; idx < node->output_count(); ++idx)
584 // store in order of index
586 for (auto out : while_outs)
588 auto while_out = loco::must_cast<luci::CircleWhileOut *>(out);
589 if (while_out->index() == static_cast<int32_t>(idx))
591 outputs_vec.push_back(get_tensor_index(while_out));
598 INTERNAL_EXN("Invalid CircleWhile output");
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);
613 ExportHelper(ExportContext &ctx) : _ctx{ctx}
620 * @brief export simple nodes
622 void export_simple(loco::Node *node, circle::BuiltinOperator bop, circle::BuiltinOptions bot,
623 flatbuffers::Offset<void> options_offset)
625 export_node(_ctx, node, bop, bot, options_offset);
629 * @brief export simple nodes having void options
631 void export_simple(loco::Node *node, circle::BuiltinOperator bop)
633 export_node(_ctx, node, bop);
653 class OperationExporter final : public ExportHelper
656 OperationExporter(ExportContext &ctx) : ExportHelper(ctx)
662 void export_node(luci::CircleNode *);
665 template <OE oe> class OpExporterLet;
668 class OpExporterLet<OE::ABC> final : public luci::CircleNodeMutableVisitor<void>,
672 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
678 // NOTE visit for luci::CircleNode is added NOT to throw NYI
679 void visit(luci::CircleNode *) final {}
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;
701 class OpExporterLet<OE::DEF> final : public luci::CircleNodeMutableVisitor<void>,
705 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
711 void visit(luci::CircleNode *) final {}
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;
731 class OpExporterLet<OE::GHIJ> final : public luci::CircleNodeMutableVisitor<void>,
735 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
741 void visit(luci::CircleNode *) final {}
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;
752 class OpExporterLet<OE::KLMN> final : public luci::CircleNodeMutableVisitor<void>,
756 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
762 void visit(luci::CircleNode *) final {}
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;
792 class OpExporterLet<OE::OPQR> final : public luci::CircleNodeMutableVisitor<void>,
796 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
802 void visit(luci::CircleNode *) final {}
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;
831 class OpExporterLet<OE::STUV> final : public luci::CircleNodeMutableVisitor<void>,
835 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
841 void visit(luci::CircleNode *) final {}
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;
875 class OpExporterLet<OE::WXYZ> final : public luci::CircleNodeMutableVisitor<void>,
879 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
885 void visit(luci::CircleNode *) final {}
888 void visit(luci::CircleWhere *) final;
889 void visit(luci::CircleWhile *) final;
890 void visit(luci::CircleZerosLike *) final;
894 class OpExporterLet<OE::CIRC> final : public luci::CircleNodeMutableVisitor<void>,
898 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
904 void visit(luci::CircleNode *) final {}
908 void visit(luci::CircleBCQFullyConnected *) final;
909 void visit(luci::CircleBCQGather *) final;
910 void visit(luci::CircleInstanceNorm *) final;
914 class OpExporterLet<OE::VIRT> final : public luci::CircleNodeMutableVisitor<void>,
918 OpExporterLet(ExportContext &ctx) : ExportHelper(ctx)
924 void visit(luci::CircleNode *) final {}
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 {}
946 void OperationExporter::export_node(luci::CircleNode *node)
948 // TODO revise return type to bool and return if handled
949 #define VISIT_OE(GRP) \
952 OpExporterLet<OE::GRP> oe(_ctx); \
969 void OpExporterLet<OE::ABC>::visit(luci::CircleAbs *node)
971 export_simple(node, circle::BuiltinOperator_ABS, circle::BuiltinOptions_AbsOptions,
972 CreateAbsOptions(_ctx.builder).Union());
975 void OpExporterLet<OE::ABC>::visit(luci::CircleAdd *node)
978 node, circle::BuiltinOperator_ADD, circle::BuiltinOptions_AddOptions,
979 CreateAddOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
982 void OpExporterLet<OE::ABC>::visit(luci::CircleAddN *node) { export_node(_ctx, node); }
984 void OpExporterLet<OE::ABC>::visit(luci::CircleArgMax *node)
987 node, circle::BuiltinOperator_ARG_MAX, circle::BuiltinOptions_ArgMaxOptions,
988 CreateArgMaxOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union());
991 void OpExporterLet<OE::ABC>::visit(luci::CircleArgMin *node)
994 node, circle::BuiltinOperator_ARG_MIN, circle::BuiltinOptions_ArgMinOptions,
995 CreateArgMinOptions(_ctx.builder, to_circle_tensortype(node->output_type())).Union());
998 void OpExporterLet<OE::ABC>::visit(luci::CircleAveragePool2D *node)
1000 export_pool_2d<luci::CircleAveragePool2D>(_ctx, node, circle::BuiltinOperator_AVERAGE_POOL_2D);
1003 void OpExporterLet<OE::ABC>::visit(luci::CircleBatchMatMul *node)
1005 export_simple(node, circle::BuiltinOperator_BATCH_MATMUL,
1006 circle::BuiltinOptions_BatchMatMulOptions,
1007 CreateBatchMatMulOptions(_ctx.builder, node->adj_x(), node->adj_y()).Union());
1010 void OpExporterLet<OE::ABC>::visit(luci::CircleBidirectionalSequenceLSTM *node)
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());
1017 std::vector<int32_t> inputs_vec{get_tensor_index(node->input())};
1018 std::vector<int32_t> outputs_vec;
1020 for (int32_t index = 0; index < 2; index++)
1022 // store in order of index
1024 for (auto out : bidi_lstm_outs)
1026 auto bidi_lstm_out = loco::must_cast<luci::CircleBidirectionalSequenceLSTMOut *>(out);
1027 if (bidi_lstm_out->index() == index)
1029 outputs_vec.push_back(get_tensor_index(bidi_lstm_out));
1036 INTERNAL_EXN("Invalid BidirectionalSequenceLSTM output");
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());
1047 CreateOperator(_ctx.builder, op_idx, inputs, outputs,
1048 circle::BuiltinOptions_BidirectionalSequenceLSTMOptions, options.Union());
1049 _ctx.gd._operators.push_back(op_offset);
1052 void OpExporterLet<OE::ABC>::visit(luci::CircleCast *node) { export_node(_ctx, node); }
1054 void OpExporterLet<OE::ABC>::visit(luci::CircleCeil *node)
1056 export_simple(node, circle::BuiltinOperator_CEIL);
1059 void OpExporterLet<OE::ABC>::visit(luci::CircleConcatenation *node) { export_node(_ctx, node); }
1061 void OpExporterLet<OE::ABC>::visit(luci::CircleBatchToSpaceND *node)
1063 export_simple(node, circle::BuiltinOperator_BATCH_TO_SPACE_ND,
1064 circle::BuiltinOptions_BatchToSpaceNDOptions,
1065 CreateBatchToSpaceNDOptions(_ctx.builder).Union());
1068 void OpExporterLet<OE::ABC>::visit(luci::CircleConv2D *node)
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())
1078 void OpExporterLet<OE::ABC>::visit(luci::CircleCos *node)
1080 export_simple(node, circle::BuiltinOperator_COS, circle::BuiltinOptions_CosOptions,
1081 CreateCosOptions(_ctx.builder).Union());
1084 void OpExporterLet<OE::ABC>::visit(luci::CircleCustom *node) { export_node(_ctx, node); }
1086 void OpExporterLet<OE::DEF>::visit(luci::CircleDepthToSpace *node)
1088 export_simple(node, circle::BuiltinOperator_DEPTH_TO_SPACE,
1089 circle::BuiltinOptions_DepthToSpaceOptions,
1090 CreateDepthToSpaceOptions(_ctx.builder, node->block_size()).Union());
1093 void OpExporterLet<OE::DEF>::visit(luci::CircleDepthwiseConv2D *node)
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())
1104 void OpExporterLet<OE::DEF>::visit(luci::CircleDequantize *node)
1106 export_simple(node, circle::BuiltinOperator_DEQUANTIZE);
1109 void OpExporterLet<OE::DEF>::visit(luci::CircleDiv *node)
1112 node, circle::BuiltinOperator_DIV, circle::BuiltinOptions_DivOptions,
1113 CreateDivOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
1116 void OpExporterLet<OE::DEF>::visit(luci::CircleElu *node)
1118 export_simple(node, circle::BuiltinOperator_ELU);
1121 void OpExporterLet<OE::DEF>::visit(luci::CircleEqual *node)
1123 export_simple(node, circle::BuiltinOperator_EQUAL, circle::BuiltinOptions_EqualOptions,
1124 CreateEqualOptions(_ctx.builder).Union());
1127 void OpExporterLet<OE::DEF>::visit(luci::CircleExp *node)
1129 export_simple(node, circle::BuiltinOperator_EXP, circle::BuiltinOptions_ExpOptions,
1130 CreateExpOptions(_ctx.builder).Union());
1133 void OpExporterLet<OE::DEF>::visit(luci::CircleExpandDims *node)
1135 export_simple(node, circle::BuiltinOperator_EXPAND_DIMS, circle::BuiltinOptions_ExpandDimsOptions,
1136 CreateExpandDimsOptions(_ctx.builder).Union());
1139 void OpExporterLet<OE::DEF>::visit(luci::CircleFakeQuant *node)
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())
1147 void OpExporterLet<OE::DEF>::visit(luci::CircleFill *node)
1149 export_simple(node, circle::BuiltinOperator_FILL, circle::BuiltinOptions_FillOptions,
1150 CreateFillOptions(_ctx.builder).Union());
1153 void OpExporterLet<OE::DEF>::visit(luci::CircleFloor *node)
1155 export_simple(node, circle::BuiltinOperator_FLOOR);
1158 void OpExporterLet<OE::DEF>::visit(luci::CircleFloorDiv *node)
1160 export_simple(node, circle::BuiltinOperator_FLOOR_DIV, circle::BuiltinOptions_FloorDivOptions,
1161 CreateFloorDivOptions(_ctx.builder).Union());
1164 void OpExporterLet<OE::DEF>::visit(luci::CircleFloorMod *node)
1166 export_simple(node, circle::BuiltinOperator_FLOOR_MOD, circle::BuiltinOptions_FloorModOptions,
1167 CreateFloorModOptions(_ctx.builder).Union());
1170 void OpExporterLet<OE::DEF>::visit(luci::CircleFullyConnected *node)
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()))
1179 void OpExporterLet<OE::GHIJ>::visit(luci::CircleGather *node)
1181 export_simple(node, circle::BuiltinOperator_GATHER, circle::BuiltinOptions_GatherOptions,
1182 CreateGatherOptions(_ctx.builder, node->axis()).Union());
1185 void OpExporterLet<OE::GHIJ>::visit(luci::CircleGatherNd *node)
1187 export_simple(node, circle::BuiltinOperator_GATHER_ND, circle::BuiltinOptions_GatherNdOptions,
1188 CreateGatherNdOptions(_ctx.builder).Union());
1191 void OpExporterLet<OE::GHIJ>::visit(luci::CircleGreater *node)
1193 export_simple(node, circle::BuiltinOperator_GREATER, circle::BuiltinOptions_GreaterOptions,
1194 CreateGreaterOptions(_ctx.builder).Union());
1197 void OpExporterLet<OE::GHIJ>::visit(luci::CircleGreaterEqual *node)
1199 export_simple(node, circle::BuiltinOperator_GREATER_EQUAL,
1200 circle::BuiltinOptions_GreaterEqualOptions,
1201 CreateGreaterEqualOptions(_ctx.builder).Union());
1204 void OpExporterLet<OE::GHIJ>::visit(luci::CircleIf *node) { export_node(_ctx, node); }
1206 void OpExporterLet<OE::KLMN>::visit(luci::CircleL2Normalize *node)
1209 node, circle::BuiltinOperator_L2_NORMALIZATION, circle::BuiltinOptions_L2NormOptions,
1210 CreateL2NormOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
1213 void OpExporterLet<OE::KLMN>::visit(luci::CircleL2Pool2D *node)
1215 export_pool_2d<luci::CircleL2Pool2D>(_ctx, node, circle::BuiltinOperator_L2_POOL_2D);
1218 void OpExporterLet<OE::KLMN>::visit(luci::CircleLeakyRelu *node)
1220 export_simple(node, circle::BuiltinOperator_LEAKY_RELU, circle::BuiltinOptions_LeakyReluOptions,
1221 CreateLeakyReluOptions(_ctx.builder, node->alpha()).Union());
1224 void OpExporterLet<OE::KLMN>::visit(luci::CircleLess *node)
1226 export_simple(node, circle::BuiltinOperator_LESS, circle::BuiltinOptions_LessOptions,
1227 CreateLessOptions(_ctx.builder).Union());
1230 void OpExporterLet<OE::KLMN>::visit(luci::CircleLessEqual *node)
1232 export_simple(node, circle::BuiltinOperator_LESS_EQUAL, circle::BuiltinOptions_LessEqualOptions,
1233 CreateLessEqualOptions(_ctx.builder).Union());
1236 void OpExporterLet<OE::KLMN>::visit(luci::CircleLocalResponseNormalization *node)
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())
1245 void OpExporterLet<OE::KLMN>::visit(luci::CircleLog *node)
1247 export_simple(node, circle::BuiltinOperator_LOG);
1250 void OpExporterLet<OE::KLMN>::visit(luci::CircleLogicalAnd *node)
1252 export_simple(node, circle::BuiltinOperator_LOGICAL_AND, circle::BuiltinOptions_LogicalAndOptions,
1253 CreateLogicalAndOptions(_ctx.builder).Union());
1256 void OpExporterLet<OE::KLMN>::visit(luci::CircleLogicalNot *node)
1258 export_simple(node, circle::BuiltinOperator_LOGICAL_NOT, circle::BuiltinOptions_LogicalNotOptions,
1259 CreateLogicalNotOptions(_ctx.builder).Union());
1262 void OpExporterLet<OE::KLMN>::visit(luci::CircleLogicalOr *node)
1264 export_simple(node, circle::BuiltinOperator_LOGICAL_OR, circle::BuiltinOptions_LogicalOrOptions,
1265 CreateLogicalOrOptions(_ctx.builder).Union());
1268 void OpExporterLet<OE::KLMN>::visit(luci::CircleLogistic *node)
1270 export_simple(node, circle::BuiltinOperator_LOGISTIC);
1273 void OpExporterLet<OE::KLMN>::visit(luci::CircleLogSoftmax *node)
1275 export_simple(node, circle::BuiltinOperator_LOG_SOFTMAX, circle::BuiltinOptions_LogSoftmaxOptions,
1276 CreateLogSoftmaxOptions(_ctx.builder).Union());
1279 void OpExporterLet<OE::KLMN>::visit(luci::CircleMatrixDiag *node)
1281 export_simple(node, circle::BuiltinOperator_MATRIX_DIAG, circle::BuiltinOptions_MatrixDiagOptions,
1282 CreateMatrixDiagOptions(_ctx.builder).Union());
1285 void OpExporterLet<OE::KLMN>::visit(luci::CircleMatrixSetDiag *node)
1287 export_simple(node, circle::BuiltinOperator_MATRIX_SET_DIAG,
1288 circle::BuiltinOptions_MatrixSetDiagOptions,
1289 CreateMatrixSetDiagOptions(_ctx.builder).Union());
1292 void OpExporterLet<OE::KLMN>::visit(luci::CircleMaximum *node)
1294 export_simple(node, circle::BuiltinOperator_MAXIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
1295 CreateMaximumMinimumOptions(_ctx.builder).Union());
1298 void OpExporterLet<OE::KLMN>::visit(luci::CircleMaxPool2D *node)
1300 export_pool_2d<luci::CircleMaxPool2D>(_ctx, node, circle::BuiltinOperator_MAX_POOL_2D);
1303 void OpExporterLet<OE::KLMN>::visit(luci::CircleMean *node)
1305 export_simple(node, circle::BuiltinOperator_MEAN, circle::BuiltinOptions_ReducerOptions,
1306 CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
1309 void OpExporterLet<OE::KLMN>::visit(luci::CircleMinimum *node)
1311 export_simple(node, circle::BuiltinOperator_MINIMUM, circle::BuiltinOptions_MaximumMinimumOptions,
1312 CreateMaximumMinimumOptions(_ctx.builder).Union());
1315 void OpExporterLet<OE::KLMN>::visit(luci::CircleMirrorPad *node)
1318 node, circle::BuiltinOperator_MIRROR_PAD, circle::BuiltinOptions_MirrorPadOptions,
1319 CreateMirrorPadOptions(_ctx.builder, to_circle_mirrorpadmode(node->mode())).Union());
1322 void OpExporterLet<OE::KLMN>::visit(luci::CircleMul *node)
1325 node, circle::BuiltinOperator_MUL, circle::BuiltinOptions_MulOptions,
1326 CreateMulOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
1329 void OpExporterLet<OE::KLMN>::visit(luci::CircleNeg *node)
1331 export_simple(node, circle::BuiltinOperator_NEG, circle::BuiltinOptions_NegOptions,
1332 CreateNegOptions(_ctx.builder).Union());
1335 void OpExporterLet<OE::KLMN>::visit(luci::CircleNonMaxSuppressionV4 *node)
1337 export_node(_ctx, node);
1340 void OpExporterLet<OE::KLMN>::visit(luci::CircleNonMaxSuppressionV5 *node)
1342 export_node(_ctx, node);
1345 void OpExporterLet<OE::KLMN>::visit(luci::CircleNotEqual *node)
1347 export_simple(node, circle::BuiltinOperator_NOT_EQUAL, circle::BuiltinOptions_NotEqualOptions,
1348 CreateNotEqualOptions(_ctx.builder).Union());
1351 void OpExporterLet<OE::OPQR>::visit(luci::CircleOneHot *node)
1353 export_simple(node, circle::BuiltinOperator_ONE_HOT, circle::BuiltinOptions_OneHotOptions,
1354 CreateOneHotOptions(_ctx.builder, node->axis()).Union());
1357 void OpExporterLet<OE::OPQR>::visit(luci::CirclePack *node)
1359 export_simple(node, circle::BuiltinOperator_PACK, circle::BuiltinOptions_PackOptions,
1360 CreatePackOptions(_ctx.builder, node->values_count(), node->axis()).Union());
1363 void OpExporterLet<OE::OPQR>::visit(luci::CirclePad *node)
1365 export_simple(node, circle::BuiltinOperator_PAD, circle::BuiltinOptions_PadOptions,
1366 CreatePadOptions(_ctx.builder).Union());
1369 void OpExporterLet<OE::OPQR>::visit(luci::CirclePadV2 *node)
1371 export_simple(node, circle::BuiltinOperator_PADV2, circle::BuiltinOptions_PadV2Options,
1372 CreatePadV2Options(_ctx.builder).Union());
1375 void OpExporterLet<OE::OPQR>::visit(luci::CirclePow *node)
1377 export_simple(node, circle::BuiltinOperator_POW, circle::BuiltinOptions_PowOptions,
1378 CreatePowOptions(_ctx.builder).Union());
1381 void OpExporterLet<OE::OPQR>::visit(luci::CirclePRelu *node)
1383 export_simple(node, circle::BuiltinOperator_PRELU);
1386 void OpExporterLet<OE::OPQR>::visit(luci::CircleQuantize *node)
1388 export_simple(node, circle::BuiltinOperator_QUANTIZE);
1391 void OpExporterLet<OE::OPQR>::visit(luci::CircleRange *node)
1393 export_simple(node, circle::BuiltinOperator_RANGE, circle::BuiltinOptions_RangeOptions,
1394 CreateRangeOptions(_ctx.builder).Union());
1397 void OpExporterLet<OE::OPQR>::visit(luci::CircleRank *node)
1399 export_simple(node, circle::BuiltinOperator_RANK, circle::BuiltinOptions_RankOptions,
1400 CreateRankOptions(_ctx.builder).Union());
1403 void OpExporterLet<OE::OPQR>::visit(luci::CircleReduceAny *node)
1405 export_simple(node, circle::BuiltinOperator_REDUCE_ANY, circle::BuiltinOptions_ReducerOptions,
1406 CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
1409 void OpExporterLet<OE::OPQR>::visit(luci::CircleReduceMax *node)
1411 export_simple(node, circle::BuiltinOperator_REDUCE_MAX, circle::BuiltinOptions_ReducerOptions,
1412 CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
1415 void OpExporterLet<OE::OPQR>::visit(luci::CircleReduceMin *node)
1417 export_simple(node, circle::BuiltinOperator_REDUCE_MIN, circle::BuiltinOptions_ReducerOptions,
1418 CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
1421 void OpExporterLet<OE::OPQR>::visit(luci::CircleReduceProd *node)
1423 export_simple(node, circle::BuiltinOperator_REDUCE_PROD, circle::BuiltinOptions_ReducerOptions,
1424 CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
1427 void OpExporterLet<OE::OPQR>::visit(luci::CircleRelu *node)
1429 export_simple(node, circle::BuiltinOperator_RELU);
1432 void OpExporterLet<OE::OPQR>::visit(luci::CircleRelu6 *node)
1434 export_simple(node, circle::BuiltinOperator_RELU6);
1437 void OpExporterLet<OE::OPQR>::visit(luci::CircleReluN1To1 *node)
1439 export_simple(node, circle::BuiltinOperator_RELU_N1_TO_1);
1442 void OpExporterLet<OE::OPQR>::visit(luci::CircleReshape *node)
1444 auto new_shape = _ctx.builder.CreateVector<int32_t>(
1445 node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); });
1447 export_simple(node, circle::BuiltinOperator_RESHAPE, circle::BuiltinOptions_ReshapeOptions,
1448 CreateReshapeOptions(_ctx.builder, new_shape).Union());
1451 void OpExporterLet<OE::OPQR>::visit(luci::CircleResizeBilinear *node)
1454 node, circle::BuiltinOperator_RESIZE_BILINEAR, circle::BuiltinOptions_ResizeBilinearOptions,
1455 CreateResizeBilinearOptions(_ctx.builder, node->align_corners(), node->half_pixel_centers())
1459 void OpExporterLet<OE::OPQR>::visit(luci::CircleResizeNearestNeighbor *node)
1461 export_simple(node, circle::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR,
1462 circle::BuiltinOptions_ResizeNearestNeighborOptions,
1463 CreateResizeNearestNeighborOptions(_ctx.builder, node->align_corners()).Union());
1466 void OpExporterLet<OE::OPQR>::visit(luci::CircleReverseSequence *node)
1469 node, circle::BuiltinOperator_REVERSE_SEQUENCE, circle::BuiltinOptions_ReverseSequenceOptions,
1470 CreateReverseSequenceOptions(_ctx.builder, node->seq_axis(), node->batch_axis()).Union());
1473 void OpExporterLet<OE::OPQR>::visit(luci::CircleReverseV2 *node) { export_node(_ctx, node); }
1475 void OpExporterLet<OE::OPQR>::visit(luci::CircleRound *node)
1477 export_simple(node, circle::BuiltinOperator_ROUND);
1480 void OpExporterLet<OE::OPQR>::visit(luci::CircleRsqrt *node)
1482 export_simple(node, circle::BuiltinOperator_RSQRT);
1485 void OpExporterLet<OE::STUV>::visit(luci::CircleScatterNd *node)
1487 export_simple(node, circle::BuiltinOperator_SCATTER_ND, circle::BuiltinOptions_ScatterNdOptions,
1488 CreateScatterNdOptions(_ctx.builder).Union());
1491 void OpExporterLet<OE::STUV>::visit(luci::CircleSegmentSum *node)
1493 export_simple(node, circle::BuiltinOperator_SEGMENT_SUM, circle::BuiltinOptions_SegmentSumOptions,
1494 CreateSegmentSumOptions(_ctx.builder).Union());
1497 void OpExporterLet<OE::STUV>::visit(luci::CircleSelect *node)
1499 export_simple(node, circle::BuiltinOperator_SELECT, circle::BuiltinOptions_SelectOptions,
1500 CreateSelectOptions(_ctx.builder).Union());
1503 void OpExporterLet<OE::STUV>::visit(luci::CircleSelectV2 *node)
1505 export_simple(node, circle::BuiltinOperator_SELECT_V2, circle::BuiltinOptions_SelectV2Options,
1506 CreateSelectV2Options(_ctx.builder).Union());
1509 void OpExporterLet<OE::STUV>::visit(luci::CircleShape *node)
1511 export_simple(node, circle::BuiltinOperator_SHAPE, circle::BuiltinOptions_ShapeOptions,
1512 CreateShapeOptions(_ctx.builder, to_circle_tensortype(node->out_type())).Union());
1515 void OpExporterLet<OE::STUV>::visit(luci::CircleSin *node)
1517 export_simple(node, circle::BuiltinOperator_SIN);
1520 void OpExporterLet<OE::STUV>::visit(luci::CircleSlice *node)
1522 export_simple(node, circle::BuiltinOperator_SLICE, circle::BuiltinOptions_SliceOptions,
1523 CreateSliceOptions(_ctx.builder).Union());
1526 void OpExporterLet<OE::STUV>::visit(luci::CircleSoftmax *node)
1528 export_simple(node, circle::BuiltinOperator_SOFTMAX, circle::BuiltinOptions_SoftmaxOptions,
1529 CreateSoftmaxOptions(_ctx.builder, node->beta()).Union());
1532 void OpExporterLet<OE::STUV>::visit(luci::CircleSpaceToBatchND *node)
1534 export_simple(node, circle::BuiltinOperator_SPACE_TO_BATCH_ND,
1535 circle::BuiltinOptions_SpaceToBatchNDOptions,
1536 CreateSpaceToBatchNDOptions(_ctx.builder).Union());
1539 void OpExporterLet<OE::STUV>::visit(luci::CircleSpaceToDepth *node)
1541 export_simple(node, circle::BuiltinOperator_SPACE_TO_DEPTH,
1542 circle::BuiltinOptions_SpaceToDepthOptions,
1543 CreateSpaceToDepthOptions(_ctx.builder, node->block_size()).Union());
1546 void OpExporterLet<OE::STUV>::visit(luci::CircleSparseToDense *node)
1548 export_simple(node, circle::BuiltinOperator_SPARSE_TO_DENSE,
1549 circle::BuiltinOptions_SparseToDenseOptions,
1550 CreateSparseToDenseOptions(_ctx.builder, node->validate_indices()).Union());
1553 void OpExporterLet<OE::STUV>::visit(luci::CircleSplit *node) { export_node(_ctx, node); }
1555 void OpExporterLet<OE::STUV>::visit(luci::CircleSplitV *node) { export_node(_ctx, node); }
1557 void OpExporterLet<OE::STUV>::visit(luci::CircleSqrt *node)
1559 export_simple(node, circle::BuiltinOperator_SQRT);
1562 void OpExporterLet<OE::STUV>::visit(luci::CircleSquare *node)
1564 export_simple(node, circle::BuiltinOperator_SQUARE, circle::BuiltinOptions_SquareOptions,
1565 CreateSquareOptions(_ctx.builder).Union());
1568 void OpExporterLet<OE::STUV>::visit(luci::CircleSquaredDifference *node)
1570 export_simple(node, circle::BuiltinOperator_SQUARED_DIFFERENCE,
1571 circle::BuiltinOptions_SquaredDifferenceOptions,
1572 CreateSquaredDifferenceOptions(_ctx.builder).Union());
1575 void OpExporterLet<OE::STUV>::visit(luci::CircleSqueeze *node)
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());
1582 void OpExporterLet<OE::STUV>::visit(luci::CircleStridedSlice *node)
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())
1592 void OpExporterLet<OE::STUV>::visit(luci::CircleSub *node)
1595 node, circle::BuiltinOperator_SUB, circle::BuiltinOptions_SubOptions,
1596 CreateSubOptions(_ctx.builder, to_circle_actfunc(node->fusedActivationFunction())).Union());
1599 void OpExporterLet<OE::STUV>::visit(luci::CircleSum *node)
1601 export_simple(node, circle::BuiltinOperator_SUM, circle::BuiltinOptions_ReducerOptions,
1602 CreateReducerOptions(_ctx.builder, node->keep_dims()).Union());
1605 void OpExporterLet<OE::STUV>::visit(luci::CircleTanh *node)
1607 export_simple(node, circle::BuiltinOperator_TANH);
1610 void OpExporterLet<OE::STUV>::visit(luci::CircleTile *node)
1612 export_simple(node, circle::BuiltinOperator_TILE, circle::BuiltinOptions_TileOptions,
1613 CreateTileOptions(_ctx.builder).Union());
1616 void OpExporterLet<OE::STUV>::visit(luci::CircleTopKV2 *node) { export_node(_ctx, node); }
1618 void OpExporterLet<OE::STUV>::visit(luci::CircleTranspose *node)
1620 export_simple(node, circle::BuiltinOperator_TRANSPOSE, circle::BuiltinOptions_TransposeOptions,
1621 CreateTransposeOptions(_ctx.builder).Union());
1624 void OpExporterLet<OE::STUV>::visit(luci::CircleTransposeConv *node)
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())
1633 void OpExporterLet<OE::STUV>::visit(luci::CircleUnidirectionalSequenceLSTM *node)
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())
1644 void OpExporterLet<OE::STUV>::visit(luci::CircleUnique *node) { export_node(_ctx, node); }
1646 void OpExporterLet<OE::STUV>::visit(luci::CircleUnpack *node) { export_node(_ctx, node); }
1648 void OpExporterLet<OE::WXYZ>::visit(luci::CircleWhere *node)
1650 export_simple(node, circle::BuiltinOperator_WHERE, circle::BuiltinOptions_WhereOptions,
1651 CreateWhereOptions(_ctx.builder).Union());
1654 void OpExporterLet<OE::WXYZ>::visit(luci::CircleWhile *node) { export_node(_ctx, node); }
1656 void OpExporterLet<OE::WXYZ>::visit(luci::CircleZerosLike *node)
1658 export_simple(node, circle::BuiltinOperator_ZEROS_LIKE, circle::BuiltinOptions_ZerosLikeOptions,
1659 CreateZerosLikeOptions(_ctx.builder).Union());
1662 void OpExporterLet<OE::CIRC>::visit(luci::CircleBCQFullyConnected *node)
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()))
1671 void OpExporterLet<OE::CIRC>::visit(luci::CircleBCQGather *node)
1674 node, circle::BuiltinOperator_BCQ_GATHER, circle::BuiltinOptions_BCQGatherOptions,
1675 CreateBCQGatherOptions(_ctx.builder, node->input_hidden_size(), node->axis()).Union());
1678 void OpExporterLet<OE::CIRC>::visit(luci::CircleInstanceNorm *node)
1680 export_simple(node, circle::BuiltinOperator_INSTANCE_NORM,
1681 circle::BuiltinOptions_InstanceNormOptions,
1682 CreateInstanceNormOptions(_ctx.builder, node->epsilon(),
1683 to_circle_actfunc(node->fusedActivationFunction()))
1687 void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md,
1688 SerializedGraphData &gd, uint32_t node_position)
1690 if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
1692 ExportContext ctx{builder, md, gd};
1693 OperationExporter exporter{ctx};
1695 const auto ops_size = gd._operators.size();
1697 exporter.export_node(circle_node);
1698 if (has_origin(circle_node) && ops_size != gd._operators.size())
1700 const auto node_id = gd._operators.size() - 1;
1701 for (auto source : get_origin(circle_node)->sources())
1703 md._metadata.add_op_table(node_id, source->id());
1706 if (has_execution_plan(circle_node))
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())
1715 execution_plan_vector.push_back(offset);
1717 md._metadata.add_execution_plan_table(node_position, execution_plan_vector);
1722 INTERNAL_EXN("Node with unsupported dialect found");
1731 void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md,
1732 SerializedGraphData &gd)
1734 uint32_t node_position = 0;
1735 for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
1737 exportNode(node, builder, md, gd, node_position);