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