cfg_ir.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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]))
  202. class DefineLocal(Value):
  203. """A value that defines a local variable."""
  204. def __init__(self, variable):
  205. Value.__init__(self)
  206. self.variable = variable
  207. def get_dependencies(self):
  208. """Gets all definitions and instructions on which this instruction depends."""
  209. return []
  210. def has_side_effects(self):
  211. """Tells if this instruction has side-effects."""
  212. return True
  213. def __str__(self):
  214. return 'define-local %d' % self.variable.node_id
  215. class DefineGlobal(Value):
  216. """A value that defines a global variable."""
  217. def __init__(self, variable):
  218. Value.__init__(self)
  219. self.variable = variable
  220. def get_dependencies(self):
  221. """Gets all definitions and instructions on which this instruction depends."""
  222. return []
  223. def has_side_effects(self):
  224. """Tells if this instruction has side-effects."""
  225. return True
  226. def __str__(self):
  227. return 'define-global %s' % self.variable.name
  228. class CheckLocalExists(Value):
  229. """A value that checks if a local value has been defined (yet)."""
  230. def __init__(self, variable):
  231. Value.__init__(self)
  232. self.variable = variable
  233. def get_dependencies(self):
  234. """Gets all definitions and instructions on which this instruction depends."""
  235. return []
  236. def __str__(self):
  237. return 'check-local-exists %d' % self.variable.node_id
  238. class ResolveLocal(Value):
  239. """A value that resolves a local as a pointer."""
  240. def __init__(self, variable):
  241. Value.__init__(self)
  242. self.variable = variable
  243. def get_dependencies(self):
  244. """Gets all definitions and instructions on which this instruction depends."""
  245. return []
  246. def __str__(self):
  247. return 'resolve-local %d' % self.variable.node_id
  248. class ResolveGlobal(Value):
  249. """A value that resolves a global as a pointer."""
  250. def __init__(self, variable):
  251. Value.__init__(self)
  252. self.variable = variable
  253. def get_dependencies(self):
  254. """Gets all definitions and instructions on which this instruction depends."""
  255. return []
  256. def __str__(self):
  257. return 'resolve-global %s' % self.variable.name
  258. class LoadPointer(Value):
  259. """A value that loads the value assigned to a pointer."""
  260. def __init__(self, pointer):
  261. Value.__init__(self)
  262. self.pointer = pointer
  263. assert isinstance(pointer, Definition)
  264. def get_dependencies(self):
  265. """Gets all definitions and instructions on which this instruction depends."""
  266. return [self.pointer]
  267. def __str__(self):
  268. return 'load %s' % self.pointer.ref_str()
  269. class StoreAtPointer(Value):
  270. """A value that assigns a value to a pointer."""
  271. def __init__(self, pointer, value):
  272. Value.__init__(self)
  273. self.pointer = pointer
  274. assert isinstance(pointer, Definition)
  275. self.value = value
  276. assert isinstance(value, Definition)
  277. def get_dependencies(self):
  278. """Gets all definitions and instructions on which this instruction depends."""
  279. return [self.pointer, self.value]
  280. def has_side_effects(self):
  281. """Tells if this instruction has side-effects."""
  282. return True
  283. def __str__(self):
  284. return 'store %s, %s' % (self.pointer.ref_str(), self.value.ref_str())
  285. class Input(Value):
  286. """A value that pops a node from the input queue."""
  287. def get_dependencies(self):
  288. """Gets all definitions and instructions on which this instruction depends."""
  289. return []
  290. def has_side_effects(self):
  291. """Tells if this instruction has side-effects."""
  292. return True
  293. def __str__(self):
  294. return 'input'
  295. class Output(Value):
  296. """A value that pushes a node onto the output queue."""
  297. def __init__(self, value):
  298. Value.__init__(self)
  299. self.value = value
  300. assert isinstance(value, Definition)
  301. def get_dependencies(self):
  302. """Gets all definitions and instructions on which this instruction depends."""
  303. return [self.value]
  304. def has_side_effects(self):
  305. """Tells if this instruction has side-effects."""
  306. return True
  307. def __str__(self):
  308. return 'output %s' % self.value.ref_str()