def patch_graph(self, copy_graph): graph = self.graph if self.db.gctransformer and self.db.gctransformer.inline: if copy_graph: graph = copygraph(graph, shallow=True) self.db.gctransformer.inline_helpers(graph) return graph
def find_portal(self): graphs = self.translator.graphs self.jit_merge_point_pos = find_jit_merge_point(graphs) graph, block, pos = self.jit_merge_point_pos op = block.operations[pos] args = op.args[2:] s_binding = self.translator.annotator.binding self.portal_args_s = [s_binding(v) for v in args] graph = copygraph(graph) graph.startblock.isstartblock = False graph.startblock = support.split_before_jit_merge_point( *find_jit_merge_point([graph])) graph.startblock.isstartblock = True # a crash in the following checkgraph() means that you forgot # to list some variable in greens=[] or reds=[] in JitDriver. checkgraph(graph) for v in graph.getargs(): assert isinstance(v, Variable) assert len(dict.fromkeys(graph.getargs())) == len(graph.getargs()) self.translator.graphs.append(graph) self.portal_graph = graph # it's a bit unbelievable to have a portal without func assert hasattr(graph, "func") graph.func._dont_inline_ = True graph.func._jit_unroll_safe_ = True self.jitdriver = block.operations[pos].args[1].value
def split_graph_and_record_jitdriver(self, graph, block, pos): op = block.operations[pos] jd = JitDriverStaticData() jd._jit_merge_point_pos = (graph, op) args = op.args[2:] s_binding = self.translator.annotator.binding jd._portal_args_s = [s_binding(v) for v in args] graph = copygraph(graph) graph.startblock.isstartblock = False [jmpp] = find_jit_merge_points([graph]) graph.startblock = support.split_before_jit_merge_point(*jmpp) graph.startblock.isstartblock = True # a crash in the following checkgraph() means that you forgot # to list some variable in greens=[] or reds=[] in JitDriver. checkgraph(graph) for v in graph.getargs(): assert isinstance(v, Variable) assert len(dict.fromkeys(graph.getargs())) == len(graph.getargs()) self.translator.graphs.append(graph) jd.portal_graph = graph # it's a bit unbelievable to have a portal without func assert hasattr(graph, "func") graph.func._dont_inline_ = True graph.func._jit_unroll_safe_ = True jd.jitdriver = block.operations[pos].args[1].value jd.portal_runner_ptr = "<not set so far>" jd.result_type = history.getkind(jd.portal_graph.getreturnvar() .concretetype)[0] self.jitdrivers_sd.append(jd)
def transform_graph_to_jitcode(self, graph, jitcode, verbose): """Transform a graph into a JitCode containing the same bytecode in a different format. """ portal_jd = self.callcontrol.jitdriver_sd_from_portal_graph(graph) graph = copygraph(graph, shallowvars=True) # # step 1: mangle the graph so that it contains the final instructions # that we want in the JitCode, but still as a control flow graph transform_graph(graph, self.cpu, self.callcontrol, portal_jd) # # step 2: perform register allocation on it regallocs = {} for kind in KINDS: regallocs[kind] = perform_register_allocation(graph, kind) # # step 3: flatten the graph to produce human-readable "assembler", # which means mostly producing a linear list of operations and # inserting jumps or conditional jumps. This is a list of tuples # of the shape ("opname", arg1, ..., argN) or (Label(...),). ssarepr = flatten_graph(graph, regallocs) # # step 3b: compute the liveness around certain operations compute_liveness(ssarepr) # # step 4: "assemble" it into a JitCode, which contains a sequence # of bytes and lists of constants. It's during this step that # constants are cast to their normalized type (Signed, GCREF or # Float). self.assembler.assemble(ssarepr, jitcode) # # print the resulting assembler if self.debug: self.print_ssa_repr(ssarepr, portal_jd, verbose)
def split_graph_and_record_jitdriver(self, graph, block, pos): op = block.operations[pos] jd = JitDriverStaticData() jd._jit_merge_point_in = graph args = op.args[2:] s_binding = self.translator.annotator.binding jd._portal_args_s = [s_binding(v) for v in args] graph = copygraph(graph) [jmpp] = find_jit_merge_points([graph]) graph.startblock = support.split_before_jit_merge_point(*jmpp) # a crash in the following checkgraph() means that you forgot # to list some variable in greens=[] or reds=[] in JitDriver, # or that a jit_merge_point() takes a constant as an argument. checkgraph(graph) for v in graph.getargs(): assert isinstance(v, Variable) assert len(dict.fromkeys(graph.getargs())) == len(graph.getargs()) self.translator.graphs.append(graph) jd.portal_graph = graph # it's a bit unbelievable to have a portal without func assert hasattr(graph, "func") graph.func._dont_inline_ = True graph.func._jit_unroll_safe_ = True jd.jitdriver = block.operations[pos].args[1].value jd.portal_runner_ptr = "<not set so far>" jd.result_type = history.getkind(jd.portal_graph.getreturnvar() .concretetype)[0] self.jitdrivers_sd.append(jd)
def build_specialized_graph(self, graph, key, nodelist): graph2 = copygraph(graph) virtualframe = VirtualFrame(graph2.startblock, 0, nodelist) graphbuilder = GraphBuilder(self, graph2) specblock = graphbuilder.start_from_virtualframe(virtualframe) specgraph = graph2 specgraph.name += '_mallocv' specgraph.startblock = specblock self.specialized_graphs[key] = ('call', specgraph) try: graphbuilder.propagate_specializations() except ForcedInline, e: if self.verbose: log.mallocv('%s inlined: %s' % (graph.name, e)) self.specialized_graphs[key] = ('inline', None)
def cachedgraph(self, key, alt_name=None): verbose = self.bookkeeper.annotator.translator.config.translation.verbose try: return self._cache[key] except KeyError: bk = self.bookkeeper look = bk.annotator.policy.look_inside_graph(self.origgraph) if look and not callable(look): # normal case graph = copygraph(self.origgraph, varmap=TIMESHIFTMAP) if not self._cache: bk.nonstuboriggraphcount += 1 if verbose: log(str(graph)) else: log.dot() else: graph = self.build_callback_graph(self.origgraph, look) if not self._cache: bk.stuboriggraphcount += 1 if verbose: log.stub(str(graph)) else: log.stub.dot() graph.tag = 'timeshifted' try: etrafo = bk.annotator.exceptiontransformer except AttributeError: pass else: # except transform the copied graph before its hint-annotation etrafo.create_exception_handling(graph, always_exc_clear=True) if alt_name is not None: graph.name = alt_name self._cache[key] = graph self.bookkeeper.annotator.translator.graphs.append(graph) return graph
def patch_graph(self): graph = self.graph self.gcrootscount = 0 if self.db.gctransformer: # inline the GC helpers (malloc, write_barrier) into # a copy of the graph graph = copygraph(graph, shallow=True) self.db.gctransformer.inline_helpers(graph) # the 'gc_reload_possibly_moved' operations make the graph not # really SSA. Fix them now. for block in graph.iterblocks(): rename = {} for op in block.operations: if rename: op.args = [rename.get(v, v) for v in op.args] if op.opname == 'gc_reload_possibly_moved': v_newaddr, v_targetvar = op.args assert isinstance(v_targetvar.concretetype, lltype.Ptr) v_newptr = Variable() v_newptr.concretetype = v_targetvar.concretetype op.opname = 'cast_adr_to_ptr' op.args = [v_newaddr] op.result = v_newptr rename[v_targetvar] = v_newptr elif op.opname == 'llvm_store_gcroot': index = op.args[0].value self.gcrootscount = max(self.gcrootscount, index+1) if rename: block.exitswitch = rename.get(block.exitswitch, block.exitswitch) for link in block.exits: link.args = [rename.get(v, v) for v in link.args] # fix special cases that llvm can't handle remove_double_links(self.db.translator.annotator, graph) no_links_to_startblock(graph) return graph
def patch_graph(self): graph = self.graph self.gcrootscount = 0 if self.db.gctransformer: # inline the GC helpers (malloc, write_barrier) into # a copy of the graph graph = copygraph(graph, shallow=True) self.db.gctransformer.inline_helpers(graph) # the 'gc_reload_possibly_moved' operations make the graph not # really SSA. Fix them now. for block in graph.iterblocks(): rename = {} for op in block.operations: if rename: op.args = [rename.get(v, v) for v in op.args] if op.opname == 'gc_reload_possibly_moved': v_newaddr, v_targetvar = op.args assert isinstance(v_targetvar.concretetype, lltype.Ptr) v_newptr = Variable() v_newptr.concretetype = v_targetvar.concretetype op.opname = 'cast_adr_to_ptr' op.args = [v_newaddr] op.result = v_newptr rename[v_targetvar] = v_newptr elif op.opname == 'llvm_store_gcroot': index = op.args[0].value self.gcrootscount = max(self.gcrootscount, index + 1) if rename: block.exitswitch = rename.get(block.exitswitch, block.exitswitch) for link in block.exits: link.args = [rename.get(v, v) for v in link.args] # fix special cases that llvm can't handle remove_double_links(self.db.translator.annotator, graph) no_links_to_startblock(graph) return graph
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 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, immortal=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 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)) 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), ASM_FRAMEDATA_HEAD_PTR], 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, c_gcrootanchor, Constant(None, lltype.Void)], resultvar=hop.spaceop.result) self.pop_roots(hop, livevars)