def update_rdict_annotations(self, s_eqfn, s_hashfn, other=None): assert self.custom_eq_hash s_eqfn = unionof(s_eqfn, self.s_rdict_eqfn) s_hashfn = unionof(s_hashfn, self.s_rdict_hashfn) self.s_rdict_eqfn = s_eqfn self.s_rdict_hashfn = s_hashfn self.emulate_rdict_calls(other=other)
def update_rdict_annotations(self, s_eqfn, s_hashfn, other=None): assert self.custom_eq_hash s_eqfn = unionof(s_eqfn, self.s_rdict_eqfn) s_hashfn = unionof(s_hashfn, self.s_rdict_hashfn) self.s_rdict_eqfn = s_eqfn self.s_rdict_hashfn = s_hashfn self.emulate_rdict_calls(other=other)
def normalize_calltable_row_annotation(annotator, graphs): if len(graphs) <= 1: return False # nothing to do graph_bindings = {} for graph in graphs: graph_bindings[graph] = [annotator.binding(v) for v in graph.getargs()] iterbindings = graph_bindings.itervalues() nbargs = len(iterbindings.next()) for binding in iterbindings: assert len(binding) == nbargs generalizedargs = [] for i in range(nbargs): args_s = [] for graph, bindings in graph_bindings.items(): args_s.append(bindings[i]) s_value = annmodel.unionof(*args_s) generalizedargs.append(s_value) result_s = [ annotator.binding(graph.getreturnvar()) for graph in graph_bindings ] generalizedresult = annmodel.unionof(*result_s) conversion = False for graph in graphs: bindings = graph_bindings[graph] need_conversion = (generalizedargs != bindings) if need_conversion: conversion = True oldblock = graph.startblock inlist = [] for j, s_value in enumerate(generalizedargs): v = Variable(graph.getargs()[j]) annotator.setbinding(v, s_value) inlist.append(v) newblock = Block(inlist) # prepare the output args of newblock and link outlist = inlist[:] newblock.closeblock(Link(outlist, oldblock)) graph.startblock = newblock # finished checkgraph(graph) annotator.annotated[newblock] = annotator.annotated[oldblock] # convert the return value too if annotator.binding(graph.getreturnvar()) != generalizedresult: conversion = True annotator.setbinding(graph.getreturnvar(), generalizedresult) return conversion
def normalize_calltable_row_annotation(annotator, graphs): if len(graphs) <= 1: return False # nothing to do graph_bindings = {} for graph in graphs: graph_bindings[graph] = [annotator.binding(v) for v in graph.getargs()] iterbindings = graph_bindings.itervalues() nbargs = len(iterbindings.next()) for binding in iterbindings: assert len(binding) == nbargs generalizedargs = [] for i in range(nbargs): args_s = [] for graph, bindings in graph_bindings.items(): args_s.append(bindings[i]) s_value = annmodel.unionof(*args_s) generalizedargs.append(s_value) result_s = [annotator.binding(graph.getreturnvar()) for graph in graph_bindings] generalizedresult = annmodel.unionof(*result_s) conversion = False for graph in graphs: bindings = graph_bindings[graph] need_conversion = (generalizedargs != bindings) if need_conversion: conversion = True oldblock = graph.startblock inlist = [] for j, s_value in enumerate(generalizedargs): v = Variable(graph.getargs()[j]) annotator.setbinding(v, s_value) inlist.append(v) newblock = Block(inlist) # prepare the output args of newblock and link outlist = inlist[:] newblock.closeblock(Link(outlist, oldblock)) graph.startblock = newblock # finished checkgraph(graph) annotator.annotated[newblock] = annotator.annotated[oldblock] # convert the return value too if annotator.binding(graph.getreturnvar()) != generalizedresult: conversion = True annotator.setbinding(graph.getreturnvar(), generalizedresult) return conversion
def pycall(self, whence, args, s_previous_result, op=None): inputcells = self.parse_arguments(args) result = self.specialize(inputcells, op) if isinstance(result, FunctionGraph): graph = result # common case annotator = self.bookkeeper.annotator # if that graph has a different signature, we need to re-parse # the arguments. # recreate the args object because inputcells may have been changed new_args = args.unmatch_signature(self.signature, inputcells) inputcells = self.parse_arguments(new_args, graph) result = annotator.recursivecall(graph, whence, inputcells) signature = getattr(self.pyobj, '_signature_', None) if signature: sigresult = enforce_signature_return(self, signature[1], result) if sigresult is not None: annotator.addpendingblock( graph, graph.returnblock, [sigresult]) result = sigresult # Some specializations may break the invariant of returning # annotations that are always more general than the previous time. # We restore it here: from rpython.annotator.model import unionof result = unionof(result, s_previous_result) return result
def pbc_call(self, pbc, args, emulated=None): """Analyse a call to a SomePBC() with the given args (list of annotations). """ if emulated is None: whence = self.position_key # fish the existing annotation for the result variable, # needed by some kinds of specialization. fn, block, i = self.position_key op = block.operations[i] s_previous_result = self.annotator.annotation(op.result) if s_previous_result is None: s_previous_result = s_ImpossibleValue else: if emulated is True: whence = None else: whence = emulated # callback case op = None s_previous_result = s_ImpossibleValue results = [] for desc in pbc.descriptions: results.append(desc.pycall(whence, args, s_previous_result, op)) s_result = unionof(*results) return s_result
def union((bltn1, bltn2)): if (bltn1.analyser != bltn2.analyser or bltn1.methodname != bltn2.methodname or bltn1.s_self is None or bltn2.s_self is None): raise UnionError(bltn1, bltn2) s_self = unionof(bltn1.s_self, bltn2.s_self) return SomeBuiltin(bltn1.analyser, s_self, methodname=bltn1.methodname)
def pycall(self, schedule, args, s_previous_result, op=None): inputcells = self.parse_arguments(args) result = self.specialize(inputcells, op) if isinstance(result, FunctionGraph): graph = result # common case # if that graph has a different signature, we need to re-parse # the arguments. # recreate the args object because inputcells may have been changed new_args = args.unmatch_signature(self.signature, inputcells) inputcells = self.parse_arguments(new_args, graph) result = schedule(graph, inputcells) signature = getattr(self.pyobj, '_signature_', None) if signature: sigresult = enforce_signature_return(self, signature[1], result) if sigresult is not None: self.bookkeeper.annotator.addpendingblock( graph, graph.returnblock, [sigresult]) result = sigresult # Some specializations may break the invariant of returning # annotations that are always more general than the previous time. # We restore it here: from rpython.annotator.model import unionof result = unionof(result, s_previous_result) return result
def addpendingblock(self, graph, block, cells): """Register an entry point into block with the given input cells.""" if graph in self.fixed_graphs: # special case for annotating/rtyping in several phases: calling # a graph that has already been rtyped. Safety-check the new # annotations that are passed in, and don't annotate the old # graph -- it's already low-level operations! for a, s_newarg in zip(block.inputargs, cells): s_oldarg = a.annotation # XXX: Should use s_oldarg.contains(s_newarg) but that breaks # PyPy translation if annmodel.unionof(s_oldarg, s_newarg) != s_oldarg: raise annmodel.AnnotatorError( "Late-stage annotation is not allowed to modify the " "existing annotation for variable %s: %s" % (a, s_oldarg)) else: assert not self.frozen if block not in self.annotated: self.bindinputargs(graph, block, cells) else: self.mergeinputargs(graph, block, cells) if not self.annotated[block]: self.pendingblocks[block] = graph
def pbc_call(self, pbc, args, emulated=None): """Analyse a call to a SomePBC() with the given args (list of annotations). """ if emulated is None: whence = self.position_key # fish the existing annotation for the result variable, # needed by some kinds of specialization. fn, block, i = self.position_key op = block.operations[i] s_previous_result = self.annotator.annotation(op.result) if s_previous_result is None: s_previous_result = s_ImpossibleValue else: if emulated is True: whence = None else: whence = emulated # callback case op = None s_previous_result = s_ImpossibleValue results = [] for desc in pbc.descriptions: results.append(desc.pycall(whence, args, s_previous_result, op)) s_result = unionof(*results) return s_result
def add_constant_source(self, classdef, source): s_value = source.s_get_value(classdef, self.name) if source.instance_level: # a prebuilt instance source forces readonly=False, see above self.modified(classdef) s_new_value = unionof(self.s_value, s_value) self.s_value = s_new_value
def union((bltn1, bltn2)): if (bltn1.analyser != bltn2.analyser or bltn1.methodname != bltn2.methodname): raise UnionError(bltn1, bltn2) s_self = unionof(bltn1.s_self, bltn2.s_self) return SomeBuiltinMethod(bltn1.analyser, s_self, methodname=bltn1.methodname)
def union((iter1, iter2)): s_cont = unionof(iter1.s_container, iter2.s_container) if iter1.variant != iter2.variant: raise UnionError( iter1, iter2, "RPython cannot unify incompatible iterator variants") return SomeIterator(s_cont, *iter1.variant)
def __call__(self, funcdesc, inputcells): from rpython.rtyper.lltypesystem import lltype args_s = [] from rpython.annotator import model as annmodel for i, argtype in enumerate(self.argtypes): if isinstance(argtype, (types.FunctionType, types.MethodType)): argtype = argtype(*inputcells) if isinstance(argtype, lltype.LowLevelType) and\ argtype is lltype.Void: # XXX the mapping between Void and annotation # is not quite well defined s_input = inputcells[i] assert isinstance(s_input, annmodel.SomePBC) assert s_input.is_constant() args_s.append(s_input) elif argtype is None: args_s.append(inputcells[i]) # no change else: args_s.append(annotation(argtype, bookkeeper=funcdesc.bookkeeper)) if len(inputcells) != len(args_s): raise SignatureError("%r: expected %d args, got %d" % (funcdesc, len(args_s), len(inputcells))) for i, (s_arg, s_input) in enumerate(zip(args_s, inputcells)): s_input = unionof(s_input, s_arg) if not s_arg.contains(s_input): raise SignatureError("%r argument %d:\n" "expected %s,\n" " got %s" % (funcdesc, i+1, s_arg, s_input)) inputcells[:] = args_s
def merge(self, other, classdef): assert self.name == other.name s_new_value = unionof(self.s_value, other.s_value) self.s_value = s_new_value if not other.readonly: self.modified(classdef) self.read_locations.update(other.read_locations)
def pbc_call(self, pbc, args, emulated=None): """Analyse a call to a SomePBC() with the given args (list of annotations). """ descs = list(pbc.descriptions) first = descs[0] first.mergecallfamilies(*descs[1:]) if emulated is None: whence = self.position_key # fish the existing annotation for the result variable, # needed by some kinds of specialization. fn, block, i = self.position_key op = block.operations[i] s_previous_result = self.annotator.annotation(op.result) if s_previous_result is None: s_previous_result = s_ImpossibleValue else: if emulated is True: whence = None else: whence = emulated # callback case op = None s_previous_result = s_ImpossibleValue def schedule(graph, inputcells): return self.annotator.recursivecall(graph, whence, inputcells) results = [] for desc in descs: results.append(desc.pycall(schedule, args, s_previous_result, op)) s_result = unionof(*results) return s_result
def merge(self, other, classdef): assert self.name == other.name s_new_value = unionof(self.s_value, other.s_value) self.s_value = s_new_value if not other.readonly: self.modified(classdef) self.read_locations.update(other.read_locations)
def pycall(self, whence, args, s_previous_result, op=None): inputcells = self.parse_arguments(args) s_result = self.specialize(inputcells, op) if isinstance(s_result, FunctionGraph): s_result = s_result.getreturnvar().annotation s_result = unionof(s_result, s_previous_result) return s_result
def pbc_call(self, pbc, args, emulated=None): """Analyse a call to a SomePBC() with the given args (list of annotations). """ descs = list(pbc.descriptions) first = descs[0] first.mergecallfamilies(*descs[1:]) if emulated is None: whence = self.position_key # fish the existing annotation for the result variable, # needed by some kinds of specialization. fn, block, i = self.position_key op = block.operations[i] s_previous_result = self.annotator.annotation(op.result) if s_previous_result is None: s_previous_result = s_ImpossibleValue else: if emulated is True: whence = None else: whence = emulated # callback case op = None s_previous_result = s_ImpossibleValue def schedule(graph, inputcells): return self.annotator.recursivecall(graph, whence, inputcells) results = [] for desc in descs: results.append(desc.pycall(schedule, args, s_previous_result, op)) s_result = unionof(*results) return s_result
def __call__(self, funcdesc, inputcells): from rpython.rtyper.lltypesystem import lltype args_s = [] from rpython.annotator import model as annmodel for i, argtype in enumerate(self.argtypes): if isinstance(argtype, (types.FunctionType, types.MethodType)): argtype = argtype(*inputcells) if argtype is lltype.Void: # XXX the mapping between Void and annotation # is not quite well defined s_input = inputcells[i] assert isinstance(s_input, (annmodel.SomePBC, annmodel.SomeNone)) assert s_input.is_constant() args_s.append(s_input) elif argtype is None: args_s.append(inputcells[i]) # no change else: args_s.append( annotation(argtype, bookkeeper=funcdesc.bookkeeper)) if len(inputcells) != len(args_s): raise SignatureError("%r: expected %d args, got %d" % (funcdesc, len(args_s), len(inputcells))) for i, (s_arg, s_input) in enumerate(zip(args_s, inputcells)): s_input = unionof(s_input, s_arg) if not s_arg.contains(s_input): raise SignatureError("%r argument %d:\n" "expected %s,\n" " got %s" % (funcdesc, i + 1, s_arg, s_input)) inputcells[:] = args_s
def add_constant_source(self, classdef, source): s_value = source.s_get_value(classdef, self.name) if source.instance_level: # a prebuilt instance source forces readonly=False, see above self.modified(classdef) s_new_value = unionof(self.s_value, s_value) self.s_value = s_new_value
def addpendingblock(self, graph, block, cells): """Register an entry point into block with the given input cells.""" if graph in self.fixed_graphs: # special case for annotating/rtyping in several phases: calling # a graph that has already been rtyped. Safety-check the new # annotations that are passed in, and don't annotate the old # graph -- it's already low-level operations! for a, s_newarg in zip(block.inputargs, cells): s_oldarg = a.annotation # XXX: Should use s_oldarg.contains(s_newarg) but that breaks # PyPy translation if annmodel.unionof(s_oldarg, s_newarg) != s_oldarg: raise annmodel.AnnotatorError( "Late-stage annotation is not allowed to modify the " "existing annotation for variable %s: %s" % (a, s_oldarg)) else: assert not self.frozen if block not in self.annotated: self.bindinputargs(graph, block, cells) else: self.mergeinputargs(graph, block, cells) if not self.annotated[block]: self.pendingblocks[block] = graph
def memo(funcdesc, arglist_s): from rpython.annotator.model import SomePBC, SomeImpossibleValue, SomeBool from rpython.annotator.model import unionof # call the function now, and collect possible results argvalues = [] for s in arglist_s: if s.is_constant(): values = [s.const] elif isinstance(s, SomePBC): values = [] assert not s.can_be_None, "memo call: cannot mix None and PBCs" for desc in s.descriptions: if desc.pyobj is None: raise annmodel.AnnotatorError( "memo call with a class or PBC that has no " "corresponding Python object (%r)" % (desc,)) values.append(desc.pyobj) elif isinstance(s, SomeImpossibleValue): return s # we will probably get more possible args later elif isinstance(s, SomeBool): values = [False, True] else: raise annmodel.AnnotatorError("memo call: argument must be a class " "or a frozen PBC, got %r" % (s,)) argvalues.append(values) # the list of all possible tuples of arguments to give to the memo function possiblevalues = cartesian_product(argvalues) # a MemoTable factory -- one MemoTable per family of arguments that can # be called together, merged via a UnionFind. bookkeeper = funcdesc.bookkeeper try: memotables = bookkeeper.all_specializations[funcdesc] except KeyError: func = funcdesc.pyobj if func is None: raise annmodel.AnnotatorError("memo call: no Python function object" "to call (%r)" % (funcdesc,)) def compute_one_result(args): value = func(*args) memotable = MemoTable(funcdesc, args, value) memotable.register_finish() return memotable memotables = UnionFind(compute_one_result) bookkeeper.all_specializations[funcdesc] = memotables # merge the MemoTables for the individual argument combinations firstvalues = possiblevalues.next() _, _, memotable = memotables.find(firstvalues) for values in possiblevalues: _, _, memotable = memotables.union(firstvalues, values) if memotable.graph is not None: return memotable.graph # if already computed else: # otherwise, for now, return the union of each possible result return unionof(*[bookkeeper.immutablevalue(v) for v in memotable.table.values()])
def union((tup1, tup2)): if len(tup1.items) != len(tup2.items): raise UnionError(tup1, tup2, "RPython cannot unify tuples of " "different length: %d versus %d" % \ (len(tup1.items), len(tup2.items))) else: unions = [unionof(x, y) for x, y in zip(tup1.items, tup2.items)] return SomeTuple(items=unions)
def getitem((tup1, int2)): if int2.is_immutable_constant(): try: return tup1.items[int2.const] except IndexError: return s_ImpossibleValue else: return unionof(*tup1.items)
def union((tup1, tup2)): if len(tup1.items) != len(tup2.items): raise UnionError(tup1, tup2, "RPython cannot unify tuples of " "different length: %d versus %d" % \ (len(tup1.items), len(tup2.items))) else: unions = [unionof(x,y) for x,y in zip(tup1.items, tup2.items)] return SomeTuple(items = unions)
def getitem((tup1, int2)): if int2.is_immutable_constant(): try: return tup1.items[int2.const] except IndexError: return s_ImpossibleValue else: return unionof(*tup1.items)
def union((tup1, tup2)): if len(tup1.items) != len(tup2.items): raise UnionError("cannot take the union of a tuple of length %d " "and a tuple of length %d" % (len(tup1.items), len(tup2.items))) else: unions = [unionof(x,y) for x,y in zip(tup1.items, tup2.items)] return SomeTuple(items = unions)
def union((bltn1, bltn2)): if (bltn1.analyser != bltn2.analyser or bltn1.methodname != bltn2.methodname or bltn1.s_self is None or bltn2.s_self is None): raise UnionError("cannot merge two different builtin functions " "or methods:\n %r\n %r" % (bltn1, bltn2)) s_self = unionof(bltn1.s_self, bltn2.s_self) return SomeBuiltin(bltn1.analyser, s_self, methodname=bltn1.methodname)
def generalize(self, s_other_value): s_new_value = unionof(self.s_value, s_other_value) updated = s_new_value != self.s_value if updated: if self.dont_change_any_more: raise TooLateForChange self.s_value = s_new_value self.notify_update() return updated
def pycall(self, whence, args, s_previous_result, op=None): inputcells = self.parse_arguments(args) s_result = self.specialize(inputcells, op) if isinstance(s_result, FunctionGraph): s_result = s_result.getreturnvar().annotation if s_result is None: s_result = s_ImpossibleValue s_result = unionof(s_result, s_previous_result) return s_result
def enter_tunnel(self, bookkeeper, s_obj): dict = self._getdict(bookkeeper) s_previousobj, reflowpositions = dict.setdefault( self, (annmodel.s_ImpossibleValue, {})) s_obj = annmodel.unionof(s_previousobj, s_obj) if s_obj != s_previousobj: dict[self] = (s_obj, reflowpositions) for position in reflowpositions: bookkeeper.annotator.reflowfromposition(position)
def enter_tunnel(self, bookkeeper, s_obj): dict = self._getdict(bookkeeper) s_previousobj, reflowpositions = dict.setdefault( self, (annmodel.s_ImpossibleValue, {})) s_obj = annmodel.unionof(s_previousobj, s_obj) if s_obj != s_previousobj: dict[self] = (s_obj, reflowpositions) for position in reflowpositions: bookkeeper.annotator.reflowfromposition(position)
def getitem((tup1, int2)): if int2.is_immutable_constant(): try: return tup1.items[int2.const] except IndexError: return s_ImpossibleValue else: getbookkeeper().count("tuple_random_getitem", tup1) return unionof(*tup1.items)
def generalize(self, s_other_value): s_new_value = unionof(self.s_value, s_other_value) updated = s_new_value != self.s_value if updated: if self.dont_change_any_more: raise TooLateForChange self.s_value = s_new_value self.notify_update() return updated
def mergeinputargs(self, graph, block, inputcells): # Merge the new 'cells' with each of the block's existing input # variables. oldcells = [self.binding(a) for a in block.inputargs] try: unions = [annmodel.unionof(c1,c2) for c1, c2 in zip(oldcells,inputcells)] except annmodel.UnionError, e: # Add source code to the UnionError e.source = '\n'.join(source_lines(graph, block, None, long=True)) raise
def mergeinputargs(self, graph, block, inputcells): # Merge the new 'cells' with each of the block's existing input # variables. oldcells = [self.binding(a) for a in block.inputargs] try: unions = [annmodel.unionof(c1,c2) for c1, c2 in zip(oldcells,inputcells)] except annmodel.UnionError, e: # Add source code to the UnionError e.source = '\n'.join(source_lines(graph, block, None, long=True)) raise
def _rtype_compare_template(hop, func): s_int1, s_int2 = hop.args_s if s_int1.unsigned or s_int2.unsigned: if not s_int1.nonneg or not s_int2.nonneg: raise TyperError("comparing a signed and an unsigned number") repr = hop.rtyper.getrepr(annmodel.unionof(s_int1, s_int2)).as_int vlist = hop.inputargs(repr, repr) hop.exception_is_here() return hop.genop(repr.opprefix + func, vlist, resulttype=Bool)
def _rtype_compare_template(hop, func): s_int1, s_int2 = hop.args_s if s_int1.unsigned or s_int2.unsigned: if not s_int1.nonneg or not s_int2.nonneg: raise TyperError("comparing a signed and an unsigned number") repr = hop.rtyper.getrepr(annmodel.unionof(s_int1, s_int2)).as_int vlist = hop.inputargs(repr, repr) hop.exception_is_here() return hop.genop(repr.opprefix+func, vlist, resulttype=Bool)
def pbc_getattr(self, pbc, s_attr): assert s_attr.is_constant() attr = s_attr.const descs = list(pbc.descriptions) if not descs: return s_ImpossibleValue first = descs[0] if len(descs) == 1: return first.s_read_attribute(attr) change = first.mergeattrfamilies(descs[1:], attr) attrfamily = first.getattrfamily(attr) position = self.position_key attrfamily.read_locations[position] = True actuals = [] for desc in descs: actuals.append(desc.s_read_attribute(attr)) s_result = unionof(*actuals) s_oldvalue = attrfamily.get_s_value(attr) attrfamily.set_s_value(attr, unionof(s_result, s_oldvalue)) if change: for position in attrfamily.read_locations: self.annotator.reflowfromposition(position) if isinstance(s_result, SomeImpossibleValue): for desc in descs: try: attrs = desc.read_attribute('_attrs_') except AttributeError: continue if isinstance(attrs, Constant): attrs = attrs.value if attr in attrs: raise HarmlesslyBlocked("getattr on enforced attr") return s_result
def generalize(self, s_other_value): s_new_value = unionof(self.s_value, s_other_value) updated = s_new_value != self.s_value if updated: if self.dont_change_any_more: raise TooLateForChange self.s_value = s_new_value # reflow from all reading points for position_key in self.read_locations: self.bookkeeper.annotator.reflowfromposition(position_key) return updated
def weakly_contains(s_bigger, s_smaller): # a special version of s_bigger.contains(s_smaller). Warning, to # support ListDefs properly, this works by trying to produce a side-effect # on s_bigger. It relies on the fact that s_bigger was created with # an expression like 'annotation([s_item])' which returns a ListDef with # no bookkeeper, on which side-effects are not allowed. try: s_union = annmodel.unionof(s_bigger, s_smaller) return s_bigger.contains(s_union) except (annmodel.UnionError, TooLateForChange): return False
def generalize(self, s_other_value): s_new_value = unionof(self.s_value, s_other_value) updated = s_new_value != self.s_value if updated: if self.dont_change_any_more: raise TooLateForChange self.s_value = s_new_value # reflow from all reading points for position_key in self.read_locations: self.bookkeeper.annotator.reflowfromposition(position_key) return updated
def weakly_contains(s_bigger, s_smaller): # a special version of s_bigger.contains(s_smaller). Warning, to # support ListDefs properly, this works by trying to produce a side-effect # on s_bigger. It relies on the fact that s_bigger was created with # an expression like 'annotation([s_item])' which returns a ListDef with # no bookkeeper, on which side-effects are not allowed. try: s_union = annmodel.unionof(s_bigger, s_smaller) return s_bigger.contains(s_union) except (annmodel.UnionError, TooLateForChange): return False
def pbc_getattr(self, pbc, s_attr): assert s_attr.is_constant() attr = s_attr.const descs = list(pbc.descriptions) first = descs[0] if len(descs) == 1: return first.s_read_attribute(attr) change = first.mergeattrfamilies(descs[1:], attr) attrfamily = first.getattrfamily(attr) position = self.position_key attrfamily.read_locations[position] = True actuals = [] for desc in descs: actuals.append(desc.s_read_attribute(attr)) s_result = unionof(*actuals) s_oldvalue = attrfamily.get_s_value(attr) attrfamily.set_s_value(attr, unionof(s_result, s_oldvalue)) if change: for position in attrfamily.read_locations: self.annotator.reflowfromposition(position) if isinstance(s_result, SomeImpossibleValue): for desc in descs: try: attrs = desc.read_attribute('_attrs_') except AttributeError: continue if isinstance(attrs, Constant): attrs = attrs.value if attr in attrs: raise HarmlesslyBlocked("getattr on enforced attr") return s_result
def builtin_max(*s_values): if len(s_values) == 1: # xxx do we support this? s_iter = s_values[0].iter() return s_iter.next() else: s = unionof(*s_values) if type(s) is SomeInteger and not s.nonneg: nonneg = False for s1 in s_values: nonneg |= s1.nonneg if nonneg: s = SomeInteger(nonneg=True, knowntype=s.knowntype) return s
def builtin_max(*s_values): if len(s_values) == 1: # xxx do we support this? s_iter = s_values[0].iter() return s_iter.next() else: s = unionof(*s_values) if type(s) is SomeInteger and not s.nonneg: nonneg = False for s1 in s_values: nonneg |= s1.nonneg if nonneg: s = SomeInteger(nonneg=True, knowntype=s.knowntype) return s
class __extend__(pairtype(FunctionReprBase, FunctionReprBase)): def rtype_is_((robj1, robj2), hop): if hop.s_result.is_constant(): return inputconst(Bool, hop.s_result.const) s_pbc = annmodel.unionof(robj1.s_pbc, robj2.s_pbc) r_pbc = hop.rtyper.getrepr(s_pbc) v1, v2 = hop.inputargs(r_pbc, r_pbc) assert v1.concretetype == v2.concretetype if v1.concretetype == Char: return hop.genop('char_eq', [v1, v2], resulttype=Bool) elif isinstance(v1.concretetype, Ptr): return hop.genop('ptr_eq', [v1, v2], resulttype=Bool) else: raise TyperError("unknown type %r" % (v1.concretetype, ))
def check_args(self, callspec): params_s = self.args_s args_s, kwargs = callspec.unpack() if kwargs: raise SignatureError( "External functions cannot be called with keyword arguments") if len(args_s) != len(params_s): raise SignatureError("Argument number mismatch") for i, s_param in enumerate(params_s): arg = unionof(args_s[i], s_param) if not s_param.contains(arg): raise SignatureError( "In call to external function %r:\n" "arg %d must be %s,\n" " got %s" % ( self.name, i + 1, s_param, args_s[i]))
def merge(self, other): if self is not other: if getattr(TLS, 'no_side_effects_in_union', 0): raise UnionError(self, other) if other.dont_change_any_more: if self.dont_change_any_more: raise TooLateForChange else: # lists using 'other' don't expect it to change any more, # so we try merging into 'other', which will give # TooLateForChange if it actually tries to make # things more general self, other = other, self self.immutable &= other.immutable if other.must_not_resize: if self.resized: raise ListChangeUnallowed("list merge with a resized") self.must_not_resize = True if other.mutated: self.mutate() if other.resized: self.resize() if other.range_step != self.range_step: self.setrangestep(self._step_map[type(self.range_step), type(other.range_step)]) self.itemof.update(other.itemof) read_locations = self.read_locations.copy() other_read_locations = other.read_locations.copy() self.read_locations.update(other.read_locations) s_value = self.s_value s_other_value = other.s_value s_new_value = unionof(s_value, s_other_value) if s_new_value != s_value: if self.dont_change_any_more: raise TooLateForChange self.patch() # which should patch all refs to 'other' if s_new_value != s_value: self.s_value = s_new_value # reflow from reading points for position_key in read_locations: self.bookkeeper.annotator.reflowfromposition(position_key) if s_new_value != s_other_value: # reflow from reading points for position_key in other_read_locations: other.bookkeeper.annotator.reflowfromposition(position_key)
def merge(self, other): if self is not other: if getattr(TLS, 'no_side_effects_in_union', 0): raise UnionError(self, other) if other.dont_change_any_more: if self.dont_change_any_more: raise TooLateForChange else: # lists using 'other' don't expect it to change any more, # so we try merging into 'other', which will give # TooLateForChange if it actually tries to make # things more general self, other = other, self self.immutable &= other.immutable if other.must_not_resize: if self.resized: raise ListChangeUnallowed("list merge with a resized") self.must_not_resize = True if other.mutated: self.mutate() if other.resized: self.resize() if other.range_step != self.range_step: self.setrangestep(self._step_map[type(self.range_step), type(other.range_step)]) self.itemof.update(other.itemof) read_locations = self.read_locations.copy() other_read_locations = other.read_locations.copy() self.read_locations.update(other.read_locations) s_value = self.s_value s_other_value = other.s_value s_new_value = unionof(s_value, s_other_value) if s_new_value != s_value: if self.dont_change_any_more: raise TooLateForChange self.patch() # which should patch all refs to 'other' if s_new_value != s_value: self.s_value = s_new_value # reflow from reading points for position_key in read_locations: self.bookkeeper.annotator.reflowfromposition(position_key) if s_new_value != s_other_value: # reflow from reading points for position_key in other_read_locations: other.bookkeeper.annotator.reflowfromposition(position_key)
def mergeinputargs(self, graph, block, inputcells): # Merge the new 'cells' with each of the block's existing input # variables. oldcells = [self.binding(a) for a in block.inputargs] try: unions = [annmodel.unionof(c1,c2) for c1, c2 in zip(oldcells,inputcells)] except annmodel.UnionError as e: # Add source code to the UnionError e.source = '\n'.join(source_lines(graph, block, None, long=True)) if self.keepgoing: self.errors.append(e) self.failed_blocks.add(block) return raise # if the merged cells changed, we must redo the analysis if unions != oldcells: self.bindinputargs(graph, block, unions)
def addpendingblock(self, graph, block, cells): """Register an entry point into block with the given input cells.""" if graph in self.fixed_graphs: # special case for annotating/rtyping in several phases: calling # a graph that has already been rtyped. Safety-check the new # annotations that are passed in, and don't annotate the old # graph -- it's already low-level operations! for a, s_newarg in zip(block.inputargs, cells): s_oldarg = self.binding(a) assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg else: assert not self.frozen if block not in self.annotated: self.bindinputargs(graph, block, cells) else: self.mergeinputargs(graph, block, cells) if not self.annotated[block]: self.pendingblocks[block] = graph
def addpendingblock(self, graph, block, cells): """Register an entry point into block with the given input cells.""" if graph in self.fixed_graphs: # special case for annotating/rtyping in several phases: calling # a graph that has already been rtyped. Safety-check the new # annotations that are passed in, and don't annotate the old # graph -- it's already low-level operations! for a, s_newarg in zip(block.inputargs, cells): s_oldarg = self.binding(a) assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg else: assert not self.frozen if block not in self.annotated: self.bindinputargs(graph, block, cells) else: self.mergeinputargs(graph, block, cells) if not self.annotated[block]: self.pendingblocks[block] = graph
def normalize_args(self, *args_s): args = self.signature_args signature_args = [annotation(arg, None) for arg in args] assert len(args_s) == len(signature_args),\ "Argument number mismatch" for i, expected in enumerate(signature_args): arg = annmodel.unionof(args_s[i], expected) if not expected.contains(arg): name = getattr(self, 'name', None) if not name: try: name = self.instance.__name__ except AttributeError: name = '?' raise Exception("In call to external function %r:\n" "arg %d must be %s,\n" " got %s" % ( name, i+1, expected, args_s[i])) return signature_args
def normalize_args(self, *args_s): args = self.signature_args signature_args = [annotation(arg, None) for arg in args] assert len(args_s) == len(signature_args),\ "Argument number mismatch" for i, expected in enumerate(signature_args): arg = annmodel.unionof(args_s[i], expected) if not expected.contains(arg): name = getattr(self, 'name', None) if not name: try: name = self.instance.__name__ except AttributeError: name = '?' raise Exception("In call to external function %r:\n" "arg %d must be %s,\n" " got %s" % (name, i + 1, expected, args_s[i])) return signature_args
def mergeinputargs(self, graph, block, inputcells): # Merge the new 'cells' with each of the block's existing input # variables. oldcells = [self.binding(a) for a in block.inputargs] try: unions = [ annmodel.unionof(c1, c2) for c1, c2 in zip(oldcells, inputcells) ] except annmodel.UnionError as e: # Add source code to the UnionError e.source = '\n'.join(source_lines(graph, block, None, long=True)) if self.keepgoing: self.errors.append(e) self.failed_blocks.add(block) return raise # if the merged cells changed, we must redo the analysis if unions != oldcells: self.bindinputargs(graph, block, unions)
def compute_result_annotation(self, **kwds_s): from rpython.annotator import model as annmodel if self.instance.__name__ == 'jit_merge_point': self.annotate_hooks(**kwds_s) driver = self.instance.im_self keys = kwds_s.keys() keys.sort() expected = [ 's_' + name for name in driver.greens + driver.reds if '.' not in name ] expected.sort() if keys != expected: raise JitHintError("%s expects the following keyword " "arguments: %s" % (self.instance, expected)) try: cache = self.bookkeeper._jit_annotation_cache[driver] except AttributeError: cache = {} self.bookkeeper._jit_annotation_cache = {driver: cache} except KeyError: cache = {} self.bookkeeper._jit_annotation_cache[driver] = cache for key, s_value in kwds_s.items(): s_previous = cache.get(key, annmodel.s_ImpossibleValue) s_value = annmodel.unionof( s_previous, s_value ) # where="mixing incompatible types in argument %s of jit_merge_point/can_enter_jit" % key[2:] cache[key] = s_value # add the attribute _dont_reach_me_in_del_ (see rpython.rtyper.rclass) try: graph = self.bookkeeper.position_key[0] graph.func._dont_reach_me_in_del_ = True except (TypeError, AttributeError): pass return annmodel.s_None
def memo(funcdesc, args_s): # call the function now, and collect possible results # the list of all possible tuples of arguments to give to the memo function possiblevalues = cartesian_product([all_values(s_arg) for s_arg in args_s]) # a MemoTable factory -- one MemoTable per family of arguments that can # be called together, merged via a UnionFind. bookkeeper = funcdesc.bookkeeper try: memotables = bookkeeper.all_specializations[funcdesc] except KeyError: func = funcdesc.pyobj if func is None: raise annmodel.AnnotatorError( "memo call: no Python function object" "to call (%r)" % (funcdesc, )) def compute_one_result(args): value = func(*args) memotable = MemoTable(funcdesc, args, value) memotable.register_finish() return memotable memotables = UnionFind(compute_one_result) bookkeeper.all_specializations[funcdesc] = memotables # merge the MemoTables for the individual argument combinations firstvalues = possiblevalues.next() _, _, memotable = memotables.find(firstvalues) for values in possiblevalues: _, _, memotable = memotables.union(firstvalues, values) if memotable.graph is not None: return memotable.graph # if already computed else: # otherwise, for now, return the union of each possible result return unionof( *[bookkeeper.immutablevalue(v) for v in memotable.table.values()])