def union((d1, d2)): assert (d1.eq_func is d2.eq_func is None) or (d1.eq_func.const is d2.eq_func.const) assert (d1.hash_func is d2.hash_func is None) or (d1.hash_func.const is d2.hash_func.const) s_new = SomeOrderedDict(d1.bookkeeper, d1.eq_func, d1.hash_func) s_new.key_type = d1.key_type = model.unionof(d1.key_type, d2.key_type) s_new.value_type = d1.value_type = model.unionof(d1.value_type, d2.value_type) return s_new
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): if not self.custom_eq_hash: self.custom_eq_hash = True else: 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 union((d1, d2)): assert (d1.eq_func is d2.eq_func is None) or (d1.eq_func.const is d2.eq_func.const) assert (d1.hash_func is d2.hash_func is None) or (d1.hash_func.const is d2.hash_func.const) s_new = SomeOrderedDict(d1.bookkeeper, d1.eq_func, d1.hash_func) s_new.key_type = d1.key_type = model.unionof(d1.key_type, d2.key_type) s_new.value_type = d1.value_type = model.unionof( d1.value_type, d2.value_type) return s_new
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)) oldblock.isstartblock = False newblock.isstartblock = True 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 __init__(self, hrtyper): self.hrtyper = hrtyper RGenOp = hrtyper.RGenOp rtyper = hrtyper.rtyper bk = rtyper.annotator.bookkeeper s_w_bool = annmodel.unionof(bk.immutablevalue(W_BoolObject.w_False), bk.immutablevalue(W_BoolObject.w_True)) r_w_bool = rtyper.getrepr(s_w_bool) self.ll_False = r_w_bool.convert_const(W_BoolObject.w_False) self.ll_True = r_w_bool.convert_const(W_BoolObject.w_True) A = lltype.Array(lltype.typeOf(self.ll_False)) self.ll_bools = lltype.malloc(A, 2, immortal=True) self.ll_bools[0] = self.ll_False self.ll_bools[1] = self.ll_True self.gv_bools = RGenOp.constPrebuiltGlobal(self.ll_bools) self.boolsToken = RGenOp.arrayToken(A) self.bools_gv = [ RGenOp.constPrebuiltGlobal(self.ll_False), RGenOp.constPrebuiltGlobal(self.ll_True) ] self.ptrkind = RGenOp.kindToken(r_w_bool.lowleveltype) self.boolkind = RGenOp.kindToken(lltype.Bool) ll_BoolObject = r_w_bool.rclass.getvtable() self.BoolObjectBox = rvalue.redbox_from_prebuilt_value( RGenOp, ll_BoolObject) self.Falsebox = rvalue.redbox_from_prebuilt_value(RGenOp, False) self.Truebox = rvalue.redbox_from_prebuilt_value(RGenOp, True) self.boolboxes = [self.Falsebox, self.Truebox]
def memo(funcdesc, arglist_s): from pypy.annotation.model import SomePBC, SomeImpossibleValue, SomeBool from pypy.annotation.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 Exception( "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 Exception("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 Exception("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 __call__(self, funcdesc, inputcells): from pypy.rpython.lltypesystem import lltype args_s = [] from pypy.annotation 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 Exception("%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 Exception("%r argument %d:\n" "expected %s,\n" " got %s" % (funcdesc, i + 1, s_arg, s_input)) inputcells[:] = args_s
def compute_result_annotation(self, **kwds_s): from pypy.annotation import model as annmodel driver = self.instance.im_self keys = kwds_s.keys() keys.sort() expected = ['s_' + name for name in driver.greens + driver.reds] 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) if annmodel.isdegenerated(s_value): raise JitHintError("mixing incompatible types in argument %s" " of jit_merge_point/can_enter_jit" % key[2:]) cache[key] = s_value if self.instance.__name__ == 'jit_merge_point': self.annotate_hooks(**kwds_s) return annmodel.s_None
def __init__(self, hrtyper): self.hrtyper = hrtyper RGenOp = hrtyper.RGenOp rtyper = hrtyper.rtyper bk = rtyper.annotator.bookkeeper s_w_bool = annmodel.unionof(bk.immutablevalue(W_BoolObject.w_False), bk.immutablevalue(W_BoolObject.w_True)) r_w_bool = rtyper.getrepr(s_w_bool) self.ll_False = r_w_bool.convert_const(W_BoolObject.w_False) self.ll_True = r_w_bool.convert_const(W_BoolObject.w_True) A = lltype.Array(lltype.typeOf(self.ll_False)) self.ll_bools = lltype.malloc(A, 2, immortal=True) self.ll_bools[0] = self.ll_False self.ll_bools[1] = self.ll_True self.gv_bools = RGenOp.constPrebuiltGlobal(self.ll_bools) self.boolsToken = RGenOp.arrayToken(A) self.bools_gv = [RGenOp.constPrebuiltGlobal(self.ll_False), RGenOp.constPrebuiltGlobal(self.ll_True)] self.ptrkind = RGenOp.kindToken(r_w_bool.lowleveltype) self.boolkind = RGenOp.kindToken(lltype.Bool) ll_BoolObject = r_w_bool.rclass.getvtable() self.BoolObjectBox = rvalue.redbox_from_prebuilt_value(RGenOp, ll_BoolObject) self.Falsebox = rvalue.redbox_from_prebuilt_value(RGenOp, False) self.Truebox = rvalue.redbox_from_prebuilt_value(RGenOp, True) self.boolboxes = [self.Falsebox, self.Truebox]
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) if not descs: return s_ImpossibleValue 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.binding(op.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 indirect_call(hs_v1, *args_hs): hs_graph_list = args_hs[-1] args_hs = args_hs[:-1] assert hs_graph_list.is_constant() graph_list = hs_graph_list.const if graph_list is None: # cannot follow indirect calls to unknown targets return variableoftype(hs_v1.concretetype.TO.RESULT) bookkeeper = getbookkeeper() myorigin = bookkeeper.myorigin() myorigin.__class__ = CallOpOriginFlags # thud fixed = myorigin.read_fixed() tsgraphs_accum = [] hs_res = bookkeeper.graph_family_call(graph_list, fixed, args_hs, tsgraphs_accum, hs_v1) myorigin.any_called_graph = tsgraphs_accum[0] if isinstance(hs_res, SomeLLAbstractConstant): hs_res.myorigin = myorigin # we need to make sure that hs_res does not become temporarily less # general as a result of calling another specialized version of the # function return annmodel.unionof(hs_res, bookkeeper.current_op_binding())
def compute_result_annotation(self, **kwds_s): from pypy.annotation 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) if annmodel.isdegenerated(s_value): raise JitHintError("mixing incompatible types in argument %s" " of jit_merge_point/can_enter_jit" % key[2:]) cache[key] = s_value return annmodel.s_None
def __call__(self, funcdesc, inputcells): from pypy.rpython.lltypesystem import lltype args_s = [] from pypy.annotation 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 Exception("%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 Exception("%r argument %d:\n" "expected %s,\n" " got %s" % (funcdesc, i+1, s_arg, s_input)) inputcells[:] = args_s
def _call_single_graph(hs_f1, graph, RESULT, *args_hs): bookkeeper = getbookkeeper() if not bookkeeper.annotator.policy.look_inside_graph(graph): return cannot_follow_call(bookkeeper, graph, args_hs, RESULT) # recursive call from the entry point to itself: ignore them and # just hope the annotations are correct if (bookkeeper.getdesc(graph)._cache.get(None, None) is bookkeeper.annotator.translator.graphs[0]): return variableoftype(RESULT) myorigin = bookkeeper.myorigin() myorigin.__class__ = CallOpOriginFlags # thud fixed = myorigin.read_fixed() tsgraphs_accum = [] hs_res = bookkeeper.graph_call(graph, fixed, args_hs, tsgraphs_accum) myorigin.any_called_graph = tsgraphs_accum[0] if isinstance(hs_res, SomeLLAbstractConstant): hs_res.myorigin = myorigin # we need to make sure that hs_res does not become temporarily less # general as a result of calling another specialized version of the # function return annmodel.unionof(hs_res, bookkeeper.current_op_binding())
def memo(funcdesc, arglist_s): from pypy.annotation.model import SomePBC, SomeImpossibleValue, SomeBool from pypy.annotation.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 Exception("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 Exception("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 Exception("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 mergeinputargs(self, graph, block, inputcells, called_from_graph=None): # Merge the new 'cells' with each of the block's existing input # variables. oldcells = [self.binding(a) for a in block.inputargs] unions = [annmodel.unionof(c1,c2) for c1, c2 in zip(oldcells,inputcells)] # if the merged cells changed, we must redo the analysis if unions != oldcells: self.bindinputargs(graph, block, unions, called_from_graph)
def unioncheck(*somevalues): s_value = unionof(*somevalues) if isdegenerated(s_value): if not getattr(TLS, 'no_side_effects_in_union', 0): bookkeeper = getbookkeeper() if bookkeeper is not None: bookkeeper.ondegenerated('union', s_value) return s_value
def generalize_key(self, s_key): new_key_type = model.unionof(self.key_type, s_key) updated = new_key_type != self.key_type if updated: self.key_type = new_key_type for position_key in self.key_read_locations: self.bookkeeper.annotator.reflowfromposition(position_key) self.emulate_rdict_calls()
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 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 __call__(self, *args): args = args[1:] assert len(self.s_args) == len(args),\ "Function %s expects %d arguments, got %d instead" % (self.name, len(self.s_args), len(args)) for num, (arg, expected) in enumerate(zip(args, self.s_args)): res = unionof(arg, expected) assert expected.contains(res) return self.s_retval
def union((gencall, pbc)): for desc in pbc.descriptions: unique_key = desc bk = desc.bookkeeper s_result = bk.emulate_pbc_call(unique_key, pbc, gencall.args_s) s_result = unionof(s_result, gencall.s_result) assert gencall.s_result.contains(s_result) gencall.descriptions.update(pbc.descriptions) return gencall
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 _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.makerepr(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 compute_result_annotation(self, *args_s): if hasattr(self, 'ann_hook'): self.ann_hook() if self.signature_args is not None: assert len(args_s) == len(self.signature_args),\ "Argument number mismatch" for arg, expected in zip(args_s, self.signature_args): arg = unionof(arg, expected) assert expected.contains(arg) return self.signature_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) if isdegenerated(s_new_value): self.bookkeeper.ondegenerated("source %r attr %s" % (source, self.name), s_new_value) self.s_value = s_new_value
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.makerepr(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 merge(self, other, classdef='?'): assert self.name == other.name s_new_value = unionof(self.s_value, other.s_value) if isdegenerated(s_new_value): what = "%s attr %s" % (classdef, self.name) self.bookkeeper.ondegenerated(what, s_new_value) self.s_value = s_new_value if not other.readonly: self.modified(classdef) self.read_locations.update(other.read_locations)
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 merge(self, other): if self is not other: if getattr(TLS, 'no_side_effects_in_union', 0): raise UnionError("merging list/dict items") 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 if isdegenerated(s_new_value): if self.bookkeeper: self.bookkeeper.ondegenerated(self, s_new_value) elif other.bookkeeper: other.bookkeeper.ondegenerated(other, s_new_value) 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 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 generalize(self, s_other_value): s_new_value = unionof(self.s_value, s_other_value) if isdegenerated(s_new_value) and self.bookkeeper: self.bookkeeper.ondegenerated(self, s_new_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 direct_call(hs_f1, *args_hs): bookkeeper = getbookkeeper() fnobj = hs_f1.const._obj if (bookkeeper.annotator.policy.oopspec and hasattr(fnobj._callable, 'oopspec')): # try to handle the call as a high-level operation try: return handle_highlevel_operation(bookkeeper, fnobj._callable, *args_hs) except NotImplementedError: pass # don't try to annotate suggested_primitive graphs if getattr(getattr(fnobj, '_callable', None), 'suggested_primitive', False): return variableoftype(lltype.typeOf(fnobj).RESULT) # normal call if not hasattr(fnobj, 'graph'): raise NotImplementedError("XXX call to externals or primitives") if not bookkeeper.annotator.policy.look_inside_graph(fnobj.graph): return cannot_follow_call(bookkeeper, fnobj.graph, args_hs, lltype.typeOf(fnobj).RESULT) # recursive call from the entry point to itself: ignore them and # just hope the annotations are correct if (bookkeeper.getdesc(fnobj.graph)._cache.get(None, None) is bookkeeper.annotator.translator.graphs[0]): return variableoftype(lltype.typeOf(fnobj).RESULT) myorigin = bookkeeper.myorigin() myorigin.__class__ = CallOpOriginFlags # thud fixed = myorigin.read_fixed() tsgraphs_accum = [] hs_res = bookkeeper.graph_call(fnobj.graph, fixed, args_hs, tsgraphs_accum) myorigin.any_called_graph = tsgraphs_accum[0] if isinstance(hs_res, SomeLLAbstractConstant): hs_res.myorigin = myorigin ## elif fnobj.graph.name.startswith('ll_stritem'): ## if isinstance(hs_res, SomeLLAbstractVariable): ## print hs_res ## import pdb; pdb.set_trace() # we need to make sure that hs_res does not become temporarily less # general as a result of calling another specialized version of the # function return annmodel.unionof(hs_res, bookkeeper.current_op_binding())
def graph_family_call(self, graph_list, fixed, args_hs, tsgraphs_accum=None, hs_callable=None): if tsgraphs_accum is None: tsgraphs = [] else: tsgraphs = tsgraphs_accum results_hs = [] for graph in graph_list: results_hs.append(self.graph_call(graph, fixed, args_hs, tsgraphs, hs_callable)) # put the tsgraphs in the same call family call_families = self.tsgraph_maximal_call_families _, rep, callfamily = call_families.find(tsgraphs[0]) for tsgraph in tsgraphs[1:]: _, rep, callfamily = call_families.union(rep, tsgraph) return annmodel.unionof(*results_hs)
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) # Some specializations may break the invariant of returning # annotations that are always more general than the previous time. # We restore it here: from pypy.annotation.model import unionof result = unionof(result, s_previous_result) return result
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 = 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 addpendingblock(self, graph, block, cells, called_from_graph=None): """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(graph.getargs(), cells): s_oldarg = self.binding(a) assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg else: assert not self.frozen for a in cells: assert isinstance(a, annmodel.SomeObject) if block not in self.annotated: self.bindinputargs(graph, block, cells, called_from_graph) else: self.mergeinputargs(graph, block, cells, called_from_graph) if not self.annotated[block]: self.pendingblocks[block] = graph
def _call_multiple_graphs(hs_v1, graph_list, RESULT, *args_hs): if graph_list is None: # cannot follow indirect calls to unknown targets return variableoftype(RESULT) bookkeeper = getbookkeeper() myorigin = bookkeeper.myorigin() myorigin.__class__ = CallOpOriginFlags # thud fixed = myorigin.read_fixed() tsgraphs_accum = [] hs_res = bookkeeper.graph_family_call(graph_list, fixed, args_hs, tsgraphs_accum, hs_v1) myorigin.any_called_graph = tsgraphs_accum[0] if isinstance(hs_res, SomeLLAbstractConstant): hs_res.myorigin = myorigin # we need to make sure that hs_res does not become temporarily less # general as a result of calling another specialized version of the # function return annmodel.unionof(hs_res, bookkeeper.current_op_binding())
def create_class_constructors(annotator): bk = annotator.bookkeeper call_families = bk.pbc_maximal_call_families for family in call_families.infos(): if len(family.descs) <= 1: continue descs = family.descs.keys() if not isinstance(descs[0], description.ClassDesc): continue # Note that if classes are in the same callfamily, their __init__ # attribute must be in the same attrfamily as well. change = descs[0].mergeattrfamilies(descs[1:], '__init__') if hasattr(descs[0].getuniqueclassdef(), 'my_instantiate_graph'): assert not change, "after the fact change to a family of classes" # minimal sanity check continue # Put __init__ into the attr family, for ClassesPBCRepr.call() attrfamily = descs[0].getattrfamily('__init__') inits_s = [desc.s_read_attribute('__init__') for desc in descs] s_value = annmodel.unionof(attrfamily.s_value, *inits_s) attrfamily.s_value = s_value # ClassesPBCRepr.call() will also need instantiate() support for desc in descs: bk.needs_generic_instantiate[desc.getuniqueclassdef()] = True