def _emit_operation(self, op): assert not rop.is_call_pure(op.getopnum()) orig_op = op op = self.get_box_replacement(op) if op.is_constant(): return # can happen e.g. if we postpone the operation that becomes # constant # XXX kill, requires thinking #op = self.replace_op_with(op, op.opnum) for i in range(op.numargs()): arg = self.force_box(op.getarg(i)) op.setarg(i, arg) self.metainterp_sd.profiler.count(jitprof.Counters.OPT_OPS) if rop.is_guard(op.opnum): assert isinstance(op, GuardResOp) self.metainterp_sd.profiler.count(jitprof.Counters.OPT_GUARDS) pendingfields = self.pendingfields self.pendingfields = None if self.replaces_guard and orig_op in self.replaces_guard: self.replace_guard_op(self.replaces_guard[orig_op], op) del self.replaces_guard[orig_op] return else: op = self.emit_guard_operation(op, pendingfields) elif op.can_raise(): self.exception_might_have_happened = True opnum = op.opnum if ((rop.has_no_side_effect(opnum) or rop.is_guard(opnum) or rop.is_jit_debug(opnum) or rop.is_ovf(opnum)) and not self.is_call_pure_pure_canraise(op)): pass else: self._last_guard_op = None self._really_emitted_operation = op self._newoperations.append(op)
def next(self): opnum = self._next() if oparity[opnum] == -1: argnum = self._next() else: argnum = oparity[opnum] args = [] for i in range(argnum): args.append(self._untag(self._next())) descr_index = -1 if opwithdescr[opnum]: descr_index = self._next() if descr_index == 0 or rop.is_guard(opnum): descr = None else: if descr_index < self.all_descr_len + 1: descr = self.metainterp_sd.all_descrs[descr_index - 1] else: descr = self.trace._descrs[descr_index - self.all_descr_len - 1] else: descr = None res = ResOperation(opnum, args, descr=descr) if rop.is_guard(opnum): assert isinstance(res, GuardResOp) res.rd_resume_position = descr_index if res.type != 'v': self._cache[self._index] = res self._index += 1 self._count += 1 return res
def emit_op(self, op): op = self.get_box_replacement(op) orig_op = op replaced = False opnum = op.getopnum() keep = (opnum == rop.JIT_DEBUG) for i in range(op.numargs()): orig_arg = op.getarg(i) arg = self.get_box_replacement(orig_arg) if isinstance(arg, ConstPtr) and bool(arg.value) and not keep: arg = self.remove_constptr(arg) if orig_arg is not arg: if not replaced: op = op.copy_and_change(opnum) orig_op.set_forwarded(op) replaced = True op.setarg(i, arg) if rop.is_guard(opnum): if not replaced: op = op.copy_and_change(opnum) orig_op.set_forwarded(op) op.setfailargs( [self.get_box_replacement(a, True) for a in op.getfailargs()]) if rop.is_guard(opnum) or opnum == rop.FINISH: llref = cast_instance_to_gcref(op.getdescr()) self.gcrefs_output_list.append(llref) self._newops.append(op)
def emit_op(self, op): op = self.get_box_replacement(op) orig_op = op replaced = False opnum = op.getopnum() keep = (opnum == rop.JIT_DEBUG) for i in range(op.numargs()): orig_arg = op.getarg(i) arg = self.get_box_replacement(orig_arg) if isinstance(arg, ConstPtr) and bool(arg.value) and not keep: arg = self.remove_constptr(arg) if orig_arg is not arg: if not replaced: op = op.copy_and_change(opnum) orig_op.set_forwarded(op) replaced = True op.setarg(i, arg) if rop.is_guard(opnum): if not replaced: op = op.copy_and_change(opnum) orig_op.set_forwarded(op) op.setfailargs([self.get_box_replacement(a, True) for a in op.getfailargs()]) if rop.is_guard(opnum) or opnum == rop.FINISH: llref = cast_instance_to_gcref(op.getdescr()) self.gcrefs_output_list.append(llref) self._newops.append(op)
def user_loop_bail_fast_path(loop, warmstate): """ In a fast path over the trace loop: try to prevent vecopt of spending time on a loop that will most probably fail. """ resop_count = 0 # the count of operations minus debug_merge_points vector_instr = 0 guard_count = 0 at_least_one_array_access = True for i,op in enumerate(loop.operations): if rop.is_jit_debug(op.opnum): continue if op.vector >= 0 and not rop.is_guard(op.opnum): vector_instr += 1 resop_count += 1 if op.is_primitive_array_access(): at_least_one_array_access = True if rop.is_call(op.opnum) or rop.is_call_assembler(op.opnum): return True if rop.is_guard(op.opnum): guard_count += 1 if not at_least_one_array_access: return True return False
def _emit_operation(self, op): assert not rop.is_call_pure(op.getopnum()) orig_op = op op = self.get_box_replacement(op) if op.is_constant(): return # can happen e.g. if we postpone the operation that becomes # constant # XXX kill, requires thinking #op = self.replace_op_with(op, op.opnum) for i in range(op.numargs()): arg = self.force_box(op.getarg(i)) op.setarg(i, arg) self.metainterp_sd.profiler.count(jitprof.Counters.OPT_OPS) if rop.is_guard(op.opnum): assert isinstance(op, GuardResOp) self.metainterp_sd.profiler.count(jitprof.Counters.OPT_GUARDS) pendingfields = self.pendingfields self.pendingfields = None if self.replaces_guard and orig_op in self.replaces_guard: self.replace_guard_op(self.replaces_guard[orig_op], op) del self.replaces_guard[orig_op] return else: op = self.emit_guard_operation(op, pendingfields) opnum = op.opnum if ((rop.has_no_side_effect(opnum) or rop.is_guard(opnum) or rop.is_jit_debug(opnum) or rop.is_ovf(opnum)) and not self.is_call_pure_pure_canraise(op)): pass else: self._last_guard_op = None self._really_emitted_operation = op self._newoperations.append(op) self._emittedoperations[op] = None
def build_dependencies(self): """ This is basically building the definition-use chain and saving this information in a graph structure. This is the same as calculating the reaching definitions and the 'looking back' whenever it is used. Write After Read, Write After Write dependencies are not possible, the operations are in SSA form """ tracker = DefTracker(self) # label_pos = 0 jump_pos = len(self.nodes)-1 intformod = IntegralForwardModification(self.memory_refs, self.index_vars, self.comparison_vars, self.invariant_vars) # pass 1 for i,node in enumerate(self.nodes): op = node.op if rop.is_always_pure(op.opnum): node.setpriority(1) if rop.is_guard(op.opnum): node.setpriority(2) # the label operation defines all operations at the # beginning of the loop intformod.inspect_operation(op,node) # definition of a new variable if op.type != 'v': # In SSA form. Modifications get a new variable tracker.define(op, node) # usage of defined variables if rop.is_always_pure(op.opnum) or rop.is_final(op.opnum): # normal case every arguments definition is set for arg in op.getarglist(): tracker.depends_on_arg(arg, node) elif rop.is_guard(op.opnum): if node.exits_early(): pass else: # consider cross iterations? if len(self.guards) > 0: last_guard = self.guards[-1] last_guard.edge_to(node, failarg=True, label="guardorder") for nonpure in tracker.non_pure: nonpure.edge_to(node, failarg=True, label="nonpure") tracker.non_pure = [] self.guards.append(node) self.build_guard_dependencies(node, tracker) else: self.build_non_pure_dependencies(node, tracker)
def is_always_pure(self, exclude_first=False, exclude_last=False): last = len(self.path)-1 count = len(self.path) i = 0 if exclude_first: i += 1 if exclude_last: count -= 1 while i < count: node = self.path[i] if node.is_imaginary(): i += 1 continue op = node.getoperation() if rop.is_guard(op.opnum): descr = op.getdescr() if not descr: return False assert isinstance(descr, AbstractFailDescr) if not descr.exits_early(): return False elif not rop.is_always_pure(op.opnum): return False i += 1 return True
def compute_vars_longevity(inputargs, operations): # compute a dictionary that maps variables to index in # operations that is a "last-time-seen" # returns a pair longevity/useful. Non-useful variables are ones that # never appear in the assembler or it does not matter if they appear on # stack or in registers. Main example is loop arguments that go # only to guard operations or to jump or to finish last_used = {} last_real_usage = {} for i in range(len(operations) - 1, -1, -1): op = operations[i] if op.type != "v": if op not in last_used and rop.has_no_side_effect(op.opnum): continue opnum = op.getopnum() for j in range(op.numargs()): arg = op.getarg(j) if isinstance(arg, Const): continue if arg not in last_used: last_used[arg] = i if opnum != rop.JUMP and opnum != rop.LABEL: if arg not in last_real_usage: last_real_usage[arg] = i if rop.is_guard(op.opnum): for arg in op.getfailargs(): if arg is None: # hole continue assert not isinstance(arg, Const) if arg not in last_used: last_used[arg] = i # longevity = {} for i, arg in enumerate(operations): if arg.type != "v" and arg in last_used: assert not isinstance(arg, Const) assert i < last_used[arg] longevity[arg] = (i, last_used[arg]) del last_used[arg] for arg in inputargs: assert not isinstance(arg, Const) if arg not in last_used: longevity[arg] = (-1, -1) else: longevity[arg] = (0, last_used[arg]) del last_used[arg] assert len(last_used) == 0 if not we_are_translated(): produced = {} for arg in inputargs: produced[arg] = None for op in operations: for arg in op.getarglist(): if not isinstance(arg, Const): assert arg in produced produced[op] = None return longevity, last_real_usage
def compute_vars_longevity(inputargs, operations): # compute a dictionary that maps variables to index in # operations that is a "last-time-seen" # returns a pair longevity/useful. Non-useful variables are ones that # never appear in the assembler or it does not matter if they appear on # stack or in registers. Main example is loop arguments that go # only to guard operations or to jump or to finish last_used = {} last_real_usage = {} for i in range(len(operations) - 1, -1, -1): op = operations[i] if op.type != 'v': if op not in last_used and rop.has_no_side_effect(op.opnum): continue opnum = op.getopnum() for j in range(op.numargs()): arg = op.getarg(j) if isinstance(arg, Const): continue if arg not in last_used: last_used[arg] = i if opnum != rop.JUMP and opnum != rop.LABEL: if arg not in last_real_usage: last_real_usage[arg] = i if rop.is_guard(op.opnum): for arg in op.getfailargs(): if arg is None: # hole continue assert not isinstance(arg, Const) if arg not in last_used: last_used[arg] = i # longevity = {} for i, arg in enumerate(operations): if arg.type != 'v' and arg in last_used: assert not isinstance(arg, Const) assert i < last_used[arg] longevity[arg] = (i, last_used[arg]) del last_used[arg] for arg in inputargs: assert not isinstance(arg, Const) if arg not in last_used: longevity[arg] = (-1, -1) else: longevity[arg] = (0, last_used[arg]) del last_used[arg] assert len(last_used) == 0 if not we_are_translated(): produced = {} for arg in inputargs: produced[arg] = None for op in operations: for arg in op.getarglist(): if not isinstance(arg, Const): assert arg in produced produced[op] = None return longevity, last_real_usage
def next(self): opnum = self._next() argnum = oparity[opnum] if argnum == -1: argnum = self._next() if not (0 <= oparity[opnum] <= 3): args = [] for i in range(argnum): args.append(self._untag(self._next())) res = ResOperation(opnum, args) else: cls = opclasses[opnum] res = cls() argnum = oparity[opnum] if argnum == 0: pass elif argnum == 1: res.setarg(0, self._untag(self._next())) elif argnum == 2: res.setarg(0, self._untag(self._next())) res.setarg(1, self._untag(self._next())) else: assert argnum == 3 res.setarg(0, self._untag(self._next())) res.setarg(1, self._untag(self._next())) res.setarg(2, self._untag(self._next())) descr_index = -1 if opwithdescr[opnum]: descr_index = self._next() if descr_index == 0 or rop.is_guard(opnum): descr = None else: if descr_index < self.all_descr_len + 1: descr = self.metainterp_sd.all_descrs[descr_index - 1] else: descr = self.trace._descrs[descr_index - self.all_descr_len - 1] res.setdescr(descr) if rop.is_guard(opnum): # all guards have descrs assert isinstance(res, GuardResOp) res.rd_resume_position = descr_index if res.type != 'v': self._cache[self._index] = res self._index += 1 self._count += 1 return res
def compute_vars_longevity(inputargs, operations): # compute a dictionary that maps variables to Lifetime information # if a variable is not in the dictionary, it's operation is dead because # it's side-effect-free and the result is unused longevity = {} for i in range(len(operations) - 1, -1, -1): op = operations[i] opnum = op.getopnum() if op not in longevity: if op.type != 'v' and rop.has_no_side_effect(opnum): # result not used, operation has no side-effect, it can be # removed continue longevity[op] = Lifetime(definition_pos=i, last_usage=i) else: longevity[op].definition_pos = i for j in range(op.numargs()): arg = op.getarg(j) if isinstance(arg, Const): continue if arg not in longevity: lifetime = longevity[arg] = Lifetime(last_usage=i) else: lifetime = longevity[arg] if opnum != rop.JUMP and opnum != rop.LABEL: if lifetime.real_usages is None: lifetime.real_usages = [] lifetime.real_usages.append(i) if rop.is_guard(op.opnum): for arg in op.getfailargs(): if arg is None: # hole continue assert not isinstance(arg, Const) if arg not in longevity: longevity[arg] = Lifetime(last_usage=i) # for arg in inputargs: assert not isinstance(arg, Const) if arg not in longevity: longevity[arg] = Lifetime(-1, -1) if not we_are_translated(): produced = {} for arg in inputargs: produced[arg] = None for op in operations: for arg in op.getarglist(): if not isinstance(arg, Const): assert arg in produced produced[op] = None for lifetime in longevity.itervalues(): if lifetime.real_usages is not None: lifetime.real_usages.reverse() if not we_are_translated(): lifetime._check_invariants() return LifetimeManager(longevity)
def next_element_update_live_range(self, index, liveranges): opnum = self._next() if oparity[opnum] == -1: argnum = self._next() else: argnum = oparity[opnum] for i in range(argnum): tagged = self._next() tag, v = untag(tagged) if tag == TAGBOX: liveranges[v] = index if opclasses[opnum].type != "v": liveranges[index] = index if opwithdescr[opnum]: descr_index = self._next() if rop.is_guard(opnum): update_liveranges(self.trace._snapshots[descr_index], index, liveranges) if opclasses[opnum].type != "v": return index + 1 return index
def next_element_update_live_range(self, index, liveranges): opnum = self._next() if oparity[opnum] == -1: argnum = self._next() else: argnum = oparity[opnum] for i in range(argnum): tagged = self._next() tag, v = untag(tagged) if tag == TAGBOX: liveranges[v] = index if opclasses[opnum].type != 'v': liveranges[index] = index if opwithdescr[opnum]: descr_index = self._next() if rop.is_guard(opnum): update_liveranges(self.trace._snapshots[descr_index], index, liveranges) if opclasses[opnum].type != 'v': return index + 1 return index
def emitting_operation(self, op): if rop.has_no_side_effect(op.opnum): return if rop.is_ovf(op.opnum): return if rop.is_guard(op.opnum): self.optimizer.pendingfields = (self.force_lazy_sets_for_guard()) return opnum = op.getopnum() if (opnum == rop.SETFIELD_GC or # handled specially opnum == rop.SETFIELD_RAW or # no effect on GC struct/array opnum == rop.SETARRAYITEM_GC or # handled specially opnum == rop.SETARRAYITEM_RAW or # no effect on GC struct opnum == rop.SETINTERIORFIELD_RAW or # no effect on GC struct opnum == rop.RAW_STORE or # no effect on GC struct opnum == rop.STRSETITEM or # no effect on GC struct/array opnum == rop.UNICODESETITEM or # no effect on GC struct/array opnum == rop.DEBUG_MERGE_POINT or # no effect whatsoever opnum == rop.JIT_DEBUG or # no effect whatsoever opnum == rop.ENTER_PORTAL_FRAME or # no effect whatsoever opnum == rop.LEAVE_PORTAL_FRAME or # no effect whatsoever opnum == rop.COPYSTRCONTENT or # no effect on GC struct/array opnum == rop.COPYUNICODECONTENT or # no effect on GC struct/array opnum == rop.CHECK_MEMORY_ERROR): # may only abort the whole loop return if rop.is_call(op.opnum): if rop.is_call_assembler(op.getopnum()): self._seen_guard_not_invalidated = False else: effectinfo = op.getdescr().get_extra_info() if effectinfo.check_can_invalidate(): self._seen_guard_not_invalidated = False if not effectinfo.has_random_effects(): self.force_from_effectinfo(effectinfo) return self.force_all_lazy_sets() self.clean_caches()
def convert_loop_to_trace(loop, metainterp_sd, skip_last=False): from rpython.jit.metainterp.opencoder import Trace from rpython.jit.metainterp.test.test_opencoder import FakeFrame from rpython.jit.metainterp import history, resume def get(a): if isinstance(a, history.Const): return a return mapping[a] class jitcode: index = 200 inputargs = [ pick_cls(inparg)(i) for i, inparg in enumerate(loop.inputargs) ] mapping = {} for one, two in zip(loop.inputargs, inputargs): mapping[one] = two trace = Trace(inputargs, metainterp_sd) ops = loop.operations if skip_last: ops = ops[:-1] for op in ops: newpos = trace.record_op(op.getopnum(), [get(arg) for arg in op.getarglist()], op.getdescr()) if rop.is_guard(op.getopnum()): failargs = [] if op.getfailargs(): failargs = [get(arg) for arg in op.getfailargs()] frame = FakeFrame(100, jitcode, failargs) resume.capture_resumedata([frame], None, [], trace) if op.type != 'v': newop = pick_cls(op)(newpos) mapping[op] = newop trace._mapping = mapping # for tests return trace
def emitting_operation(self, op): if rop.has_no_side_effect(op.opnum): return if rop.is_ovf(op.opnum): return if rop.is_guard(op.opnum): self.optimizer.pendingfields = ( self.force_lazy_sets_for_guard()) return opnum = op.getopnum() if (opnum == rop.SETFIELD_GC or # handled specially opnum == rop.SETFIELD_RAW or # no effect on GC struct/array opnum == rop.SETARRAYITEM_GC or # handled specially opnum == rop.SETARRAYITEM_RAW or # no effect on GC struct opnum == rop.SETINTERIORFIELD_RAW or # no effect on GC struct opnum == rop.RAW_STORE or # no effect on GC struct opnum == rop.STRSETITEM or # no effect on GC struct/array opnum == rop.UNICODESETITEM or # no effect on GC struct/array opnum == rop.DEBUG_MERGE_POINT or # no effect whatsoever opnum == rop.JIT_DEBUG or # no effect whatsoever opnum == rop.ENTER_PORTAL_FRAME or # no effect whatsoever opnum == rop.LEAVE_PORTAL_FRAME or # no effect whatsoever opnum == rop.COPYSTRCONTENT or # no effect on GC struct/array opnum == rop.COPYUNICODECONTENT or # no effect on GC struct/array opnum == rop.CHECK_MEMORY_ERROR): # may only abort the whole loop return if rop.is_call(op.opnum): if rop.is_call_assembler(op.getopnum()): self._seen_guard_not_invalidated = False else: effectinfo = op.getdescr().get_extra_info() if effectinfo.check_can_invalidate(): self._seen_guard_not_invalidated = False if not effectinfo.has_random_effects(): self.force_from_effectinfo(effectinfo) return self.force_all_lazy_sets() self.clean_caches()
def convert_loop_to_trace(loop, metainterp_sd, skip_last=False): from rpython.jit.metainterp.opencoder import Trace from rpython.jit.metainterp.test.test_opencoder import FakeFrame from rpython.jit.metainterp import history, resume def get(a): if isinstance(a, history.Const): return a return mapping[a] class jitcode: index = 200 inputargs = [pick_cls(inparg)(i) for i, inparg in enumerate(loop.inputargs)] mapping = {} for one, two in zip(loop.inputargs, inputargs): mapping[one] = two trace = Trace(inputargs, metainterp_sd) ops = loop.operations if skip_last: ops = ops[:-1] for op in ops: newpos = trace.record_op(op.getopnum(), [get(arg) for arg in op.getarglist()], op.getdescr()) if rop.is_guard(op.getopnum()): failargs = [] if op.getfailargs(): failargs = [get(arg) for arg in op.getfailargs()] frame = FakeFrame(100, jitcode, failargs) resume.capture_resumedata([frame], None, [], trace) if op.type != 'v': newop = pick_cls(op)(newpos) mapping[op] = newop trace._mapping = mapping # for tests return trace
def equaloplists(oplist1, oplist2, strict_fail_args=True, remap={}, text_right=None): # try to use the full width of the terminal to display the list # unfortunately, does not work with the default capture method of py.test # (which is fd), you you need to use either -s or --capture=sys, else you # get the standard 80 columns width totwidth = py.io.get_terminal_width() width = totwidth / 2 - 1 print ' Comparing lists '.center(totwidth, '-') text_right = text_right or 'expected' memo = {} print '%s| %s' % ('optimized'.center(width), text_right.center(width)) for op1, op2 in itertools.izip_longest(oplist1, oplist2, fillvalue=''): if op1: txt1 = op1.repr(memo) else: txt1 = '' if op2: txt2 = op2.repr(memo) else: txt2 = '' while txt1 or txt2: part1 = txt1[:width] part2 = txt2[:width] if part1 == part2: sep = '| ' else: sep = '<>' print '%s%s%s' % (part1.ljust(width), sep, part2) txt1 = txt1[width:] txt2 = txt2[width:] print '-' * totwidth for i_count, (op1, op2) in enumerate(zip(oplist1, oplist2)): assert op1.getopnum() == op2.getopnum() assert op1.numargs() == op2.numargs() for i in range(op1.numargs()): x = op1.getarg(i) y = op2.getarg(i) assert x.same_box(remap.get(y, y)) assert x.same_shape(remap.get(y, y)) if op2 in remap: assert op1.same_box(remap[op2]) else: if op1.type != 'v': remap[op2] = op1 if (op1.getopnum() not in [rop.JUMP, rop.LABEL, rop.FINISH] and not rop.is_guard(op1.getopnum())): assert op1.getdescr() == op2.getdescr() if op1.getfailargs() or op2.getfailargs(): assert len(op1.getfailargs()) == len(op2.getfailargs()) if strict_fail_args: for x, y in zip(op1.getfailargs(), op2.getfailargs()): if x is None: assert remap.get(y, y) is None else: assert x.same_box(remap.get(y, y)) assert x.same_shape(remap.get(y, y)) else: fail_args1 = set(op1.getfailargs()) fail_args2 = set([remap.get(y, y) for y in op2.getfailargs()]) for x in fail_args1: for y in fail_args2: if x.same_box(y): fail_args2.remove(y) break else: assert False assert len(oplist1) == len(oplist2) return True
def rewrite(self, operations, gcrefs_output_list): # we can only remember one malloc since the next malloc can possibly # collect; but we can try to collapse several known-size mallocs into # one, both for performance and to reduce the number of write # barriers. We do this on each "basic block" of operations, which in # this case means between CALLs or unknown-size mallocs. # self.gcrefs_output_list = gcrefs_output_list self.gcrefs_map = None self.gcrefs_recently_loaded = None operations = self.remove_bridge_exception(operations) self._changed_op = None for i in range(len(operations)): op = operations[i] assert op.get_forwarded() is None if op.getopnum() == rop.DEBUG_MERGE_POINT: continue if op is self._changed_op: op = self._changed_op_to # ---------- GC_LOAD/STORE transformations -------------- if self.transform_to_gc_load(op): continue # ---------- turn NEWxxx into CALL_MALLOC_xxx ---------- if rop.is_malloc(op.opnum): self.handle_malloc_operation(op) continue if (rop.is_guard(op.opnum) or self.could_merge_with_next_guard(op, i, operations)): self.emit_pending_zeros() elif rop.can_malloc(op.opnum): self.emitting_an_operation_that_can_collect() elif op.getopnum() == rop.LABEL: self.emit_label() # ---------- write barriers ---------- if self.gc_ll_descr.write_barrier_descr is not None: if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) self.handle_write_barrier_setfield(op) continue if op.getopnum() == rop.SETINTERIORFIELD_GC: self.handle_write_barrier_setinteriorfield(op) continue if op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) self.handle_write_barrier_setarrayitem(op) continue else: # this is dead code, but in case we have a gc that does # not have a write barrier and does not zero memory, we would # need to clal it if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) elif op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) # ---------- call assembler ----------- if OpHelpers.is_call_assembler(op.getopnum()): self.handle_call_assembler(op) continue if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH: self.emit_pending_zeros() # self.emit_op(op) return self._newops
def rewrite(self, operations, gcrefs_output_list): # we can only remember one malloc since the next malloc can possibly # collect; but we can try to collapse several known-size mallocs into # one, both for performance and to reduce the number of write # barriers. We do this on each "basic block" of operations, which in # this case means between CALLs or unknown-size mallocs. # self.gcrefs_output_list = gcrefs_output_list self.gcrefs_map = None self.gcrefs_recently_loaded = None operations = self.remove_bridge_exception(operations) self._changed_op = None for i in range(len(operations)): op = operations[i] if op.get_forwarded(): msg = '[rewrite] operations at %d has forwarded info %s\n' % ( i, op.repr({})) if we_are_translated(): llop.debug_print(lltype.Void, msg) raise NotImplementedError(msg) if op.getopnum() == rop.DEBUG_MERGE_POINT: continue if op is self._changed_op: op = self._changed_op_to # ---------- GC_LOAD/STORE transformations -------------- if self.transform_to_gc_load(op): continue # ---------- turn NEWxxx into CALL_MALLOC_xxx ---------- if rop.is_malloc(op.opnum): self.handle_malloc_operation(op) continue if (rop.is_guard(op.opnum) or self.could_merge_with_next_guard(op, i, operations)): self.emit_pending_zeros() elif rop.can_malloc(op.opnum): self.emitting_an_operation_that_can_collect() elif op.getopnum() == rop.LABEL: self.emit_label() # ---------- write barriers ---------- if self.gc_ll_descr.write_barrier_descr is not None: if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) self.handle_write_barrier_setfield(op) continue if op.getopnum() == rop.SETINTERIORFIELD_GC: self.handle_write_barrier_setinteriorfield(op) continue if op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) self.handle_write_barrier_setarrayitem(op) continue else: # this is dead code, but in case we have a gc that does # not have a write barrier and does not zero memory, we would # need to call it if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) elif op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) # ---------- call assembler ----------- if OpHelpers.is_call_assembler(op.getopnum()): self.handle_call_assembler(op) continue if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH: self.emit_pending_zeros() # self.emit_op(op) return self._newops