intrinsics.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. import time
  2. import modelverse_jit.jit as jit
  3. import modelverse_jit.tree_ir as tree_ir
  4. import modelverse_jit.cfg_ir as cfg_ir
  5. import modelverse_jit.runtime as jit_runtime
  6. BINARY_INTRINSICS = {
  7. 'value_eq' : '==',
  8. 'value_neq' : '!=',
  9. 'bool_and' : 'and',
  10. 'bool_or' : 'or',
  11. 'integer_addition' : '+',
  12. 'integer_subtraction' : '-',
  13. 'integer_multiplication' : '*',
  14. 'integer_division' : '/',
  15. 'integer_gt' : '>',
  16. 'integer_gte' : '>=',
  17. 'integer_lt' : '<',
  18. 'integer_lte' : '<=',
  19. 'float_addition' : '+',
  20. 'float_subtraction' : '-',
  21. 'float_multiplication' : '*',
  22. 'float_division' : '/',
  23. 'float_gt' : '>',
  24. 'float_gte' : '>=',
  25. 'float_lt' : '<',
  26. 'float_lte' : '<='
  27. }
  28. UNARY_INTRINSICS = {
  29. 'bool_not' : 'not',
  30. 'integer_neg' : '-',
  31. 'float_neg' : '-'
  32. }
  33. CAST_INTRINSICS = {
  34. 'cast_i2f' : float,
  35. 'cast_i2s' : str,
  36. 'cast_i2b' : bool,
  37. 'cast_f2i' : int,
  38. 'cast_f2s' : str,
  39. 'cast_f2b' : bool,
  40. 'cast_s2i' : int,
  41. 'cast_s2f' : float,
  42. 'cast_s2b' : bool,
  43. 'cast_b2i' : int,
  44. 'cast_b2f' : float,
  45. 'cast_b2s' : str
  46. }
  47. def create_get_length(expression):
  48. """Creates an expression that evaluates the given expression, and then
  49. computes the length of its result."""
  50. return tree_ir.CallInstruction(
  51. tree_ir.LoadGlobalInstruction('len'),
  52. [expression])
  53. # Don't compain about the variable names, pylint. It's important that we
  54. # get them right.
  55. # pylint: disable=I0011,C0103
  56. def __set_add(a, b):
  57. store_a, load_a = tree_ir.evaluate_and_load(a)
  58. store_b, load_b = tree_ir.evaluate_and_load(b)
  59. return tree_ir.create_block(
  60. store_a,
  61. store_b,
  62. tree_ir.CreateEdgeInstruction(
  63. tree_ir.CreateEdgeInstruction(load_a, a),
  64. load_b),
  65. load_a)
  66. def __dict_add(a, b, c):
  67. store_a, load_a = tree_ir.evaluate_and_load(a)
  68. store_b, load_b = tree_ir.evaluate_and_load(b)
  69. return tree_ir.create_block(
  70. store_a,
  71. store_b,
  72. tree_ir.CreateEdgeInstruction(
  73. tree_ir.CreateEdgeInstruction(load_a, c),
  74. load_b),
  75. load_a)
  76. def __dict_add_fast(a, b, c):
  77. # TODO This might be possible to optimize slightly?
  78. store_a, load_a = tree_ir.evaluate_and_load(a)
  79. b_val = tree_ir.StoreLocalInstruction(
  80. None,
  81. tree_ir.ReadValueInstruction(b))
  82. store_c, load_c = tree_ir.evaluate_and_load(c)
  83. return tree_ir.create_block(
  84. store_a,
  85. b_val,
  86. store_c,
  87. tree_ir.CreateDictionaryEdgeInstruction(
  88. load_a,
  89. b_val.create_load(),
  90. load_c),
  91. load_a)
  92. def __list_read(a, b):
  93. # The statements in this function generate the following code:
  94. #
  95. # a_tmp = a # To make sure a is evaluated before b.
  96. # b_value, = yield [("RV", [b])]
  97. # result, = yield [("RD", [a_tmp, b_value])]
  98. # if result is None:
  99. # raise Exception("List read out of bounds: %s" % b_value)
  100. # result
  101. store_a, load_a = tree_ir.evaluate_and_load(a)
  102. b_val = tree_ir.StoreLocalInstruction(
  103. None,
  104. tree_ir.ReadValueInstruction(b))
  105. result = tree_ir.StoreLocalInstruction(
  106. None,
  107. tree_ir.ReadDictionaryValueInstruction(
  108. load_a.create_load(), b_val.create_load()))
  109. return tree_ir.create_block(
  110. store_a,
  111. b_val,
  112. result,
  113. tree_ir.SelectInstruction(
  114. tree_ir.BinaryInstruction(
  115. result.create_load(),
  116. 'is',
  117. tree_ir.LiteralInstruction(None)),
  118. tree_ir.RaiseInstruction(
  119. tree_ir.CallInstruction(
  120. tree_ir.LoadGlobalInstruction('Exception'),
  121. [tree_ir.BinaryInstruction(
  122. tree_ir.LiteralInstruction('List read out of bounds: %s'),
  123. '%',
  124. b_val.create_load())])),
  125. tree_ir.EmptyInstruction()),
  126. result.create_load())
  127. def __list_append(a, b):
  128. # We want to generate code that is more or less equivalent to:
  129. #
  130. # a_tmp = a
  131. # b_tmp = b
  132. # a_outgoing, = yield [("RO", [a_tmp])]
  133. # _ = yield [("CD", [a_tmp, len(a_outgoing), b_tmp])]
  134. # a
  135. store_a, load_a = tree_ir.evaluate_and_load(a)
  136. store_b, load_b = tree_ir.evaluate_and_load(b)
  137. return tree_ir.create_block(
  138. store_a,
  139. store_b,
  140. tree_ir.CreateDictionaryEdgeInstruction(
  141. load_a,
  142. create_get_length(
  143. tree_ir.ReadOutgoingEdgesInstruction(
  144. load_a)),
  145. load_b),
  146. load_a)
  147. def __log(a):
  148. # Original definition:
  149. #
  150. # def log(a, **remainder):
  151. # a_value, = yield [("RV", [a])]
  152. # print("== LOG == " + str(a_value))
  153. # raise PrimitiveFinished(a)
  154. store_a, load_a = tree_ir.evaluate_and_load(a)
  155. return tree_ir.CompoundInstruction(
  156. tree_ir.create_block(
  157. store_a,
  158. tree_ir.PrintInstruction(
  159. tree_ir.BinaryInstruction(
  160. tree_ir.LiteralInstruction("== LOG == "),
  161. '+',
  162. tree_ir.CallInstruction(
  163. tree_ir.LoadGlobalInstruction('str'),
  164. [tree_ir.ReadValueInstruction(load_a)])))),
  165. load_a)
  166. def __read_nr_out(a):
  167. # Original definition:
  168. #
  169. # def read_nr_out(a, **remainder):
  170. # outgoing, = yield [("RO", [a])]
  171. # result, = yield [("CNV", [len(outgoing)])]
  172. # raise PrimitiveFinished(result)
  173. return tree_ir.CreateNodeWithValueInstruction(
  174. create_get_length(tree_ir.ReadOutgoingEdgesInstruction(a)))
  175. MISC_INTRINSICS = {
  176. # Reference equality
  177. 'element_eq' :
  178. lambda a, b:
  179. tree_ir.CreateNodeWithValueInstruction(
  180. tree_ir.BinaryInstruction(a, '==', b)),
  181. 'element_neq' :
  182. lambda a, b:
  183. tree_ir.CreateNodeWithValueInstruction(
  184. tree_ir.BinaryInstruction(a, '!=', b)),
  185. # Strings
  186. 'string_get' :
  187. lambda a, b:
  188. tree_ir.CreateNodeWithValueInstruction(
  189. tree_ir.LoadIndexInstruction(
  190. tree_ir.ReadValueInstruction(a),
  191. tree_ir.ReadValueInstruction(b))),
  192. 'string_len' :
  193. lambda a:
  194. tree_ir.CreateNodeWithValueInstruction(
  195. tree_ir.CallInstruction(
  196. tree_ir.LoadGlobalInstruction('len'),
  197. [tree_ir.ReadValueInstruction(a)])),
  198. 'string_join' :
  199. lambda a, b:
  200. tree_ir.CreateNodeWithValueInstruction(
  201. tree_ir.BinaryInstruction(
  202. tree_ir.CallInstruction(
  203. tree_ir.LoadGlobalInstruction('str'),
  204. [tree_ir.ReadValueInstruction(a)]),
  205. '+',
  206. tree_ir.CallInstruction(
  207. tree_ir.LoadGlobalInstruction('str'),
  208. [tree_ir.ReadValueInstruction(b)]))),
  209. 'string_startswith' :
  210. lambda a, b:
  211. tree_ir.CreateNodeWithValueInstruction(
  212. tree_ir.CallInstruction(
  213. tree_ir.LoadMemberInstruction(
  214. tree_ir.ReadValueInstruction(a),
  215. 'startswith'),
  216. [tree_ir.ReadValueInstruction(b)])),
  217. # State creation
  218. 'create_node' : tree_ir.CreateNodeInstruction,
  219. 'create_edge' :
  220. # Lambda is totally necessary here, pylint.
  221. # You totally dropped the ball on this one.
  222. # pylint: disable=I0011,W0108
  223. lambda a, b:
  224. tree_ir.CreateEdgeInstruction(a, b),
  225. 'create_value' :
  226. lambda a:
  227. tree_ir.CreateNodeWithValueInstruction(
  228. tree_ir.ReadValueInstruction(a)),
  229. # State reads
  230. 'read_edge_src' :
  231. lambda a:
  232. tree_ir.LoadIndexInstruction(
  233. tree_ir.ReadEdgeInstruction(a),
  234. tree_ir.LiteralInstruction(0)),
  235. 'read_edge_dst' :
  236. lambda a:
  237. tree_ir.LoadIndexInstruction(
  238. tree_ir.ReadEdgeInstruction(a),
  239. tree_ir.LiteralInstruction(1)),
  240. 'is_edge' :
  241. lambda a:
  242. tree_ir.CreateNodeWithValueInstruction(
  243. tree_ir.BinaryInstruction(
  244. tree_ir.LoadIndexInstruction(
  245. tree_ir.ReadEdgeInstruction(a),
  246. tree_ir.LiteralInstruction(0)),
  247. 'is not',
  248. tree_ir.LiteralInstruction(None))),
  249. 'read_nr_out' : __read_nr_out,
  250. # read_root
  251. 'read_root' :
  252. lambda:
  253. tree_ir.LoadIndexInstruction(
  254. tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME),
  255. tree_ir.LiteralInstruction('root')),
  256. # read_taskroot
  257. 'read_taskroot' :
  258. lambda:
  259. tree_ir.LoadIndexInstruction(
  260. tree_ir.LoadLocalInstruction(jit_runtime.KWARGS_PARAMETER_NAME),
  261. tree_ir.LiteralInstruction('task_root')),
  262. # Dictionary operations
  263. 'dict_create' : tree_ir.CreateNodeInstruction,
  264. 'dict_len': __read_nr_out,
  265. 'dict_read' :
  266. lambda a, b:
  267. tree_ir.ReadDictionaryValueInstruction(
  268. a, tree_ir.ReadValueInstruction(b)),
  269. 'dict_read_edge' :
  270. lambda a, b:
  271. tree_ir.ReadDictionaryEdgeInstruction(
  272. a, tree_ir.ReadValueInstruction(b)),
  273. 'dict_add' : __dict_add,
  274. 'dict_add_fast' : __dict_add_fast,
  275. 'dict_len' : __read_nr_out,
  276. # Set operations
  277. 'set_create' : tree_ir.CreateNodeInstruction,
  278. 'set_add' : __set_add,
  279. 'set_len' : __read_nr_out,
  280. # List operations
  281. 'list_create' : tree_ir.CreateNodeInstruction,
  282. 'list_len' : __read_nr_out,
  283. 'list_read' : __list_read,
  284. 'list_append' : __list_append,
  285. # log
  286. 'log' : __log
  287. }
  288. def __read_nr_out_cfg(original_def, a):
  289. # Original definition:
  290. #
  291. # def read_nr_out(a, **remainder):
  292. # outgoing, = yield [("RO", [a])]
  293. # result, = yield [("CNV", [len(outgoing)])]
  294. # raise PrimitiveFinished(result)
  295. original_def.redefine(
  296. cfg_ir.CreateNode(
  297. original_def.insert_before(
  298. cfg_ir.create_pure_simple_call(
  299. 'len',
  300. original_def.insert_before(
  301. cfg_ir.create_read_outgoing_edges(a))))))
  302. def __dict_in_cfg(original_def, a, b):
  303. # Original definition:
  304. #
  305. # def dict_in(a, b, **remainder):
  306. # b_value, = yield [("RV", [b])]
  307. # value, = yield [("RD", [a, b_value])]
  308. # is_in = value is not None
  309. # result, = yield [("CNV", [is_in])]
  310. # raise PrimitiveFinished(result)
  311. original_def.redefine(
  312. cfg_ir.CreateNode(
  313. original_def.insert_before(
  314. cfg_ir.Binary(
  315. original_def.insert_before(
  316. cfg_ir.create_read_dict_value(
  317. a, original_def.insert_before(cfg_ir.Read(b)))),
  318. 'is not',
  319. original_def.insert_before(cfg_ir.Literal(None))))))
  320. def __dict_in_node_cfg(original_def, a, b):
  321. # Original definition:
  322. #
  323. # def dict_in_node(a, b, **remainder):
  324. # value, = yield [("RDN", [a, b])]
  325. # result, = yield [("CNV", [value is not None])]
  326. # raise PrimitiveFinished(result)
  327. original_def.redefine(
  328. cfg_ir.CreateNode(
  329. original_def.insert_before(
  330. cfg_ir.Binary(
  331. original_def.insert_before(cfg_ir.create_read_dict_node(a, b)),
  332. 'is not',
  333. original_def.insert_before(cfg_ir.Literal(None))))))
  334. def __dict_read_cfg(original_def, a, b):
  335. # Original definition:
  336. #
  337. # def dict_read(a, b, **remainder):
  338. # b_value, = yield [("RV", [b])]
  339. # result, = yield [("RD", [a, b_value])]
  340. # raise PrimitiveFinished(result)
  341. original_def.redefine(
  342. cfg_ir.create_read_dict_value(
  343. a,
  344. original_def.insert_before(cfg_ir.Read(b))))
  345. MISC_CFG_INTRINSICS = {
  346. # Reference equality
  347. 'element_eq' :
  348. lambda original_def, a, b:
  349. original_def.redefine(
  350. cfg_ir.CreateNode(
  351. original_def.insert_before(
  352. cfg_ir.Binary(a, '==', b)))),
  353. 'element_neq' :
  354. lambda original_def, a, b:
  355. original_def.redefine(
  356. cfg_ir.CreateNode(
  357. original_def.insert_before(
  358. cfg_ir.Binary(a, '!=', b)))),
  359. # String operations
  360. 'string_get' :
  361. lambda original_def, a, b:
  362. original_def.redefine(
  363. cfg_ir.CreateNode(
  364. original_def.insert_before(
  365. cfg_ir.create_index(
  366. original_def.insert_before(cfg_ir.Read(a)),
  367. original_def.insert_before(cfg_ir.Read(b)))))),
  368. 'string_len' :
  369. lambda original_def, a:
  370. original_def.redefine(
  371. cfg_ir.CreateNode(
  372. original_def.insert_before(
  373. cfg_ir.create_pure_simple_call(
  374. 'len',
  375. original_def.insert_before(cfg_ir.Read(a)))))),
  376. 'string_join' :
  377. lambda original_def, a, b:
  378. original_def.redefine(
  379. cfg_ir.CreateNode(
  380. original_def.insert_before(
  381. cfg_ir.Binary(
  382. original_def.insert_before(
  383. cfg_ir.create_pure_simple_call(
  384. 'str',
  385. original_def.insert_before(cfg_ir.Read(a)))),
  386. '+',
  387. original_def.insert_before(
  388. cfg_ir.create_pure_simple_call(
  389. 'str',
  390. original_def.insert_before(cfg_ir.Read(b)))))))),
  391. 'string_startswith' :
  392. lambda original_def, a, b:
  393. original_def.redefine(
  394. cfg_ir.CreateNode(
  395. original_def.insert_before(
  396. cfg_ir.DirectFunctionCall(
  397. 'startswith',
  398. [('self', original_def.insert_before(cfg_ir.Read(a))),
  399. ('substring', original_def.insert_before(cfg_ir.Read(b)))],
  400. calling_convention=cfg_ir.SELF_POSITIONAL_CALLING_CONVENTION,
  401. has_value=True, has_side_effects=False)))),
  402. # State creation
  403. 'create_node' :
  404. lambda original_def:
  405. original_def.redefine(
  406. cfg_ir.CreateNode(original_def.insert_before(cfg_ir.Literal(None)))),
  407. 'create_value' :
  408. lambda original_def, a:
  409. original_def.redefine(
  410. cfg_ir.CreateNode(original_def.insert_before(cfg_ir.Read(a)))),
  411. # State reads
  412. 'read_nr_out' : __read_nr_out_cfg,
  413. # Dictionary operations
  414. 'dict_len' : __read_nr_out_cfg,
  415. 'dict_read' : __dict_read_cfg,
  416. 'dict_in' : __dict_in_cfg,
  417. 'dict_in_node' : __dict_in_node_cfg,
  418. 'set_in' : __dict_in_cfg,
  419. 'set_len' : __read_nr_out_cfg,
  420. # List operations
  421. 'list_len' : __read_nr_out_cfg,
  422. 'list_create' :
  423. lambda original_def:
  424. original_def.redefine(
  425. cfg_ir.CreateNode(original_def.insert_before(cfg_ir.Literal(None)))),
  426. 'set_create' :
  427. lambda original_def:
  428. original_def.redefine(
  429. cfg_ir.CreateNode(original_def.insert_before(cfg_ir.Literal(None)))),
  430. 'dict_create' :
  431. lambda original_def:
  432. original_def.redefine(
  433. cfg_ir.CreateNode(original_def.insert_before(cfg_ir.Literal(None)))),
  434. }
  435. def register_time_intrinsic(target_jit):
  436. """Registers the time() intrinsic with the given JIT."""
  437. import_name = target_jit.import_value(time.time, 'time')
  438. target_jit.register_intrinsic(
  439. 'time',
  440. lambda: tree_ir.CreateNodeWithValueInstruction(
  441. tree_ir.CallInstruction(
  442. tree_ir.LoadGlobalInstruction(import_name),
  443. [])))
  444. def register_intrinsics(target_jit):
  445. """Registers all intrinsics in the module with the given JIT."""
  446. for (key, value) in BINARY_INTRINSICS.items():
  447. target_jit.register_binary_intrinsic(key, value)
  448. for (key, value) in UNARY_INTRINSICS.items():
  449. target_jit.register_unary_intrinsic(key, value)
  450. for (key, value) in CAST_INTRINSICS.items():
  451. target_jit.register_cast_intrinsic(key, value)
  452. for (key, value) in MISC_INTRINSICS.items():
  453. target_jit.register_intrinsic(key, value)
  454. for (key, value) in MISC_CFG_INTRINSICS.items():
  455. target_jit.register_cfg_intrinsic(key, value)
  456. register_time_intrinsic(target_jit)