bytecode_ir.py 10 KB

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