bytecode_to_tree.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. """Naively converts bytecode IR to tree IR."""
  2. import modelverse_jit.bytecode_ir as bytecode_ir
  3. import modelverse_jit.tree_ir as tree_ir
  4. import modelverse_jit.runtime as jit_runtime
  5. import modelverse_kernel.primitives as primitive_functions
  6. def get_parameter_names(compiled_function):
  7. """Gets the given compiled function's parameter names."""
  8. if hasattr(compiled_function, '__code__'):
  9. return compiled_function.__code__.co_varnames[
  10. :compiled_function.__code__.co_argcount]
  11. elif hasattr(compiled_function, '__init__'):
  12. return get_parameter_names(compiled_function.__init__)[1:]
  13. else:
  14. raise ValueError("'compiled_function' must be a function or a type.")
  15. def apply_intrinsic(intrinsic_function, named_args):
  16. """Applies the given intrinsic to the given sequence of named arguments."""
  17. param_names = get_parameter_names(intrinsic_function)
  18. if tuple(param_names) == tuple([n for n, _ in named_args]):
  19. # Perfect match. Yay!
  20. return intrinsic_function(**dict(named_args))
  21. else:
  22. # We'll have to store the arguments into locals to preserve
  23. # the order of evaluation.
  24. stored_args = [(name, tree_ir.StoreLocalInstruction(None, arg)) for name, arg in named_args]
  25. arg_value_dict = dict([(name, arg.create_load()) for name, arg in stored_args])
  26. store_instructions = [instruction for _, instruction in stored_args]
  27. return tree_ir.CompoundInstruction(
  28. tree_ir.create_block(*store_instructions),
  29. intrinsic_function(**arg_value_dict))
  30. class AnalysisState(object):
  31. """The state of a bytecode analysis call graph."""
  32. def __init__(self, jit, body_id, user_root, local_mapping, max_instructions=None):
  33. self.analyzed_instructions = set()
  34. self.function_vars = set()
  35. self.local_vars = set()
  36. self.body_id = body_id
  37. self.max_instructions = max_instructions
  38. self.user_root = user_root
  39. self.jit = jit
  40. self.local_mapping = local_mapping
  41. self.function_name = jit.jitted_entry_points[body_id]
  42. self.enclosing_loop_instruction = None
  43. def get_local_name(self, local_id):
  44. """Gets the name for a local with the given id."""
  45. if local_id not in self.local_mapping:
  46. self.local_mapping[local_id] = 'local%d' % local_id
  47. return self.local_mapping[local_id]
  48. def register_local_var(self, local_id):
  49. """Registers the given variable node id as a local."""
  50. if local_id in self.function_vars:
  51. raise jit_runtime.JitCompilationFailedException(
  52. "Local is used as target of function call.")
  53. self.local_vars.add(local_id)
  54. def register_function_var(self, local_id):
  55. """Registers the given variable node id as a function."""
  56. if local_id in self.local_vars:
  57. raise jit_runtime.JitCompilationFailedException(
  58. "Local is used as target of function call.")
  59. self.function_vars.add(local_id)
  60. def retrieve_user_root(self):
  61. """Creates an instruction that stores the user_root variable
  62. in a local."""
  63. return tree_ir.StoreLocalInstruction(
  64. 'user_root',
  65. tree_ir.LoadIndexInstruction(
  66. tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME),
  67. tree_ir.LiteralInstruction('user_root')))
  68. def load_kernel(self):
  69. """Creates an instruction that loads the Modelverse kernel."""
  70. return tree_ir.LoadIndexInstruction(
  71. tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME),
  72. tree_ir.LiteralInstruction('mvk'))
  73. def analyze(self, instruction):
  74. """Tries to build an intermediate representation from the instruction with the
  75. given id."""
  76. # Check the analyzed_instructions set for instruction_id to avoid
  77. # infinite loops.
  78. if instruction in self.analyzed_instructions:
  79. raise jit_runtime.JitCompilationFailedException(
  80. 'Cannot jit non-tree instruction graph.')
  81. elif (self.max_instructions is not None and
  82. len(self.analyzed_instructions) > self.max_instructions):
  83. raise jit_runtime.JitCompilationFailedException(
  84. 'Maximum number of instructions exceeded.')
  85. self.analyzed_instructions.add(instruction)
  86. instruction_type = type(instruction)
  87. if instruction_type in self.instruction_analyzers:
  88. # Analyze the instruction itself.
  89. outer_result, = yield [
  90. ("CALL_ARGS", [self.instruction_analyzers[instruction_type], (self, instruction)])]
  91. if self.jit.tracing_enabled and instruction.debug_information is not None:
  92. outer_result = tree_ir.with_debug_info_trace(
  93. outer_result, instruction.debug_information, self.function_name)
  94. # Check if the instruction has a 'next' instruction.
  95. if instruction.next_instruction is None:
  96. raise primitive_functions.PrimitiveFinished(outer_result)
  97. else:
  98. next_result, = yield [
  99. ("CALL_ARGS", [self.analyze, (instruction.next_instruction,)])]
  100. raise primitive_functions.PrimitiveFinished(
  101. tree_ir.CompoundInstruction(
  102. outer_result,
  103. next_result))
  104. else:
  105. raise jit_runtime.JitCompilationFailedException(
  106. "Unknown instruction type: '%s'" % type(instruction))
  107. def analyze_all(self, instruction_ids):
  108. """Tries to compile a list of IR trees from the given list of instruction ids."""
  109. results = []
  110. for inst in instruction_ids:
  111. analyzed_inst, = yield [("CALL_ARGS", [self.analyze, (inst,)])]
  112. results.append(analyzed_inst)
  113. raise primitive_functions.PrimitiveFinished(results)
  114. def analyze_return(self, instruction):
  115. """Tries to analyze the given 'return' instruction."""
  116. def create_return(return_value):
  117. return tree_ir.ReturnInstruction(
  118. tree_ir.CompoundInstruction(
  119. return_value,
  120. tree_ir.DeleteEdgeInstruction(
  121. tree_ir.LoadLocalInstruction(jit_runtime.LOCALS_EDGE_NAME))))
  122. if instruction.value is None:
  123. raise primitive_functions.PrimitiveFinished(
  124. create_return(
  125. tree_ir.EmptyInstruction()))
  126. else:
  127. retval, = yield [("CALL_ARGS", [self.analyze, (instruction.value,)])]
  128. raise primitive_functions.PrimitiveFinished(
  129. create_return(retval))
  130. def analyze_if(self, instruction):
  131. """Tries to analyze the given 'if' instruction."""
  132. if instruction.else_clause is None:
  133. (cond_r, true_r), = yield [
  134. ("CALL_ARGS",
  135. [self.analyze_all,
  136. ([instruction.condition, instruction.if_clause],)])]
  137. false_r = tree_ir.EmptyInstruction()
  138. else:
  139. (cond_r, true_r, false_r), = yield [
  140. ("CALL_ARGS",
  141. [self.analyze_all,
  142. ([instruction.condition, instruction.if_clause, instruction.else_clause],)])]
  143. raise primitive_functions.PrimitiveFinished(
  144. tree_ir.SelectInstruction(
  145. tree_ir.ReadValueInstruction(cond_r),
  146. true_r,
  147. false_r))
  148. def analyze_while(self, instruction):
  149. """Tries to analyze the given 'while' instruction."""
  150. # Analyze the condition.
  151. cond_r, = yield [("CALL_ARGS", [self.analyze, (instruction.condition,)])]
  152. # Store the old enclosing loop on the stack, and make this loop the
  153. # new enclosing loop.
  154. old_loop_instruction = self.enclosing_loop_instruction
  155. self.enclosing_loop_instruction = instruction
  156. body_r, = yield [("CALL_ARGS", [self.analyze, (instruction.body,)])]
  157. # Restore hte old enclosing loop.
  158. self.enclosing_loop_instruction = old_loop_instruction
  159. if self.jit.nop_insertion_enabled:
  160. create_loop_body = lambda check, body: tree_ir.create_block(
  161. check,
  162. body_r,
  163. tree_ir.NopInstruction())
  164. else:
  165. create_loop_body = tree_ir.CompoundInstruction
  166. raise primitive_functions.PrimitiveFinished(
  167. tree_ir.LoopInstruction(
  168. create_loop_body(
  169. tree_ir.SelectInstruction(
  170. tree_ir.ReadValueInstruction(cond_r),
  171. tree_ir.EmptyInstruction(),
  172. tree_ir.BreakInstruction()),
  173. body_r)))
  174. def analyze_constant(self, instruction):
  175. """Tries to analyze the given 'constant' (literal) instruction."""
  176. raise primitive_functions.PrimitiveFinished(
  177. tree_ir.LiteralInstruction(instruction.constant_id))
  178. def analyze_output(self, instruction):
  179. """Tries to analyze the given 'output' instruction."""
  180. # The plan is to basically generate this tree:
  181. #
  182. # value = <some tree>
  183. # last_output, last_output_link, new_last_output = \
  184. # yield [("RD", [user_root, "last_output"]),
  185. # ("RDE", [user_root, "last_output"]),
  186. # ("CN", []),
  187. # ]
  188. # _, _, _, _ = \
  189. # yield [("CD", [last_output, "value", value]),
  190. # ("CD", [last_output, "next", new_last_output]),
  191. # ("CD", [user_root, "last_output", new_last_output]),
  192. # ("DE", [last_output_link])
  193. # ]
  194. # yield None
  195. value_val, = yield [("CALL_ARGS", [self.analyze, (instruction.value,)])]
  196. value_local = tree_ir.StoreLocalInstruction('value', value_val)
  197. store_user_root = self.retrieve_user_root()
  198. last_output = tree_ir.StoreLocalInstruction(
  199. 'last_output',
  200. tree_ir.ReadDictionaryValueInstruction(
  201. store_user_root.create_load(),
  202. tree_ir.LiteralInstruction('last_output')))
  203. last_output_link = tree_ir.StoreLocalInstruction(
  204. 'last_output_link',
  205. tree_ir.ReadDictionaryEdgeInstruction(
  206. store_user_root.create_load(),
  207. tree_ir.LiteralInstruction('last_output')))
  208. new_last_output = tree_ir.StoreLocalInstruction(
  209. 'new_last_output',
  210. tree_ir.CreateNodeInstruction())
  211. result = tree_ir.create_block(
  212. value_local,
  213. store_user_root,
  214. last_output,
  215. last_output_link,
  216. new_last_output,
  217. tree_ir.CreateDictionaryEdgeInstruction(
  218. last_output.create_load(),
  219. tree_ir.LiteralInstruction('value'),
  220. value_local.create_load()),
  221. tree_ir.CreateDictionaryEdgeInstruction(
  222. last_output.create_load(),
  223. tree_ir.LiteralInstruction('next'),
  224. new_last_output.create_load()),
  225. tree_ir.CreateDictionaryEdgeInstruction(
  226. store_user_root.create_load(),
  227. tree_ir.LiteralInstruction('last_output'),
  228. new_last_output.create_load()),
  229. tree_ir.DeleteEdgeInstruction(last_output_link.create_load()),
  230. tree_ir.NopInstruction())
  231. raise primitive_functions.PrimitiveFinished(result)
  232. def analyze_input(self, _):
  233. """Tries to analyze the given 'input' instruction."""
  234. # Possible alternative to the explicit syntax tree:
  235. if self.jit.input_function_enabled:
  236. raise primitive_functions.PrimitiveFinished(
  237. tree_ir.create_jit_call(
  238. tree_ir.LoadGlobalInstruction(jit_runtime.GET_INPUT_FUNCTION_NAME),
  239. [],
  240. tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME)))
  241. # The plan is to generate this tree:
  242. #
  243. # value = None
  244. # while True:
  245. # _input = yield [("RD", [user_root, "input"])]
  246. # value = yield [("RD", [_input, "value"])]
  247. #
  248. # if value is None:
  249. # kwargs['mvk'].success = False # to avoid blocking
  250. # yield None # nop/interrupt
  251. # else:
  252. # break
  253. #
  254. # _next = yield [("RD", [_input, "next"])]
  255. # yield [("CD", [user_root, "input", _next])]
  256. # yield [("CE", [jit_locals, value])]
  257. # yield [("DN", [_input])]
  258. user_root = self.retrieve_user_root()
  259. _input = tree_ir.StoreLocalInstruction(
  260. None,
  261. tree_ir.ReadDictionaryValueInstruction(
  262. user_root.create_load(),
  263. tree_ir.LiteralInstruction('input')))
  264. value = tree_ir.StoreLocalInstruction(
  265. None,
  266. tree_ir.ReadDictionaryValueInstruction(
  267. _input.create_load(),
  268. tree_ir.LiteralInstruction('value')))
  269. raise primitive_functions.PrimitiveFinished(
  270. tree_ir.CompoundInstruction(
  271. tree_ir.create_block(
  272. user_root,
  273. value.create_store(tree_ir.LiteralInstruction(None)),
  274. tree_ir.LoopInstruction(
  275. tree_ir.create_block(
  276. _input,
  277. value,
  278. tree_ir.SelectInstruction(
  279. tree_ir.BinaryInstruction(
  280. value.create_load(),
  281. 'is',
  282. tree_ir.LiteralInstruction(None)),
  283. tree_ir.create_block(
  284. tree_ir.StoreMemberInstruction(
  285. self.load_kernel(),
  286. 'success',
  287. tree_ir.LiteralInstruction(False)),
  288. tree_ir.NopInstruction()),
  289. tree_ir.BreakInstruction()))),
  290. tree_ir.CreateDictionaryEdgeInstruction(
  291. user_root.create_load(),
  292. tree_ir.LiteralInstruction('input'),
  293. tree_ir.ReadDictionaryValueInstruction(
  294. _input.create_load(),
  295. tree_ir.LiteralInstruction('next'))),
  296. tree_ir.CreateEdgeInstruction(
  297. tree_ir.LoadLocalInstruction(jit_runtime.LOCALS_NODE_NAME),
  298. value.create_load()),
  299. tree_ir.DeleteNodeInstruction(_input.create_load())),
  300. value.create_load()))
  301. def analyze_resolve(self, instruction):
  302. """Tries to analyze the given 'resolve' instruction."""
  303. # To resolve a variable, we'll do something along the
  304. # lines of:
  305. #
  306. # if 'local_var' in locals():
  307. # tmp = local_var
  308. # else:
  309. # _globals, = yield [("RD", [user_root, "globals"])]
  310. # global_var, = yield [("RD", [_globals, var_name])]
  311. #
  312. # if global_var is None:
  313. # raise Exception("Not found as global: %s" % (var_name))
  314. #
  315. # tmp = global_var
  316. name = self.get_local_name(instruction.variable.node_id)
  317. if instruction.variable.name is None:
  318. raise primitive_functions.PrimitiveFinished(
  319. tree_ir.LoadLocalInstruction(name))
  320. user_root = self.retrieve_user_root()
  321. global_var = tree_ir.StoreLocalInstruction(
  322. 'global_var',
  323. tree_ir.ReadDictionaryValueInstruction(
  324. tree_ir.ReadDictionaryValueInstruction(
  325. user_root.create_load(),
  326. tree_ir.LiteralInstruction('globals')),
  327. tree_ir.LiteralInstruction(instruction.variable.name)))
  328. err_block = tree_ir.SelectInstruction(
  329. tree_ir.BinaryInstruction(
  330. global_var.create_load(),
  331. 'is',
  332. tree_ir.LiteralInstruction(None)),
  333. tree_ir.RaiseInstruction(
  334. tree_ir.CallInstruction(
  335. tree_ir.LoadGlobalInstruction('Exception'),
  336. [tree_ir.LiteralInstruction(
  337. "Not found as global: %s" % instruction.variable.name)
  338. ])),
  339. tree_ir.EmptyInstruction())
  340. raise primitive_functions.PrimitiveFinished(
  341. tree_ir.SelectInstruction(
  342. tree_ir.LocalExistsInstruction(name),
  343. tree_ir.LoadLocalInstruction(name),
  344. tree_ir.CompoundInstruction(
  345. tree_ir.create_block(
  346. user_root,
  347. global_var,
  348. err_block),
  349. global_var.create_load())))
  350. def analyze_declare(self, instruction):
  351. """Tries to analyze the given 'declare' function."""
  352. self.register_local_var(instruction.variable.node_id)
  353. name = self.get_local_name(instruction.variable.node_id)
  354. # The following logic declares a local:
  355. #
  356. # if 'local_name' not in locals():
  357. # local_name, = yield [("CN", [])]
  358. # yield [("CE", [LOCALS_NODE_NAME, local_name])]
  359. raise primitive_functions.PrimitiveFinished(
  360. tree_ir.SelectInstruction(
  361. tree_ir.LocalExistsInstruction(name),
  362. tree_ir.EmptyInstruction(),
  363. tree_ir.create_new_local_node(
  364. name,
  365. tree_ir.LoadLocalInstruction(jit_runtime.LOCALS_NODE_NAME))))
  366. def analyze_global(self, instruction):
  367. """Tries to analyze the given 'global' (declaration) instruction."""
  368. # To resolve a variable, we'll do something along the
  369. # lines of:
  370. #
  371. # _globals, = yield [("RD", [user_root, "globals"])]
  372. # global_var = yield [("RD", [_globals, var_name])]
  373. #
  374. # if global_var is None:
  375. # global_var, = yield [("CN", [])]
  376. # yield [("CD", [_globals, var_name, global_var])]
  377. #
  378. # tmp = global_var
  379. user_root = self.retrieve_user_root()
  380. _globals = tree_ir.StoreLocalInstruction(
  381. '_globals',
  382. tree_ir.ReadDictionaryValueInstruction(
  383. user_root.create_load(),
  384. tree_ir.LiteralInstruction('globals')))
  385. global_var = tree_ir.StoreLocalInstruction(
  386. 'global_var',
  387. tree_ir.ReadDictionaryValueInstruction(
  388. _globals.create_load(),
  389. tree_ir.LiteralInstruction(instruction.variable.name)))
  390. raise primitive_functions.PrimitiveFinished(
  391. tree_ir.CompoundInstruction(
  392. tree_ir.create_block(
  393. user_root,
  394. _globals,
  395. global_var,
  396. tree_ir.SelectInstruction(
  397. tree_ir.BinaryInstruction(
  398. global_var.create_load(),
  399. 'is',
  400. tree_ir.LiteralInstruction(None)),
  401. tree_ir.create_block(
  402. global_var.create_store(
  403. tree_ir.CreateNodeInstruction()),
  404. tree_ir.CreateDictionaryEdgeInstruction(
  405. _globals.create_load(),
  406. tree_ir.LiteralInstruction(
  407. instruction.variable.name),
  408. global_var.create_load())),
  409. tree_ir.EmptyInstruction())),
  410. global_var.create_load()))
  411. def analyze_assign(self, instruction):
  412. """Tries to analyze the given 'assign' instruction."""
  413. (var_r, value_r), = yield [
  414. ("CALL_ARGS", [self.analyze_all, ([instruction.pointer, instruction.value],)])]
  415. # Assignments work like this:
  416. #
  417. # value_link = yield [("RDE", [variable, "value"])]
  418. # _, _ = yield [("CD", [variable, "value", value]),
  419. # ("DE", [value_link])]
  420. variable = tree_ir.StoreLocalInstruction(None, var_r)
  421. value = tree_ir.StoreLocalInstruction(None, value_r)
  422. value_link = tree_ir.StoreLocalInstruction(
  423. 'value_link',
  424. tree_ir.ReadDictionaryEdgeInstruction(
  425. variable.create_load(),
  426. tree_ir.LiteralInstruction('value')))
  427. raise primitive_functions.PrimitiveFinished(
  428. tree_ir.create_block(
  429. variable,
  430. value,
  431. value_link,
  432. tree_ir.CreateDictionaryEdgeInstruction(
  433. variable.create_load(),
  434. tree_ir.LiteralInstruction('value'),
  435. value.create_load()),
  436. tree_ir.DeleteEdgeInstruction(
  437. value_link.create_load())))
  438. def analyze_access(self, instruction):
  439. """Tries to analyze the given 'access' instruction."""
  440. var_r, = yield [("CALL_ARGS", [self.analyze, (instruction.pointer,)])]
  441. # Accessing a variable is pretty easy. It really just boils
  442. # down to reading the value corresponding to the 'value' key
  443. # of the variable.
  444. #
  445. # value, = yield [("RD", [returnvalue, "value"])]
  446. raise primitive_functions.PrimitiveFinished(
  447. tree_ir.ReadDictionaryValueInstruction(
  448. var_r,
  449. tree_ir.LiteralInstruction('value')))
  450. def analyze_direct_call(self, callee_id, callee_name, argument_list):
  451. """Tries to analyze a direct 'call' instruction."""
  452. body_id, = yield [("RD", [callee_id, jit_runtime.FUNCTION_BODY_KEY])]
  453. # Make this function dependent on the callee.
  454. if body_id in self.jit.compilation_dependencies:
  455. self.jit.compilation_dependencies[body_id].add(self.body_id)
  456. # Figure out if the function might be an intrinsic.
  457. intrinsic = self.jit.get_intrinsic(callee_name)
  458. if intrinsic is None:
  459. if callee_name is not None:
  460. self.jit.register_global(body_id, callee_name)
  461. compiled_func = self.jit.lookup_compiled_function(callee_name)
  462. else:
  463. compiled_func = None
  464. if compiled_func is None:
  465. # Compile the callee.
  466. yield [
  467. ("CALL_ARGS", [self.jit.jit_compile, (self.user_root, body_id, callee_name)])]
  468. # Get the callee's name.
  469. compiled_func_name = self.jit.get_compiled_name(body_id)
  470. # This handles the corner case where a constant node is called, like
  471. # 'call(constant(9), ...)'. In this case, `callee_name` is `None`
  472. # because 'constant(9)' doesn't give us a name. However, we can look up
  473. # the name of the function at a specific node. If that turns out to be
  474. # an intrinsic, then we still want to pick the intrinsic over a call.
  475. intrinsic = self.jit.get_intrinsic(compiled_func_name)
  476. # Analyze the argument dictionary.
  477. named_args, = yield [("CALL_ARGS", [self.analyze_arguments, (argument_list,)])]
  478. if intrinsic is not None:
  479. raise primitive_functions.PrimitiveFinished(
  480. apply_intrinsic(intrinsic, named_args))
  481. else:
  482. raise primitive_functions.PrimitiveFinished(
  483. tree_ir.create_jit_call(
  484. tree_ir.LoadGlobalInstruction(compiled_func_name),
  485. named_args,
  486. tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME)))
  487. def analyze_arguments(self, argument_list):
  488. """Analyzes the given parameter-to-value mapping."""
  489. named_args = []
  490. for param_name, arg in argument_list:
  491. param_val, = yield [("CALL_ARGS", [self.analyze, (arg,)])]
  492. named_args.append((param_name, param_val))
  493. raise primitive_functions.PrimitiveFinished(named_args)
  494. def analyze_indirect_call(self, target, argument_list):
  495. """Analyzes a call to an unknown function."""
  496. # First off, let's analyze the callee and the argument list.
  497. func_val, = yield [("CALL_ARGS", [self.analyze, (target,)])]
  498. named_args, = yield [("CALL_ARGS", [self.analyze_arguments, (argument_list,)])]
  499. # Call the __call_function function to run the interpreter, like so:
  500. #
  501. # __call_function(function_id, { first_param_name : first_param_val, ... }, **kwargs)
  502. #
  503. dict_literal = tree_ir.DictionaryLiteralInstruction(
  504. [(tree_ir.LiteralInstruction(key), val) for key, val in named_args])
  505. raise primitive_functions.PrimitiveFinished(
  506. tree_ir.create_jit_call(
  507. tree_ir.LoadGlobalInstruction(jit_runtime.CALL_FUNCTION_NAME),
  508. [('function_id', func_val), ('named_arguments', dict_literal)],
  509. tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME)))
  510. def try_analyze_direct_call(self, target, argument_list):
  511. """Tries to analyze the given 'call' instruction as a direct call."""
  512. if not self.jit.direct_calls_allowed:
  513. raise jit_runtime.JitCompilationFailedException(
  514. 'Direct calls are not allowed by the JIT.')
  515. # Figure out what the 'func' instruction's type is.
  516. if isinstance(target, bytecode_ir.AccessInstruction):
  517. # 'access(resolve(var))' instructions are translated to direct calls.
  518. if isinstance(target.pointer, bytecode_ir.ResolveInstruction):
  519. self.register_function_var(target.pointer.variable.node_id)
  520. resolved_var_name = target.pointer.variable.name
  521. # Try to look up the name as a global.
  522. _globals, = yield [("RD", [self.user_root, "globals"])]
  523. global_var, = yield [("RD", [_globals, resolved_var_name])]
  524. global_val, = yield [("RD", [global_var, "value"])]
  525. if global_val is not None:
  526. result, = yield [("CALL_ARGS", [self.analyze_direct_call, (
  527. global_val, resolved_var_name, argument_list)])]
  528. raise primitive_functions.PrimitiveFinished(result)
  529. elif isinstance(target, bytecode_ir.ConstantInstruction):
  530. # 'const(func_id)' instructions are also translated to direct calls.
  531. result, = yield [("CALL_ARGS", [self.analyze_direct_call, (
  532. target.constant_id, None, argument_list)])]
  533. raise primitive_functions.PrimitiveFinished(result)
  534. raise jit_runtime.JitCompilationFailedException(
  535. "Cannot JIT function calls that target an unknown value as direct calls.")
  536. def analyze_call(self, instruction):
  537. """Tries to analyze the given 'call' instruction."""
  538. def handle_exception(_):
  539. # Looks like we'll have to compile it as an indirect call.
  540. gen = self.analyze_indirect_call(instruction.target, instruction.argument_list)
  541. result, = yield [("CALL", [gen])]
  542. raise primitive_functions.PrimitiveFinished(result)
  543. # Try to analyze the call as a direct call.
  544. yield [("TRY", [])]
  545. yield [("CATCH", [jit_runtime.JitCompilationFailedException, handle_exception])]
  546. result, = yield [
  547. ("CALL_ARGS",
  548. [self.try_analyze_direct_call, (instruction.target, instruction.argument_list)])]
  549. yield [("END_TRY", [])]
  550. raise primitive_functions.PrimitiveFinished(result)
  551. def analyze_break(self, instruction):
  552. """Tries to analyze the given 'break' instruction."""
  553. if instruction.loop == self.enclosing_loop_instruction:
  554. raise primitive_functions.PrimitiveFinished(tree_ir.BreakInstruction())
  555. else:
  556. raise jit_runtime.JitCompilationFailedException(
  557. "Multilevel 'break' is not supported by the baseline JIT.")
  558. def analyze_continue(self, instruction):
  559. """Tries to analyze the given 'continue' instruction."""
  560. if instruction.loop == self.enclosing_loop_instruction:
  561. raise primitive_functions.PrimitiveFinished(tree_ir.ContinueInstruction())
  562. else:
  563. raise jit_runtime.JitCompilationFailedException(
  564. "Multilevel 'continue' is not supported by the baseline JIT.")
  565. instruction_analyzers = {
  566. bytecode_ir.SelectInstruction : analyze_if,
  567. bytecode_ir.WhileInstruction : analyze_while,
  568. bytecode_ir.ReturnInstruction : analyze_return,
  569. bytecode_ir.ConstantInstruction : analyze_constant,
  570. bytecode_ir.ResolveInstruction : analyze_resolve,
  571. bytecode_ir.DeclareInstruction : analyze_declare,
  572. bytecode_ir.GlobalInstruction : analyze_global,
  573. bytecode_ir.AssignInstruction : analyze_assign,
  574. bytecode_ir.AccessInstruction : analyze_access,
  575. bytecode_ir.OutputInstruction : analyze_output,
  576. bytecode_ir.InputInstruction : analyze_input,
  577. bytecode_ir.CallInstruction : analyze_call,
  578. bytecode_ir.BreakInstruction : analyze_break,
  579. bytecode_ir.ContinueInstruction : analyze_continue
  580. }