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