cfg_to_tree.py 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. """Lowers CFG-IR to tree-IR."""
  2. import modelverse_jit.cfg_ir as cfg_ir
  3. import modelverse_jit.cfg_optimization as cfg_optimization
  4. import modelverse_jit.tree_ir as tree_ir
  5. # The CFG deconstruction code here is based on the relooper algorithm
  6. # as detailed in https://github.com/kripken/Relooper/blob/master/paper.pdf
  7. class FlowGraphComponent(object):
  8. """Represents a control-flow graph component."""
  9. def __init__(self, entry_blocks, blocks, reachable):
  10. self.entry_blocks = entry_blocks
  11. self.blocks = blocks
  12. self.reachable = reachable
  13. def get_entry_reachable_blocks(self):
  14. """Gets the set of all blocks that are reachable from the entry points."""
  15. return set.union(*[self.get_reachable_blocks(block) for block in self.entry_blocks])
  16. def get_reachable_blocks(self, block):
  17. """Gets all blocks in this component that are reachable from the given block."""
  18. return set.intersection(self.reachable[block], self.blocks)
  19. def get_directly_reachable_blocks(self, block):
  20. """Gets all blocks in this component that are reachable from the given block by taking
  21. exactly one branch."""
  22. return set.intersection(cfg_optimization.get_directly_reachable_blocks(block), self.blocks)
  23. def can_reach(self, source_block, target_block):
  24. """Tests if the first block can reach the second."""
  25. return target_block in self.reachable[source_block] and target_block in self.blocks
  26. def sub_component(self, new_entry_points):
  27. """Creates a sub-component of this component, with the given new entry points."""
  28. result = FlowGraphComponent(new_entry_points, self.blocks, self.reachable)
  29. result.blocks = result.get_entry_reachable_blocks()
  30. return result
  31. class SimpleBlock(object):
  32. """A 'simple' block in the relooper algorithm."""
  33. def __init__(self, body, next_block):
  34. self.body = body
  35. self.next_block = next_block
  36. class LoopBlock(object):
  37. """A 'loop' block in the relooper algorithm."""
  38. def __init__(self, inner, next_block):
  39. self.inner = inner
  40. self.next_block = next_block
  41. class MultipleBlock(object):
  42. """A 'multiple' block in the relooper algorithm."""
  43. def __init__(self, handled_blocks, next_block):
  44. self.handled_blocks = handled_blocks
  45. self.next_block = next_block
  46. class ContinueFlow(cfg_ir.FlowInstruction):
  47. """Represents a control flow instruction which continues to the next loop iteration."""
  48. def __init__(self, loop):
  49. cfg_ir.FlowInstruction.__init__(self)
  50. self.loop = loop
  51. def get_dependencies(self):
  52. """Gets all definitions and instructions on which this instruction depends."""
  53. return []
  54. def branches(self):
  55. """Gets a list of basic blocks targeted by this flow instruction."""
  56. return []
  57. def __str__(self):
  58. return 'continue'
  59. class BreakFlow(cfg_ir.FlowInstruction):
  60. """Represents a control flow instruction which breaks out of a loop."""
  61. def __init__(self, loop):
  62. cfg_ir.FlowInstruction.__init__(self)
  63. self.loop = loop
  64. def get_dependencies(self):
  65. """Gets all definitions and instructions on which this instruction depends."""
  66. return []
  67. def branches(self):
  68. """Gets a list of basic blocks targeted by this flow instruction."""
  69. return []
  70. def __str__(self):
  71. return 'break'
  72. def solipsize(graph_component, loop, entry_blocks, next_blocks):
  73. """Replaces branches to entry blocks and next blocks by jumps to 'continue' and 'break'
  74. blocks, respectively."""
  75. all_blocks = set()
  76. reachable_from_inner = set()
  77. for block in graph_component.blocks:
  78. all_blocks.add(block)
  79. for branch in block.flow.branches():
  80. if branch.block in entry_blocks:
  81. # Create a 'continue' block, and point to that.
  82. continue_block = cfg_ir.BasicBlock(block.counter)
  83. for param in branch.block.parameters:
  84. continue_block.append_parameter(param)
  85. continue_block.flow = ContinueFlow(loop)
  86. branch.block = continue_block
  87. all_blocks.add(continue_block)
  88. elif branch.block in next_blocks:
  89. # Create a 'break' block, and point to that.
  90. break_block = cfg_ir.BasicBlock(block.counter)
  91. for param in branch.block.parameters:
  92. break_block.append_parameter(param)
  93. break_block.flow = BreakFlow(loop)
  94. branch.block = break_block
  95. all_blocks.add(break_block)
  96. reachable_from_inner.add(branch.block)
  97. return reachable_from_inner, FlowGraphComponent(
  98. graph_component.entry_blocks,
  99. all_blocks,
  100. {
  101. block : cfg_optimization.get_reachable_blocks(block)
  102. for block in all_blocks
  103. })
  104. def to_relooper_loop(graph_component):
  105. """Converts the given graph component to a relooper 'loop'."""
  106. entry_blocks = graph_component.entry_blocks
  107. result = LoopBlock(None, None)
  108. inner_blocks = []
  109. next_blocks = []
  110. for block in graph_component.blocks:
  111. if any([graph_component.can_reach(block, ep) for ep in entry_blocks]):
  112. inner_blocks.append(block)
  113. else:
  114. next_blocks.append(block)
  115. inner_component = FlowGraphComponent(
  116. entry_blocks, inner_blocks, graph_component.reachable)
  117. reachable_from_inner, inner_component = solipsize(
  118. inner_component, result, entry_blocks, next_blocks)
  119. result.inner = reloop(inner_component)
  120. next_component = FlowGraphComponent(
  121. reachable_from_inner, next_blocks, graph_component.reachable)
  122. result.next_block = reloop(next_component)
  123. return result
  124. def to_relooper_multiple_or_loop(graph_component):
  125. """Converts the given graph component to a relooper 'multiple' or 'loop'."""
  126. # From the Emscripten paper:
  127. #
  128. # If we have more than one entry, try to create a Multiple block:
  129. # For each entry, find all the labels it reaches that
  130. # cannot be reached by any other entry. If at least one entry
  131. # has such labels, return a Multiple block, whose Handled
  132. # blocks are blocks for those labels (and whose entries are
  133. # those labels), and whose Next block is all the rest. Entries
  134. # for the next block are entries that did not become part of
  135. # the Handled blocks, and also labels that can be reached
  136. # from the Handled blocks.
  137. entry_blocks = graph_component.entry_blocks
  138. if len(entry_blocks) <= 1:
  139. return to_relooper_loop(graph_component)
  140. entry_reachables = {ep : graph_component.get_reachable_blocks(ep) for ep in entry_blocks}
  141. exclusive_entries = {}
  142. for entry in entry_blocks:
  143. exclusive_blocks = set(entry_reachables[entry])
  144. for other_entry in entry_blocks:
  145. if other_entry != entry:
  146. exclusive_blocks.difference_update(entry_reachables[other_entry])
  147. if len(exclusive_blocks) > 0:
  148. exclusive_entries[entry] = exclusive_blocks
  149. if len(exclusive_entries) == 0:
  150. return to_relooper_loop(graph_component)
  151. next_entries = set(graph_component.entry_blocks)
  152. for block_set in exclusive_entries.values():
  153. for elem in block_set:
  154. directly_reachable = graph_component.get_directly_reachable_blocks(elem)
  155. directly_reachable.remove(elem)
  156. next_entries.update(directly_reachable)
  157. next_entries.difference_update(exclusive_entries.keys())
  158. result = MultipleBlock({}, None)
  159. for entry, exclusive_blocks in exclusive_entries.items():
  160. other_blocks = set(graph_component.blocks)
  161. other_blocks.difference_update(exclusive_blocks)
  162. result.handled_blocks[entry] = reloop(
  163. solipsize(graph_component, result, set([entry]), other_blocks))
  164. result.next_block = reloop(graph_component.sub_component(next_entries))
  165. return result
  166. def reloop(graph_component):
  167. """Applies the relooper algorithm to the given graph component."""
  168. reachable_set = graph_component.get_entry_reachable_blocks()
  169. entry_blocks = graph_component.entry_blocks
  170. if len(entry_blocks) == 1 and entry_blocks[0] not in reachable_set:
  171. graph_component.blocks.remove(entry_blocks[0])
  172. return SimpleBlock(
  173. entry_blocks[0],
  174. reloop(
  175. FlowGraphComponent(
  176. graph_component.get_directly_reachable_blocks(entry_blocks[0]),
  177. graph_component.blocks,
  178. graph_component.reachable)))
  179. elif all([block in reachable_set for block in entry_blocks]):
  180. return to_relooper_loop(graph_component)
  181. else:
  182. return to_relooper_multiple_or_loop(graph_component)