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