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 rpython.translator.unsimplify import varoftype from rpython.flowspace.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 = getfunctionptr(graph) #FTYPE = FuncType c_ret = self._dispatch_cache[key] = inputconst(typeOf(ll_ret), ll_ret) return c_ret
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_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 test_funny_links(): from rpython.flowspace.model import Block, FunctionGraph, \ Variable, Constant, Link from rpython.flowspace.operation import op for i in range(2): v_i = Variable("i") block = Block([v_i]) g = FunctionGraph("is_one", block) op1 = op.eq(v_i, Constant(1)) block.operations.append(op1) block.exitswitch = op1.result 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 call_final_function(translator, final_func, annhelper=None): """When the program finishes normally, call 'final_func()'.""" from rpython.annotator import model as annmodel from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator own_annhelper = (annhelper is None) if own_annhelper: annhelper = MixLevelHelperAnnotator(translator.rtyper) c_final_func = annhelper.constfunc(final_func, [], annmodel.s_None) if own_annhelper: annhelper.finish() entry_point = translator.entry_point_graph v = entry_point.getreturnvar().copy() extrablock = Block([v]) v_none = varoftype(lltype.Void) newop = SpaceOperation('direct_call', [c_final_func], v_none) extrablock.operations = [newop] extrablock.closeblock(Link([v], entry_point.returnblock)) for block in entry_point.iterblocks(): if block is not extrablock: for link in block.exits: if link.target is entry_point.returnblock: link.target = extrablock checkgraph(entry_point)
def make_dispatcher(self, shape, index, argtypes, resulttype): 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) return graph
def builder(translator, func): # build a hacked graph that doesn't take a *arg any more, but # individual extra arguments graph = translator.buildflowgraph(func) argnames, vararg, kwarg = graph.signature assert vararg, "graph should have a *arg at this point" assert not kwarg, "where does this **arg come from??" argscopy = [Variable(v) for v in graph.getargs()] starargs = [ Variable('stararg%d' % i) for i in range(nb_extra_args) ] newstartblock = Block(argscopy[:-1] + starargs) newtup = op.newtuple(*starargs) newtup.result = argscopy[-1] newstartblock.operations.append(newtup) newstartblock.closeblock(Link(argscopy, graph.startblock)) graph.startblock = newstartblock argnames = argnames + ['.star%d' % i for i in range(nb_extra_args)] graph.signature = Signature(argnames) # note that we can mostly ignore defaults: if nb_extra_args > 0, # then defaults aren't applied. if nb_extra_args == 0, then this # just removes the *arg and the defaults keep their meaning. if nb_extra_args > 0: graph.defaults = None # shouldn't be used in this case checkgraph(graph) return graph
def call_final_function(translator, final_func, annhelper=None): """When the program finishes normally, call 'final_func()'.""" from rpython.annotator import model as annmodel from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator own_annhelper = (annhelper is None) if own_annhelper: annhelper = MixLevelHelperAnnotator(translator.rtyper) c_final_func = annhelper.constfunc(final_func, [], annmodel.s_None) if own_annhelper: annhelper.finish() entry_point = translator.entry_point_graph v = copyvar(translator.annotator, entry_point.getreturnvar()) extrablock = Block([v]) v_none = varoftype(lltype.Void) newop = SpaceOperation('direct_call', [c_final_func], v_none) extrablock.operations = [newop] extrablock.closeblock(Link([v], entry_point.returnblock)) for block in entry_point.iterblocks(): if block is not extrablock: for link in block.exits: if link.target is entry_point.returnblock: link.target = extrablock checkgraph(entry_point)
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 insert_along_link(link, opname, args, cache): b2 = link.target if b2 not in cache: newblock = Block([v.copy() for v in b2.inputargs]) newblock.operations.append( SpaceOperation(opname, args, varoftype(lltype.Void))) newblock.closeblock(Link(list(newblock.inputargs), b2)) cache[b2] = newblock link.target = cache[b2]
def copy_block(self, block): if block in self._copied_blocks: return self._copied_blocks[block] args = ([self.get_new_name(var) for var in block.inputargs] + self.passon_vars(block)) newblock = Block(args) self._copied_blocks[block] = newblock newblock.operations = [self.copy_operation(op) for op in block.operations] newblock.closeblock(*[self.copy_link(link, block) for link in block.exits]) newblock.exitswitch = self.get_new_name(block.exitswitch) self.search_for_calls(newblock) return newblock
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 = op.result.copy() 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 replace_graph_with_bootstrap(GeneratorIterator, graph): Entry = GeneratorIterator.Entry newblock = Block(graph.startblock.inputargs) op_entry = op.simple_call(const(Entry)) v_entry = op_entry.result newblock.operations.append(op_entry) assert len(graph.startblock.inputargs) == len(Entry.varnames) for v, name in zip(graph.startblock.inputargs, Entry.varnames): newblock.operations.append(op.setattr(v_entry, Constant(name), v)) op_generator = op.simple_call(const(GeneratorIterator), v_entry) newblock.operations.append(op_generator) newblock.closeblock(Link([op_generator.result], graph.returnblock)) graph.startblock = newblock
def test_rename_on_links(): v1 = Variable() v2 = Variable() v2.concretetype = llmemory.Address v3 = Variable() block = Block([v1]) block.operations = [SpaceOperation("cast_pointer", [v1], v2)] block2 = Block([v3]) block.closeblock(Link([v2], block2)) Transformer().optimize_block(block) assert block.inputargs == [v1] assert block.operations == [] assert block.exits[0].target is block2 assert block.exits[0].args == [v1]
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 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 replace_graph_with_bootstrap(GeneratorIterator, graph): Entry = GeneratorIterator.Entry newblock = Block(graph.startblock.inputargs) v_generator = Variable('generator') v_entry = Variable('entry') newblock.operations.append( SpaceOperation('simple_call', [Constant(Entry)], v_entry)) assert len(graph.startblock.inputargs) == len(Entry.varnames) for v, name in zip(graph.startblock.inputargs, Entry.varnames): newblock.operations.append( SpaceOperation('setattr', [v_entry, Constant(name), v], Variable())) newblock.operations.append( SpaceOperation('simple_call', [Constant(GeneratorIterator), v_entry], v_generator)) newblock.closeblock(Link([v_generator], graph.returnblock)) graph.startblock = newblock
def test_promote_2(): v1 = varoftype(lltype.Signed) v2 = varoftype(lltype.Signed) op = SpaceOperation("hint", [v1, Constant({"promote": True}, lltype.Void)], v2) returnblock = Block([varoftype(lltype.Signed)]) returnblock.operations = () block = Block([v1]) block.operations = [op] block.closeblock(Link([v2], returnblock)) Transformer().optimize_block(block) assert len(block.operations) == 2 assert block.operations[0].opname == "-live-" assert block.operations[0].args == [] assert block.operations[1].opname == "int_guard_value" assert block.operations[1].args == [v1] assert block.operations[1].result is None assert block.exits[0].args == [v1]
def get_exc_reconstruction_block(self, typedesc): exceptblock = self.graph.exceptblock self.mallocv.fixup_except_block(exceptblock) TEXC = exceptblock.inputargs[0].concretetype TVAL = exceptblock.inputargs[1].concretetype # v_ignored_type = varoftype(TEXC) v_incoming_value = varoftype(TVAL) block = Block([v_ignored_type, v_incoming_value]) # c_EXCTYPE = Constant(typedesc.MALLOCTYPE, lltype.Void) v = varoftype(lltype.Ptr(typedesc.MALLOCTYPE)) c_flavor = Constant({'flavor': 'gc'}, lltype.Void) op = SpaceOperation('malloc', [c_EXCTYPE, c_flavor], v) block.operations.append(op) # for name, FIELDTYPE in typedesc.names_and_types: EXACTPTR = lltype.Ptr(typedesc.name2subtype[name]) c_name = Constant(name) c_name.concretetype = lltype.Void # v_in = varoftype(EXACTPTR) op = SpaceOperation('cast_pointer', [v_incoming_value], v_in) block.operations.append(op) # v_field = varoftype(FIELDTYPE) op = SpaceOperation('getfield', [v_in, c_name], v_field) block.operations.append(op) # v_out = varoftype(EXACTPTR) op = SpaceOperation('cast_pointer', [v], v_out) block.operations.append(op) # v0 = varoftype(lltype.Void) op = SpaceOperation('setfield', [v_out, c_name, v_field], v0) block.operations.append(op) # v_exc_value = varoftype(TVAL) op = SpaceOperation('cast_pointer', [v], v_exc_value) block.operations.append(op) # exc_type = self.mallocv.EXCTYPE_to_vtable[typedesc.MALLOCTYPE] c_exc_type = Constant(exc_type, TEXC) block.closeblock(Link([c_exc_type, v_exc_value], exceptblock)) return block
def test_SSA_to_SSI_2(): x = Variable('x') y = Variable('y') z = Variable('z') b1 = Block([x]) b2 = Block([y]) b3 = Block([]) b3.operations.append(SpaceOperation('hello', [y], z)) b1.closeblock(Link([x], b2), Link([], b3)) graph = FunctionGraph('x', b1) SSA_to_SSI(graph) assert b1.inputargs == [x] assert b2.inputargs == [y] assert b3.inputargs == [b3.operations[0].args[0]] assert b1.exits[0].args == [x] assert b1.exits[1].args == [x]
def test_regalloc_call(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 block = Block([v1]) block.operations = [ SpaceOperation('int_add', [v1, Constant(1, lltype.Signed)], v2), 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] -> %i0 int_return %i0 """)
def test_SSA_to_SSI_2(): x = Variable('x') y = Variable('y') z = Variable('z') b1 = Block([x]) b2 = Block([y]) b3 = Block([]) b3.operations.append(SpaceOperation('hello', [y], z)) b1.closeblock(Link([x], b2), Link([], b3)) SSA_to_SSI({b1: True, # reachable from outside b2: False, b3: False}) assert b1.inputargs == [x] assert b2.inputargs == [y] assert b3.inputargs == [b3.operations[0].args[0]] assert b1.exits[0].args == [x] assert b1.exits[1].args == [x]
def generic_exception_matching(self, afterblock, copiedexceptblock): #XXXXX don't look: insert blocks that do exception matching #for the cases where direct matching did not work exc_match = Constant( self.translator.rtyper.exceptiondata.fn_exception_match) exc_match.concretetype = typeOf(exc_match.value) blocks = [] for i, link in enumerate(afterblock.exits[1:]): etype = copyvar(None, copiedexceptblock.inputargs[0]) evalue = copyvar(None, copiedexceptblock.inputargs[1]) passon_vars = self.passon_vars(i) block = Block([etype, evalue] + passon_vars) res = Variable() res.concretetype = Bool cexitcase = Constant(link.llexitcase) cexitcase.concretetype = typeOf(cexitcase.value) args = [exc_match, etype, cexitcase] block.operations.append(SpaceOperation("direct_call", args, res)) block.exitswitch = res linkargs = self.find_args_in_exceptional_case(link, link.target, etype, evalue, afterblock, passon_vars) l = Link(linkargs, link.target) l.prevblock = block l.exitcase = True l.llexitcase = True block.closeblock(l) if i > 0: l = Link(blocks[-1].inputargs, block) l.exitcase = False l.llexitcase = False blocks[-1].recloseblock(l, *blocks[-1].exits) blocks.append(block) blocks[-1].recloseblock(*blocks[-1].exits[:1]) blocks[-1].operations = [] blocks[-1].exitswitch = None blocks[-1].exits[0].exitcase = None del blocks[-1].exits[0].llexitcase linkargs = copiedexceptblock.inputargs copiedexceptblock.recloseblock(Link(linkargs, blocks[0]))
def generic_exception_matching(self, afterblock, copiedexceptblock): #XXXXX don't look: insert blocks that do exception matching #for the cases where direct matching did not work exc_match = Constant( self.translator.rtyper.exceptiondata.fn_exception_match) exc_match.concretetype = typeOf(exc_match.value) blocks = [] for i, link in enumerate(afterblock.exits[1:]): etype = copiedexceptblock.inputargs[0].copy() evalue = copiedexceptblock.inputargs[1].copy() passon_vars = self.passon_vars(i) block = Block([etype, evalue] + passon_vars) res = Variable() res.concretetype = Bool cexitcase = Constant(link.llexitcase) cexitcase.concretetype = typeOf(cexitcase.value) args = [exc_match, etype, cexitcase] block.operations.append(SpaceOperation("direct_call", args, res)) block.exitswitch = res linkargs = self.find_args_in_exceptional_case(link, link.target, etype, evalue, afterblock, passon_vars) l = Link(linkargs, link.target) l.prevblock = block l.exitcase = True l.llexitcase = True block.closeblock(l) if i > 0: l = Link(blocks[-1].inputargs, block) l.exitcase = False l.llexitcase = False blocks[-1].recloseblock(l, *blocks[-1].exits) blocks.append(block) blocks[-1].recloseblock(*blocks[-1].exits[:1]) blocks[-1].operations = [] blocks[-1].exitswitch = None blocks[-1].exits[0].exitcase = None del blocks[-1].exits[0].llexitcase linkargs = copiedexceptblock.inputargs copiedexceptblock.recloseblock(Link(linkargs, blocks[0]))
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 test_SSA_to_SSI(): c = Variable('c') x = Variable('x') y = Variable('y') b1 = Block([c]) b2 = Block([x]) b3 = Block([]) graph = FunctionGraph('x', b1) b2.operations.append(SpaceOperation('add', [x, c], y)) b2.exitswitch = y b1.closeblock(Link([Constant(0)], b2)) b2.closeblock(Link([y], b2), Link([], b3)) b3.closeblock(Link([y, c], graph.exceptblock)) SSA_to_SSI(graph) assert len(b1.inputargs) == 1 assert len(b2.inputargs) == 2 assert len(b3.inputargs) == 2 assert b2.inputargs == b2.operations[0].args assert len(b1.exits[0].args) == 2 assert b1.exits[0].args[1] is c assert len(b2.exits[0].args) == 2 assert b2.exits[0].args == [y, b2.inputargs[1]] assert len(b2.exits[1].args) == 2 assert len(b3.exits[0].args) == 2 index = b3.inputargs.index(b3.exits[0].args[0]) assert b2.exits[1].args[index] is b2.operations[0].result index = b3.inputargs.index(b3.exits[0].args[1]) assert b2.exits[1].args[index] is b2.inputargs[1]
def insert_empty_block(link, newops=[]): """Insert and return a new block along the given link.""" vars = {} for v in link.args: if isinstance(v, Variable): vars[v] = True for op in newops: for v in op.args: if isinstance(v, Variable): vars.setdefault(v, True) vars[op.result] = False vars = [v for v, keep in vars.items() if keep] mapping = {} for v in vars: mapping[v] = v.copy() newblock = Block(vars) newblock.operations.extend(newops) newblock.closeblock(Link(link.args, link.target)) newblock.renamevariables(mapping) link.args[:] = vars link.target = newblock return newblock
def insert_empty_block(annotator, link, newops=[]): """Insert and return a new block along the given link.""" vars = {} for v in link.args: if isinstance(v, Variable): vars[v] = True for op in newops: for v in op.args: if isinstance(v, Variable): vars.setdefault(v, True) vars[op.result] = False vars = [v for v, keep in vars.items() if keep] mapping = {} for v in vars: mapping[v] = copyvar(annotator, v) newblock = Block(vars) newblock.operations.extend(newops) newblock.closeblock(Link(link.args, link.target)) newblock.renamevariables(mapping) link.args[:] = vars link.target = newblock return newblock
def call_initial_function(translator, initial_func, annhelper=None): """Before the program starts, call 'initial_func()'.""" from rpython.annotator import model as annmodel from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator own_annhelper = (annhelper is None) if own_annhelper: annhelper = MixLevelHelperAnnotator(translator.rtyper) c_initial_func = annhelper.constfunc(initial_func, [], annmodel.s_None) if own_annhelper: annhelper.finish() entry_point = translator.entry_point_graph args = [copyvar(translator.annotator, v) for v in entry_point.getargs()] extrablock = Block(args) v_none = varoftype(lltype.Void) newop = SpaceOperation('direct_call', [c_initial_func], v_none) extrablock.operations = [newop] extrablock.closeblock(Link(args, entry_point.startblock)) entry_point.startblock = extrablock checkgraph(entry_point)
def call_initial_function(translator, initial_func, annhelper=None): """Before the program starts, call 'initial_func()'.""" from rpython.annotator import model as annmodel from rpython.rtyper.lltypesystem import lltype from rpython.rtyper.annlowlevel import MixLevelHelperAnnotator own_annhelper = (annhelper is None) if own_annhelper: annhelper = MixLevelHelperAnnotator(translator.rtyper) c_initial_func = annhelper.constfunc(initial_func, [], annmodel.s_None) if own_annhelper: annhelper.finish() entry_point = translator.entry_point_graph args = [v.copy() for v in entry_point.getargs()] extrablock = Block(args) v_none = varoftype(lltype.Void) newop = SpaceOperation('direct_call', [c_initial_func], v_none) extrablock.operations = [newop] extrablock.closeblock(Link(args, entry_point.startblock)) entry_point.startblock = extrablock checkgraph(entry_point)
def builder(translator, func): # build a hacked graph that doesn't take a *arg any more, but # individual extra arguments graph = translator.buildflowgraph(func) argnames, vararg, kwarg = graph.signature assert vararg, "graph should have a *arg at this point" assert not kwarg, "where does this **arg come from??" argscopy = [Variable(v) for v in graph.getargs()] starargs = [Variable('stararg%d'%i) for i in range(nb_extra_args)] newstartblock = Block(argscopy[:-1] + starargs) newtup = op.newtuple(*starargs) newtup.result = argscopy[-1] newstartblock.operations.append(newtup) newstartblock.closeblock(Link(argscopy, graph.startblock)) graph.startblock = newstartblock argnames = argnames + ['.star%d' % i for i in range(nb_extra_args)] graph.signature = Signature(argnames) # note that we can mostly ignore defaults: if nb_extra_args > 0, # then defaults aren't applied. if nb_extra_args == 0, then this # just removes the *arg and the defaults keep their meaning. if nb_extra_args > 0: graph.defaults = None # shouldn't be used in this case checkgraph(graph) return graph
def test_SSA_to_SSI(): c = Variable('c') x = Variable('x') y = Variable('y') b1 = Block([c]) b2 = Block([x]) b3 = Block([]) b2.operations.append(SpaceOperation('add', [x, c], y)) b2.exitswitch = y b1.closeblock(Link([Constant(0)], b2)) b2.closeblock(Link([y], b2), Link([], b3)) b3.closeblock(Link([y, c], None)) SSA_to_SSI({b1: True, # reachable from outside b2: False, b3: False}) assert len(b1.inputargs) == 1 assert len(b2.inputargs) == 2 assert len(b3.inputargs) == 2 assert b2.inputargs == b2.operations[0].args assert len(b1.exits[0].args) == 2 assert b1.exits[0].args[1] is c assert len(b2.exits[0].args) == 2 assert b2.exits[0].args == [y, b2.inputargs[1]] assert len(b2.exits[1].args) == 2 assert len(b3.exits[0].args) == 2 index = b3.inputargs.index(b3.exits[0].args[0]) assert b2.exits[1].args[index] is b2.operations[0].result index = b3.inputargs.index(b3.exits[0].args[1]) assert b2.exits[1].args[index] is b2.inputargs[1]
def gen_exc_check(self, block, returnblock, normalafterblock=None): #var_exc_occured = Variable() #var_exc_occured.concretetype = lltype.Bool #block.operations.append(SpaceOperation("safe_call", [self.rpyexc_occured_ptr], var_exc_occured)) llops = rtyper.LowLevelOpList(None) spaceop = block.operations[-1] alloc_shortcut = self.check_for_alloc_shortcut(spaceop) if alloc_shortcut: var_no_exc = self.gen_nonnull(spaceop.result, llops) else: v_exc_type = self.gen_getfield('exc_type', llops) var_no_exc = self.gen_isnull(v_exc_type, llops) block.operations.extend(llops) block.exitswitch = var_no_exc #exception occurred case b = Block([]) b.operations = [SpaceOperation('debug_record_traceback', [], varoftype(lltype.Void))] l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock) b.closeblock(l) l = Link([], b) l.exitcase = l.llexitcase = False #non-exception case l0 = block.exits[0] l0.exitcase = l0.llexitcase = True block.recloseblock(l0, l) insert_zeroing_op = False if spaceop.opname == 'malloc': flavor = spaceop.args[1].value['flavor'] if flavor == 'gc': insert_zeroing_op = True elif spaceop.opname == 'malloc_nonmovable': # xxx we cannot insert zero_gc_pointers_inside after # malloc_nonmovable, because it can return null. For now # we simply always force the zero=True flag on # malloc_nonmovable. c_flags = spaceop.args[1] c_flags.value = c_flags.value.copy() spaceop.args[1].value['zero'] = True # NB. when inserting more special-cases here, keep in mind that # you also need to list the opnames in transform_block() # (see "special cases") if insert_zeroing_op: if normalafterblock is None: normalafterblock = insert_empty_block(None, l0) v_result = spaceop.result if v_result in l0.args: result_i = l0.args.index(v_result) v_result_after = normalafterblock.inputargs[result_i] else: v_result_after = copyvar(None, v_result) l0.args.append(v_result) normalafterblock.inputargs.append(v_result_after) normalafterblock.operations.insert( 0, SpaceOperation('zero_gc_pointers_inside', [v_result_after], varoftype(lltype.Void)))
def tweak_generator_body_graph(Entry, graph): # First, always run simplify_graph in order to reduce the number of # variables passed around simplify_graph(graph) insert_empty_startblock(graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) op0 = op.simple_call(const(StopIteration)) op1 = op.type(op0.result) stopblock.operations = [op0, op1] stopblock.closeblock(Link([op1.result, op0.result], graph.exceptblock)) # for block in list(graph.iterblocks()): for exit in block.exits: if exit.target is graph.returnblock: exit.args = [] exit.target = stopblock assert block is not stopblock for index in range(len(block.operations) - 1, -1, -1): hlop = block.operations[index] if hlop.opname == 'yield_': [v_yielded_value] = hlop.args del block.operations[index] newlink = split_block(block, index) newblock = newlink.target # class Resume(AbstractPosition): _immutable_ = True block = newblock Resume.__name__ = 'Resume%d' % len(mappings) mappings.append(Resume) varnames = get_variable_names(newlink.args) # _insert_reads(newblock, varnames) # op_resume = op.simple_call(const(Resume)) block.operations.append(op_resume) v_resume = op_resume.result for i, name in enumerate(varnames): block.operations.append( op.setattr(v_resume, const(name), newlink.args[i])) op_pair = op.newtuple(v_resume, v_yielded_value) block.operations.append(op_pair) newlink.args = [op_pair.result] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: op_check = op.isinstance(block.inputargs[0], const(Resume)) block.operations.append(op_check) block.exitswitch = op_check.result link1 = Link([block.inputargs[0]], Resume.block) link1.exitcase = True nextblock = Block([Variable('entry')]) link2 = Link([block.inputargs[0]], nextblock) link2.exitcase = False block.closeblock(link1, link2) block = nextblock block.closeblock( Link([ Constant(AssertionError), Constant(AssertionError("bad generator class")) ], graph.exceptblock)) graph.startblock = regular_entry_block graph.signature = Signature(['entry']) graph.defaults = () checkgraph(graph) eliminate_empty_blocks(graph)
class BlockSpecializer(object): def __init__(self, graphbuilder, v_expand_malloc=None): self.graphbuilder = graphbuilder self.v_expand_malloc = v_expand_malloc self.specblock = Block([]) def initialize_renamings(self, virtualframe, keep_inputargs=False): # we make a copy of the original 'virtualframe' because the # specialize_operations() will mutate some of its content. virtualframe = virtualframe.copy({}) self.virtualframe = virtualframe self.nodes = virtualframe.get_nodes_in_use() self.renamings = {} # {RuntimeSpecNode(): Variable()} if keep_inputargs: assert virtualframe.varlist == virtualframe.sourceblock.inputargs specinputargs = [] for i, rtnode in enumerate(virtualframe.find_rt_nodes()): if keep_inputargs: v = virtualframe.varlist[i] assert v.concretetype == rtnode.TYPE else: v = rtnode.newvar() self.renamings[rtnode] = v specinputargs.append(v) self.specblock.inputargs = specinputargs def setnode(self, v, node): assert v not in self.nodes self.nodes[v] = node def getnode(self, v): if isinstance(v, Variable): return self.nodes[v] else: return getconstnode(v, self.renamings) def rename_nonvirtual(self, v, where=None): if not isinstance(v, Variable): return v node = self.nodes[v] if not isinstance(node, RuntimeSpecNode): raise CannotVirtualize(where) return self.renamings[node] def expand_nodes(self, nodelist): rtnodes, vtnodes = find_all_nodes(nodelist) return [self.renamings[rtnode] for rtnode in rtnodes] def specialize_operations(self): newoperations = [] self.ops_produced_by_last_op = 0 # note that 'self.virtualframe' can be changed during the loop! while True: operations = self.virtualframe.sourceblock.operations try: op = operations[self.virtualframe.nextopindex] self.virtualframe.nextopindex += 1 except IndexError: break meth = getattr(self, 'handle_op_' + op.opname, self.handle_default) newops_for_this_op = meth(op) newoperations += newops_for_this_op self.ops_produced_by_last_op = len(newops_for_this_op) for op in newoperations: if op.opname == 'direct_call': graph = graph_called_by(op) if graph in self.virtualframe.calledgraphs: raise CannotVirtualize("recursion in residual call") self.specblock.operations = newoperations def follow_exits(self): block = self.virtualframe.sourceblock self.specblock.exitswitch = self.rename_nonvirtual( block.exitswitch, 'exitswitch') links = block.exits catch_exc = self.specblock.exitswitch == c_last_exception if not catch_exc and isinstance(self.specblock.exitswitch, Constant): # constant-fold the switch for link in links: if link.exitcase == 'default': break if link.llexitcase == self.specblock.exitswitch.value: break else: raise Exception("exit case not found?") links = (link, ) self.specblock.exitswitch = None if catch_exc and self.ops_produced_by_last_op == 0: # the last op of the sourceblock did not produce any # operation in specblock, so we need to discard the # exception-catching. catch_exc = False links = links[:1] assert links[0].exitcase is None # the non-exception-catching case self.specblock.exitswitch = None newlinks = [] for link in links: is_catch_link = catch_exc and link.exitcase is not None if is_catch_link: extravars = [] for attr in ['last_exception', 'last_exc_value']: v = getattr(link, attr) if isinstance(v, Variable): rtnode = RuntimeSpecNode(v, v.concretetype) self.setnode(v, rtnode) self.renamings[rtnode] = v = rtnode.newvar() extravars.append(v) linkargsnodes = [self.getnode(v1) for v1 in link.args] # newlink = self.graphbuilder.create_outgoing_link( self.virtualframe, link.target, linkargsnodes, self.renamings, self.v_expand_malloc) # if self.specblock.exitswitch is not None: newlink.exitcase = link.exitcase if hasattr(link, 'llexitcase'): newlink.llexitcase = link.llexitcase if is_catch_link: newlink.extravars(*extravars) newlinks.append(newlink) self.specblock.closeblock(*newlinks) def make_rt_result(self, v_result): newrtnode = RuntimeSpecNode(v_result, v_result.concretetype) self.setnode(v_result, newrtnode) v_new = newrtnode.newvar() self.renamings[newrtnode] = v_new return v_new def make_const_rt_result(self, v_result, value): newrtnode = RuntimeSpecNode(v_result, v_result.concretetype) self.setnode(v_result, newrtnode) if v_result.concretetype is not lltype.Void: assert v_result.concretetype == lltype.typeOf(value) c_value = Constant(value) c_value.concretetype = v_result.concretetype self.renamings[newrtnode] = c_value def handle_default(self, op): newargs = [self.rename_nonvirtual(v, op) for v in op.args] constresult = try_fold_operation(op.opname, newargs, op.result.concretetype) if constresult: self.make_const_rt_result(op.result, constresult[0]) return [] else: newresult = self.make_rt_result(op.result) return [SpaceOperation(op.opname, newargs, newresult)] def handle_unreachable(self, op): from rpython.rtyper.lltypesystem.rstr import string_repr msg = 'unreachable: %s' % (op, ) ll_msg = string_repr.convert_const(msg) c_msg = Constant(ll_msg, lltype.typeOf(ll_msg)) newresult = self.make_rt_result(op.result) return [SpaceOperation('debug_fatalerror', [c_msg], newresult)] def handle_op_getfield(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): fieldname = op.args[1].value index = node.typedesc.name2index[fieldname] self.setnode(op.result, node.fields[index]) return [] else: return self.handle_default(op) def handle_op_setfield(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): if node.readonly: raise ForcedInline(op) fieldname = op.args[1].value index = node.typedesc.name2index[fieldname] node.fields[index] = self.getnode(op.args[2]) return [] else: return self.handle_default(op) def handle_op_same_as(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): node = self.getnode(op.args[0]) self.setnode(op.result, node) return [] else: return self.handle_default(op) def handle_op_cast_pointer(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): node = self.getnode(op.args[0]) SOURCEPTR = lltype.Ptr(node.typedesc.MALLOCTYPE) TARGETPTR = op.result.concretetype try: if lltype.castable(TARGETPTR, SOURCEPTR) < 0: raise lltype.InvalidCast except lltype.InvalidCast: return self.handle_unreachable(op) self.setnode(op.result, node) return [] else: return self.handle_default(op) def handle_op_ptr_nonzero(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): self.make_const_rt_result(op.result, True) return [] else: return self.handle_default(op) def handle_op_ptr_iszero(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): self.make_const_rt_result(op.result, False) return [] else: return self.handle_default(op) def handle_op_ptr_eq(self, op): node0 = self.getnode(op.args[0]) node1 = self.getnode(op.args[1]) if (isinstance(node0, VirtualSpecNode) or isinstance(node1, VirtualSpecNode)): self.make_const_rt_result(op.result, node0 is node1) return [] else: return self.handle_default(op) def handle_op_ptr_ne(self, op): node0 = self.getnode(op.args[0]) node1 = self.getnode(op.args[1]) if (isinstance(node0, VirtualSpecNode) or isinstance(node1, VirtualSpecNode)): self.make_const_rt_result(op.result, node0 is not node1) return [] else: return self.handle_default(op) def handle_op_malloc(self, op): if op.result is self.v_expand_malloc: MALLOCTYPE = op.result.concretetype.TO typedesc = self.graphbuilder.mallocv.getmalloctypedesc(MALLOCTYPE) virtualnode = VirtualSpecNode(typedesc, []) self.setnode(op.result, virtualnode) for name, FIELDTYPE in typedesc.names_and_types: fieldnode = RuntimeSpecNode(name, FIELDTYPE) virtualnode.fields.append(fieldnode) c = Constant(FIELDTYPE._defl()) c.concretetype = FIELDTYPE self.renamings[fieldnode] = c self.v_expand_malloc = None # done return [] else: return self.handle_default(op) def handle_op_direct_call(self, op): graph = graph_called_by(op) if graph is None: return self.handle_default(op) nb_args = len(op.args) - 1 assert nb_args == len(graph.getargs()) newnodes = [self.getnode(v) for v in op.args[1:]] myframe = self.get_updated_frame(op) mallocv = self.graphbuilder.mallocv if op.result is self.v_expand_malloc: # move to inlining the callee, and continue looking for the # malloc to expand in the callee's graph op_to_remove = mallocv.inline_and_remove[graph] self.v_expand_malloc = op_to_remove.result return self.handle_inlined_call(myframe, graph, newnodes) argnodes = copynodes(newnodes, flagreadonly=myframe.find_vt_nodes()) kind, newgraph = mallocv.get_specialized_graph(graph, argnodes) if kind == 'trivial': return self.handle_default(op) elif kind == 'inline': return self.handle_inlined_call(myframe, graph, newnodes) elif kind == 'call': return self.handle_residual_call(op, newgraph, newnodes) elif kind == 'fail': raise CannotVirtualize(op) else: raise ValueError(kind) def get_updated_frame(self, op): sourceblock = self.virtualframe.sourceblock nextopindex = self.virtualframe.nextopindex self.nodes[op.result] = FutureReturnValue(op) myframe = VirtualFrame(sourceblock, nextopindex, self.nodes, self.virtualframe.callerframe, self.virtualframe.calledgraphs) del self.nodes[op.result] return myframe def handle_residual_call(self, op, newgraph, newnodes): fspecptr = getfunctionptr(newgraph) newargs = [Constant(fspecptr, concretetype=lltype.typeOf(fspecptr))] newargs += self.expand_nodes(newnodes) newresult = self.make_rt_result(op.result) newop = SpaceOperation('direct_call', newargs, newresult) return [newop] def handle_inlined_call(self, myframe, graph, newnodes): assert len(graph.getargs()) == len(newnodes) targetnodes = dict(zip(graph.getargs(), newnodes)) calledgraphs = myframe.calledgraphs.copy() if graph in calledgraphs: raise CannotVirtualize("recursion during inlining") calledgraphs[graph] = True calleeframe = VirtualFrame(graph.startblock, 0, targetnodes, myframe, calledgraphs) self.virtualframe = calleeframe self.nodes = calleeframe.get_nodes_in_use() return [] def handle_op_indirect_call(self, op): v_func = self.rename_nonvirtual(op.args[0], op) if isinstance(v_func, Constant): op = SpaceOperation('direct_call', [v_func] + op.args[1:-1], op.result) return self.handle_op_direct_call(op) else: return self.handle_default(op)
def _transform_hint_close_stack(self, fnptr): # We cannot easily pass variable amount of arguments of the call # across the call to the pypy_asm_stackwalk helper. So we store # them away and restore them. More precisely, we need to # replace 'graph' with code that saves the arguments, and make # a new graph that starts with restoring the arguments. if self._asmgcc_save_restore_arguments is None: self._asmgcc_save_restore_arguments = {} sradict = self._asmgcc_save_restore_arguments sra = [] # list of pointers to raw-malloced containers for args seen = {} FUNC1 = lltype.typeOf(fnptr).TO for TYPE in FUNC1.ARGS: if isinstance(TYPE, lltype.Ptr): TYPE = llmemory.Address num = seen.get(TYPE, 0) seen[TYPE] = num + 1 key = (TYPE, num) if key not in sradict: CONTAINER = lltype.FixedSizeArray(TYPE, 1) p = lltype.malloc(CONTAINER, flavor='raw', zero=True, immortal=True) sradict[key] = Constant(p, lltype.Ptr(CONTAINER)) sra.append(sradict[key]) # # make a copy of the graph that will reload the values graph = fnptr._obj.graph graph2 = copygraph(graph) # # edit the original graph to only store the value of the arguments block = Block(graph.startblock.inputargs) c_item0 = Constant('item0', lltype.Void) assert len(block.inputargs) == len(sra) for v_arg, c_p in zip(block.inputargs, sra): if isinstance(v_arg.concretetype, lltype.Ptr): v_adr = varoftype(llmemory.Address) block.operations.append( SpaceOperation("cast_ptr_to_adr", [v_arg], v_adr)) v_arg = v_adr v_void = varoftype(lltype.Void) block.operations.append( SpaceOperation("bare_setfield", [c_p, c_item0, v_arg], v_void)) # # call asm_stackwalk(graph2) FUNC2 = lltype.FuncType([], FUNC1.RESULT) fnptr2 = lltype.functionptr(FUNC2, fnptr._obj._name + '_reload', graph=graph2) c_fnptr2 = Constant(fnptr2, lltype.Ptr(FUNC2)) HELPERFUNC = lltype.FuncType([lltype.Ptr(FUNC2), ASM_FRAMEDATA_HEAD_PTR], FUNC1.RESULT) v_asm_stackwalk = varoftype(lltype.Ptr(HELPERFUNC), "asm_stackwalk") block.operations.append( SpaceOperation("cast_pointer", [c_asm_stackwalk], v_asm_stackwalk)) v_result = varoftype(FUNC1.RESULT) block.operations.append( SpaceOperation("indirect_call", [v_asm_stackwalk, c_fnptr2, c_gcrootanchor, Constant(None, lltype.Void)], v_result)) block.closeblock(Link([v_result], graph.returnblock)) graph.startblock = block # # edit the copy of the graph to reload the values block2 = graph2.startblock block1 = Block([]) reloadedvars = [] for v, c_p in zip(block2.inputargs, sra): v = v.copy() if isinstance(v.concretetype, lltype.Ptr): w = varoftype(llmemory.Address) else: w = v block1.operations.append(SpaceOperation('getfield', [c_p, c_item0], w)) if w is not v: block1.operations.append(SpaceOperation('cast_adr_to_ptr', [w], v)) reloadedvars.append(v) block1.closeblock(Link(reloadedvars, block2)) graph2.startblock = block1 # checkgraph(graph) checkgraph(graph2)
def gen_exc_check(self, block, returnblock, normalafterblock=None): llops = rtyper.LowLevelOpList(None) spaceop = block.operations[-1] alloc_shortcut = self.check_for_alloc_shortcut(spaceop) if alloc_shortcut: var_no_exc = self.gen_nonnull(spaceop.result, llops) else: v_exc_type = self.gen_getfield('exc_type', llops) var_no_exc = self.gen_isnull(v_exc_type, llops) # # We could add a "var_no_exc is likely true" hint, but it seems # not to help, so it was commented out again. #var_no_exc = llops.genop('likely', [var_no_exc], lltype.Bool) block.operations.extend(llops) block.exitswitch = var_no_exc #exception occurred case b = Block([]) b.operations = [ SpaceOperation('debug_record_traceback', [], varoftype(lltype.Void)) ] l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock) b.closeblock(l) l = Link([], b) l.exitcase = l.llexitcase = False #non-exception case l0 = block.exits[0] l0.exitcase = l0.llexitcase = True block.recloseblock(l0, l) insert_zeroing_op = False if spaceop.opname in ['malloc', 'malloc_varsize']: flavor = spaceop.args[1].value['flavor'] if flavor == 'gc': insert_zeroing_op = True true_zero = spaceop.args[1].value.get('zero', False) # NB. when inserting more special-cases here, keep in mind that # you also need to list the opnames in transform_block() # (see "special cases") if insert_zeroing_op: if normalafterblock is None: normalafterblock = insert_empty_block(None, l0) v_result = spaceop.result if v_result in l0.args: result_i = l0.args.index(v_result) v_result_after = normalafterblock.inputargs[result_i] else: v_result_after = v_result.copy() l0.args.append(v_result) normalafterblock.inputargs.append(v_result_after) if true_zero: opname = "zero_everything_inside" else: opname = "zero_gc_pointers_inside" normalafterblock.operations.insert( 0, SpaceOperation(opname, [v_result_after], varoftype(lltype.Void)))
def insert_empty_startblock(graph): vars = [v.copy() for v in graph.startblock.inputargs] newblock = Block(vars) newblock.closeblock(Link(vars, graph.startblock)) graph.startblock = newblock
def normalize_calltable_row_signature(annotator, shape, row): graphs = row.values() assert graphs, "no graph??" sig0 = graphs[0].signature defaults0 = graphs[0].defaults for graph in graphs[1:]: if graph.signature != sig0: break if graph.defaults != defaults0: break else: return False # nothing to do, all signatures already match shape_cnt, shape_keys, shape_star = shape if shape_star: raise TyperError( "not implemented: a call is done with a '*' argument, and the" " multiple functions or methods that it can go to don't have" " all the same signature (different argument names or defaults)." " The call can go to:\n%s" % '\n'.join(map(repr, graphs))) # for the first 'shape_cnt' arguments we need to generalize to # a common type call_nbargs = shape_cnt + len(shape_keys) did_something = False for graph in graphs: argnames, varargname, kwargname = graph.signature assert not varargname, "XXX not implemented" assert not kwargname, "XXX not implemented" # ? inputargs_s = [annotator.binding(v) for v in graph.getargs()] argorder = range(shape_cnt) for key in shape_keys: i = list(argnames).index(key) assert i not in argorder argorder.append(i) need_reordering = (argorder != range(call_nbargs)) if need_reordering or len(graph.getargs()) != call_nbargs: oldblock = graph.startblock inlist = [] defaults = graph.defaults or () num_nondefaults = len(inputargs_s) - len(defaults) defaults = [description.NODEFAULT] * num_nondefaults + list(defaults) newdefaults = [] for j in argorder: v = Variable(graph.getargs()[j]) annotator.setbinding(v, inputargs_s[j]) inlist.append(v) newdefaults.append(defaults[j]) newblock = Block(inlist) # prepare the output args of newblock: # 1. collect the positional arguments outlist = inlist[:shape_cnt] # 2. add defaults and keywords for j in range(shape_cnt, len(inputargs_s)): try: i = argorder.index(j) v = inlist[i] except ValueError: default = defaults[j] if default is description.NODEFAULT: raise TyperError( "call pattern has %d positional arguments, " "but %r takes at least %d arguments" % ( shape_cnt, graph.name, num_nondefaults)) v = Constant(default) outlist.append(v) newblock.closeblock(Link(outlist, oldblock)) graph.startblock = newblock for i in range(len(newdefaults)-1,-1,-1): if newdefaults[i] is description.NODEFAULT: newdefaults = newdefaults[i:] break graph.defaults = tuple(newdefaults) graph.signature = Signature([argnames[j] for j in argorder], None, None) # finished checkgraph(graph) annotator.annotated[newblock] = annotator.annotated[oldblock] did_something = True return did_something
def _transform_hint_close_stack(self, fnptr): # We cannot easily pass variable amount of arguments of the call # across the call to the pypy_asm_stackwalk helper. So we store # them away and restore them. More precisely, we need to # replace 'graph' with code that saves the arguments, and make # a new graph that starts with restoring the arguments. if self._asmgcc_save_restore_arguments is None: self._asmgcc_save_restore_arguments = {} sradict = self._asmgcc_save_restore_arguments sra = [] # list of pointers to raw-malloced containers for args seen = {} FUNC1 = lltype.typeOf(fnptr).TO for TYPE in FUNC1.ARGS: if isinstance(TYPE, lltype.Ptr): TYPE = llmemory.Address num = seen.get(TYPE, 0) seen[TYPE] = num + 1 key = (TYPE, num) if key not in sradict: CONTAINER = lltype.FixedSizeArray(TYPE, 1) p = lltype.malloc(CONTAINER, flavor='raw', zero=True, immortal=True) sradict[key] = Constant(p, lltype.Ptr(CONTAINER)) sra.append(sradict[key]) # # make a copy of the graph that will reload the values graph = fnptr._obj.graph graph2 = copygraph(graph) # # edit the original graph to only store the value of the arguments block = Block(graph.startblock.inputargs) c_item0 = Constant('item0', lltype.Void) assert len(block.inputargs) == len(sra) for v_arg, c_p in zip(block.inputargs, sra): if isinstance(v_arg.concretetype, lltype.Ptr): v_adr = varoftype(llmemory.Address) block.operations.append( SpaceOperation("cast_ptr_to_adr", [v_arg], v_adr)) v_arg = v_adr v_void = varoftype(lltype.Void) block.operations.append( SpaceOperation("bare_setfield", [c_p, c_item0, v_arg], v_void)) # # call asm_stackwalk(graph2) FUNC2 = lltype.FuncType([], FUNC1.RESULT) fnptr2 = lltype.functionptr(FUNC2, fnptr._obj._name + '_reload', graph=graph2) c_fnptr2 = Constant(fnptr2, lltype.Ptr(FUNC2)) HELPERFUNC = lltype.FuncType( [lltype.Ptr(FUNC2), ASM_FRAMEDATA_HEAD_PTR], FUNC1.RESULT) v_asm_stackwalk = varoftype(lltype.Ptr(HELPERFUNC), "asm_stackwalk") block.operations.append( SpaceOperation("cast_pointer", [c_asm_stackwalk], v_asm_stackwalk)) v_result = varoftype(FUNC1.RESULT) block.operations.append( SpaceOperation("indirect_call", [ v_asm_stackwalk, c_fnptr2, c_gcrootanchor, Constant(None, lltype.Void) ], v_result)) block.closeblock(Link([v_result], graph.returnblock)) graph.startblock = block # # edit the copy of the graph to reload the values block2 = graph2.startblock block1 = Block([]) reloadedvars = [] for v, c_p in zip(block2.inputargs, sra): v = v.copy() if isinstance(v.concretetype, lltype.Ptr): w = varoftype(llmemory.Address) else: w = v block1.operations.append( SpaceOperation('getfield', [c_p, c_item0], w)) if w is not v: block1.operations.append( SpaceOperation('cast_adr_to_ptr', [w], v)) reloadedvars.append(v) block1.closeblock(Link(reloadedvars, block2)) graph2.startblock = block1 # checkgraph(graph) checkgraph(graph2)
def insert_empty_startblock(annotator, graph): vars = [copyvar(annotator, v) for v in graph.startblock.inputargs] newblock = Block(vars) newblock.closeblock(Link(vars, graph.startblock)) graph.startblock = newblock
class BlockSpecializer(object): def __init__(self, graphbuilder, v_expand_malloc=None): self.graphbuilder = graphbuilder self.v_expand_malloc = v_expand_malloc self.specblock = Block([]) def initialize_renamings(self, virtualframe, keep_inputargs=False): # we make a copy of the original 'virtualframe' because the # specialize_operations() will mutate some of its content. virtualframe = virtualframe.copy({}) self.virtualframe = virtualframe self.nodes = virtualframe.get_nodes_in_use() self.renamings = {} # {RuntimeSpecNode(): Variable()} if keep_inputargs: assert virtualframe.varlist == virtualframe.sourceblock.inputargs specinputargs = [] for i, rtnode in enumerate(virtualframe.find_rt_nodes()): if keep_inputargs: v = virtualframe.varlist[i] assert v.concretetype == rtnode.TYPE else: v = rtnode.newvar() self.renamings[rtnode] = v specinputargs.append(v) self.specblock.inputargs = specinputargs def setnode(self, v, node): assert v not in self.nodes self.nodes[v] = node def getnode(self, v): if isinstance(v, Variable): return self.nodes[v] else: return getconstnode(v, self.renamings) def rename_nonvirtual(self, v, where=None): if not isinstance(v, Variable): return v node = self.nodes[v] if not isinstance(node, RuntimeSpecNode): raise CannotVirtualize(where) return self.renamings[node] def expand_nodes(self, nodelist): rtnodes, vtnodes = find_all_nodes(nodelist) return [self.renamings[rtnode] for rtnode in rtnodes] def specialize_operations(self): newoperations = [] self.ops_produced_by_last_op = 0 # note that 'self.virtualframe' can be changed during the loop! while True: operations = self.virtualframe.sourceblock.operations try: op = operations[self.virtualframe.nextopindex] self.virtualframe.nextopindex += 1 except IndexError: break meth = getattr(self, 'handle_op_' + op.opname, self.handle_default) newops_for_this_op = meth(op) newoperations += newops_for_this_op self.ops_produced_by_last_op = len(newops_for_this_op) for op in newoperations: if op.opname == 'direct_call': graph = graph_called_by(op) if graph in self.virtualframe.calledgraphs: raise CannotVirtualize("recursion in residual call") self.specblock.operations = newoperations def follow_exits(self): block = self.virtualframe.sourceblock self.specblock.exitswitch = self.rename_nonvirtual(block.exitswitch, 'exitswitch') links = block.exits catch_exc = self.specblock.canraise if not catch_exc and isinstance(self.specblock.exitswitch, Constant): # constant-fold the switch for link in links: if link.exitcase == 'default': break if link.llexitcase == self.specblock.exitswitch.value: break else: raise Exception("exit case not found?") links = (link,) self.specblock.exitswitch = None if catch_exc and self.ops_produced_by_last_op == 0: # the last op of the sourceblock did not produce any # operation in specblock, so we need to discard the # exception-catching. catch_exc = False links = links[:1] assert links[0].exitcase is None # the non-exception-catching case self.specblock.exitswitch = None newlinks = [] for link in links: is_catch_link = catch_exc and link.exitcase is not None if is_catch_link: extravars = [] for attr in ['last_exception', 'last_exc_value']: v = getattr(link, attr) if isinstance(v, Variable): rtnode = RuntimeSpecNode(v, v.concretetype) self.setnode(v, rtnode) self.renamings[rtnode] = v = rtnode.newvar() extravars.append(v) linkargsnodes = [self.getnode(v1) for v1 in link.args] # newlink = self.graphbuilder.create_outgoing_link( self.virtualframe, link.target, linkargsnodes, self.renamings, self.v_expand_malloc) # if self.specblock.exitswitch is not None: newlink.exitcase = link.exitcase if hasattr(link, 'llexitcase'): newlink.llexitcase = link.llexitcase if is_catch_link: newlink.extravars(*extravars) newlinks.append(newlink) self.specblock.closeblock(*newlinks) def make_rt_result(self, v_result): newrtnode = RuntimeSpecNode(v_result, v_result.concretetype) self.setnode(v_result, newrtnode) v_new = newrtnode.newvar() self.renamings[newrtnode] = v_new return v_new def make_const_rt_result(self, v_result, value): newrtnode = RuntimeSpecNode(v_result, v_result.concretetype) self.setnode(v_result, newrtnode) if v_result.concretetype is not lltype.Void: assert v_result.concretetype == lltype.typeOf(value) c_value = Constant(value) c_value.concretetype = v_result.concretetype self.renamings[newrtnode] = c_value def handle_default(self, op): newargs = [self.rename_nonvirtual(v, op) for v in op.args] constresult = try_fold_operation(op.opname, newargs, op.result.concretetype) if constresult: self.make_const_rt_result(op.result, constresult[0]) return [] else: newresult = self.make_rt_result(op.result) return [SpaceOperation(op.opname, newargs, newresult)] def handle_unreachable(self, op): from rpython.rtyper.lltypesystem.rstr import string_repr msg = 'unreachable: %s' % (op,) ll_msg = string_repr.convert_const(msg) c_msg = Constant(ll_msg, lltype.typeOf(ll_msg)) newresult = self.make_rt_result(op.result) return [SpaceOperation('debug_fatalerror', [c_msg], newresult)] def handle_op_getfield(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): fieldname = op.args[1].value index = node.typedesc.name2index[fieldname] self.setnode(op.result, node.fields[index]) return [] else: return self.handle_default(op) def handle_op_setfield(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): if node.readonly: raise ForcedInline(op) fieldname = op.args[1].value index = node.typedesc.name2index[fieldname] node.fields[index] = self.getnode(op.args[2]) return [] else: return self.handle_default(op) def handle_op_same_as(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): node = self.getnode(op.args[0]) self.setnode(op.result, node) return [] else: return self.handle_default(op) def handle_op_cast_pointer(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): node = self.getnode(op.args[0]) SOURCEPTR = lltype.Ptr(node.typedesc.MALLOCTYPE) TARGETPTR = op.result.concretetype try: if lltype.castable(TARGETPTR, SOURCEPTR) < 0: raise lltype.InvalidCast except lltype.InvalidCast: return self.handle_unreachable(op) self.setnode(op.result, node) return [] else: return self.handle_default(op) def handle_op_ptr_nonzero(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): self.make_const_rt_result(op.result, True) return [] else: return self.handle_default(op) def handle_op_ptr_iszero(self, op): node = self.getnode(op.args[0]) if isinstance(node, VirtualSpecNode): self.make_const_rt_result(op.result, False) return [] else: return self.handle_default(op) def handle_op_ptr_eq(self, op): node0 = self.getnode(op.args[0]) node1 = self.getnode(op.args[1]) if (isinstance(node0, VirtualSpecNode) or isinstance(node1, VirtualSpecNode)): self.make_const_rt_result(op.result, node0 is node1) return [] else: return self.handle_default(op) def handle_op_ptr_ne(self, op): node0 = self.getnode(op.args[0]) node1 = self.getnode(op.args[1]) if (isinstance(node0, VirtualSpecNode) or isinstance(node1, VirtualSpecNode)): self.make_const_rt_result(op.result, node0 is not node1) return [] else: return self.handle_default(op) def handle_op_malloc(self, op): if op.result is self.v_expand_malloc: MALLOCTYPE = op.result.concretetype.TO typedesc = self.graphbuilder.mallocv.getmalloctypedesc(MALLOCTYPE) virtualnode = VirtualSpecNode(typedesc, []) self.setnode(op.result, virtualnode) for name, FIELDTYPE in typedesc.names_and_types: fieldnode = RuntimeSpecNode(name, FIELDTYPE) virtualnode.fields.append(fieldnode) c = Constant(FIELDTYPE._defl()) c.concretetype = FIELDTYPE self.renamings[fieldnode] = c self.v_expand_malloc = None # done return [] else: return self.handle_default(op) def handle_op_direct_call(self, op): graph = graph_called_by(op) if graph is None: return self.handle_default(op) nb_args = len(op.args) - 1 assert nb_args == len(graph.getargs()) newnodes = [self.getnode(v) for v in op.args[1:]] myframe = self.get_updated_frame(op) mallocv = self.graphbuilder.mallocv if op.result is self.v_expand_malloc: # move to inlining the callee, and continue looking for the # malloc to expand in the callee's graph op_to_remove = mallocv.inline_and_remove[graph] self.v_expand_malloc = op_to_remove.result return self.handle_inlined_call(myframe, graph, newnodes) argnodes = copynodes(newnodes, flagreadonly=myframe.find_vt_nodes()) kind, newgraph = mallocv.get_specialized_graph(graph, argnodes) if kind == 'trivial': return self.handle_default(op) elif kind == 'inline': return self.handle_inlined_call(myframe, graph, newnodes) elif kind == 'call': return self.handle_residual_call(op, newgraph, newnodes) elif kind == 'fail': raise CannotVirtualize(op) else: raise ValueError(kind) def get_updated_frame(self, op): sourceblock = self.virtualframe.sourceblock nextopindex = self.virtualframe.nextopindex self.nodes[op.result] = FutureReturnValue(op) myframe = VirtualFrame(sourceblock, nextopindex, self.nodes, self.virtualframe.callerframe, self.virtualframe.calledgraphs) del self.nodes[op.result] return myframe def handle_residual_call(self, op, newgraph, newnodes): fspecptr = getfunctionptr(newgraph) newargs = [Constant(fspecptr, concretetype=lltype.typeOf(fspecptr))] newargs += self.expand_nodes(newnodes) newresult = self.make_rt_result(op.result) newop = SpaceOperation('direct_call', newargs, newresult) return [newop] def handle_inlined_call(self, myframe, graph, newnodes): assert len(graph.getargs()) == len(newnodes) targetnodes = dict(zip(graph.getargs(), newnodes)) calledgraphs = myframe.calledgraphs.copy() if graph in calledgraphs: raise CannotVirtualize("recursion during inlining") calledgraphs[graph] = True calleeframe = VirtualFrame(graph.startblock, 0, targetnodes, myframe, calledgraphs) self.virtualframe = calleeframe self.nodes = calleeframe.get_nodes_in_use() return [] def handle_op_indirect_call(self, op): v_func = self.rename_nonvirtual(op.args[0], op) if isinstance(v_func, Constant): op = SpaceOperation('direct_call', [v_func] + op.args[1:-1], op.result) return self.handle_op_direct_call(op) else: return self.handle_default(op)
def normalize_calltable_row_signature(annotator, shape, row): graphs = row.values() assert graphs, "no graph??" sig0 = graphs[0].signature defaults0 = graphs[0].defaults for graph in graphs[1:]: if graph.signature != sig0: break if graph.defaults != defaults0: break else: return False # nothing to do, all signatures already match shape_cnt, shape_keys, shape_star = shape assert not shape_star, "should have been removed at this stage" # for the first 'shape_cnt' arguments we need to generalize to # a common type call_nbargs = shape_cnt + len(shape_keys) did_something = False for graph in graphs: argnames, varargname, kwargname = graph.signature assert not varargname, "XXX not implemented" assert not kwargname, "XXX not implemented" # ? inputargs_s = [annotator.binding(v) for v in graph.getargs()] argorder = range(shape_cnt) for key in shape_keys: i = list(argnames).index(key) assert i not in argorder argorder.append(i) need_reordering = (argorder != range(call_nbargs)) if need_reordering or len(graph.getargs()) != call_nbargs: oldblock = graph.startblock inlist = [] defaults = graph.defaults or () num_nondefaults = len(inputargs_s) - len(defaults) defaults = [description.NODEFAULT ] * num_nondefaults + list(defaults) newdefaults = [] for j in argorder: v = Variable(graph.getargs()[j]) annotator.setbinding(v, inputargs_s[j]) inlist.append(v) newdefaults.append(defaults[j]) newblock = Block(inlist) # prepare the output args of newblock: # 1. collect the positional arguments outlist = inlist[:shape_cnt] # 2. add defaults and keywords for j in range(shape_cnt, len(inputargs_s)): try: i = argorder.index(j) v = inlist[i] except ValueError: default = defaults[j] if default is description.NODEFAULT: raise TyperError( "call pattern has %d positional arguments, " "but %r takes at least %d arguments" % (shape_cnt, graph.name, num_nondefaults)) v = Constant(default) outlist.append(v) newblock.closeblock(Link(outlist, oldblock)) graph.startblock = newblock for i in range(len(newdefaults) - 1, -1, -1): if newdefaults[i] is description.NODEFAULT: newdefaults = newdefaults[i:] break graph.defaults = tuple(newdefaults) graph.signature = Signature([argnames[j] for j in argorder], None, None) # finished checkgraph(graph) annotator.annotated[newblock] = annotator.annotated[oldblock] did_something = True return did_something