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_with_keepalive(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 do_inline(self, block, index_operation): splitlink = split_block_with_keepalive(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 test_sbwk_should_insert_keepalives(): # this is testing something like: # v0 <- op_producing_non_gc # v1 <- op_using_v0 <- split here llops = LowLevelOpList() ptr_a = varoftype(lltype.Ptr(GcA)) v_res = llops.genop("getfield", [ptr_a, model.Constant('b', lltype.Void)], resulttype=lltype.Ptr(NonGcB)) llops.genop("direct_call", [model.Constant(None, lltype.Void), v_res], resulttype=lltype.Void) block = model.Block([ptr_a]) block.operations.extend(llops) block.closeblock(model.Link([], None)) link = split_block_with_keepalive(block, 1) assert 'keepalive' in [op.opname for op in link.target.operations]
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)): 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 = support.split_block_with_keepalive(block, i+1, False) 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