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