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

/rpython/jit/metainterp/optimizeopt/dependency.py

https://bitbucket.org/pypy/pypy/
Python | 1189 lines | 1173 code | 15 blank | 1 comment | 17 complexity | dbc9afa6433d591458530740e8b883c1 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. from rpython.jit.metainterp import compile
  3. from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
  4. from rpython.jit.metainterp.resoperation import (rop, GuardResOp, ResOperation)
  5. from rpython.jit.codewriter.effectinfo import EffectInfo
  6. from rpython.jit.metainterp.history import (ConstPtr, ConstInt,Const,
  7. AbstractValue, AbstractFailDescr)
  8. from rpython.rtyper.lltypesystem import llmemory
  9. from rpython.rlib.unroll import unrolling_iterable
  10. from rpython.rlib.objectmodel import we_are_translated
  11. MODIFY_COMPLEX_OBJ = [ (rop.SETARRAYITEM_GC, 0, 1)
  12. , (rop.SETARRAYITEM_RAW, 0, 1)
  13. , (rop.RAW_STORE, 0, 1)
  14. , (rop.SETINTERIORFIELD_GC, 0, -1)
  15. , (rop.SETINTERIORFIELD_RAW, 0, -1)
  16. , (rop.SETFIELD_GC, 0, -1)
  17. , (rop.SETFIELD_RAW, 0, -1)
  18. , (rop.ZERO_ARRAY, 0, -1)
  19. , (rop.STRSETITEM, 0, -1)
  20. , (rop.UNICODESETITEM, 0, -1)
  21. ]
  22. UNROLLED_MODIFY_COMPLEX_OBJ = unrolling_iterable(MODIFY_COMPLEX_OBJ)
  23. LOAD_COMPLEX_OBJ = [ (rop.GETARRAYITEM_GC_I, 0, 1)
  24. , (rop.GETARRAYITEM_GC_F, 0, 1)
  25. , (rop.GETARRAYITEM_GC_R, 0, 1)
  26. , (rop.GETARRAYITEM_RAW_I, 0, 1)
  27. , (rop.GETARRAYITEM_RAW_F, 0, 1)
  28. , (rop.RAW_LOAD_I, 0, 1)
  29. , (rop.RAW_LOAD_F, 0, 1)
  30. , (rop.GETINTERIORFIELD_GC_I, 0, 1)
  31. , (rop.GETINTERIORFIELD_GC_F, 0, 1)
  32. , (rop.GETINTERIORFIELD_GC_R, 0, 1)
  33. , (rop.GETFIELD_GC_I, 0, -1)
  34. , (rop.GETFIELD_GC_F, 0, -1)
  35. , (rop.GETFIELD_GC_R, 0, -1)
  36. , (rop.GETFIELD_RAW_I, 0, -1)
  37. , (rop.GETFIELD_RAW_F, 0, -1)
  38. , (rop.GETFIELD_RAW_R, 0, -1)
  39. ]
  40. UNROLLED_LOAD_COMPLEX_OBJ = unrolling_iterable(LOAD_COMPLEX_OBJ)
  41. class Path(object):
  42. def __init__(self,path):
  43. self.path = path
  44. def second(self):
  45. if len(self.path) <= 1:
  46. return None
  47. return self.path[1]
  48. def last_but_one(self):
  49. if len(self.path) < 2:
  50. return None
  51. return self.path[-2]
  52. def last(self):
  53. if len(self.path) < 1:
  54. return None
  55. return self.path[-1]
  56. def first(self):
  57. return self.path[0]
  58. def is_always_pure(self, exclude_first=False, exclude_last=False):
  59. last = len(self.path)-1
  60. count = len(self.path)
  61. i = 0
  62. if exclude_first:
  63. i += 1
  64. if exclude_last:
  65. count -= 1
  66. while i < count:
  67. node = self.path[i]
  68. if node.is_imaginary():
  69. i += 1
  70. continue
  71. op = node.getoperation()
  72. if rop.is_guard(op.opnum):
  73. descr = op.getdescr()
  74. if not descr:
  75. return False
  76. assert isinstance(descr, AbstractFailDescr)
  77. if not descr.exits_early():
  78. return False
  79. elif not rop.is_always_pure(op.opnum):
  80. return False
  81. i += 1
  82. return True
  83. def set_schedule_priority(self, p):
  84. for node in self.path:
  85. node.setpriority(p)
  86. def walk(self, node):
  87. self.path.append(node)
  88. def cut_off_at(self, index):
  89. self.path = self.path[:index]
  90. def check_acyclic(self):
  91. """NOT_RPYTHON"""
  92. seen = set()
  93. for segment in self.path:
  94. if segment in seen:
  95. print "path:"
  96. for segment in self.path:
  97. print " ->", segment
  98. print ""
  99. assert 0, "segment %s was already seen. this makes the path cyclic!" % segment
  100. else:
  101. seen.add(segment)
  102. return True
  103. def clone(self):
  104. return Path(self.path[:])
  105. def as_str(self):
  106. """ NOT_RPYTHON """
  107. return ' -> '.join([str(p) for p in self.path])
  108. class Node(object):
  109. def __init__(self, op, opidx):
  110. self.op = op
  111. self.opidx = opidx
  112. self.adjacent_list = []
  113. self.adjacent_list_back = []
  114. self.memory_ref = None
  115. self.pack = None
  116. self.pack_position = -1
  117. self.emitted = False
  118. self.schedule_position = -1
  119. self.priority = 0
  120. self._stack = False
  121. def is_imaginary(self):
  122. return False
  123. def getoperation(self):
  124. return self.op
  125. def getindex(self):
  126. return self.opidx
  127. def getopnum(self):
  128. return self.op.getopnum()
  129. def getopname(self):
  130. return self.op.getopname()
  131. def setpriority(self, value):
  132. self.priority = value
  133. def can_be_relaxed(self):
  134. return self.op.getopnum() in (rop.GUARD_TRUE, rop.GUARD_FALSE)
  135. def edge_to(self, to, arg=None, failarg=False, label=None):
  136. if self is to:
  137. return
  138. dep = self.depends_on(to)
  139. if not dep:
  140. #if force or self.independent(idx_from, idx_to):
  141. dep = Dependency(self, to, arg, failarg)
  142. self.adjacent_list.append(dep)
  143. dep_back = Dependency(to, self, arg, failarg)
  144. dep.backward = dep_back
  145. to.adjacent_list_back.append(dep_back)
  146. if not we_are_translated():
  147. if label is None:
  148. label = ''
  149. dep.label = label
  150. else:
  151. if not dep.because_of(arg):
  152. dep.add_dependency(self,to,arg)
  153. # if a fail argument is overwritten by another normal
  154. # dependency it will remove the failarg flag
  155. if not (dep.is_failarg() and failarg):
  156. dep.set_failarg(False)
  157. if not we_are_translated() and label is not None:
  158. _label = getattr(dep, 'label', '')
  159. dep.label = _label + ", " + label
  160. return dep
  161. def clear_dependencies(self):
  162. self.adjacent_list = []
  163. self.adjacent_list_back = []
  164. def exits_early(self):
  165. if self.op.is_guard():
  166. descr = self.op.getdescr()
  167. return descr.exits_early()
  168. return False
  169. def loads_from_complex_object(self):
  170. return rop._ALWAYS_PURE_LAST <= self.op.getopnum() < rop._MALLOC_FIRST
  171. def modifies_complex_object(self):
  172. return rop.SETARRAYITEM_GC <= self.op.getopnum() <= rop.UNICODESETITEM
  173. def side_effect_arguments(self):
  174. # if an item in array p0 is modified or a call contains an argument
  175. # it can modify it is returned in the destroyed list.
  176. args = []
  177. op = self.op
  178. if self.modifies_complex_object():
  179. for opnum, i, j in UNROLLED_MODIFY_COMPLEX_OBJ: #unrolling_iterable(MODIFY_COMPLEX_OBJ):
  180. if op.getopnum() == opnum:
  181. op_args = op.getarglist()
  182. if j == -1:
  183. args.append((op.getarg(i), None, True))
  184. for j in range(i+1,len(op_args)):
  185. args.append((op.getarg(j), None, False))
  186. else:
  187. args.append((op.getarg(i), op.getarg(j), True))
  188. for x in range(j+1,len(op_args)):
  189. args.append((op.getarg(x), None, False))
  190. return args
  191. # assume this destroys every argument... can be enhanced by looking
  192. # at the effect info of a call for instance
  193. for arg in op.getarglist():
  194. # if it is a constant argument it cannot be destroyed.
  195. # neither can a box float be destroyed. BoxInt can
  196. # contain a reference thus it is assumed to be destroyed
  197. if arg.is_constant() or arg.type == 'f':
  198. args.append((arg, None, False))
  199. else:
  200. args.append((arg, None, True))
  201. return args
  202. def provides_count(self):
  203. return len(self.adjacent_list)
  204. def provides(self):
  205. return self.adjacent_list
  206. def depends_count(self):
  207. return len(self.adjacent_list_back)
  208. def depends(self):
  209. return self.adjacent_list_back
  210. def depends_on(self, to):
  211. """ Does there exist a dependency from the instruction to another?
  212. Returns None if there is no dependency or the Dependency object in
  213. any other case.
  214. """
  215. for edge in self.adjacent_list:
  216. if edge.to is to:
  217. return edge
  218. return None
  219. def dependencies(self):
  220. return self.adjacent_list[:] + self.adjacent_list_back[:] # COPY
  221. def is_after(self, other):
  222. return self.opidx > other.opidx
  223. def is_before(self, other):
  224. return self.opidx < other.opidx
  225. def independent(self, other):
  226. """ An instruction depends on another if there is a path from
  227. self to other. """
  228. if self == other:
  229. return True
  230. # forward
  231. worklist = [self]
  232. while len(worklist) > 0:
  233. node = worklist.pop()
  234. for dep in node.provides():
  235. if dep.to.is_after(other):
  236. continue
  237. if dep.points_to(other):
  238. # dependent. There is a path from self to other
  239. return False
  240. worklist.append(dep.to)
  241. # backward
  242. worklist = [self]
  243. while len(worklist) > 0:
  244. node = worklist.pop()
  245. for dep in node.depends():
  246. if dep.to.is_before(other):
  247. continue
  248. if dep.points_to(other):
  249. # dependent. There is a path from self to other
  250. return False
  251. worklist.append(dep.to)
  252. return True
  253. def iterate_paths(self, to, backwards=False, path_max_len=-1, blacklist=False):
  254. """ Yield all nodes from self leading to 'to'.
  255. backwards: Determines the iteration direction.
  256. blacklist: Marks nodes that have already been visited.
  257. It comes in handy if a property must hold for every path.
  258. Not *every* possible instance must be iterated, but trees
  259. that have already been visited can be ignored after the
  260. first visit.
  261. """
  262. if self is to:
  263. return
  264. blacklist_visit = {}
  265. path = Path([self])
  266. worklist = [(0, self, 1)]
  267. while len(worklist) > 0:
  268. index,node,pathlen = worklist.pop()
  269. if backwards:
  270. iterdir = node.depends()
  271. else:
  272. iterdir = node.provides()
  273. if index >= len(iterdir):
  274. if to is None and index == 0:
  275. yield Path(path.path[:])
  276. if blacklist:
  277. blacklist_visit[node] = None
  278. continue
  279. else:
  280. next_dep = iterdir[index]
  281. next_node = next_dep.to
  282. index += 1
  283. if index < len(iterdir):
  284. worklist.append((index, node, pathlen))
  285. else:
  286. blacklist_visit[node] = None
  287. path.cut_off_at(pathlen)
  288. path.walk(next_node)
  289. if blacklist and next_node in blacklist_visit:
  290. yield Path(path.path[:])
  291. continue
  292. pathlen += 1
  293. if next_node is to or \
  294. (path_max_len > 0 and pathlen >= path_max_len):
  295. yield Path(path.path[:])
  296. # note that the destiantion node ``to'' is never blacklisted
  297. #if blacklist:
  298. # blacklist_visit[next_node] = None
  299. else:
  300. worklist.append((0, next_node, pathlen))
  301. def remove_edge_to(self, node):
  302. i = 0
  303. while i < len(self.adjacent_list):
  304. dep = self.adjacent_list[i]
  305. if dep.to is node:
  306. del self.adjacent_list[i]
  307. break
  308. i += 1
  309. i = 0
  310. while i < len(node.adjacent_list_back):
  311. dep = node.adjacent_list_back[i]
  312. if dep.to is self:
  313. del node.adjacent_list_back[i]
  314. break
  315. i += 1
  316. def getedge_to(self, other):
  317. for dep in self.adjacent_list:
  318. if dep.to == other:
  319. return dep
  320. return None
  321. def __repr__(self):
  322. pack = ''
  323. if self.pack:
  324. pack = "p: %d" % self.pack.numops()
  325. return "Node(%s,%s i: %d)" % (self.op, pack, self.opidx)
  326. def getdotlabel(self):
  327. """ NOT_RPTYHON """
  328. op_str = str(self.op)
  329. if self.op.is_guard():
  330. args_str = []
  331. for arg in self.op.getfailargs():
  332. name = 'None'
  333. if arg:
  334. name = arg.repr_short(arg._repr_memo)
  335. args_str.append(name)
  336. op_str += " " + ','.join(args_str)
  337. return "[%d] %s" % (self.opidx, op_str)
  338. class ImaginaryNode(Node):
  339. _index = 987654321 # big enough? :)
  340. def __init__(self, label):
  341. index = -1
  342. if not we_are_translated():
  343. self.dotlabel = label
  344. index = ImaginaryNode._index
  345. ImaginaryNode._index += 1
  346. Node.__init__(self, None, index)
  347. def is_imaginary(self):
  348. return True
  349. def getdotlabel(self):
  350. """ NOT_RPTYHON """
  351. return self.dotlabel
  352. class Dependency(object):
  353. def __init__(self, at, to, arg, failarg=False):
  354. assert at != to
  355. self.args = []
  356. if arg is not None:
  357. self.add_dependency(at, to, arg)
  358. self.at = at
  359. self.to = to
  360. self.failarg = failarg
  361. self.backward = None
  362. def because_of(self, var):
  363. for arg in self.args:
  364. if arg[1] == var:
  365. return True
  366. return False
  367. def target_node(self):
  368. return self.to
  369. def origin_node(self):
  370. return self.at
  371. def to_index(self):
  372. return self.to.getindex()
  373. def at_index(self):
  374. return self.at.getindex()
  375. def points_after_to(self, to):
  376. return self.to.opidx < to.opidx
  377. def points_above_at(self, at):
  378. return self.at.opidx < at.opidx
  379. def i_points_above_at(self, idx):
  380. return self.at.opidx < idx
  381. def points_to(self, to):
  382. return self.to == to
  383. def points_at(self, at):
  384. return self.at == at
  385. def add_dependency(self, at, to, arg):
  386. self.args.append((at,arg))
  387. def set_failarg(self, value):
  388. self.failarg = value
  389. if self.backward:
  390. self.backward.failarg = value
  391. def is_failarg(self):
  392. return self.failarg
  393. def reverse_direction(self, ref):
  394. """ if the parameter index is the same as idx_to then
  395. this edge is in reverse direction.
  396. """
  397. return self.to == ref
  398. def __repr__(self):
  399. return 'Dep(T[%d] -> T[%d], arg: %s)' \
  400. % (self.at.opidx, self.to.opidx, self.args)
  401. class DefTracker(object):
  402. def __init__(self, graph):
  403. self.graph = graph
  404. self.defs = {}
  405. self.non_pure = []
  406. def add_non_pure(self, node):
  407. self.non_pure.append(node)
  408. def define(self, arg, node, argcell=None):
  409. if isinstance(arg, Const):
  410. return
  411. if arg in self.defs:
  412. self.defs[arg].append((node,argcell))
  413. else:
  414. self.defs[arg] = [(node,argcell)]
  415. def redefinitions(self, arg):
  416. for _def in self.defs[arg]:
  417. yield _def[0]
  418. def is_defined(self, arg):
  419. return arg in self.defs
  420. def definition(self, arg, node=None, argcell=None):
  421. if arg.is_constant():
  422. return None
  423. def_chain = self.defs.get(arg,None)
  424. if not def_chain:
  425. return None
  426. if not argcell:
  427. return def_chain[-1][0]
  428. else:
  429. assert node is not None
  430. i = len(def_chain)-1
  431. try:
  432. mref = node.memory_ref
  433. while i >= 0:
  434. def_node = def_chain[i][0]
  435. oref = def_node.memory_ref
  436. if oref is not None and mref.alias(oref):
  437. return def_node
  438. elif oref is None:
  439. return def_node
  440. i -= 1
  441. return None
  442. except KeyError:
  443. # when a key error is raised, this means
  444. # no information is available, safe default
  445. pass
  446. return def_chain[-1][0]
  447. def depends_on_arg(self, arg, to, argcell=None):
  448. try:
  449. at = self.definition(arg, to, argcell)
  450. if at is None:
  451. return
  452. at.edge_to(to, arg)
  453. except KeyError:
  454. if not we_are_translated():
  455. if not isinstance(arg, Const):
  456. assert False, "arg %s must be defined" % arg
  457. class DependencyGraph(object):
  458. """ A graph that represents one of the following dependencies:
  459. * True dependency
  460. * Anti dependency (not present in SSA traces)
  461. * Ouput dependency (not present in SSA traces)
  462. Traces in RPython are not in SSA form when it comes to complex
  463. object modification such as array or object side effects.
  464. Representation is an adjacent list. The number of edges between the
  465. vertices is expected to be small.
  466. Note that adjacent lists order their dependencies. They are ordered
  467. by the target instruction they point to if the instruction is
  468. a dependency.
  469. memory_refs: a dict that contains indices of memory references
  470. (load,store,getarrayitem,...). If none provided, the construction
  471. is conservative. It will never dismiss dependencies of two
  472. modifications of one array even if the indices can never point to
  473. the same element.
  474. """
  475. def __init__(self, loop):
  476. self.loop = loop
  477. self.label = Node(loop.label, 0)
  478. self.nodes = [ Node(op,0) for op in loop.operations if not rop.is_jit_debug(op.opnum) ]
  479. for i,node in enumerate(self.nodes):
  480. node.opidx = i+1
  481. self.inodes = [] # imaginary nodes
  482. self.jump = Node(loop.jump, len(self.nodes)+1)
  483. self.invariant_vars = {}
  484. self.update_invariant_vars()
  485. self.memory_refs = {}
  486. self.schedulable_nodes = []
  487. self.index_vars = {}
  488. self.comparison_vars = {}
  489. self.guards = []
  490. self.build_dependencies()
  491. def getnode(self, i):
  492. return self.nodes[i]
  493. def imaginary_node(self, label):
  494. node = ImaginaryNode(label)
  495. self.inodes.append(node)
  496. return node
  497. def update_invariant_vars(self):
  498. label_op = self.label.getoperation()
  499. jump_op = self.jump.getoperation()
  500. assert label_op.numargs() == jump_op.numargs()
  501. for i in range(label_op.numargs()):
  502. label_box = label_op.getarg(i)
  503. jump_box = jump_op.getarg(i)
  504. if label_box == jump_box:
  505. self.invariant_vars[label_box] = None
  506. def box_is_invariant(self, box):
  507. return box in self.invariant_vars
  508. def build_dependencies(self):
  509. """ This is basically building the definition-use chain and saving this
  510. information in a graph structure. This is the same as calculating
  511. the reaching definitions and the 'looking back' whenever it is used.
  512. Write After Read, Write After Write dependencies are not possible,
  513. the operations are in SSA form
  514. """
  515. tracker = DefTracker(self)
  516. #
  517. label_pos = 0
  518. jump_pos = len(self.nodes)-1
  519. intformod = IntegralForwardModification(self.memory_refs, self.index_vars,
  520. self.comparison_vars, self.invariant_vars)
  521. # pass 1
  522. for i,node in enumerate(self.nodes):
  523. op = node.op
  524. if rop.is_always_pure(op.opnum):
  525. node.setpriority(1)
  526. if rop.is_guard(op.opnum):
  527. node.setpriority(2)
  528. # the label operation defines all operations at the
  529. # beginning of the loop
  530. intformod.inspect_operation(op,node)
  531. # definition of a new variable
  532. if op.type != 'v':
  533. # In SSA form. Modifications get a new variable
  534. tracker.define(op, node)
  535. # usage of defined variables
  536. if rop.is_always_pure(op.opnum) or rop.is_final(op.opnum):
  537. # normal case every arguments definition is set
  538. for arg in op.getarglist():
  539. tracker.depends_on_arg(arg, node)
  540. elif rop.is_guard(op.opnum):
  541. if node.exits_early():
  542. pass
  543. else:
  544. # consider cross iterations?
  545. if len(self.guards) > 0:
  546. last_guard = self.guards[-1]
  547. last_guard.edge_to(node, failarg=True, label="guardorder")
  548. for nonpure in tracker.non_pure:
  549. nonpure.edge_to(node, failarg=True, label="nonpure")
  550. tracker.non_pure = []
  551. self.guards.append(node)
  552. self.build_guard_dependencies(node, tracker)
  553. else:
  554. self.build_non_pure_dependencies(node, tracker)
  555. def guard_argument_protection(self, guard_node, tracker):
  556. """ the parameters the guard protects are an indicator for
  557. dependencies. Consider the example:
  558. i3 = ptr_eq(p1,p2)
  559. guard_true(i3) [...]
  560. guard_true|false are exceptions because they do not directly
  561. protect the arguments, but a comparison function does.
  562. """
  563. guard_op = guard_node.getoperation()
  564. guard_opnum = guard_op.getopnum()
  565. for arg in guard_op.getarglist():
  566. if not arg.is_constant() and arg.type not in ('i','f'):
  567. # redefine pointers, consider the following example
  568. # guard_nonnull(r1)
  569. # i1 = getfield(r1, ...)
  570. # guard must be emitted before the getfield, thus
  571. # redefine r1 at guard_nonnull
  572. tracker.define(arg, guard_node)
  573. if guard_opnum == rop.GUARD_NOT_FORCED_2:
  574. # must be emitted before finish, thus delayed the longest
  575. guard_node.setpriority(-10)
  576. elif guard_opnum in (rop.GUARD_OVERFLOW, rop.GUARD_NO_OVERFLOW):
  577. # previous operation must be an ovf_operation
  578. guard_node.setpriority(100)
  579. i = guard_node.getindex()-1
  580. while i >= 0:
  581. node = self.nodes[i]
  582. op = node.getoperation()
  583. if op.is_ovf():
  584. break
  585. i -= 1
  586. else:
  587. raise AssertionError("(no)overflow: no overflowing op present")
  588. node.edge_to(guard_node, None, label='overflow')
  589. elif guard_opnum in (rop.GUARD_NO_EXCEPTION, rop.GUARD_EXCEPTION, rop.GUARD_NOT_FORCED):
  590. # previous op must be one that can raise or a not forced guard
  591. guard_node.setpriority(100)
  592. i = guard_node.getindex() - 1
  593. while i >= 0:
  594. node = self.nodes[i]
  595. op = node.getoperation()
  596. if op.can_raise():
  597. node.edge_to(guard_node, None, label='exception/notforced')
  598. break
  599. if op.is_guard():
  600. node.edge_to(guard_node, None, label='exception/notforced')
  601. break
  602. i -= 1
  603. else:
  604. raise AssertionError("(no)exception/not_forced: not op raises for them")
  605. else:
  606. pass # not invalidated, future condition!
  607. def guard_exit_dependence(self, guard_node, var, tracker):
  608. def_node = tracker.definition(var)
  609. if def_node is None:
  610. return
  611. for dep in def_node.provides():
  612. if guard_node.is_before(dep.to) and dep.because_of(var):
  613. guard_node.edge_to(dep.to, var, label='guard_exit('+str(var)+')')
  614. def build_guard_dependencies(self, guard_node, tracker):
  615. guard_op = guard_node.op
  616. if guard_op.getopnum() >= rop.GUARD_FUTURE_CONDITION:
  617. # ignore invalidated & future condition guard & early exit
  618. return
  619. # true dependencies
  620. for arg in guard_op.getarglist():
  621. tracker.depends_on_arg(arg, guard_node)
  622. # dependencies to uses of arguments it protects
  623. self.guard_argument_protection(guard_node, tracker)
  624. #
  625. descr = guard_op.getdescr()
  626. if descr.exits_early():
  627. return
  628. # handle fail args
  629. if guard_op.getfailargs():
  630. for i,arg in enumerate(guard_op.getfailargs()):
  631. if arg is None:
  632. continue
  633. if not tracker.is_defined(arg):
  634. continue
  635. try:
  636. for at in tracker.redefinitions(arg):
  637. # later redefinitions are prohibited
  638. if at.is_before(guard_node):
  639. at.edge_to(guard_node, arg, failarg=True, label="fail")
  640. except KeyError:
  641. assert False
  642. def build_non_pure_dependencies(self, node, tracker):
  643. op = node.op
  644. if node.loads_from_complex_object():
  645. # If this complex object load operation loads an index that has been
  646. # modified, the last modification should be used to put a def-use edge.
  647. for opnum, i, j in UNROLLED_LOAD_COMPLEX_OBJ:
  648. if opnum == op.getopnum():
  649. cobj = op.getarg(i)
  650. if j != -1:
  651. index_var = op.getarg(j)
  652. tracker.depends_on_arg(cobj, node, index_var)
  653. tracker.depends_on_arg(index_var, node)
  654. else:
  655. tracker.depends_on_arg(cobj, node)
  656. break
  657. else:
  658. for arg, argcell, destroyed in node.side_effect_arguments():
  659. if argcell is not None:
  660. # tracks the exact cell that is modified
  661. tracker.depends_on_arg(arg, node, argcell)
  662. tracker.depends_on_arg(argcell, node)
  663. else:
  664. if destroyed:
  665. # cannot be sure that only a one cell is modified
  666. # assume all cells are (equivalent to a redefinition)
  667. try:
  668. # A trace is not entirely in SSA form. complex object
  669. # modification introduces WAR/WAW dependencies
  670. def_node = tracker.definition(arg)
  671. if def_node:
  672. for dep in def_node.provides():
  673. if dep.to != node:
  674. dep.to.edge_to(node, argcell, label='war')
  675. def_node.edge_to(node, argcell)
  676. except KeyError:
  677. pass
  678. else:
  679. # not destroyed, just a normal use of arg
  680. tracker.depends_on_arg(arg, node)
  681. if destroyed:
  682. tracker.define(arg, node, argcell=argcell)
  683. # it must be assumed that a side effect operation must not be executed
  684. # before the last guard operation
  685. if len(self.guards) > 0:
  686. last_guard = self.guards[-1]
  687. last_guard.edge_to(node, label="sideeffect")
  688. # and the next guard instruction
  689. tracker.add_non_pure(node)
  690. def cycles(self):
  691. """ NOT_RPYTHON """
  692. stack = []
  693. for node in self.nodes:
  694. node._stack = False
  695. #
  696. label = self.nodes[0]
  697. if _first_cycle(stack, label):
  698. return stack
  699. return None
  700. def __repr__(self):
  701. graph = "graph([\n"
  702. for node in self.nodes:
  703. graph += " " + str(node.opidx) + ": "
  704. for dep in node.provides():
  705. graph += "=>" + str(dep.to.opidx) + ","
  706. graph += " | "
  707. for dep in node.depends():
  708. graph += "<=" + str(dep.to.opidx) + ","
  709. graph += "\n"
  710. return graph + " ])"
  711. def view(self):
  712. """ NOT_RPYTHON """
  713. from rpython.translator.tool.graphpage import GraphPage
  714. page = GraphPage()
  715. page.source = self.as_dot()
  716. page.links = []
  717. page.display()
  718. def as_dot(self):
  719. """ NOT_RPTYHON """
  720. if not we_are_translated():
  721. dot = "digraph dep_graph {\n"
  722. for node in self.nodes + self.inodes:
  723. dot += " n%d [label=\"%s\"];\n" % (node.getindex(),node.getdotlabel())
  724. dot += "\n"
  725. for node in self.nodes + self.inodes:
  726. for dep in node.provides():
  727. label = ''
  728. if getattr(dep, 'label', None):
  729. label = '[label="%s"]' % dep.label
  730. dot += " n%d -> n%d %s;\n" % (node.getindex(),dep.to_index(),label)
  731. dot += "\n}\n"
  732. return dot
  733. raise NotImplementedError("dot only for debug purpose")
  734. def _first_cycle(stack, node):
  735. node._stack = True
  736. stack.append(node)
  737. for dep in node.provides():
  738. succ = dep.to
  739. if succ._stack:
  740. # found cycle!
  741. while stack[0] is not succ:
  742. del stack[0]
  743. return True
  744. else:
  745. return _first_cycle(stack, succ)
  746. return False
  747. def _strongly_connect(index, stack, cycles, node):
  748. """ currently unused """
  749. node._scc_index = index
  750. node._scc_lowlink = index
  751. index += 1
  752. stack.append(node)
  753. node._scc_stack = True
  754. for dep in node.provides():
  755. succ = dep.to
  756. if succ._scc_index == -1:
  757. index = _strongly_connect(index, stack, cycles, succ)
  758. node._scc_lowlink = min(node._scc_lowlink, succ._scc_lowlink)
  759. elif succ._scc_stack:
  760. node._scc_lowlink = min(node._scc_lowlink, succ._scc_index)
  761. if node._scc_lowlink == node._scc_index:
  762. cycle = []
  763. while True:
  764. w = stack.pop()
  765. w._scc_stack = False
  766. cycle.append(w)
  767. if w is node:
  768. break
  769. cycles.append(cycle)
  770. return index
  771. class IntegralForwardModification(object):
  772. """ Calculates integral modifications on integer boxes. """
  773. def __init__(self, memory_refs, index_vars, comparison_vars, invariant_vars):
  774. self.index_vars = index_vars
  775. self.comparison_vars = comparison_vars
  776. self.memory_refs = memory_refs
  777. self.invariant_vars = invariant_vars
  778. def is_const_integral(self, box):
  779. if isinstance(box, ConstInt):
  780. return True
  781. return False
  782. def get_or_create(self, arg):
  783. var = self.index_vars.get(arg, None)
  784. if not var:
  785. var = self.index_vars[arg] = IndexVar(arg)
  786. return var
  787. additive_func_source = """
  788. def operation_{name}(self, op, node):
  789. box_r = op
  790. box_a0 = op.getarg(0)
  791. box_a1 = op.getarg(1)
  792. if self.is_const_integral(box_a0) and self.is_const_integral(box_a1):
  793. idx_ref = IndexVar(box_r)
  794. idx_ref.constant = box_a0.getint() {op} box_a1.getint()
  795. self.index_vars[box_r] = idx_ref
  796. elif self.is_const_integral(box_a0):
  797. idx_ref = self.get_or_create(box_a1)
  798. idx_ref = idx_ref.clone()
  799. idx_ref.constant {op}= box_a0.getint()
  800. self.index_vars[box_r] = idx_ref
  801. elif self.is_const_integral(box_a1):
  802. idx_ref = self.get_or_create(box_a0)
  803. idx_ref = idx_ref.clone()
  804. idx_ref.constant {op}= box_a1.getint()
  805. self.index_vars[box_r] = idx_ref
  806. """
  807. exec py.code.Source(additive_func_source
  808. .format(name='INT_ADD', op='+')).compile()
  809. exec py.code.Source(additive_func_source
  810. .format(name='INT_SUB', op='-')).compile()
  811. del additive_func_source
  812. multiplicative_func_source = """
  813. def operation_{name}(self, op, node):
  814. box_r = op
  815. if not box_r:
  816. return
  817. box_a0 = op.getarg(0)
  818. box_a1 = op.getarg(1)
  819. if self.is_const_integral(box_a0) and self.is_const_integral(box_a1):
  820. idx_ref = IndexVar(box_r)
  821. idx_ref.constant = box_a0.getint() {cop} box_a1.getint()
  822. self.index_vars[box_r] = idx_ref
  823. elif self.is_const_integral(box_a0):
  824. idx_ref = self.get_or_create(box_a1)
  825. idx_ref = idx_ref.clone()
  826. idx_ref.coefficient_{tgt} *= box_a0.getint()
  827. idx_ref.constant {cop}= box_a0.getint()
  828. self.index_vars[box_r] = idx_ref
  829. elif self.is_const_integral(box_a1):
  830. idx_ref = self.get_or_create(box_a0)
  831. idx_ref = idx_ref.clone()
  832. idx_ref.coefficient_{tgt} {op}= box_a1.getint()
  833. idx_ref.constant {cop}= box_a1.getint()
  834. self.index_vars[box_r] = idx_ref
  835. """
  836. exec py.code.Source(multiplicative_func_source
  837. .format(name='INT_MUL', op='*', tgt='mul', cop='*')).compile()
  838. #exec py.code.Source(multiplicative_func_source
  839. # .format(name='INT_PY_DIV', op='*', tgt='div', cop='/')).compile()
  840. #exec py.code.Source(multiplicative_func_source
  841. # .format(name='UINT_FLOORDIV', op='*', tgt='div', cop='/')).compile()
  842. del multiplicative_func_source
  843. array_access_source = """
  844. def operation_{name}(self, op, node):
  845. descr = op.getdescr()
  846. idx_ref = self.get_or_create(op.getarg(1))
  847. if descr and descr.is_array_of_primitives():
  848. node.memory_ref = MemoryRef(op, idx_ref, {raw_access})
  849. self.memory_refs[node] = node.memory_ref
  850. """
  851. exec py.code.Source(array_access_source
  852. .format(name='RAW_LOAD_I',raw_access=True)).compile()
  853. exec py.code.Source(array_access_source
  854. .format(name='RAW_LOAD_F',raw_access=True)).compile()
  855. exec py.code.Source(array_access_source
  856. .format(name='RAW_STORE',raw_access=True)).compile()
  857. exec py.code.Source(array_access_source
  858. .format(name='GETARRAYITEM_RAW_I',raw_access=False)).compile()
  859. exec py.code.Source(array_access_source
  860. .format(name='GETARRAYITEM_RAW_F',raw_access=False)).compile()
  861. exec py.code.Source(array_access_source
  862. .format(name='SETARRAYITEM_RAW',raw_access=False)).compile()
  863. exec py.code.Source(array_access_source
  864. .format(name='GETARRAYITEM_GC_I',raw_access=False)).compile()
  865. exec py.code.Source(array_access_source
  866. .format(name='GETARRAYITEM_GC_F',raw_access=False)).compile()
  867. exec py.code.Source(array_access_source
  868. .format(name='SETARRAYITEM_GC',raw_access=False)).compile()
  869. del array_access_source
  870. integral_dispatch_opt = make_dispatcher_method(IntegralForwardModification, 'operation_')
  871. IntegralForwardModification.inspect_operation = integral_dispatch_opt
  872. del integral_dispatch_opt
  873. class IndexVar(AbstractValue):
  874. """ IndexVar is an AbstractValue only to ensure that a box can be assigned
  875. to the same variable as an index var.
  876. """
  877. def __init__(self, var, coeff_mul=1, coeff_div=1, constant=0):
  878. self.var = var
  879. self.coefficient_mul = coeff_mul
  880. self.coefficient_div = coeff_div
  881. self.constant = constant
  882. # saves the next modification that uses a variable
  883. self.next_nonconst = None
  884. self.current_end = None
  885. def stride_const(self):
  886. return self.next_nonconst is None
  887. def add_const(self, number):
  888. if self.current_end is None:
  889. self.constant += number
  890. else:
  891. self.current_end.constant += number
  892. def set_next_nonconst_mod(self, idxvar):
  893. if self.current_end is None:
  894. self.next_nonconst = idxvar
  895. else:
  896. self.current_end.next_nonconst = idxvar
  897. self.current_end = idxvar
  898. def getvariable(self):
  899. return self.var
  900. def is_identity(self):
  901. return self.coefficient_mul == 1 and \
  902. self.coefficient_div == 1 and \
  903. self.constant == 0
  904. def clone(self):
  905. c = IndexVar(self.var)
  906. c.coefficient_mul = self.coefficient_mul
  907. c.coefficient_div = self.coefficient_div
  908. c.constant = self.constant
  909. return c
  910. def same_variable(self, other):
  911. assert isinstance(other, IndexVar)
  912. return other.var == self.var
  913. def same_mulfactor(self, other):
  914. coeff = self.coefficient_mul == other.coefficient_mul
  915. coeff = coeff and (self.coefficient_div == other.coefficient_div)
  916. if not coeff:
  917. # if not equal, try to check if they divide without rest
  918. selfmod = self.coefficient_mul % self.coefficient_div
  919. othermod = other.coefficient_mul % other.coefficient_div
  920. if selfmod == 0 and othermod == 0:
  921. # yet another chance for them to be equal
  922. selfdiv = self.coefficient_mul // self.coefficient_div
  923. otherdiv = other.coefficient_mul // other.coefficient_div
  924. coeff = selfdiv == otherdiv
  925. return coeff
  926. def constant_diff(self, other):
  927. """ calculates the difference as a second parameter """
  928. assert isinstance(other, IndexVar)
  929. return self.constant - other.constant
  930. def emit_operations(self, opt, result_box=None):
  931. var = self.var
  932. if self.is_identity():
  933. return var
  934. if self.coefficient_mul != 1:
  935. args = [var, ConstInt(self.coefficient_mul)]
  936. var = ResOperation(rop.INT_MUL, args)
  937. opt.emit_operation(var)
  938. if self.coefficient_div != 1:
  939. assert 0 # XXX for now; should never be the case with handling
  940. # of INT_PY_DIV commented out in this file...
  941. #args = [var, ConstInt(self.coefficient_div)]
  942. #var = ResOperation(rop.INT_FLOORDIV, args)
  943. #opt.emit_operation(var)
  944. if self.constant > 0:
  945. args = [var, ConstInt(self.constant)]
  946. var = ResOperation(rop.INT_ADD, args)
  947. opt.emit_operation(var)
  948. if self.constant < 0:
  949. args = [var, ConstInt(self.constant)]
  950. var = ResOperation(rop.INT_SUB, args)
  951. opt.emit_operation(var)
  952. return var
  953. def compare(self, other):
  954. """ Returns if the two are compareable as a first result
  955. and a number (-1,0,1) of the ordering
  956. """
  957. coeff = self.coefficient_mul == other.coefficient_mul
  958. coeff = coeff and (self.coefficient_div == other.coefficient_div)
  959. if not coeff:
  960. # if not equal, try to check if they divide without rest
  961. selfmod = self.coefficient_mul % self.coefficient_div
  962. othermod = other.coefficient_mul % other.coefficient_div
  963. if selfmod == 0 and othermod == 0:
  964. # yet another chance for them to be equal
  965. selfdiv = self.coefficient_mul // self.coefficient_div
  966. otherdiv = other.coefficient_mul // other.coefficient_div
  967. coeff = selfdiv == otherdiv
  968. #
  969. if not coeff:
  970. return False, 0
  971. #
  972. c = (self.constant - other.constant)
  973. svar = self.var
  974. ovar = other.var
  975. if isinstance(svar, ConstInt) and isinstance(ovar, ConstInt):
  976. return True, (svar.getint() - ovar.getint())
  977. if svar.same_box(ovar):
  978. return True, c
  979. return False, 0
  980. def __eq__(self, other):
  981. if not self.same_variable(other):
  982. return False
  983. if not self.same_mulfactor(other):
  984. return False
  985. return self.constant_diff(other) == 0
  986. def __ne__(self, other):
  987. return not self.__eq__(other)
  988. def __repr__(self):
  989. if self.is_identity():
  990. return 'idx(%s)' % (self.var,)
  991. return 'idx(%s*(%s/%s)+%s)' % (self.var, self.coefficient_mul,
  992. self.coefficient_div, self.constant)
  993. class MemoryRef(object):
  994. """ a memory reference to an array object. IntegralForwardModification is able
  995. to propagate changes to this object if applied in backwards direction.
  996. Example:
  997. i1 = int_add(i0,1)
  998. i2 = int_mul(i1,2)
  999. setarrayitem_gc(p0, i2, 1, ...)
  1000. will result in the linear combination i0 * (2/1) + 2
  1001. """
  1002. def __init__(self, op, index_var, raw_access=False):
  1003. assert op.getdescr() is not None
  1004. self.array = op.getarg(0)
  1005. self.descr = op.getdescr()
  1006. self.index_var = index_var
  1007. self.raw_access = raw_access
  1008. def is_adjacent_to(self, other):
  1009. """ this is a symmetric relation """
  1010. if not self.same_array(other):
  1011. return False
  1012. if not self.index_var.same_variable(other.index_var):
  1013. return False
  1014. if not self.index_var.same_mulfactor(other.index_var):
  1015. return False
  1016. stride = self.stride()
  1017. return abs(self.index_var.constant_diff(other.index_var)) - stride == 0
  1018. def is_adjacent_after(self, other):
  1019. """ the asymetric relation to is_adjacent_to """
  1020. if not self.same_array(other):
  1021. return False
  1022. if not self.index_var.same_variable(other.index_var):
  1023. return False
  1024. if not self.index_var.same_mulfactor(other.index_var):
  1025. return False
  1026. stride = self.stride()
  1027. return other.index_var.constant_diff(self.index_var) == stride
  1028. def alias(self, other):
  1029. """ is this reference an alias to other?
  1030. they can alias iff self.origin != other.origin, or their
  1031. linear combination point to the same element.
  1032. """
  1033. assert other is not None
  1034. if not self.same_array(other):
  1035. return False
  1036. svar = self.index_var
  1037. ovar = other.index_var
  1038. if not svar.same_variable(ovar):
  1039. return True
  1040. if not svar.same_mulfactor(ovar):
  1041. return True
  1042. return abs(svar.constant_diff(ovar)) < self.stride()
  1043. def same_array(self, other):
  1044. return self.array is other.array and self.descr == other.descr
  1045. def __eq__(self, other):
  1046. """ NOT_RPYTHON """
  1047. if not self.same_array(other):
  1048. return False
  1049. if not self.index_var.same_variable(other.index_var):
  1050. return False
  1051. if not self.index_var.same_mulfactor(other.index_var):
  1052. return False
  1053. stride = self.stride()
  1054. return other.index_var.constant_diff(self.index_var) == 0
  1055. #def __ne__(self, other):
  1056. # return not self.__eq__(other)
  1057. def stride(self):
  1058. """ the stride in bytes """
  1059. if not self.raw_access:
  1060. return 1
  1061. return self.descr.get_item_size_in_bytes()
  1062. def __repr__(self):
  1063. return 'MemRef(%s,%s)' % (self.array, self.index_var)