ONE - On-device Neural Engine
Loading...
Searching...
No Matches
onert::compiler::StaticShapeInferer Class Reference

Class to infer shape before running kernels. It does the following: More...

#include <StaticShapeInferer.h>

Collaboration diagram for onert::compiler::StaticShapeInferer:

Public Member Functions

 StaticShapeInferer (compiler::ILoweredGraph *lowered_subg)
 
virtual ~StaticShapeInferer ()=default
 
void appendSubgInputObserver (const ir::SubgraphIndex &subg_idx, std::unique_ptr< OperandObserver > &&subg_input_observer) noexcept
 
void setControlflowOutputObserver (std::unique_ptr< OperandObserver > &&output_observer) noexcept
 
void appendChildInferer (const ir::SubgraphIndex &subg_idx, compiler::StaticShapeInferer *inferer)
 
void infer (void)
 Infer shape of operands belonging to ops and set the output shape. If output shape cannot be known without running op, mark it so that it can be allocated when running kernel.
 
void dump ()
 
- Public Member Functions inherited from onert::ir::OperationVisitor
virtual ~OperationVisitor ()=default
 

Static Public Member Functions

static std::unordered_map< ir::SubgraphIndex, std::unique_ptr< StaticShapeInferer > > createStaticShapeInferers (const std::unordered_map< ir::SubgraphIndex, ILoweredGraph * > &lowered_subgs)
 Create a shape inferer map for a lowered model.
 

Detailed Description

Class to infer shape before running kernels. It does the following:

  • re-calculate and set output shape at compile time (before running kernels)
  • if calculation cannot be done at compile time, mark the outputs to be dynamic, meaning shapes of outputs will be calculated during running kernels

Definition at line 66 of file StaticShapeInferer.h.

Constructor & Destructor Documentation

◆ StaticShapeInferer()

onert::compiler::StaticShapeInferer::StaticShapeInferer ( compiler::ILoweredGraph lowered_subg)
inline

Definition at line 69 of file StaticShapeInferer.h.

70 : _lowered_subg{lowered_subg}, _subg_input_observers{}, _controlflow_output_observer{nullptr},
71 _child_inferers{}
72 {
73 }

◆ ~StaticShapeInferer()

virtual onert::compiler::StaticShapeInferer::~StaticShapeInferer ( )
virtualdefault

Member Function Documentation

◆ appendChildInferer()

void onert::compiler::StaticShapeInferer::appendChildInferer ( const ir::SubgraphIndex subg_idx,
compiler::StaticShapeInferer inferer 
)
inline

Definition at line 88 of file StaticShapeInferer.h.

89 {
90 _child_inferers[subg_idx] = inferer;
91 }

Referenced by createStaticShapeInferers().

◆ appendSubgInputObserver()

void onert::compiler::StaticShapeInferer::appendSubgInputObserver ( const ir::SubgraphIndex subg_idx,
std::unique_ptr< OperandObserver > &&  subg_input_observer 
)
inlinenoexcept

Definition at line 77 of file StaticShapeInferer.h.

79 {
80 _subg_input_observers[subg_idx] = std::move(subg_input_observer);
81 }

◆ createStaticShapeInferers()

std::unordered_map< ir::SubgraphIndex, std::unique_ptr< StaticShapeInferer > > onert::compiler::StaticShapeInferer::createStaticShapeInferers ( const std::unordered_map< ir::SubgraphIndex, ILoweredGraph * > &  lowered_subgs)
static

Create a shape inferer map for a lowered model.

Parameters
[in]lowered_subgslowered model map
Returns
Shape inferer map

Definition at line 204 of file StaticShapeInferer.cc.

206{
207 // Allocate StaticShapeInferer per each subgraph
208 std::unordered_map<ir::SubgraphIndex, std::unique_ptr<StaticShapeInferer>> inferers;
209 for (auto &&[subg_index, lowered_subg] : lowered_subgs)
210 {
211 inferers[subg_index] = std::make_unique<StaticShapeInferer>(lowered_subg);
212 }
213
214 // Append observers in all StaticShapeInferers
215 for (auto &&pair : lowered_subgs)
216 {
217 const auto &subg_index = pair.first;
218 auto &lowered_subg = pair.second;
219
220 // TODO: Change this iteration for all to controlflow iteration
221 lowered_subg->graph().operations().iterate(
222 [&](const ir::OperationIndex &, const ir::IOperation &op) {
223 // A Function to append child inferers. These make it possible for a StaticShapeInferer to
224 // call StaticShapeInferes of child subgraphs recursively
225 auto appendChildInferer = [&](const ir::SubgraphIndex &child_subg_idx) {
226 auto *child_inferer = inferers.at(child_subg_idx).get();
227 inferers.at(subg_index)->appendChildInferer(child_subg_idx, child_inferer);
228 };
229
230 // A Function to appaend subg input observers. This makes it possible for a
231 // StaticShapeInferer to update inputs of child subgraphs
232 auto appendSubgraphInputObserver = [&](const ir::SubgraphIndex &child_subg_idx) {
233 std::vector<ir::Operand *> child_subg_inputs;
234 auto &child_subg = lowered_subgs.at(child_subg_idx)->graph();
235 for (const auto &input_idx : child_subg.getInputs())
236 {
237 auto operand_ptr = child_subg.operands().getRawPtr(input_idx);
238 child_subg_inputs.emplace_back(operand_ptr);
239 }
240 inferers.at(subg_index)
241 ->appendSubgInputObserver(child_subg_idx,
242 std::make_unique<OperandObserver>(child_subg_inputs));
243 };
244
245 // A Function to set controlflow output observers. This makes it possible for a
246 // StaticShapeInferer to update outputs of parent controlflow opeerations
247 auto setControlFlowOutputObserver = [&](const ir::SubgraphIndex &child_subg_idx) {
248 std::vector<ir::Operand *> cf_outputs;
249 auto &subg = lowered_subg->graph();
250 for (const auto &output_idx : op.getOutputs())
251 {
252 auto operand_ptr = subg.operands().getRawPtr(output_idx);
253 cf_outputs.emplace_back(operand_ptr);
254 }
255 inferers.at(child_subg_idx)
256 ->setControlflowOutputObserver(std::make_unique<OperandObserver>(cf_outputs));
257 };
258
259 // Append Observers in a StaticShapeInferer
260 if (op.opcode() == ir::OpCode::If)
261 {
262 // TODO Remove dynamic_cast
263 // An virtual base class cannot be downcasted by static_cast
264 try
265 {
266 const auto &if_op = dynamic_cast<const ir::operation::If &>(op);
267
268 appendChildInferer(if_op.param().then_subg_index);
269 appendChildInferer(if_op.param().else_subg_index);
270
271 appendSubgraphInputObserver(if_op.param().then_subg_index);
272 appendSubgraphInputObserver(if_op.param().else_subg_index);
273
274 setControlFlowOutputObserver(if_op.param().then_subg_index);
275 }
276 catch (const std::bad_cast &)
277 {
278 throw std::runtime_error("StaticShapeInferer: Invalid If operation");
279 }
280 }
281 else if (op.opcode() == ir::OpCode::While)
282 {
283 // TODO Remove dynamic_cast
284 try
285 {
286 const auto &while_op = dynamic_cast<const ir::operation::While &>(op);
287
288 appendChildInferer(while_op.param().cond_subg_index);
289 appendChildInferer(while_op.param().body_subg_index);
290
291 appendSubgraphInputObserver(while_op.param().cond_subg_index);
292 appendSubgraphInputObserver(while_op.param().body_subg_index);
293
294 setControlFlowOutputObserver(while_op.param().body_subg_index);
295 }
296 catch (const std::bad_cast &)
297 {
298 throw std::runtime_error("StaticShapeInferer: Invalid While operation");
299 }
300 }
301 else if (op.opcode() == ir::OpCode::Call)
302 {
303 // TODO Remove dynamic_cast
304 try
305 {
306 const auto &call_op = dynamic_cast<const ir::operation::Call &>(op);
307
308 appendChildInferer(call_op.param().callee_subg_index);
309
310 appendSubgraphInputObserver(call_op.param().callee_subg_index);
311 setControlFlowOutputObserver(call_op.param().callee_subg_index);
312 }
313 catch (const std::bad_cast &)
314 {
315 throw std::runtime_error("StaticShapeInferer: Invalid Call operation");
316 }
317 }
318 });
319 }
320
321 return inferers;
322}
void appendChildInferer(const ir::SubgraphIndex &subg_idx, compiler::StaticShapeInferer *inferer)
::onert::util::Index< uint32_t, OperationIndexTag > OperationIndex
Definition Index.h:30
::onert::util::Index< uint16_t, SubgraphIndexTag > SubgraphIndex
Definition Index.h:39

References appendChildInferer(), onert::ir::IOperation::getOutputs(), and onert::ir::IOperation::opcode().

◆ dump()

void onert::compiler::StaticShapeInferer::dump ( )

Definition at line 179 of file StaticShapeInferer.cc.

180{
181 auto get_shape_str = [](const ir::Shape &shape) {
182 std::stringstream sstream;
183 sstream << "shape : {";
184 for (int i = 0; i < shape.rank(); i++)
185 {
186 if (i == 0)
187 sstream << shape.dim(i);
188 else
189 sstream << " " << shape.dim(i);
190 }
191 sstream << "}";
192 return sstream.str();
193 };
194
195 _lowered_subg->graph().operands().iterate(
196 [&](const ir::OperandIndex &ind, const ir::Operand &operand) {
197 VERBOSE(StaticShapeInferer) << " " << ind << ", "
198 << (operand.info().isDynamic() ? "Dynamic" : "Static") << ", "
199 << get_shape_str(operand.info().shape()) << std::endl;
200 });
201}
const Operands & operands() const override
Definition Graph.h:103
void iterate(const std::function< void(const Index &, const Object &)> &fn) const
Iterate over the container with given function.
#define VERBOSE(name, lv)
Definition Log.h:71
::onert::util::Index< uint32_t, OperandIndexTag > OperandIndex
Definition Index.h:33
virtual ir::Graph & graph()=0

References onert::compiler::ILoweredGraph::graph(), onert::ir::Operand::info(), onert::ir::OperandInfo::isDynamic(), onert::util::ObjectManager< Index, Object >::iterate(), onert::ir::Graph::operands(), onert::ir::OperandInfo::shape(), and VERBOSE.

◆ infer()

void onert::compiler::StaticShapeInferer::infer ( void  )

Infer shape of operands belonging to ops and set the output shape. If output shape cannot be known without running op, mark it so that it can be allocated when running kernel.

Definition at line 56 of file StaticShapeInferer.cc.

57{
58 for (const auto &op_idx : _lowered_subg->graph().topolSortOperations())
59 {
60 const auto &op = _lowered_subg->graph().operations().at(op_idx);
61
62 // Automatically mark any input with a dynamic dimension (-1)
63 // so its shape is computed at execution time.
64 for (const auto &idx : op.getUsedInputSet())
65 {
66 auto &input = _lowered_subg->graph().operands().at(idx);
67 const auto &shape = input.info().shape();
68 if (shape.hasUnspecifiedDims())
69 input.info().setDynamic();
70 }
71
72 bool has_dynamic_tensor = false;
73 const auto opcode = op.opcode();
74 // IF: requires shape inference for then, else
75 // While: requires shape inference for condition, body
76 // Call: requires shape inference for callee
77 if (opcode == ir::OpCode::If || opcode == ir::OpCode::While || opcode == ir::OpCode::Call)
78 {
79 op.accept(*this);
80 }
81 else
82 {
83 has_dynamic_tensor = checkDynamicInput(op);
84 if (has_dynamic_tensor)
85 {
86 setDynamicOutput(op);
87 }
88 else
89 {
90 op.accept(*this);
91 }
92 }
93 has_dynamic_tensor = has_dynamic_tensor || checkDynamicOutput(op);
94 _lowered_subg->setHasDynamicTensor(op_idx, has_dynamic_tensor);
95 }
96
97 if (_controlflow_output_observer != nullptr)
98 {
99 // re-sizing output shapes of the controflow operation branching to this subgraph
100 std::vector<ir::OperandInfo> outputs_info;
101 const auto &graph = _lowered_subg->graph();
102 const auto &outputs = graph.getOutputs();
103 for (size_t i = 0; i < outputs.size(); ++i)
104 {
105 const auto &operand_info = graph.operands().at(outputs.at(i)).info();
106 outputs_info.emplace_back(operand_info);
107 }
108 _controlflow_output_observer->updateShapes(outputs_info);
109 }
110}
const Operations & operations() const override
Definition Graph.h:105
const Object & at(const Index &index) const
Get the object that is associated with the given index.
virtual void setHasDynamicTensor(ir::OperationIndex ind, bool val)=0

References onert::util::ObjectManager< Index, Object >::at(), onert::compiler::ILoweredGraph::graph(), onert::ir::Graph::operands(), onert::ir::Graph::operations(), onert::compiler::ILoweredGraph::setHasDynamicTensor(), and onert::ir::Graph::topolSortOperations().

◆ setControlflowOutputObserver()

void onert::compiler::StaticShapeInferer::setControlflowOutputObserver ( std::unique_ptr< OperandObserver > &&  output_observer)
inlinenoexcept

Definition at line 83 of file StaticShapeInferer.h.

84 {
85 _controlflow_output_observer = std::move(output_observer);
86 }

The documentation for this class was generated from the following files: