cfg_to_tree.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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. def lower(self, state):
  37. """Lowers this 'simple' block to a tree."""
  38. return tree_ir.create_block(
  39. state.lower_block(self.body),
  40. self.next_block.lower(state))
  41. class LoopBlock(object):
  42. """A 'loop' block in the relooper algorithm."""
  43. def __init__(self, inner, next_block):
  44. self.inner = inner
  45. self.next_block = next_block
  46. def lower(self, state):
  47. """Lowers this 'loop' block to a tree."""
  48. old_loop = state.current_loop
  49. state.current_loop = self
  50. inner = tree_ir.LoopInstruction(
  51. tree_ir.create_block(
  52. self.inner.lower(state),
  53. tree_ir.BreakInstruction()))
  54. state.current_loop = old_loop
  55. return tree_ir.create_block(
  56. inner,
  57. self.next_block.lower(state))
  58. class MultipleBlock(object):
  59. """A 'multiple' block in the relooper algorithm that does _not_ require a loop."""
  60. def __init__(self, handled_blocks, next_block):
  61. self.handled_blocks = handled_blocks
  62. self.next_block = next_block
  63. def lower_handled_blocks(self, state):
  64. """Lowers the handled blocks of this 'multiple' block to a tree."""
  65. result = tree_ir.EmptyInstruction()
  66. for entry, block in self.handled_blocks:
  67. result = tree_ir.SelectInstruction(
  68. tree_ir.BinaryInstruction(
  69. state.label_variable,
  70. '==',
  71. tree_ir.LiteralInstruction(state.get_label_value(entry))),
  72. block.lower(state),
  73. result)
  74. return result
  75. def lower(self, state):
  76. """Lowers this 'multiple' block to a tree."""
  77. return tree_ir.create_block(
  78. self.lower_handled_blocks(state),
  79. self.next_block.lower(state))
  80. class MultipleLoopBlock(MultipleBlock):
  81. """A 'multiple' block in the relooper algorithm."""
  82. def __init__(self, handled_blocks, next_block):
  83. MultipleBlock.__init__(self, handled_blocks, next_block)
  84. def lower(self, state):
  85. """Lowers this 'multiple' block to a tree."""
  86. old_loop = state.current_loop
  87. state.current_loop = self
  88. inner = tree_ir.LoopInstruction(
  89. tree_ir.create_block(
  90. self.lower_handled_blocks(state),
  91. tree_ir.BreakInstruction()))
  92. state.current_loop = old_loop
  93. return tree_ir.create_block(
  94. inner,
  95. self.next_block.lower(state))
  96. class ContinueFlow(cfg_ir.FlowInstruction):
  97. """Represents a control flow instruction which continues to the next loop iteration."""
  98. def __init__(self, loop):
  99. cfg_ir.FlowInstruction.__init__(self)
  100. self.loop = loop
  101. def get_dependencies(self):
  102. """Gets all definitions and instructions on which this instruction depends."""
  103. return []
  104. def branches(self):
  105. """Gets a list of basic blocks targeted by this flow instruction."""
  106. return []
  107. def __str__(self):
  108. return 'continue'
  109. class BreakFlow(cfg_ir.FlowInstruction):
  110. """Represents a control flow instruction which breaks out of a loop."""
  111. def __init__(self, loop):
  112. cfg_ir.FlowInstruction.__init__(self)
  113. self.loop = loop
  114. def get_dependencies(self):
  115. """Gets all definitions and instructions on which this instruction depends."""
  116. return []
  117. def branches(self):
  118. """Gets a list of basic blocks targeted by this flow instruction."""
  119. return []
  120. def __str__(self):
  121. return 'break'
  122. def solipsize(graph_component, loop, entry_blocks, next_blocks):
  123. """Replaces branches to entry blocks and next blocks by jumps to 'continue' and 'break'
  124. blocks, respectively."""
  125. all_blocks = set()
  126. reachable_from_inner = set()
  127. for block in graph_component.blocks:
  128. if block in next_blocks:
  129. continue
  130. all_blocks.add(block)
  131. for branch in block.flow.branches():
  132. if branch.block in entry_blocks:
  133. # Create a 'continue' block, and point to that.
  134. continue_block = cfg_ir.BasicBlock(block.counter)
  135. for param in branch.block.parameters:
  136. continue_block.append_parameter(param)
  137. continue_block.flow = ContinueFlow(loop)
  138. branch.block = continue_block
  139. all_blocks.add(continue_block)
  140. elif branch.block in next_blocks:
  141. # Create a 'break' block, and point to that.
  142. break_block = cfg_ir.BasicBlock(block.counter)
  143. for param in branch.block.parameters:
  144. break_block.append_parameter(param)
  145. break_block.flow = BreakFlow(loop)
  146. branch.block = break_block
  147. all_blocks.add(break_block)
  148. reachable_from_inner.add(branch.block)
  149. return reachable_from_inner, FlowGraphComponent(
  150. graph_component.entry_blocks,
  151. all_blocks,
  152. {
  153. block : cfg_optimization.get_reachable_blocks(block)
  154. for block in all_blocks
  155. })
  156. def to_relooper_loop(graph_component):
  157. """Converts the given graph component to a relooper 'loop'."""
  158. entry_blocks = graph_component.entry_blocks
  159. result = LoopBlock(None, None)
  160. inner_blocks = []
  161. next_blocks = []
  162. for block in graph_component.blocks:
  163. if any([graph_component.can_reach(block, ep) for ep in entry_blocks]):
  164. inner_blocks.append(block)
  165. else:
  166. next_blocks.append(block)
  167. reachable_from_inner, inner_component = solipsize(
  168. graph_component, result, entry_blocks, next_blocks)
  169. result.inner = reloop(inner_component)
  170. next_component = FlowGraphComponent(
  171. reachable_from_inner, next_blocks, graph_component.reachable)
  172. result.next_block = reloop(next_component)
  173. return result
  174. def to_relooper_multiple_or_loop(graph_component):
  175. """Converts the given graph component to a relooper 'multiple' or 'loop'."""
  176. # From the Emscripten paper:
  177. #
  178. # If we have more than one entry, try to create a Multiple block:
  179. # For each entry, find all the labels it reaches that
  180. # cannot be reached by any other entry. If at least one entry
  181. # has such labels, return a Multiple block, whose Handled
  182. # blocks are blocks for those labels (and whose entries are
  183. # those labels), and whose Next block is all the rest. Entries
  184. # for the next block are entries that did not become part of
  185. # the Handled blocks, and also labels that can be reached
  186. # from the Handled blocks.
  187. entry_blocks = graph_component.entry_blocks
  188. if len(entry_blocks) <= 1:
  189. return to_relooper_loop(graph_component)
  190. entry_reachables = {ep : graph_component.get_reachable_blocks(ep) for ep in entry_blocks}
  191. exclusive_entries = {}
  192. for entry in entry_blocks:
  193. exclusive_blocks = set(entry_reachables[entry])
  194. for other_entry in entry_blocks:
  195. if other_entry != entry:
  196. exclusive_blocks.difference_update(entry_reachables[other_entry])
  197. if len(exclusive_blocks) > 0:
  198. exclusive_entries[entry] = exclusive_blocks
  199. if len(exclusive_entries) == 0:
  200. return to_relooper_loop(graph_component)
  201. next_entries = set(graph_component.entry_blocks)
  202. for block_set in exclusive_entries.values():
  203. for elem in block_set:
  204. directly_reachable = graph_component.get_directly_reachable_blocks(elem)
  205. directly_reachable.remove(elem)
  206. next_entries.update(directly_reachable)
  207. next_entries.difference_update(exclusive_entries.keys())
  208. result = MultipleLoopBlock({}, None)
  209. for entry, exclusive_blocks in exclusive_entries.items():
  210. other_blocks = set(graph_component.blocks)
  211. other_blocks.difference_update(exclusive_blocks)
  212. result.handled_blocks[entry] = reloop(
  213. solipsize(graph_component, result, set([entry]), other_blocks))
  214. result.next_block = reloop(graph_component.sub_component(next_entries))
  215. return result
  216. def reloop(graph_component):
  217. """Applies the relooper algorithm to the given graph component."""
  218. reachable_set = graph_component.get_entry_reachable_blocks()
  219. entry_blocks = graph_component.entry_blocks
  220. if len(entry_blocks) == 1 and entry_blocks[0] not in reachable_set:
  221. graph_component.blocks.remove(entry_blocks[0])
  222. return SimpleBlock(
  223. entry_blocks[0],
  224. reloop(
  225. FlowGraphComponent(
  226. graph_component.get_directly_reachable_blocks(entry_blocks[0]),
  227. graph_component.blocks,
  228. graph_component.reachable)))
  229. elif all([block in reachable_set for block in entry_blocks]):
  230. return to_relooper_loop(graph_component)
  231. else:
  232. return to_relooper_multiple_or_loop(graph_component)
  233. class LoweringState(object):
  234. """Stores information related to the relooper->tree conversion."""
  235. def __init__(self, jit):
  236. self.jit = jit
  237. self.label_variable = tree_ir.VariableName('__label')
  238. self.label_map = {}
  239. self.definition_loads = {}
  240. def get_label_value(self, block):
  241. """Gets the label value for the given block."""
  242. if block in self.label_map:
  243. return self.label_map[block]
  244. else:
  245. result = len(self.label_map)
  246. self.label_map[block] = result
  247. return result
  248. def load_definition(self, definition):
  249. """Loads the given definition's variable."""
  250. if definition in self.definition_loads:
  251. return self.definition_loads[definition]
  252. if definition.instruction.has_value():
  253. raise NotImplementedError()
  254. else:
  255. raise NotImplementedError()
  256. def lower_block(self, block):
  257. """Lowers the given (relooped) block to a tree."""
  258. statements = []
  259. for definition in block.definitions:
  260. statements.append(self.lower_definition(definition))
  261. statements.append(self.lower_flow(block.flow))
  262. return tree_ir.create_block(statements)
  263. def lower_definition(self, definition):
  264. """Lowers the given definition to a tree."""
  265. instruction = definition.value
  266. tree_instruction = self.lower_instruction(instruction)
  267. if instruction.has_value():
  268. return self.load_definition(definition).create_store(tree_instruction)
  269. else:
  270. return tree_instruction
  271. def lower_instruction(self, instruction):
  272. """Lowers the given instruction to a tree."""
  273. raise NotImplementedError()
  274. def lower_flow(self, flow):
  275. """Lowers the given (relooped) flow instruction to a tree."""
  276. raise NotImplementedError()