cfg_optimization.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. """Optimizes and analyzes CFG-IR."""
  2. from collections import defaultdict
  3. import modelverse_jit.cfg_ir as cfg_ir
  4. import modelverse_jit.cfg_dominators as cfg_dominators
  5. def get_directly_reachable_blocks(block):
  6. """Gets the set of all blocks that can be reached by taking a single branch from the
  7. given block."""
  8. return [branch.block for branch in block.flow.branches()]
  9. def get_reachable_blocks(entry_point):
  10. """Constructs the set of all reachable vertices from the given block."""
  11. # This is a simple O(n^2) algorithm. Maybe a faster algorithm is more appropriate here.
  12. def __add_block_children(block, results):
  13. for child in get_directly_reachable_blocks(block):
  14. if child not in results:
  15. results.add(child)
  16. __add_block_children(child, results)
  17. return results
  18. return __add_block_children(entry_point, set())
  19. def get_all_reachable_blocks(entry_point):
  20. """Constructs the set of all reachable vertices, for every block that is
  21. reachable from the given entry point."""
  22. # This is a simple O(n^3) algorithm. Maybe a faster algorithm is more appropriate here.
  23. results = {}
  24. all_blocks = get_reachable_blocks(entry_point)
  25. results[entry_point] = all_blocks
  26. for block in all_blocks:
  27. if block not in results:
  28. results[block] = get_reachable_blocks(block)
  29. return results
  30. def is_empty_block(block):
  31. """Tests if the given block contains no parameters or definitions."""
  32. return len(block.parameters) == 0 and len(block.definitions) == 0
  33. def optimize_flow(block):
  34. """Optimizes the given block's flow instruction."""
  35. changed = True
  36. while changed:
  37. changed = False
  38. # Select flow with a literal condition can be optimized to a direct jump.
  39. if (isinstance(block.flow, cfg_ir.SelectFlow)
  40. and cfg_ir.is_literal_def(block.flow.condition)):
  41. literal = cfg_ir.get_literal_def_value(block.flow.condition)
  42. block.flow = cfg_ir.JumpFlow(
  43. block.flow.if_branch if literal else block.flow.else_branch)
  44. changed = True
  45. # Jumps to blocks which contain no parameters or definitions can be replaced
  46. # by the target block's flow.
  47. if (isinstance(block.flow, cfg_ir.JumpFlow)
  48. and is_empty_block(block.flow.branch.block)
  49. and block.flow.branch.block is not block):
  50. block.flow = block.flow.branch.block.flow
  51. changed = True
  52. def get_all_blocks(entry_point):
  53. """Gets all basic blocks in the control-flow graph defined by the given entry point."""
  54. yield entry_point
  55. for block in get_reachable_blocks(entry_point):
  56. yield block
  57. def optimize_graph_flow(entry_point):
  58. """Optimizes all flow instructions in the graph defined by the given entry point."""
  59. for block in get_all_blocks(entry_point):
  60. optimize_flow(block)
  61. def elide_local_checks(entry_point):
  62. """Tries to elide redundant checks on local variables."""
  63. # The plan here is to replace all check-local-exists defs by literals if
  64. # they are either dominated by an appropriate declare-local or not reachable
  65. # from a declare-local.
  66. local_checks = defaultdict(set)
  67. local_defs = defaultdict(set)
  68. for block in get_all_blocks(entry_point):
  69. for definition in block.definitions:
  70. if cfg_ir.is_value_def(definition, cfg_ir.CheckLocalExists):
  71. local_checks[cfg_ir.get_def_variable(definition).node_id].add(definition)
  72. elif cfg_ir.is_value_def(definition, cfg_ir.DeclareLocal):
  73. local_defs[cfg_ir.get_def_variable(definition).node_id].add(definition)
  74. dominator_tree = cfg_dominators.get_dominator_tree(entry_point)
  75. reachable_blocks = get_all_reachable_blocks(entry_point)
  76. for (variable, all_checks) in local_checks.items():
  77. for check in all_checks:
  78. is_reachable = False
  79. for local_def in local_defs[variable]:
  80. if dominator_tree.dominates_instruction(local_def, check):
  81. # Check is dominated by a definition. Replace it by a 'True' literal.
  82. check.redefine(cfg_ir.Literal(True))
  83. break
  84. elif check.block in reachable_blocks[local_def.block]:
  85. is_reachable = True
  86. if not is_reachable:
  87. # Check cannot be reached from any definition. Replace it by a 'False' literal.
  88. check.redefine(cfg_ir.Literal(False))
  89. def optimize(entry_point):
  90. """Optimizes the control-flow graph defined by the given entry point."""
  91. optimize_graph_flow(entry_point)
  92. elide_local_checks(entry_point)
  93. optimize_graph_flow(entry_point)