1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use std::str::FromStr;

use come::ir::{
    self,
    analyzer::{self, control_flow::structural::FoldedCFG, ControlFlowGraph, IsAnalyzer},
    optimize::{optimize as optimize_ir, pass::Pass},
    IR,
};
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn parse(code: &str) -> JsValue {
    let (_, parsed_ir) = ir::parse(code).unwrap();
    let result = parsed_ir.as_function_definition();
    serde_wasm_bindgen::to_value(&result).unwrap()
}

#[wasm_bindgen]
pub fn optimize(code: &str, pass: &str) -> String {
    let ir_code = ir::parse(code).unwrap().1;
    let pass = Pass::from_str(pass).unwrap();
    let result = optimize_ir(vec![ir_code], vec![pass])
        .into_iter()
        .next()
        .unwrap();
    format!("{result}")
}

#[wasm_bindgen]
#[derive(Clone, Debug)]
pub struct Edge {
    pub from: u32,
    pub to: u32,
    pub back: bool,
}

#[wasm_bindgen(getter_with_clone)]
#[derive(Default, Debug)]
pub struct CFGraph {
    pub nodes: Vec<String>,
    pub edges: Vec<Edge>,
}

#[wasm_bindgen]
pub fn dump_control_flow_graph(code: &str) -> CFGraph {
    let ir_code = ir::parse(code).unwrap().1;
    if let IR::FunctionDefinition(f) = ir_code {
        let cfg = analyzer::ControlFlowGraph::new();
        let cfg = cfg.bind(&f);
        let backedges = cfg.back_edges();
        let mut result = CFGraph::default();
        let g = cfg.graph();
        for n in g.node_indices() {
            if n.index() == g.node_count() - 1 {
                result.nodes.push("_dummy_end".to_string());
            } else {
                result
                    .nodes
                    .push(cfg.basic_block_name_by_index(n.index()).to_string());
            }
        }
        for e in g.edge_indices() {
            let (from, to) = g.edge_endpoints(e).unwrap();
            let is_backedge = backedges.contains(&(from.index() as _, to.index() as _));
            result.edges.push(Edge {
                from: from.index() as _,
                to: to.index() as _,
                back: is_backedge,
            });
        }
        result
    } else {
        panic!("faq")
    }
}

#[wasm_bindgen]
pub fn structural_analysis(code: &str) -> JsValue {
    let ir_code = ir::parse(code).unwrap().1;
    let f = ir_code.as_function_definition();
    let cfg = ControlFlowGraph::new();
    let cfg = cfg.bind(f);
    let folded = FoldedCFG::from_control_flow_graph(&cfg);
    let result = folded.structural_analysis(&cfg);
    serde_wasm_bindgen::to_value(&result).unwrap()
}

#[test]
fn test_optimize() {
    dbg!(optimize(
        r"fn main() -> () {
      %0 = add i32 1, 2
      ret
    }",
        "FixIrreducible"
    ));
}

#[test]
fn test_dump_cfg() {
    dbg!(dump_control_flow_graph(
        r"fn test_condition(i32 %a, i32 %b) -> i32 {
          test_condition_entry:
            %a_0_addr = alloca i32
            store i32 %a, address %a_0_addr
            %b_0_addr = alloca i32
            store i32 %b, address %b_0_addr
            %result_0_addr = alloca i32
            store i32 0, address %result_0_addr
            %i_0_addr = alloca i32
            %0 = load i32 %a_0_addr
            store i32 %0, address %i_0_addr
            j loop_0_condition
          loop_0_condition:
            %2 = load i32 %i_0_addr
            %3 = load i32 %b_0_addr
            %1 = slt i32 %2, %3
            bne %1, 0, loop_0_success, loop_0_fail
          loop_0_success:
            %5 = load i32 %result_0_addr
            %6 = load i32 %i_0_addr
            %4 = add i32 %5, %6
            store i32 %4, address %result_0_addr
            %8 = load i32 %i_0_addr
            %7 = add i32 %8, 1
            store i32 %7, address %i_0_addr
            j loop_0_condition
          loop_0_fail:
            %9 = load i32 %result_0_addr
            ret %9
        }"
    ));
}

#[test]
fn test_structural_analysis() {
    let code = r"fn test_condition(i32 %a, i32 %b) -> i32 {
          test_condition_entry:
            %a_0_addr = alloca i32
            store i32 %a, address %a_0_addr
            %b_0_addr = alloca i32
            store i32 %b, address %b_0_addr
            %1 = load i32 %a_0_addr
            %2 = load i32 %b_0_addr
            %0 = slt i32 %1, %2
            bne %0, 0, if_0_success, if_0_fail
          if_0_success:
            %3 = load i32 %a_0_addr
            ret %3
          if_0_fail:
            %4 = load i32 %b_0_addr
            ret %4
        }";
    let ir_code = ir::parse(code).unwrap().1;
    let f = ir_code.as_function_definition();
    let cfg = ControlFlowGraph::new();
    let cfg = cfg.bind(f);
    let folded = FoldedCFG::from_control_flow_graph(&cfg);
    let result = folded.structural_analysis(&cfg);
    dbg!(result);
}