PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/jit/metainterp/optimizeopt/unroll.py

https://bitbucket.org/pjenvey/pypy-mq
Python | 554 lines | 543 code | 6 blank | 5 comment | 28 complexity | 2ffa50e31f5dc5c7041714a44cfbc7a5 MD5 | raw file
Possible License(s): Apache-2.0, AGPL-3.0, BSD-3-Clause
  1. import sys
  2. from rpython.jit.metainterp.history import Const, TargetToken, JitCellToken
  3. from rpython.jit.metainterp.optimizeopt.shortpreamble import ShortBoxes,\
  4. ShortPreambleBuilder, ExtendedShortPreambleBuilder, PreambleOp
  5. from rpython.jit.metainterp.optimizeopt import info, intutils
  6. from rpython.jit.metainterp.optimize import InvalidLoop, SpeculativeError
  7. from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer,\
  8. Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo
  9. from rpython.jit.metainterp.optimizeopt.vstring import StrPtrInfo
  10. from rpython.jit.metainterp.optimizeopt.virtualstate import (
  11. VirtualStateConstructor, VirtualStatesCantMatch)
  12. from rpython.jit.metainterp.resoperation import rop, ResOperation, GuardResOp,\
  13. AbstractResOp
  14. from rpython.jit.metainterp import compile
  15. from rpython.rlib.debug import debug_print, debug_start, debug_stop,\
  16. have_debug_prints
  17. class UnrollableOptimizer(Optimizer):
  18. def force_op_from_preamble(self, preamble_op):
  19. if isinstance(preamble_op, PreambleOp):
  20. if self.optunroll.short_preamble_producer is None:
  21. assert False # unreachable code
  22. op = preamble_op.op
  23. self.optimizer.inparg_dict[op] = None # XXX ARGH
  24. # special hack for int_add(x, accumulator-const) optimization
  25. self.optunroll.short_preamble_producer.use_box(op,
  26. preamble_op.preamble_op, self)
  27. if not preamble_op.op.is_constant():
  28. if preamble_op.invented_name:
  29. op = self.get_box_replacement(op)
  30. self.optunroll.potential_extra_ops[op] = preamble_op
  31. return preamble_op.op
  32. return preamble_op
  33. def setinfo_from_preamble_list(self, lst, infos):
  34. for item in lst:
  35. if item is None:
  36. continue
  37. i = infos.get(item, None)
  38. if i is not None:
  39. self.setinfo_from_preamble(item, i, infos)
  40. else:
  41. item.set_forwarded(None)
  42. # let's not inherit stuff we don't
  43. # know anything about
  44. def setinfo_from_preamble(self, op, preamble_info, exported_infos):
  45. op = self.get_box_replacement(op)
  46. if op.get_forwarded() is not None:
  47. return
  48. if op.is_constant():
  49. return # nothing we can learn
  50. if isinstance(preamble_info, info.PtrInfo):
  51. if preamble_info.is_virtual():
  52. op.set_forwarded(preamble_info)
  53. self.setinfo_from_preamble_list(preamble_info.all_items(),
  54. exported_infos)
  55. return
  56. if preamble_info.is_constant():
  57. # but op is not
  58. op.set_forwarded(preamble_info.getconst())
  59. return
  60. if preamble_info.get_descr() is not None:
  61. if isinstance(preamble_info, info.StructPtrInfo):
  62. op.set_forwarded(info.StructPtrInfo(
  63. preamble_info.get_descr()))
  64. if isinstance(preamble_info, info.InstancePtrInfo):
  65. op.set_forwarded(info.InstancePtrInfo(
  66. preamble_info.get_descr()))
  67. known_class = preamble_info.get_known_class(self.cpu)
  68. if known_class:
  69. self.make_constant_class(op, known_class, False)
  70. if isinstance(preamble_info, info.ArrayPtrInfo):
  71. arr_info = info.ArrayPtrInfo(preamble_info.descr)
  72. bound = preamble_info.getlenbound(None).clone()
  73. assert isinstance(bound, intutils.IntBound)
  74. arr_info.lenbound = bound
  75. op.set_forwarded(arr_info)
  76. if isinstance(preamble_info, StrPtrInfo):
  77. str_info = StrPtrInfo(preamble_info.mode)
  78. bound = preamble_info.getlenbound(None).clone()
  79. assert isinstance(bound, intutils.IntBound)
  80. str_info.lenbound = bound
  81. op.set_forwarded(str_info)
  82. if preamble_info.is_nonnull():
  83. self.make_nonnull(op)
  84. elif isinstance(preamble_info, intutils.IntBound):
  85. if preamble_info.lower > MININT/2 or preamble_info.upper < MAXINT/2:
  86. intbound = self.getintbound(op)
  87. if preamble_info.has_lower and preamble_info.lower > MININT/2:
  88. intbound.has_lower = True
  89. intbound.lower = preamble_info.lower
  90. if preamble_info.has_upper and preamble_info.upper < MAXINT/2:
  91. intbound.has_upper = True
  92. intbound.upper = preamble_info.upper
  93. elif isinstance(preamble_info, info.FloatConstInfo):
  94. op.set_forwarded(preamble_info._const)
  95. class UnrollOptimizer(Optimization):
  96. """Unroll the loop into two iterations. The first one will
  97. become the preamble or entry bridge (don't think there is a
  98. distinction anymore)"""
  99. short_preamble_producer = None
  100. def __init__(self, metainterp_sd, jitdriver_sd, optimizations):
  101. self.optimizer = UnrollableOptimizer(metainterp_sd, jitdriver_sd,
  102. optimizations)
  103. self.optimizer.optunroll = self
  104. def get_virtual_state(self, args):
  105. modifier = VirtualStateConstructor(self.optimizer)
  106. return modifier.get_virtual_state(args)
  107. def _check_no_forwarding(self, lsts, check_newops=True):
  108. for lst in lsts:
  109. for op in lst:
  110. assert op.get_forwarded() is None
  111. if check_newops:
  112. assert not self.optimizer._newoperations
  113. def optimize_preamble(self, trace, runtime_boxes, call_pure_results, memo):
  114. info, newops = self.optimizer.propagate_all_forward(
  115. trace.get_iter(), call_pure_results, flush=False)
  116. exported_state = self.export_state(info.jump_op.getarglist(),
  117. info.inputargs,
  118. runtime_boxes, memo)
  119. exported_state.quasi_immutable_deps = info.quasi_immutable_deps
  120. # we need to absolutely make sure that we've cleaned up all
  121. # the optimization info
  122. self.optimizer._clean_optimization_info(self.optimizer._newoperations)
  123. return exported_state, self.optimizer._newoperations
  124. def optimize_peeled_loop(self, trace, celltoken, state,
  125. call_pure_results, inline_short_preamble=True):
  126. trace = trace.get_iter()
  127. try:
  128. label_args = self.import_state(trace.inputargs, state)
  129. except VirtualStatesCantMatch:
  130. raise InvalidLoop("Cannot import state, virtual states don't match")
  131. self.potential_extra_ops = {}
  132. self.optimizer.init_inparg_dict_from(label_args)
  133. try:
  134. info, _ = self.optimizer.propagate_all_forward(
  135. trace, call_pure_results, flush=False)
  136. except SpeculativeError:
  137. raise InvalidLoop("Speculative heap access would be ill-typed")
  138. end_jump = info.jump_op
  139. label_op = ResOperation(rop.LABEL, label_args,
  140. descr=celltoken)
  141. for a in end_jump.getarglist():
  142. self.optimizer.force_box_for_end_of_preamble(
  143. self.optimizer.get_box_replacement(a))
  144. current_vs = self.get_virtual_state(end_jump.getarglist())
  145. # pick the vs we want to jump to
  146. assert isinstance(celltoken, JitCellToken)
  147. target_virtual_state = self.pick_virtual_state(current_vs,
  148. state.virtual_state,
  149. celltoken.target_tokens)
  150. # force the boxes for virtual state to match
  151. try:
  152. args = target_virtual_state.make_inputargs(
  153. [self.get_box_replacement(x) for x in end_jump.getarglist()],
  154. self.optimizer, force_boxes=True)
  155. for arg in args:
  156. self.optimizer.force_box(arg)
  157. except VirtualStatesCantMatch:
  158. raise InvalidLoop("Virtual states did not match "
  159. "after picking the virtual state, when forcing"
  160. " boxes")
  161. extra_same_as = self.short_preamble_producer.extra_same_as[:]
  162. target_token = self.finalize_short_preamble(label_op,
  163. state.virtual_state)
  164. label_op.setdescr(target_token)
  165. if not inline_short_preamble:
  166. self.jump_to_preamble(celltoken, end_jump, info)
  167. return (UnrollInfo(target_token, label_op, extra_same_as,
  168. self.optimizer.quasi_immutable_deps),
  169. self.optimizer._newoperations)
  170. try:
  171. new_virtual_state = self.jump_to_existing_trace(
  172. end_jump, label_op, state.runtime_boxes, force_boxes=False)
  173. except InvalidLoop:
  174. # inlining short preamble failed, jump to preamble
  175. self.jump_to_preamble(celltoken, end_jump, info)
  176. return (UnrollInfo(target_token, label_op, extra_same_as,
  177. self.optimizer.quasi_immutable_deps),
  178. self.optimizer._newoperations)
  179. if new_virtual_state is not None:
  180. # Attempt to force virtual boxes in order to avoid jumping
  181. # to the preamble.
  182. try:
  183. new_virtual_state = self.jump_to_existing_trace(
  184. end_jump, label_op, state.runtime_boxes, force_boxes=True)
  185. except InvalidLoop:
  186. pass
  187. if new_virtual_state is not None:
  188. self.jump_to_preamble(celltoken, end_jump, info)
  189. return (UnrollInfo(target_token, label_op, extra_same_as,
  190. self.optimizer.quasi_immutable_deps),
  191. self.optimizer._newoperations)
  192. self.disable_retracing_if_max_retrace_guards(
  193. self.optimizer._newoperations, target_token)
  194. return (UnrollInfo(target_token, label_op, extra_same_as,
  195. self.optimizer.quasi_immutable_deps),
  196. self.optimizer._newoperations)
  197. def disable_retracing_if_max_retrace_guards(self, ops, target_token):
  198. maxguards = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.max_retrace_guards
  199. count = 0
  200. for op in ops:
  201. if op.is_guard():
  202. count += 1
  203. if count > maxguards:
  204. assert isinstance(target_token, TargetToken)
  205. target_token.targeting_jitcell_token.retraced_count = sys.maxint
  206. def pick_virtual_state(self, my_vs, label_vs, target_tokens):
  207. if target_tokens is None:
  208. return label_vs # for tests
  209. for token in target_tokens:
  210. if token.virtual_state is None:
  211. continue
  212. if token.virtual_state.generalization_of(my_vs, self.optimizer):
  213. return token.virtual_state
  214. return label_vs
  215. def optimize_bridge(self, trace, runtime_boxes, call_pure_results,
  216. inline_short_preamble, box_names_memo):
  217. trace = trace.get_iter()
  218. self._check_no_forwarding([trace.inputargs])
  219. info, ops = self.optimizer.propagate_all_forward(trace,
  220. call_pure_results, False)
  221. jump_op = info.jump_op
  222. cell_token = jump_op.getdescr()
  223. assert isinstance(cell_token, JitCellToken)
  224. if not inline_short_preamble or len(cell_token.target_tokens) == 1:
  225. return self.jump_to_preamble(cell_token, jump_op, info)
  226. # force all the information that does not go to the short
  227. # preamble at all
  228. self.optimizer.flush()
  229. for a in jump_op.getarglist():
  230. self.optimizer.force_box_for_end_of_preamble(a)
  231. try:
  232. vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes,
  233. force_boxes=False)
  234. except InvalidLoop:
  235. return self.jump_to_preamble(cell_token, jump_op, info)
  236. if vs is None:
  237. return info, self.optimizer._newoperations[:]
  238. warmrunnerdescr = self.optimizer.metainterp_sd.warmrunnerdesc
  239. limit = warmrunnerdescr.memory_manager.retrace_limit
  240. if cell_token.retraced_count < limit:
  241. cell_token.retraced_count += 1
  242. debug_print('Retracing (%d/%d)' % (cell_token.retraced_count, limit))
  243. else:
  244. # Try forcing boxes to avoid jumping to the preamble
  245. try:
  246. vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes,
  247. force_boxes=True)
  248. except InvalidLoop:
  249. pass
  250. if vs is None:
  251. return info, self.optimizer._newoperations[:]
  252. debug_print("Retrace count reached, jumping to preamble")
  253. return self.jump_to_preamble(cell_token, jump_op, info)
  254. exported_state = self.export_state(info.jump_op.getarglist(),
  255. info.inputargs, runtime_boxes,
  256. box_names_memo)
  257. exported_state.quasi_immutable_deps = self.optimizer.quasi_immutable_deps
  258. self.optimizer._clean_optimization_info(self.optimizer._newoperations)
  259. return exported_state, self.optimizer._newoperations
  260. def finalize_short_preamble(self, label_op, virtual_state):
  261. sb = self.short_preamble_producer
  262. self.optimizer._clean_optimization_info(sb.short_inputargs)
  263. short_preamble = sb.build_short_preamble()
  264. jitcelltoken = label_op.getdescr()
  265. assert isinstance(jitcelltoken, JitCellToken)
  266. if jitcelltoken.target_tokens is None:
  267. jitcelltoken.target_tokens = []
  268. target_token = TargetToken(jitcelltoken,
  269. original_jitcell_token=jitcelltoken)
  270. target_token.original_jitcell_token = jitcelltoken
  271. target_token.virtual_state = virtual_state
  272. target_token.short_preamble = short_preamble
  273. jitcelltoken.target_tokens.append(target_token)
  274. self.short_preamble_producer = ExtendedShortPreambleBuilder(
  275. target_token, sb)
  276. label_op.initarglist(label_op.getarglist() + sb.used_boxes)
  277. return target_token
  278. def jump_to_preamble(self, cell_token, jump_op, info):
  279. assert cell_token.target_tokens[0].virtual_state is None
  280. jump_op = jump_op.copy_and_change(rop.JUMP,
  281. descr=cell_token.target_tokens[0])
  282. self.optimizer.send_extra_operation(jump_op)
  283. return info, self.optimizer._newoperations[:]
  284. def jump_to_existing_trace(self, jump_op, label_op, runtime_boxes, force_boxes=False):
  285. jitcelltoken = jump_op.getdescr()
  286. assert isinstance(jitcelltoken, JitCellToken)
  287. virtual_state = self.get_virtual_state(jump_op.getarglist())
  288. args = [self.get_box_replacement(op) for op in jump_op.getarglist()]
  289. for target_token in jitcelltoken.target_tokens:
  290. target_virtual_state = target_token.virtual_state
  291. if target_virtual_state is None:
  292. continue
  293. try:
  294. extra_guards = target_virtual_state.generate_guards(
  295. virtual_state, args, runtime_boxes, self.optimizer,
  296. force_boxes=force_boxes)
  297. patchguardop = self.optimizer.patchguardop
  298. for guard in extra_guards.extra_guards:
  299. if isinstance(guard, GuardResOp):
  300. guard.rd_resume_position = patchguardop.rd_resume_position
  301. guard.setdescr(compile.ResumeAtPositionDescr())
  302. self.send_extra_operation(guard)
  303. except VirtualStatesCantMatch:
  304. continue
  305. # When force_boxes == True, creating the virtual args can fail when
  306. # components of the virtual state alias. If this occurs, we must
  307. # recompute the virtual state as boxes will have been forced.
  308. try:
  309. args, virtuals = target_virtual_state.make_inputargs_and_virtuals(
  310. args, self.optimizer, force_boxes=force_boxes)
  311. except VirtualStatesCantMatch:
  312. assert force_boxes
  313. virtual_state = self.get_virtual_state(args)
  314. continue
  315. short_preamble = target_token.short_preamble
  316. try:
  317. extra = self.inline_short_preamble(args + virtuals, args,
  318. short_preamble, self.optimizer.patchguardop,
  319. target_token, label_op)
  320. except KeyError:
  321. # SHOULD NOT OCCUR BUT DOES: WHY?? issue #2185
  322. self.optimizer.metainterp_sd.logger_ops.log_short_preamble([],
  323. short_preamble, {})
  324. raise
  325. self.send_extra_operation(jump_op.copy_and_change(rop.JUMP,
  326. args=args + extra,
  327. descr=target_token))
  328. return None # explicit because the return can be non-None
  329. return virtual_state
  330. def _map_args(self, mapping, arglist):
  331. result = []
  332. for box in arglist:
  333. if not isinstance(box, Const):
  334. box = mapping[box]
  335. result.append(box)
  336. return result
  337. def inline_short_preamble(self, jump_args, args_no_virtuals, short,
  338. patchguardop, target_token, label_op):
  339. short_inputargs = short[0].getarglist()
  340. short_jump_args = short[-1].getarglist()
  341. sb = self.short_preamble_producer
  342. if sb is not None:
  343. assert isinstance(sb, ExtendedShortPreambleBuilder)
  344. if sb.target_token is target_token:
  345. # this means we're inlining the short preamble that's being
  346. # built. Make sure we modify the correct things in-place
  347. self.short_preamble_producer.setup(short_jump_args,
  348. short, label_op.getarglist())
  349. # after this call, THE REST OF THIS FUNCTION WILL MODIFY ALL
  350. # THE LISTS PROVIDED, POTENTIALLY
  351. # We need to make a list of fresh new operations corresponding
  352. # to the short preamble operations. We could temporarily forward
  353. # the short operations to the fresh ones, but there are obscure
  354. # issues: send_extra_operation() below might occasionally invoke
  355. # use_box(), which assumes the short operations are not forwarded.
  356. # So we avoid such temporary forwarding and just use a dict here.
  357. assert len(short_inputargs) == len(jump_args)
  358. mapping = {}
  359. for i in range(len(jump_args)):
  360. mapping[short_inputargs[i]] = jump_args[i]
  361. # a fix-point loop, runs only once in almost all cases
  362. i = 1
  363. while 1:
  364. self._check_no_forwarding([short_inputargs, short], False)
  365. while i < len(short) - 1:
  366. sop = short[i]
  367. arglist = self._map_args(mapping, sop.getarglist())
  368. if sop.is_guard():
  369. op = sop.copy_and_change(sop.getopnum(), arglist,
  370. descr=compile.ResumeAtPositionDescr())
  371. assert isinstance(op, GuardResOp)
  372. op.rd_resume_position = patchguardop.rd_resume_position
  373. else:
  374. op = sop.copy_and_change(sop.getopnum(), arglist)
  375. mapping[sop] = op
  376. i += 1
  377. self.optimizer.send_extra_operation(op)
  378. # force all of them except the virtuals
  379. for arg in args_no_virtuals + short_jump_args:
  380. self.optimizer.force_box(self.get_box_replacement(arg))
  381. self.optimizer.flush()
  382. # done unless "short" has grown again
  383. if i == len(short) - 1:
  384. break
  385. return [self.get_box_replacement(box)
  386. for box in self._map_args(mapping, short_jump_args)]
  387. def _expand_info(self, arg, infos):
  388. if isinstance(arg, AbstractResOp) and rop.is_same_as(arg.opnum):
  389. info = self.optimizer.getinfo(arg.getarg(0))
  390. else:
  391. info = self.optimizer.getinfo(arg)
  392. if arg in infos:
  393. return
  394. if info:
  395. infos[arg] = info
  396. if info.is_virtual():
  397. self._expand_infos_from_virtual(info, infos)
  398. def _expand_infos_from_virtual(self, info, infos):
  399. items = info.all_items()
  400. for item in items:
  401. if item is None:
  402. continue
  403. self._expand_info(item, infos)
  404. def export_state(self, original_label_args, renamed_inputargs,
  405. runtime_boxes, memo):
  406. end_args = [self.optimizer.force_box_for_end_of_preamble(a)
  407. for a in original_label_args]
  408. self.optimizer.flush()
  409. virtual_state = self.get_virtual_state(end_args)
  410. end_args = [self.get_box_replacement(arg) for arg in end_args]
  411. infos = {}
  412. for arg in end_args:
  413. self._expand_info(arg, infos)
  414. label_args, virtuals = virtual_state.make_inputargs_and_virtuals(
  415. end_args, self.optimizer)
  416. for arg in label_args:
  417. self._expand_info(arg, infos)
  418. sb = ShortBoxes()
  419. short_boxes = sb.create_short_boxes(self.optimizer, renamed_inputargs,
  420. label_args + virtuals)
  421. short_inputargs = sb.create_short_inputargs(label_args + virtuals)
  422. for produced_op in short_boxes:
  423. op = produced_op.short_op.res
  424. if not isinstance(op, Const):
  425. self._expand_info(op, infos)
  426. self.optimizer._clean_optimization_info(end_args)
  427. return ExportedState(label_args, end_args, virtual_state, infos,
  428. short_boxes, renamed_inputargs,
  429. short_inputargs, runtime_boxes, memo)
  430. def import_state(self, targetargs, exported_state):
  431. # the mapping between input args (from old label) and what we need
  432. # to actually emit. Update the info
  433. assert (len(exported_state.next_iteration_args) ==
  434. len(targetargs))
  435. for i, target in enumerate(exported_state.next_iteration_args):
  436. source = targetargs[i]
  437. assert source is not target
  438. source.set_forwarded(target)
  439. info = exported_state.exported_infos.get(target, None)
  440. if info is not None:
  441. self.optimizer.setinfo_from_preamble(source, info,
  442. exported_state.exported_infos)
  443. # import the optimizer state, starting from boxes that can be produced
  444. # by short preamble
  445. label_args = exported_state.virtual_state.make_inputargs(
  446. targetargs, self.optimizer)
  447. self.short_preamble_producer = ShortPreambleBuilder(
  448. label_args, exported_state.short_boxes,
  449. exported_state.short_inputargs, exported_state.exported_infos,
  450. self.optimizer)
  451. for produced_op in exported_state.short_boxes:
  452. produced_op.produce_op(self, exported_state.exported_infos)
  453. return label_args
  454. class UnrollInfo(BasicLoopInfo):
  455. """ A state after optimizing the peeled loop, contains the following:
  456. * target_token - generated target token
  457. * label_args - label operations at the beginning
  458. * extra_same_as - list of extra same as to add at the end of the preamble
  459. """
  460. def __init__(self, target_token, label_op, extra_same_as,
  461. quasi_immutable_deps):
  462. self.target_token = target_token
  463. self.label_op = label_op
  464. self.extra_same_as = extra_same_as
  465. self.quasi_immutable_deps = quasi_immutable_deps
  466. def final(self):
  467. return True
  468. class ExportedState(LoopInfo):
  469. """ Exported state consists of a few pieces of information:
  470. * next_iteration_args - starting arguments for next iteration
  471. * exported_infos - a mapping from ops to infos, including inputargs
  472. * end_args - arguments that end up in the label leading to the next
  473. iteration
  474. * virtual_state - instance of VirtualState representing current state
  475. of virtuals at this label
  476. * short boxes - a mapping op -> preamble_op
  477. * renamed_inputargs - the start label arguments in optimized version
  478. * short_inputargs - the renamed inputargs for short preamble
  479. * quasi_immutable_deps - for tracking quasi immutables
  480. * runtime_boxes - runtime values for boxes, necessary when generating
  481. guards to jump to
  482. """
  483. def __init__(self, end_args, next_iteration_args, virtual_state,
  484. exported_infos, short_boxes, renamed_inputargs,
  485. short_inputargs, runtime_boxes, memo):
  486. self.end_args = end_args
  487. self.next_iteration_args = next_iteration_args
  488. self.virtual_state = virtual_state
  489. self.exported_infos = exported_infos
  490. self.short_boxes = short_boxes
  491. self.renamed_inputargs = renamed_inputargs
  492. self.short_inputargs = short_inputargs
  493. self.runtime_boxes = runtime_boxes
  494. self.dump(memo)
  495. def dump(self, memo):
  496. if have_debug_prints():
  497. debug_start("jit-log-exported-state")
  498. debug_print("[" + ", ".join([x.repr_short(memo) for x in self.next_iteration_args]) + "]")
  499. for box in self.short_boxes:
  500. debug_print(" " + box.repr(memo))
  501. debug_stop("jit-log-exported-state")
  502. def final(self):
  503. return False