Commits

timfel committed 340ef09

add W_LargePositiveInteger1Word for LargePositiveInteger operations that fit in 1 word

  • Participants
  • Parent commits b6d01c5

Comments (0)

Files changed (8)

File spyvm/model.py

         W_AbstractObjectWithIdentityHash
             W_Float
             W_AbstractObjectWithClassReference
-                W_PointersObject 
+                W_PointersObject
                 W_BytesObject
                 W_WordsObject
             W_CompiledMethod
 
     def size(self):
         """Return bytesize that conforms to Blue Book.
-        
+
         The reported size may differ from the actual size in Spy's object
         space, as memory representation varies depending on PyPy translation."""
         return 0
         consult the Blue Book)."""
         # TODO check the Blue Book
         raise NotImplementedError()
-        
-    def store(self, space, n0, w_value):    
+
+    def store(self, space, n0, w_value):
         """Access fixed-size part, maybe also variable-sized part (we have to
         consult the Blue Book)."""
         raise NotImplementedError()
 class W_SmallInteger(W_Object):
     """Boxed integer value"""
     # TODO can we tell pypy that its never larger then 31-bit?
-    _attrs_ = ['value'] 
+    _attrs_ = ['value']
     __slots__ = ('value',)     # the only allowed slot here
     _immutable_fields_ = ["value"]
 
     def _become(self, w_other):
         self.hash, w_other.hash = w_other.hash, self.hash
 
+class W_LargePositiveInteger1Word(W_AbstractObjectWithIdentityHash):
+    """Large positive integer for exactly 1 word"""
+    _attrs_ = ["value", "_exposed_size"]
+
+    def __init__(self, value, size=4):
+        self.value = value
+        self._exposed_size = size
+
+    def fillin(self, space, g_self):
+        self.hash = g_self.get_hash()
+        word = 0
+        bytes = g_self.get_bytes()
+        for idx, byte in enumerate(bytes):
+            assert idx < 4
+            word |= ord(byte) << (idx * 8)
+        self.value = word
+        self._exposed_size = len(bytes)
+
+    def getclass(self, space):
+        return space.w_LargePositiveInteger
+
+    def gethash(self):
+        if self.hash == self.UNASSIGNED_HASH:
+            """Integer >> hash
+            (self lastDigit bitShift: 8) + (self digitAt: 1)
+            """
+            self.hash = (self.at0(self.size() - 1) << 8) + self.at0(0)
+        return self.value
+
+    def invariant(self):
+        return isinstance(self.value, int)
+
+    def __repr__(self):
+        return "W_LargePositiveInteger1Word(%d)" % r_uint(self.value)
+
+    def clone(self, space):
+        return W_LargePositiveInteger1Word(self.value)
+
+    def at0(self, space, index0):
+        if index0 >= self.size():
+            raise IndexError()
+        skew = index0 * 8
+        mask = 0xff << skew
+        return space.wrap_int((self.value & mask) >> skew)
+
+    def atput0(self, space, index0, w_byte):
+        if index0 >= self.size():
+            raise IndexError()
+        skew = index0 * 8
+        byte = space.unwrap_int(w_byte)
+        assert byte <= 0xff
+        new_value = self.value & r_uint(~(0xff << skew))
+        new_value |= r_uint(byte << skew)
+        self.value = intmask(new_value)
+
+    def size(self):
+        return self._exposed_size
+
+    def invariant(self):
+        return isinstance(self.value, int)
+
 class W_Float(W_AbstractObjectWithIdentityHash):
     """Boxed float value."""
     _attrs_ = ['value']
     _attrs_ = ['_shadow', '_vars']
 
     _shadow = None # Default value
-    
+
     @jit.unroll_safe
     def __init__(self, w_class, size):
         """Create new object with size = fixed + variable size."""
 
     def _fetch(self, n0):
         return self._vars[n0]
-        
-    def store(self, space, n0, w_value):    
+
+    def store(self, space, n0, w_value):
         if self.has_shadow():
             return self._shadow.store(n0, w_value)
         return self._store(n0, w_value)
         if w_other.has_shadow(): w_other._shadow._w_self = w_other
         W_AbstractObjectWithClassReference._become(self, w_other)
         return True
-        
+
     def clone(self, space):
         w_result = W_PointersObject(self.w_class, len(self._vars))
         w_result._vars = [self.fetch(space, i) for i in range(len(self._vars))]
 
     def at0(self, space, index0):
         return space.wrap_int(ord(self.getchar(index0)))
-       
+
     def atput0(self, space, index0, w_value):
         self.setchar(index0, chr(space.unwrap_int(w_value)))
 
     def getchar(self, n0):
         return self.bytes[n0]
-    
+
     def setchar(self, n0, character):
         assert len(character) == 1
         self.bytes[n0] = character
 
     def size(self):
-        return len(self.bytes)    
+        return len(self.bytes)
 
     def __str__(self):
         return self.as_string()
         self.words = g_self.get_ruints()
         self.w_class = g_self.get_class()
         self.hash = g_self.get_hash()
-        
+
     def at0(self, space, index0):
         val = self.getword(index0)
         return space.wrap_uint(val)
- 
+
     def atput0(self, space, index0, w_value):
         word = space.unwrap_uint(w_value)
         self.setword(index0, word)
 
     def getword(self, n):
         return self.words[n]
-        
+
     def setword(self, n, word):
         self.words[n] = r_uint(word)
 
     def size(self):
-        return len(self.words)   
+        return len(self.words)
 
     def invariant(self):
         return (W_AbstractObjectWithClassReference.invariant(self) and
     def as_string(self, markBytecode=0):
         from spyvm.interpreter import BYTECODE_TABLE
         j = 1
-        retval  = "\nMethodname: " + self.get_identifier_string() 
+        retval  = "\nMethodname: " + self.get_identifier_string()
         retval += "\nBytecode:------------\n"
         for i in self.bytes:
             retval += '->' if j is markBytecode else '  '
     def invariant(self):
         return (W_Object.invariant(self) and
                 hasattr(self, 'literals') and
-                self.literals is not None and 
+                self.literals is not None and
                 hasattr(self, 'bytes') and
-                self.bytes is not None and 
+                self.bytes is not None and
                 hasattr(self, 'argsize') and
-                self.argsize is not None and 
+                self.argsize is not None and
                 hasattr(self, 'tempsize') and
-                self.tempsize is not None and 
+                self.tempsize is not None and
                 hasattr(self, 'primitive') and
-                self.primitive is not None)       
+                self.primitive is not None)
 
     def size(self):
-        return self.headersize() + self.getliteralsize() + len(self.bytes) 
+        return self.headersize() + self.getliteralsize() + len(self.bytes)
 
     def gettempsize(self):
         return self.tempsize
             # From blue book:
             # The literal count indicates the size of the
             # CompiledMethod's literal frame.
-            # This, in turn, indicates where the 
-            # CompiledMethod's bytecodes start. 
+            # This, in turn, indicates where the
+            # CompiledMethod's bytecodes start.
             index0 = index0 - self.bytecodeoffset()
             assert index0 < len(self.bytes)
             return space.wrap_int(ord(self.bytes[index0]))
-        
+
     def atput0(self, space, index0, w_value):
         if index0 < self.bytecodeoffset():
             if index0 % constants.BYTES_PER_WORD != 0:

File spyvm/objspace.py

         from spyvm import constants
         if int_between(constants.TAGGED_MININT, val, constants.TAGGED_MAXINT + 1):
             return model.W_SmallInteger(val)
+        elif val > 0:
+            return model.W_LargePositiveInteger1Word(val)
         raise WrappingError("integer too large to fit into a tagged pointer")
 
     def wrap_uint(self, val):
     def unwrap_int(self, w_value):
         if isinstance(w_value, model.W_SmallInteger):
             return w_value.value
-        raise UnwrappingError("expected a W_SmallInteger, got %s" % (w_value,))
+        elif isinstance(w_value, model.W_LargePositiveInteger1Word):
+            if w_value.value > 0:
+                return w_value.value
+        raise UnwrappingError("expected a W_SmallInteger or W_LargePositiveInteger1Word, got %s" % (w_value,))
 
     def unwrap_uint(self, w_value):
         if isinstance(w_value, model.W_SmallInteger):
             if val < 0:
                 raise UnwrappingError("got negative integer")
             return r_uint(w_value.value)
-        if isinstance(w_value, model.W_BytesObject):
+        elif isinstance(w_value, model.W_LargePositiveInteger1Word):
+            return r_uint(w_value.value)
+        elif isinstance(w_value, model.W_BytesObject):
             # TODO: Completely untested! This failed translation bigtime...
             # XXX Probably we want to allow all subclasses
             if not w_value.getclass(self).is_same_object(self.w_LargePositiveInteger):

File spyvm/primitives.py

         whileTrue: [self at: index put: (replacement at: repOff + index)]"""
     if (start < 0 or start - 1 > stop or repStart < 0):
         raise PrimitiveFailedError()
-    if w_rcvr.__class__ is not w_replacement.__class__:
+    if w_rcvr.getclass(interp.space) is not w_replacement.getclass(interp.space):
         raise PrimitiveFailedError()
     if (w_rcvr.size() <= stop
             or w_replacement.size() < repStart + (stop - start)):

File spyvm/shadow.py

 WEAK_POINTERS = 3
 COMPILED_METHOD = 4
 FLOAT = 5
+LARGE_POSITIVE_INTEGER = 6
 
 class MethodNotFound(error.SmalltalkException):
     pass
                     raise ClassShadowError("can't have both words and a non-zero "
                                            "base instance size")
             elif 8 <= format <= 11:
-                self.instance_kind = BYTES
+                if self.space.w_LargePositiveInteger.is_same_object(self.w_self()):
+                    self.instance_kind = LARGE_POSITIVE_INTEGER
+                else:
+                    self.instance_kind = BYTES
                 if self.instsize() != 0:
                     raise ClassShadowError("can't have both bytes and a non-zero "
                                            "base instance size")
             w_new = model.W_CompiledMethod(extrasize)
         elif self.instance_kind == FLOAT:
             w_new = model.W_Float(0) # Squeak gives a random piece of memory
+        elif self.instance_kind == LARGE_POSITIVE_INTEGER:
+            if extrasize == 4:
+                w_new = model.W_LargePositiveInteger1Word(0, extrasize)
+            else:
+                w_new = model.W_BytesObject(w_cls, extrasize)
         else:
             raise NotImplementedError(self.instance_kind)
         return w_new

File spyvm/squeakimage.py

     def isbytes(self):
         return 8 <= self.format <= 11
 
+    def is32bitlargepositiveinteger(self):
+        return (self.format == 8 and
+                self.space.w_LargePositiveInteger.is_same_object(self.g_class.w_object) and
+                len(self.get_bytes()) <= 4)
+
     def iswords(self):
         return self.format == 6
 
                 raise CorruptImageError("Unknown format 5")
             elif self.isfloat():
                 self.w_object = objectmodel.instantiate(model.W_Float)
+            elif self.is32bitlargepositiveinteger():
+                self.w_object = objectmodel.instantiate(model.W_LargePositiveInteger1Word)
             elif self.iswords():
                 self.w_object = objectmodel.instantiate(model.W_WordsObject)
             elif self.format == 7:

File spyvm/test/test_miniimage.py

     assert w_result is not None
     assert isinstance(w_result, model.W_Float)
 
+def test_compiling_float():
+    sourcecode = """aFloat
+                        ^ 1.1"""
+    perform(w(10).getclass(space), "compile:classified:notifying:", w(sourcecode), w('pypy'), w(None))
+    w_result = perform(w(10), "aFloat")
+    assert isinstance(w_result, model.W_Float)
+    assert w_result.value == 1.1
+
+def test_existing_large_positive_integer_as_W_LargePositiveInteger1Word():
+    import math
+    w_result = perform(interp.space.w_Float, "pi")
+    assert w_result is not None
+    assert isinstance(w_result, model.W_Float)
+    assert w_result.value == math.pi
+
+def test_large_positive_integer_operations():
+    w_result = perform(interp.space.w_SmallInteger, "maxVal")
+    w_result = perform(w_result, "+", space.wrap_int(42))
+    assert w_result is not None
+    assert isinstance(w_result, model.W_LargePositiveInteger1Word)
+
+    w_result = perform(interp.space.w_SmallInteger, "maxVal")
+    w_result = perform(w_result, "*", w_result)
+    assert w_result is not None
+    assert isinstance(w_result, model.W_BytesObject)
+
+def test_compiling_large_positive_integer():
+    sourcecode = """aLargeInteger
+                        ^ 16rFFFFFFFF"""
+    perform(w(10).getclass(space), "compile:classified:notifying:", w(sourcecode), w('pypy'), w(None))
+    w_result = perform(w(10), "aLargeInteger")
+    assert isinstance(w_result, model.W_LargePositiveInteger1Word)
+
 def test_doesNotUnderstand():
     w_dnu = interp.space.objtable["w_doesNotUnderstand"]
     assert isinstance(w_dnu, model.W_BytesObject)

File spyvm/test/test_model.py

 from spyvm import model, shadow
 from spyvm.shadow import MethodNotFound
 from spyvm import objspace, error
+from rpython.rlib.rarithmetic import intmask, r_uint
 
 mockclass = objspace.bootstrap_class
 
 def test_float_at():
     b = model.W_Float(64.0)
     r = b.fetch(space, 0)
-    assert isinstance(r, model.W_BytesObject)
+    assert isinstance(r, model.W_LargePositiveInteger1Word)
     assert r.size() == 4
-    assert r.bytes == [chr(0), chr(0), chr(80), chr(64)]
+    assert space.unwrap_int(r.at0(space, 0)) == 0
+    assert space.unwrap_int(r.at0(space, 1)) == 0
+    assert space.unwrap_int(r.at0(space, 2)) == 80
+    assert space.unwrap_int(r.at0(space, 3)) == 64
     r = b.fetch(space, 1)
     assert isinstance(r, model.W_SmallInteger)
     assert r.value == 0
     target.store(space, 0, space.wrap_int(42))
     assert target.gethash() != model.W_Float(1.1).gethash()
 
+def test_large_positive_integer_1word_at():
+    b = model.W_LargePositiveInteger1Word(-1)
+    for i in range(4):
+        r = b.at0(space, i)
+        assert isinstance(r, model.W_SmallInteger)
+        assert space.unwrap_int(r) == 0xff
+    assert b.value == -1
+
+def test_large_positive_integer_1word_at_put():
+    target = model.W_LargePositiveInteger1Word(0)
+    source = model.W_LargePositiveInteger1Word(-1)
+    for i in range(4):
+        target.atput0(space, i, source.at0(space, i))
+        assert target.at0(space, i) == source.at0(space, i)
+    assert hex(r_uint(target.value)) == hex(r_uint(source.value))
+
 def test_display_bitmap():
     target = model.W_DisplayBitmap.create(space.w_Array, 100, 1, None)
     target.setword(0, 0xFF00)

File spyvm/test/test_primitives.py

     assert prim(primitives.ADD, [3,4]).value == 7
 
 def test_small_int_add_fail():
-    prim_fails(primitives.ADD, [constants.TAGGED_MAXINT,2])
+    w_result = prim(primitives.ADD, [constants.TAGGED_MAXINT, 2])
+    assert isinstance(w_result, model.W_LargePositiveInteger1Word)
+    assert w_result.value == constants.TAGGED_MAXINT + 2
+    prim_fails(primitives.ADD, [constants.TAGGED_MAXINT, constants.TAGGED_MAXINT * 2])
 
 def test_small_int_minus():
     assert prim(primitives.SUBTRACT, [5,9]).value == -4
     assert prim(primitives.MULTIPLY, [6,3]).value == 18
 
 def test_small_int_multiply_overflow():
-    prim_fails(primitives.MULTIPLY, [constants.TAGGED_MAXINT, 2])
+    w_result = prim(primitives.MULTIPLY, [constants.TAGGED_MAXINT, 2])
+    assert isinstance(w_result, model.W_LargePositiveInteger1Word)
+    assert w_result.value == constants.TAGGED_MAXINT * 2
     prim_fails(primitives.MULTIPLY, [constants.TAGGED_MAXINT, constants.TAGGED_MAXINT])
     prim_fails(primitives.MULTIPLY, [constants.TAGGED_MAXINT, -4])
+    prim_fails(primitives.MULTIPLY, [constants.TAGGED_MININT, constants.TAGGED_MAXINT])
     prim_fails(primitives.MULTIPLY, [constants.TAGGED_MININT, 2])
     
 def test_small_int_divide():
     prim_fails(primitives.BIT_SHIFT, [4, 31])
     prim_fails(primitives.BIT_SHIFT, [4, 30])
     prim_fails(primitives.BIT_SHIFT, [4, 29])
-    prim_fails(primitives.BIT_SHIFT, [4, 28])
+    w_result = prim(primitives.BIT_SHIFT, [4, 28])
+    assert isinstance(w_result, model.W_LargePositiveInteger1Word)
+    assert w_result.value == 4 << 28
 
 def test_smallint_as_float():
     assert prim(primitives.SMALLINT_AS_FLOAT, [12]).value == 12.0