Blender  V3.3
multi_function_procedure.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
4 
5 #include "BLI_dot_export.hh"
6 #include "BLI_stack.hh"
7 
8 namespace blender::fn {
9 
10 void MFInstructionCursor::set_next(MFProcedure &procedure, MFInstruction *new_instruction) const
11 {
12  switch (type_) {
13  case Type::None: {
14  break;
15  }
16  case Type::Entry: {
17  procedure.set_entry(*new_instruction);
18  break;
19  }
20  case Type::Call: {
21  static_cast<MFCallInstruction *>(instruction_)->set_next(new_instruction);
22  break;
23  }
24  case Type::Branch: {
25  MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_);
26  if (branch_output_) {
27  branch_instruction.set_branch_true(new_instruction);
28  }
29  else {
30  branch_instruction.set_branch_false(new_instruction);
31  }
32  break;
33  }
34  case Type::Destruct: {
35  static_cast<MFDestructInstruction *>(instruction_)->set_next(new_instruction);
36  break;
37  }
38  case Type::Dummy: {
39  static_cast<MFDummyInstruction *>(instruction_)->set_next(new_instruction);
40  break;
41  }
42  }
43 }
44 
46 {
47  switch (type_) {
48  case Type::None:
49  return nullptr;
50  case Type::Entry:
51  return procedure.entry();
52  case Type::Call:
53  return static_cast<MFCallInstruction *>(instruction_)->next();
54  case Type::Branch: {
55  MFBranchInstruction &branch_instruction = *static_cast<MFBranchInstruction *>(instruction_);
56  if (branch_output_) {
57  return branch_instruction.branch_true();
58  }
59  return branch_instruction.branch_false();
60  }
61  case Type::Destruct:
62  return static_cast<MFDestructInstruction *>(instruction_)->next();
63  case Type::Dummy:
64  return static_cast<MFDummyInstruction *>(instruction_)->next();
65  }
66  return nullptr;
67 }
68 
69 void MFVariable::set_name(std::string name)
70 {
71  name_ = std::move(name);
72 }
73 
75 {
76  if (next_ != nullptr) {
77  next_->prev_.remove_first_occurrence_and_reorder(*this);
78  }
79  if (instruction != nullptr) {
80  instruction->prev_.append(*this);
81  }
82  next_ = instruction;
83 }
84 
85 void MFCallInstruction::set_param_variable(int param_index, MFVariable *variable)
86 {
87  if (params_[param_index] != nullptr) {
88  params_[param_index]->users_.remove_first_occurrence_and_reorder(this);
89  }
90  if (variable != nullptr) {
91  BLI_assert(fn_->param_type(param_index).data_type() == variable->data_type());
92  variable->users_.append(this);
93  }
94  params_[param_index] = variable;
95 }
96 
98 {
99  BLI_assert(variables.size() == params_.size());
100  for (const int i : variables.index_range()) {
101  this->set_param_variable(i, variables[i]);
102  }
103 }
104 
106 {
107  if (condition_ != nullptr) {
108  condition_->users_.remove_first_occurrence_and_reorder(this);
109  }
110  if (variable != nullptr) {
111  variable->users_.append(this);
112  }
113  condition_ = variable;
114 }
115 
117 {
118  if (branch_true_ != nullptr) {
119  branch_true_->prev_.remove_first_occurrence_and_reorder({*this, true});
120  }
121  if (instruction != nullptr) {
122  instruction->prev_.append({*this, true});
123  }
124  branch_true_ = instruction;
125 }
126 
128 {
129  if (branch_false_ != nullptr) {
130  branch_false_->prev_.remove_first_occurrence_and_reorder({*this, false});
131  }
132  if (instruction != nullptr) {
133  instruction->prev_.append({*this, false});
134  }
135  branch_false_ = instruction;
136 }
137 
139 {
140  if (variable_ != nullptr) {
141  variable_->users_.remove_first_occurrence_and_reorder(this);
142  }
143  if (variable != nullptr) {
144  variable->users_.append(this);
145  }
146  variable_ = variable;
147 }
148 
150 {
151  if (next_ != nullptr) {
152  next_->prev_.remove_first_occurrence_and_reorder(*this);
153  }
154  if (instruction != nullptr) {
155  instruction->prev_.append(*this);
156  }
157  next_ = instruction;
158 }
159 
161 {
162  if (next_ != nullptr) {
163  next_->prev_.remove_first_occurrence_and_reorder(*this);
164  }
165  if (instruction != nullptr) {
166  instruction->prev_.append(*this);
167  }
168  next_ = instruction;
169 }
170 
171 MFVariable &MFProcedure::new_variable(MFDataType data_type, std::string name)
172 {
173  MFVariable &variable = *allocator_.construct<MFVariable>().release();
174  variable.name_ = std::move(name);
175  variable.data_type_ = data_type;
176  variable.index_in_graph_ = variables_.size();
177  variables_.append(&variable);
178  return variable;
179 }
180 
182 {
183  MFCallInstruction &instruction = *allocator_.construct<MFCallInstruction>().release();
184  instruction.type_ = MFInstructionType::Call;
185  instruction.fn_ = &fn;
186  instruction.params_ = allocator_.allocate_array<MFVariable *>(fn.param_amount());
187  instruction.params_.fill(nullptr);
188  call_instructions_.append(&instruction);
189  return instruction;
190 }
191 
193 {
194  MFBranchInstruction &instruction = *allocator_.construct<MFBranchInstruction>().release();
195  instruction.type_ = MFInstructionType::Branch;
196  branch_instructions_.append(&instruction);
197  return instruction;
198 }
199 
201 {
202  MFDestructInstruction &instruction = *allocator_.construct<MFDestructInstruction>().release();
203  instruction.type_ = MFInstructionType::Destruct;
204  destruct_instructions_.append(&instruction);
205  return instruction;
206 }
207 
209 {
210  MFDummyInstruction &instruction = *allocator_.construct<MFDummyInstruction>().release();
211  instruction.type_ = MFInstructionType::Dummy;
212  dummy_instructions_.append(&instruction);
213  return instruction;
214 }
215 
217 {
218  MFReturnInstruction &instruction = *allocator_.construct<MFReturnInstruction>().release();
219  instruction.type_ = MFInstructionType::Return;
220  return_instructions_.append(&instruction);
221  return instruction;
222 }
223 
225 {
226  params_.append({interface_type, &variable});
227 }
228 
230 {
231  if (entry_ != nullptr) {
232  entry_->prev_.remove_first_occurrence_and_reorder(MFInstructionCursor::ForEntry());
233  }
234  entry_ = &entry;
235  entry_->prev_.append(MFInstructionCursor::ForEntry());
236 }
237 
239 {
240  for (MFCallInstruction *instruction : call_instructions_) {
241  instruction->~MFCallInstruction();
242  }
243  for (MFBranchInstruction *instruction : branch_instructions_) {
244  instruction->~MFBranchInstruction();
245  }
246  for (MFDestructInstruction *instruction : destruct_instructions_) {
247  instruction->~MFDestructInstruction();
248  }
249  for (MFDummyInstruction *instruction : dummy_instructions_) {
250  instruction->~MFDummyInstruction();
251  }
252  for (MFReturnInstruction *instruction : return_instructions_) {
253  instruction->~MFReturnInstruction();
254  }
255  for (MFVariable *variable : variables_) {
256  variable->~MFVariable();
257  }
258 }
259 
261 {
262  if (entry_ == nullptr) {
263  return false;
264  }
265  if (!this->validate_all_instruction_pointers_set()) {
266  return false;
267  }
268  if (!this->validate_all_params_provided()) {
269  return false;
270  }
271  if (!this->validate_same_variables_in_one_call()) {
272  return false;
273  }
274  if (!this->validate_parameters()) {
275  return false;
276  }
277  if (!this->validate_initialization()) {
278  return false;
279  }
280  return true;
281 }
282 
283 bool MFProcedure::validate_all_instruction_pointers_set() const
284 {
285  for (const MFCallInstruction *instruction : call_instructions_) {
286  if (instruction->next_ == nullptr) {
287  return false;
288  }
289  }
290  for (const MFDestructInstruction *instruction : destruct_instructions_) {
291  if (instruction->next_ == nullptr) {
292  return false;
293  }
294  }
295  for (const MFBranchInstruction *instruction : branch_instructions_) {
296  if (instruction->branch_true_ == nullptr) {
297  return false;
298  }
299  if (instruction->branch_false_ == nullptr) {
300  return false;
301  }
302  }
303  for (const MFDummyInstruction *instruction : dummy_instructions_) {
304  if (instruction->next_ == nullptr) {
305  return false;
306  }
307  }
308  return true;
309 }
310 
311 bool MFProcedure::validate_all_params_provided() const
312 {
313  for (const MFCallInstruction *instruction : call_instructions_) {
314  const MultiFunction &fn = instruction->fn();
315  for (const int param_index : fn.param_indices()) {
316  const MFParamType param_type = fn.param_type(param_index);
317  if (param_type.category() == MFParamCategory::SingleOutput) {
318  /* Single outputs are optional. */
319  continue;
320  }
321  const MFVariable *variable = instruction->params_[param_index];
322  if (variable == nullptr) {
323  return false;
324  }
325  }
326  }
327  for (const MFBranchInstruction *instruction : branch_instructions_) {
328  if (instruction->condition_ == nullptr) {
329  return false;
330  }
331  }
332  for (const MFDestructInstruction *instruction : destruct_instructions_) {
333  if (instruction->variable_ == nullptr) {
334  return false;
335  }
336  }
337  return true;
338 }
339 
340 bool MFProcedure::validate_same_variables_in_one_call() const
341 {
342  for (const MFCallInstruction *instruction : call_instructions_) {
343  const MultiFunction &fn = *instruction->fn_;
344  for (const int param_index : fn.param_indices()) {
345  const MFParamType param_type = fn.param_type(param_index);
346  const MFVariable *variable = instruction->params_[param_index];
347  if (variable == nullptr) {
348  continue;
349  }
350  for (const int other_param_index : fn.param_indices()) {
351  if (other_param_index == param_index) {
352  continue;
353  }
354  const MFVariable *other_variable = instruction->params_[other_param_index];
355  if (other_variable != variable) {
356  continue;
357  }
358  if (ELEM(param_type.interface_type(), MFParamType::Mutable, MFParamType::Output)) {
359  /* When a variable is used as mutable or output parameter, it can only be used once. */
360  return false;
361  }
362  const MFParamType other_param_type = fn.param_type(other_param_index);
363  /* A variable is allowed to be used as input more than once. */
364  if (other_param_type.interface_type() != MFParamType::Input) {
365  return false;
366  }
367  }
368  }
369  }
370  return true;
371 }
372 
373 bool MFProcedure::validate_parameters() const
374 {
375  Set<const MFVariable *> variables;
376  for (const MFParameter &param : params_) {
377  /* One variable cannot be used as multiple parameters. */
378  if (!variables.add(param.variable)) {
379  return false;
380  }
381  }
382  return true;
383 }
384 
385 bool MFProcedure::validate_initialization() const
386 {
387  /* TODO: Issue warning when it maybe wrongly initialized. */
388  for (const MFDestructInstruction *instruction : destruct_instructions_) {
389  const MFVariable &variable = *instruction->variable_;
390  const InitState state = this->find_initialization_state_before_instruction(*instruction,
391  variable);
392  if (!state.can_be_initialized) {
393  return false;
394  }
395  }
396  for (const MFBranchInstruction *instruction : branch_instructions_) {
397  const MFVariable &variable = *instruction->condition_;
398  const InitState state = this->find_initialization_state_before_instruction(*instruction,
399  variable);
400  if (!state.can_be_initialized) {
401  return false;
402  }
403  }
404  for (const MFCallInstruction *instruction : call_instructions_) {
405  const MultiFunction &fn = *instruction->fn_;
406  for (const int param_index : fn.param_indices()) {
407  const MFParamType param_type = fn.param_type(param_index);
408  /* If the parameter was an unneeded output, it could be null. */
409  if (!instruction->params_[param_index]) {
410  continue;
411  }
412  const MFVariable &variable = *instruction->params_[param_index];
413  const InitState state = this->find_initialization_state_before_instruction(*instruction,
414  variable);
415  switch (param_type.interface_type()) {
416  case MFParamType::Input:
417  case MFParamType::Mutable: {
418  if (!state.can_be_initialized) {
419  return false;
420  }
421  break;
422  }
423  case MFParamType::Output: {
424  if (!state.can_be_uninitialized) {
425  return false;
426  }
427  break;
428  }
429  }
430  }
431  }
432  Set<const MFVariable *> variables_that_should_be_initialized_on_return;
433  for (const MFParameter &param : params_) {
434  if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) {
435  variables_that_should_be_initialized_on_return.add_new(param.variable);
436  }
437  }
438  for (const MFReturnInstruction *instruction : return_instructions_) {
439  for (const MFVariable *variable : variables_) {
440  const InitState init_state = this->find_initialization_state_before_instruction(*instruction,
441  *variable);
442  if (variables_that_should_be_initialized_on_return.contains(variable)) {
443  if (!init_state.can_be_initialized) {
444  return false;
445  }
446  }
447  else {
448  if (!init_state.can_be_uninitialized) {
449  return false;
450  }
451  }
452  }
453  }
454  return true;
455 }
456 
457 MFProcedure::InitState MFProcedure::find_initialization_state_before_instruction(
458  const MFInstruction &target_instruction, const MFVariable &target_variable) const
459 {
460  InitState state;
461 
462  auto check_entry_instruction = [&]() {
463  bool caller_initialized_variable = false;
464  for (const MFParameter &param : params_) {
465  if (param.variable == &target_variable) {
466  if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) {
467  caller_initialized_variable = true;
468  break;
469  }
470  }
471  }
472  if (caller_initialized_variable) {
473  state.can_be_initialized = true;
474  }
475  else {
476  state.can_be_uninitialized = true;
477  }
478  };
479 
480  if (&target_instruction == entry_) {
481  check_entry_instruction();
482  }
483 
484  Set<const MFInstruction *> checked_instructions;
485  Stack<const MFInstruction *> instructions_to_check;
486  for (const MFInstructionCursor &cursor : target_instruction.prev_) {
487  if (cursor.instruction() != nullptr) {
488  instructions_to_check.push(cursor.instruction());
489  }
490  }
491 
492  while (!instructions_to_check.is_empty()) {
493  const MFInstruction &instruction = *instructions_to_check.pop();
494  if (!checked_instructions.add(&instruction)) {
495  /* Skip if the instruction has been checked already. */
496  continue;
497  }
498  bool state_modified = false;
499  switch (instruction.type_) {
501  const MFCallInstruction &call_instruction = static_cast<const MFCallInstruction &>(
502  instruction);
503  const MultiFunction &fn = *call_instruction.fn_;
504  for (const int param_index : fn.param_indices()) {
505  if (call_instruction.params_[param_index] == &target_variable) {
506  const MFParamType param_type = fn.param_type(param_index);
507  if (param_type.interface_type() == MFParamType::Output) {
508  state.can_be_initialized = true;
509  state_modified = true;
510  break;
511  }
512  }
513  }
514  break;
515  }
517  const MFDestructInstruction &destruct_instruction =
518  static_cast<const MFDestructInstruction &>(instruction);
519  if (destruct_instruction.variable_ == &target_variable) {
520  state.can_be_uninitialized = true;
521  state_modified = true;
522  }
523  break;
524  }
528  /* These instruction types don't change the initialization state of variables. */
529  break;
530  }
531  }
532 
533  if (!state_modified) {
534  if (&instruction == entry_) {
535  check_entry_instruction();
536  }
537  for (const MFInstructionCursor &cursor : instruction.prev_) {
538  if (cursor.instruction() != nullptr) {
539  instructions_to_check.push(cursor.instruction());
540  }
541  }
542  }
543  }
544 
545  return state;
546 }
547 
549  private:
550  const MFProcedure &procedure_;
551  dot::DirectedGraph digraph_;
552  Map<const MFInstruction *, dot::Node *> dot_nodes_by_begin_;
553  Map<const MFInstruction *, dot::Node *> dot_nodes_by_end_;
554 
555  public:
556  MFProcedureDotExport(const MFProcedure &procedure) : procedure_(procedure)
557  {
558  }
559 
560  std::string generate()
561  {
562  this->create_nodes();
563  this->create_edges();
564  return digraph_.to_dot_string();
565  }
566 
568  {
569  Vector<const MFInstruction *> all_instructions;
570  auto add_instructions = [&](auto instructions) {
571  all_instructions.extend(instructions.begin(), instructions.end());
572  };
573  add_instructions(procedure_.call_instructions_);
574  add_instructions(procedure_.branch_instructions_);
575  add_instructions(procedure_.destruct_instructions_);
576  add_instructions(procedure_.dummy_instructions_);
577  add_instructions(procedure_.return_instructions_);
578 
579  Set<const MFInstruction *> handled_instructions;
580 
581  for (const MFInstruction *representative : all_instructions) {
582  if (handled_instructions.contains(representative)) {
583  continue;
584  }
585  Vector<const MFInstruction *> block_instructions = this->get_instructions_in_block(
586  *representative);
587  std::stringstream ss;
588  ss << "<";
589 
590  for (const MFInstruction *current : block_instructions) {
591  handled_instructions.add_new(current);
592  switch (current->type()) {
594  this->instruction_to_string(*static_cast<const MFCallInstruction *>(current), ss);
595  break;
596  }
598  this->instruction_to_string(*static_cast<const MFDestructInstruction *>(current), ss);
599  break;
600  }
602  this->instruction_to_string(*static_cast<const MFDummyInstruction *>(current), ss);
603  break;
604  }
606  this->instruction_to_string(*static_cast<const MFReturnInstruction *>(current), ss);
607  break;
608  }
610  this->instruction_to_string(*static_cast<const MFBranchInstruction *>(current), ss);
611  break;
612  }
613  }
614  ss << R"(<br align="left" />)";
615  }
616  ss << ">";
617 
618  dot::Node &dot_node = digraph_.new_node(ss.str());
619  dot_node.set_shape(dot::Attr_shape::Rectangle);
620  dot_nodes_by_begin_.add_new(block_instructions.first(), &dot_node);
621  dot_nodes_by_end_.add_new(block_instructions.last(), &dot_node);
622  }
623  }
624 
626  {
627  auto create_edge = [&](dot::Node &from_node,
628  const MFInstruction *to_instruction) -> dot::DirectedEdge & {
629  if (to_instruction == nullptr) {
630  dot::Node &to_node = digraph_.new_node("missing");
631  to_node.set_shape(dot::Attr_shape::Diamond);
632  return digraph_.new_edge(from_node, to_node);
633  }
634  dot::Node &to_node = *dot_nodes_by_begin_.lookup(to_instruction);
635  return digraph_.new_edge(from_node, to_node);
636  };
637 
638  for (auto item : dot_nodes_by_end_.items()) {
639  const MFInstruction &from_instruction = *item.key;
640  dot::Node &from_node = *item.value;
641  switch (from_instruction.type()) {
643  const MFInstruction *to_instruction =
644  static_cast<const MFCallInstruction &>(from_instruction).next();
645  create_edge(from_node, to_instruction);
646  break;
647  }
649  const MFInstruction *to_instruction =
650  static_cast<const MFDestructInstruction &>(from_instruction).next();
651  create_edge(from_node, to_instruction);
652  break;
653  }
655  const MFInstruction *to_instruction =
656  static_cast<const MFDummyInstruction &>(from_instruction).next();
657  create_edge(from_node, to_instruction);
658  break;
659  }
661  break;
662  }
664  const MFBranchInstruction &branch_instruction = static_cast<const MFBranchInstruction &>(
665  from_instruction);
666  const MFInstruction *to_true_instruction = branch_instruction.branch_true();
667  const MFInstruction *to_false_instruction = branch_instruction.branch_false();
668  create_edge(from_node, to_true_instruction).attributes.set("color", "#118811");
669  create_edge(from_node, to_false_instruction).attributes.set("color", "#881111");
670  break;
671  }
672  }
673  }
674 
675  dot::Node &entry_node = this->create_entry_node();
676  create_edge(entry_node, procedure_.entry());
677  }
678 
679  bool has_to_be_block_begin(const MFInstruction &instruction)
680  {
681  if (instruction.prev().size() != 1) {
682  return true;
683  }
684  if (ELEM(instruction.prev()[0].type(),
685  MFInstructionCursor::Type::Branch,
687  return true;
688  }
689  return false;
690  }
691 
693  {
694  const MFInstruction *current = &representative;
695  while (!this->has_to_be_block_begin(*current)) {
696  current = current->prev()[0].instruction();
697  if (current == &representative) {
698  /* There is a loop without entry or exit, just break it up here. */
699  break;
700  }
701  }
702  return *current;
703  }
704 
706  const MFInstruction &block_begin)
707  {
708  const MFInstruction *next = nullptr;
709  switch (instruction.type()) {
711  next = static_cast<const MFCallInstruction &>(instruction).next();
712  break;
713  }
715  next = static_cast<const MFDestructInstruction &>(instruction).next();
716  break;
717  }
719  next = static_cast<const MFDummyInstruction &>(instruction).next();
720  break;
721  }
724  break;
725  }
726  }
727  if (next == nullptr) {
728  return nullptr;
729  }
730  if (next == &block_begin) {
731  return nullptr;
732  }
733  if (this->has_to_be_block_begin(*next)) {
734  return nullptr;
735  }
736  return next;
737  }
738 
740  {
741  Vector<const MFInstruction *> instructions;
742  const MFInstruction &begin = this->get_first_instruction_in_block(representative);
743  for (const MFInstruction *current = &begin; current != nullptr;
744  current = this->get_next_instruction_in_block(*current, begin)) {
745  instructions.append(current);
746  }
747  return instructions;
748  }
749 
750  void variable_to_string(const MFVariable *variable, std::stringstream &ss)
751  {
752  if (variable == nullptr) {
753  ss << "null";
754  }
755  else {
756  ss << "$" << variable->index_in_procedure();
757  if (!variable->name().is_empty()) {
758  ss << "(" << variable->name() << ")";
759  }
760  }
761  }
762 
763  void instruction_name_format(StringRef name, std::stringstream &ss)
764  {
765  ss << name;
766  }
767 
768  void instruction_to_string(const MFCallInstruction &instruction, std::stringstream &ss)
769  {
770  const MultiFunction &fn = instruction.fn();
771  this->instruction_name_format(fn.debug_name() + ": ", ss);
772  for (const int param_index : fn.param_indices()) {
773  const MFParamType param_type = fn.param_type(param_index);
774  const MFVariable *variable = instruction.params()[param_index];
775  ss << R"(<font color="grey30">)";
776  switch (param_type.interface_type()) {
777  case MFParamType::Input: {
778  ss << "in";
779  break;
780  }
781  case MFParamType::Mutable: {
782  ss << "mut";
783  break;
784  }
785  case MFParamType::Output: {
786  ss << "out";
787  break;
788  }
789  }
790  ss << " </font> ";
791  variable_to_string(variable, ss);
792  if (param_index < fn.param_amount() - 1) {
793  ss << ", ";
794  }
795  }
796  }
797 
798  void instruction_to_string(const MFDestructInstruction &instruction, std::stringstream &ss)
799  {
800  instruction_name_format("Destruct ", ss);
801  variable_to_string(instruction.variable(), ss);
802  }
803 
804  void instruction_to_string(const MFDummyInstruction &UNUSED(instruction), std::stringstream &ss)
805  {
806  instruction_name_format("Dummy ", ss);
807  }
808 
809  void instruction_to_string(const MFReturnInstruction &UNUSED(instruction), std::stringstream &ss)
810  {
811  instruction_name_format("Return ", ss);
812 
813  Vector<ConstMFParameter> outgoing_parameters;
814  for (const ConstMFParameter &param : procedure_.params()) {
815  if (ELEM(param.type, MFParamType::Mutable, MFParamType::Output)) {
816  outgoing_parameters.append(param);
817  }
818  }
819  for (const int param_index : outgoing_parameters.index_range()) {
820  const ConstMFParameter &param = outgoing_parameters[param_index];
821  variable_to_string(param.variable, ss);
822  if (param_index < outgoing_parameters.size() - 1) {
823  ss << ", ";
824  }
825  }
826  }
827 
828  void instruction_to_string(const MFBranchInstruction &instruction, std::stringstream &ss)
829  {
830  instruction_name_format("Branch ", ss);
831  variable_to_string(instruction.condition(), ss);
832  }
833 
835  {
836  std::stringstream ss;
837  ss << "Entry: ";
838  Vector<ConstMFParameter> incoming_parameters;
839  for (const ConstMFParameter &param : procedure_.params()) {
840  if (ELEM(param.type, MFParamType::Input, MFParamType::Mutable)) {
841  incoming_parameters.append(param);
842  }
843  }
844  for (const int param_index : incoming_parameters.index_range()) {
845  const ConstMFParameter &param = incoming_parameters[param_index];
846  variable_to_string(param.variable, ss);
847  if (param_index < incoming_parameters.size() - 1) {
848  ss << ", ";
849  }
850  }
851 
852  dot::Node &node = digraph_.new_node(ss.str());
853  node.set_shape(dot::Attr_shape::Ellipse);
854  return node;
855  }
856 };
857 
858 std::string MFProcedure::to_dot() const
859 {
860  MFProcedureDotExport dot_export{*this};
861  return dot_export.generate();
862 }
863 
864 } // namespace blender::fn
#define BLI_assert(a)
Definition: BLI_assert.h:46
struct Entry Entry
#define UNUSED(x)
#define ELEM(...)
MutableSpan< T > allocate_array(int64_t size)
destruct_ptr< T > construct(Args &&...args)
const Value & lookup(const Key &key) const
Definition: BLI_map.hh:485
void add_new(const Key &key, const Value &value)
Definition: BLI_map.hh:220
ItemIterator items() const
Definition: BLI_map.hh:859
bool contains(const Key &key) const
Definition: BLI_set.hh:296
void add_new(const Key &key)
Definition: BLI_set.hh:238
constexpr int64_t size() const
Definition: BLI_span.hh:240
constexpr IndexRange index_range() const
Definition: BLI_span.hh:401
constexpr bool is_empty() const
int64_t size() const
Definition: BLI_vector.hh:694
void append(const T &value)
Definition: BLI_vector.hh:433
const T & last(const int64_t n=0) const
Definition: BLI_vector.hh:663
IndexRange index_range() const
Definition: BLI_vector.hh:920
const T & first() const
Definition: BLI_vector.hh:680
void extend(Span< T > array)
Definition: BLI_vector.hh:530
DirectedEdge & new_edge(NodePort from, NodePort to)
Definition: dot_export.cc:37
std::string to_dot_string() const
Definition: dot_export.cc:121
Node & new_node(StringRef label)
Definition: dot_export.cc:12
void set_shape(Attr_shape shape)
void set_branch_true(MFInstruction *instruction)
void set_condition(MFVariable *variable)
void set_branch_false(MFInstruction *instruction)
void set_next(MFInstruction *instruction)
void set_param_variable(int param_index, MFVariable *variable)
void set_params(Span< MFVariable * > variables)
void set_next(MFInstruction *instruction)
void set_next(MFInstruction *instruction)
MFInstruction * next(MFProcedure &procedure) const
void set_next(MFProcedure &procedure, MFInstruction *new_instruction) const
Vector< MFInstructionCursor > prev_
Span< MFInstructionCursor > prev() const
void instruction_to_string(const MFDummyInstruction &UNUSED(instruction), std::stringstream &ss)
void instruction_to_string(const MFReturnInstruction &UNUSED(instruction), std::stringstream &ss)
const MFInstruction * get_next_instruction_in_block(const MFInstruction &instruction, const MFInstruction &block_begin)
Vector< const MFInstruction * > get_instructions_in_block(const MFInstruction &representative)
void instruction_to_string(const MFBranchInstruction &instruction, std::stringstream &ss)
void variable_to_string(const MFVariable *variable, std::stringstream &ss)
bool has_to_be_block_begin(const MFInstruction &instruction)
MFProcedureDotExport(const MFProcedure &procedure)
const MFInstruction & get_first_instruction_in_block(const MFInstruction &representative)
void instruction_to_string(const MFCallInstruction &instruction, std::stringstream &ss)
void instruction_to_string(const MFDestructInstruction &instruction, std::stringstream &ss)
void instruction_name_format(StringRef name, std::stringstream &ss)
MFBranchInstruction & new_branch_instruction()
void add_parameter(MFParamType::InterfaceType interface_type, MFVariable &variable)
MFDummyInstruction & new_dummy_instruction()
void set_entry(MFInstruction &entry)
MFReturnInstruction & new_return_instruction()
MFCallInstruction & new_call_instruction(const MultiFunction &fn)
MFDestructInstruction & new_destruct_instruction()
MFVariable & new_variable(MFDataType data_type, std::string name="")
Span< ConstMFParameter > params() const
void set_name(std::string name)
MFParamType param_type(int param_index) const
virtual std::string debug_name() const
IndexRange param_indices() const
OperationNode * node
const int state
static ulong * next