bytecode_to_tree.py 32 KB

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