bytecode_ir.py 9.9 KB

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