Skip to main content

luaur_code_gen/functions/
to_dot_dj_graph.rs

1extern crate alloc;
2
3use crate::enums::ir_block_kind::IrBlockKind;
4use crate::functions::append::append;
5use crate::functions::dom_children::dom_children;
6use crate::functions::successors::successors;
7use crate::functions::to_string_ir_dump_alt_c::to_string_ir_to_string_context_ir_block_u32 as to_string_block;
8use crate::records::ir_function::IrFunction;
9use crate::records::ir_to_string_context::ir_to_string_context;
10use alloc::string::String;
11use core::ffi::c_void;
12
13pub fn to_dot_dj_graph(function: &IrFunction) -> String {
14    let mut result = String::new();
15
16    {
17        let mut ctx = ir_to_string_context {
18            result: &mut result,
19            blocks: &function.blocks,
20            constants: &function.constants,
21            cfg: &function.cfg,
22            vm_exit_info: &function.vm_exit_info,
23            proto: function.proto as *mut c_void,
24        };
25
26        ctx.result.push_str("digraph CFG {\n");
27
28        for i in 0..function.blocks.len() {
29            let block = function.blocks[i];
30
31            append(&mut ctx.result, format_args!("b{} [", i as u32));
32
33            if block.kind == IrBlockKind::Fallback {
34                append(
35                    &mut ctx.result,
36                    format_args!("style=filled;fillcolor=salmon;"),
37                );
38            } else if block.kind == IrBlockKind::Bytecode {
39                append(
40                    &mut ctx.result,
41                    format_args!("style=filled;fillcolor=palegreen;"),
42                );
43            }
44
45            ctx.result.push_str("label=\"");
46            to_string_block(&mut ctx, &block, i as u32);
47            ctx.result.push_str("\"];\n");
48        }
49
50        let cfg = ctx.cfg;
51
52        // Layer by depth in tree
53        let mut depth: u32 = 0;
54        let mut found = true;
55
56        while found {
57            found = false;
58
59            ctx.result.push_str("{rank = same;");
60            for i in 0..cfg.dom_ordering.len() {
61                if cfg.dom_ordering[i].depth == depth {
62                    append(&mut ctx.result, format_args!("b{};", i as u32));
63                    found = true;
64                }
65            }
66            ctx.result.push_str("}\n");
67
68            depth += 1;
69        }
70
71        for i in 0..cfg.dom_children_offsets.len() {
72            let dom = dom_children(cfg, i as u32);
73
74            for target in dom {
75                append(
76                    &mut ctx.result,
77                    format_args!("b{} -> b{};\n", i as u32, target),
78                );
79            }
80
81            // Join edges are all successor edges that do not strongly dominate
82            let succ = successors(cfg, i as u32);
83
84            for successor in succ {
85                let mut found_edge = false;
86
87                for target in dom {
88                    if target == successor {
89                        found_edge = true;
90                        break;
91                    }
92                }
93
94                if !found_edge {
95                    append(
96                        &mut ctx.result,
97                        format_args!("b{} -> b{} [style=dotted];\n", i as u32, successor),
98                    );
99                }
100            }
101        }
102
103        ctx.result.push_str("}\n");
104    }
105
106    result
107}