def optimize_INT_MOD(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) known_nonneg = (v1.getintbound().known_ge(IntBound(0, 0)) and v2.getintbound().known_ge(IntBound(0, 0))) if known_nonneg and v2.is_constant(): val = v2.box.getint() if (val & (val-1)) == 0: # nonneg % power-of-two ==> nonneg & (power-of-two - 1) arg1 = op.getarg(0) arg2 = ConstInt(val-1) op = op.copy_and_change(rop.INT_AND, args=[arg1, arg2]) self.emit_operation(op) if v2.is_constant(): val = v2.box.getint() r = self.getvalue(op.result) if val < 0: if val == -sys.maxint-1: return # give up val = -val if known_nonneg: r.getintbound().make_ge(IntBound(0, 0)) else: r.getintbound().make_gt(IntBound(-val, -val)) r.getintbound().make_lt(IntBound(val, val))
def make_constant(self, constbox): """Replace 'self.box' with a Const box.""" assert isinstance(constbox, ConstInt) self.box = constbox self.setlevel(LEVEL_CONSTANT) val = constbox.getint() self.intbound = IntBound(val, val)
def optimize_INT_MOD(self, op): b1 = self.getintbound(op.getarg(0)) b2 = self.getintbound(op.getarg(1)) known_nonneg = (b1.known_ge(IntBound(0, 0)) and b2.known_ge(IntBound(0, 0))) if known_nonneg and b2.is_constant(): val = b2.getint() if (val & (val - 1)) == 0: # nonneg % power-of-two ==> nonneg & (power-of-two - 1) arg1 = op.getarg(0) arg2 = ConstInt(val - 1) op = self.replace_op_with(op, rop.INT_AND, args=[arg1, arg2]) self.emit_operation(op) if b2.is_constant(): val = b2.getint() r = self.getintbound(op) if val < 0: if val == -sys.maxint - 1: return # give up val = -val if known_nonneg: r.make_ge(IntBound(0, 0)) else: r.make_gt(IntBound(-val, -val)) r.make_lt(IntBound(val, val))
def propagate_bounds_INT_IS_TRUE(self, op): r = self.getvalue(op.result) if r.is_constant(): if r.box.same_constant(CONST_1): v1 = self.getvalue(op.getarg(0)) if v1.getintbound().known_ge(IntBound(0, 0)): v1.getintbound().make_gt(IntBound(0, 0)) self.propagate_bounds_backward(op.getarg(0))
def is_nonnull(self): if OptValue.is_nonnull(self): return True if self.intbound: if self.intbound.known_gt(IntBound(0, 0)) or \ self.intbound.known_lt(IntBound(0, 0)): return True return False
def postprocess_INT_OR_or_XOR(self, op): v1 = self.get_box_replacement(op.getarg(0)) b1 = self.getintbound(v1) v2 = self.get_box_replacement(op.getarg(1)) b2 = self.getintbound(v2) if b1.known_ge(IntBound(0, 0)) and \ b2.known_ge(IntBound(0, 0)): r = self.getintbound(op) mostsignificant = b1.upper | b2.upper r.intersect(IntBound(0, next_pow2_m1(mostsignificant)))
def optimize_INT_SIGNEXT(self, op): b = self.getintbound(op.getarg(0)) numbits = op.getarg(1).getint() * 8 start = -(1 << (numbits - 1)) stop = 1 << (numbits - 1) bounds = IntBound(start, stop - 1) if bounds.contains_bound(b): self.make_equal_to(op, op.getarg(0)) else: return self.emit(op)
def propagate_bounds_INT_IS_ZERO(self, op): r = self.getvalue(op.result) if r.is_constant(): if r.box.same_constant(CONST_1): v1 = self.getvalue(op.getarg(0)) # Clever hack, we can't use self.make_constant_int yet because # the args aren't in the values dictionary yet so it runs into # an assert, this is a clever way of expressing the same thing. v1.getintbound().make_ge(IntBound(0, 0)) v1.getintbound().make_lt(IntBound(1, 1)) self.propagate_bounds_backward(op.getarg(0))
def post_call_INT_PY_MOD(self, op): b2 = self.getintbound(op.getarg(2)) if b2.is_constant(): val = b2.getint() r = self.getintbound(op) if val >= 0: # with Python's modulo: 0 <= (x % pos) < pos r.make_ge(IntBound(0, 0)) r.make_lt(IntBound(val, val)) else: # with Python's modulo: neg < (x % neg) <= 0 r.make_gt(IntBound(val, val)) r.make_le(IntBound(0, 0))
def __init__(self, box, level=None, known_class=None, intbound=None): OptValue.__init__(self, box, level, None, None) if isinstance(box, Const): value = box.getint() self.intbound = IntBound(value, value) return if intbound: self.intbound = intbound else: assert isinstance(box, BoxInt) self.intbound = IntBound(MININT, MAXINT)
def __init__(self, box, level=None, known_class=None, intbound=None): OptValue.__init__(self, box, level, None, None) if isinstance(box, Const): return if intbound: self.intbound = intbound else: if isinstance(box, BoxInt): self.intbound = IntBound(MININT, MAXINT) else: self.intbound = IntUnbounded()
def optimize_INT_SIGNEXT(self, op): value = self.getvalue(op.getarg(0)) numbits = op.getarg(1).getint() * 8 start = -(1 << (numbits - 1)) stop = 1 << (numbits - 1) bounds = IntBound(start, stop - 1) if bounds.contains_bound(value.getintbound()): self.make_equal_to(op.result, value) else: self.emit_operation(op) vres = self.getvalue(op.result) vres.getintbound().intersect(bounds)
def optimize_INT_SIGNEXT(self, op): b = self.getintbound(op.getarg(0)) numbits = op.getarg(1).getint() * 8 start = -(1 << (numbits - 1)) stop = 1 << (numbits - 1) bounds = IntBound(start, stop - 1) if bounds.contains_bound(b): self.make_equal_to(op, op.getarg(0)) else: self.emit_operation(op) bres = self.getintbound(op) bres.intersect(bounds)
def postprocess_INT_AND(self, op): b1 = self.getintbound(op.getarg(0)) b2 = self.getintbound(op.getarg(1)) r = self.getintbound(op) pos1 = b1.known_ge(IntBound(0, 0)) pos2 = b2.known_ge(IntBound(0, 0)) if pos1 or pos2: r.make_ge(IntBound(0, 0)) if pos1: r.make_le(b1) if pos2: r.make_le(b2)
def test_shift_overflow(): b10 = IntBound(0, 10) b100 = IntBound(0, 100) bmax = IntBound(0, sys.maxint/2) assert not b10.lshift_bound(b100).has_upper assert not bmax.lshift_bound(b10).has_upper assert b10.lshift_bound(b10).has_upper for b in (b10, b100, bmax, IntBound(0, 0)): for shift_count_bound in (IntBound(7, LONG_BIT), IntBound(-7, 7)): #assert not b.lshift_bound(shift_count_bound).has_upper assert not b.rshift_bound(shift_count_bound).has_upper
def _propagate_int_is_true_or_zero(self, op, constnonzero, constzero): r = self.getvalue(op.result) if r.is_constant(): if r.box.same_constant(constnonzero): v1 = self.getvalue(op.getarg(0)) if v1.getintbound().known_ge(IntBound(0, 0)): v1.getintbound().make_gt(IntBound(0, 0)) self.propagate_bounds_backward(op.getarg(0)) elif r.box.same_constant(constzero): v1 = self.getvalue(op.getarg(0)) # Clever hack, we can't use self.make_constant_int yet because # the args aren't in the values dictionary yet so it runs into # an assert, this is a clever way of expressing the same thing. v1.getintbound().make_ge(IntBound(0, 0)) v1.getintbound().make_lt(IntBound(1, 1)) self.propagate_bounds_backward(op.getarg(0))
def postprocess_INT_SIGNEXT(self, op): numbits = op.getarg(1).getint() * 8 start = -(1 << (numbits - 1)) stop = 1 << (numbits - 1) bounds = IntBound(start, stop - 1) bres = self.getintbound(op) bres.intersect(bounds)
def test_intbounds(self): value1 = OptValue(BoxInt()) value1.intbound.make_ge(IntBound(0, 10)) value1.intbound.make_le(IntBound(20, 30)) info1 = NotVirtualStateInfo(value1) info2 = NotVirtualStateInfo(OptValue(BoxInt())) expected = """ [i0] i1 = int_ge(i0, 0) guard_true(i1) [] i2 = int_le(i0, 30) guard_true(i2) [] """ self.guards(info1, info2, BoxInt(15), expected) py.test.raises(InvalidLoop, self.guards, info1, info2, BoxInt(50), expected)
def optimize_INT_OR_or_XOR(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) if v1 is v2: if op.getopnum() == rop.INT_OR: self.make_equal_to(op.result, v1) else: self.make_constant_int(op.result, 0) return self.emit_operation(op) bound1 = v1.getintbound() bound2 = v2.getintbound() if bound1.known_ge(IntBound(0, 0)) and \ bound2.known_ge(IntBound(0, 0)): r = self.getvalue(op.result).getintbound() mostsignificant = bound1.upper | bound2.upper r.intersect(IntBound(0, next_pow2_m1(mostsignificant)))
def optimize_INT_AND(self, op): b1 = self.getintbound(op.getarg(0)) b2 = self.getintbound(op.getarg(1)) self.emit_operation(op) r = self.getintbound(op) if b2.is_constant(): val = b2.lower if val >= 0: r.intersect(IntBound(0, val)) elif b1.is_constant(): val = b1.lower if val >= 0: r.intersect(IntBound(0, val)) elif b1.known_ge(IntBound(0, 0)) and b2.known_ge(IntBound(0, 0)): lesser = min(b1.upper, b2.upper) r.intersect(IntBound(0, next_pow2_m1(lesser)))
def optimize_INT_OR_or_XOR(self, op): v1 = self.get_box_replacement(op.getarg(0)) b1 = self.getintbound(v1) v2 = self.get_box_replacement(op.getarg(1)) b2 = self.getintbound(v2) if v1 is v2: if op.getopnum() == rop.INT_OR: self.make_equal_to(op, v1) else: self.make_constant_int(op, 0) return self.emit_operation(op) if b1.known_ge(IntBound(0, 0)) and \ b2.known_ge(IntBound(0, 0)): r = self.getintbound(op) mostsignificant = b1.upper | b2.upper r.intersect(IntBound(0, next_pow2_m1(mostsignificant)))
def bound(a, b): if a is None and b is None: return IntUnbounded() elif a is None: return IntUpperBound(b) elif b is None: return IntLowerBound(a) else: return IntBound(a, b)
def optimize_INT_AND(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) self.emit_operation(op) r = self.getvalue(op.result) if v2.is_constant(): val = v2.box.getint() if val >= 0: r.getintbound().intersect(IntBound(0, val)) elif v1.is_constant(): val = v1.box.getint() if val >= 0: r.getintbound().intersect(IntBound(0, val)) elif v1.getintbound().known_ge(IntBound(0, 0)) and \ v2.getintbound().known_ge(IntBound(0, 0)): lesser = min(v1.getintbound().upper, v2.getintbound().upper) r.getintbound().intersect(IntBound(0, next_pow2_m1(lesser)))
def _propagate_int_is_true_or_zero(self, op, valnonzero, valzero): if self.is_raw_ptr(op.getarg(0)): return r = self.getintbound(op) if r.is_constant(): if r.getint() == valnonzero: b1 = self.getintbound(op.getarg(0)) if b1.known_ge(IntBound(0, 0)): b1.make_gt(IntBound(0, 0)) self.propagate_bounds_backward(op.getarg(0)) elif r.getint() == valzero: b1 = self.getintbound(op.getarg(0)) # XXX remove this hack maybe? # Clever hack, we can't use self.make_constant_int yet because # the args aren't in the values dictionary yet so it runs into # an assert, this is a clever way of expressing the same thing. b1.make_ge(IntBound(0, 0)) b1.make_lt(IntBound(1, 1)) self.propagate_bounds_backward(op.getarg(0))
def make_len_gt(self, mode, descr, val): if self.lenbound: if self.lenbound.mode != mode or self.lenbound.descr != descr: # XXX a rare case? it seems to occur sometimes when # running lib-python's test_io.py in PyPy on Linux 32... from rpython.jit.metainterp.optimize import InvalidLoop raise InvalidLoop("bad mode/descr") self.lenbound.bound.make_gt(IntBound(val, val)) else: self.lenbound = LenBound(mode, descr, IntLowerBound(val + 1))
def _optimize_CALL_DICT_LOOKUP(self, op): # Cache consecutive lookup() calls on the same dict and key, # depending on the 'flag_store' argument passed: # FLAG_LOOKUP: always cache and use the cached result. # FLAG_STORE: don't cache (it might return -1, which would be # incorrect for future lookups); but if found in # the cache and the cached value was already checked # non-negative, then we can reuse it. # FLAG_DELETE: never cache, never use the cached result (because # if there is a cached result, the FLAG_DELETE call # is needed for its side-effect of removing it). # In theory we could cache a -1 for the case where # the delete is immediately followed by a lookup, # but too obscure. # from rpython.rtyper.lltypesystem.rordereddict import FLAG_LOOKUP from rpython.rtyper.lltypesystem.rordereddict import FLAG_STORE flag_value = self.getintbound(op.getarg(4)) if not flag_value.is_constant(): return False flag = flag_value.getint() if flag != FLAG_LOOKUP and flag != FLAG_STORE: return False # descrs = op.getdescr().get_extra_info().extradescrs assert descrs # translation hint descr1 = descrs[0] try: d = self.cached_dict_reads[descr1] except KeyError: d = self.cached_dict_reads[descr1] = args_dict() self.corresponding_array_descrs[descrs[1]] = descr1 # key = [ get_box_replacement(op.getarg(1)), # dict get_box_replacement(op.getarg(2)) ] # key # other args can be ignored here (hash, store_flag) try: res_v = d[key] except KeyError: if flag == FLAG_LOOKUP: d[key] = op return False else: if flag != FLAG_LOOKUP: if not self.getintbound(res_v).known_ge(IntBound(0, 0)): return False self.make_equal_to(op, res_v) self.last_emitted_operation = REMOVED return True
def getintbound(self, op): assert op.type == 'i' op = get_box_replacement(op) if isinstance(op, ConstInt): return ConstIntBound(op.getint()) fw = op.get_forwarded() if fw is not None: if isinstance(fw, IntBound): return fw # rare case: fw might be a RawBufferPtrInfo return IntUnbounded() assert op.type == 'i' intbound = IntBound(MININT, MAXINT) op.set_forwarded(intbound) return intbound
def optimize_INT_FLOORDIV(self, op): v1 = self.getvalue(op.getarg(0)) v2 = self.getvalue(op.getarg(1)) if v2.is_constant() and v2.box.getint() == 1: self.make_equal_to(op.result, v1) return elif v1.is_constant() and v1.box.getint() == 0: self.make_constant_int(op.result, 0) return if v1.getintbound().known_ge(IntBound(0, 0)) and v2.is_constant(): val = v2.box.getint() if val & (val - 1) == 0 and val > 0: # val == 2**shift op = op.copy_and_change( rop.INT_RSHIFT, args=[op.getarg(0), ConstInt(highest_bit(val))]) self.emit_operation(op)
def _optimize_CALL_INT_PY_MOD(self, op): arg1 = op.getarg(1) b1 = self.getintbound(arg1) arg2 = op.getarg(2) b2 = self.getintbound(arg2) if b1.is_constant() and b1.getint() == 0: self.make_constant_int(op, 0) self.last_emitted_operation = REMOVED return True # This is Python's integer division: 'x // (2**shift)' can always # be replaced with 'x >> shift', even for negative values of x if not b2.is_constant(): return False val = b2.getint() if val <= 0: return False if val == 1: self.make_constant_int(op, 0) self.last_emitted_operation = REMOVED return True elif val & (val - 1) == 0: # val == 2**shift from rpython.jit.metainterp.history import DONT_CHANGE # x % power-of-two ==> x & (power-of-two - 1) # with Python's modulo, this is valid even if 'x' is negative. op = self.replace_op_with( op, rop.INT_AND, args=[arg1, ConstInt(val - 1)], descr=DONT_CHANGE) # <- xxx rename? means "kill" self.optimizer.send_extra_operation(op) return True else: from rpython.jit.metainterp.optimizeopt import intdiv known_nonneg = b1.known_ge(IntBound(0, 0)) operations = intdiv.modulo_operations(arg1, val, known_nonneg) newop = None for newop in operations: self.optimizer.send_extra_operation(newop) self.make_equal_to(op, newop) return True
def optimize_INT_FLOORDIV(self, op): arg0 = op.getarg(0) b1 = self.getintbound(arg0) arg1 = op.getarg(1) b2 = self.getintbound(arg1) if b2.is_constant() and b2.getint() == 1: self.make_equal_to(op, arg0) return elif b1.is_constant() and b1.getint() == 0: self.make_constant_int(op, 0) return if b1.known_ge(IntBound(0, 0)) and b2.is_constant(): val = b2.getint() if val & (val - 1) == 0 and val > 0: # val == 2**shift op = self.replace_op_with( op, rop.INT_RSHIFT, args=[op.getarg(0), ConstInt(highest_bit(val))]) self.emit_operation(op)
def test_NotVirtualStateInfo_generalization(self): def isgeneral(value1, value2): info1 = NotVirtualStateInfo(value1) info1.position = 0 info2 = NotVirtualStateInfo(value2) info2.position = 0 return info1.generalization_of(info2, {}, {}) assert isgeneral(OptValue(BoxInt()), OptValue(ConstInt(7))) assert not isgeneral(OptValue(ConstInt(7)), OptValue(BoxInt())) ptr = OptValue(BoxPtr()) nonnull = OptValue(BoxPtr()) nonnull.make_nonnull(0) knownclass = OptValue(BoxPtr()) knownclass.make_constant_class(ConstPtr(self.someptr1), 0) const = OptValue(BoxPtr) const.make_constant_class(ConstPtr(self.someptr1), 0) const.make_constant(ConstPtr(self.someptr1)) inorder = [ptr, nonnull, knownclass, const] for i in range(len(inorder)): for j in range(i, len(inorder)): assert isgeneral(inorder[i], inorder[j]) if i != j: assert not isgeneral(inorder[j], inorder[i]) value1 = OptValue(BoxInt()) value2 = OptValue(BoxInt()) value2.intbound.make_lt(IntBound(10, 10)) assert isgeneral(value1, value2) assert not isgeneral(value2, value1) assert isgeneral(OptValue(ConstInt(7)), OptValue(ConstInt(7))) S = lltype.GcStruct('S') foo = lltype.malloc(S) fooref = lltype.cast_opaque_ptr(llmemory.GCREF, foo) assert isgeneral(OptValue(ConstPtr(fooref)), OptValue(ConstPtr(fooref)))
class IntOptValue(OptValue): _attrs_ = ('intbound',) intbound = ImmutableIntUnbounded() def __init__(self, box, level=None, known_class=None, intbound=None): OptValue.__init__(self, box, level, None, None) if isinstance(box, Const): return if intbound: self.intbound = intbound else: if isinstance(box, BoxInt): self.intbound = IntBound(MININT, MAXINT) else: self.intbound = IntUnbounded() def copy_from(self, other_value): assert isinstance(other_value, IntOptValue) self.box = other_value.box self.intbound = other_value.intbound self._tag = other_value._tag def make_constant(self, constbox): """Replace 'self.box' with a Const box.""" assert isinstance(constbox, ConstInt) self.box = constbox self.setlevel(LEVEL_CONSTANT) val = constbox.getint() self.intbound = IntBound(val, val) def is_nonnull(self): if OptValue.is_nonnull(self): return True if self.intbound: if self.intbound.known_gt(IntBound(0, 0)) or \ self.intbound.known_lt(IntBound(0, 0)): return True return False def make_nonnull(self, optimizer): assert self.getlevel() < LEVEL_NONNULL self.setlevel(LEVEL_NONNULL) def import_from(self, other, optimizer): OptValue.import_from(self, other, optimizer) if self.getlevel() != LEVEL_CONSTANT: if other.getintbound() is not None: # VRawBufferValue self.intbound.intersect(other.getintbound()) def make_guards(self, box): guards = [] level = self.getlevel() if level == LEVEL_CONSTANT: op = ResOperation(rop.GUARD_VALUE, [box, self.box], None) guards.append(op) elif level == LEVEL_KNOWNCLASS: op = ResOperation(rop.GUARD_NONNULL, [box], None) guards.append(op) else: if level == LEVEL_NONNULL: op = ResOperation(rop.GUARD_NONNULL, [box], None) guards.append(op) self.intbound.make_guards(box, guards) return guards def getintbound(self): return self.intbound def get_last_guard(self, optimizer): return None def get_known_class(self): return None def getlenbound(self): return None