def test_func_simple(): # -------------------- flowgraph building -------------------- # def f(x): # return x+1 x = Variable("x") x.concretetype = Signed result = Variable("result") result.concretetype = Signed one = Constant(1) one.concretetype = Signed op = SpaceOperation("int_add", [x, one], result) block = Block([x]) graph = FunctionGraph("f", block) block.operations.append(op) block.closeblock(Link([result], graph.returnblock)) graph.getreturnvar().concretetype = Signed # -------------------- end -------------------- F = FuncType([Signed], Signed) f = functionptr(F, "f", graph=graph) db = LowLevelDatabase() db.get(f) db.complete() dump_on_stdout(db) S = GcStruct('testing', ('fptr', Ptr(F))) s = malloc(S) s.fptr = f db = LowLevelDatabase() db.get(s) db.complete() dump_on_stdout(db)
def test_regalloc_lists(self): v1 = Variable() v1.concretetype = lltype.Signed v2 = Variable() v2.concretetype = lltype.Signed v3 = Variable() v3.concretetype = lltype.Signed v4 = Variable() v4.concretetype = lltype.Signed v5 = Variable() v5.concretetype = lltype.Signed block = Block([v1]) block.operations = [ SpaceOperation('int_add', [v1, Constant(1, lltype.Signed)], v2), SpaceOperation('rescall', [ListOfKind('int', [v1, v2])], v5), SpaceOperation('rescall', [ListOfKind('int', [v1, v2])], v3), ] graph = FunctionGraph('f', block, v4) block.closeblock(Link([v3], graph.returnblock)) # self.check_assembler( graph, """ int_add %i0, $1 -> %i1 rescall I[%i0, %i1] -> %i2 rescall I[%i0, %i1] -> %i0 int_return %i0 """)
def test_funny_links(): from pypy.objspace.flow.model import Block, FunctionGraph, \ SpaceOperation, Variable, Constant, Link for i in range(2): v_i = Variable("i") v_case = Variable("case") block = Block([v_i]) g = FunctionGraph("is_one", block) block.operations.append( SpaceOperation("eq", [v_i, Constant(1)], v_case)) block.exitswitch = v_case tlink = Link([Constant(1)], g.returnblock, True) flink = Link([Constant(0)], g.returnblock, False) links = [tlink, flink] if i: links.reverse() block.closeblock(*links) t = TranslationContext() a = t.buildannotator() a.build_graph_types(g, [annmodel.SomeInteger()]) rtyper = t.buildrtyper() rtyper.specialize() interp = LLInterpreter(rtyper) assert interp.eval_graph(g, [1]) == 1 assert interp.eval_graph(g, [0]) == 0
def dispatcher(self, shape, index, argtypes, resulttype): key = shape, index, tuple(argtypes), resulttype if key in self._dispatch_cache: return self._dispatch_cache[key] from pypy.translator.unsimplify import varoftype from pypy.objspace.flow.model import FunctionGraph, Link, Block, SpaceOperation inputargs = [varoftype(t) for t in [Char] + argtypes] startblock = Block(inputargs) startblock.exitswitch = inputargs[0] graph = FunctionGraph("dispatcher", startblock, varoftype(resulttype)) row_of_graphs = self.callfamily.calltables[shape][index] links = [] descs = list(self.s_pbc.descriptions) if self.s_pbc.can_be_None: descs.insert(0, None) for desc in descs: if desc is None: continue args_v = [varoftype(t) for t in argtypes] b = Block(args_v) llfn = self.rtyper.getcallable(row_of_graphs[desc]) v_fn = inputconst(typeOf(llfn), llfn) v_result = varoftype(resulttype) b.operations.append( SpaceOperation("direct_call", [v_fn] + args_v, v_result)) b.closeblock(Link([v_result], graph.returnblock)) i = self.descriptions.index(desc) links.append(Link(inputargs[1:], b, chr(i))) links[-1].llexitcase = chr(i) startblock.closeblock(*links) self.rtyper.annotator.translator.graphs.append(graph) ll_ret = self.rtyper.type_system.getcallable(graph) #FTYPE = FuncType c_ret = self._dispatch_cache[key] = inputconst(typeOf(ll_ret), ll_ret) return c_ret
def create_instantiate_function(annotator, classdef): # build the graph of a function that looks like # # def my_instantiate(): # return instantiate(cls) # if hasattr(classdef, 'my_instantiate_graph'): return v = Variable() block = Block([]) block.operations.append(SpaceOperation('instantiate1', [], v)) name = valid_identifier('instantiate_'+classdef.name) graph = FunctionGraph(name, block) block.closeblock(Link([v], graph.returnblock)) annotator.setbinding(v, annmodel.SomeInstance(classdef)) annotator.annotated[block] = graph # force the result to be converted to a generic OBJECTPTR generalizedresult = annmodel.SomeInstance(classdef=None) annotator.setbinding(graph.getreturnvar(), generalizedresult) classdef.my_instantiate_graph = graph annotator.translator.graphs.append(graph)
def build_callback_graph(self, graph, metadesccls=False): args_v = [copyvar(None, v) for v in graph.getargs()] v_res = copyvar(None, graph.getreturnvar()) rtyper = self.bookkeeper.annotator.base_translator.rtyper # fish fnptr = rtyper.getcallable(graph) v_ptr = Constant(fnptr, lltype.typeOf(fnptr)) newstartblock = Block(args_v) if metadesccls: v_metadesccls = Constant(metadesccls, lltype.Void) args_v = [v_metadesccls] + args_v opname = 'ts_metacall' suffix = 'ts_metacall' else: opname = 'direct_call' suffix = 'ts_stub' newstartblock.operations.append( SpaceOperation(opname, [v_ptr] + args_v, v_res)) newgraph = FunctionGraph('%s_%s' % (graph.name, suffix), newstartblock) newgraph.getreturnvar().concretetype = v_res.concretetype newstartblock.closeblock(Link([v_res], newgraph.returnblock)) return newgraph
def create_proxy_graph(self, op): """ creates a graph which calls the original function, checks for raised exceptions, fetches and then raises them again. If this graph is inlined, the correct exception matching blocks are produced.""" # XXX slightly annoying: construct a graph by hand # but better than the alternative result = copyvar(None, op.result) opargs = [] inputargs = [] callargs = [] ARGTYPES = [] for var in op.args: if isinstance(var, Variable): v = Variable() v.concretetype = var.concretetype inputargs.append(v) opargs.append(v) callargs.append(var) ARGTYPES.append(var.concretetype) else: opargs.append(var) newop = SpaceOperation(op.opname, opargs, result) startblock = Block(inputargs) startblock.operations.append(newop) newgraph = FunctionGraph("dummy_exc1", startblock) startblock.closeblock(Link([result], newgraph.returnblock)) newgraph.returnblock.inputargs[0].concretetype = op.result.concretetype self.gen_exc_check(startblock, newgraph.returnblock) excblock = Block([]) llops = rtyper.LowLevelOpList(None) var_value = self.gen_getfield('exc_value', llops) var_type = self.gen_getfield('exc_type', llops) # c_check1 = self.c_assertion_error_ll_exc_type c_check2 = self.c_n_i_error_ll_exc_type llops.genop('debug_catch_exception', [var_type, c_check1, c_check2]) # self.gen_setfield('exc_value', self.c_null_evalue, llops) self.gen_setfield('exc_type', self.c_null_etype, llops) excblock.operations[:] = llops newgraph.exceptblock.inputargs[ 0].concretetype = self.lltype_of_exception_type newgraph.exceptblock.inputargs[ 1].concretetype = self.lltype_of_exception_value excblock.closeblock(Link([var_type, var_value], newgraph.exceptblock)) startblock.exits[True].target = excblock startblock.exits[True].args = [] fptr = self.constant_func("dummy_exc1", ARGTYPES, op.result.concretetype, newgraph) return newgraph, SpaceOperation("direct_call", [fptr] + callargs, op.result)
def test_switch_no_default(): from pypy.objspace.flow.model import FunctionGraph, Block, Constant, Link from pypy.rpython.lltypesystem.lltype import FuncType, Signed, functionptr from pypy.translator.unsimplify import varoftype block = Block([varoftype(Signed)]) block.exitswitch = block.inputargs[0] graph = FunctionGraph("t", block, varoftype(Signed)) links = [] for i in range(10): links.append(Link([Constant(i*i, Signed)], graph.returnblock, i)) links[-1].llexitcase = i block.closeblock(*links) fptr = functionptr(FuncType([Signed], Signed), "t", graph=graph) def func(x): return fptr(x) f = compile_function(func, [int]) res = f(4) assert res == 16
def test_regalloc_exitswitch_2(self): v1 = Variable() v1.concretetype = rclass.CLASSTYPE v2 = Variable() v2.concretetype = rclass.CLASSTYPE v3 = Variable() v3.concretetype = rclass.CLASSTYPE v4 = Variable() v4.concretetype = rclass.CLASSTYPE block = Block([]) block.operations = [ SpaceOperation('res_call', [], v1), SpaceOperation('-live-', [], None), ] graph = FunctionGraph('f', block, v4) exclink = Link([v2], graph.returnblock) exclink.llexitcase = 123 # normally an exception class exclink.last_exception = v2 exclink.last_exc_value = "unused" block.exitswitch = c_last_exception block.closeblock(Link([v1], graph.returnblock), exclink) # self.check_assembler( graph, """ res_call -> %i0 -live- catch_exception L1 int_return %i0 --- L1: goto_if_exception_mismatch $123, L2 last_exception -> %i0 int_return %i0 --- L2: reraise """)
def new_wrapper(func, translator, newname=None): # The basic idea is to produce a flow graph from scratch, using the # help of the rtyper for the conversion of the arguments after they # have been decoded. bk = translator.annotator.bookkeeper graph = bk.getdesc(func).getuniquegraph() f = getfunctionptr(graph) FUNCTYPE = typeOf(f).TO newops = LowLevelOpList(translator.rtyper) varguments = [] for var in graph.startblock.inputargs: v = Variable(var) v.concretetype = PyObjPtr varguments.append(v) wrapper_inputargs = varguments[:] # use the rtyper to produce the conversions inputargs = f._obj.graph.getargs() for i in range(len(varguments)): if FUNCTYPE.ARGS[i] != PyObjPtr: rtyper = translator.rtyper r_arg = rtyper.bindingrepr(inputargs[i]) # give the rtyper a chance to know which function we are wrapping rtyper.set_wrapper_context(func) varguments[i] = newops.convertvar(varguments[i], r_from = pyobj_repr, r_to = r_arg) rtyper.set_wrapper_context(None) vlist = [inputconst(typeOf(f), f)] + varguments vresult = newops.genop('direct_call', vlist, resulttype=FUNCTYPE.RESULT) if FUNCTYPE.RESULT != PyObjPtr: # convert "result" back to a PyObject rtyper = translator.rtyper assert rtyper is not None, ( "needs the rtyper to perform function result conversions") r_result = rtyper.bindingrepr(f._obj.graph.getreturnvar()) vresult = newops.convertvar(vresult, r_from = r_result, r_to = pyobj_repr) # "return result" block = Block(wrapper_inputargs) wgraph = FunctionGraph('pyfn_' + (newname or func.func_name), block) translator.update_call_graph(wgraph, graph, object()) translator.graphs.append(wgraph) block.operations[:] = newops block.closeblock(Link([vresult], wgraph.returnblock)) wgraph.getreturnvar().concretetype = PyObjPtr checkgraph(wgraph) # the above convertvar()s may have created and annotated new helpers # that need to be specialized now translator.rtyper.specialize_more_blocks() return functionptr(FuncType([PyObjPtr] * len(wrapper_inputargs), PyObjPtr), wgraph.name, graph = wgraph, exception_policy = "CPython")
def build_pytypeobject(r_inst): rtyper = r_inst.rtyper cache = rtyper.classdef_to_pytypeobject try: return cache[r_inst.classdef] except KeyError: for parentdef in r_inst.classdef.getmro(): cpytype = parentdef._cpy_exported_type_ if cpytype is not None: break else: # for classes that cannot be exported at all return lltype.nullptr(lltype.PyObject) from pypy.rpython.lltypesystem.rclass import CPYOBJECTPTR from pypy.rpython.rtyper import LowLevelOpList typetype = lltype.pyobjectptr(type) # XXX default tp_new should go away # make the graph of tp_new manually v1 = Variable('tp'); v1.concretetype = lltype.Ptr(PY_TYPE_OBJECT) v2 = Variable('args'); v2.concretetype = PyObjPtr v3 = Variable('kwds'); v3.concretetype = PyObjPtr block = Block([v1, v2, v3]) llops = LowLevelOpList(None) v4 = r_inst.new_instance(llops, v_cpytype = v1) v5 = llops.genop('cast_pointer', [v4], resulttype = PyObjPtr) block.operations = list(llops) tp_new_graph = FunctionGraph('ll_tp_new', block) block.closeblock(Link([v5], tp_new_graph.returnblock)) tp_new_graph.getreturnvar().concretetype = v5.concretetype # build the PyTypeObject structure pytypeobj = lltype.malloc(PY_TYPE_OBJECT, flavor='cpy', extra_args=(typetype,)) name = cpytype.name T = lltype.FixedSizeArray(lltype.Char, len(name)+1) p = lltype.malloc(T, immortal=True) for i in range(len(name)): p[i] = name[i] p[len(name)] = '\x00' pytypeobj.c_tp_name = lltype.direct_arrayitems(p) pytypeobj.c_tp_basicsize = llmemory.sizeof(r_inst.lowleveltype.TO) if cpytype.subclassable and False: # XXX deallocation of subclass object segfaults! pytypeobj.c_tp_flags = CDefinedIntSymbolic('''(Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES | Py_TPFLAGS_BASETYPE)''') else: pytypeobj.c_tp_flags = CDefinedIntSymbolic('''(Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES)''') pytypeobj.c_tp_new = rtyper.type_system.getcallable(tp_new_graph) pytypeobj.c_tp_dealloc = rtyper.annotate_helper_fn(ll_tp_dealloc, [PyObjPtr]) pytypeobj.c_tp_as_number = lltype.malloc(PyNumberMethods, immortal=True) pytypeobj.c_tp_as_sequence = lltype.malloc(PySequenceMethods, immortal=True) pytypeobj.c_tp_as_mapping = lltype.malloc(PyMappingMethods, immortal=True) result = lltype.cast_pointer(PyObjPtr, pytypeobj) # the llsetup function that will store the 'objects' into the # type's tp_dict Py_TPFLAGS_HEAPTYPE = CDefinedIntSymbolic('Py_TPFLAGS_HEAPTYPE') if cpytype.objects: objects = [(lltype.pyobjectptr(name), value) for name, value in cpytype.objects.items() if name != '__new__'] if '__new__' in cpytype.objects: new = cpytype.objects['__new__']._obj.value objects.append((lltype.pyobjectptr('__new__'), lltype.pyobjectptr(staticmethod(new)))) def ll_type_setup(p): tp = lltype.cast_pointer(lltype.Ptr(PY_TYPE_OBJECT), p) old_flags = tp.c_tp_flags tp.c_tp_flags |= Py_TPFLAGS_HEAPTYPE for name, value in objects: llop.setattr(PyObjPtr, tp, name, value) tp.c_tp_flags = old_flags result._obj.setup_fnptr = rtyper.annotate_helper_fn(ll_type_setup, [PyObjPtr]) cache[r_inst.classdef] = result return result