bytecode_ir.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. """Provides data structures that represent parsed Modelverse bytecode graphs."""
  2. class Instruction(object):
  3. """Represents a Modelverse bytecode instruction."""
  4. def __init__(self):
  5. self.next_instruction = None
  6. self.debug_information = None
  7. def get_directly_reachable(self):
  8. """Gets all instructions that are directly reachable from this instruction, excluding the
  9. next instruction."""
  10. raise NotImplementedError()
  11. def get_reachable(self, filter_children=lambda _: True):
  12. """Gets the set of all instructions that are reachable from the given instruction, including
  13. this instruction. A function can be used to filter out certain instructions' children."""
  14. results = set()
  15. stack = [self]
  16. while len(stack) > 0:
  17. instr = stack.pop()
  18. results.add(instr)
  19. next_instr = instr.next_instruction
  20. if next_instr is not None and next_instr not in results:
  21. stack.append(next_instr)
  22. if filter_children(instr):
  23. for other in instr.get_directly_reachable():
  24. if other not in results:
  25. assert other is not None
  26. stack.append(other)
  27. return results
  28. class VariableNode(object):
  29. """Represents a variable node, which has an identifier and an optional name."""
  30. def __init__(self, node_id, name):
  31. self.node_id = node_id
  32. if node_id is None:
  33. print(locals())
  34. raise Exception("NONE")
  35. self.name = name
  36. def __str__(self):
  37. return 'var(%d, %s)' % (self.node_id, self.name)
  38. def __repr__(self):
  39. return 'VariableNode(%r, %r)' % (self.node_id, self.name)
  40. class SelectInstruction(Instruction):
  41. """Represents an 'if/else' instruction."""
  42. def __init__(self, condition, if_clause, else_clause):
  43. Instruction.__init__(self)
  44. self.condition = condition
  45. self.if_clause = if_clause
  46. self.else_clause = else_clause
  47. def get_directly_reachable(self):
  48. """Gets all instructions that are directly reachable from this instruction."""
  49. if self.else_clause is None:
  50. return (self.condition, self.if_clause)
  51. else:
  52. return (self.condition, self.if_clause, self.else_clause)
  53. constructor_parameters = (
  54. ('cond', Instruction),
  55. ('then', Instruction),
  56. ('else', Instruction))
  57. def __repr__(self):
  58. return '@%r: SelectInstruction(@%r, @%r, @%r)' % (
  59. id(self), id(self.condition), id(self.if_clause), id(self.else_clause))
  60. class WhileInstruction(Instruction):
  61. """Represents a 'while' instruction."""
  62. def __init__(self, condition, body):
  63. Instruction.__init__(self)
  64. self.condition = condition
  65. self.body = body
  66. def get_directly_reachable(self):
  67. """Gets all instructions that are directly reachable from this instruction."""
  68. return (self.condition, self.body)
  69. constructor_parameters = (
  70. ('cond', Instruction),
  71. ('body', Instruction))
  72. def __repr__(self):
  73. return '@%r: WhileInstruction(@%r, @%r)' % (
  74. id(self), id(self.condition), id(self.body))
  75. class BreakInstruction(Instruction):
  76. """Represents a 'break' instruction."""
  77. def __init__(self, loop):
  78. Instruction.__init__(self)
  79. self.loop = loop
  80. def get_directly_reachable(self):
  81. """Gets all instructions that are directly reachable from this instruction."""
  82. return (self.loop,)
  83. constructor_parameters = (('while', WhileInstruction),)
  84. def __repr__(self):
  85. return '@%r: BreakInstruction(@%r)' % (
  86. id(self), id(self.loop))
  87. class ContinueInstruction(Instruction):
  88. """Represents a 'continue' instruction."""
  89. def __init__(self, loop):
  90. Instruction.__init__(self)
  91. self.loop = loop
  92. def get_directly_reachable(self):
  93. """Gets all instructions that are directly reachable from this instruction."""
  94. return (self.loop,)
  95. constructor_parameters = (('while', WhileInstruction),)
  96. def __repr__(self):
  97. return '@%r: ContinueInstruction(@%r)' % (
  98. id(self), id(self.loop))
  99. class ReturnInstruction(Instruction):
  100. """Represents a 'return' instruction, which terminates the current function
  101. and optionally returns a value."""
  102. def __init__(self, value):
  103. Instruction.__init__(self)
  104. self.value = value
  105. def get_directly_reachable(self):
  106. """Gets all instructions that are directly reachable from this instruction."""
  107. if self.value is None:
  108. return ()
  109. else:
  110. return (self.value,)
  111. constructor_parameters = (('value', Instruction),)
  112. def __repr__(self):
  113. return '@%r: ReturnInstruction(@%r)' % (
  114. id(self), id(self.value))
  115. class CallInstruction(Instruction):
  116. """Represents a 'call' instruction, which calls a function with an argument
  117. list, encoded as a list of name-instruction tuples."""
  118. def __init__(self, target, argument_list):
  119. Instruction.__init__(self)
  120. self.target = target
  121. self.argument_list = argument_list
  122. def get_directly_reachable(self):
  123. """Gets all instructions that are directly reachable from this instruction."""
  124. return (self.target,) + tuple((value for _, value in self.argument_list))
  125. def __repr__(self):
  126. return '@%r: CallInstruction(@%r, [%s])' % (
  127. id(self), id(self.target),
  128. ', '.join(['%s=@%r' % (name, id(value)) for name, value in self.argument_list]))
  129. class ConstantInstruction(Instruction):
  130. """Represents a 'constant' instruction, which produces a reference
  131. to a constant node."""
  132. def __init__(self, constant_id):
  133. Instruction.__init__(self)
  134. self.constant_id = constant_id
  135. assert self.constant_id is not None
  136. def get_directly_reachable(self):
  137. """Gets all instructions that are directly reachable from this instruction."""
  138. return ()
  139. constructor_parameters = (('node', int),)
  140. def __repr__(self):
  141. return '@%r: ConstantInstruction(%r)' % (id(self), self.constant_id)
  142. class InputInstruction(Instruction):
  143. """Represents an 'input' instruction, which pops a node from the input
  144. queue."""
  145. def __init__(self):
  146. Instruction.__init__(self)
  147. def get_directly_reachable(self):
  148. """Gets all instructions that are directly reachable from this instruction."""
  149. return ()
  150. constructor_parameters = ()
  151. def __repr__(self):
  152. return '@%r: InputInstruction()' % id(self)
  153. class OutputInstruction(Instruction):
  154. """Represents an 'output' instruction, which pushes a node onto the output
  155. queue."""
  156. def __init__(self, value):
  157. Instruction.__init__(self)
  158. self.value = value
  159. def get_directly_reachable(self):
  160. """Gets all instructions that are directly reachable from this instruction."""
  161. return (self.value,)
  162. constructor_parameters = (('value', Instruction),)
  163. def __repr__(self):
  164. return '@%r: OutputInstruction(@%r)' % (
  165. id(self), id(self.value))
  166. class DeclareInstruction(Instruction):
  167. """Represents a 'declare' instruction, which declares a local variable."""
  168. def __init__(self, variable):
  169. Instruction.__init__(self)
  170. self.variable = variable
  171. def get_directly_reachable(self):
  172. """Gets all instructions that are directly reachable from this instruction."""
  173. return ()
  174. constructor_parameters = (('var', VariableNode),)
  175. def __repr__(self):
  176. return '@%r: DeclareInstruction(%r)' % (
  177. id(self), self.variable)
  178. class GlobalInstruction(Instruction):
  179. """Represents a 'global' instruction, which declares a global variable."""
  180. def __init__(self, variable):
  181. Instruction.__init__(self)
  182. self.variable = variable
  183. def get_directly_reachable(self):
  184. """Gets all instructions that are directly reachable from this instruction."""
  185. return ()
  186. constructor_parameters = (('var', VariableNode),)
  187. def __repr__(self):
  188. return '@%r: GlobalInstruction(%r)' % (
  189. id(self), self.variable)
  190. class ResolveInstruction(Instruction):
  191. """Represents a 'resolve' instruction, which resolves a variable node/name as
  192. either a local or global variable."""
  193. def __init__(self, variable):
  194. Instruction.__init__(self)
  195. self.variable = variable
  196. def get_directly_reachable(self):
  197. """Gets all instructions that are directly reachable from this instruction."""
  198. return ()
  199. constructor_parameters = (('var', VariableNode),)
  200. def __repr__(self):
  201. return '@%r: ResolveInstruction(%r)' % (
  202. id(self), self.variable)
  203. class AccessInstruction(Instruction):
  204. """Represents an 'access' instruction, which loads the node pointed to by a
  205. pointer node."""
  206. def __init__(self, pointer):
  207. Instruction.__init__(self)
  208. self.pointer = pointer
  209. def get_directly_reachable(self):
  210. """Gets all instructions that are directly reachable from this instruction."""
  211. return (self.pointer,)
  212. constructor_parameters = (('var', Instruction),)
  213. def __repr__(self):
  214. return '@%r: AccessInstruction(@%r)' % (
  215. id(self), id(self.pointer))
  216. class AssignInstruction(Instruction):
  217. """Represents an 'assign' instruction, which sets the node pointed to by a
  218. pointer node to the given node."""
  219. def __init__(self, pointer, value):
  220. Instruction.__init__(self)
  221. self.pointer = pointer
  222. self.value = value
  223. def get_directly_reachable(self):
  224. """Gets all instructions that are directly reachable from this instruction."""
  225. return (self.pointer, self.value)
  226. constructor_parameters = (
  227. ('var', Instruction),
  228. ('value', Instruction))
  229. def __repr__(self):
  230. return '@%r: AssignInstruction(@%r, @%r)' % (
  231. id(self), id(self.pointer), id(self.value))
  232. INSTRUCTION_TYPE_MAPPING = {
  233. 'if' : SelectInstruction,
  234. 'while' : WhileInstruction,
  235. 'return' : ReturnInstruction,
  236. 'constant' : ConstantInstruction,
  237. 'resolve' : ResolveInstruction,
  238. 'declare' : DeclareInstruction,
  239. 'global' : GlobalInstruction,
  240. 'assign' : AssignInstruction,
  241. 'access' : AccessInstruction,
  242. 'output' : OutputInstruction,
  243. 'input' : InputInstruction,
  244. 'call' : CallInstruction,
  245. 'break' : BreakInstruction,
  246. 'continue' : ContinueInstruction
  247. }
  248. """Maps instruction names to types."""