]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/graphviz/mod.rs
Auto merge of #30145 - petrochenkov:hyg, r=nrc
[rust.git] / src / librustc_mir / graphviz / mod.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use dot;
12 use rustc::mir::repr::*;
13 use std::borrow::IntoCow;
14
15 #[derive(Copy, Clone, PartialEq, Eq)]
16 pub struct EdgeIndex {
17     source: BasicBlock,
18     target: BasicBlock,
19     index: usize,
20 }
21
22 impl<'a,'tcx> dot::Labeller<'a, BasicBlock, EdgeIndex> for Mir<'tcx> {
23     fn graph_id(&'a self) -> dot::Id<'a> {
24         dot::Id::new("Mir").unwrap()
25     }
26
27     fn node_id(&'a self, n: &BasicBlock) -> dot::Id<'a> {
28         dot::Id::new(format!("BB{}", n.index())).unwrap()
29     }
30
31     fn node_shape(&'a self, _: &BasicBlock) -> Option<dot::LabelText<'a>> {
32         Some(dot::LabelText::label("none"))
33     }
34
35     fn node_label(&'a self, &n: &BasicBlock) -> dot::LabelText<'a> {
36         let mut buffer = String::new();
37         buffer.push_str("<TABLE ALIGN=\"LEFT\">");
38
39         buffer.push_str("<TR><TD>");
40         buffer.push_str(&format!("{:?}", n));
41         buffer.push_str("</TD></TR>");
42
43         let data = self.basic_block_data(n);
44         for statement in &data.statements {
45             buffer.push_str("<TR><TD>");
46             buffer.push_str(&escape(format!("{:?}", statement)));
47             buffer.push_str("</TD></TR>");
48         }
49
50         buffer.push_str("<TR><TD>");
51         buffer.push_str(&escape(format!("{:?}", &data.terminator)));
52         buffer.push_str("</TD></TR>");
53
54         buffer.push_str("</TABLE>");
55
56         dot::LabelText::html(buffer)
57     }
58
59     fn edge_label(&'a self, edge: &EdgeIndex) -> dot::LabelText<'a> {
60         dot::LabelText::label(format!("{}", edge.index))
61     }
62 }
63
64 impl<'a,'tcx> dot::GraphWalk<'a, BasicBlock, EdgeIndex> for Mir<'tcx> {
65     fn nodes(&'a self) -> dot::Nodes<'a, BasicBlock> {
66         self.all_basic_blocks().into_cow()
67     }
68
69     fn edges(&'a self) -> dot::Edges<'a, EdgeIndex> {
70         self.all_basic_blocks()
71             .into_iter()
72             .flat_map(|source| {
73                 self.basic_block_data(source)
74                     .terminator
75                     .successors()
76                     .iter()
77                     .enumerate()
78                     .map(move |(index, &target)| {
79                         EdgeIndex {
80                             source: source,
81                             target: target,
82                             index: index,
83                         }
84                     })
85             })
86             .collect::<Vec<_>>()
87             .into_cow()
88     }
89
90     fn source(&'a self, edge: &EdgeIndex) -> BasicBlock {
91         edge.source
92     }
93
94     fn target(&'a self, edge: &EdgeIndex) -> BasicBlock {
95         edge.target
96     }
97 }
98
99 fn escape(text: String) -> String {
100     let text = dot::escape_html(&text);
101     let text = all_to_subscript("Temp", text);
102     let text = all_to_subscript("Var", text);
103     let text = all_to_subscript("Arg", text);
104     let text = all_to_subscript("BB", text);
105     text
106 }
107
108 /// A call like `all_to_subscript("Temp", "Temp(123)")` will convert
109 /// to `Temp₁₂₃`.
110 fn all_to_subscript(header: &str, mut text: String) -> String {
111     let mut offset = 0;
112     while offset < text.len() {
113         if let Some(text1) = to_subscript1(header, &text, &mut offset) {
114             text = text1;
115         }
116     }
117     return text;
118
119     /// Looks for `Foo(\d*)` where `header=="Foo"` and replaces the `\d` with subscripts.
120     /// Updates `offset` to point to the next location where we might want to search.
121     /// Returns an updated string if changes were made, else None.
122     fn to_subscript1(header: &str, text: &str, offset: &mut usize) -> Option<String> {
123         let a = match text[*offset..].find(header) {
124             None => {
125                 *offset = text.len();
126                 return None;
127             }
128             Some(a) => a + *offset,
129         };
130
131         // Example:
132         //
133         // header: "Foo"
134         // text:   ....Foo(123)...
135         //             ^  ^
136         //             a  b
137
138         let b = a + header.len();
139         *offset = b;
140
141         let mut chars = text[b..].chars();
142         if Some('(') != chars.next() {
143             return None;
144         }
145
146         let mut result = String::new();
147         result.push_str(&text[..b]);
148
149         while let Some(c) = chars.next() {
150             if c == ')' {
151                 break;
152             }
153             if !c.is_digit(10) {
154                 return None;
155             }
156
157             // 0x208 is _0 in unicode, 0x209 is _1, etc
158             const SUBSCRIPTS: &'static str = "₀₁₂₃₄₅₆₇₈₉";
159             let n = (c as usize) - ('0' as usize);
160             result.extend(SUBSCRIPTS.chars().skip(n).take(1));
161         }
162
163         result.extend(chars);
164         return Some(result);
165     }
166 }