def get_resume_point_link(self, block): try: return self.resumepoints[block] except KeyError: resumeblock = Block([]) redcount = 0 greencount = 0 newvars = [] for v in block.inputargs: if v.concretetype is lltype.Void: v1 = self.c_dummy elif self.hannotator.binding(v).is_green(): c = inputconst(lltype.Signed, greencount) v1 = self.genop(resumeblock, 'restore_green', [c], result_like = v) greencount += 1 else: c = inputconst(lltype.Signed, redcount) v1 = self.genop(resumeblock, 'restore_local', [c], result_like = v) redcount += 1 newvars.append(v1) resumeblock.closeblock(Link(newvars, block)) reenter_link = Link([], resumeblock) N = len(self.resumepoints) reenter_link.exitcase = N self.resumepoints[block] = reenter_link return reenter_link
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 go_to_if(self, block, target, v_finished_flag): block.exitswitch = v_finished_flag [link_f] = block.exits link_t = Link([self.c_dummy], target) link_f.exitcase = False link_t.exitcase = True block.recloseblock(link_f, link_t)
def genswitch(self, block, v_exitswitch, false, true): block.exitswitch = v_exitswitch link_f = Link([], false) link_f.exitcase = False link_t = Link([], true) link_t.exitcase = True block.recloseblock(link_f, link_t)
def copy_link(self, link, prevblock): newargs = [self.get_new_name(a) for a in link.args] + self.passon_vars(prevblock) newlink = Link(newargs, self.copy_block(link.target), link.exitcase) newlink.last_exception = self.get_new_name(link.last_exception) newlink.last_exc_value = self.get_new_name(link.last_exc_value) if hasattr(link, 'llexitcase'): newlink.llexitcase = link.llexitcase return newlink
def handle_after_residual_call_details(self, block, pos, newops, blockset, withexc, oop = False): dopts = {'withexc': withexc, 'oop': oop } copts = Constant(dopts, lltype.Void) v_flags = self.genop(newops, 'after_residual_call', [copts], resulttype=lltype.Signed, red=True) residual_fetch_index = len(newops) self.genop(newops, 'residual_fetch', [v_flags, copts]) residual_fetch_pos = pos+residual_fetch_index block.operations[pos:pos+1] = newops link_t = split_block(self.hannotator, block, residual_fetch_pos) nextblock = link_t.target blockset[nextblock] = False i_flags = link_t.args.index(v_flags) reds, greens = self.sort_by_color(link_t.args) self.genop(block, 'save_locals', reds) SPLIT_FOR_ZERO = False if SPLIT_FOR_ZERO: promoteblock = Block([copyvar(self.hannotator, v) for v in link_t.args]) link_f = Link(link_t.args, promoteblock) promoteblock.recloseblock(Link(promoteblock.inputargs, nextblock)) blockset[promoteblock] = False v_flags2 = promoteblock.inputargs[i_flags] else: promoteblock = block v_flags2 = v_flags # if there is no global merge point, this 'promote' will actually # always see a constant red box v_finished_flag = self.genop(promoteblock, 'promote', [v_flags2], resulttype = lltype.Bool) self.go_to_dispatcher_if(promoteblock, v_finished_flag) if SPLIT_FOR_ZERO: c_zero = inputconst(lltype.Signed, 0) link_t.args = link_t.args[:] link_t.args[i_flags] = c_zero resumepoint = self.get_resume_point(promoteblock) c_resumepoint = inputconst(lltype.Signed, resumepoint) v_is_zero = self.genop(block, 'int_eq', [v_flags, c_zero], resulttype=lltype.Bool, red=True) v_is_zero = self.genop(block, 'split', [v_is_zero, c_resumepoint] + greens, resulttype = lltype.Bool) block.exitswitch = v_is_zero link_t.exitcase = True link_f.exitcase = False block.recloseblock(link_f, link_t)
def transform_except_block(self, graph, block): # attach an except block -- let's hope that nobody uses it graph.exceptblock = Block([ Variable('etype'), # exception class Variable('evalue') ]) # exception value graph.exceptblock.operations = () graph.exceptblock.closeblock() result = Variable() result.concretetype = lltype.Void block.operations = [ SpaceOperation("direct_call", [self.rpyexc_raise_ptr] + block.inputargs, result) ] l = Link([error_constant(graph.returnblock.inputargs[0].concretetype)], graph.returnblock) block.recloseblock(l)
def test_switch_no_default(): from pypy.objspace.flow.model import FunctionGraph, Block, Constant, Link from pypy.rpython.lltypesystem.lltype import FuncType, Signed, functionptr from pypy.translator.unsimplify import varoftype block = Block([varoftype(Signed)]) block.exitswitch = block.inputargs[0] graph = FunctionGraph("t", block, varoftype(Signed)) links = [] for i in range(10): links.append(Link([Constant(i*i, Signed)], graph.returnblock, i)) links[-1].llexitcase = i block.closeblock(*links) fptr = functionptr(FuncType([Signed], Signed), "t", graph=graph) def func(x): return fptr(x) f = compile_function(func, [int]) res = f(4) assert res == 16
def test_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 create_outgoing_link(self, currentframe, targetblock, nodelist, renamings, v_expand_malloc=None): assert len(nodelist) == len(targetblock.inputargs) # if is_except(targetblock): v_expand_malloc = None while currentframe.callerframe is not None: currentframe = currentframe.callerframe newlink = self.handle_catch(currentframe, nodelist, renamings) if newlink: return newlink else: targetblock = self.exception_escapes(nodelist, renamings) assert len(nodelist) == len(targetblock.inputargs) if (currentframe.callerframe is None and is_trivial_nodelist(nodelist)): # there is no more VirtualSpecNodes being passed around, # so we can stop specializing rtnodes = nodelist specblock = targetblock else: if is_return(targetblock): v_expand_malloc = None newframe = self.return_to_caller(currentframe, nodelist[0]) else: targetnodes = dict(zip(targetblock.inputargs, nodelist)) newframe = VirtualFrame(targetblock, 0, targetnodes, callerframe=currentframe.callerframe, calledgraphs=currentframe.calledgraphs) rtnodes = newframe.find_rt_nodes() specblock = self.get_specialized_block(newframe, v_expand_malloc) linkargs = [renamings[rtnode] for rtnode in rtnodes] return Link(linkargs, specblock)
def build_callback_graph(self, graph, metadesccls=False): args_v = [copyvar(None, v) for v in graph.getargs()] v_res = copyvar(None, graph.getreturnvar()) rtyper = self.bookkeeper.annotator.base_translator.rtyper # fish fnptr = rtyper.getcallable(graph) v_ptr = Constant(fnptr, lltype.typeOf(fnptr)) newstartblock = Block(args_v) if metadesccls: v_metadesccls = Constant(metadesccls, lltype.Void) args_v = [v_metadesccls] + args_v opname = 'ts_metacall' suffix = 'ts_metacall' else: opname = 'direct_call' suffix = 'ts_stub' newstartblock.operations.append( SpaceOperation(opname, [v_ptr] + args_v, v_res)) newgraph = FunctionGraph('%s_%s' % (graph.name, suffix), newstartblock) newgraph.getreturnvar().concretetype = v_res.concretetype newstartblock.closeblock(Link([v_res], newgraph.returnblock)) return newgraph
def create_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 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.getexceptiondata().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])) copiedexceptblock.operations += self.generate_keepalive(linkargs)
def transform_jump_to_except_block(self, graph, entrymap, link): reraise = self.comes_from_last_exception(entrymap, link) result = Variable() result.concretetype = lltype.Void block = Block([copyvar(None, v) for v in graph.exceptblock.inputargs]) if reraise: block.operations = [ SpaceOperation("direct_call", [self.rpyexc_reraise_ptr] + block.inputargs, result), ] else: block.operations = [ SpaceOperation("direct_call", [self.rpyexc_raise_ptr] + block.inputargs, result), SpaceOperation('debug_record_traceback', [], varoftype(lltype.Void)), ] link.target = block RETTYPE = graph.returnblock.inputargs[0].concretetype l = Link([error_constant(RETTYPE)], graph.returnblock) block.recloseblock(l)
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 cutoff_alwaysraising_block(self, block): "Fix a block whose end can never be reached at run-time." # search the operation that cannot succeed can_succeed = [op for op in block.operations if op.result in self.bindings] cannot_succeed = [ op for op in block.operations if op.result not in self.bindings ] n = len(can_succeed) # check consistency assert can_succeed == block.operations[:n] assert cannot_succeed == block.operations[n:] assert 0 <= n < len(block.operations) # chop off the unreachable end of the block del block.operations[n + 1:] s_impossible = annmodel.SomeImpossibleValue() self.bindings[block.operations[n].result] = s_impossible # insert the equivalent of 'raise AssertionError' graph = self.annotated[block] msg = "Call to %r should have raised an exception" % (getattr( graph, 'func', None), ) c1 = Constant(AssertionError) c2 = Constant(AssertionError(msg)) errlink = Link([c1, c2], graph.exceptblock) block.recloseblock(errlink, *block.exits) # record new link to make the transformation idempotent self.links_followed[errlink] = True # fix the annotation of the exceptblock.inputargs etype, evalue = graph.exceptblock.inputargs s_type = annmodel.SomeObject() s_type.knowntype = type s_type.is_type_of = [evalue] s_value = annmodel.SomeInstance( self.bookkeeper.getuniqueclassdef(Exception)) self.setbinding(etype, s_type) self.setbinding(evalue, s_value) # make sure the bookkeeper knows about AssertionError self.bookkeeper.getuniqueclassdef(AssertionError)
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 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) # XXX: does alloc_shortcut make sense also for ootype? if alloc_shortcut: T = spaceop.result.concretetype 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 # XXX this is not right. it also inserts zero_gc_pointers_inside # XXX on a path that malloc_nonmovable returns null, but does not raise # XXX which might end up with a segfault. But we don't have such gc now if spaceop.opname == 'malloc' or spaceop.opname == 'malloc_nonmovable': flavor = spaceop.args[1].value['flavor'] if flavor == 'gc': insert_zeroing_op = True 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))) if self.always_exc_clear: # insert code that clears the exception even in the non-exceptional # case... this is a hint for the JIT, but pointless otherwise if normalafterblock is None: normalafterblock = insert_empty_block(None, l0) llops = rtyper.LowLevelOpList(None) self.gen_setfield('exc_value', self.c_null_evalue, llops) self.gen_setfield('exc_type', self.c_null_etype, llops) normalafterblock.operations[:0] = llops
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) # assert graph.startblock.operations[0].opname == 'generator_mark' graph.startblock.operations.pop(0) # insert_empty_startblock(None, graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) v0 = Variable(); v1 = Variable() stopblock.operations = [ SpaceOperation('simple_call', [Constant(StopIteration)], v0), SpaceOperation('type', [v0], v1), ] stopblock.closeblock(Link([v1, v0], 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): op = block.operations[index] if op.opname == 'yield': [v_yielded_value] = op.args del block.operations[index] newlink = split_block(None, 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) # v_resume = Variable('resume') block.operations.append( SpaceOperation('simple_call', [Constant(Resume)], v_resume)) for i, name in enumerate(varnames): block.operations.append( SpaceOperation('setattr', [v_resume, Constant(name), newlink.args[i]], Variable())) v_pair = Variable('pair') block.operations.append( SpaceOperation('newtuple', [v_resume, v_yielded_value], v_pair)) newlink.args = [v_pair] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: v_check = Variable() block.operations.append( SpaceOperation('simple_call', [Constant(isinstance), block.inputargs[0], Constant(Resume)], v_check)) block.exitswitch = v_check 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)
def insert_integer_search(self, block, cases, defaultlink, blockset, range_start, range_stop): # fix the exit of the 'block' to check for the given remaining # 'cases', knowing that if we get there then the value must # be contained in range(range_start, range_stop). if not cases: assert defaultlink is not None block.exitswitch = None block.recloseblock(Link(defaultlink.args, defaultlink.target)) elif len(cases) == 1 and (defaultlink is None or range_start == range_stop-1): block.exitswitch = None block.recloseblock(cases.values()[0]) else: intvalues = cases.keys() intvalues.sort() if len(intvalues) <= 3: # not much point in being clever with no more than 3 cases intval = intvalues[-1] remainingcases = cases.copy() link = remainingcases.pop(intval) c_intval = inputconst(lltype.Signed, intval) v = self.genop(block, 'int_eq', [block.exitswitch, c_intval], resulttype=lltype.Bool, red=True) link.exitcase = True link.llexitcase = True falseblock = Block([]) falseblock.exitswitch = block.exitswitch blockset[falseblock] = False falselink = Link([], falseblock) falselink.exitcase = False falselink.llexitcase = False block.exitswitch = v block.recloseblock(falselink, link) if defaultlink is None or intval == range_stop-1: range_stop = intval self.insert_integer_search(falseblock, remainingcases, defaultlink, blockset, range_start, range_stop) else: intval = intvalues[len(intvalues) // 2] c_intval = inputconst(lltype.Signed, intval) v = self.genop(block, 'int_ge', [block.exitswitch, c_intval], resulttype=lltype.Bool, red=True) falseblock = Block([]) falseblock.exitswitch = block.exitswitch trueblock = Block([]) trueblock.exitswitch = block.exitswitch blockset[falseblock] = False blockset[trueblock] = False falselink = Link([], falseblock) falselink.exitcase = False falselink.llexitcase = False truelink = Link([], trueblock) truelink.exitcase = True truelink.llexitcase = True block.exitswitch = v block.recloseblock(falselink, truelink) falsecases = {} truecases = {} for intval1, link1 in cases.items(): if intval1 < intval: falsecases[intval1] = link1 else: truecases[intval1] = link1 self.insert_integer_search(falseblock, falsecases, defaultlink, blockset, range_start, intval) self.insert_integer_search(trueblock, truecases, defaultlink, blockset, intval, range_stop)
def handle_red_call(self, block, pos, withexc, color='red'): link = split_block(self.hannotator, block, pos+1) op = block.operations.pop(pos) #if op.opname == 'direct_call': # f = open('LOG', 'a') # print >> f, color, op.args[0].value # f.close() assert len(block.operations) == pos nextblock = link.target linkargs = link.args varsalive = list(linkargs) if color != 'gray': # the result will be either passed as an extra local 0 # by the caller, or restored by a restore_local try: index = varsalive.index(op.result) except ValueError: linkargs.insert(0, op.result) v_result = copyvar(self.hannotator, op.result) nextblock.inputargs.insert(0, v_result) else: del varsalive[index] old_v_result = linkargs.pop(index) linkargs.insert(0, old_v_result) v_result = nextblock.inputargs.pop(index) nextblock.inputargs.insert(0, v_result) else: if op.result in varsalive: index = varsalive.index(op.result) del varsalive[index] linkargs.pop(index) c_void = Constant(None, lltype.Void) linkargs.insert(0, c_void) v_result = nextblock.inputargs.pop(index) nextblock.inputargs.insert(0, v_result) reds, greens = self.sort_by_color(varsalive) blockset = {} blockset[block] = True # reachable from outside blockset[nextblock] = False v_func = op.args[0] hs_func = self.hannotator.binding(v_func) if hs_func.is_green(): constantblock = block nonconstantblock = None else: constantblock = Block([]) nonconstantblock = Block([]) blockset[constantblock] = False blockset[nonconstantblock] = False v_is_constant = self.genop(block, 'is_constant', [v_func], resulttype = lltype.Bool) self.genswitch(block, v_is_constant, true = constantblock, false = nonconstantblock) postconstantblock = self.naive_split_block(constantblock, len(constantblock.operations)) blockset[postconstantblock] = False self.make_call(constantblock, op, reds, color) conversionblock = nextblock if color == 'red': assert not self.hannotator.binding(op.result).is_green() elif color == 'yellow': conversionblock = Block([copyvar(self.hannotator, v) for v in nextblock.inputargs]) v0 = conversionblock.inputargs[0] already_green = self.hannotator.binding(op.result).is_green() assert already_green == self.hannotator.binding(v0).is_green() if not already_green: RESULT = self.hannotator.binding(v0).concretetype hs = hintmodel.SomeLLAbstractConstant(RESULT, {}) self.hannotator.bindings[v0] = hs conversionblock.closeblock(Link(conversionblock.inputargs, nextblock)) blockset[conversionblock] = False # to merge some of the possibly many return jitstates self.mergepoint_set[nextblock] = 'local' resumepoint = self.get_resume_point(conversionblock) c_resumepoint = inputconst(lltype.Signed, resumepoint) self.genop(postconstantblock, 'collect_split', [c_resumepoint] + greens) resumeblock = self.get_resume_point_link(conversionblock).target postconstantblock.recloseblock(Link([], resumeblock)) blockset[resumeblock] = True # reachable from outside if nonconstantblock is not None: nonconstantblock.recloseblock(Link(linkargs, nextblock)) v_res = self.handle_residual_call_details( nonconstantblock, 0, op, color, blockset, preserve_res = (color != 'gray'), withexc=withexc) SSA_to_SSI(blockset, self.hannotator)
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_stst = shape assert not shape_star, "XXX not implemented" assert not shape_stst, "XXX not implemented" # for the first 'shape_cnt' arguments we need to generalize to # a common type call_nbargs = shape_cnt + len(shape_keys) did_something = False NODEFAULT = object() 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 = [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 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 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 new_wrapper(func, translator, newname=None): # The basic idea is to produce a flow graph from scratch, using the # help of the rtyper for the conversion of the arguments after they # have been decoded. bk = translator.annotator.bookkeeper graph = bk.getdesc(func).getuniquegraph() f = getfunctionptr(graph) FUNCTYPE = typeOf(f).TO newops = LowLevelOpList(translator.rtyper) varguments = [] for var in graph.startblock.inputargs: v = Variable(var) v.concretetype = PyObjPtr varguments.append(v) wrapper_inputargs = varguments[:] # use the rtyper to produce the conversions inputargs = f._obj.graph.getargs() for i in range(len(varguments)): if FUNCTYPE.ARGS[i] != PyObjPtr: rtyper = translator.rtyper r_arg = rtyper.bindingrepr(inputargs[i]) # give the rtyper a chance to know which function we are wrapping rtyper.set_wrapper_context(func) varguments[i] = newops.convertvar(varguments[i], r_from = pyobj_repr, r_to = r_arg) rtyper.set_wrapper_context(None) vlist = [inputconst(typeOf(f), f)] + varguments vresult = newops.genop('direct_call', vlist, resulttype=FUNCTYPE.RESULT) if FUNCTYPE.RESULT != PyObjPtr: # convert "result" back to a PyObject rtyper = translator.rtyper assert rtyper is not None, ( "needs the rtyper to perform function result conversions") r_result = rtyper.bindingrepr(f._obj.graph.getreturnvar()) vresult = newops.convertvar(vresult, r_from = r_result, r_to = pyobj_repr) # "return result" block = Block(wrapper_inputargs) wgraph = FunctionGraph('pyfn_' + (newname or func.func_name), block) translator.update_call_graph(wgraph, graph, object()) translator.graphs.append(wgraph) block.operations[:] = newops block.closeblock(Link([vresult], wgraph.returnblock)) wgraph.getreturnvar().concretetype = PyObjPtr checkgraph(wgraph) # the above convertvar()s may have created and annotated new helpers # that need to be specialized now translator.rtyper.specialize_more_blocks() return functionptr(FuncType([PyObjPtr] * len(wrapper_inputargs), PyObjPtr), wgraph.name, graph = wgraph, exception_policy = "CPython")
# _, origblock, origindex = self.jit_merge_point_pos op = origblock.operations[origindex] assert op.opname == 'jit_marker' assert op.args[0].value == 'jit_merge_point' greens_v, reds_v = decode_hp_hint_args(op) vlist = [Constant(self.portal_runner_ptr, self.PTR_PORTAL_FUNCTYPE)] vlist += greens_v vlist += reds_v v_result = Variable() v_result.concretetype = PORTALFUNC.RESULT newop = SpaceOperation('direct_call', vlist, v_result) del origblock.operations[origindex:] origblock.operations.append(newop) origblock.exitswitch = None origblock.recloseblock(Link([v_result], origportalgraph.returnblock)) checkgraph(origportalgraph) def add_finish(self): def finish(): if self.metainterp_sd.profiler.initialized: self.metainterp_sd.profiler.finish() self.metainterp_sd.cpu.finish_once() if self.cpu.translate_support_code: call_final_function(self.translator, finish, annhelper = self.annhelper) def rewrite_set_param(self): closures = {} graphs = self.translator.graphs
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) # XXX: does alloc_shortcut make sense also for ootype? if alloc_shortcut: T = spaceop.result.concretetype 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 handle_call_with_close_stack(self, hop): fnptr = hop.spaceop.args[0].value # 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. We need to 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) sradict[key] = Constant(p, lltype.Ptr(CONTAINER)) sra.append(sradict[key]) # # store the value of the arguments livevars = self.push_roots(hop) c_item0 = Constant('item0', lltype.Void) for v_arg, c_p in zip(hop.spaceop.args[1:], sra): if isinstance(v_arg.concretetype, lltype.Ptr): v_arg = hop.genop("cast_ptr_to_adr", [v_arg], resulttype=llmemory.Address) hop.genop("bare_setfield", [c_p, c_item0, v_arg]) # # make a copy of the graph that will reload the values graph2 = copygraph(fnptr._obj.graph) block2 = graph2.startblock block2.isstartblock = False block1 = Block([]) reloadedvars = [] for v, c_p in zip(block2.inputargs, sra): v = copyvar(None, v) if isinstance(v.concretetype, lltype.Ptr): w = Variable('tmp') w.concretetype = 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)) block1.isstartblock = True graph2.startblock = block1 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)], FUNC1.RESULT) # v_asm_stackwalk = hop.genop("cast_pointer", [c_asm_stackwalk], resulttype=lltype.Ptr(HELPERFUNC)) hop.genop("indirect_call", [v_asm_stackwalk, c_fnptr2, Constant(None, lltype.Void)], resultvar=hop.spaceop.result) self.pop_roots(hop, livevars)
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) # assert graph.startblock.operations[0].opname == 'generator_mark' graph.startblock.operations.pop(0) # insert_empty_startblock(None, graph) _insert_reads(graph.startblock, Entry.varnames) Entry.block = graph.startblock # mappings = [Entry] # stopblock = Block([]) v0 = Variable() v1 = Variable() stopblock.operations = [ SpaceOperation('simple_call', [Constant(StopIteration)], v0), SpaceOperation('type', [v0], v1), ] stopblock.closeblock(Link([v1, v0], 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): op = block.operations[index] if op.opname == 'yield': [v_yielded_value] = op.args del block.operations[index] newlink = split_block(None, 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) # v_resume = Variable('resume') block.operations.append( SpaceOperation('simple_call', [Constant(Resume)], v_resume)) for i, name in enumerate(varnames): block.operations.append( SpaceOperation( 'setattr', [v_resume, Constant(name), newlink.args[i]], Variable())) v_pair = Variable('pair') block.operations.append( SpaceOperation('newtuple', [v_resume, v_yielded_value], v_pair)) newlink.args = [v_pair] newlink.target = graph.returnblock # regular_entry_block = Block([Variable('entry')]) block = regular_entry_block for Resume in mappings: v_check = Variable() block.operations.append( SpaceOperation( 'simple_call', [Constant(isinstance), block.inputargs[0], Constant(Resume)], v_check)) block.exitswitch = v_check 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)