PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://bitbucket.org/pypy/pypy/
Python | 523 lines | 512 code | 6 blank | 5 comment | 28 complexity | b70bfe030093da2d048833b3ce23f4d8 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  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(end_jump, label_op,
  172. state.runtime_boxes)
  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. self.jump_to_preamble(celltoken, end_jump, info)
  181. return (UnrollInfo(target_token, label_op, extra_same_as,
  182. self.optimizer.quasi_immutable_deps),
  183. self.optimizer._newoperations)
  184. self.disable_retracing_if_max_retrace_guards(
  185. self.optimizer._newoperations, target_token)
  186. return (UnrollInfo(target_token, label_op, extra_same_as,
  187. self.optimizer.quasi_immutable_deps),
  188. self.optimizer._newoperations)
  189. def disable_retracing_if_max_retrace_guards(self, ops, target_token):
  190. maxguards = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.max_retrace_guards
  191. count = 0
  192. for op in ops:
  193. if op.is_guard():
  194. count += 1
  195. if count > maxguards:
  196. assert isinstance(target_token, TargetToken)
  197. target_token.targeting_jitcell_token.retraced_count = sys.maxint
  198. def pick_virtual_state(self, my_vs, label_vs, target_tokens):
  199. if target_tokens is None:
  200. return label_vs # for tests
  201. for token in target_tokens:
  202. if token.virtual_state is None:
  203. continue
  204. if token.virtual_state.generalization_of(my_vs, self.optimizer):
  205. return token.virtual_state
  206. return label_vs
  207. def optimize_bridge(self, trace, runtime_boxes, call_pure_results,
  208. inline_short_preamble, box_names_memo):
  209. trace = trace.get_iter()
  210. self._check_no_forwarding([trace.inputargs])
  211. info, ops = self.optimizer.propagate_all_forward(trace,
  212. call_pure_results, False)
  213. jump_op = info.jump_op
  214. cell_token = jump_op.getdescr()
  215. assert isinstance(cell_token, JitCellToken)
  216. if not inline_short_preamble or len(cell_token.target_tokens) == 1:
  217. return self.jump_to_preamble(cell_token, jump_op, info)
  218. # force all the information that does not go to the short
  219. # preamble at all
  220. self.optimizer.flush()
  221. for a in jump_op.getarglist():
  222. self.optimizer.force_box_for_end_of_preamble(a)
  223. try:
  224. vs = self.jump_to_existing_trace(jump_op, None, runtime_boxes)
  225. except InvalidLoop:
  226. return self.jump_to_preamble(cell_token, jump_op, info)
  227. if vs is None:
  228. return info, self.optimizer._newoperations[:]
  229. warmrunnerdescr = self.optimizer.metainterp_sd.warmrunnerdesc
  230. limit = warmrunnerdescr.memory_manager.retrace_limit
  231. if cell_token.retraced_count < limit:
  232. cell_token.retraced_count += 1
  233. debug_print('Retracing (%d/%d)' % (cell_token.retraced_count, limit))
  234. else:
  235. debug_print("Retrace count reached, jumping to preamble")
  236. return self.jump_to_preamble(cell_token, jump_op, info)
  237. exported_state = self.export_state(info.jump_op.getarglist(),
  238. info.inputargs, runtime_boxes,
  239. box_names_memo)
  240. exported_state.quasi_immutable_deps = self.optimizer.quasi_immutable_deps
  241. self.optimizer._clean_optimization_info(self.optimizer._newoperations)
  242. return exported_state, self.optimizer._newoperations
  243. def finalize_short_preamble(self, label_op, virtual_state):
  244. sb = self.short_preamble_producer
  245. self.optimizer._clean_optimization_info(sb.short_inputargs)
  246. short_preamble = sb.build_short_preamble()
  247. jitcelltoken = label_op.getdescr()
  248. assert isinstance(jitcelltoken, JitCellToken)
  249. if jitcelltoken.target_tokens is None:
  250. jitcelltoken.target_tokens = []
  251. target_token = TargetToken(jitcelltoken,
  252. original_jitcell_token=jitcelltoken)
  253. target_token.original_jitcell_token = jitcelltoken
  254. target_token.virtual_state = virtual_state
  255. target_token.short_preamble = short_preamble
  256. jitcelltoken.target_tokens.append(target_token)
  257. self.short_preamble_producer = ExtendedShortPreambleBuilder(
  258. target_token, sb)
  259. label_op.initarglist(label_op.getarglist() + sb.used_boxes)
  260. return target_token
  261. def jump_to_preamble(self, cell_token, jump_op, info):
  262. assert cell_token.target_tokens[0].virtual_state is None
  263. jump_op = jump_op.copy_and_change(rop.JUMP,
  264. descr=cell_token.target_tokens[0])
  265. self.optimizer.send_extra_operation(jump_op)
  266. return info, self.optimizer._newoperations[:]
  267. def jump_to_existing_trace(self, jump_op, label_op, runtime_boxes):
  268. jitcelltoken = jump_op.getdescr()
  269. assert isinstance(jitcelltoken, JitCellToken)
  270. virtual_state = self.get_virtual_state(jump_op.getarglist())
  271. args = [self.get_box_replacement(op) for op in jump_op.getarglist()]
  272. for target_token in jitcelltoken.target_tokens:
  273. target_virtual_state = target_token.virtual_state
  274. if target_virtual_state is None:
  275. continue
  276. try:
  277. extra_guards = target_virtual_state.generate_guards(
  278. virtual_state, args, runtime_boxes, self.optimizer)
  279. patchguardop = self.optimizer.patchguardop
  280. for guard in extra_guards.extra_guards:
  281. if isinstance(guard, GuardResOp):
  282. guard.rd_resume_position = patchguardop.rd_resume_position
  283. guard.setdescr(compile.ResumeAtPositionDescr())
  284. self.send_extra_operation(guard)
  285. except VirtualStatesCantMatch:
  286. continue
  287. args, virtuals = target_virtual_state.make_inputargs_and_virtuals(
  288. args, self.optimizer)
  289. short_preamble = target_token.short_preamble
  290. try:
  291. extra = self.inline_short_preamble(args + virtuals, args,
  292. short_preamble, self.optimizer.patchguardop,
  293. target_token, label_op)
  294. except KeyError:
  295. # SHOULD NOT OCCUR BUT DOES: WHY?? issue #2185
  296. self.optimizer.metainterp_sd.logger_ops.log_short_preamble([],
  297. short_preamble, {})
  298. raise
  299. self.send_extra_operation(jump_op.copy_and_change(rop.JUMP,
  300. args=args + extra,
  301. descr=target_token))
  302. return None # explicit because the return can be non-None
  303. return virtual_state
  304. def _map_args(self, mapping, arglist):
  305. result = []
  306. for box in arglist:
  307. if not isinstance(box, Const):
  308. box = mapping[box]
  309. result.append(box)
  310. return result
  311. def inline_short_preamble(self, jump_args, args_no_virtuals, short,
  312. patchguardop, target_token, label_op):
  313. short_inputargs = short[0].getarglist()
  314. short_jump_args = short[-1].getarglist()
  315. sb = self.short_preamble_producer
  316. if sb is not None:
  317. assert isinstance(sb, ExtendedShortPreambleBuilder)
  318. if sb.target_token is target_token:
  319. # this means we're inlining the short preamble that's being
  320. # built. Make sure we modify the correct things in-place
  321. self.short_preamble_producer.setup(short_jump_args,
  322. short, label_op.getarglist())
  323. # after this call, THE REST OF THIS FUNCTION WILL MODIFY ALL
  324. # THE LISTS PROVIDED, POTENTIALLY
  325. # We need to make a list of fresh new operations corresponding
  326. # to the short preamble operations. We could temporarily forward
  327. # the short operations to the fresh ones, but there are obscure
  328. # issues: send_extra_operation() below might occasionally invoke
  329. # use_box(), which assumes the short operations are not forwarded.
  330. # So we avoid such temporary forwarding and just use a dict here.
  331. assert len(short_inputargs) == len(jump_args)
  332. mapping = {}
  333. for i in range(len(jump_args)):
  334. mapping[short_inputargs[i]] = jump_args[i]
  335. # a fix-point loop, runs only once in almost all cases
  336. i = 1
  337. while 1:
  338. self._check_no_forwarding([short_inputargs, short], False)
  339. while i < len(short) - 1:
  340. sop = short[i]
  341. arglist = self._map_args(mapping, sop.getarglist())
  342. if sop.is_guard():
  343. op = sop.copy_and_change(sop.getopnum(), arglist,
  344. descr=compile.ResumeAtPositionDescr())
  345. assert isinstance(op, GuardResOp)
  346. op.rd_resume_position = patchguardop.rd_resume_position
  347. else:
  348. op = sop.copy_and_change(sop.getopnum(), arglist)
  349. mapping[sop] = op
  350. i += 1
  351. self.optimizer.send_extra_operation(op)
  352. # force all of them except the virtuals
  353. for arg in args_no_virtuals + short_jump_args:
  354. self.optimizer.force_box(self.get_box_replacement(arg))
  355. self.optimizer.flush()
  356. # done unless "short" has grown again
  357. if i == len(short) - 1:
  358. break
  359. return [self.get_box_replacement(box)
  360. for box in self._map_args(mapping, short_jump_args)]
  361. def _expand_info(self, arg, infos):
  362. if isinstance(arg, AbstractResOp) and rop.is_same_as(arg.opnum):
  363. info = self.optimizer.getinfo(arg.getarg(0))
  364. else:
  365. info = self.optimizer.getinfo(arg)
  366. if arg in infos:
  367. return
  368. if info:
  369. infos[arg] = info
  370. if info.is_virtual():
  371. self._expand_infos_from_virtual(info, infos)
  372. def _expand_infos_from_virtual(self, info, infos):
  373. items = info.all_items()
  374. for item in items:
  375. if item is None:
  376. continue
  377. self._expand_info(item, infos)
  378. def export_state(self, original_label_args, renamed_inputargs,
  379. runtime_boxes, memo):
  380. end_args = [self.optimizer.force_box_for_end_of_preamble(a)
  381. for a in original_label_args]
  382. self.optimizer.flush()
  383. virtual_state = self.get_virtual_state(end_args)
  384. end_args = [self.get_box_replacement(arg) for arg in end_args]
  385. infos = {}
  386. for arg in end_args:
  387. self._expand_info(arg, infos)
  388. label_args, virtuals = virtual_state.make_inputargs_and_virtuals(
  389. end_args, self.optimizer)
  390. for arg in label_args:
  391. self._expand_info(arg, infos)
  392. sb = ShortBoxes()
  393. short_boxes = sb.create_short_boxes(self.optimizer, renamed_inputargs,
  394. label_args + virtuals)
  395. short_inputargs = sb.create_short_inputargs(label_args + virtuals)
  396. for produced_op in short_boxes:
  397. op = produced_op.short_op.res
  398. if not isinstance(op, Const):
  399. self._expand_info(op, infos)
  400. self.optimizer._clean_optimization_info(end_args)
  401. return ExportedState(label_args, end_args, virtual_state, infos,
  402. short_boxes, renamed_inputargs,
  403. short_inputargs, runtime_boxes, memo)
  404. def import_state(self, targetargs, exported_state):
  405. # the mapping between input args (from old label) and what we need
  406. # to actually emit. Update the info
  407. assert (len(exported_state.next_iteration_args) ==
  408. len(targetargs))
  409. for i, target in enumerate(exported_state.next_iteration_args):
  410. source = targetargs[i]
  411. assert source is not target
  412. source.set_forwarded(target)
  413. info = exported_state.exported_infos.get(target, None)
  414. if info is not None:
  415. self.optimizer.setinfo_from_preamble(source, info,
  416. exported_state.exported_infos)
  417. # import the optimizer state, starting from boxes that can be produced
  418. # by short preamble
  419. label_args = exported_state.virtual_state.make_inputargs(
  420. targetargs, self.optimizer)
  421. self.short_preamble_producer = ShortPreambleBuilder(
  422. label_args, exported_state.short_boxes,
  423. exported_state.short_inputargs, exported_state.exported_infos,
  424. self.optimizer)
  425. for produced_op in exported_state.short_boxes:
  426. produced_op.produce_op(self, exported_state.exported_infos)
  427. return label_args
  428. class UnrollInfo(BasicLoopInfo):
  429. """ A state after optimizing the peeled loop, contains the following:
  430. * target_token - generated target token
  431. * label_args - label operations at the beginning
  432. * extra_same_as - list of extra same as to add at the end of the preamble
  433. """
  434. def __init__(self, target_token, label_op, extra_same_as,
  435. quasi_immutable_deps):
  436. self.target_token = target_token
  437. self.label_op = label_op
  438. self.extra_same_as = extra_same_as
  439. self.quasi_immutable_deps = quasi_immutable_deps
  440. def final(self):
  441. return True
  442. class ExportedState(LoopInfo):
  443. """ Exported state consists of a few pieces of information:
  444. * next_iteration_args - starting arguments for next iteration
  445. * exported_infos - a mapping from ops to infos, including inputargs
  446. * end_args - arguments that end up in the label leading to the next
  447. iteration
  448. * virtual_state - instance of VirtualState representing current state
  449. of virtuals at this label
  450. * short boxes - a mapping op -> preamble_op
  451. * renamed_inputargs - the start label arguments in optimized version
  452. * short_inputargs - the renamed inputargs for short preamble
  453. * quasi_immutable_deps - for tracking quasi immutables
  454. * runtime_boxes - runtime values for boxes, necessary when generating
  455. guards to jump to
  456. """
  457. def __init__(self, end_args, next_iteration_args, virtual_state,
  458. exported_infos, short_boxes, renamed_inputargs,
  459. short_inputargs, runtime_boxes, memo):
  460. self.end_args = end_args
  461. self.next_iteration_args = next_iteration_args
  462. self.virtual_state = virtual_state
  463. self.exported_infos = exported_infos
  464. self.short_boxes = short_boxes
  465. self.renamed_inputargs = renamed_inputargs
  466. self.short_inputargs = short_inputargs
  467. self.runtime_boxes = runtime_boxes
  468. self.dump(memo)
  469. def dump(self, memo):
  470. if have_debug_prints():
  471. debug_start("jit-log-exported-state")
  472. debug_print("[" + ", ".join([x.repr_short(memo) for x in self.next_iteration_args]) + "]")
  473. for box in self.short_boxes:
  474. debug_print(" " + box.repr(memo))
  475. debug_stop("jit-log-exported-state")
  476. def final(self):
  477. return False