def transform_graph(self, graph): if graph in self.minimal_transform: if self.minimalgctransformer: self.minimalgctransformer.transform_graph(graph) del self.minimal_transform[graph] return if graph in self.seen_graphs: return self.seen_graphs[graph] = True self.links_to_split = {} # link -> vars to pop_alive across the link # for sanity, we need an empty block at the start of the graph inserted_empty_startblock = False if not starts_with_empty_block(graph): insert_empty_startblock(self.translator.annotator, graph) inserted_empty_startblock = True is_borrowed = self.compute_borrowed_vars(graph) for block in graph.iterblocks(): self.transform_block(block, is_borrowed) for link, livecounts in self.links_to_split.iteritems(): llops = LowLevelOpList() for var, livecount in livecounts.iteritems(): for i in range(livecount): self.pop_alive(var, llops) for i in range(-livecount): self.push_alive(var, llops) if llops: if link.prevblock.exitswitch is None: link.prevblock.operations.extend(llops) else: insert_empty_block(self.translator.annotator, link, llops) # remove the empty block at the start of the graph, which should # still be empty (but let's check) if starts_with_empty_block(graph) and inserted_empty_startblock: old_startblock = graph.startblock graph.startblock.isstartblock = False graph.startblock = graph.startblock.exits[0].target graph.startblock.isstartblock = True checkgraph(graph) self.links_to_split = None v = Variable('vanishing_exc_value') v.concretetype = self.get_lltype_of_exception_value() llops = LowLevelOpList() self.pop_alive(v, llops) graph.exc_cleanup = (v, list(llops)) return is_borrowed # xxx for tests only
def insert_return_conversion(self, link, targettype, retvar): llops = LowLevelOpList() newvar = gen_cast(llops, targettype, retvar) convertblock = unsimplify.insert_empty_block(None, link, llops) # begin ouch! for index, linkvar in enumerate(convertblock.exits[0].args): # does this var come from retval ? try: index1 = convertblock.inputargs.index(linkvar) except ValueError: # e.g. linkvar is a Constant continue if link.args[index1] is retvar: # yes convertblock.exits[0].args[index] = newvar
def prepare_constant_fold_link(link, constants, splitblocks): block = link.target if not block.operations: # when the target block has no operation, there is nothing we can do # except trying to fold an exitswitch if block.exitswitch is not None and block.exitswitch in constants: llexitvalue = constants[block.exitswitch].value rewire_link_for_known_exitswitch(link, llexitvalue) return folded_count = fold_op_list(block.operations, constants, exit_early=True) n = len(block.operations) if block.exitswitch == c_last_exception: n -= 1 # is the next, non-folded operation an indirect_call? m = folded_count while m < n and block.operations[m].opname == 'keepalive': m += 1 if m < n: nextop = block.operations[m] if nextop.opname == 'indirect_call' and nextop.args[0] in constants: # indirect_call -> direct_call callargs = [constants[nextop.args[0]]] constants1 = constants.copy() complete_constants(link, constants1) newkeepalives = [] for i in range(folded_count, m): [v] = block.operations[i].args v = constants1.get(v, v) v_void = Variable() v_void.concretetype = lltype.Void newkeepalives.append(SpaceOperation('keepalive', [v], v_void)) for v in nextop.args[1:-1]: callargs.append(constants1.get(v, v)) v_result = Variable(nextop.result) v_result.concretetype = nextop.result.concretetype constants[nextop.result] = v_result callop = SpaceOperation('direct_call', callargs, v_result) newblock = insert_empty_block(None, link, newkeepalives + [callop]) [link] = newblock.exits assert link.target is block folded_count = m+1 if folded_count > 0: splits = splitblocks.setdefault(block, []) splits.append((folded_count, link, constants))
def prepare_constant_fold_link(link, constants, splitblocks): block = link.target if not block.operations: # when the target block has no operation, there is nothing we can do # except trying to fold an exitswitch if block.exitswitch is not None and block.exitswitch in constants: llexitvalue = constants[block.exitswitch].value rewire_link_for_known_exitswitch(link, llexitvalue) return folded_count = fold_op_list(block.operations, constants, exit_early=True) n = len(block.operations) if block.exitswitch == c_last_exception: n -= 1 # is the next, non-folded operation an indirect_call? m = folded_count while m < n and block.operations[m].opname == 'keepalive': m += 1 if m < n: nextop = block.operations[m] if nextop.opname == 'indirect_call' and nextop.args[0] in constants: # indirect_call -> direct_call callargs = [constants[nextop.args[0]]] constants1 = constants.copy() complete_constants(link, constants1) newkeepalives = [] for i in range(folded_count, m): [v] = block.operations[i].args v = constants1.get(v, v) v_void = Variable() v_void.concretetype = lltype.Void newkeepalives.append(SpaceOperation('keepalive', [v], v_void)) for v in nextop.args[1:-1]: callargs.append(constants1.get(v, v)) v_result = Variable(nextop.result) v_result.concretetype = nextop.result.concretetype constants[nextop.result] = v_result callop = SpaceOperation('direct_call', callargs, v_result) newblock = insert_empty_block(None, link, newkeepalives + [callop]) [link] = newblock.exits assert link.target is block folded_count = m + 1 if folded_count > 0: splits = splitblocks.setdefault(block, []) splits.append((folded_count, link, constants))
def insert_link_conversions(self, block, skip=0): # insert the needed conversions on the links can_insert_here = block.exitswitch is None and len(block.exits) == 1 for link in block.exits[skip:]: self._convert_link(block, link) inputargs_reprs = self.setup_block_entry(link.target) newops = self.make_new_lloplist(block) newlinkargs = {} for i in range(len(link.args)): a1 = link.args[i] r_a2 = inputargs_reprs[i] if isinstance(a1, Constant): link.args[i] = inputconst(r_a2, a1.value) continue # the Constant was typed, done if a1 is link.last_exception: r_a1 = self.exceptiondata.r_exception_type elif a1 is link.last_exc_value: r_a1 = self.exceptiondata.r_exception_value else: r_a1 = self.bindingrepr(a1) if r_a1 == r_a2: continue # no conversion needed try: new_a1 = newops.convertvar(a1, r_a1, r_a2) except TyperError, e: self.gottypererror(e, block, link, newops) continue # try other args if new_a1 != a1: newlinkargs[i] = new_a1 if newops: if can_insert_here: block.operations.extend(newops) else: # cannot insert conversion operations around a single # link, unless it is the only exit of this block. # create a new block along the link... newblock = insert_empty_block( self.annotator, link, # ...and store the conversions there. newops=newops) link = newblock.exits[0] for i, new_a1 in newlinkargs.items(): link.args[i] = new_a1
def insert_link_conversions(self, block, skip=0): # insert the needed conversions on the links can_insert_here = block.exitswitch is None and len(block.exits) == 1 for link in block.exits[skip:]: self._convert_link(block, link) inputargs_reprs = self.setup_block_entry(link.target) newops = self.make_new_lloplist(block) newlinkargs = {} for i in range(len(link.args)): a1 = link.args[i] r_a2 = inputargs_reprs[i] if isinstance(a1, Constant): link.args[i] = inputconst(r_a2, a1.value) continue # the Constant was typed, done if a1 is link.last_exception: r_a1 = self.exceptiondata.r_exception_type elif a1 is link.last_exc_value: r_a1 = self.exceptiondata.r_exception_value else: r_a1 = self.bindingrepr(a1) if r_a1 == r_a2: continue # no conversion needed try: new_a1 = newops.convertvar(a1, r_a1, r_a2) except TyperError, e: self.gottypererror(e, block, link, newops) continue # try other args if new_a1 != a1: newlinkargs[i] = new_a1 if newops: if can_insert_here: block.operations.extend(newops) else: # cannot insert conversion operations around a single # link, unless it is the only exit of this block. # create a new block along the link... newblock = insert_empty_block(self.annotator, link, # ...and store the conversions there. newops=newops) link = newblock.exits[0] for i, new_a1 in newlinkargs.items(): link.args[i] = new_a1
# the exception cannot actually occur at all. # This is set by calling exception_cannot_occur(). # We just remove all exception links. block.exitswitch = None block.exits = block.exits[:1] else: # We have to split the block in two, with the exception-catching # exitswitch after the llop at 'pos', and the extra operations # in the new part of the block, corresponding to the # no-exception case. See for example test_rlist.test_indexerror # or test_rpbc.test_multiple_ll_one_hl_op. assert 0 <= pos < len(newops) - 1 extraops = block.operations[pos+1:] del block.operations[pos+1:] extrablock = insert_empty_block(self.annotator, noexclink, newops = extraops) if extrablock is None: self.insert_link_conversions(block) else: # skip the extrablock as a link target, its link doesn't need conversions # by construction, OTOH some of involved vars have no annotation # so proceeding with it would kill information self.insert_link_conversions(block, skip=1) # consider it as a link source instead self.insert_link_conversions(extrablock) def _convert_link(self, block, link): if link.exitcase is not None and link.exitcase != 'default': if isinstance(block.exitswitch, Variable):
# the exception cannot actually occur at all. # This is set by calling exception_cannot_occur(). # We just remove all exception links. block.exitswitch = None block.exits = block.exits[:1] else: # We have to split the block in two, with the exception-catching # exitswitch after the llop at 'pos', and the extra operations # in the new part of the block, corresponding to the # no-exception case. See for example test_rlist.test_indexerror # or test_rpbc.test_multiple_ll_one_hl_op. assert 0 <= pos < len(newops) - 1 extraops = block.operations[pos + 1:] del block.operations[pos + 1:] extrablock = insert_empty_block(self.annotator, noexclink, newops=extraops) if extrablock is None: self.insert_link_conversions(block) else: # skip the extrablock as a link target, its link doesn't need conversions # by construction, OTOH some of involved vars have no annotation # so proceeding with it would kill information self.insert_link_conversions(block, skip=1) # consider it as a link source instead self.insert_link_conversions(extrablock) def _convert_link(self, block, link): if link.exitcase is not None and link.exitcase != 'default': if isinstance(block.exitswitch, Variable):
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 run(self, vlist, vmeth, appendblock): # first check that the 'append' method object doesn't escape for op in appendblock.operations: if op.opname == 'simple_call' and op.args[0] is vmeth: pass elif vmeth in op.args: raise DetectorFailed # used in another operation for link in appendblock.exits: if vmeth in link.args: raise DetectorFailed # escapes to a next block self.vmeth = vmeth self.vlistfamily = self.variable_families.find_rep(vlist) newlistblock = self.newlist_v[self.vlistfamily] self.vlistcone = {newlistblock: True} self.escapes = {self.graph.returnblock: True, self.graph.exceptblock: True} # in which loop are we? for loopnextblock, iterblock, viterfamily in self.loops: # check that the vlist is alive across the loop head block, # which ensures that we have a whole loop where the vlist # doesn't change if not self.vlist_alive(loopnextblock): continue # no - unrelated loop # check that we cannot go from 'newlist' to 'append' without # going through the 'iter' of our loop (and the following 'next'). # This ensures that the lifetime of vlist is cleanly divided in # "before" and "after" the loop... if self.reachable(newlistblock, appendblock, avoid=iterblock): continue # ... with the possible exception of links from the loop # body jumping back to the loop prologue, between 'newlist' and # 'iter', which we must forbid too: if self.reachable(loopnextblock, iterblock, avoid=newlistblock): continue # there must not be a larger number of calls to 'append' than # the number of elements that 'next' returns, so we must ensure # that we cannot go from 'append' to 'append' again without # passing 'next'... if self.reachable(appendblock, appendblock, avoid=loopnextblock): continue # ... and when the iterator is exhausted, we should no longer # reach 'append' at all. stopblocks = [link.target for link in loopnextblock.exits if link.exitcase is not None] accepted = True for stopblock1 in stopblocks: if self.reachable(stopblock1, appendblock, avoid=newlistblock): accepted = False if not accepted: continue # now explicitly find the "loop body" blocks: they are the ones # from which we can reach 'append' without going through 'iter'. # (XXX inefficient) loopbody = {} for block in self.graph.iterblocks(): if (self.vlist_alive(block) and self.reachable(block, appendblock, iterblock)): loopbody[block] = True # if the 'append' is actually after a 'break' or on a path that # can only end up in a 'break', then it won't be recorded as part # of the loop body at all. This is a strange case where we have # basically proved that the list will be of length 1... too # uncommon to worry about, I suspect if appendblock not in loopbody: continue # This candidate loop is acceptable if the list is not escaping # too early, i.e. in the loop header or in the loop body. loopheader = list(self.enum_blocks_with_vlist_from(newlistblock, avoid=loopnextblock)) assert loopheader[0] is newlistblock escapes = False for block in loopheader + loopbody.keys(): assert self.vlist_alive(block) if self.vlist_escapes(block): escapes = True break if not escapes: break # accept this loop! else: raise DetectorFailed # no suitable loop # Found a suitable loop, let's patch the graph: assert iterblock not in loopbody assert loopnextblock in loopbody for stopblock1 in stopblocks: assert stopblock1 not in loopbody # at StopIteration, the new list is exactly of the same length as # the one we iterate over if it's not possible to skip the appendblock # in the body: exactlength = not self.reachable_within(loopnextblock, loopnextblock, avoid = appendblock, stay_within = loopbody) # - add a hint(vlist, iterable, {'maxlength'}) in the iterblock, # where we can compute the known maximum length link = iterblock.exits[0] vlist = self.contains_vlist(link.args) assert vlist for op in iterblock.operations: res = self.variable_families.find_rep(op.result) if res is viterfamily: break else: raise AssertionError("lost 'iter' operation") vlength = Variable('maxlength') vlist2 = Variable(vlist) chint = Constant({'maxlength': True}) iterblock.operations += [ SpaceOperation('hint', [vlist, op.args[0], chint], vlist2)] link.args = list(link.args) for i in range(len(link.args)): if link.args[i] is vlist: link.args[i] = vlist2 # - wherever the list exits the loop body, add a 'hint({fence})' from pypy.translator.unsimplify import insert_empty_block for block in loopbody: for link in block.exits: if link.target not in loopbody: vlist = self.contains_vlist(link.args) if vlist is None: continue # list not passed along this link anyway hints = {'fence': True} if (exactlength and block is loopnextblock and link.target in stopblocks): hints['exactlength'] = True chints = Constant(hints) newblock = insert_empty_block(None, link) index = link.args.index(vlist) vlist2 = newblock.inputargs[index] vlist3 = Variable(vlist2) newblock.inputargs[index] = vlist3 newblock.operations.append( SpaceOperation('hint', [vlist3, chints], vlist2))
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 run(self, vlist, vmeth, appendblock): # first check that the 'append' method object doesn't escape for op in appendblock.operations: if op.opname == 'simple_call' and op.args[0] is vmeth: pass elif vmeth in op.args: raise DetectorFailed # used in another operation for link in appendblock.exits: if vmeth in link.args: raise DetectorFailed # escapes to a next block self.vmeth = vmeth self.vlistfamily = self.variable_families.find_rep(vlist) newlistblock = self.newlist_v[self.vlistfamily] self.vlistcone = {newlistblock: True} self.escapes = { self.graph.returnblock: True, self.graph.exceptblock: True } # in which loop are we? for loopnextblock, iterblock, viterfamily in self.loops: # check that the vlist is alive across the loop head block, # which ensures that we have a whole loop where the vlist # doesn't change if not self.vlist_alive(loopnextblock): continue # no - unrelated loop # check that we cannot go from 'newlist' to 'append' without # going through the 'iter' of our loop (and the following 'next'). # This ensures that the lifetime of vlist is cleanly divided in # "before" and "after" the loop... if self.reachable(newlistblock, appendblock, avoid=iterblock): continue # ... with the possible exception of links from the loop # body jumping back to the loop prologue, between 'newlist' and # 'iter', which we must forbid too: if self.reachable(loopnextblock, iterblock, avoid=newlistblock): continue # there must not be a larger number of calls to 'append' than # the number of elements that 'next' returns, so we must ensure # that we cannot go from 'append' to 'append' again without # passing 'next'... if self.reachable(appendblock, appendblock, avoid=loopnextblock): continue # ... and when the iterator is exhausted, we should no longer # reach 'append' at all. stopblocks = [ link.target for link in loopnextblock.exits if link.exitcase is not None ] accepted = True for stopblock1 in stopblocks: if self.reachable(stopblock1, appendblock, avoid=newlistblock): accepted = False if not accepted: continue # now explicitly find the "loop body" blocks: they are the ones # from which we can reach 'append' without going through 'iter'. # (XXX inefficient) loopbody = {} for block in self.graph.iterblocks(): if (self.vlist_alive(block) and self.reachable(block, appendblock, iterblock)): loopbody[block] = True # if the 'append' is actually after a 'break' or on a path that # can only end up in a 'break', then it won't be recorded as part # of the loop body at all. This is a strange case where we have # basically proved that the list will be of length 1... too # uncommon to worry about, I suspect if appendblock not in loopbody: continue # This candidate loop is acceptable if the list is not escaping # too early, i.e. in the loop header or in the loop body. loopheader = list( self.enum_blocks_with_vlist_from(newlistblock, avoid=loopnextblock)) assert loopheader[0] is newlistblock escapes = False for block in loopheader + loopbody.keys(): assert self.vlist_alive(block) if self.vlist_escapes(block): escapes = True break if not escapes: break # accept this loop! else: raise DetectorFailed # no suitable loop # Found a suitable loop, let's patch the graph: assert iterblock not in loopbody assert loopnextblock in loopbody for stopblock1 in stopblocks: assert stopblock1 not in loopbody # at StopIteration, the new list is exactly of the same length as # the one we iterate over if it's not possible to skip the appendblock # in the body: exactlength = not self.reachable_within(loopnextblock, loopnextblock, avoid=appendblock, stay_within=loopbody) # - add a hint(vlist, iterable, {'maxlength'}) in the iterblock, # where we can compute the known maximum length link = iterblock.exits[0] vlist = self.contains_vlist(link.args) assert vlist for op in iterblock.operations: res = self.variable_families.find_rep(op.result) if res is viterfamily: break else: raise AssertionError("lost 'iter' operation") vlength = Variable('maxlength') vlist2 = Variable(vlist) chint = Constant({'maxlength': True}) iterblock.operations += [ SpaceOperation('hint', [vlist, op.args[0], chint], vlist2) ] link.args = list(link.args) for i in range(len(link.args)): if link.args[i] is vlist: link.args[i] = vlist2 # - wherever the list exits the loop body, add a 'hint({fence})' from pypy.translator.unsimplify import insert_empty_block for block in loopbody: for link in block.exits: if link.target not in loopbody: vlist = self.contains_vlist(link.args) if vlist is None: continue # list not passed along this link anyway hints = {'fence': True} if (exactlength and block is loopnextblock and link.target in stopblocks): hints['exactlength'] = True chints = Constant(hints) newblock = insert_empty_block(None, link) index = link.args.index(vlist) vlist2 = newblock.inputargs[index] vlist3 = Variable(vlist2) newblock.inputargs[index] = vlist3 newblock.operations.append( SpaceOperation('hint', [vlist3, chints], vlist2))
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) 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 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)) )