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 produce_potential_short_preamble_ops(self, sb): ops = self.optimizer._newoperations for i, op in enumerate(ops): if rop.is_always_pure(op.opnum): sb.add_pure_op(op) if rop.is_ovf( op.opnum) and ops[i + 1].getopnum() == rop.GUARD_NO_OVERFLOW: sb.add_pure_op(op) for i in self.call_pure_positions: op = ops[i] # don't move call_pure_with_exception in the short preamble... # issue #2015 # Also, don't move cond_call_value in the short preamble. # The issue there is that it's usually pointless to try to # because the 'value' argument is typically not a loop # invariant, and would really need to be in order to end up # in the short preamble. Maybe the code works anyway in the # other rare case, but better safe than sorry and don't try. effectinfo = op.getdescr().get_extra_info() if not effectinfo.check_can_raise(ignore_memoryerror=True): assert rop.is_call(op.opnum) if not OpHelpers.is_cond_call_value(op.opnum): sb.add_pure_op(op)
def produce_potential_short_preamble_ops(self, sb): ops = self.optimizer._newoperations for i, op in enumerate(ops): if rop.is_always_pure(op.opnum): sb.add_pure_op(op) if rop.is_ovf( op.opnum) and ops[i + 1].getopnum() == rop.GUARD_NO_OVERFLOW: sb.add_pure_op(op) for i in self.call_pure_positions: op = ops[i] # don't move call_pure_with_exception in the short preamble... # issue #2015 effectinfo = op.getdescr().get_extra_info() if not effectinfo.check_can_raise(ignore_memoryerror=True): assert rop.is_call(op.opnum) sb.add_pure_op(op)
def optimize_default(self, op): canfold = rop.is_always_pure(op.opnum) if rop.is_ovf(op.opnum): self.postponed_op = op return if self.postponed_op: nextop = op op = self.postponed_op self.postponed_op = None canfold = nextop.getopnum() == rop.GUARD_NO_OVERFLOW else: nextop = None save = False if canfold: for i in range(op.numargs()): if self.get_constant_box(op.getarg(i)) is None: break else: # all constant arguments: constant-fold away resbox = self.optimizer.constant_fold(op) # note that INT_xxx_OVF is not done from here, and the # overflows in the INT_xxx operations are ignored self.optimizer.make_constant(op, resbox) return # did we do the exact same operation already? recentops = self.getrecentops(op.getopnum()) save = True oldop = recentops.lookup(self.optimizer, op) if oldop is not None: self.optimizer.make_equal_to(op, oldop) return # otherwise, the operation remains self.emit_operation(op) if rop.returns_bool_result(op.opnum): self.getintbound(op).make_bool() if save: recentops = self.getrecentops(op.getopnum()) recentops.add(op) if nextop: self.emit_operation(nextop)
def produce_potential_short_preamble_ops(self, sb): ops = self.optimizer._newoperations for i, op in enumerate(ops): if rop.is_always_pure(op.opnum): sb.add_pure_op(op) if rop.is_ovf(op.opnum) and ops[i + 1].getopnum() == rop.GUARD_NO_OVERFLOW: sb.add_pure_op(op) for i in self.call_pure_positions: op = ops[i] # don't move call_pure_with_exception in the short preamble... # issue #2015 # Also, don't move cond_call_value in the short preamble. # The issue there is that it's usually pointless to try to # because the 'value' argument is typically not a loop # invariant, and would really need to be in order to end up # in the short preamble. Maybe the code works anyway in the # other rare case, but better safe than sorry and don't try. effectinfo = op.getdescr().get_extra_info() if not effectinfo.check_can_raise(ignore_memoryerror=True): assert rop.is_call(op.opnum) if not OpHelpers.is_cond_call_value(op.opnum): sb.add_pure_op(op)
def optimize_default(self, op): canfold = rop.is_always_pure(op.opnum) if rop.is_ovf(op.opnum): self.postponed_op = op return if self.postponed_op: nextop = op op = self.postponed_op self.postponed_op = None canfold = nextop.getopnum() == rop.GUARD_NO_OVERFLOW else: nextop = None save = False if canfold: for i in range(op.numargs()): if self.get_constant_box(op.getarg(i)) is None: break else: # all constant arguments: constant-fold away resbox = self.optimizer.constant_fold(op) # note that INT_xxx_OVF is not done from here, and the # overflows in the INT_xxx operations are ignored self.optimizer.make_constant(op, resbox) return # did we do the exact same operation already? recentops = self.getrecentops(op.getopnum()) save = True oldop = recentops.lookup(self.optimizer, op) if oldop is not None: self.optimizer.make_equal_to(op, oldop) return # otherwise, the operation remains return self.emit_result(DefaultOptimizationResult(self, op, save, nextop))
def is_pure(self): return rop.is_always_pure(self.op.getopnum())