def test_split_block_exceptions(): for i in range(2): def raises(x): if x == 1: raise ValueError elif x == 2: raise KeyError return x def catches(x): try: y = x + 1 raises(y) except ValueError: return 0 except KeyError: return 1 return x graph, t = translate(catches, [int]) split_block(t.annotator, graph.startblock, i) checkgraph(graph) interp = LLInterpreter(t.rtyper) result = interp.eval_graph(graph, [0]) assert result == 0 result = interp.eval_graph(graph, [1]) assert result == 1 result = interp.eval_graph(graph, [2]) assert result == 2
def before_return_block(self): block = self.graph.returnblock block.operations = [] split_block(self.hannotator, block, 0) [link] = block.exits assert len(link.args) == 0 link.args = [self.c_dummy] link.target.inputargs = [self.new_void_var('dummy')] self.graph.returnblock = link.target self.graph.returnblock.operations = () return block
def test_split_blocks_simple(): for i in range(4): def f(x, y): z = x + y w = x * y return z + w graph, t = translate(f, [int, int]) split_block(t.annotator, graph.startblock, i) checkgraph(graph) interp = LLInterpreter(t.rtyper) result = interp.eval_graph(graph, [1, 2]) assert result == 5
def test_split_blocks_conditional(): for i in range(3): def f(x, y): if x + 12: return y + 1 else: return y + 2 graph, t = translate(f, [int, int]) split_block(t.annotator, graph.startblock, i) checkgraph(graph) interp = LLInterpreter(t.rtyper) result = interp.eval_graph(graph, [-12, 2]) assert result == 4 result = interp.eval_graph(graph, [0, 2]) assert result == 3
def handle_oopspec_call(self, block, pos, withexc): op = block.operations[pos] assert op.opname == 'direct_call' if withexc: op.opname = 'oopspec_call' else: op.opname = 'oopspec_call_noexc' if withexc: link = split_block(self.hannotator, block, pos+1) nextblock = link.target linkargs = link.args v_residual =self.genop(block, 'oopspec_was_residual', [], resulttype = lltype.Bool) residualblock = Block([]) self.genswitch(block, v_residual, true = residualblock, false = None) link_f = block.exits[0] link_f.args = linkargs link_f.target = nextblock residualblock.closeblock(Link(linkargs, nextblock)) blockset = { block: True, nextblock: False, residualblock: False } self.handle_after_residual_call_details( residualblock, 0, [], blockset, oop=True, withexc=True) SSA_to_SSI(blockset, self.hannotator)
def split_before_jit_merge_point(graph, portalblock, portalopindex): """Split the block just before the 'jit_merge_point', making sure the input args are in the canonical order. """ # split the block just before the jit_merge_point() if portalopindex > 0: link = split_block(None, portalblock, portalopindex) portalblock = link.target portalop = portalblock.operations[0] # split again, this time enforcing the order of the live vars # specified by decode_hp_hint_args(). assert portalop.opname == 'jit_marker' assert portalop.args[0].value == 'jit_merge_point' greens_v, reds_v = decode_hp_hint_args(portalop) link = split_block(None, portalblock, 0, greens_v + reds_v) return link.target
def rewire_links(splitblocks, graph): for block, splits in splitblocks.items(): # A splitting position is given by how many operations were # folded with the knowledge of an incoming link's constant. # Various incoming links may cause various splitting positions. # We split the block gradually, starting from the end. splits.sort() splits.reverse() for position, link, constants in splits: assert link.target is block if position == len(block.operations) and block.exitswitch is None: # a split here would leave nothing in the 2nd part, so # directly rewire the links assert len(block.exits) == 1 splitlink = block.exits[0] else: # split the block at the given position splitlink = split_block(None, block, position) assert list(block.exits) == [splitlink] assert link.target is block assert splitlink.prevblock is block complete_constants(link, constants) args = [constants.get(v, v) for v in splitlink.args] link.args = args link.target = splitlink.target
def split_before_jit_merge_point(graph, portalblock, portalopindex): """Find the block with 'jit_merge_point' and split just before, making sure the input args are in the canonical order. """ # split the block just before the jit_merge_point() if portalopindex > 0: link = split_block(None, portalblock, portalopindex) portalblock = link.target portalop = portalblock.operations[0] # split again, this time enforcing the order of the live vars # specified by the user in the jit_merge_point() call assert portalop.opname == "jit_marker" assert portalop.args[0].value == "jit_merge_point" livevars = [v for v in portalop.args[2:] if v.concretetype is not lltype.Void] link = split_block(None, portalblock, 0, livevars) return link.target
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)] assert afterblock.operations[0].opname == self.op.opname self.op = afterblock.operations.pop(0) #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 if ootype.isSubclass(LINK_SELF, INPUT_SELF): opname = 'ooupcast' else: assert ootype.isSubclass(INPUT_SELF, LINK_SELF) opname = 'oodowncast' v = Variable() v.concretetype = INPUT_SELF upcast = SpaceOperation(opname, [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 split_block_with_keepalive(block, index_operation, keep_alive_op_args=True, annotator=None): splitlink = split_block(annotator, block, index_operation) afterblock = splitlink.target conservative_keepalives = needs_conservative_livevar_calculation(block) if conservative_keepalives: keep_alive_vars = [var for var in block.getvariables() if var_needsgc(var)] # XXX you could maybe remove more, if the variables are kept # alive by something else. but this is sometimes hard to know for i, var in enumerate(keep_alive_vars): try: index = splitlink.args.index(var) newvar = afterblock.inputargs[index] except ValueError: splitlink.args.append(var) newvar = copyvar(annotator, var) afterblock.inputargs.append(newvar) keep_alive_vars[i] = newvar elif keep_alive_op_args and afterblock.operations: keep_alive_vars = [var for var in afterblock.operations[0].args if isinstance(var, Variable) and var_needsgc(var)] if len(afterblock.operations) > 1 or afterblock.exitswitch != c_last_exception: afterblock.operations[1:1] = generate_keepalive(keep_alive_vars, annotator=annotator) keep_alive_vars = [] else: keep_alive_vars = [] pos = len(afterblock.operations) if afterblock.exitswitch == c_last_exception: pos -= 1 # insert the keepalives just before the last operation # in case of exception-catching afterblock.operations[pos:pos] = generate_keepalive(keep_alive_vars) return splitlink
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 split_before_jit_merge_point(graph, portalblock, portalopindex): """Find the block with 'jit_merge_point' and split just before, making sure the input args are in the canonical order. """ # split the block just before the jit_merge_point() if portalopindex > 0: link = split_block(None, portalblock, portalopindex) portalblock = link.target portalop = portalblock.operations[0] # split again, this time enforcing the order of the live vars # specified by the user in the jit_merge_point() call assert portalop.opname == 'jit_marker' assert portalop.args[0].value == 'jit_merge_point' livevars = [v for v in portalop.args[2:] if v.concretetype is not lltype.Void] link = split_block(None, portalblock, 0, livevars) return link.target
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 handle_promote_hint(self, block, i): self.contains_promotion = True op = block.operations[i] v_promote = op.args[0] newop = SpaceOperation('revealconst', [v_promote], op.result) block.operations[i] = newop link = split_block(self.hannotator, block, i) reds, greens = self.sort_by_color(link.args) self.genop(block, 'save_locals', reds) v_finished_flag = self.genop(block, 'promote', [v_promote], resulttype = lltype.Bool) self.go_to_dispatcher_if(block, v_finished_flag)
def transform_block(self, graph, block): need_exc_matching = False n_gen_exc_checks = 0 if block is graph.exceptblock: return need_exc_matching, n_gen_exc_checks elif block is graph.returnblock: return need_exc_matching, n_gen_exc_checks last_operation = len(block.operations) - 1 if block.exitswitch == c_last_exception: need_exc_matching = True last_operation -= 1 elif ( len(block.exits) == 1 and block.exits[0].target is graph.returnblock and len(block.operations) and ( block.exits[0].args[0].concretetype is lltype.Void or block.exits[0].args[0] is block.operations[-1].result ) and block.operations[-1].opname not in ("malloc", "malloc_nonmovable") # special cases ): last_operation -= 1 lastblock = block for i in range(last_operation, -1, -1): op = block.operations[i] if not self.raise_analyzer.can_raise(op): continue splitlink = split_block(None, block, i + 1) afterblock = splitlink.target if lastblock is block: lastblock = afterblock self.gen_exc_check(block, graph.returnblock, afterblock) n_gen_exc_checks += 1 if need_exc_matching: assert lastblock.exitswitch == c_last_exception if not self.raise_analyzer.can_raise(lastblock.operations[-1]): # print ("operation %s cannot raise, but has exception" # " guarding in graph %s" % (lastblock.operations[-1], # graph)) lastblock.exitswitch = None lastblock.recloseblock(lastblock.exits[0]) lastblock.exits[0].exitcase = None else: self.insert_matching(lastblock, graph) return need_exc_matching, n_gen_exc_checks
def transform_block(self, graph, block): need_exc_matching = False n_gen_exc_checks = 0 if block is graph.exceptblock: return need_exc_matching, n_gen_exc_checks elif block is graph.returnblock: return need_exc_matching, n_gen_exc_checks last_operation = len(block.operations) - 1 if block.exitswitch == c_last_exception: need_exc_matching = True last_operation -= 1 elif (len(block.exits) == 1 and block.exits[0].target is graph.returnblock and len(block.operations) and (block.exits[0].args[0].concretetype is lltype.Void or block.exits[0].args[0] is block.operations[-1].result) and block.operations[-1].opname not in ( 'malloc', # special cases 'malloc_nonmovable')): last_operation -= 1 lastblock = block for i in range(last_operation, -1, -1): op = block.operations[i] if not self.raise_analyzer.can_raise(op): continue splitlink = split_block(None, block, i + 1) afterblock = splitlink.target if lastblock is block: lastblock = afterblock self.gen_exc_check(block, graph.returnblock, afterblock) n_gen_exc_checks += 1 if need_exc_matching: assert lastblock.exitswitch == c_last_exception if not self.raise_analyzer.can_raise(lastblock.operations[-1]): #print ("operation %s cannot raise, but has exception" # " guarding in graph %s" % (lastblock.operations[-1], # graph)) lastblock.exitswitch = None lastblock.recloseblock(lastblock.exits[0]) lastblock.exits[0].exitcase = None else: self.insert_matching(lastblock, graph) return need_exc_matching, n_gen_exc_checks
def split_block_with_keepalive(block, index_operation, keep_alive_op_args=True, annotator=None): splitlink = split_block(annotator, block, index_operation) afterblock = splitlink.target conservative_keepalives = needs_conservative_livevar_calculation(block) if conservative_keepalives: keep_alive_vars = [ var for var in block.getvariables() if var_needsgc(var) ] # XXX you could maybe remove more, if the variables are kept # alive by something else. but this is sometimes hard to know for i, var in enumerate(keep_alive_vars): try: index = splitlink.args.index(var) newvar = afterblock.inputargs[index] except ValueError: splitlink.args.append(var) newvar = copyvar(annotator, var) afterblock.inputargs.append(newvar) keep_alive_vars[i] = newvar elif keep_alive_op_args and afterblock.operations: keep_alive_vars = [ var for var in afterblock.operations[0].args if isinstance(var, Variable) and var_needsgc(var) ] if len(afterblock.operations ) > 1 or afterblock.exitswitch != c_last_exception: afterblock.operations[1:1] = generate_keepalive( keep_alive_vars, annotator=annotator) keep_alive_vars = [] else: keep_alive_vars = [] pos = len(afterblock.operations) if afterblock.exitswitch == c_last_exception: pos -= 1 # insert the keepalives just before the last operation # in case of exception-catching afterblock.operations[pos:pos] = generate_keepalive(keep_alive_vars) return splitlink
def handle_raisingop(self, block, i, opdesc): op = block.operations[i] if self.hannotator.binding(op.result).is_green(): # case not really well supported v_red = Variable(op.result) v_red.concretetype = op.result.concretetype hs_red = hintmodel.SomeLLAbstractVariable(op.result.concretetype) self.hannotator.setbinding(v_red, hs_red) spaceop = SpaceOperation('revealconst', [v_red], op.result) op.result = v_red i += 1 block.operations.insert(i, spaceop) link = split_block(self.hannotator, block, i+1) reds, greens = self.sort_by_color(link.args) self.genop(block, 'save_locals', reds) resumepoint = self.get_resume_point(link.target) c_resumepoint = inputconst(lltype.Signed, resumepoint) assert len(opdesc.canraise) == 1 # for now c_canraise = inputconst(lltype.Void, opdesc.canraise[0]) self.genop(block, 'split_raisingop', [self.c_dummy, c_resumepoint, c_canraise] + greens)
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_unwind_handling(self, block, i): # for the case where we are resuming to an except: # block we need to store here a list of links that # might be resumed to, and in insert_resume_handling # we need to basically copy each link onto the # resuming block. # # it probably also makes sense to compute the list of # args to save once, here, and save that too. # # finally, it is important that the fetch_retval # function be called right at the end of the resuming # block, and that it is called even if the return # value is not again used. edata = self.translator.rtyper.getexceptiondata() etype = edata.lltype_of_exception_type evalue = edata.lltype_of_exception_value if i == len(block.operations) - 1 \ and block.exitswitch == model.c_last_exception: link = block.exits[0] exitcases = dict.fromkeys([l.exitcase for l in block.exits]) nextblock = None else: link = split_block(None, block, i+1) nextblock = link.target block.exitswitch = model.c_last_exception link.llexitcase = None # add a general Exception link, because all calls can # raise anything v_exctype = varoftype(etype) v_excvalue = varoftype(evalue) newlink = model.Link([v_exctype, v_excvalue], self.curr_graph.exceptblock, Exception) newlink.last_exception = v_exctype newlink.last_exc_value = v_excvalue newexits = list(block.exits) newexits.append(newlink) block.recloseblock(*newexits) self.translator.rtyper._convert_link(block, newlink) v_unwind_exception = varoftype(evalue) op = block.operations[i] args = vars_to_save(block) save_block, varsforcall = self.generate_save_and_resume_blocks( args, v_unwind_exception, op.result, block.exits) newlink = model.Link(varsforcall + [v_unwind_exception], save_block, code.UnwindException) newlink.last_exception = model.Constant(code.UnwindException, etype) newlink.last_exc_value = v_unwind_exception newexits = list(block.exits) newexits.insert(1, newlink) block.recloseblock(*newexits) self.translator.rtyper._convert_link(block, newlink) return nextblock
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 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)