bytecode_to_cfg.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. """Converts bytecode IR to CFG IR."""
  2. import modelverse_jit.bytecode_ir as bytecode_ir
  3. import modelverse_jit.cfg_ir as cfg_ir
  4. import modelverse_jit.runtime as jit_runtime
  5. class AnalysisState(object):
  6. """State that is common to the bytecode->CFG transformation of a function."""
  7. def __init__(self):
  8. self.counter = cfg_ir.SharedCounter()
  9. self.analyzed_instructions = set()
  10. self.loop_instructions = {}
  11. self.entry_point = cfg_ir.BasicBlock(self.counter)
  12. self.current_block = self.entry_point
  13. def analyze(self, instruction):
  14. """Analyzes the given instruction as a basic block."""
  15. if instruction in self.analyzed_instructions:
  16. raise jit_runtime.JitCompilationFailedException(
  17. 'Cannot jit non-tree instruction graph.')
  18. self.analyzed_instructions.add(instruction)
  19. # Find an analyzer.
  20. instruction_type = type(instruction)
  21. if instruction_type in self.instruction_analyzers:
  22. # Analyze the instruction.
  23. result = self.instruction_analyzers[instruction_type](self, instruction)
  24. # Check if the instruction has a 'next' instruction. If so, analyze it!
  25. if instruction.next_instruction is not None:
  26. next_result = self.analyze(instruction.next_instruction)
  27. if next_result.value.has_value() or (not result.value.has_value()):
  28. result = next_result
  29. return result
  30. else:
  31. raise jit_runtime.JitCompilationFailedException(
  32. "Unknown instruction type: '%s'" % type(instruction))
  33. def emit_select(self, create_condition, create_if_body, create_else_body):
  34. """Emits a 'select' instruction."""
  35. # Create blocks that look like this:
  36. #
  37. # !current_block(...):
  38. # ...
  39. # $cond = <condition>
  40. # select $cond, !if_block(), !else_block()
  41. #
  42. # !if_block():
  43. # $if_result = <if-body>
  44. # jump !phi_block($if_result)
  45. #
  46. # !else_block():
  47. # $else_result = <else-body>
  48. # jump !phi_block($else_result)
  49. #
  50. # !phi_block($result = block-parameter):
  51. # ...
  52. #
  53. if_block = cfg_ir.BasicBlock(self.counter)
  54. else_block = cfg_ir.BasicBlock(self.counter)
  55. phi_block = cfg_ir.BasicBlock(self.counter)
  56. param_def = phi_block.append_parameter(cfg_ir.BlockParameter())
  57. condition = create_condition()
  58. self.current_block.flow = cfg_ir.SelectFlow(
  59. condition, cfg_ir.Branch(if_block), cfg_ir.Branch(else_block))
  60. self.current_block = if_block
  61. if_result = create_if_body()
  62. self.current_block.flow = cfg_ir.create_jump(phi_block, [if_result])
  63. self.current_block = else_block
  64. else_result = create_else_body()
  65. self.current_block.flow = cfg_ir.create_jump(phi_block, [else_result])
  66. self.current_block = phi_block
  67. return param_def
  68. def analyze_if(self, instruction):
  69. """Analyzes an 'if' instruction."""
  70. return self.emit_select(
  71. lambda: self.analyze(instruction.condition),
  72. lambda: self.analyze(instruction.if_clause),
  73. lambda:
  74. self.current_block.append_definition(cfg_ir.Literal(None))
  75. if instruction.else_clause is None
  76. else self.analyze(instruction.else_clause))
  77. def analyze_while(self, instruction):
  78. """Analyzes a 'while' instruction."""
  79. # Create blocks that look like this:
  80. #
  81. # !current_block(...):
  82. # ...
  83. # jump !loop_condition()
  84. #
  85. # !loop_condition():
  86. # $condition = <condition>
  87. # select $condition, !loop_body(), !loop_exit()
  88. #
  89. # !loop_body():
  90. # $result = <body>
  91. # jump !loop_condition()
  92. #
  93. # !loop_exit():
  94. # $nothing = literal None
  95. # ...
  96. loop_condition_block = cfg_ir.BasicBlock(self.counter)
  97. loop_body_block = cfg_ir.BasicBlock(self.counter)
  98. loop_exit_block = cfg_ir.BasicBlock(self.counter)
  99. self.loop_instructions[instruction] = (loop_condition_block, loop_exit_block)
  100. self.current_block.flow = cfg_ir.create_jump(loop_condition_block)
  101. self.current_block = loop_condition_block
  102. condition_value = self.analyze(instruction.condition)
  103. self.current_block.flow = cfg_ir.SelectFlow(
  104. condition_value, cfg_ir.Branch(loop_body_block), cfg_ir.Branch(loop_exit_block))
  105. self.current_block = loop_body_block
  106. self.analyze(instruction.body)
  107. self.current_block.flow = cfg_ir.create_jump(loop_condition_block)
  108. self.current_block = loop_exit_block
  109. return loop_exit_block.append_definition(cfg_ir.Literal(None))
  110. def analyze_return(self, instruction):
  111. """Analyzes a 'return' instruction."""
  112. if instruction.value is None:
  113. return_value = self.current_block.append_definition(cfg_ir.Literal(None))
  114. else:
  115. return_value = self.analyze(instruction.value)
  116. self.current_block.flow = cfg_ir.ReturnFlow(return_value)
  117. self.current_block = cfg_ir.BasicBlock(self.counter)
  118. return self.current_block.append_definition(cfg_ir.Literal(None))
  119. def analyze_constant(self, instruction):
  120. """Analyzes a 'constant' instruction."""
  121. return self.current_block.append_definition(cfg_ir.Literal(instruction.constant_id))
  122. def analyze_resolve(self, instruction):
  123. """Analyzes a 'resolve' instruction."""
  124. return self.emit_select(
  125. lambda:
  126. self.current_block.append_definition(
  127. cfg_ir.CheckLocalExists(instruction.variable)),
  128. lambda:
  129. self.current_block.append_definition(
  130. cfg_ir.ResolveLocal(instruction.variable)),
  131. lambda:
  132. self.current_block.append_definition(
  133. cfg_ir.ResolveGlobal(instruction.variable)))
  134. def analyze_declare(self, instruction):
  135. """Analyzes a 'declare' instruction."""
  136. return self.current_block.append_definition(cfg_ir.DeclareLocal(instruction.variable))
  137. def analyze_global(self, instruction):
  138. """Analyzes a 'global' instruction."""
  139. return self.current_block.append_definition(cfg_ir.DeclareGlobal(instruction.variable))
  140. def analyze_assign(self, instruction):
  141. """Analyzes an 'assign' instruction."""
  142. pointer_result = self.analyze(instruction.pointer)
  143. value_result = self.analyze(instruction.value)
  144. return self.current_block.append_definition(
  145. cfg_ir.StoreAtPointer(pointer_result, value_result))
  146. def analyze_access(self, instruction):
  147. """Analyzes an 'access' instruction."""
  148. pointer_result = self.analyze(instruction.pointer)
  149. return self.current_block.append_definition(cfg_ir.LoadPointer(pointer_result))
  150. def analyze_output(self, instruction):
  151. """Analyzes an 'output' instruction."""
  152. value_result = self.analyze(instruction.value)
  153. return self.current_block.append_definition(cfg_ir.Output(value_result))
  154. def analyze_input(self, _):
  155. """Analyzes an 'input' instruction."""
  156. return self.current_block.append_definition(cfg_ir.Input())
  157. def analyze_break(self, instruction):
  158. """Analyzes a 'break' instruction."""
  159. if instruction.loop not in self.loop_instructions:
  160. raise jit_runtime.JitCompilationFailedException(
  161. "'break' instruction targets a 'while' loop that has not been defined yet.")
  162. _, exit_block = self.loop_instructions[instruction.loop]
  163. self.current_block.flow = cfg_ir.create_jump(exit_block)
  164. self.current_block = cfg_ir.BasicBlock(self.counter)
  165. return self.current_block.append_definition(cfg_ir.Literal(None))
  166. def analyze_continue(self, instruction):
  167. """Analyzes a 'continue' instruction."""
  168. if instruction.loop not in self.loop_instructions:
  169. raise jit_runtime.JitCompilationFailedException(
  170. "'continue' instruction targets a 'while' loop that has not been defined yet.")
  171. cond_block, _ = self.loop_instructions[instruction.loop]
  172. self.current_block.flow = cfg_ir.create_jump(cond_block)
  173. self.current_block = cfg_ir.BasicBlock(self.counter)
  174. return self.current_block.append_definition(cfg_ir.Literal(None))
  175. def analyze_call(self, instruction):
  176. """Analyzes the given 'call' instruction."""
  177. target = self.analyze(instruction.target)
  178. arg_list = []
  179. for key, arg_instruction in instruction.argument_list:
  180. arg_list.append((key, self.analyze(arg_instruction)))
  181. return self.current_block.append_definition(cfg_ir.JitFunctionCall(target, arg_list))
  182. instruction_analyzers = {
  183. bytecode_ir.SelectInstruction : analyze_if,
  184. bytecode_ir.WhileInstruction : analyze_while,
  185. bytecode_ir.ReturnInstruction : analyze_return,
  186. bytecode_ir.ConstantInstruction : analyze_constant,
  187. bytecode_ir.ResolveInstruction : analyze_resolve,
  188. bytecode_ir.DeclareInstruction : analyze_declare,
  189. bytecode_ir.GlobalInstruction : analyze_global,
  190. bytecode_ir.AssignInstruction : analyze_assign,
  191. bytecode_ir.AccessInstruction : analyze_access,
  192. bytecode_ir.OutputInstruction : analyze_output,
  193. bytecode_ir.InputInstruction : analyze_input,
  194. bytecode_ir.CallInstruction : analyze_call,
  195. bytecode_ir.BreakInstruction : analyze_break,
  196. bytecode_ir.ContinueInstruction : analyze_continue
  197. }