def insert_merge(self, block, kind): allvars = block.inputargs[:] block.inputargs[:] = [copyvar(self.hannotator, v) for v in allvars] reds1, greens1 = self.sort_by_color(block.inputargs) reds3, greens3 = self.sort_by_color(allvars) nextblock = self.naive_split_block(block, 0) self.genop(block, 'save_locals', reds1) mp = self.mergepointfamily.add(kind) c_mp = inputconst(lltype.Void, mp) if kind == 'global': prefix = 'global_' greens2 = [copyvar(self.hannotator, v) for v in greens1] mergeblock = self.naive_split_block(block, len(block.operations)) mergeblock.inputargs[:] = greens2 self.genop(block, 'save_greens', greens1) block.recloseblock(Link([self.c_dummy], self.graph.returnblock)) N = self.get_resume_point(mergeblock) c_resumeindex = inputconst(lltype.Signed, N) self.genop(block, 'guard_global_merge', [c_resumeindex]) # Note: the jitstate.greens list will contain the correct # green gv's for the following global_merge_point, because # the green values have just been restored by the resume # point logic here else: mergeblock = block greens2 = greens1 prefix = '' mergeblock.exits[0].args[:] = greens2 nextblock.inputargs[:] = greens3 v_finished_flag = self.genop(mergeblock, '%smerge_point' % (prefix,), [self.c_mpfamily, c_mp] + greens2, resulttype = lltype.Bool) self.go_to_dispatcher_if(mergeblock, v_finished_flag) restoreops = [] for i, v in enumerate(reds3): c = inputconst(lltype.Signed, i) restoreops.append(SpaceOperation('restore_local', [c], v)) nextblock.operations[:0] = restoreops if kind == 'global': N = self.get_resume_point(nextblock) self.mergepointfamily.resumepoint_after_mergepoint[mp] = N
def insert_resume_handling(self, graph): old_start_block = graph.startblock newinputargs = [unsimplify.copyvar(self.translator.annotator, v) for v in old_start_block.inputargs] new_start_block = model.Block(newinputargs) v_resume_substate = varoftype(lltype.Signed) new_start_block.operations.append( model.SpaceOperation("getfield", [self.ll_global_state, self.c_restart_substate_name], v_resume_substate)) not_resuming_link = model.Link(newinputargs, old_start_block, -1) not_resuming_link.llexitcase = -1 resuming_links = [] for resume_index, resume_block in enumerate(self.resume_blocks): resuming_links.append( model.Link([v_resume_substate], resume_block, resume_index)) resuming_links[-1].llexitcase = resume_index new_start_block.exitswitch = v_resume_substate new_start_block.closeblock(not_resuming_link, *resuming_links) old_start_block.isstartblock = False new_start_block.isstartblock = True graph.startblock = new_start_block for block in graph.iterblocks(): if len(block.exits) == 1 and block.exitswitch is not None: block.exitswitch = None block.exits[0].exitcase = block.exits[0].llexitcase = None simplify.simplify_graph(graph, [simplify.eliminate_empty_blocks, simplify.join_blocks, simplify.transform_dead_op_vars])
def passon_vars(self, cache_key): if cache_key in self._passon_vars: return self._passon_vars[cache_key] result = [copyvar(None, var) for var in self.original_passon_vars] self._passon_vars[cache_key] = result return result
def new_block_before(self, block): newinputargs = [copyvar(self.hannotator, var) for var in block.inputargs] newblock = Block(newinputargs) bridge = Link(newinputargs, block) newblock.closeblock(bridge) return newblock
def SSA_to_SSI(graph_or_blocks, annotator=None): """Turn a number of blocks belonging to a flow graph into valid (i.e. SSI) form, assuming that they are only in SSA form (i.e. they can use each other's variables directly, without having to pass and rename them along links). 'graph_or_blocks' can be a graph, or just a dict that lists some blocks from a graph, as follows: {block: reachable-from-outside-flag}. """ from pypy.translator.unsimplify import copyvar entrymap = mkinsideentrymap(graph_or_blocks) builder = DataFlowFamilyBuilder(graph_or_blocks) variable_families = builder.get_variable_families() del builder pending = [] # list of (block, var-used-but-not-defined) for block in entrymap: variables_created = variables_created_in(block) variables_used = {} for op in block.operations: for v in op.args: variables_used[v] = True variables_used[block.exitswitch] = True for link in block.exits: for v in link.args: variables_used[v] = True for v in variables_used: if isinstance(v, Variable): if v not in variables_created: pending.append((block, v)) while pending: block, v = pending.pop() v_rep = variable_families.find_rep(v) variables_created = variables_created_in(block) if v in variables_created: continue # already ok for w in variables_created: w_rep = variable_families.find_rep(w) if v_rep is w_rep: # 'w' is in the same family as 'v', so we can simply # reuse its value for 'v' block.renamevariables({v: w}) break else: # didn't find it. Add it to all incoming links. try: links = entrymap[block] except KeyError: raise Exception("SSA_to_SSI failed: no way to give a value to" " %r in %r" % (v, block)) w = copyvar(annotator, v) variable_families.union(v, w) block.renamevariables({v: w}) block.inputargs.append(w) for link in links: link.args.append(v) pending.append((link.prevblock, v))
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 get_new_name(self, var): if var is None: return None if isinstance(var, Constant): return var if var not in self.varmap: self.varmap[var] = copyvar(None, var) return self.varmap[var]
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 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 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 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 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 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 genop(self, block, opname, args, resulttype=None, result_like=None, red=False): # 'result_like' can be a template variable whose hintannotation is # copied if resulttype is not None: v_res = varoftype(resulttype) if red: hs = hintmodel.SomeLLAbstractVariable(resulttype) else: hs = hintmodel.SomeLLAbstractConstant(resulttype, {}) self.hannotator.setbinding(v_res, hs) elif result_like is not None: v_res = copyvar(self.hannotator, result_like) else: v_res = self.new_void_var() spaceop = SpaceOperation(opname, args, v_res) if isinstance(block, list): block.append(spaceop) else: block.operations.append(spaceop) return v_res
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 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 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_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 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 copyvar(var): if isinstance(var, model.Variable): return unsimplify.copyvar(None, var) else: return varoftype(var.concretetype)
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) alloc_shortcut = False spaceop = block.operations[-1] if spaceop.opname in ('malloc', 'malloc_varsize'): alloc_shortcut = True elif spaceop.opname == 'direct_call': fnobj = spaceop.args[0].value._obj if hasattr(fnobj, '_callable'): oopspec = getattr(fnobj._callable, 'oopspec', None) if oopspec and oopspec == 'newlist(length)': alloc_shortcut = True if alloc_shortcut: T = spaceop.result.concretetype var_no_exc = llops.genop('ptr_nonzero', [spaceop.result], lltype.Bool) else: v_exc_type = self.gen_getfield('exc_type', llops) var_no_exc = llops.genop('ptr_iszero', [v_exc_type], lltype.Bool) block.operations.extend(llops) block.exitswitch = var_no_exc #exception occurred case l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock) 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': insert_zeroing_op = True elif spaceop.opname == 'flavored_malloc': flavor = spaceop.args[0].value if flavor.startswith('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 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 l = Link([error_constant(returnblock.inputargs[0].concretetype)], returnblock) 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 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 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)
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 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)))