cfg_ir.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. """Defines control flow graph IR data structures."""
  2. # Let's just agree to disagree on map vs list comprehensions, pylint.
  3. # pylint: disable=I0011,W0141
  4. class SharedCounter(object):
  5. """Defines a shared counter."""
  6. def __init__(self):
  7. self.index = 0
  8. def next_value(self):
  9. """Gets the next value for this counter."""
  10. result = self.index
  11. self.index += 1
  12. return result
  13. class BasicBlock(object):
  14. """Represents a basic block."""
  15. def __init__(self, counter):
  16. self.parameters = []
  17. self.definitions = []
  18. self.counter = counter
  19. self.index = counter.next_value()
  20. self.flow = UnreachableFlow()
  21. def append_parameter(self, parameter):
  22. """Appends a parameter to this basic block."""
  23. assert isinstance(parameter, BlockParameter)
  24. result = Definition(self.counter.next_value(), parameter)
  25. self.parameters.append(result)
  26. return result
  27. def prepend_definition(self, value):
  28. """Defines the given value in this basic block."""
  29. assert isinstance(value, Value)
  30. result = Definition(self.counter.next_value(), value)
  31. self.definitions.insert(0, result)
  32. return result
  33. def append_definition(self, value):
  34. """Defines the given value in this basic block."""
  35. assert isinstance(value, Value)
  36. result = Definition(self.counter.next_value(), value)
  37. self.definitions.append(result)
  38. return result
  39. def remove_definition(self, definition):
  40. """Removes the given definition from this basic block."""
  41. return self.definitions.remove(definition)
  42. def __str__(self):
  43. prefix = '!%d(%s):' % (self.index, ', '.join(map(str, self.parameters)))
  44. return '\n'.join(
  45. [prefix] +
  46. [' ' * 4 + str(item) for item in self.definitions + [self.flow]])
  47. class Definition(object):
  48. """Maps a value to a variable."""
  49. def __init__(self, index, value):
  50. self.index = index
  51. self.value = value
  52. assert isinstance(value, Value)
  53. def redefine(self, new_value):
  54. """Tweaks this definition to take on the given new value."""
  55. self.value = new_value
  56. assert isinstance(new_value, Value)
  57. def ref_str(self):
  58. """Gets a string that represents a reference to this definition."""
  59. return '$%d' % self.index
  60. def __str__(self):
  61. return '$%d = %s' % (self.index, str(self.value))
  62. class Instruction(object):
  63. """Represents an instruction."""
  64. def get_dependencies(self):
  65. """Gets all definitions and instructions on which this instruction depends."""
  66. raise NotImplementedError()
  67. def get_all_dependencies(self):
  68. """Gets all definitions and instructions on which this instruction depends,
  69. along with any dependencies of dependencies."""
  70. results = list(self.get_dependencies())
  71. for item in results:
  72. results.extend(item.get_all_dependencies())
  73. return results
  74. class Branch(Instruction):
  75. """Represents a branch from one basic block to another."""
  76. def __init__(self, block, arguments):
  77. self.block = block
  78. assert isinstance(block, BasicBlock)
  79. self.arguments = arguments
  80. assert all([isinstance(arg, Definition) for arg in arguments])
  81. def get_dependencies(self):
  82. """Gets all definitions and instructions on which this instruction depends."""
  83. return self.arguments
  84. def __str__(self):
  85. return '!%d(%s)' % (self.block.index, ', '.join(map(str, self.arguments)))
  86. class FlowInstruction(Instruction):
  87. """Represents a control flow instruction which terminates a basic block."""
  88. def branches(self):
  89. """Gets a list of basic blocks targeted by this flow instruction."""
  90. raise NotImplementedError()
  91. class JumpFlow(FlowInstruction):
  92. """Represents a control flow instruction which jumps directly to a basic block."""
  93. def __init__(self, branch):
  94. FlowInstruction.__init__(self)
  95. self.branch = branch
  96. assert isinstance(branch, Branch)
  97. def get_dependencies(self):
  98. """Gets all definitions and instructions on which this instruction depends."""
  99. return self.branches()
  100. def branches(self):
  101. """Gets a list of basic blocks targeted by this flow instruction."""
  102. return [self.branch]
  103. def __str__(self):
  104. return 'jump %s' % self.branch
  105. class SelectFlow(FlowInstruction):
  106. """Represents a control flow instruction which jumps to one of two basic blocks depending
  107. on whether a condition is truthy or not."""
  108. def __init__(self, condition, if_branch, else_branch):
  109. FlowInstruction.__init__(self)
  110. self.condition = condition
  111. assert isinstance(condition, Definition)
  112. self.if_branch = if_branch
  113. assert isinstance(if_branch, Branch)
  114. self.else_branch = else_branch
  115. assert isinstance(else_branch, Branch)
  116. def get_dependencies(self):
  117. """Gets all definitions and instructions on which this instruction depends."""
  118. return [self.condition] + self.branches()
  119. def branches(self):
  120. """Gets a list of basic blocks targeted by this flow instruction."""
  121. return [self.if_branch, self.else_branch]
  122. def __str__(self):
  123. return 'select %s, %s, %s' % (self.condition.ref_str(), self.if_branch, self.else_branch)
  124. class ReturnFlow(FlowInstruction):
  125. """Represents a control flow instruction which terminates the execution of the current
  126. function and returns a value."""
  127. def __init__(self, value):
  128. FlowInstruction.__init__(self)
  129. self.value = value
  130. assert isinstance(value, Value)
  131. def get_dependencies(self):
  132. """Gets all definitions and instructions on which this instruction depends."""
  133. return [self.value]
  134. def branches(self):
  135. """Gets a list of basic blocks targeted by this flow instruction."""
  136. return []
  137. def __str__(self):
  138. return 'return %s' % self.value.ref_str()
  139. class UnreachableFlow(FlowInstruction):
  140. """Represents a control flow instruction which is unreachable."""
  141. def get_dependencies(self):
  142. """Gets all definitions and instructions on which this instruction depends."""
  143. return []
  144. def branches(self):
  145. """Gets a list of basic blocks targeted by this flow instruction."""
  146. return []
  147. def __str__(self):
  148. return 'unreachable'
  149. class Value(Instruction):
  150. """A value: an instruction that produces some result."""
  151. def get_dependencies(self):
  152. """Gets all definitions and instructions on which this instruction depends."""
  153. raise NotImplementedError()
  154. def has_side_effects(self):
  155. """Tells if this instruction has side-effects."""
  156. return False
  157. class BlockParameter(Value):
  158. """A basic block parameter."""
  159. def get_dependencies(self):
  160. """Gets all definitions and instructions on which this instruction depends."""
  161. return []
  162. def __str__(self):
  163. return 'block-parameter'
  164. class FunctionParameter(Value):
  165. """A function parameter."""
  166. def __init__(self, name):
  167. Value.__init__(self)
  168. self.name = name
  169. def get_dependencies(self):
  170. """Gets all definitions and instructions on which this instruction depends."""
  171. return []
  172. def __str__(self):
  173. return 'func-parameter %s' % self.name
  174. class Literal(Value):
  175. """A literal value."""
  176. def __init__(self, literal):
  177. Value.__init__(self)
  178. self.literal = literal
  179. def get_dependencies(self):
  180. """Gets all definitions and instructions on which this instruction depends."""
  181. return []
  182. def __str__(self):
  183. return 'literal %r' % self.literal
  184. class JitFunctionCall(Value):
  185. """A value that is the result of a function call."""
  186. def __init__(self, target, argument_list):
  187. Value.__init__(self)
  188. assert isinstance(target, Definition)
  189. self.target = target
  190. assert all([isinstance(val, Definition) for val in argument_list])
  191. self.argument_list = argument_list
  192. def has_side_effects(self):
  193. """Tells if this instruction has side-effects."""
  194. return True
  195. def get_dependencies(self):
  196. """Gets all definitions and instructions on which this instruction depends."""
  197. return [self.target] + [val for _, val in self.argument_list]
  198. def __str__(self):
  199. return 'call %s(%s)' % (
  200. self.target.ref_str(),
  201. ', '.join(['%s=%s' % (key, val.ref_str()) for key, val in self.argument_list]))