Commits

Lukas Diekmann committed b15e618 Merge

(cfbolz, l.diekmann): merge int-tag-untag-as-operationus to get the necessary
optimizations to make this branch useful

  • Participants
  • Parent commits 7636a49, 9cf4919
  • Branches type-specialized-instances

Comments (0)

Files changed (30)

pypy/jit/backend/llgraph/llimpl.py

     'int_is_true'     : (('int',), 'bool'),
     'int_is_zero'     : (('int',), 'bool'),
     'int_neg'         : (('int',), 'int'),
+    'int_tag'         : (('int', ), 'int'),
+    'int_untag'       : (('int', ), 'int'),
     'int_invert'      : (('int',), 'int'),
     'int_add_ovf'     : (('int', 'int'), 'int'),
     'int_sub_ovf'     : (('int', 'int'), 'int'),
     'int_mul_ovf'     : (('int', 'int'), 'int'),
+    'int_tag_ovf'     : (('int', ), 'int'),
     'uint_add'        : (('int', 'int'), 'int'),
     'uint_sub'        : (('int', 'int'), 'int'),
     'uint_mul'        : (('int', 'int'), 'int'),
         if not flag:
             raise GuardFailed
 
+    def op_int_tag_ovf(self, _, x):
+        try:
+            z = ovfcheck(x << 1) + 1
+        except OverflowError:
+            ovf = True
+            z = 0
+        else:
+            ovf = False
+        self.overflow_flag = ovf
+        return z
+
     def op_int_add_ovf(self, _, x, y):
         try:
             z = ovfcheck(x + y)

pypy/jit/backend/test/test_random.py

 class BinaryOvfOperation(AbstractOvfOperation, BinaryOperation):
     pass
 
+class UnaryOvfOperation(AbstractOvfOperation, UnaryOperation):
+    pass
+
 class AbstractFloatOperation(AbstractOperation):
     def filter(self, builder):
         if not builder.cpu.supports_floats:
 
 for _op in [rop.INT_NEG,
             rop.INT_INVERT,
+            rop.INT_TAG,
+            rop.INT_UNTAG,
             ]:
     OPERATIONS.append(UnaryOperation(_op))
 
             rop.INT_MUL_OVF,
             ]:
     OPERATIONS.append(BinaryOvfOperation(_op))
+OPERATIONS.append(UnaryOvfOperation(rop.INT_TAG_OVF))
 
 for _op in [rop.FLOAT_ADD,
             rop.FLOAT_SUB,

pypy/jit/backend/x86/assembler.py

     genop_guard_float_gt = _cmpop_guard_float("A", "B", "BE","AE")
     genop_guard_float_ge = _cmpop_guard_float("AE","BE", "B", "A")
 
+
+    def genop_int_tag(self, op, arglocs, resloc):
+        loc, = arglocs
+        assert isinstance(loc, RegLoc)
+        assert isinstance(resloc, RegLoc)
+        # res = loc + (loc << 0) + 1
+        self.mc.LEA_ra(resloc.value, (loc.value, loc.value, 0, 1))
+
+    def genop_int_untag(self, op, arglocs, resloc):
+        loc, = arglocs
+        self.mc.SAR(loc, imm1)
+
     def genop_math_sqrt(self, op, arglocs, resloc):
         self.mc.SQRTSD(arglocs[0], resloc)
 
         self.genop_int_mul(op, arglocs, result_loc)
         return self._gen_guard_overflow(guard_op, guard_token)
 
+    def genop_guard_int_tag_ovf(self, op, guard_op, guard_token, arglocs, result_loc):
+        self.mc.STC()
+        self.mc.ADC(arglocs[0], arglocs[0])
+        return self._gen_guard_overflow(guard_op, guard_token)
+
     def genop_guard_guard_false(self, ign_1, guard_op, guard_token, locs, ign_2):
         loc = locs[0]
         self.mc.TEST(loc, loc)

pypy/jit/backend/x86/regalloc.py

     consider_int_sub_ovf = _consider_binop_with_guard
     consider_int_add_ovf = _consider_binop_with_guard
 
+    def consider_int_tag_ovf(self, op, guard_op):
+        loc = self.rm.force_result_in_reg(op.result, op.getarg(0))
+        self.perform_with_guard(op, guard_op, [loc], loc)
+
+    def consider_int_tag(self, op):
+        loc = self.rm.make_sure_var_in_reg(op.getarg(0))
+        self.rm.possibly_free_vars_for_op(op)
+        res = self.rm.force_allocate_reg(op.result)
+        self.Perform(op, [loc], res)
+
     def consider_int_neg(self, op):
         res = self.rm.force_result_in_reg(op.result, op.getarg(0))
         self.Perform(op, [res], res)
 
     consider_int_invert = consider_int_neg
+    consider_int_untag = consider_int_neg
 
     def consider_int_lshift(self, op):
         if isinstance(op.getarg(1), Const):

pypy/jit/backend/x86/regloc.py

     BTS = _binaryop('BTS')
 
     ADD = _binaryop('ADD')
+    ADC = _binaryop('ADC')
     SUB = _binaryop('SUB')
     IMUL = _binaryop('IMUL')
     NEG = _unaryop('NEG')

pypy/jit/backend/x86/rx86.py

     # ------------------------------ Arithmetic ------------------------------
 
     ADD_ri,ADD_rr,ADD_rb,_,_,ADD_rm,ADD_rj,_,_ = common_modes(0)
+    ADC_ri,ADC_rr,ADC_rb,_,_,ADC_rm,ADC_rj,_,_ = common_modes(2)
     OR_ri, OR_rr, OR_rb, _,_,OR_rm, OR_rj, _,_ = common_modes(1)
     AND_ri,AND_rr,AND_rb,_,_,AND_rm,AND_rj,_,_ = common_modes(4)
     SUB_ri,SUB_rr,SUB_rb,_,_,SUB_rm,SUB_rj,SUB_ji8,SUB_mi8 = common_modes(5)
     FSTPL_b = insn('\xDD', orbyte(3<<3), stack_bp(1)) # rffi.DOUBLE ('as' wants L??)
     FSTPS_s = insn('\xD9', orbyte(3<<3), stack_sp(1)) # lltype.SingleFloat
 
+    STC = insn('\xF9')
+
     # ------------------------------ Random mess -----------------------
     RDTSC = insn('\x0F\x31')
 

pypy/jit/backend/x86/test/test_regalloc.py

         self.run(loop, 4, 7)
         assert self.getint(0) == 29
 
+    def test_int_untag(self):
+        ops = '''
+        [i0]
+        i1 = int_untag(i0)
+        finish(i1)
+        '''
+        self.interpret(ops, [1129])
+        assert self.getint(0) == 564
+        self.interpret(ops, [-1129])
+        assert self.getint(0) == -565
+
+    def test_int_tag(self):
+        ops = '''
+        [i0]
+        i1 = int_tag(i0)
+        i2 = int_tag(i0)
+        finish(i1, i2)
+        '''
+        self.interpret(ops, [1129])
+        assert self.getint(0) == 1129 * 2 + 1
+        assert self.getint(1) == 1129 * 2 + 1
+        self.interpret(ops, [-1129])
+        assert self.getint(0) == -1129 * 2 + 1
+        assert self.getint(1) == -1129 * 2 + 1

pypy/jit/codewriter/jtransform.py

         op1 = SpaceOperation('-live-', [], None)
         return [op, op1]
 
+    def rewrite_op_int_tag_ovf(self, op):
+        op1 = SpaceOperation('-live-', [], None)
+        return [op, op1]
+
     # ----------
     # Various kinds of calls
 

pypy/jit/codewriter/support.py

File contents unchanged.

pypy/jit/metainterp/blackhole.py

     def bhimpl_int_invert(a):
         return intmask(~a)
 
+    @arguments("i", returns="i")
+    def bhimpl_int_untag(a):
+        ll_assert((a & 1) == 1, "bhimpl_int_untag: not an odd int")
+        return a >> 1
+    @arguments("i", returns="i")
+    def bhimpl_int_tag(a):
+        return intmask(a << 1) + 1 # mostly there for test_random
+    @arguments("i", returns="i")
+    def bhimpl_int_tag_ovf(a):
+        return ovfcheck(a << 1) + 1
+
+
     @arguments("i", "i", returns="i")
     def bhimpl_int_lt(a, b):
         return a < b
     @arguments("r", returns="i")
     def bhimpl_cast_ptr_to_int(a):
         i = lltype.cast_ptr_to_int(a)
-        ll_assert((i & 1) == 1, "bhimpl_cast_ptr_to_int: not an odd int")
         return i
     @arguments("i", returns="r")
     def bhimpl_cast_int_to_ptr(i):

pypy/jit/metainterp/executor.py

         z = 0
     return BoxInt(z)
 
+def do_int_tag_ovf(cpu, metainterp, box1):
+    # the overflow operations can be called without a metainterp, if an
+    # overflow cannot occur
+    a = box1.getint()
+    try:
+        z = ovfcheck(a << 1)
+    except OverflowError:
+        assert metainterp is not None
+        metainterp.execute_raised(OverflowError(), constant=True)
+        z = 0
+    return BoxInt(z + 1)
+
 def do_same_as(cpu, _, box):
     return box.clonebox()
 

pypy/jit/metainterp/optimizeopt/intbounds.py

     def optimize_INT_AND(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
+        if (self.optimizer.metainterp_sd.config.translation.taggedpointers and
+                v2.is_constant() and v2.box.getint() == 1):
+            if self.has_pure_result(rop.INT_UNTAG, [v1.box], None):
+                # the result of untagging the int is known, so the box must be
+                # tagged, so int_and(x, 1) == 1
+                value = self.getvalue(ConstInt(1))
+                self.optimizer.make_equal_to(op.result, value)
+                return
         self.emit_operation(op)
 
         r = self.getvalue(op.result)
     def optimize_GUARD_NO_OVERFLOW(self, op):
         lastop = self.last_emitted_operation
         if lastop is not None:
+            # If the INT_xxx_OVF was replaced with INT_xxx, then we can kill
+            # the GUARD_NO_OVERFLOW.
+            if not lastop.is_ovf():
+                return
             opnum = lastop.getopnum()
             args = lastop.getarglist()
             result = lastop.result
-            # If the INT_xxx_OVF was replaced with INT_xxx, then we can kill
-            # the GUARD_NO_OVERFLOW.
-            if (opnum == rop.INT_ADD or
-                opnum == rop.INT_SUB or
-                opnum == rop.INT_MUL):
-                return
             # Else, synthesize the non overflowing op for optimize_default to
-            # reuse, as well as the reverse op
-            elif opnum == rop.INT_ADD_OVF:
+            # reuse, as well as the reverse ops
+            if opnum == rop.INT_ADD_OVF:
                 self.pure(rop.INT_ADD, args[:], result)
                 self.pure(rop.INT_SUB, [result, args[1]], args[0])
                 self.pure(rop.INT_SUB, [result, args[0]], args[1])
                 self.pure(rop.INT_SUB, [args[0], result], args[1])
             elif opnum == rop.INT_MUL_OVF:
                 self.pure(rop.INT_MUL, args[:], result)
+            elif opnum == rop.INT_TAG_OVF:
+                v1 = self.getvalue(lastop.getarg(0))
+                maxbounds = IntBound((-sys.maxint-1) >> 1, sys.maxint >> 1)
+                v1.intbound.intersect(maxbounds)
+                self.pure(rop.INT_UNTAG, [result], args[0])
         self.emit_operation(op)
 
     def optimize_GUARD_OVERFLOW(self, op):
         if lastop is None:
             raise InvalidLoop
         opnum = lastop.getopnum()
-        if opnum not in (rop.INT_ADD_OVF, rop.INT_SUB_OVF, rop.INT_MUL_OVF):
+        if not lastop.is_ovf():
             raise InvalidLoop
         self.emit_operation(op)
 
         else:
             self.emit_operation(op)
 
+    def optimize_INT_TAG_OVF(self, op):
+        v1 = self.getvalue(op.getarg(0))
+        resbound = v1.intbound.mul(2).add(1)
+        if resbound.bounded():
+            op = op.copy_and_change(rop.INT_TAG)
+            self.optimize_INT_TAG(op) # emit the op
+        else:
+            self.emit_operation(op)
+
+    def optimize_INT_TAG(self, op):
+        v1 = self.getvalue(op.getarg(0))
+        self.emit_operation(op)
+        r = self.getvalue(op.result)
+        resbound = v1.intbound.mul(2).add(1)
+        r.intbound.intersect(resbound)
+        maxbounds = IntBound((-sys.maxint-1) >> 1, sys.maxint >> 1)
+        v1.intbound.intersect(maxbounds)
+        self.pure(rop.INT_UNTAG, [op.result], op.getarg(0))
+
+    def optimize_INT_UNTAG(self, op):
+        v1 = self.getvalue(op.getarg(0))
+        self.pure(rop.INT_TAG, [op.result], op.getarg(0))
+        self.emit_operation(op)
+        r = self.getvalue(op.result)
+        r.intbound.intersect(v1.intbound.rshift_bound(IntBound(1, 1)))
+
     def optimize_ARRAYLEN_GC(self, op):
         self.emit_operation(op)
         array  = self.getvalue(op.getarg(0))

pypy/jit/metainterp/optimizeopt/intutils.py

         return res
 
     def mul(self, value):
-        return self.mul_bound(IntBound(value, value))
-    
+        upper = 0
+        has_upper = False
+        if self.has_upper:
+            try:
+                upper = ovfcheck(self.upper * value)
+            except OverflowError:
+                pass
+            else:
+                has_upper = True
+        lower = 0
+        has_lower = False
+        if self.has_lower:
+            try:
+                lower = ovfcheck(self.lower * value)
+            except OverflowError:
+                pass
+            else:
+                has_lower = True
+        if value < 0:
+            has_upper, has_lower = has_lower, has_upper
+            upper, lower = lower, upper
+        result = IntBound(lower, upper)
+        result.has_lower = has_lower
+        result.has_upper = has_upper
+        return result
+
     def add_bound(self, other):
         res = self.clone()
         if other.has_upper:

pypy/jit/metainterp/optimizeopt/test/test_intutils.py

+from pypy.jit.metainterp.optimizeopt import intutils
+
+# XXX this file should really be filled with tests for all operations!
+
+def test_mul_with_constant():
+    x = intutils.IntBound(0, 100)
+    y = x.mul(2)
+    assert y.has_lower
+    assert y.has_upper
+    assert y.lower == 0
+    assert y.upper == 200
+
+    y = x.mul(-5)
+    assert y.has_lower
+    assert y.has_upper
+    assert y.lower == -500
+    assert y.upper == 0
+
+    x = intutils.IntUpperBound(100)
+    y = x.mul(2)
+    assert not y.has_lower
+    assert y.has_upper
+    assert y.upper == 200
+
+    y = x.mul(-5)
+    assert y.has_lower
+    assert not y.has_upper
+    assert y.lower == -500
+    assert y.upper == 0

pypy/jit/metainterp/optimizeopt/test/test_optimizebasic.py

             args = []
             for argtype in argtypes:
                 assert argtype in ('int', 'bool')
-                args.append(random.randrange(1, 20))
+                arg = random.randrange(1, 20)
+                if opnum == rop.INT_UNTAG:
+                    arg = arg | 1 # must be an odd int
+                args.append(arg)
             assert restype in ('int', 'bool')
             ops = """
             []

pypy/jit/metainterp/optimizeopt/test/test_optimizeopt.py

             args = []
             for argtype in argtypes:
                 assert argtype in ('int', 'bool')
-                args.append(random.randrange(1, 20))
+                arg = random.randrange(1, 20)
+                if opnum == rop.INT_UNTAG:
+                    arg = arg | 1 # must be an odd int
+                args.append(arg)
             assert restype in ('int', 'bool')
             ops = """
             []
         """
         self.optimize_loop(ops, ops, ops)
 
+    def test_int_tag_untag_reverses(self):
+        ops = """
+        [i0]
+        i1 = int_tag_ovf(i0)
+        guard_no_overflow() []
+        i2 = int_untag(i1)
+        i3 = int_add(i2, 1)
+        jump(i3)
+        """
+        expected = """
+        [i0]
+        i1 = int_tag_ovf(i0)
+        guard_no_overflow() []
+        i2 = int_add(i0, 1)
+        jump(i2)
+        """
+        self.optimize_loop(ops, expected)
+        ops = """
+        [i0]
+        i1 = int_untag(i0)
+        i2 = int_tag_ovf(i1)
+        guard_no_overflow() []
+        i3 = int_untag(i2)
+        i4 = int_add(i3, 1)
+        jump(i4)
+        """
+        expected = """
+        [i0]
+        i1 = int_untag(i0)
+        i2 = int_add(i1, 1)
+        jump(i2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_int_tag_remove_overflow_checking(self):
+        ops = """
+        [i0]
+        i1 = int_lt(i0, 1000)
+        guard_true(i1), []
+        i2 = int_tag_ovf(i0)
+        guard_no_overflow() []
+        i3 = int_untag(i2)
+        i4 = int_add_ovf(i3, 1)
+        guard_no_overflow() []
+        jump(i4)
+        """
+        expected = """
+        [i0]
+        i1 = int_lt(i0, 1000)
+        guard_true(i1), []
+        i2 = int_tag(i0)
+        i3 = int_add(i0, 1)
+        jump(i3)
+        """
+        preamble = """
+        [i0]
+        i1 = int_lt(i0, 1000)
+        guard_true(i1), []
+        i2 = int_tag_ovf(i0)
+        guard_no_overflow() []
+        i3 = int_add(i0, 1)
+        jump(i3)
+        """
+        self.optimize_loop(ops, expected, preamble)
+
+    def test_int_tag_remove_overflow_checking2(self):
+        ops = """
+        [i0]
+        i1 = int_lt(i0, 1000)
+        guard_true(i1), []
+        i2 = int_gt(i0, 0)
+        guard_true(i2), []
+        i3 = int_tag_ovf(i0)
+        guard_no_overflow() []
+        i4 = escape(i3)
+        jump(i4)
+        """
+        expected = """
+        [i0]
+        i1 = int_lt(i0, 1000)
+        guard_true(i1), []
+        i2 = int_gt(i0, 0)
+        guard_true(i2), []
+        i3 = int_tag(i0)
+        i4 = escape(i3)
+        jump(i4)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_int_tag_removes_int_and(self):
+        ops = """
+        [i0]
+        i1 = int_tag_ovf(i0)
+        guard_no_overflow() []
+        i2 = int_and(i1, 1)
+        i3 = int_is_true(i2)
+        guard_true(i3) []
+        i4 = int_untag(i1)
+        i5 = int_add(i4, 1)
+        jump(i5)
+        """
+        expected = """
+        [i0]
+        i1 = int_tag_ovf(i0)
+        guard_no_overflow() []
+        i2 = int_add(i0, 1)
+        jump(i2)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_int_tag_constfold(self):
+        ops = """
+        [i0]
+        i1 = int_tag_ovf(1)
+        guard_no_overflow() []
+        i4 = int_untag(i1)
+        i5 = int_add(i4, 1)
+        escape(i5)
+        jump(i5)
+        """
+        expected = """
+        []
+        escape(2)
+        jump()
+        """
+        preamble = """
+        [i0]
+        escape(2)
+        jump()
+        """
+        self.optimize_loop(ops, expected, preamble)
 
     def test_mul_ovf(self):
         ops = """

pypy/jit/metainterp/optimizeopt/test/test_util.py

         self.globaldata = Fake()
         self.config = get_pypy_config(translating=True)
         self.config.translation.jit_ffi = True
+        self.config.translation.taggedpointers = True
 
     class logger_noopt:
         @classmethod

pypy/jit/metainterp/pyjitpl.py

                 return resbox
         ''' % (_opimpl, _opimpl.upper())).compile()
 
-    for _opimpl in ['int_is_true', 'int_is_zero', 'int_neg', 'int_invert',
+    for _opimpl in ['int_is_true', 'int_is_zero', 'int_neg', 'int_invert', 'int_untag',
                     'cast_float_to_int', 'cast_int_to_float',
                     'cast_float_to_singlefloat', 'cast_singlefloat_to_float',
                     'float_neg', 'float_abs',
         ''' % (_opimpl, _opimpl.upper())).compile()
 
     @arguments("box")
+    def opimpl_int_tag_ovf(self, b1):
+        self.metainterp.clear_exception()
+        resbox = self.execute(rop.INT_TAG_OVF, b1)
+        self.make_result_of_lastop(resbox)
+        if not isinstance(resbox, Const):
+            self.metainterp.handle_possible_overflow_error()
+        return resbox
+
+    @arguments("box")
     def opimpl_ptr_nonzero(self, box):
         return self.execute(rop.PTR_NE, box, history.CONST_NULL)
 

pypy/jit/metainterp/resoperation.py

     'INT_IS_TRUE/1b',
     'INT_NEG/1',
     'INT_INVERT/1',
+    'INT_UNTAG/1',
+    'INT_TAG/1',
     #
     'SAME_AS/1',      # gets a Const or a Box, turns it into another Box
     'CAST_PTR_TO_INT/1',
     'INT_ADD_OVF/2',
     'INT_SUB_OVF/2',
     'INT_MUL_OVF/2',
+    'INT_TAG_OVF/1',
     '_OVF_LAST', # ----- end of is_ovf operations -----
     '_LAST',     # for the backend to add more internal operations
 ]

pypy/jit/metainterp/test/support.py

File contents unchanged.

pypy/jit/metainterp/test/test_ajit.py

         translationoptions = {'withsmallfuncsets': 3}
         self.interp_operations(f, [5], translationoptions=translationoptions)
 
-
     def test_annotation_gives_class_knowledge_to_tracer(self):
         py.test.skip("disabled")
         class Base(object):
         self.check_operations_history(guard_class=0, record_known_class=1)
 
 
-class TestLLtype(BaseLLtypeTests, LLJitMixin):
     def test_tagged(self):
         from pypy.rlib.objectmodel import UnboxedValue
         class Base(object):
             return 42
         self.interp_operations(f, [1, 2, 3])
         self.check_operations_history(call=1, guard_no_exception=0)
+
+class TestLLtype(BaseLLtypeTests, LLJitMixin):
+    pass # should be empty

pypy/jit/metainterp/test/test_immutable.py

             l[2] = 30
             a = escape(X(l))
             return a.y[index]
-        res = self.interp_operations(f, [2], listops=True)
+        res = self.interp_operations(f, [2])
         assert res == 30
         self.check_operations_history(getfield_gc=0, getfield_gc_pure=1,
                             getarrayitem_gc=0, getarrayitem_gc_pure=1)
         def f(x, index):
             y = escape(X([x], x+1))
             return y.lst[index] + y.y + 5
-        res = self.interp_operations(f, [23, 0], listops=True)
+        res = self.interp_operations(f, [23, 0])
         assert res == 23 + 24 + 5
         self.check_operations_history(getfield_gc=0, getfield_gc_pure=2,
                             getarrayitem_gc=0, getarrayitem_gc_pure=1,

pypy/jit/metainterp/test/test_list.py

             l2 = l[:]
             return l2[0] + l2[1] + l2[2] + l2[3]
 
-        res = self.interp_operations(f, [], listops=True)
+        res = self.interp_operations(f, [])
         assert res == 10
 
     def test_arraycopy_full(self):

pypy/jit/metainterp/test/test_slist.py

             lst2.append(FooBar(5))
             m += lst2.pop().z     # 49
             return m
-        res = self.interp_operations(f, [11], listops=True)
+        res = self.interp_operations(f, [11])
         assert res == 49
         self.check_operations_history(call=3)
 

pypy/rlib/rerased.py

     from pypy.rlib.debug import ll_assert
     x = llop.cast_ptr_to_int(lltype.Signed, gcref)
     ll_assert((x&1) != 0, "unerased_int(): not an integer")
-    return x >> 1
+    return llop.int_untag(lltype.Signed, x)
 
 
 class Entry(ExtRegistryEntry):
         [v_value] = hop.inputargs(lltype.Signed)
         c_one = hop.inputconst(lltype.Signed, 1)
         hop.exception_is_here()
-        v2 = hop.genop('int_add_ovf', [v_value, v_value],
+        v2 = hop.genop('int_tag_ovf', [v_value],
                        resulttype = lltype.Signed)
-        v2p1 = hop.genop('int_add', [v2, c_one],
-                         resulttype = lltype.Signed)
-        v_instance = hop.genop('cast_int_to_ptr', [v2p1],
+        v_instance = hop.genop('cast_int_to_ptr', [v2],
                                resulttype=self.lowleveltype)
         return v_instance
 

pypy/rpython/llinterp.py

             assert y >= 0
         return self.op_int_add_ovf(x, y)
 
+    def op_int_tag_ovf(self, x):
+        try:
+            return ovfcheck(x + x + 1)
+        except OverflowError:
+            self.make_llexception()
+
+    def op_int_untag(self, x):
+        assert x & 1, "argument has to be tagged!"
+        return x >> 1
+
     def op_int_is_true(self, x):
         # special case
         if type(x) is CDefinedIntSymbolic:

pypy/rpython/lltypesystem/lloperation.py

     'int_abs':              LLOp(canfold=True),
     'int_abs_ovf':          LLOp(canraise=(OverflowError,), tryfold=True),
     'int_invert':           LLOp(canfold=True),
+    'int_tag_ovf':          LLOp(canraise=(OverflowError, ), tryfold=True),
+    'int_untag':            LLOp(canfold=True),
 
     'int_add':              LLOp(canfold=True),
     'int_sub':              LLOp(canfold=True),

pypy/rpython/lltypesystem/rtagged.py

 from pypy.rpython.lltypesystem.rclass import InstanceRepr, CLASSTYPE, ll_inst_type
 from pypy.rpython.lltypesystem.rclass import MissingRTypeAttribute
 from pypy.rpython.lltypesystem.rclass import ll_issubclass_const
+from pypy.rpython.lltypesystem.lloperation import llop
 from pypy.rpython.rmodel import TyperError, inputconst
 
 
         v_value = hop.inputarg(lltype.Signed, arg=1)
         c_one = hop.inputconst(lltype.Signed, 1)
         hop.exception_is_here()
-        v2 = hop.genop('int_add_ovf', [v_value, v_value],
+        v2 = hop.genop('int_tag_ovf', [v_value],
                        resulttype = lltype.Signed)
-        v2p1 = hop.genop('int_add', [v2, c_one],
-                         resulttype = lltype.Signed)
-        v_instance =  hop.genop('cast_int_to_ptr', [v2p1],
+        v_instance =  hop.genop('cast_int_to_ptr', [v2],
                                 resulttype = self.lowleveltype)
         return v_instance, False   # don't call __init__
 
     def convert_const_exact(self, value):
         self.setup()
         number = value.get_untagged_value()
-        return ll_int_to_unboxed(self.lowleveltype, number)
+        tagged = number * 2 + 1
+        return lltype.cast_int_to_ptr(self.lowleveltype, tagged)
 
     def getvalue_from_unboxed(self, llops, vinst):
         assert not self.is_parent
         v2 = llops.genop('cast_ptr_to_int', [vinst],  resulttype=lltype.Signed)
-        c_one = inputconst(lltype.Signed, 1)
-        return llops.genop('int_rshift', [v2, c_one], resulttype=lltype.Signed)
+        return llops.genop('int_untag', [v2], resulttype=lltype.Signed)
 
     def gettype_from_unboxed(self, llops, vinst, can_be_none=False):
         unboxedclass_repr = getclassrepr(self.rtyper, self.unboxedclassdef)
                                  minid, maxid, c_answer_if_unboxed)
 
 
-def ll_int_to_unboxed(PTRTYPE, value):
-    return lltype.cast_int_to_ptr(PTRTYPE, value*2+1)
 
 def ll_unboxed_to_int(p):
-    return lltype.cast_ptr_to_int(p) >> 1
+    return llop.int_untag(lltype.Signed, lltype.cast_ptr_to_int(p))
 
 def ll_unboxed_getclass_canbenone(instance, class_if_unboxed):
     if instance:

pypy/rpython/memory/gc/minimark.py

 
     def can_move(self, obj):
         """Overrides the parent can_move()."""
+        if not self.is_valid_gc_object(obj):
+            return False
         return self.is_in_nursery(obj)
 
 

pypy/translator/c/src/int.h

 	if ((x) == LONG_MIN) FAIL_OVF("integer absolute"); \
 	OP_INT_ABS(x,r)
 
+#define OP_INT_TAG_OVF(x, r) \
+    r = (long)((unsigned long)x << 1); \
+    if ((r ^ x) < 0) FAIL_OVF("integer tagging"); \
+    r = r + 1
+#define OP_INT_UNTAG(x, r) \
+    r = x >> 1
+
 /***  binary operations ***/
 
 #define OP_INT_EQ(x,y,r)	  r = ((x) == (y))