control_flow.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. use std::collections::{HashMap, HashSet};
  2. use std::iter;
  3. use jit::{BasicBlock, BasicBlockType};
  4. use profiler;
  5. const ENTRY_NODE_ID: u32 = 0xffff_ffff;
  6. type Graph = HashMap<u32, HashSet<u32>>;
  7. /// Reverse the direction of all edges in the graph
  8. fn rev_graph_edges(nodes: &Graph) -> Graph {
  9. let mut rev_nodes = Graph::new();
  10. for (from, tos) in nodes {
  11. for to in tos {
  12. rev_nodes
  13. .entry(*to)
  14. .or_insert_with(|| HashSet::new())
  15. .insert(*from);
  16. }
  17. }
  18. rev_nodes
  19. }
  20. pub fn make_graph(basic_blocks: &Vec<BasicBlock>) -> Graph {
  21. let mut nodes = Graph::new();
  22. let mut entry_edges = HashSet::new();
  23. for b in basic_blocks.iter() {
  24. let mut edges = HashSet::new();
  25. match &b.ty {
  26. BasicBlockType::ConditionalJump {
  27. next_block_addr,
  28. next_block_branch_taken_addr,
  29. ..
  30. } => {
  31. if let Some(next_block_addr) = next_block_addr {
  32. edges.insert(*next_block_addr);
  33. }
  34. if let Some(next_block_branch_taken_addr) = next_block_branch_taken_addr {
  35. edges.insert(*next_block_branch_taken_addr);
  36. }
  37. },
  38. BasicBlockType::Normal { next_block_addr } => {
  39. edges.insert(*next_block_addr);
  40. },
  41. BasicBlockType::Exit => {},
  42. BasicBlockType::AbsoluteEip => {
  43. // Not necessary: We generate a loop around the outer brtable unconditionally
  44. //edges.insert(ENTRY_NODE_ID);
  45. },
  46. }
  47. nodes.insert(b.addr, edges);
  48. if b.is_entry_block {
  49. entry_edges.insert(b.addr);
  50. }
  51. }
  52. // Entry node that represents the initial basic block of the generated function (must be
  53. // able to reach all entry nodes)
  54. nodes.insert(ENTRY_NODE_ID, entry_edges);
  55. return nodes;
  56. }
  57. pub enum WasmStructure {
  58. BasicBlock(u32),
  59. Dispatcher(Vec<u32>),
  60. Loop(Vec<WasmStructure>),
  61. Block(Vec<WasmStructure>),
  62. }
  63. impl WasmStructure {
  64. pub fn print(&self, depth: usize) {
  65. match self {
  66. Self::BasicBlock(addr) => dbg_log!("{} 0x{:x}", " ".repeat(depth), addr),
  67. Self::Dispatcher(entries) => {
  68. dbg_log!("{} Dispatcher entries:", " ".repeat(depth));
  69. for e in entries {
  70. dbg_log!("{} {:x}", " ".repeat(depth), e);
  71. }
  72. },
  73. Self::Loop(elements) => {
  74. dbg_log!("{} loop_void({})", " ".repeat(depth), elements.len());
  75. for e in elements {
  76. e.print(depth + 1)
  77. }
  78. dbg_log!("{} loop_end({})", " ".repeat(depth), elements.len());
  79. },
  80. Self::Block(elements) => {
  81. dbg_log!("{} block_void({})", " ".repeat(depth), elements.len());
  82. for e in elements {
  83. e.print(depth + 1)
  84. }
  85. dbg_log!("{} block_end({})", " ".repeat(depth), elements.len());
  86. },
  87. }
  88. }
  89. fn branches(&self, edges: &Graph) -> HashSet<u32> {
  90. fn handle(block: &WasmStructure, edges: &Graph, result: &mut HashSet<u32>) {
  91. match block {
  92. WasmStructure::BasicBlock(addr) => result.extend(edges.get(&addr).unwrap()),
  93. WasmStructure::Dispatcher(entries) => result.extend(entries),
  94. WasmStructure::Loop(children) | WasmStructure::Block(children) => {
  95. for c in children.iter() {
  96. handle(c, edges, result);
  97. }
  98. },
  99. }
  100. };
  101. let mut result = HashSet::new();
  102. handle(self, edges, &mut result);
  103. result
  104. }
  105. pub fn head(&self) -> Box<dyn iter::Iterator<Item = u32> + '_> {
  106. match self {
  107. Self::BasicBlock(addr) => Box::new(iter::once(*addr)),
  108. Self::Dispatcher(entries) => Box::new(entries.iter().copied()),
  109. Self::Loop(children) => children.first().unwrap().head(),
  110. Self::Block(elements) => elements.first().unwrap().head(),
  111. }
  112. }
  113. }
  114. /// Check:
  115. /// - Dispatcher appears at the beginning of a loop
  116. /// - No two nested blocks at the end
  117. /// - No two nested loops at the beginning
  118. /// - No empty blocks or loops
  119. /// - The entry node block is not present
  120. pub fn assert_invariants(blocks: &Vec<WasmStructure>) {
  121. fn check(node: &WasmStructure, in_tail_block: bool, in_head_loop: bool, is_first: bool) {
  122. match node {
  123. WasmStructure::Block(children) => {
  124. dbg_assert!(!in_tail_block);
  125. dbg_assert!(!children.is_empty());
  126. for (i, c) in children.iter().enumerate() {
  127. let is_first = i == 0;
  128. let is_last = i == children.len() - 1;
  129. check(c, is_last, in_head_loop && is_first, is_first);
  130. }
  131. },
  132. WasmStructure::Loop(children) => {
  133. dbg_assert!(!in_head_loop);
  134. dbg_assert!(!children.is_empty());
  135. for (i, c) in children.iter().enumerate() {
  136. let is_first = i == 0;
  137. let is_last = i == children.len() - 1;
  138. check(c, in_tail_block && is_last, is_first, is_first);
  139. }
  140. },
  141. &WasmStructure::BasicBlock(addr) => dbg_assert!(addr != ENTRY_NODE_ID),
  142. WasmStructure::Dispatcher(_) => {
  143. dbg_assert!(is_first);
  144. //dbg_assert!(in_head_loop); // fails for module dispatcher
  145. },
  146. }
  147. }
  148. for (i, b) in blocks.iter().enumerate() {
  149. check(b, false, false, i == 0);
  150. }
  151. }
  152. /// Strongly connected components via Kosaraju's algorithm
  153. fn scc(edges: &Graph, rev_edges: &Graph) -> Vec<Vec<u32>> {
  154. fn visit(
  155. node: u32,
  156. edges: &Graph,
  157. rev_edges: &Graph,
  158. visited: &mut HashSet<u32>,
  159. l: &mut Vec<u32>,
  160. ) {
  161. if visited.contains(&node) {
  162. return;
  163. }
  164. visited.insert(node);
  165. for &next in edges.get(&node).unwrap() {
  166. visit(next, edges, rev_edges, visited, l);
  167. }
  168. l.push(node);
  169. }
  170. let mut l = Vec::new();
  171. let mut visited = HashSet::new();
  172. for &node in edges.keys() {
  173. visit(node, edges, rev_edges, &mut visited, &mut l);
  174. }
  175. fn assign(
  176. node: u32,
  177. edges: &Graph,
  178. rev_edges: &Graph,
  179. assigned: &mut HashSet<u32>,
  180. group: &mut Vec<u32>,
  181. ) {
  182. if assigned.contains(&node) {
  183. return;
  184. }
  185. assigned.insert(node);
  186. group.push(node);
  187. if let Some(nexts) = rev_edges.get(&node) {
  188. for &next in nexts {
  189. assign(next, edges, rev_edges, assigned, group);
  190. }
  191. }
  192. }
  193. let mut assigned = HashSet::new();
  194. let mut assignment = Vec::new();
  195. for &node in l.iter().rev() {
  196. let mut group = Vec::new();
  197. assign(node, edges, rev_edges, &mut assigned, &mut group);
  198. if !group.is_empty() {
  199. assignment.push(group);
  200. }
  201. }
  202. assignment
  203. }
  204. pub fn loopify(nodes: &Graph) -> Vec<WasmStructure> {
  205. let rev_nodes = rev_graph_edges(nodes);
  206. let groups = scc(nodes, &rev_nodes);
  207. return groups
  208. .iter()
  209. .flat_map(|group| {
  210. dbg_assert!(!group.is_empty());
  211. if group.len() == 1 {
  212. let addr = group[0];
  213. if addr == ENTRY_NODE_ID {
  214. let entries = nodes.get(&ENTRY_NODE_ID).unwrap().iter().copied().collect();
  215. return vec![WasmStructure::Dispatcher(entries)].into_iter();
  216. }
  217. let block = WasmStructure::BasicBlock(addr);
  218. // self-loops
  219. if nodes.get(&group[0]).unwrap().contains(&group[0]) {
  220. return vec![WasmStructure::Loop(vec![block])].into_iter();
  221. }
  222. else {
  223. return vec![block].into_iter();
  224. }
  225. }
  226. let entries_to_group: Vec<u32> = group
  227. .iter()
  228. .filter(|addr| {
  229. // reachable from outside of the group
  230. rev_nodes.get(addr).map_or(false, |x| {
  231. x.iter().any(|incoming| !group.contains(incoming))
  232. })
  233. })
  234. .copied()
  235. .collect();
  236. if entries_to_group.len() != 1 {
  237. dbg_log!(
  238. "Compiling multi-entry loop with {} entries and {} basic blocks",
  239. entries_to_group.len(),
  240. group.len()
  241. );
  242. }
  243. let max_extra_basic_blocks = 100;
  244. if entries_to_group.len() * group.len() > max_extra_basic_blocks {
  245. let mut subgroup_edges: Graph = Graph::new();
  246. for elem in group {
  247. subgroup_edges.insert(
  248. *elem,
  249. nodes
  250. .get(&elem)
  251. .unwrap()
  252. .iter()
  253. .filter(|dest| {
  254. // XXX: This might remove forward edges to other loop entries
  255. // Probably not an issue since it can go through the
  256. // dispatcher
  257. group.contains(dest) && !entries_to_group.contains(dest)
  258. })
  259. .copied()
  260. .collect(),
  261. );
  262. }
  263. let mut loop_nodes = loopify(&subgroup_edges);
  264. if entries_to_group.len() > 1 {
  265. loop_nodes.insert(0, WasmStructure::Dispatcher(entries_to_group));
  266. }
  267. return vec![WasmStructure::Loop(loop_nodes)].into_iter();
  268. }
  269. else {
  270. profiler::stat_increment_by(
  271. profiler::stat::COMPILE_DUPLICATED_BASIC_BLOCK,
  272. ((entries_to_group.len() - 1) * group.len()) as u64,
  273. );
  274. let nodes: Vec<WasmStructure> = entries_to_group
  275. .iter()
  276. .map(|&entry| {
  277. let mut subgroup_edges: Graph = Graph::new();
  278. for &elem in group {
  279. subgroup_edges.insert(
  280. elem,
  281. nodes
  282. .get(&elem)
  283. .unwrap()
  284. .iter()
  285. .copied()
  286. .filter(|dest| group.contains(dest) && *dest != entry)
  287. .collect(),
  288. );
  289. }
  290. let loop_nodes = loopify(&subgroup_edges);
  291. WasmStructure::Loop(loop_nodes)
  292. })
  293. .collect();
  294. nodes.into_iter()
  295. }
  296. })
  297. .collect();
  298. }
  299. pub fn blockify(blocks: &mut Vec<WasmStructure>, edges: &Graph) {
  300. let mut cached_branches: Vec<HashSet<u32>> = Vec::new();
  301. for i in 0..blocks.len() {
  302. cached_branches.push(blocks[i].branches(edges));
  303. }
  304. let mut i = 0;
  305. while i < blocks.len() {
  306. match &mut blocks[i] {
  307. WasmStructure::BasicBlock(_) | WasmStructure::Dispatcher(_) => {},
  308. WasmStructure::Loop (
  309. blocks
  310. )
  311. // TODO: Might be faster to do this *after* inserting blocks in this block
  312. | WasmStructure::Block(blocks) => blockify(blocks, edges),
  313. }
  314. let source = {
  315. let mut source = None;
  316. for j in 0..i {
  317. if blocks[i].head().any(|bb| cached_branches[j].contains(&bb)) {
  318. source = Some(j);
  319. break;
  320. }
  321. }
  322. match source {
  323. Some(s) => s,
  324. None => {
  325. i += 1;
  326. continue;
  327. },
  328. }
  329. };
  330. // This is optional: Avoid putting a single basic block into a block
  331. if source == i - 1 {
  332. match &blocks[source] {
  333. &WasmStructure::BasicBlock(_) => {
  334. i += 1;
  335. continue;
  336. },
  337. _ => {},
  338. }
  339. }
  340. let replacement = WasmStructure::Block(Vec::new());
  341. let children: Vec<WasmStructure> =
  342. blocks.splice(source..i, iter::once(replacement)).collect();
  343. match &mut blocks[source] {
  344. WasmStructure::Block(c) => c.extend(children),
  345. _ => dbg_assert!(false),
  346. }
  347. match &blocks[source + 1] {
  348. WasmStructure::BasicBlock(_) =>
  349. //dbg_assert!(*b == bbs.next().unwrap())
  350. {}
  351. WasmStructure::Dispatcher(_) => {},
  352. WasmStructure::Loop(_blocks) | WasmStructure::Block(_blocks) => {}, //dbg_assert!(blocks[0].head() == bb),
  353. }
  354. {
  355. let replacement = HashSet::new();
  356. let children: Vec<HashSet<u32>> = cached_branches
  357. .splice(source..i, iter::once(replacement))
  358. .collect();
  359. dbg_assert!(cached_branches[source].len() == 0);
  360. let mut iter = children.into_iter();
  361. cached_branches[source] = iter.next().unwrap();
  362. for c in iter {
  363. cached_branches[source].extend(c);
  364. }
  365. }
  366. // skip the inserted block and this block
  367. i = source + 2;
  368. }
  369. }