Skip to main content

luaur_analysis/functions/
dump_cfg_json.rs

1extern crate alloc;
2
3use crate::functions::dump_instruction::dump_instruction;
4use crate::functions::index_of_block::index_of_block;
5use crate::functions::json_escape::json_escape;
6use crate::records::control_flow_graph::ControlFlowGraph;
7use alloc::string::String;
8use alloc::vec;
9use alloc::vec::Vec;
10use core::fmt::Write;
11
12pub fn dump_cfg_json(cfg: &ControlFlowGraph) -> String {
13    // iongraph requires every "loopheader" block to have exactly one predecessor
14    // marked "backedge" (asserted in Graph.ts). A loop header is any block whose
15    // predecessor has a higher index than itself (the back-edge comes from within
16    // the loop body). Pre-compute both flags plus per-block loopDepth in a single
17    // pass; loopDepth is the number of loops enclosing the block, and iongraph
18    // uses it to resolve each block to its innermost loop header.
19    let mut is_loop_header: Vec<bool> = vec![false; cfg.blocks.len()];
20    let mut is_backedge: Vec<bool> = vec![false; cfg.blocks.len()];
21    let mut backedges: Vec<(usize, usize)> = Vec::new(); // (loop header index, back-edge source index)
22    for i in 0..cfg.blocks.len() {
23        unsafe {
24            for pred in (*cfg.blocks[i]).get_predecessors() {
25                let pred_idx = index_of_block(cfg, *pred);
26                if pred_idx > i {
27                    is_loop_header[i] = true;
28                    is_backedge[pred_idx] = true;
29                    backedges.push((i, pred_idx));
30                }
31            }
32        }
33    }
34    let mut loop_depth: Vec<i32> = vec![0; cfg.blocks.len()];
35    for i in 0..cfg.blocks.len() {
36        for be in &backedges {
37            if be.0 <= i && i <= be.1 {
38                loop_depth[i] += 1;
39            }
40        }
41    }
42
43    let mut out = String::from(
44        "{\"functions\":[{\"name\":\"cfg\",\"passes\":[{\"name\":\"CFG\",\"mir\":{\"blocks\":[",
45    );
46
47    let mut next_instr_id: i32 = 1;
48    for i in 0..cfg.blocks.len() {
49        let block = cfg.blocks[i];
50        unsafe {
51            let block = &*block;
52            if i > 0 {
53                out.push(',');
54            }
55
56            let _ = write!(out, "{{\"number\":{}", i);
57            let _ = write!(out, ",\"loopDepth\":{}", loop_depth[i]);
58
59            out.push_str(",\"attributes\":[");
60            let mut first_attr = true;
61            if is_loop_header[i] {
62                out.push_str("\"loopheader\"");
63                first_attr = false;
64            }
65            if is_backedge[i] {
66                if !first_attr {
67                    out.push(',');
68                }
69                out.push_str("\"backedge\"");
70            }
71            out.push(']');
72
73            out.push_str(",\"predecessors\":[");
74            let preds = block.get_predecessors();
75            for j in 0..preds.len() {
76                if j > 0 {
77                    out.push(',');
78                }
79                let _ = write!(out, "{}", index_of_block(cfg, preds[j]));
80            }
81            out.push(']');
82
83            out.push_str(",\"successors\":[");
84            let succs = block.get_successors();
85            for j in 0..succs.len() {
86                if j > 0 {
87                    out.push(',');
88                }
89                let _ = write!(out, "{}", index_of_block(cfg, succs[j]));
90            }
91            out.push(']');
92
93            out.push_str(",\"instructions\":[");
94            let instructions = block.get_instructions();
95            for j in 0..instructions.len() {
96                if j > 0 {
97                    out.push(',');
98                }
99                let _ = write!(out, "{{\"id\":{}", next_instr_id);
100                next_instr_id += 1;
101                let _ = write!(
102                    out,
103                    ",\"opcode\":\"{}\"",
104                    json_escape(&dump_instruction(instructions[j], &cfg.use_defs))
105                );
106                out.push_str(
107                    ",\"attributes\":[],\"inputs\":[],\"uses\":[],\"memInputs\":[],\"type\":\"\"}",
108                );
109            }
110            out.push_str("]}");
111        }
112    }
113
114    // Close blocks array and mir object, then add an empty lir (iongraph touches p.lir.blocks unconditionally).
115    out.push_str("]},\"lir\":{\"blocks\":[]}");
116    // Close: pass object, passes array, function object, functions array, root object.
117    out.push_str("}]}]}");
118    out
119}