Imported Upstream version 1.9.0
[platform/core/ml/nnfw.git] / runtime / onert / core / src / exec / DynamicShapeInference.cc
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "exec/DynamicShapeInference.h"
18 #include "util/ShapeInference.h"
19 #include <assert.h>
20
21 namespace onert
22 {
23 namespace exec
24 {
25
26 inline backend::IDynamicTensorManager *
27 dynamicTensorManagerOf(const std::shared_ptr<backend::ITensor> &tensor)
28 {
29   if (!tensor->dynamic_tensor_manager())
30     throw std::runtime_error{"Dynamic Tensor Manager is not available for this tensor."};
31   return tensor->dynamic_tensor_manager();
32 }
33
34 void DynamicShapeInferer::handleBinaryArithmeticOp(const ir::Operation &op,
35                                                    const ir::OperandIndex lhs_idx,
36                                                    const ir::OperandIndex rhs_idx)
37 {
38   auto lhs = _tensor_registry->getITensor(lhs_idx);
39   auto lhs_shape = lhs->getShape();
40
41   auto rhs = _tensor_registry->getITensor(rhs_idx);
42   auto rhs_shape = rhs->getShape();
43
44   /*
45     Here, the state after compilation (satic shape inference) could be one of the following:
46
47               lhs       rhs              output     execution-time shape inf required
48       ------------------------------------------    ---------------------------------
49       case 1) static    static           static      X
50       case 2) one or both are dynamic    dynamic     O
51
52     Then nnfw_apply_tensorinf() could change one or both inputs dynamic.
53     So, in this method, we have one more state and we have to re-calculate shape for this shape.
54
55       case 3) one or both are dynamic    static      O
56
57     So, only when all inputs are static, we can skip dynamic shape inference.
58   */
59   if ((!lhs->is_dynamic()) && (!rhs->is_dynamic()))
60     return;
61
62   auto output_idx = op.getOutputs().at(0);
63   auto output = _tensor_registry->getITensor(output_idx);
64
65   ir::Shape new_shape = shape_inference::inferEltwiseShape(lhs_shape, rhs_shape);
66
67   dynamicTensorManagerOf(output)->applyShape(output_idx, new_shape);
68   assert(output->buffer() != nullptr);
69 }
70
71 void DynamicShapeInferer::handleSimpleUnaryOp(const ir::Operation &op,
72                                               const ir::OperandIndex input_ind)
73 {
74   // check if input is not dynamic
75   auto input = _tensor_registry->getITensor(input_ind);
76   auto output_shape = input->getShape();
77
78   /*
79     Here, the state after compilation (satic shape inference) could be one of the following:
80
81               input      output    execution-time shape inf required
82       -------------------------    ---------------------------------
83       case 1) static     static      X
84       case 2) dynamic    dynamic     O
85
86     Then nnfw_apply_tensorinf() could change input dynamic.
87     So, in this method, we have one more state and we have to re-calculate shape for this shape.
88
89       case 3) dynamic    static      O
90
91     So, only when input is static, we can skip dynamic shape inference.
92   */
93   if (!input->is_dynamic())
94     return;
95
96   auto output_ind = op.getOutputs().at(0);
97   auto output = _tensor_registry->getITensor(output_ind);
98
99   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
100   assert(output->buffer() != nullptr);
101 }
102
103 void DynamicShapeInferer::visit(const ir::operation::ArgMax &op)
104 {
105   const auto input_idx{op.getInputs().at(ir::operation::ArgMax::Input::INPUT)};
106   const auto &input = _tensor_registry->getITensor(input_idx);
107   auto input_shape = input->getShape();
108
109   if (!input->is_dynamic())
110     return;
111
112   const auto rank = input_shape.rank();
113   const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
114
115   assert(0 <= axis && axis < rank);
116
117   auto output_ind = op.getOutputs().at(0);
118   auto output = _tensor_registry->getITensor(output_ind);
119
120   ir::Shape new_shape = shape_inference::inferArgMaxShape(input_shape, axis, rank);
121
122   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
123   assert(output->buffer() != nullptr);
124 }
125
126 void DynamicShapeInferer::visit(const ir::operation::BatchMatMul &op)
127 {
128   const auto lhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::LHS);
129   const auto rhs_index = op.getInputs().at(ir::operation::BatchMatMul::Input::RHS);
130   auto lhs = _tensor_registry->getITensor(lhs_index);
131   auto rhs = _tensor_registry->getITensor(rhs_index);
132
133   if (!lhs->is_dynamic() && !rhs->is_dynamic())
134     return;
135
136   const auto output_index = op.getOutputs().at(0);
137   auto output = _tensor_registry->getITensor(output_index);
138
139   auto lhs_shape = lhs->getShape();
140   auto rhs_shape = rhs->getShape();
141   // TODO
142
143   auto new_shape = shape_inference::inferBatchMatMulShape(lhs_shape, rhs_shape, op.param());
144   dynamicTensorManagerOf(output)->applyShape(output_index, new_shape);
145 }
146
147 void DynamicShapeInferer::visit(const ir::operation::BinaryArithmetic &op)
148 {
149   handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::BinaryArithmetic::Input::LHS),
150                            op.getInputs().at(ir::operation::BinaryArithmetic::Input::RHS));
151 }
152
153 void DynamicShapeInferer::visit(const ir::operation::BroadcastTo &op)
154 {
155   auto output_ind = op.getOutputs().at(0);
156   auto output = _tensor_registry->getITensor(output_ind);
157
158   auto input_idx = op.getInputs().at(ir::operation::BroadcastTo::INPUT);
159   auto input = _tensor_registry->getITensor(input_idx);
160
161   if ((!input->is_dynamic()) && (!output->is_dynamic()))
162     return;
163
164   auto shape_idx = op.getInputs().at(ir::operation::Tile::Input::MULTIPLES);
165   const auto &shape = _tensor_registry->getITensor(shape_idx);
166
167   assert(shape); // It shouldn't be 0.
168
169   auto output_shape = shape_inference::inferBroadcastToShape(
170       shape->getShape(), reinterpret_cast<const int32_t *>(shape->buffer()));
171
172   // set output shape and output buffer
173   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
174   assert(output->buffer() != nullptr);
175 }
176
177 void DynamicShapeInferer::visit(const ir::operation::Comparison &op)
178 {
179   handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Comparison::Input::INPUT0),
180                            op.getInputs().at(ir::operation::Comparison::Input::INPUT1));
181 }
182
183 void DynamicShapeInferer::visit(const ir::operation::Concat &op)
184 {
185   /*
186     The state after compilation (satic shape inference) could be one of the following:
187
188               inputs                  output        execution-time shape inf required
189       ------------------------------------------    ---------------------------------
190       case 1) all static              static         X
191       case 2) at least on is dynamic  dynamic        O
192
193     Then nnfw_apply_tensorinf() could change one or both inputs dynamic.
194     So, in this method, we have one more state and we have to re-calculate shape for this shape.
195
196       case 3) at least on is dynamic  static         O
197
198     So, only when all inputs are static, we can skip dynamic shape inference.
199   */
200   bool all_static = true;
201   for (auto input_ind : op.getInputs())
202   {
203     auto input = _tensor_registry->getITensor(input_ind);
204     if (input->is_dynamic())
205     {
206       all_static = false;
207       break;
208     }
209   }
210
211   if (all_static)
212     return;
213
214   // sanity check
215   {
216     auto isConcatible = [](const backend::ITensor *input1, const backend::ITensor *input2,
217                            int32_t axis) {
218       if (input1->num_dimensions() != input2->num_dimensions())
219         return false;
220
221       for (size_t i = 0; i < input1->num_dimensions(); i++)
222       {
223         auto positive_axis = (axis >= 0) ? axis : axis + input1->num_dimensions();
224
225         if (i != positive_axis)
226           if (input1->dimension(i) != input2->dimension(i))
227             return false;
228       }
229
230       return true;
231     };
232
233     auto first_input_ind = op.getInputs().at(0);
234     auto first_input = _tensor_registry->getITensor(first_input_ind);
235
236     for (auto input_ind : op.getInputs())
237     {
238       auto input = _tensor_registry->getITensor(input_ind);
239       if (input != first_input && !isConcatible(first_input.get(), input.get(), op.param().axis))
240         throw std::runtime_error("input shapes does not matched for concat");
241     }
242   }
243
244   // getting output shape
245   onert::shape_inference::Shapes in_shapes;
246   for (auto input_ind : op.getInputs())
247   {
248     auto input = _tensor_registry->getITensor(input_ind);
249     ir::Shape shape = input->getShape();
250
251     in_shapes.emplace_back(shape);
252   }
253
254   auto output_ind = op.getOutputs().at(0);
255   auto output = _tensor_registry->getITensor(output_ind);
256   auto output_shape = shape_inference::inferConcatShape(in_shapes, op.param());
257
258   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
259 }
260
261 void DynamicShapeInferer::visit(const ir::operation::Conv2D &op)
262 {
263   // check if input is not dynamic
264   auto input_ind = op.getInputs().at(ir::operation::Conv2D::INPUT);
265   auto input = _tensor_registry->getITensor(input_ind);
266
267   auto ker_ind = op.getInputs().at(ir::operation::Conv2D::KERNEL);
268   auto ker = _tensor_registry->getITensor(ker_ind);
269
270   if ((!input->is_dynamic()) && (!ker->is_dynamic()))
271     return;
272
273   ir::Shape input_shape = input->getShape();
274   ir::Shape ker_shape = ker->getShape();
275
276   auto output_ind = op.getOutputs().at(0);
277   auto output = _tensor_registry->getITensor(output_ind);
278
279   ir::Shape output_shape = shape_inference::inferConv2DShape(input_shape, ker_shape, op.param());
280
281   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
282   assert(output->buffer() != nullptr);
283 }
284
285 void DynamicShapeInferer::visit(const ir::operation::ElementwiseActivation &op)
286 {
287   handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseActivation::INPUT));
288 }
289
290 void DynamicShapeInferer::visit(const ir::operation::ElementwiseBinary &op)
291 {
292   handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::ElementwiseBinary::Input::LHS),
293                            op.getInputs().at(ir::operation::ElementwiseBinary::Input::RHS));
294 }
295
296 void DynamicShapeInferer::visit(const ir::operation::ElementwiseUnary &op)
297 {
298   handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::ElementwiseUnary::Input::INPUT));
299 }
300
301 void DynamicShapeInferer::visit(const ir::operation::ExpandDims &op)
302 {
303   // check if input is not dynamic
304   auto input_ind = op.getInputs().at(ir::operation::ExpandDims::INPUT);
305   auto input = _tensor_registry->getITensor(input_ind);
306
307   // check if output is not dynamic, meaning when 1st input is static and 2nd input is const
308   auto output_ind = op.getOutputs().at(0);
309   auto output = _tensor_registry->getITensor(output_ind);
310
311   /*
312     Here, the state after compilation (satic shape inference) could be one of the following:
313
314               input1     input2      output     execution-time shape inf required
315               -----------------------------     --------------------------------
316       case 1) static     const       static      X
317       case 2) static    placeholder  dynamic     O
318       case 3) dynamic    const       dynamic     O
319       case 4) dynamic   placeholder  dynamic     O
320
321     Then nnfw_apply_tensorinf() could change input dynamic.
322     So, in this method, we could have one more state and we have to re-calculate shape
323     for this shape.
324
325       case 5) dynamic    const       static       O
326
327     So, only when input1 and ouput are static, we can skip dynamic shape inference.
328   */
329   if ((!input->is_dynamic()) && (!output->is_dynamic()))
330     return;
331
332   ir::Shape input_shape = input->getShape();
333
334   auto axis_ind = op.getInputs().at(ir::operation::ExpandDims::AXIS);
335   auto axis = _tensor_registry->getITensor(axis_ind);
336   auto axis_buf = reinterpret_cast<const int32_t *>(axis->buffer());
337   assert(axis_buf);
338
339   auto output_shape = shape_inference::inferExpandDimsShape(input_shape, axis_buf[0]);
340
341   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
342   assert(output->buffer() != nullptr);
343 }
344
345 void DynamicShapeInferer::visit(const ir::operation::Fill &op)
346 {
347   // check if output is not dynamic
348   auto output_ind = op.getOutputs().at(0);
349   auto output = _tensor_registry->getITensor(output_ind);
350   auto input_ind = op.getInputs().at(ir::operation::Fill::Input::INPUT);
351   auto input = _tensor_registry->getITensor(input_ind);
352   ir::Shape input_shape = input->getShape();
353
354   if ((!input->is_dynamic()) && (!output->is_dynamic()))
355     return;
356
357   assert(input.get()->data_type() == ir::DataType::INT32);
358
359   auto input_buf = reinterpret_cast<const int32_t *>(input->buffer());
360   assert(input_buf);
361
362   auto output_shape = shape_inference::inferFillShape(input_shape, input_buf);
363
364   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
365   assert(output->buffer() != nullptr);
366 }
367
368 void DynamicShapeInferer::visit(const ir::operation::FullyConnected &op)
369 {
370   const auto input_idx{op.getInputs().at(ir::operation::FullyConnected::Input::INPUT)};
371   const auto &input = _tensor_registry->getITensor(input_idx);
372
373   const auto ker_idx{op.getInputs().at(ir::operation::FullyConnected::Input::WEIGHT)};
374   const auto &ker = _tensor_registry->getITensor(ker_idx);
375
376   if (!input->is_dynamic() && !ker->is_dynamic())
377     return;
378
379   auto input_shape = input->getShape();
380   auto ker_shape = ker->getShape();
381
382   ir::Shape new_shape = shape_inference::inferFullyConnectedShape(input_shape, ker_shape);
383
384   auto output_ind = op.getOutputs().at(0);
385   auto output = _tensor_registry->getITensor(output_ind);
386
387   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
388   assert(output->buffer() != nullptr);
389 }
390
391 void DynamicShapeInferer::visit(const ir::operation::FusedBatchNorm &op)
392 {
393   handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::FusedBatchNorm::Input::INPUT));
394 }
395
396 void DynamicShapeInferer::visit(const ir::operation::Gather &op)
397 {
398   const auto input_idx{op.getInputs().at(ir::operation::Gather::Input::INPUT)};
399   const auto &input = _tensor_registry->getITensor(input_idx);
400   auto input_shape = input->getShape();
401
402   const auto indices_idx{op.getInputs().at(ir::operation::Gather::Input::INDICES)};
403   const auto &indices = _tensor_registry->getITensor(indices_idx);
404   auto indices_shape = indices->getShape();
405
406   if (!(input->is_dynamic()) && !(indices->is_dynamic()))
407     return;
408
409   const auto rank = input_shape.rank();
410   const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
411
412   assert(0 <= axis && axis < rank);
413
414   ir::Shape new_shape = shape_inference::inferGatherShape(input_shape, indices_shape, axis, rank);
415
416   auto output_ind = op.getOutputs().at(0);
417   auto output = _tensor_registry->getITensor(output_ind);
418
419   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
420   assert(output->buffer() != nullptr);
421 }
422
423 void DynamicShapeInferer::visit(const ir::operation::L2Normalization &op)
424 {
425   handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::L2Normalization::INPUT));
426 }
427
428 void DynamicShapeInferer::visit(const ir::operation::MatrixBandPart &op)
429 {
430   handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::MatrixBandPart::INPUT));
431 }
432
433 void DynamicShapeInferer::visit(const ir::operation::OneHot &op)
434 {
435   auto output_ind = op.getOutputs().at(0);
436   auto output = _tensor_registry->getITensor(output_ind);
437
438   auto indices_ind = op.getInputs().at(ir::operation::OneHot::INDICES);
439   const auto &indices = _tensor_registry->getITensor(indices_ind);
440   auto indices_shape = indices->getShape();
441
442   auto depth_ind = op.getInputs().at(ir::operation::OneHot::DEPTH);
443   const auto &depth = _tensor_registry->getITensor(depth_ind);
444
445   if (!indices->is_dynamic() && !depth->is_dynamic())
446   {
447     return;
448   }
449
450   int32_t *depth_buf = reinterpret_cast<int32_t *>(depth->buffer());
451   assert(depth_buf);
452   const auto axis_val = op.param().axis;
453
454   ir::Shape new_shape = shape_inference::inferOnehotShape(indices_shape, *depth_buf, axis_val);
455   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
456   assert(output->buffer() != nullptr);
457 }
458
459 void DynamicShapeInferer::visit(const ir::operation::Pack &op)
460 {
461   bool is_any_of_inputs_dynamic = [&]() -> bool {
462     for (uint32_t i = 0; i < op.getInputs().size(); ++i)
463     {
464       const auto &input = _tensor_registry->getITensor(op.getInputs().at(i));
465       if (input->is_dynamic())
466       {
467         return true;
468       }
469     }
470     return false;
471   }();
472
473   const auto input_idx{op.getInputs().at(0)};
474   const auto &input = _tensor_registry->getITensor(input_idx);
475   auto input_shape = input->getShape();
476
477   auto output_ind = op.getOutputs().at(0);
478   auto output = _tensor_registry->getITensor(output_ind);
479
480   if (!is_any_of_inputs_dynamic && !output->is_dynamic())
481     return;
482
483   const auto rank = input_shape.rank() + 1;
484   const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
485   const auto num = op.param().num;
486
487   assert(0 <= axis && axis < rank);
488
489   ir::Shape new_shape = shape_inference::inferPackShape(input_shape, axis, rank, num);
490
491   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
492   assert(output->buffer() != nullptr);
493 }
494
495 void DynamicShapeInferer::visit(const ir::operation::Pad &op)
496 {
497   // check if output is not dynamic
498   auto output_ind = op.getOutputs().at(0);
499   auto output = _tensor_registry->getITensor(output_ind);
500
501   auto input_ind = op.getInputs().at(ir::operation::Pad::Input::INPUT);
502   auto input = _tensor_registry->getITensor(input_ind);
503
504   auto pad_ind = op.getInputs().at(ir::operation::Pad::Input::PAD);
505   auto pad = _tensor_registry->getITensor(pad_ind);
506
507   // check if input and output are not dynamic
508   if ((!input->is_dynamic()) && (!output->is_dynamic()))
509     return;
510
511   int32_t *pad_buf = reinterpret_cast<int32_t *>(pad->buffer());
512   assert(pad_buf);
513
514   auto output_shape =
515       shape_inference::inferPadShape(input->getShape(), pad_buf, pad->getShape().num_elements());
516
517   // change output shape and reallocate output tensor memory
518   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
519   assert(output->buffer() != nullptr);
520 }
521
522 void DynamicShapeInferer::visit(const ir::operation::Permute & /* op */)
523 {
524   // NOTE Permute is a special operation which does not do shape inference before the actual
525   // function(kernel) execution. Shape inference and output allocation will be done in the kernel
526   // on-the-fly, as it must support inter-backend inference/allocation.
527 }
528
529 void DynamicShapeInferer::visit(const ir::operation::Pow &op)
530 {
531   handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::Pow::Input::LHS),
532                            op.getInputs().at(ir::operation::Pow::Input::RHS));
533 }
534
535 void DynamicShapeInferer::visit(const ir::operation::Range &op)
536 {
537   // check if output is not dynamic
538   auto output_ind = op.getOutputs().at(0);
539   auto output = _tensor_registry->getITensor(output_ind);
540
541   // from op, access the buffer of second input to read new shape
542   auto start_idx = op.getInputs().at(ir::operation::Range::Input::START);
543   auto start_tensor = _tensor_registry->getITensor(start_idx);
544
545   auto limit_idx = op.getInputs().at(ir::operation::Range::Input::LIMIT);
546   auto limit_tensor = _tensor_registry->getITensor(limit_idx);
547
548   auto delta_idx = op.getInputs().at(ir::operation::Range::Input::DELTA);
549   auto delta_tensor = _tensor_registry->getITensor(delta_idx);
550
551   if (!start_tensor->is_dynamic() && !limit_tensor->is_dynamic() && !delta_tensor->is_dynamic() &&
552       !output->is_dynamic())
553     return;
554
555   ir::Shape new_shape;
556   if (output->data_type() == ir::DataType::FLOAT32)
557   {
558     new_shape =
559         shape_inference::inferRangeShape<float>(*reinterpret_cast<float *>(start_tensor->buffer()),
560                                                 *reinterpret_cast<float *>(limit_tensor->buffer()),
561                                                 *reinterpret_cast<float *>(delta_tensor->buffer()));
562   }
563   else if (output->data_type() == ir::DataType::INT32)
564   {
565     new_shape = shape_inference::inferRangeShape<int32_t>(
566         *reinterpret_cast<int32_t *>(start_tensor->buffer()),
567         *reinterpret_cast<int32_t *>(limit_tensor->buffer()),
568         *reinterpret_cast<int32_t *>(delta_tensor->buffer()));
569   }
570   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
571   assert(output->buffer() != nullptr);
572 }
573
574 void DynamicShapeInferer::visit(const ir::operation::Reduce &op)
575 {
576   const auto input_idx{op.getInputs().at(ir::operation::Reduce::Input::INPUT)};
577   const auto &input = _tensor_registry->getITensor(input_idx);
578   auto input_shape = input->getShape();
579
580   const auto axes_idx{op.getInputs().at(ir::operation::Reduce::Input::AXES)};
581   const auto &axes = _tensor_registry->getITensor(axes_idx);
582
583   if (!input->is_dynamic())
584     return;
585
586   std::vector<int32_t> axes_vec;
587   for (uint32_t i = 0; i < axes->getShape().num_elements(); ++i)
588   {
589     const auto buffer = axes->buffer() + axes->calcOffset({i});
590     switch (axes->data_type())
591     {
592       case ir::DataType::INT32:
593       {
594         axes_vec.emplace_back(*reinterpret_cast<const int32_t *>(buffer));
595         break;
596       }
597       case ir::DataType::INT64:
598       {
599         axes_vec.emplace_back(*reinterpret_cast<const int64_t *>(buffer));
600         break;
601       }
602       default:
603         throw std::runtime_error("DynamicShapeInferer " + op.name() + ": Not supported data type");
604         break;
605     }
606   }
607   const auto keep_dims = op.param().keep_dims;
608
609   auto output_ind = op.getOutputs().at(0);
610   auto output = _tensor_registry->getITensor(output_ind);
611
612   ir::Shape new_shape = shape_inference::inferReduceShape(input_shape, axes_vec, keep_dims);
613
614   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
615   assert(output->buffer() != nullptr);
616 }
617
618 void DynamicShapeInferer::visit(const ir::operation::Reshape &op)
619 {
620   // check if output is not dynamic
621   auto output_ind = op.getOutputs().at(0);
622   auto output = _tensor_registry->getITensor(output_ind);
623
624   auto input_ind = op.getInputs().at(ir::operation::Reshape::Input::INPUT);
625   auto input = _tensor_registry->getITensor(input_ind);
626
627   /*
628     Here, the state after compilation (satic shape inference) could be one of the following:
629
630               input1   input2 (or option)   output     execution-time shape inf required
631               ------------------------------------     --------------------------------
632       case 1) static         const          static       X
633       case 2) static      placeholder       dynamic      O
634       case 3) dynamic        const          dynamic      O
635       case 4) dynamic     placeholder       dynamic      O
636
637     Then nnfw_apply_tensorinf() could change input dynamic.
638     So, in this method, we could have one more state and we have to re-calculate shape
639     for this shape.
640
641       case 5) dynamic    const       static       O
642
643     So, only when both input1 and ouput are static, we can skip dynamic shape inference.
644   */
645   if ((!input->is_dynamic()) && (!output->is_dynamic()))
646     return;
647
648   // New shape is given by second input tensor
649   if (op.getInputs().size() == 2)
650   {
651     // from op, access the buffer of second input to read new shape
652     auto new_shape_ind = op.getInputs().at(ir::operation::Reshape::Input::SHAPE);
653
654     // getting output shape by reading new_shape tensor buffer
655     auto new_shape = _tensor_registry->getITensor(new_shape_ind);
656     assert(new_shape);
657
658     int32_t *new_shape_buf = reinterpret_cast<int32_t *>(new_shape->buffer());
659     assert(new_shape_buf);
660
661     auto output_shape = shape_inference::inferReshapeShape(
662         new_shape_buf, new_shape->getShape().num_elements(), input->getShape().num_elements());
663
664     // if shape is changed, change output shape and reallocate output tensor memory
665     if (output_shape != output->getShape() || output->buffer() == nullptr)
666     {
667       // change on output shape
668       dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
669     }
670     assert(output->buffer() != nullptr);
671   }
672   // New shape is given by option
673   else if (op.param().new_shape.size() != 0)
674   {
675     // Let's check the new_shape option
676     auto shape = op.param().new_shape;
677     auto output_shape = shape_inference::inferReshapeShape(shape.data(), shape.size(),
678                                                            input->getShape().num_elements());
679
680     // if shape is changed, change output shape and reallocate output tensor memory
681     if (output_shape != output->getShape() || output->buffer() == nullptr)
682     {
683       // change on output shape
684       dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
685     }
686     assert(output->buffer() != nullptr);
687   }
688   else
689   {
690     throw std::runtime_error("Reshape: new shape is missing");
691     return;
692   }
693 }
694
695 void DynamicShapeInferer::visit(const ir::operation::ResizeBilinear &op)
696 {
697   // check if output is not dynamic
698   auto output_ind = op.getOutputs().at(0);
699   auto output = _tensor_registry->getITensor(output_ind);
700
701   auto input_ind = op.getInputs().at(ir::operation::Reshape::Input::INPUT);
702   auto input = _tensor_registry->getITensor(input_ind);
703
704   if ((!input->is_dynamic()) && (!output->is_dynamic()))
705     return;
706
707   // getting output shape from input shape and Params
708   auto output_shape = shape_inference::inferResizeBilinearShape(
709       input->getShape(), op.param().height_out, op.param().width_out);
710
711   // if shape is changed, change output shape and reallocate output tensor memory
712   if (output_shape != output->getShape() || output->buffer() == nullptr)
713   {
714     // change on output shape
715     dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
716   }
717   assert(output->buffer() != nullptr);
718 }
719
720 void DynamicShapeInferer::visit(const ir::operation::Reverse &op)
721 {
722   handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Reverse::INPUT));
723 }
724
725 void DynamicShapeInferer::visit(const ir::operation::Select &op)
726 {
727   const auto input_cond_idx = op.getInputs().at(ir::operation::Select::Input::CONDITION);
728   const auto &input_cond = _tensor_registry->getITensor(input_cond_idx);
729
730   const auto input_true_idx = op.getInputs().at(ir::operation::Select::Input::INPUT_TRUE);
731   const auto &input_true = _tensor_registry->getITensor(input_true_idx);
732
733   const auto input_false_idx = op.getInputs().at(ir::operation::Select::Input::INPUT_FALSE);
734   const auto &input_false = _tensor_registry->getITensor(input_false_idx);
735
736   if ((!input_cond->is_dynamic()) && (!input_true->is_dynamic()) && (!input_false->is_dynamic()))
737   {
738     return;
739   }
740
741   auto input_cond_shape = input_cond->getShape();
742   auto input_true_shape = input_true->getShape();
743   auto input_false_shape = input_false->getShape();
744
745   // Select output shpae
746   ir::Shape new_shape =
747       shape_inference::inferSelectShape(input_cond_shape, input_true_shape, input_false_shape);
748
749   auto output_ind = op.getOutputs().at(0);
750   auto output = _tensor_registry->getITensor(output_ind);
751
752   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
753   assert(output->buffer() != nullptr);
754 }
755
756 void DynamicShapeInferer::visit(const ir::operation::Shape &op)
757 {
758   const auto input_idx{op.getInputs().at(0)};
759   const auto &input = _tensor_registry->getITensor(input_idx);
760   auto input_shape = input->getShape();
761
762   if (!input->is_dynamic())
763     return;
764
765   auto output_ind = op.getOutputs().at(0);
766   auto output = _tensor_registry->getITensor(output_ind);
767
768   ir::Shape output_shape;
769   output_shape.append(input_shape.rank());
770
771   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
772   assert(output->buffer() != nullptr);
773 }
774
775 void DynamicShapeInferer::visit(const ir::operation::Slice &op)
776 {
777   const auto input_index{op.getInputs().at(ir::operation::Slice::Input::INPUT)};
778   const auto input = _tensor_registry->getITensor(input_index);
779   const auto begins_index{op.getInputs().at(ir::operation::Slice::Input::BEGINS)};
780   const auto begins = _tensor_registry->getITensor(begins_index);
781   const auto sizes_index{op.getInputs().at(ir::operation::Slice::Input::SIZES)};
782   const auto sizes = _tensor_registry->getITensor(sizes_index);
783   auto output_index = op.getOutputs().at(0);
784   auto output = _tensor_registry->getITensor(output_index);
785
786   if (!(input->is_dynamic() || begins->is_dynamic() || sizes->is_dynamic() || output->is_dynamic()))
787   {
788     return;
789   }
790
791   ir::Shape input_shape = input->getShape();
792   auto begins_buf = reinterpret_cast<const int32_t *>(begins->buffer());
793   auto sizes_buf = reinterpret_cast<const int32_t *>(sizes->buffer());
794
795   ir::Shape new_shape = shape_inference::inferSliceShape(input_shape, begins_buf, sizes_buf);
796
797   dynamicTensorManagerOf(output)->applyShape(output_index, new_shape);
798   assert(output->buffer() != nullptr);
799 }
800
801 void DynamicShapeInferer::visit(const ir::operation::Softmax &op)
802 {
803   handleSimpleUnaryOp(op, op.getInputs().at(ir::operation::Softmax::INPUT));
804 }
805
806 void DynamicShapeInferer::visit(const ir::operation::SpaceToBatchND &op)
807 {
808   const auto input_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::INPUT)};
809   const auto block_shape_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::BLOCK_SIZE)};
810   const auto padding_idx{op.getInputs().at(ir::operation::SpaceToBatchND::Input::PADDINGS)};
811   auto output_idx{op.getOutputs().at(0)};
812
813   const auto &input = _tensor_registry->getITensor(input_idx);
814   const auto &block_shape = _tensor_registry->getITensor(block_shape_idx);
815   const auto &padding = _tensor_registry->getITensor(padding_idx);
816   auto output = _tensor_registry->getITensor(output_idx);
817
818   if (!(input->is_dynamic() || block_shape->is_dynamic() || padding->is_dynamic() ||
819         output->is_dynamic()))
820   {
821     return;
822   }
823
824   auto input_shape = input->getShape();
825   auto block_shape_shape = block_shape->getShape();
826   auto padding_shape = padding->getShape();
827
828   auto block_shape_data = reinterpret_cast<int32_t *>(block_shape->buffer());
829   auto padding_data = reinterpret_cast<int32_t *>(padding->buffer());
830
831   ir::Shape new_shape = shape_inference::inferSpaceToBatchNDShape(
832       input_shape, block_shape_shape, padding_shape, block_shape_data, padding_data);
833
834   dynamicTensorManagerOf(output)->applyShape(output_idx, new_shape);
835   assert(output->buffer() != nullptr);
836 }
837
838 void DynamicShapeInferer::visit(const ir::operation::Split &op)
839 {
840   const auto input_idx{op.getInputs().at(ir::operation::Split::Input::INPUT)};
841   const auto &input = _tensor_registry->getITensor(input_idx);
842
843   if (!input->is_dynamic())
844   {
845     return;
846   }
847
848   auto input_shape = input->getShape();
849
850   const auto axis = op.param().axis;
851   const auto num_splits = op.param().num_splits;
852   const auto rank = input_shape.rank();
853   auto axis_resolved = axis < 0 ? axis + rank : axis;
854
855   assert(0 <= axis_resolved && axis_resolved < rank);
856
857   ir::Shape new_shape = shape_inference::inferSplitShape(input_shape, axis_resolved, num_splits);
858   for (int out_tensor_idx = 0; out_tensor_idx < num_splits; out_tensor_idx++)
859   {
860     auto output_ind = op.getOutputs().at(out_tensor_idx);
861     auto output = _tensor_registry->getITensor(output_ind);
862
863     dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
864     assert(output->buffer() != nullptr);
865   }
866 }
867
868 void DynamicShapeInferer::visit(const ir::operation::SquaredDifference &op)
869 {
870   handleBinaryArithmeticOp(op, op.getInputs().at(ir::operation::SquaredDifference::Input::LHS),
871                            op.getInputs().at(ir::operation::SquaredDifference::Input::RHS));
872 }
873
874 void DynamicShapeInferer::visit(const ir::operation::Squeeze &op)
875 {
876   const auto input_idx{op.getInputs().at(ir::operation::Squeeze::Input::INPUT)};
877   const auto &input = _tensor_registry->getITensor(input_idx);
878
879   if (!input->is_dynamic())
880   {
881     return;
882   }
883
884   auto input_shape = input->getShape();
885
886   // Squeeze output shpae
887   ir::Shape new_shape = shape_inference::inferSqueezeShape(input_shape, op.param());
888
889   auto output_ind = op.getOutputs().at(0);
890   auto output = _tensor_registry->getITensor(output_ind);
891
892   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
893   assert(output->buffer() != nullptr);
894 }
895
896 void DynamicShapeInferer::visit(const ir::operation::StridedSlice &op)
897 {
898
899   const auto input_index{op.getInputs().at(ir::operation::StridedSlice::Input::INPUT)};
900   auto input = _tensor_registry->getITensor(input_index);
901   ir::Shape input_shape = input->getShape();
902
903   const auto starts_index{op.getInputs().at(ir::operation::StridedSlice::Input::STARTS)};
904   auto starts = _tensor_registry->getITensor(starts_index);
905
906   const auto ends_index{op.getInputs().at(ir::operation::StridedSlice::Input::ENDS)};
907   auto ends = _tensor_registry->getITensor(ends_index);
908
909   const auto strides_index{op.getInputs().at(ir::operation::StridedSlice::Input::STRIDES)};
910   auto strides = _tensor_registry->getITensor(strides_index);
911
912   if (!(input->is_dynamic() || starts->is_dynamic() || ends->is_dynamic() || strides->is_dynamic()))
913   {
914     return;
915   }
916
917   const auto begin_mask = op.param().begin_mask;
918   const auto end_mask = op.param().end_mask;
919   const auto shrink_axis_mask = op.param().shrink_axis_mask;
920   const auto rank = input_shape.rank();
921
922   auto op_params = shape_inference::buildStridedSliceParams(
923       reinterpret_cast<uint32_t *>(starts->buffer()), reinterpret_cast<uint32_t *>(ends->buffer()),
924       reinterpret_cast<uint32_t *>(strides->buffer()), begin_mask, end_mask, shrink_axis_mask,
925       rank);
926
927   auto output_index = op.getOutputs().at(0);
928   auto output = _tensor_registry->getITensor(output_index);
929
930   ir::Shape output_shape =
931       onert::shape_inference::inferStridedSliceShape(input_shape, op_params, rank);
932
933   dynamicTensorManagerOf(output)->applyShape(output_index, output_shape);
934   assert(output->buffer() != nullptr);
935 }
936
937 void DynamicShapeInferer::visit(const ir::operation::Tile &op)
938 {
939   auto output_ind = op.getOutputs().at(0);
940   auto output = _tensor_registry->getITensor(output_ind);
941
942   auto input_idx = op.getInputs().at(ir::operation::Tile::Input::INPUT);
943   auto input = _tensor_registry->getITensor(input_idx);
944
945   auto multiplier_idx = op.getInputs().at(ir::operation::Tile::Input::MULTIPLES);
946   auto multiplier = _tensor_registry->getITensor(multiplier_idx);
947
948   if ((!input->is_dynamic()) && (!output->is_dynamic()))
949     return;
950
951   auto input_shape = input->getShape();
952   auto multiplier_buffer = reinterpret_cast<const int32_t *>(multiplier->buffer());
953   assert(multiplier_buffer);
954
955   auto output_shape = shape_inference::inferTileShape(input_shape, multiplier_buffer);
956
957   // set output shape and output buffer
958   dynamicTensorManagerOf(output)->applyShape(output_ind, output_shape);
959   assert(output->buffer() != nullptr);
960 }
961
962 void DynamicShapeInferer::visit(const ir::operation::Transpose &op)
963 {
964   // check if output is not dynamic
965   auto output_ind = op.getOutputs().at(0);
966   auto output = _tensor_registry->getITensor(output_ind);
967
968   // from op, access the buffer of second input to read new shape
969   auto input_ind = op.getInputs().at(ir::operation::Transpose::Input::INPUT);
970   auto input_tensor = _tensor_registry->getITensor(input_ind);
971   auto input_shape = input_tensor->getShape();
972
973   if (!input_tensor->is_dynamic())
974     return;
975
976   const auto perm{op.param().perm};
977   // set output shape, based on input and params
978   ir::Shape new_shape = shape_inference::inferTransposeShape(input_shape, perm);
979
980   dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
981   assert(output->buffer() != nullptr);
982 }
983
984 void DynamicShapeInferer::visit(const ir::operation::Unpack &op)
985 {
986   // check if output is not dynamic
987   const auto input_idx{op.getInputs().at(0)};
988   const auto &input = _tensor_registry->getITensor(input_idx);
989
990   if (!input->is_dynamic())
991     return;
992
993   auto input_shape = input->getShape();
994
995   const auto rank = input_shape.rank();
996   const auto axis = ((op.param().axis < 0) ? rank + op.param().axis : op.param().axis);
997   const auto num = op.param().num;
998
999   assert(0 <= axis && axis < rank);
1000
1001   ir::Shape new_shape = shape_inference::inferUnpackShape(input_shape, axis, rank);
1002
1003   for (int out_tensor_idx = 0; out_tensor_idx < num; out_tensor_idx++)
1004   {
1005     auto output_ind = op.getOutputs().at(out_tensor_idx);
1006     auto output = _tensor_registry->getITensor(output_ind);
1007
1008     dynamicTensorManagerOf(output)->applyShape(output_ind, new_shape);
1009
1010     assert(output->buffer() != nullptr);
1011   }
1012 }
1013
1014 } // namespace exec
1015 } // namespace onert