Imported Upstream version 1.12.0
[platform/core/ml/nnfw.git] / runtime / onert / api / src / nnfw_api_internal.cc
1 /*
2  * Copyright (c) 2019 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 "nnfw_api_internal.h"
18 #include "CustomKernelRegistry.h"
19 #include "compiler/Compiler.h"
20 #include "util/ConfigSource.h"
21 #include "util/Exceptions.h"
22 #include "util/logging.h"
23 #include "exec/Execution.h"
24 #include "circle_loader.h"
25 #include "tflite_loader.h"
26 #include "json/json.h"
27 #include "ir/OpCode.h"
28 #include "util/TracingCtx.h"
29
30 #include <fstream>
31 #include <iostream>
32 #include <string>
33 #include <vector>
34 #include <dirent.h>
35 #include <misc/string_helpers.h>
36
37 /*
38  * API does not accept string argument longer than max length below
39  */
40 #define MAX_BACKEND_NAME_LENGTH 32
41 #define MAX_OP_NAME_LENGTH 64
42 #define MAX_PATH_LENGTH 1024
43 #define MAX_TENSOR_NAME_LENGTH 64
44
45 namespace
46 {
47
48 // Is null-terminating in length ?
49 bool null_terminating(const char *str, uint32_t length)
50 {
51   for (uint32_t i = 0; i < length; i++)
52   {
53     if (*(str + i) == '\0')
54     {
55       return true;
56     }
57   }
58   return false;
59 }
60
61 onert::ir::Layout convertLayout(NNFW_LAYOUT layout)
62 {
63   if (layout == NNFW_LAYOUT_CHANNELS_LAST)
64   {
65     return onert::ir::Layout::NHWC;
66   }
67   else if (layout == NNFW_LAYOUT_CHANNELS_FIRST)
68   {
69     return onert::ir::Layout::NCHW;
70   }
71   return onert::ir::Layout::UNKNOWN;
72 }
73
74 NNFW_STATUS getTensorIndexImpl(const onert::ir::Graph &graph, const char *tensorname,
75                                uint32_t *index, bool is_input)
76 {
77   if (!tensorname || !index)
78     return NNFW_STATUS_UNEXPECTED_NULL;
79
80   if (!null_terminating(tensorname, MAX_TENSOR_NAME_LENGTH))
81   {
82     std::cerr << "nnpackage path is too long" << std::endl;
83     return NNFW_STATUS_ERROR;
84   }
85
86   auto ind_found = is_input ? graph.getInputIndex(tensorname) : graph.getOutputIndex(tensorname);
87
88   if (ind_found.undefined())
89   {
90     // Not found
91     return NNFW_STATUS_ERROR;
92   }
93   else
94   {
95     *index = ind_found.value();
96     return NNFW_STATUS_NO_ERROR;
97   }
98 }
99
100 std::string trim(const std::string &value)
101 {
102   std::string whitespace = " \t";
103   auto begin = value.find_first_not_of(whitespace);
104   if (begin == std::string::npos)
105     return ""; // no content
106
107   auto end = value.find_last_not_of(whitespace);
108   auto range = end - begin + 1;
109   return value.substr(begin, range);
110 }
111
112 using CfgKeyValues = std::unordered_map<std::string, std::string>;
113
114 bool loadConfigure(const std::string cfgfile, CfgKeyValues &keyValues)
115 {
116   std::ifstream ifs(cfgfile);
117   if (ifs.is_open())
118   {
119     std::string line;
120     while (std::getline(ifs, line))
121     {
122       auto cmtpos = line.find('#');
123       if (cmtpos != std::string::npos)
124       {
125         line = line.substr(0, cmtpos);
126       }
127       std::istringstream isline(line);
128       std::string key;
129       if (std::getline(isline, key, '='))
130       {
131         std::string value;
132         if (std::getline(isline, value))
133         {
134           key = trim(key);
135           keyValues[key] = trim(value);
136         }
137       }
138     }
139     ifs.close();
140     return true;
141   }
142   return false;
143 }
144
145 void setConfigKeyValues(const CfgKeyValues &keyValues)
146 {
147   auto configsrc = std::make_unique<onert::util::GeneralConfigSource>();
148
149   for (auto it = keyValues.begin(); it != keyValues.end(); ++it)
150   {
151     VERBOSE(NNPKG_CONFIGS) << "(" << it->first << ") = (" << it->second << ")" << std::endl;
152     configsrc->set(it->first, it->second);
153   }
154
155   onert::util::config_source_ext(std::move(configsrc));
156 }
157
158 } // namespace
159
160 nnfw_session::nnfw_session()
161   : _subgraphs{nullptr}, _execution{nullptr},
162     _kernel_registry{std::make_shared<onert::frontend::custom::KernelRegistry>()}, _tracing_ctx{
163                                                                                      nullptr}
164 {
165   // DO NOTHING
166 }
167
168 nnfw_session::~nnfw_session() = default;
169
170 NNFW_STATUS nnfw_session::load_circle_from_buffer(uint8_t *buffer, size_t size)
171 {
172   if (!isStateInitialized())
173     return NNFW_STATUS_INVALID_STATE;
174
175   if (!buffer)
176     return NNFW_STATUS_UNEXPECTED_NULL;
177
178   if (size == 0)
179     return NNFW_STATUS_ERROR;
180
181   try
182   {
183     _subgraphs = onert::circle_loader::loadModel(buffer, size);
184   }
185   catch (const std::exception &e)
186   {
187     std::cerr << "Error during model loading : " << e.what() << std::endl;
188     return NNFW_STATUS_ERROR;
189   }
190
191   _tracing_ctx = std::make_unique<onert::util::TracingCtx>(_subgraphs.get());
192
193   _compiler = std::make_unique<onert::compiler::Compiler>(_subgraphs, _tracing_ctx.get());
194
195   _state = State::MODEL_LOADED;
196   return NNFW_STATUS_NO_ERROR;
197 }
198
199 NNFW_STATUS nnfw_session::load_model_from_modelfile(const char *model_file_path)
200 {
201   if (!isStateInitialized())
202     return NNFW_STATUS_INVALID_STATE;
203
204   if (!model_file_path)
205   {
206     std::cerr << "Model file path is null." << std::endl;
207     return NNFW_STATUS_UNEXPECTED_NULL;
208   }
209
210   std::string filename{model_file_path};
211   if (filename.size() < 8) // .tflite or .circle
212   {
213     std::cerr << "Invalid model file path." << std::endl;
214     return NNFW_STATUS_ERROR;
215   }
216
217   std::string model_type = filename.substr(filename.size() - 7, 7);
218
219   try
220   {
221     if (model_type == ".tflite")
222     {
223       _subgraphs = onert::tflite_loader::loadModel(filename.c_str());
224     }
225     else if (model_type == ".circle")
226     {
227       _subgraphs = onert::circle_loader::loadModel(filename.c_str());
228     }
229     else
230     {
231       std::cerr << "Unsupported model type" << std::endl;
232       return NNFW_STATUS_ERROR;
233     }
234   }
235   catch (const std::exception &e)
236   {
237     std::cerr << "Error during model loading : " << e.what() << std::endl;
238     return NNFW_STATUS_ERROR;
239   }
240
241   _tracing_ctx = std::make_unique<onert::util::TracingCtx>(_subgraphs.get());
242
243   _compiler = std::make_unique<onert::compiler::Compiler>(_subgraphs, _tracing_ctx.get());
244
245   _state = State::MODEL_LOADED;
246   return NNFW_STATUS_NO_ERROR;
247 }
248
249 NNFW_STATUS nnfw_session::load_model_from_nnpackage(const char *package_dir)
250 {
251   if (!isStateInitialized())
252     return NNFW_STATUS_INVALID_STATE;
253
254   if (!package_dir)
255   {
256     std::cerr << "package_dir is null." << std::endl;
257     return NNFW_STATUS_UNEXPECTED_NULL;
258   }
259
260   if (!null_terminating(package_dir, MAX_PATH_LENGTH))
261   {
262     std::cerr << "nnpackage path is too long" << std::endl;
263     return NNFW_STATUS_ERROR;
264   }
265
266   // TODO : add support for zipped package file load
267   DIR *dir;
268   if (!(dir = opendir(package_dir)))
269   {
270     std::cerr << "invalid nnpackge directory: " << package_dir << std::endl;
271     return NNFW_STATUS_ERROR;
272   }
273   closedir(dir);
274
275   try
276   {
277     std::string manifest_file_name(package_dir);
278     manifest_file_name += "/metadata/MANIFEST";
279     std::ifstream mfs(manifest_file_name);
280
281     // extract the filename of the first(index 0) model
282     // e.g. In MANIFEST file, { "models" : [ "firstmodel.tflite", "2nd.tflite" ] }
283     Json::Value root;
284     mfs >> root;
285     const Json::Value &models = root["models"];
286     const Json::Value &model_types = root["model-types"];
287     const Json::Value &configs = root["configs"];
288
289     if (!configs.empty() && !configs[0].empty())
290     {
291       auto filepath = package_dir + std::string("/metadata/") + configs[0].asCString();
292
293       CfgKeyValues keyValues;
294       if (loadConfigure(filepath, keyValues))
295       {
296         setConfigKeyValues(keyValues);
297       }
298     }
299
300     auto model_file_path = package_dir + std::string("/") + models[0].asString(); // first model
301     auto model_type = model_types[0].asString(); // first model's type
302     if (model_type == "tflite")
303     {
304       _subgraphs = onert::tflite_loader::loadModel(model_file_path.c_str());
305     }
306     else if (model_type == "circle")
307     {
308       _subgraphs = onert::circle_loader::loadModel(model_file_path.c_str());
309     }
310     else
311     {
312       std::cerr << "Unsupported model type in MANIFEST" << std::endl;
313       return NNFW_STATUS_ERROR;
314     }
315     _subgraphs->primary()->bindKernelBuilder(_kernel_registry->getBuilder());
316   }
317   catch (const std::exception &e)
318   {
319     std::cerr << "Error during model loading : " << e.what() << std::endl;
320     return NNFW_STATUS_ERROR;
321   }
322
323   _tracing_ctx = std::make_unique<onert::util::TracingCtx>(_subgraphs.get());
324
325   _compiler = std::make_unique<onert::compiler::Compiler>(_subgraphs, _tracing_ctx.get());
326
327   _state = State::MODEL_LOADED;
328   return NNFW_STATUS_NO_ERROR;
329 }
330
331 NNFW_STATUS nnfw_session::prepare()
332 {
333   // NOTE. If users want to run prepare() more than one time, this could be removed.
334   if (!isStateModelLoaded())
335   {
336     std::cerr << "Error during model prepare : ";
337     if (isStateInitialized())
338     {
339       std::cerr << "prepare should be run once";
340     }
341     else
342     {
343       std::cerr << "invalid state";
344     }
345     std::cerr << std::endl;
346     return NNFW_STATUS_INVALID_STATE;
347   }
348
349   if (!_subgraphs || !primary_subgraph() || primary_subgraph()->isBuildingPhase())
350   {
351     std::cerr << "Error during model prepare : "
352               << "prepare should be run after load_model" << std::endl;
353     return NNFW_STATUS_ERROR;
354   }
355
356   try
357   {
358     _subgraphs.reset();
359     std::shared_ptr<onert::exec::ExecutorMap> executors = _compiler->compile();
360     _execution = std::make_unique<onert::exec::Execution>(executors);
361   }
362   catch (const std::exception &e)
363   {
364     std::cerr << "Error during model prepare : " << e.what() << std::endl;
365     return NNFW_STATUS_ERROR;
366   }
367
368   _state = State::PREPARED;
369   return NNFW_STATUS_NO_ERROR;
370 }
371
372 NNFW_STATUS nnfw_session::run()
373 {
374   if (!isStatePreparedOrFinishedRun())
375   {
376     std::cerr << "Error during nnfw_session::run : "
377               << "run should be run after prepare" << std::endl;
378     return NNFW_STATUS_INVALID_STATE;
379   }
380
381   try
382   {
383     _execution->execute();
384   }
385   catch (const onert::InsufficientBufferSizeException &e)
386   {
387     // Currently insufficient buffer always means output buffer.
388     std::cerr << "Error during nnfw_session::run : " << e.what() << std::endl;
389     return NNFW_STATUS_INSUFFICIENT_OUTPUT_SIZE;
390   }
391   catch (const std::exception &e)
392   {
393     std::cerr << "Error during nnfw_session::run : " << e.what() << std::endl;
394     return NNFW_STATUS_ERROR;
395   }
396
397   _state = State::FINISHED_RUN;
398   return NNFW_STATUS_NO_ERROR;
399 }
400
401 NNFW_STATUS nnfw_session::run_async()
402 {
403   if (!isStatePreparedOrFinishedRun())
404   {
405     std::cerr << "Error during nnfw_session::run_async : "
406               << "run_async should be run after prepare" << std::endl;
407     return NNFW_STATUS_INVALID_STATE;
408   }
409
410   _execution->startExecute();
411
412   _state = State::RUNNING;
413   return NNFW_STATUS_NO_ERROR;
414 }
415
416 NNFW_STATUS nnfw_session::await()
417 {
418   if (!isStateRunning())
419   {
420     std::cerr << "Error during nnfw_session::run_await : "
421               << "run_await should be run after run_async" << std::endl;
422     return NNFW_STATUS_ERROR;
423   }
424
425   _execution->waitFinish();
426
427   _state = State::FINISHED_RUN;
428   return NNFW_STATUS_NO_ERROR;
429 }
430
431 NNFW_STATUS nnfw_session::set_input(uint32_t index, NNFW_TYPE /*type*/, const void *buffer,
432                                     size_t length)
433 {
434   if (!isStatePreparedOrFinishedRun())
435   {
436     std::cerr << "Error during nnfw_session::set_input : invalid state" << std::endl;
437     return NNFW_STATUS_INVALID_STATE;
438   }
439
440   if (!buffer && length != 0)
441   {
442     std::cerr
443       << "Error during nnfw_session::set_input : given buffer is NULL but the length is not 0"
444       << std::endl;
445     return NNFW_STATUS_ERROR;
446   }
447
448   try
449   {
450     _execution->setInput(onert::ir::IOIndex(index), buffer, length);
451   }
452   catch (const std::exception &e)
453   {
454     std::cerr << "Error during nnfw_session::set_input : " << e.what() << std::endl;
455     return NNFW_STATUS_ERROR;
456   }
457   return NNFW_STATUS_NO_ERROR;
458 }
459
460 NNFW_STATUS nnfw_session::set_output(uint32_t index, NNFW_TYPE /*type*/, void *buffer,
461                                      size_t length)
462 {
463   if (!isStatePreparedOrFinishedRun())
464   {
465     std::cerr << "Error during nnfw_session::set_output : invalid state" << std::endl;
466     return NNFW_STATUS_INVALID_STATE;
467   }
468
469   if (!buffer && length != 0)
470   {
471     std::cerr
472       << "Error during nnfw_session::set_output : given buffer is NULL but the length is not 0"
473       << std::endl;
474     return NNFW_STATUS_ERROR;
475   }
476
477   try
478   {
479     _execution->setOutput(onert::ir::IOIndex(index), buffer, length);
480   }
481   catch (const std::exception &e)
482   {
483     std::cerr << "Error during nnfw_session::set_output : " << e.what() << std::endl;
484     return NNFW_STATUS_ERROR;
485   }
486   return NNFW_STATUS_NO_ERROR;
487 }
488
489 NNFW_STATUS nnfw_session::input_size(uint32_t *number)
490 {
491   if (isStateInitialized()) // Model is not loaded
492     return NNFW_STATUS_INVALID_STATE;
493
494   try
495   {
496     if (number == nullptr)
497     {
498       std::cerr << "Error during nnfw_session::input_size, number is null pointer." << std::endl;
499       return NNFW_STATUS_UNEXPECTED_NULL;
500     }
501     *number = primary_subgraph()->getInputs().size();
502   }
503   catch (const std::exception &e)
504   {
505     std::cerr << "Error during nnfw_session::input_size : " << e.what() << std::endl;
506     return NNFW_STATUS_ERROR;
507   }
508   return NNFW_STATUS_NO_ERROR;
509 }
510
511 NNFW_STATUS nnfw_session::output_size(uint32_t *number)
512 {
513   if (isStateInitialized()) // Model is not loaded
514     return NNFW_STATUS_INVALID_STATE;
515
516   try
517   {
518     if (number == nullptr)
519     {
520       std::cerr << "Error during nnfw_session::output_size, number is null pointer." << std::endl;
521       return NNFW_STATUS_UNEXPECTED_NULL;
522     }
523     *number = primary_subgraph()->getOutputs().size();
524   }
525   catch (const std::exception &e)
526   {
527     std::cerr << "Error during nnfw_session::output_size" << e.what() << std::endl;
528     return NNFW_STATUS_ERROR;
529   }
530   return NNFW_STATUS_NO_ERROR;
531 }
532
533 NNFW_STATUS nnfw_session::set_input_layout(uint32_t index, NNFW_LAYOUT layout)
534 {
535   try
536   {
537     if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
538         layout != NNFW_LAYOUT_CHANNELS_LAST)
539     {
540       std::cerr << "Error during nnfw_session::set_input_layout, not supported layout" << std::endl;
541       return NNFW_STATUS_ERROR;
542     }
543     _execution->setInputLayout(onert::ir::IOIndex(index), convertLayout(layout));
544   }
545   catch (const std::exception &e)
546   {
547     std::cerr << "Error during nnfw_session::set_input_layout : " << e.what() << std::endl;
548     return NNFW_STATUS_ERROR;
549   }
550   return NNFW_STATUS_NO_ERROR;
551 }
552
553 NNFW_STATUS nnfw_session::set_output_layout(uint32_t index, NNFW_LAYOUT layout)
554 {
555   try
556   {
557     if (layout != NNFW_LAYOUT_NONE && layout != NNFW_LAYOUT_CHANNELS_FIRST &&
558         layout != NNFW_LAYOUT_CHANNELS_LAST)
559     {
560       std::cerr << "Error during nnfw_session::set_output_layout, not supported layout"
561                 << std::endl;
562       return NNFW_STATUS_ERROR;
563     }
564     _execution->setOutputLayout(onert::ir::IOIndex(index), convertLayout(layout));
565   }
566   catch (const std::exception &e)
567   {
568     std::cerr << "Error during nnfw_session::set_output_layout : " << e.what() << std::endl;
569     return NNFW_STATUS_ERROR;
570   }
571   return NNFW_STATUS_NO_ERROR;
572 }
573
574 static NNFW_TYPE datatype_to_nnfw_dtype(onert::ir::DataType dt)
575 {
576   using onert::ir::DataType;
577   switch (dt)
578   {
579     case DataType::FLOAT32:
580       return NNFW_TYPE_TENSOR_FLOAT32;
581     case DataType::INT32:
582       return NNFW_TYPE_TENSOR_INT32;
583     case DataType::QUANT_UINT8_ASYMM:
584       return NNFW_TYPE_TENSOR_QUANT8_ASYMM;
585     case DataType::BOOL8:
586       return NNFW_TYPE_TENSOR_BOOL;
587     case DataType::UINT8:
588       return NNFW_TYPE_TENSOR_UINT8;
589     case DataType::INT64:
590       return NNFW_TYPE_TENSOR_INT64;
591     case DataType::QUANT_INT8_ASYMM:
592       return NNFW_TYPE_TENSOR_QUANT8_ASYMM_SIGNED;
593     case DataType::UINT32:
594     case DataType::QUANT_INT8_SYMM:
595     default:
596       throw std::runtime_error("Error: Model has type that runtime API does not support.");
597   }
598 }
599
600 NNFW_STATUS nnfw_session::apply_tensorinfo(uint32_t index, nnfw_tensorinfo ti)
601 {
602   // sanity check
603   {
604     if (isStateInitialized())
605     {
606       std::cerr << "Error during set_input_tensorinfo : should be run after load_model"
607                 << std::endl;
608       return NNFW_STATUS_INVALID_STATE;
609     }
610
611     if (ti.rank <= 0 || ti.rank > NNFW_MAX_RANK)
612     {
613       std::cerr << "unsupported rank: " << ti.rank << std::endl;
614       return NNFW_STATUS_ERROR;
615     }
616
617     for (int32_t i = 0; i < ti.rank; ++i)
618     {
619       if (ti.dims[i] <= 0)
620       {
621         std::cerr << "dim must be positive integer but was " << ti.dims[i] << std::endl;
622         return NNFW_STATUS_ERROR;
623       }
624     }
625   }
626
627   onert::ir::Shape new_shape(ti.rank);
628   for (int32_t i = 0; i < ti.rank; i++)
629     new_shape.dim(i) = ti.dims[i];
630
631   if (!isStatePreparedOrFinishedRun())
632   {
633     // In this case, if we apply input shape in primary_subgraph, it will propagate after
634     // compilation and excution
635     auto ind = primary_subgraph()->getInputs().at(index);
636     auto &input = primary_subgraph()->operands().at(ind);
637
638     // overwrite input shape with the shape from ti
639     input.info().shape(new_shape);
640   }
641   else // when called after nnfw_session::prepare()
642   {
643     _execution->changeInputShape(onert::ir::IOIndex(index), new_shape);
644   }
645
646   return NNFW_STATUS_NO_ERROR;
647 }
648
649 NNFW_STATUS nnfw_session::set_input_tensorinfo(uint32_t index, const nnfw_tensorinfo *ti)
650 {
651   nnfw_tensorinfo ti_copy = *ti;
652   return apply_tensorinfo(index, ti_copy);
653 }
654
655 NNFW_STATUS nnfw_session::input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
656 {
657   if (isStateInitialized())
658     return NNFW_STATUS_INVALID_STATE;
659
660   try
661   {
662     if (ti == nullptr)
663     {
664       std::cerr << "Error during nnfw_session::input_tensorinfo, tensorinfo is null pointer."
665                 << std::endl;
666       return NNFW_STATUS_UNEXPECTED_NULL;
667     }
668     if (index >= primary_subgraph()->getInputs().size())
669     {
670       std::cerr << "Error during nnfw_session::input_tensorinfo, index is out of range."
671                 << std::endl;
672       return NNFW_STATUS_ERROR;
673     }
674     auto opidx = primary_subgraph()->getInputs().at(index);
675     auto shape = primary_subgraph()->operands().at(opidx).shape();
676     if (isStatePreparedOrFinishedRun())
677       shape = _execution->getInputShape(onert::ir::IOIndex{index});
678     ti->rank = shape.rank();
679     for (int j = 0; j < ti->rank; ++j)
680     {
681       ti->dims[j] = shape.dim(j);
682     }
683     ti->dtype = datatype_to_nnfw_dtype(primary_subgraph()->operands().at(opidx).typeInfo().type());
684   }
685   catch (const std::exception &e)
686   {
687     std::cerr << "Error during nnfw_session::input_tensorinfo : " << e.what() << std::endl;
688     return NNFW_STATUS_ERROR;
689   }
690   return NNFW_STATUS_NO_ERROR;
691 }
692
693 NNFW_STATUS nnfw_session::output_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
694 {
695   if (isStateInitialized())
696     return NNFW_STATUS_INVALID_STATE;
697
698   if (ti == nullptr)
699   {
700     std::cerr << "Error during nnfw_session::output_tensorinfo, tensorinfo is null pointer."
701               << std::endl;
702     return NNFW_STATUS_UNEXPECTED_NULL;
703   }
704
705   if (index >= primary_subgraph()->getOutputs().size())
706   {
707     std::cerr << "Error during nnfw_session::output_tensorinfo, index is out of range."
708               << std::endl;
709     return NNFW_STATUS_ERROR;
710   }
711
712   try
713   {
714     auto opidx = primary_subgraph()->getOutputs().at(index);
715     auto shape = primary_subgraph()->operands().at(opidx).shape();
716     // If it is called after `nnfw_run` then get the shape from Execution, not from the graph
717     if (isStateFinishedRun())
718       shape = _execution->getOutputShape(onert::ir::IOIndex{index});
719     ti->rank = shape.rank();
720     for (int j = 0; j < ti->rank; ++j)
721     {
722       ti->dims[j] = shape.dim(j);
723     }
724     ti->dtype = datatype_to_nnfw_dtype(primary_subgraph()->operands().at(opidx).typeInfo().type());
725   }
726   catch (const std::exception &e)
727   {
728     std::cerr << "Error during nnfw_session::output_tensorinfo : " << e.what() << std::endl;
729     return NNFW_STATUS_ERROR;
730   }
731
732   return NNFW_STATUS_NO_ERROR;
733 }
734 NNFW_STATUS nnfw_session::register_custom_operation(const std::string &id,
735                                                     nnfw_custom_eval eval_func)
736 {
737   _kernel_registry->registerKernel(id, eval_func);
738   return NNFW_STATUS_NO_ERROR;
739 }
740
741 static std::string get_op_backend_string(std::string op)
742 {
743 #define MAP_MACRO(CircleName, OneRTName) {#CircleName, #OneRTName},
744
745   static std::unordered_map<std::string, std::string> operation_map = {
746 #include "OpMap.lst"
747   };
748
749 #undef MAP_MACRO
750
751   auto n = operation_map.find(op);
752
753   if (n == operation_map.end())
754   {
755     // this return value is handled by a caller to return error code
756     return std::string("");
757   }
758   else
759   {
760     return n->second;
761   }
762 }
763
764 NNFW_STATUS nnfw_session::set_available_backends(const char *backends)
765 {
766   if (!isStateModelLoaded())
767     return NNFW_STATUS_INVALID_STATE;
768
769   try
770   {
771     if (!backends)
772       return NNFW_STATUS_UNEXPECTED_NULL;
773     if (null_terminating(backends, MAX_BACKEND_NAME_LENGTH) == false)
774       return NNFW_STATUS_ERROR;
775
776     auto &options = _compiler->options();
777
778     using namespace onert::util;
779
780     options.backend_list = nnfw::misc::split(std::string{backends}, ';');
781   }
782   catch (const std::exception &e)
783   {
784     std::cerr << "Error during nnfw_session::set_available_backends : " << e.what() << std::endl;
785     return NNFW_STATUS_ERROR;
786   }
787   return NNFW_STATUS_NO_ERROR;
788 }
789
790 NNFW_STATUS nnfw_session::set_op_backend(const char *op, const char *backend)
791 {
792   if (!isStateModelLoaded())
793     return NNFW_STATUS_INVALID_STATE;
794
795   try
796   {
797     if (!op || !backend)
798       return NNFW_STATUS_UNEXPECTED_NULL;
799     if (!null_terminating(op, MAX_OP_NAME_LENGTH) ||
800         !null_terminating(backend, MAX_BACKEND_NAME_LENGTH))
801       return NNFW_STATUS_ERROR;
802
803     auto key = get_op_backend_string(op);
804
805     if (key.empty())
806     {
807       return NNFW_STATUS_ERROR;
808     }
809
810     auto &opcode_to_backend = _compiler->options().manual_scheduler_options.opcode_to_backend;
811     opcode_to_backend.emplace(onert::ir::toOpCode(key), backend);
812   }
813   catch (const std::exception &e)
814   {
815     std::cerr << "Error during nnfw_session::set_op_backend : " << e.what() << std::endl;
816     return NNFW_STATUS_ERROR;
817   }
818   return NNFW_STATUS_NO_ERROR;
819 }
820
821 NNFW_STATUS nnfw_session::set_config(const char *key, const char *value)
822 {
823   if (!isStateModelLoaded())
824     return NNFW_STATUS_INVALID_STATE;
825
826   if (!key || !value)
827     return NNFW_STATUS_UNEXPECTED_NULL;
828
829   auto &options = _compiler->options();
830
831   using namespace onert::util;
832
833   const std::string skey = key;
834
835   if (skey == config::TRACE_FILEPATH)
836   {
837     options.trace_filepath = value;
838   }
839   else if (skey == config::GRAPH_DOT_DUMP)
840   {
841     options.graph_dump_level = toInt(value);
842   }
843   else if (skey == config::OP_SEQ_MAX_NODE)
844   {
845     options.op_seq_max_node = toInt(value);
846   }
847   else if (skey == config::EXECUTOR)
848   {
849     options.executor = value;
850   }
851   else if (skey == config::OP_BACKEND_ALLOPS)
852   {
853     options.manual_scheduler_options.backend_for_all = value;
854   }
855   else if (skey == config::USE_SCHEDULER)
856   {
857     options.he_scheduler = toBool(value);
858   }
859   else if (skey == config::PROFILING_MODE)
860   {
861     options.he_profiling_mode = toBool(value);
862   }
863   else if (skey == config::DISABLE_COMPILE)
864   {
865     options.disable_compile = toBool(value);
866   }
867   else
868   {
869     return NNFW_STATUS_ERROR;
870   }
871   return NNFW_STATUS_NO_ERROR;
872 }
873
874 onert::ir::Graph *nnfw_session::primary_subgraph()
875 {
876   if (_subgraphs)
877   {
878     assert(!_execution);
879     return _subgraphs->primary().get();
880   }
881   else
882   {
883     assert(_execution);
884     // TODO Remove const_cast
885     // We assumed the graph will not change after compilation, but shape could change
886     return const_cast<onert::ir::Graph *>(&_execution->primary_subgraph());
887   }
888 }
889
890 NNFW_STATUS nnfw_session::get_config(const char *key, char *value, size_t value_size)
891 {
892   if (!isStateModelLoaded())
893     return NNFW_STATUS_INVALID_STATE;
894
895   if (!key || !value)
896     return NNFW_STATUS_UNEXPECTED_NULL;
897
898   auto &options = _compiler->options();
899
900   auto check_boundary = [](size_t dest_size, std::string &src) {
901     if (dest_size < src.length() + 1 /* for '\0' */)
902     {
903       std::cerr << "buffer is small to copy config value." << std::endl;
904       return false;
905     }
906     return true;
907   };
908
909   const std::string skey = key;
910
911   if (skey == onert::util::config::BACKENDS)
912   {
913     if (options.backend_list.size() == 0)
914       return NNFW_STATUS_NO_ERROR; // no setting backend is not an error of get_config_str()
915
916     auto str = nnfw::misc::join(options.backend_list.begin(), options.backend_list.end(), ";");
917
918     if (!check_boundary(value_size, str))
919       return NNFW_STATUS_ERROR;
920
921     strncpy(value, str.c_str(), value_size);
922   }
923   else if (skey == onert::util::config::EXECUTOR)
924   {
925     if (!check_boundary(value_size, options.executor))
926       return NNFW_STATUS_ERROR;
927
928     strncpy(value, options.executor.c_str(), options.executor.length());
929   }
930   else
931   {
932     return NNFW_STATUS_ERROR;
933   }
934
935   return NNFW_STATUS_NO_ERROR;
936 }
937
938 bool nnfw_session::isStateInitialized()
939 {
940   if (_state == State::INITIALIZED)
941   {
942     assert(!_subgraphs);
943     assert(!_compiler);
944     assert(!_execution);
945     return true;
946   }
947   else
948   {
949     return false;
950   }
951 }
952
953 bool nnfw_session::isStateModelLoaded()
954 {
955   if (_state == State::MODEL_LOADED)
956   {
957     assert(_subgraphs);
958     assert(_compiler);
959     assert(!_execution);
960     assert(!primary_subgraph()->isBuildingPhase());
961     return true;
962   }
963   else
964   {
965     return false;
966   }
967 }
968
969 bool nnfw_session::isStatePrepared()
970 {
971   if (_state == State::PREPARED)
972   {
973     assert(!_subgraphs);
974     assert(_compiler);
975     assert(_execution);
976     assert(!primary_subgraph()->isBuildingPhase());
977     return true;
978   }
979   else
980   {
981     return false;
982   }
983 }
984
985 bool nnfw_session::isStateRunning()
986 {
987   if (_state == State::RUNNING)
988   {
989     assert(!_subgraphs);
990     assert(_compiler);
991     assert(_execution);
992     assert(!primary_subgraph()->isBuildingPhase());
993     return true;
994   }
995   return false;
996 }
997
998 bool nnfw_session::isStateFinishedRun()
999 {
1000   if (_state == State::FINISHED_RUN)
1001   {
1002     assert(!_subgraphs);
1003     assert(_compiler);
1004     assert(_execution);
1005     assert(!primary_subgraph()->isBuildingPhase());
1006     return true;
1007   }
1008   else
1009   {
1010     return false;
1011   }
1012 }
1013
1014 bool nnfw_session::isStatePreparedOrFinishedRun()
1015 {
1016   return isStatePrepared() || isStateFinishedRun();
1017 }
1018
1019 NNFW_STATUS nnfw_session::input_tensorindex(const char *tensorname, uint32_t *index)
1020 {
1021   return getTensorIndexImpl(*primary_subgraph(), tensorname, index, true);
1022 }
1023
1024 NNFW_STATUS nnfw_session::output_tensorindex(const char *tensorname, uint32_t *index)
1025 {
1026   return getTensorIndexImpl(*primary_subgraph(), tensorname, index, false);
1027 }