def flowin_op(self, op, vars, newvarsmap): if op.opname == "oogetfield": S = op.args[0].concretetype fldname = op.args[1].value key = self.key_for_field_access(S, fldname) newop = SpaceOperation("same_as", [newvarsmap[key]], op.result) self.newops.append(newop) last_removed_access = len(self.newops) elif op.opname == "oosetfield": S = op.args[0].concretetype fldname = op.args[1].value key = self.key_for_field_access(S, fldname) assert key in newvarsmap newvarsmap[key] = op.args[2] last_removed_access = len(self.newops) elif op.opname in ("same_as", "oodowncast", "ooupcast"): assert op.result not in vars vars[op.result] = True # Consider the two pointers (input and result) as # equivalent. We can, and indeed must, use the same # flattened list of variables for both, as a "setfield" # via one pointer must be reflected in the other. elif op.opname == "oononnull": # we know the pointer is not NULL if it comes from # a successful malloc c = Constant(True, lltype.Bool) newop = SpaceOperation('same_as', [c], op.result) self.newops.append(newop) else: raise AssertionError, op.opname
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_stackcheck(ann): from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles edges = [] graphs_to_patch = {} for callposition, (caller, callee) in ann.translator.callgraph.items(): if getattr(getattr(callee, 'func', None), 'insert_stack_check_here', False): graphs_to_patch[callee] = True continue edge = Edge(caller, callee) edge.callposition = callposition edges.append(edge) for graph in graphs_to_patch: v = Variable() ann.setbinding(v, annmodel.SomeImpossibleValue()) unwind_op = SpaceOperation('simple_call', [Constant(stack_check)], v) graph.startblock.operations.insert(0, unwind_op) edgedict = make_edge_dict(edges) for edge in break_cycles(edgedict, edgedict): caller = edge.source _, _, call_tag = edge.callposition if call_tag: caller_block, _ = call_tag else: ann.warning("cycle detected but no information on where to insert " "stack_check()") continue # caller block found, insert stack_check() v = Variable() # push annotation on v ann.setbinding(v, annmodel.SomeImpossibleValue()) unwind_op = SpaceOperation('simple_call', [Constant(stack_check)], v) caller_block.operations.insert(0, unwind_op)
def fold_op_list(operations, constants, exit_early=False, exc_catch=False): newops = [] keepalives = [] folded_count = 0 first_sideeffect_index = None for spaceop in operations: vargsmodif = False vargs = [] args = [] for v in spaceop.args: if isinstance(v, Constant): args.append(v.value) elif v in constants: v = constants[v] vargsmodif = True args.append(v.value) vargs.append(v) try: op = getattr(llop, spaceop.opname) except AttributeError: sideeffects = True else: sideeffects = op.sideeffects if not sideeffects and len(args) == len(vargs): RESTYPE = spaceop.result.concretetype try: result = op(RESTYPE, *args) except TypeError: pass except (KeyboardInterrupt, SystemExit): raise except Exception, e: log.WARNING('constant-folding %r:' % (spaceop, )) log.WARNING(' %s: %s' % (e.__class__.__name__, e)) else: # success in folding this space operation if spaceop.opname in fixup_op_result: result = fixup_op_result[spaceop.opname](result) constants[spaceop.result] = Constant(result, RESTYPE) folded_count += 1 continue # failed to fold an operation, exit early if requested if exit_early: return folded_count if spaceop.opname == 'keepalive' and first_sideeffect_index is None: if vargsmodif: continue # keepalive(constant) is not useful keepalives.append(spaceop) else: if vargsmodif: if (spaceop.opname == 'indirect_call' and isinstance(vargs[0], Constant)): spaceop = SpaceOperation('direct_call', vargs[:-1], spaceop.result) else: spaceop = SpaceOperation(spaceop.opname, vargs, spaceop.result) if sideeffects and first_sideeffect_index is None: first_sideeffect_index = len(newops) newops.append(spaceop)
def rewrite_can_enter_jit(self, jd, can_enter_jits): FUNCPTR = jd._PTR_JIT_ENTER_FUNCTYPE jit_enter_fnptr = self.helper_func(FUNCPTR, jd._maybe_enter_jit_fn) if len(can_enter_jits) == 0: # see test_warmspot.test_no_loop_at_all operations = jd.portal_graph.startblock.operations op1 = operations[0] assert (op1.opname == 'jit_marker' and op1.args[0].value == 'jit_merge_point') op0 = SpaceOperation( 'jit_marker', [Constant('can_enter_jit', lltype.Void)] + op1.args[1:], None) operations.insert(0, op0) can_enter_jits = [(jd.portal_graph, jd.portal_graph.startblock, 0)] for graph, block, index in can_enter_jits: if graph is jd._jit_merge_point_in: continue op = block.operations[index] greens_v, reds_v = support.decode_hp_hint_args(op) args_v = greens_v + reds_v vlist = [Constant(jit_enter_fnptr, FUNCPTR)] + args_v v_result = Variable() v_result.concretetype = lltype.Void newop = SpaceOperation('direct_call', vlist, v_result) block.operations[index] = newop
def test_nongc_ptr_eq(): v1 = varoftype(rclass.NONGCOBJECTPTR) v2 = varoftype(rclass.NONGCOBJECTPTR) v3 = varoftype(lltype.Bool) c0 = const(lltype.nullptr(rclass.NONGCOBJECT)) # for opname, reducedname in [('ptr_eq', 'int_is_zero'), ('ptr_ne', 'int_is_true')]: op = SpaceOperation(opname, [v1, v2], v3) op1 = Transformer().rewrite_operation(op) assert op1.opname == opname.replace('ptr_', 'int_') assert op1.args == [v1, v2] # op = SpaceOperation(opname, [v1, c0], v3) op1 = Transformer().rewrite_operation(op) assert op1.opname == reducedname assert op1.args == [v1] # op = SpaceOperation(opname, [c0, v2], v3) op1 = Transformer().rewrite_operation(op) assert op1.opname == reducedname assert op1.args == [v2] # op = SpaceOperation('ptr_iszero', [v1], v3) op1 = Transformer().rewrite_operation(op) assert op1.opname == 'int_is_zero' assert op1.args == [v1] # op = SpaceOperation('ptr_nonzero', [v1], v3) op1 = Transformer().rewrite_operation(op) assert op1.opname == 'int_is_true' assert op1.args == [v1]
def rename_op(op): args = [rename(a) for a in op.args] op = SpaceOperation(op.opname, args, rename(op.result), op.offset) # special case... if op.opname == 'indirect_call': if isinstance(op.args[0], Constant): assert isinstance(op.args[-1], Constant) del op.args[-1] op.opname = 'direct_call' return op
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_dict_setinteriorfield(): DICT = lltype.GcArray( lltype.Struct('ENTRY', ('v', lltype.Signed), ('k', lltype.Signed))) v = varoftype(lltype.Ptr(DICT)) i = varoftype(lltype.Signed) v_void = varoftype(lltype.Void) op = SpaceOperation('setinteriorfield', [v, i, Constant('v', lltype.Void), i], v_void) op1 = Transformer(FakeCPU()).rewrite_operation(op) assert op1.opname == 'setinteriorfield_gc_i' assert op1.args == [v, i, i, ('interiorfielddescr', DICT, 'v')] op = SpaceOperation('setinteriorfield', [v, i, Constant('v', lltype.Void), v_void], v_void) op1 = Transformer(FakeCPU()).rewrite_operation(op) assert not op1
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 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 test_symmetric(): ops = { 'int_add': 'int_add', 'int_or': 'int_or', 'int_gt': ('int_gt', 'int_lt'), 'uint_eq': 'int_eq', 'uint_le': ('uint_le', 'uint_ge'), 'char_ne': 'int_ne', 'char_lt': ('int_lt', 'int_gt'), 'uint_xor': 'int_xor', 'float_mul': 'float_mul', 'float_gt': ('float_gt', 'float_lt'), } v3 = varoftype(lltype.Signed) for v1 in [varoftype(lltype.Signed), const(42)]: for v2 in [varoftype(lltype.Signed), const(43)]: for name1, name2 in ops.items(): op = SpaceOperation(name1, [v1, v2], v3) op1 = Transformer(FakeCPU()).rewrite_operation(op) if isinstance(name2, str): name2 = name2, name2 if isinstance(v1, Constant) and isinstance(v2, Variable): assert op1.args == [v2, v1] assert op1.result == v3 assert op1.opname == name2[1] else: assert op1.args == [v1, v2] assert op1.result == v3 assert op1.opname == name2[0]
def test_optimize_goto_if_not(): v1 = Variable() v2 = Variable() v3 = Variable() v3.concretetype = lltype.Bool sp1 = SpaceOperation('foobar', [], None) sp2 = SpaceOperation('foobaz', [], None) block = Block([v1, v2]) block.operations = [sp1, SpaceOperation('int_gt', [v1, v2], v3), sp2] block.exitswitch = v3 block.exits = exits = [FakeLink(False), FakeLink(True)] res = Transformer().optimize_goto_if_not(block) assert res == True assert block.operations == [sp1, sp2] assert block.exitswitch == ('int_gt', v1, v2) assert block.exits == exits
def test_unicode_concat(): # test that the oopspec is present and correctly transformed PSTR = lltype.Ptr(rstr.UNICODE) FUNC = lltype.FuncType([PSTR, PSTR], PSTR) func = lltype.functionptr(FUNC, 'll_strconcat', _callable=rstr.LLHelpers.ll_strconcat) v1 = varoftype(PSTR) v2 = varoftype(PSTR) v3 = varoftype(PSTR) op = SpaceOperation('direct_call', [const(func), v1, v2], v3) cc = FakeBuiltinCallControl() tr = Transformer(FakeCPU(), cc) op1 = tr.rewrite_operation(op) assert op1.opname == 'residual_call_r_r' assert op1.args[0].value == func assert op1.args[1] == 'calldescr-%d' % effectinfo.EffectInfo.OS_UNI_CONCAT assert op1.args[2] == ListOfKind('ref', [v1, v2]) assert op1.result == v3 # # check the callinfo_for_oopspec got = cc.callinfocollection.seen[0] assert got[0] == effectinfo.EffectInfo.OS_UNI_CONCAT assert got[1] == op1.args[1] # the calldescr assert heaptracker.int2adr(got[2]) == llmemory.cast_ptr_to_adr(func)
def handle_forget_hint(self, block, i): # a hint for testing only op = block.operations[i] assert self.hannotator.binding(op.result).is_green() assert not self.hannotator.binding(op.args[0]).is_green() newop = SpaceOperation('revealconst', [op.args[0]], op.result) block.operations[i] = newop
def insert_ll_stackcheck(translator): from pypy.translator.backendopt.support import find_calls_from from pypy.rlib.rstack import stack_check from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles_v rtyper = translator.rtyper graph = rtyper.annotate_helper(stack_check, []) rtyper.specialize_more_blocks() stack_check_ptr = rtyper.getcallable(graph) stack_check_ptr_const = Constant(stack_check_ptr, lltype.typeOf(stack_check_ptr)) edges = set() insert_in = set() for caller in translator.graphs: for block, callee in find_calls_from(translator, caller): if getattr(getattr(callee, 'func', None), 'insert_stack_check_here', False): insert_in.add(callee.startblock) continue if block is not caller.startblock: edges.add((caller.startblock, block)) edges.add((block, callee.startblock)) edgelist = [Edge(block1, block2) for (block1, block2) in edges] edgedict = make_edge_dict(edgelist) for block in break_cycles_v(edgedict, edgedict): insert_in.add(block) for block in insert_in: v = Variable() v.concretetype = lltype.Void unwind_op = SpaceOperation('direct_call', [stack_check_ptr_const], v) block.operations.insert(0, unwind_op) return len(insert_in)
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 = SpaceOperation('newtuple', starargs, argscopy[-1]) newstartblock.operations.append(newtup) newstartblock.closeblock(Link(argscopy, graph.startblock)) graph.startblock.isstartblock = False graph.startblock = newstartblock newstartblock.isstartblock = True 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 handle_unreachable(self, op): from pypy.rpython.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 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 test_unknown_operation(): op = SpaceOperation('foobar', [], varoftype(lltype.Void)) tr = Transformer() try: tr.rewrite_operation(op) except Exception, e: assert 'foobar' in str(e)
def insert_ll_stackcheck(translator): from pypy.translator.backendopt.support import find_calls_from from pypy.rlib.rstack import stack_check from pypy.tool.algo.graphlib import Edge, make_edge_dict, break_cycles rtyper = translator.rtyper graph = rtyper.annotate_helper(stack_check, []) rtyper.specialize_more_blocks() stack_check_ptr = rtyper.getcallable(graph) stack_check_ptr_const = Constant(stack_check_ptr, lltype.typeOf(stack_check_ptr)) edges = [] graphs_to_patch = {} insert_in = {} for caller in translator.graphs: for block, callee in find_calls_from(translator, caller): if getattr(getattr(callee, 'func', None), 'insert_stack_check_here', False): insert_in[callee.startblock] = True continue edge = Edge(caller, callee) edge.block = block edges.append(edge) edgedict = make_edge_dict(edges) for edge in break_cycles(edgedict, edgedict): block = edge.block insert_in[block] = True for block in insert_in: v = Variable() v.concretetype = lltype.Void unwind_op = SpaceOperation('direct_call', [stack_check_ptr_const], v) block.operations.insert(0, unwind_op) return len(insert_in)
def instrument_inline_candidates(graphs, threshold): cache = {None: False} def candidate(graph): try: return cache[graph] except KeyError: res = static_instruction_count(graph) <= threshold cache[graph] = res return res n = 0 for parentgraph in graphs: for block in parentgraph.iterblocks(): ops = block.operations i = len(ops)-1 while i >= 0: op = ops[i] i -= 1 if op.opname == "direct_call": funcobj = get_funcobj(op.args[0].value) graph = getattr(funcobj, 'graph', None) if graph is not None: if getattr(getattr(funcobj, '_callable', None), '_dont_inline_', False): continue if candidate(graph): tag = Constant('inline', Void) label = Constant(n, Signed) dummy = Variable() dummy.concretetype = Void count = SpaceOperation('instrument_count', [tag, label], dummy) ops.insert(i+1, count) n += 1 log.inlining("%d call sites instrumented" % n)
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_is_true(self): for opname, T in [('llong_is_true', lltype.SignedLongLong), ('ullong_is_true', lltype.UnsignedLongLong)]: v = varoftype(T) v_result = varoftype(lltype.Bool) op = SpaceOperation(opname, [v], v_result) tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) oplist = tr.rewrite_operation(op) assert len(oplist) == 2 assert oplist[0].opname == 'residual_call_irf_f' assert oplist[0].args[0].value == opname.split( '_')[0] + '_from_int' assert oplist[0].args[1] == 'calldescr-84' assert list(oplist[0].args[2]) == [const(0)] assert list(oplist[0].args[3]) == [] assert list(oplist[0].args[4]) == [] v_x = oplist[0].result assert isinstance(v_x, Variable) assert v_x.concretetype is T assert oplist[1].opname == 'residual_call_irf_i' assert oplist[1].args[0].value == 'llong_ne' assert oplist[1].args[1] == 'calldescr-76' assert list(oplist[1].args[2]) == [] assert list(oplist[1].args[3]) == [] assert list(oplist[1].args[4]) == [v, v_x] assert oplist[1].result == v_result
def test_decode_builtin_call_method(): A = lltype.GcArray(lltype.Signed) def myfoobar(a, i, marker, c): assert marker == 'mymarker' return a[i] * ord(c) myfoobar.oopspec = 'spam.foobar(a, 2, c, i)' TYPE = lltype.FuncType([lltype.Ptr(A), lltype.Signed, lltype.Void, lltype.Char], lltype.Signed) fnobj = lltype.functionptr(TYPE, 'foobar', _callable=myfoobar) vi = Variable('i') vi.concretetype = lltype.Signed vc = Variable('c') vc.concretetype = lltype.Char v_result = Variable('result') v_result.concretetype = lltype.Signed myarray = lltype.malloc(A, 10) myarray[5] = 42 op = SpaceOperation('direct_call', [newconst(fnobj), newconst(myarray), vi, voidconst('mymarker'), vc], v_result) oopspec, opargs = decode_builtin_call(op) assert oopspec == 'spam.foobar' assert opargs == [newconst(myarray), newconst(2), vc, vi]
def do_check(self, opname, oopspecindex, ARGS, RESULT): vlist = [varoftype(ARG) for ARG in ARGS] v_result = varoftype(RESULT) op = SpaceOperation(opname, vlist, v_result) tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) op1 = tr.rewrite_operation(op) if isinstance(op1, list): [op1] = op1 # def is_llf(TYPE): return (TYPE == lltype.SignedLongLong or TYPE == lltype.UnsignedLongLong or TYPE == lltype.Float) if is_llf(RESULT): assert op1.opname == 'residual_call_irf_f' else: assert op1.opname == 'residual_call_irf_i' gotindex = getattr(EffectInfo, 'OS_' + op1.args[0].value.upper().lstrip('U')) assert gotindex == oopspecindex assert op1.args[1] == 'calldescr-%d' % oopspecindex assert list( op1.args[2]) == [v for v in vlist if not is_llf(v.concretetype)] assert list(op1.args[3]) == [] assert list( op1.args[4]) == [v for v in vlist if is_llf(v.concretetype)] assert op1.result == v_result
def prepare_constant_fold_link(link, constants, splitblocks): block = link.target if not block.operations: # when the target block has no operation, there is nothing we can do # except trying to fold an exitswitch if block.exitswitch is not None and block.exitswitch in constants: llexitvalue = constants[block.exitswitch].value rewire_link_for_known_exitswitch(link, llexitvalue) return folded_count = fold_op_list(block.operations, constants, exit_early=True) n = len(block.operations) if block.exitswitch == c_last_exception: n -= 1 # is the next, non-folded operation an indirect_call? m = folded_count while m < n and block.operations[m].opname == 'keepalive': m += 1 if m < n: nextop = block.operations[m] if nextop.opname == 'indirect_call' and nextop.args[0] in constants: # indirect_call -> direct_call callargs = [constants[nextop.args[0]]] constants1 = constants.copy() complete_constants(link, constants1) newkeepalives = [] for i in range(folded_count, m): [v] = block.operations[i].args v = constants1.get(v, v) v_void = Variable() v_void.concretetype = lltype.Void newkeepalives.append(SpaceOperation('keepalive', [v], v_void)) for v in nextop.args[1:-1]: callargs.append(constants1.get(v, v)) v_result = Variable(nextop.result) v_result.concretetype = nextop.result.concretetype constants[nextop.result] = v_result callop = SpaceOperation('direct_call', callargs, v_result) newblock = insert_empty_block(None, link, newkeepalives + [callop]) [link] = newblock.exits assert link.target is block folded_count = m + 1 if folded_count > 0: splits = splitblocks.setdefault(block, []) splits.append((folded_count, link, constants))
def do_inline(self, block, index_operation): splitlink = split_block(None, block, index_operation) afterblock = splitlink.target # these variables have to be passed along all the links in the inlined # graph because the original function needs them in the blocks after # the inlined function # for every inserted block we need a new copy of these variables, # this copy is created with the method passon_vars self.original_passon_vars = [arg for arg in block.exits[0].args if isinstance(arg, Variable)] n = 0 while afterblock.operations[n].opname == 'keepalive': n += 1 assert afterblock.operations[n].opname == self.op.opname self.op = afterblock.operations.pop(n) #vars that need to be passed through the blocks of the inlined function linktoinlined = splitlink copiedstartblock = self.copy_block(self.graph_to_inline.startblock) copiedstartblock.isstartblock = False #find args passed to startblock of inlined function passon_args = [] for arg in self.op.args[1:]: if isinstance(arg, Constant): passon_args.append(arg) else: index = afterblock.inputargs.index(arg) passon_args.append(linktoinlined.args[index]) passon_args += self.original_passon_vars if self.op.opname == 'oosend' and not isinstance(self.op.args[1], Constant): # if we try to inline a graph defined in a superclass, the # type of 'self' on the graph differs from the current linkv = passon_args[0] inputv = copiedstartblock.inputargs[0] LINK_SELF = linkv.concretetype INPUT_SELF = inputv.concretetype if LINK_SELF != INPUT_SELF: # need to insert an upcast assert ootype.isSubclass(LINK_SELF, INPUT_SELF) v = Variable() v.concretetype = INPUT_SELF upcast = SpaceOperation('ooupcast', [linkv], v) block.operations.append(upcast) passon_args[0] = v #rewire blocks linktoinlined.target = copiedstartblock linktoinlined.args = passon_args afterblock.inputargs = [self.op.result] + afterblock.inputargs if self.graph_to_inline.returnblock in self.entrymap: self.rewire_returnblock(afterblock) if self.graph_to_inline.exceptblock in self.entrymap: self.rewire_exceptblock(afterblock) if self.exception_guarded: assert afterblock.exits[0].exitcase is None afterblock.recloseblock(afterblock.exits[0]) afterblock.exitswitch = None self.search_for_calls(afterblock) self.search_for_calls(block)
def genop(self, opname, args, resulttype=None, resultvar=None): assert resulttype is None or resultvar is None if resultvar is None: return self.llops.genop(opname, args, resulttype=resulttype) else: newop = SpaceOperation(opname, args, resultvar) self.llops.append(newop) return resultvar
def get_direct_call_op(argtypes, restype): FUNC = lltype.FuncType(argtypes, restype) fnptr = lltype.functionptr(FUNC, "g") # no graph c_fnptr = const(fnptr) vars = [varoftype(TYPE) for TYPE in argtypes] v_result = varoftype(restype) op = SpaceOperation('direct_call', [c_fnptr] + vars, v_result) return op