1b1994897Sopenharmony_ci#!/usr/bin/env python3
2b1994897Sopenharmony_ci# -- coding: utf-8 --
3b1994897Sopenharmony_ci# Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
4b1994897Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
5b1994897Sopenharmony_ci# you may not use this file except in compliance with the License.
6b1994897Sopenharmony_ci# You may obtain a copy of the License at
7b1994897Sopenharmony_ci#
8b1994897Sopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
9b1994897Sopenharmony_ci#
10b1994897Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
11b1994897Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
12b1994897Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b1994897Sopenharmony_ci# See the License for the specific language governing permissions and
14b1994897Sopenharmony_ci# limitations under the License.
15b1994897Sopenharmony_ci#
16b1994897Sopenharmony_ci# Copyright (c) 2024 Huawei Device Co., Ltd.
17b1994897Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
18b1994897Sopenharmony_ci# you may not use this file except in compliance with the License.
19b1994897Sopenharmony_ci# You may obtain a copy of the License at
20b1994897Sopenharmony_ci#
21b1994897Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0
22b1994897Sopenharmony_ci#
23b1994897Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software
24b1994897Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
25b1994897Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26b1994897Sopenharmony_ci# See the License for the specific language governing permissions and
27b1994897Sopenharmony_ci# limitations under the License.
28b1994897Sopenharmony_ci
29b1994897Sopenharmony_cifrom typing import List
30b1994897Sopenharmony_ciimport graphviz as gv
31b1994897Sopenharmony_ciimport sys
32b1994897Sopenharmony_ciimport os
33b1994897Sopenharmony_ciimport argparse
34b1994897Sopenharmony_ci
35b1994897Sopenharmony_ci
36b1994897Sopenharmony_ciclass BasicBlock:
37b1994897Sopenharmony_ci    def __init__(self, ident: int) -> None:
38b1994897Sopenharmony_ci        self.id = ident
39b1994897Sopenharmony_ci        self.insts = []
40b1994897Sopenharmony_ci        self.preds = []
41b1994897Sopenharmony_ci        self.succs = []
42b1994897Sopenharmony_ci        self.props = []
43b1994897Sopenharmony_ci
44b1994897Sopenharmony_ci    def text(self, no_insts=False) -> str:
45b1994897Sopenharmony_ci        s = 'BB ' + str(self.id) + '\n'
46b1994897Sopenharmony_ci        s += 'props: '
47b1994897Sopenharmony_ci        for i, prop in enumerate(self.props):
48b1994897Sopenharmony_ci            if i > 0:
49b1994897Sopenharmony_ci                s += ', '
50b1994897Sopenharmony_ci            s += prop
51b1994897Sopenharmony_ci        s += '\n'
52b1994897Sopenharmony_ci
53b1994897Sopenharmony_ci        if not no_insts:
54b1994897Sopenharmony_ci            for inst in self.insts:
55b1994897Sopenharmony_ci                s += inst + '\n'
56b1994897Sopenharmony_ci        return s
57b1994897Sopenharmony_ci
58b1994897Sopenharmony_ci
59b1994897Sopenharmony_ciclass Function:
60b1994897Sopenharmony_ci    def __init__(self, method: str, blocks: List[BasicBlock]) -> None:
61b1994897Sopenharmony_ci        self.method = method
62b1994897Sopenharmony_ci        self.blocks = blocks
63b1994897Sopenharmony_ci
64b1994897Sopenharmony_ci
65b1994897Sopenharmony_ciclass GraphDumpParser:
66b1994897Sopenharmony_ci    def __init__(self) -> None:
67b1994897Sopenharmony_ci        self.method = None
68b1994897Sopenharmony_ci        self.block = None
69b1994897Sopenharmony_ci        self.blocks = []
70b1994897Sopenharmony_ci        self.functions = []
71b1994897Sopenharmony_ci
72b1994897Sopenharmony_ci    def finish_block(self):
73b1994897Sopenharmony_ci        if self.block:
74b1994897Sopenharmony_ci            self.blocks.append(self.block)
75b1994897Sopenharmony_ci            self.block = None
76b1994897Sopenharmony_ci
77b1994897Sopenharmony_ci    def finish_function(self):
78b1994897Sopenharmony_ci        self.finish_block()
79b1994897Sopenharmony_ci        if self.method:
80b1994897Sopenharmony_ci            self.functions.append(Function(self.method, self.blocks))
81b1994897Sopenharmony_ci            self.blocks = []
82b1994897Sopenharmony_ci
83b1994897Sopenharmony_ci    def begin_block(self, line: str):
84b1994897Sopenharmony_ci        self.finish_block()
85b1994897Sopenharmony_ci
86b1994897Sopenharmony_ci        preds_start = line.find('preds:')
87b1994897Sopenharmony_ci        block_id = int(line[len('BB'):preds_start].strip())
88b1994897Sopenharmony_ci        self.block = BasicBlock(block_id)
89b1994897Sopenharmony_ci        if preds_start != -1:
90b1994897Sopenharmony_ci            self.parse_block_preds(line, preds_start)
91b1994897Sopenharmony_ci
92b1994897Sopenharmony_ci    def begin_function(self, line: str):
93b1994897Sopenharmony_ci        self.finish_function()
94b1994897Sopenharmony_ci        self.method = line[len('method:'):].strip()
95b1994897Sopenharmony_ci
96b1994897Sopenharmony_ci    def parse_block_preds(self, line: str, preds_start: int):
97b1994897Sopenharmony_ci        preds = line[preds_start+len('preds:'):].strip().strip('[]')
98b1994897Sopenharmony_ci        for pred in preds.split(','):
99b1994897Sopenharmony_ci            pred = pred.strip()
100b1994897Sopenharmony_ci            self.block.preds.append(int(pred[len('bb'):].strip()))
101b1994897Sopenharmony_ci
102b1994897Sopenharmony_ci    def parse_block_props(self, line: str):
103b1994897Sopenharmony_ci        for s in line[len('prop:'):].strip().split(','):
104b1994897Sopenharmony_ci            s = s.strip()
105b1994897Sopenharmony_ci            if not s.startswith('bc'):
106b1994897Sopenharmony_ci                self.block.props.append(s)
107b1994897Sopenharmony_ci
108b1994897Sopenharmony_ci    def parse_block_succs(self, line: str):
109b1994897Sopenharmony_ci        succs = line[len('succs:'):].strip().strip('[]')
110b1994897Sopenharmony_ci        for succ in succs.split(','):
111b1994897Sopenharmony_ci            succ = succ.strip()
112b1994897Sopenharmony_ci            self.block.succs.append(int(succ[len('bb'):].strip()))
113b1994897Sopenharmony_ci        self.finish_block()
114b1994897Sopenharmony_ci
115b1994897Sopenharmony_ci    def parse(self, lines: List[str]) -> List[Function]:
116b1994897Sopenharmony_ci        for line in lines:
117b1994897Sopenharmony_ci            if line.startswith('Method:'):
118b1994897Sopenharmony_ci                self.begin_function(line)
119b1994897Sopenharmony_ci            elif line.startswith('BB'):
120b1994897Sopenharmony_ci                self.begin_block(line)
121b1994897Sopenharmony_ci            elif self.block:
122b1994897Sopenharmony_ci                if line.startswith('prop:'):
123b1994897Sopenharmony_ci                    self.parse_block_props(line)
124b1994897Sopenharmony_ci                elif line.startswith('succs:'):
125b1994897Sopenharmony_ci                    self.parse_block_succs(line)
126b1994897Sopenharmony_ci                else:
127b1994897Sopenharmony_ci                    self.block.insts.append(line)
128b1994897Sopenharmony_ci        self.finish_function()
129b1994897Sopenharmony_ci        return self.functions
130b1994897Sopenharmony_ci
131b1994897Sopenharmony_ci
132b1994897Sopenharmony_cidef draw_function(function: Function, out_dir=None, no_insts=False):
133b1994897Sopenharmony_ci    dot = gv.Digraph(format='png')
134b1994897Sopenharmony_ci    for block in function.blocks:
135b1994897Sopenharmony_ci        dot.node(str(block.id), block.text(no_insts), shape='box')
136b1994897Sopenharmony_ci    for block in function.blocks:
137b1994897Sopenharmony_ci        for succ in block.succs:
138b1994897Sopenharmony_ci            dot.edge(str(block.id), str(succ))
139b1994897Sopenharmony_ci    basename = 'cfg_' + function.method
140b1994897Sopenharmony_ci    dotfile_path = os.path.join(out_dir, basename)
141b1994897Sopenharmony_ci    dot.render(basename, out_dir, format="png")
142b1994897Sopenharmony_ci    os.rename(dotfile_path, dotfile_path + '.dot')
143b1994897Sopenharmony_ci
144b1994897Sopenharmony_ci
145b1994897Sopenharmony_cidef main():
146b1994897Sopenharmony_ci    parser = argparse.ArgumentParser(description="A tool for drawing CFGs by reading ir dump from stdin")
147b1994897Sopenharmony_ci    parser.add_argument("--no-insts", action="store_true", help="drawing without ir instructions")
148b1994897Sopenharmony_ci    parser.add_argument("--out", type=str, default="./out", help="output directory, default to './out'")
149b1994897Sopenharmony_ci    args = parser.parse_args()
150b1994897Sopenharmony_ci
151b1994897Sopenharmony_ci    functions = GraphDumpParser().parse(sys.stdin.readlines())
152b1994897Sopenharmony_ci    for function in functions:
153b1994897Sopenharmony_ci        draw_function(function, args.out, no_insts=args.no_insts)
154b1994897Sopenharmony_ci
155b1994897Sopenharmony_ci
156b1994897Sopenharmony_ciif __name__ == "__main__":
157b1994897Sopenharmony_ci    main()
158