[pypy-svn] r72843 - in pypy/trunk/pypy: lib lib/app_test module/_socket/test module/readline/test objspace/std rpython/lltypesystem rpython/test rpython/tool rpython/tool/test translator/c translator/c/test

arigo at codespeak.net arigo at codespeak.net
Thu Mar 25 19:29:55 CET 2010


Author: arigo
Date: Thu Mar 25 19:29:52 2010
New Revision: 72843

Modified:
   pypy/trunk/pypy/lib/app_test/test_dbm_extra.py
   pypy/trunk/pypy/lib/resource.py
   pypy/trunk/pypy/module/_socket/test/test_sock_app.py
   pypy/trunk/pypy/module/readline/test/test_c_readline.py
   pypy/trunk/pypy/module/readline/test/test_with_pypy.py
   pypy/trunk/pypy/objspace/std/typeobject.py
   pypy/trunk/pypy/rpython/lltypesystem/lltype.py
   pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
   pypy/trunk/pypy/rpython/test/test_rbuiltin.py
   pypy/trunk/pypy/rpython/test/test_rdict.py
   pypy/trunk/pypy/rpython/test/test_rint.py
   pypy/trunk/pypy/rpython/tool/rffi_platform.py
   pypy/trunk/pypy/rpython/tool/rfficache.py
   pypy/trunk/pypy/rpython/tool/test/test_rffi_platform.py
   pypy/trunk/pypy/translator/c/genc.py
   pypy/trunk/pypy/translator/c/node.py
   pypy/trunk/pypy/translator/c/test/test_lltyped.py
   pypy/trunk/pypy/translator/c/test/test_newgc.py
   pypy/trunk/pypy/translator/c/test/test_typed.py
Log:
Merge branch/fix-64.


Modified: pypy/trunk/pypy/lib/app_test/test_dbm_extra.py
==============================================================================
--- pypy/trunk/pypy/lib/app_test/test_dbm_extra.py	(original)
+++ pypy/trunk/pypy/lib/app_test/test_dbm_extra.py	Thu Mar 25 19:29:52 2010
@@ -1,6 +1,9 @@
 import py
-from pypy.lib import dbm
 from pypy.tool.udir import udir
+try:
+    from pypy.lib import dbm
+except ImportError, e:
+    py.test.skip(e)
 
 def test_get():
     path = str(udir.join('test_dbm_extra.test_get'))

Modified: pypy/trunk/pypy/lib/resource.py
==============================================================================
--- pypy/trunk/pypy/lib/resource.py	(original)
+++ pypy/trunk/pypy/lib/resource.py	Thu Mar 25 19:29:52 2010
@@ -28,8 +28,8 @@
 
 class timeval(Structure):
     _fields_ = (
-        ("tv_sec", c_int),
-        ("tv_usec", c_int),
+        ("tv_sec", c_long),
+        ("tv_usec", c_long),
     )
     def __str__(self):
         return "(%s, %s)" % (self.tv_sec, self.tv_usec)

Modified: pypy/trunk/pypy/module/_socket/test/test_sock_app.py
==============================================================================
--- pypy/trunk/pypy/module/_socket/test/test_sock_app.py	(original)
+++ pypy/trunk/pypy/module/_socket/test/test_sock_app.py	Thu Mar 25 19:29:52 2010
@@ -32,13 +32,17 @@
 
 def test_gethostbyaddr():
     host = "localhost"
+    expected = socket.gethostbyaddr(host)
+    expecteds = (expected, expected[:2]+(['0.0.0.0'],))
     ip = space.appexec([w_socket, space.wrap(host)],
                        "(_socket, host): return _socket.gethostbyaddr(host)")
-    assert space.unwrap(ip) == socket.gethostbyaddr(host)
+    assert space.unwrap(ip) in expecteds
     host = "127.0.0.1"
+    expected = socket.gethostbyaddr(host)
+    expecteds = (expected, expected[:2]+(['0.0.0.0'],))
     ip = space.appexec([w_socket, space.wrap(host)],
                        "(_socket, host): return _socket.gethostbyaddr(host)")
-    assert space.unwrap(ip) == socket.gethostbyaddr(host)
+    assert space.unwrap(ip) in expecteds
 
 def test_getservbyname():
     name = "smtp"

Modified: pypy/trunk/pypy/module/readline/test/test_c_readline.py
==============================================================================
--- pypy/trunk/pypy/module/readline/test/test_c_readline.py	(original)
+++ pypy/trunk/pypy/module/readline/test/test_c_readline.py	Thu Mar 25 19:29:52 2010
@@ -2,8 +2,14 @@
 Directly test the basic ctypes wrappers.
 """
 
+import py
 from pypy import conftest; conftest.translation_test_so_skip_if_appdirect()
-from pypy.module.readline import c_readline 
+from pypy.rpython.tool import rffi_platform as platform
+
+try:
+    from pypy.module.readline import c_readline
+except platform.CompilationError, e:
+    py.test.skip(e)
 
 
 def test_basic_import():

Modified: pypy/trunk/pypy/module/readline/test/test_with_pypy.py
==============================================================================
--- pypy/trunk/pypy/module/readline/test/test_with_pypy.py	(original)
+++ pypy/trunk/pypy/module/readline/test/test_with_pypy.py	Thu Mar 25 19:29:52 2010
@@ -3,7 +3,14 @@
 in the PyPy interpreter, itself running on top of CPython
 """
 
+import py
 from pypy.conftest import gettestobjspace
+from pypy.rpython.tool import rffi_platform as platform
+
+try:
+    from pypy.module.readline import c_readline
+except platform.CompilationError, e:
+    py.test.skip(e)
 
 
 class AppTestReadline:

Modified: pypy/trunk/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/typeobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/typeobject.py	Thu Mar 25 19:29:52 2010
@@ -264,7 +264,8 @@
     @purefunction
     def _pure_lookup_where_with_method_cache(w_self, name, version_tag):
         space = w_self.space
-        SHIFT = r_uint.BITS - space.config.objspace.std.methodcachesizeexp
+        SHIFT2 = r_uint.BITS - space.config.objspace.std.methodcachesizeexp
+        SHIFT1 = SHIFT2 - 5
         version_tag_as_int = current_object_addr_as_int(version_tag)
         # ^^^Note: if the version_tag object is moved by a moving GC, the
         # existing method cache entries won't be found any more; new
@@ -273,7 +274,12 @@
         # the time - so using the fast current_object_addr_as_int() instead
         # of a slower solution like hash() is still a good trade-off.
         hash_name = compute_hash(name)
-        method_hash = r_uint(intmask(version_tag_as_int * hash_name)) >> SHIFT
+        product = intmask(version_tag_as_int * hash_name)
+        method_hash = (r_uint(product) ^ (r_uint(product) << SHIFT1)) >> SHIFT2
+        # ^^^Note2: we used to just take product>>SHIFT2, but on 64-bit
+        # platforms SHIFT2 is really large, and we loose too much information
+        # that way (as shown by failures of the tests that typically have
+        # method names like 'f' who hash to a number that has only ~33 bits).
         cached_version_tag = space.method_cache_versions[method_hash]
         if cached_version_tag is version_tag:
             cached_name = space.method_cache_names[method_hash]

Modified: pypy/trunk/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lltype.py	Thu Mar 25 19:29:52 2010
@@ -1494,8 +1494,13 @@
         if n < 0:
             raise ValueError, "negative array length"
         _parentable.__init__(self, TYPE)
-        self.items = [TYPE.OF._allocate(initialization=initialization, parent=self, parentindex=j)
-                      for j in range(n)]
+        try:
+            myrange = range(n)
+        except OverflowError:
+            raise MemoryError("definitely too many items")
+        self.items = [TYPE.OF._allocate(initialization=initialization,
+                                        parent=self, parentindex=j)
+                      for j in myrange]
         if parent is not None:
             self._setparentstructure(parent, parentindex)
 

Modified: pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/opimpl.py	(original)
+++ pypy/trunk/pypy/rpython/lltypesystem/opimpl.py	Thu Mar 25 19:29:52 2010
@@ -18,13 +18,20 @@
 
 # global synonyms for some types
 from pypy.rlib.rarithmetic import intmask
-from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
+from pypy.rlib.rarithmetic import r_int, r_uint, r_longlong, r_ulonglong
 
-type_by_name = {
+if r_longlong is r_int:
+    r_longlong_arg = (r_longlong, int)
+    r_longlong_result = int
+else:
+    r_longlong_arg = r_longlong
+    r_longlong_result = r_longlong
+
+argtype_by_name = {
     'int': int,
     'float': float,
     'uint': r_uint,
-    'llong': r_longlong,
+    'llong': r_longlong_arg,
     'ullong': r_ulonglong,
     }
 
@@ -56,9 +63,9 @@
             adjust_result = intmask
         else:
             adjust_result = no_op
-        assert typname in type_by_name, "%s: not a primitive op" % (
+        assert typname in argtype_by_name, "%s: not a primitive op" % (
             fullopname,)
-        argtype = type_by_name[typname]
+        argtype = argtype_by_name[typname]
 
         if opname in ops_unary:
             def op_function(x):
@@ -226,16 +233,16 @@
     return r
 
 def op_llong_floordiv(x, y):
-    assert isinstance(x, r_longlong)
-    assert isinstance(y, r_longlong)
+    assert isinstance(x, r_longlong_arg)
+    assert isinstance(y, r_longlong_arg)
     r = x//y
     if x^y < 0 and x%y != 0:
         r += 1
     return r
 
 def op_llong_mod(x, y):
-    assert isinstance(x, r_longlong)
-    assert isinstance(y, r_longlong)
+    assert isinstance(x, r_longlong_arg)
+    assert isinstance(y, r_longlong_arg)
     r = x%y
     if x^y < 0 and x%y != 0:
         r -= y
@@ -258,7 +265,7 @@
     return float(u)
 
 def op_cast_longlong_to_float(i):
-    assert type(i) is r_longlong
+    assert isinstance(i, r_longlong_arg)
     # take first 31 bits
     li = float(int(i & r_longlong(0x7fffffff)))
     ui = float(int(i >> 31)) * float(0x80000000)
@@ -290,7 +297,7 @@
     small = f / r
     high = int(small)
     truncated = int((small - high) * r)
-    return r_longlong(high) * 0x100000000 + truncated
+    return r_longlong_result(high) * 0x100000000 + truncated
 
 def op_cast_char_to_int(b):
     assert type(b) is str and len(b) == 1
@@ -314,10 +321,10 @@
 
 def op_cast_int_to_longlong(b):
     assert type(b) is int
-    return r_longlong(b)
+    return r_longlong_result(b)
 
 def op_truncate_longlong_to_int(b):
-    assert type(b) is r_longlong
+    assert isinstance(b, r_longlong_arg)
     return intmask(b)
 
 def op_cast_pointer(RESTYPE, obj):

Modified: pypy/trunk/pypy/rpython/test/test_rbuiltin.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rbuiltin.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rbuiltin.py	Thu Mar 25 19:29:52 2010
@@ -5,7 +5,8 @@
 from pypy.rlib.debug import llinterpcall
 from pypy.rpython.lltypesystem import lltype
 from pypy.tool import udir
-from pypy.rlib.rarithmetic import r_uint, intmask, r_longlong, r_ulonglong
+from pypy.rlib.rarithmetic import intmask
+from pypy.rlib.rarithmetic import r_int, r_uint, r_longlong, r_ulonglong
 from pypy.annotation.builtin import *
 from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
 from pypy.rpython.lltypesystem import rffi
@@ -526,11 +527,21 @@
             return rffi.cast(rffi.VOIDP, v)
         res = self.interpret(llfn, [r_ulonglong(0)])
         assert res == lltype.nullptr(rffi.VOIDP.TO)
+        #
         def llfn(v):
             return rffi.cast(rffi.LONGLONG, v)
         res = self.interpret(llfn, [lltype.nullptr(rffi.VOIDP.TO)])
         assert res == 0
-        assert isinstance(res, r_longlong)
+        if r_longlong is not r_int:
+            assert isinstance(res, r_longlong)
+        else:
+            assert isinstance(res, int)
+        #
+        def llfn(v):
+            return rffi.cast(rffi.ULONGLONG, v)
+        res = self.interpret(llfn, [lltype.nullptr(rffi.VOIDP.TO)])
+        assert res == 0
+        assert isinstance(res, r_ulonglong)
         
 class TestOOtype(BaseTestRbuiltin, OORtypeMixin):
 

Modified: pypy/trunk/pypy/rpython/test/test_rdict.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rdict.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rdict.py	Thu Mar 25 19:29:52 2010
@@ -4,7 +4,7 @@
 from pypy.rpython.lltypesystem import rdict, rstr
 from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
 from pypy.rlib.objectmodel import r_dict
-from pypy.rlib.rarithmetic import r_uint, r_longlong, r_ulonglong
+from pypy.rlib.rarithmetic import r_int, r_uint, r_longlong, r_ulonglong
 
 import py
 py.log.setconsumer("rtyper", py.log.STDOUT)
@@ -567,6 +567,8 @@
 
     def test_dict_of_r_uint(self):
         for r_t in [r_uint, r_longlong, r_ulonglong]:
+            if r_t is r_int:
+                continue    # for 64-bit platforms: skip r_longlong
             d = {r_t(2): 3, r_t(4): 5}
             def fn(x, y):
                 d[r_t(x)] = 123

Modified: pypy/trunk/pypy/rpython/test/test_rint.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rint.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rint.py	Thu Mar 25 19:29:52 2010
@@ -110,10 +110,10 @@
         def f(i):
             return str(i)
 
-        res = self.interpret(f, [r_longlong(0)])
+        res = self.interpret(f, [int64(0)])
         assert self.ll_to_string(res) == '0'
 
-        res = self.interpret(f, [r_longlong(413974738222117)])
+        res = self.interpret(f, [int64(413974738222117)])
         assert self.ll_to_string(res) == '413974738222117'
 
     def test_unsigned(self):
@@ -135,7 +135,7 @@
         f._annspecialcase_ = "specialize:argtype(0)"
         def g(n):
             if n > 0:
-                return f(r_longlong(0))
+                return f(int64(0))
             else:
                 return f(0)
         res = self.interpret(g, [0])
@@ -147,7 +147,7 @@
     def test_downcast_int(self):
         def f(i):
             return int(i)
-        res = self.interpret(f, [r_longlong(0)])
+        res = self.interpret(f, [int64(0)])
         assert res == 0
 
     def test_isinstance_vs_int_types(self):

Modified: pypy/trunk/pypy/rpython/tool/rffi_platform.py
==============================================================================
--- pypy/trunk/pypy/rpython/tool/rffi_platform.py	(original)
+++ pypy/trunk/pypy/rpython/tool/rffi_platform.py	Thu Mar 25 19:29:52 2010
@@ -299,11 +299,16 @@
             fieldoffsets.append(offset)
             seen[cell] = True
 
+        allfields = tuple(['c_' + name for name, _ in fields])
+        padfields = tuple(padfields)
         name = self.name
+        padding_drop = PaddingDrop(name, allfields, padfields,
+                                   config_result.CConfig._compilation_info_)
         hints = {'align': info['align'],
                  'size': info['size'],
                  'fieldoffsets': tuple(fieldoffsets),
-                 'padding': tuple(padfields)}
+                 'padding': padfields,
+                 'get_padding_drop': padding_drop}
         if name.startswith('struct '):
             name = name[7:]
         else:
@@ -477,6 +482,89 @@
         return info['size']
 
 # ____________________________________________________________
+
+class PaddingDrop(object):
+    # Compute (lazily) the padding_drop for a structure.
+    # See test_generate_padding for more information.
+    cache = None
+
+    def __init__(self, name, allfields, padfields, eci):
+        self.name = name
+        self.allfields = allfields
+        self.padfields = padfields
+        self.eci = eci
+
+    def __call__(self, types):
+        if self.cache is None:
+            self.compute_now(types)
+        return self.cache
+
+    def compute_now(self, types):
+        # Some simplifying assumptions there.  We assume that all fields
+        # are either integers or pointers, so can be written in C as '0'.
+        # We also assume that the C backend gives us in 'types' a dictionary
+        # mapping non-padding field names to their C type (without '@').
+        drops = []
+        staticfields = []
+        consecutive_pads = []
+        for fieldname in self.allfields:
+            if fieldname in self.padfields:
+                consecutive_pads.append(fieldname)
+                continue
+            staticfields.append(types[fieldname])
+            if consecutive_pads:
+                # In that case we have to ask: how many of these pads are
+                # really needed?  The correct answer might be between none
+                # and all of the pads listed in 'consecutive_pads'.
+                for i in range(len(consecutive_pads)+1):
+                    class CConfig:
+                        _compilation_info_ = self.eci
+                        FIELDLOOKUP = _PaddingDropFieldLookup(self.name,
+                                                              staticfields,
+                                                              fieldname)
+                    try:
+                        got = configure(CConfig)['FIELDLOOKUP']
+                        if got == 1:
+                            break     # found
+                    except CompilationError:
+                        pass
+                    staticfields.insert(-1, None)
+                else:
+                    raise Exception("could not determine the detailed field"
+                                    " layout of %r" % (self.name,))
+                # succeeded with 'i' pads.  Drop all pads beyond that.
+                drops += consecutive_pads[i:]
+            consecutive_pads = []
+        drops += consecutive_pads   # drop the final pads too
+        self.cache = drops
+
+class _PaddingDropFieldLookup(CConfigEntry):
+    def __init__(self, name, staticfields, fieldname):
+        self.name = name
+        self.staticfields = staticfields
+        self.fieldname = fieldname
+
+    def prepare_code(self):
+        yield 'typedef %s platcheck_t;' % (self.name,)
+        yield 'static platcheck_t s = {'
+        for i, type in enumerate(self.staticfields):
+            if i == len(self.staticfields)-1:
+                value = -1
+            else:
+                value = 0
+            if type:
+                yield '\t(%s)%s,' % (type, value)
+            else:
+                yield '\t%s,' % (value,)
+        yield '};'
+        fieldname = self.fieldname
+        assert fieldname.startswith('c_')
+        yield 'dump("fieldlookup", s.%s != 0);' % (fieldname[2:],)
+
+    def build_result(self, info, config_result):
+        return info['fieldlookup']
+
+# ____________________________________________________________
 #
 # internal helpers
 

Modified: pypy/trunk/pypy/rpython/tool/rfficache.py
==============================================================================
--- pypy/trunk/pypy/rpython/tool/rfficache.py	(original)
+++ pypy/trunk/pypy/rpython/tool/rfficache.py	Thu Mar 25 19:29:52 2010
@@ -13,7 +13,7 @@
 from pypy.tool.gcc_cache import build_executable_cache
 
 def ask_gcc(question, add_source=""):
-    includes = ['stdlib.h', 'sys/types.h']
+    includes = ['stdlib.h', 'stdio.h', 'sys/types.h']
     include_string = "\n".join(["#include <%s>" % i for i in includes])
     c_source = py.code.Source('''
     // includes
@@ -34,8 +34,8 @@
     return build_executable_cache([c_file], eci)
 
 def sizeof_c_type(c_typename, **kwds):
-    question = 'printf("sizeof %s=%%d", sizeof(%s));' % (c_typename,
-                                                         c_typename)
+    question = 'printf("sizeof %s=%%ld", (long)sizeof(%s));' % (c_typename,
+                                                                c_typename)
     answer = ask_gcc(question, **kwds).split('=')
     assert answer[0] == "sizeof " + c_typename, "wrong program: " \
            "sizeof %s expected, got %s" % (c_typename, answer[0])

Modified: pypy/trunk/pypy/rpython/tool/test/test_rffi_platform.py
==============================================================================
--- pypy/trunk/pypy/rpython/tool/test/test_rffi_platform.py	(original)
+++ pypy/trunk/pypy/rpython/tool/test/test_rffi_platform.py	Thu Mar 25 19:29:52 2010
@@ -256,3 +256,104 @@
         library_dirs = [str(tmpdir)]
         )
     rffi_platform.verify_eci(eci)
+
+def test_generate_padding():
+    # 'padding_drop' is a bit strange, but is what we need to write C code
+    # that defines prebuilt structures of that type.  Normally, the C
+    # backend would generate '0' entries for every field c__pad#.  That's
+    # usually much more than the number of real fields in the real structure
+    # definition.  So 'padding_drop' allows a quick fix: it lists fields
+    # that should be ignored by the C backend.  It should only be used in
+    # that situation because it lists some of the c__pad# fields a bit
+    # randomly -- to the effect that writing '0' for the other fields gives
+    # the right amount of '0's.
+    S = rffi_platform.getstruct("foobar_t", """
+           typedef struct {
+                char c1;        /* followed by one byte of padding */
+                short s1;
+           } foobar_t;
+           """, [("c1", lltype.Signed),
+                 ("s1", lltype.Signed)])
+    assert S._hints['padding'] == ('c__pad0',)
+    d = {'c_c1': 'char', 'c_s1': 'short'}
+    assert S._hints['get_padding_drop'](d) == ['c__pad0']
+    #
+    S = rffi_platform.getstruct("foobar_t", """
+           typedef struct {
+                char c1;
+                char c2;  /* _pad0 */
+                short s1;
+           } foobar_t;
+           """, [("c1", lltype.Signed),
+                 ("s1", lltype.Signed)])
+    assert S._hints['padding'] == ('c__pad0',)
+    d = {'c_c1': 'char', 'c_s1': 'short'}
+    assert S._hints['get_padding_drop'](d) == []
+    #
+    S = rffi_platform.getstruct("foobar_t", """
+           typedef struct {
+                char c1;
+                char c2;  /* _pad0 */
+                /* _pad1, _pad2 */
+                int i1;
+           } foobar_t;
+           """, [("c1", lltype.Signed),
+                 ("i1", lltype.Signed)])
+    assert S._hints['padding'] == ('c__pad0', 'c__pad1', 'c__pad2')
+    d = {'c_c1': 'char', 'c_i1': 'int'}
+    assert S._hints['get_padding_drop'](d) == ['c__pad1', 'c__pad2']
+    #
+    S = rffi_platform.getstruct("foobar_t", """
+           typedef struct {
+                char c1;
+                char c2;  /* _pad0 */
+                char c3;  /* _pad1 */
+                /* _pad2 */
+                int i1;
+           } foobar_t;
+           """, [("c1", lltype.Signed),
+                 ("i1", lltype.Signed)])
+    assert S._hints['padding'] == ('c__pad0', 'c__pad1', 'c__pad2')
+    d = {'c_c1': 'char', 'c_i1': 'int'}
+    assert S._hints['get_padding_drop'](d) == ['c__pad2']
+    #
+    S = rffi_platform.getstruct("foobar_t", """
+           typedef struct {
+                char c1;
+                /* _pad0 */
+                short s1;  /* _pad1, _pad2 */
+                int i1;
+           } foobar_t;
+           """, [("c1", lltype.Signed),
+                 ("i1", lltype.Signed)])
+    assert S._hints['padding'] == ('c__pad0', 'c__pad1', 'c__pad2')
+    d = {'c_c1': 'char', 'c_i1': 'int'}
+    assert S._hints['get_padding_drop'](d) == ['c__pad1', 'c__pad2']
+    #
+    S = rffi_platform.getstruct("foobar_t", """
+           typedef struct {
+                char c1;
+                char c2;  /* _pad0 */
+                /* _pad1, _pad2 */
+                int i1;
+                char c3;  /* _pad3 */
+                /* _pad4 */
+                short s1;
+           } foobar_t;
+           """, [("c1", lltype.Signed),
+                 ("i1", lltype.Signed),
+                 ("s1", lltype.Signed)])
+    assert S._hints['padding'] == ('c__pad0', 'c__pad1', 'c__pad2',
+                                   'c__pad3', 'c__pad4')
+    d = {'c_c1': 'char', 'c_i1': 'int', 'c_s1': 'short'}
+    assert S._hints['get_padding_drop'](d) == ['c__pad1', 'c__pad2', 'c__pad4']
+    #
+    S = rffi_platform.getstruct("foobar_t", """
+           typedef struct {
+                char c1;
+                long l2;  /* some number of _pads */
+           } foobar_t;
+           """, [("c1", lltype.Signed)])
+    padding = list(S._hints['padding'])
+    d = {'c_c1': 'char'}
+    assert S._hints['get_padding_drop'](d) == padding

Modified: pypy/trunk/pypy/translator/c/genc.py
==============================================================================
--- pypy/trunk/pypy/translator/c/genc.py	(original)
+++ pypy/trunk/pypy/translator/c/genc.py	Thu Mar 25 19:29:52 2010
@@ -167,16 +167,19 @@
 
     have___thread = None
 
+    def merge_eci(self, *ecis):
+        self.eci = self.eci.merge(*ecis)
+
     def collect_compilation_info(self, db):
         # we need a concrete gcpolicy to do this
-        self.eci = self.eci.merge(db.gcpolicy.compilation_info())
+        self.merge_eci(db.gcpolicy.compilation_info())
 
         all = []
         for node in self.db.globalcontainers():
             eci = getattr(node, 'compilation_info', None)
             if eci:
                 all.append(eci)
-        self.eci = self.eci.merge(*all)
+        self.merge_eci(*all)
 
     def get_gcpolicyclass(self):
         if self.gcpolicy is None:

Modified: pypy/trunk/pypy/translator/c/node.py
==============================================================================
--- pypy/trunk/pypy/translator/c/node.py	(original)
+++ pypy/trunk/pypy/translator/c/node.py	Thu Mar 25 19:29:52 2010
@@ -37,6 +37,8 @@
 
 class StructDefNode:
     typetag = 'struct'
+    extra_union_for_varlength = True
+
     def __init__(self, db, STRUCT, varlength=1):
         self.db = db
         self.STRUCT = STRUCT
@@ -82,7 +84,8 @@
         self.fields = []
         db = self.db
         STRUCT = self.STRUCT
-        varlength = self.varlength
+        if self.varlength != 1:
+            self.normalizedtypename = db.gettype(STRUCT, who_asks=self)
         if needs_gcheader(self.STRUCT):
             for fname, T in db.gcpolicy.struct_gcheader_definition(self):
                 self.fields.append((fname, db.gettype(T, who_asks=self)))
@@ -151,6 +154,12 @@
         if is_empty:
             yield '\t' + 'char _dummy; /* this struct is empty */'
         yield '};'
+        if self.varlength != 1:
+            assert self.typetag == 'struct'
+            yield 'union %su {' % self.name
+            yield '  struct %s a;' % self.name
+            yield '  %s;' % cdecl(self.normalizedtypename, 'b')
+            yield '};'
 
     def visitor_lines(self, prefix, on_field):
         for name in self.fieldnames:
@@ -162,6 +171,7 @@
 
     def debug_offsets(self):
         # generate number exprs giving the offset of the elements in the struct
+        assert self.varlength == 1
         for name in self.fieldnames:
             FIELD_T = self.c_struct_field_type(name)
             if FIELD_T is Void:
@@ -178,15 +188,15 @@
 
 class ArrayDefNode:
     typetag = 'struct'
+    extra_union_for_varlength = True
 
     def __init__(self, db, ARRAY, varlength=1):
         self.db = db
         self.ARRAY = ARRAY
         self.LLTYPE = ARRAY
-        original_varlength = varlength
         self.gcfields = []
         self.varlength = varlength
-        if original_varlength == 1:
+        if varlength == 1:
             basename = 'array'
             with_number = True
         else:
@@ -204,6 +214,8 @@
         db = self.db
         ARRAY = self.ARRAY
         self.gcinfo    # force it to be computed
+        if self.varlength != 1:
+            self.normalizedtypename = db.gettype(ARRAY, who_asks=self)
         if needs_gcheader(ARRAY):
             for fname, T in db.gcpolicy.array_gcheader_definition(self):
                 self.gcfields.append((fname, db.gettype(T, who_asks=self)))
@@ -251,8 +263,14 @@
                 line = 'char _dummy; ' + line
         yield '\t' + line
         yield '};'
+        if self.varlength != 1:
+            yield 'union %su {' % self.name
+            yield '  struct %s a;' % self.name
+            yield '  %s;' % cdecl(self.normalizedtypename, 'b')
+            yield '};'
 
     def visitor_lines(self, prefix, on_item):
+        assert self.varlength == 1
         ARRAY = self.ARRAY
         # we need a unique name for this C variable, or at least one that does
         # not collide with the expression in 'prefix'
@@ -279,6 +297,7 @@
 
     def debug_offsets(self):
         # generate three offsets for debugging inspection
+        assert self.varlength == 1
         if not self.ARRAY._hints.get('nolength', False):
             yield 'offsetof(struct %s, length)' % (self.name,)
         else:
@@ -299,6 +318,7 @@
     gcinfo = None
     name = None
     forward_decl = None
+    extra_union_for_varlength = False
 
     def __init__(self, db, ARRAY, varlength=1):
         self.db = db
@@ -349,6 +369,7 @@
     gcinfo = None
     name = None
     typetag = 'struct'
+    extra_union_for_varlength = False
 
     def __init__(self, db, FIXEDARRAY):
         self.db = db
@@ -461,28 +482,42 @@
             parentnode = db.getcontainernode(parent)
             defnode = db.gettypedefnode(parentnode.T)
             self.name = defnode.access_expr(parentnode.name, parentindex)
-        self.ptrname = '(&%s)' % self.name
         if self.typename != self.implementationtypename:
-            ptrtypename = db.gettype(Ptr(T))
-            self.ptrname = '((%s)(void*)%s)' % (cdecl(ptrtypename, ''),
-                                                self.ptrname)
+            if db.gettypedefnode(T).extra_union_for_varlength:
+                self.name += '.b'
+        self.ptrname = '(&%s)' % self.name
 
     def is_thread_local(self):
         return hasattr(self.T, "_hints") and self.T._hints.get('thread_local')
 
+    def get_declaration(self):
+        if self.name[-2:] == '.b':
+            # xxx fish fish
+            assert self.implementationtypename.startswith('struct ')
+            assert self.implementationtypename.endswith(' @')
+            uniontypename = 'union %su @' % self.implementationtypename[7:-2]
+            return uniontypename, self.name[:-2]
+        else:
+            return self.implementationtypename, self.name
+
     def forward_declaration(self):
         if llgroup.member_of_group(self.obj):
             return
+        type, name = self.get_declaration()
         yield '%s;' % (
-            forward_cdecl(self.implementationtypename,
-                self.name, self.db.standalone, self.is_thread_local()))
+            forward_cdecl(type, name, self.db.standalone,
+                          self.is_thread_local()))
 
     def implementation(self):
         if llgroup.member_of_group(self.obj):
             return []
         lines = list(self.initializationexpr())
+        type, name = self.get_declaration()
+        if name != self.name:
+            lines[0] = '{ ' + lines[0]    # extra braces around the 'a' part
+            lines[-1] += ' }'             # of the union
         lines[0] = '%s = %s' % (
-            cdecl(self.implementationtypename, self.name, self.is_thread_local()),
+            cdecl(type, name, self.is_thread_local()),
             lines[0])
         lines[-1] += ';'
         return lines
@@ -536,7 +571,19 @@
         if hasattr(self.T, "_hints") and self.T._hints.get('union'):
             data = data[0:1]
 
+        if 'get_padding_drop' in self.T._hints:
+            d = {}
+            for name, _ in data:
+                T = defnode.c_struct_field_type(name)
+                typename = self.db.gettype(T)
+                d[name] = cdecl(typename, '')
+            padding_drop = self.T._hints['get_padding_drop'](d)
+        else:
+            padding_drop = []
+
         for name, value in data:
+            if name in padding_drop:
+                continue
             c_expr = defnode.access_expr(self.name, name)
             lines = generic_initializationexpr(self.db, value, c_expr,
                                                decoration + name)
@@ -560,6 +607,7 @@
         return 'struct _hashT_%s @' % self.name
 
     def forward_declaration(self):
+        assert self.typename == self.implementationtypename  # no array part
         hash_typename = self.get_hash_typename()
         hash_offset = self.db.gctransformer.get_hash_offset(self.T)
         yield '%s {' % cdecl(hash_typename, '')
@@ -675,6 +723,7 @@
         return 1    # not variable-sized!
 
     def initializationexpr(self, decoration=''):
+        assert self.typename == self.implementationtypename  # not var-sized
         is_empty = True
         yield '{'
         # _names == ['item0', 'item1', ...]

Modified: pypy/trunk/pypy/translator/c/test/test_lltyped.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_lltyped.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_lltyped.py	Thu Mar 25 19:29:52 2010
@@ -782,3 +782,48 @@
         fn = self.getcompiled(f, [])
         res = fn()
         assert res == 42
+
+    def test_padding_in_prebuilt_struct(self):
+        from pypy.rpython.lltypesystem import rffi
+        from pypy.rpython.tool import rffi_platform
+        eci = rffi_platform.eci_from_header("""
+            typedef struct {
+                char c1;        /* followed by one byte of padding */
+                short s1;
+                char c2;        /* followed by 3 bytes of padding */
+                int i2;
+                char c3;        /* followed by 3 or 7 bytes of padding */
+                long l3;
+                char c4;
+            } foobar_t;
+        """)
+        class CConfig:
+            _compilation_info_ = eci
+            STRUCT = rffi_platform.Struct("foobar_t",
+                                          [("c1", Signed),
+                                           ("s1", Signed),
+                                           ("l3", Signed)])
+        S = rffi_platform.configure(CConfig)['STRUCT']
+        assert 'get_padding_drop' in S._hints
+        s1 = malloc(S, immortal=True)
+        s1.c_c1 = rffi.cast(S.c_c1, -12)
+        s1.c_s1 = rffi.cast(S.c_s1, -7843)
+        s1.c_l3 = -98765432
+        s2 = malloc(S, immortal=True)
+        s2.c_c1 = rffi.cast(S.c_c1, -123)
+        s2.c_s1 = rffi.cast(S.c_s1, -789)
+        s2.c_l3 = -9999999
+        #
+        def f(n):
+            if n > 5:
+                s = s1
+            else:
+                s = s2
+            return s.c_l3
+        #
+        self.include_also_eci = eci
+        fn = self.getcompiled(f, [int])
+        res = fn(10)
+        assert res == -98765432
+        res = fn(1)
+        assert res == -9999999

Modified: pypy/trunk/pypy/translator/c/test/test_newgc.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_newgc.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_newgc.py	Thu Mar 25 19:29:52 2010
@@ -653,8 +653,8 @@
             to_sort[2] = 1
             to_sort[3] = 2
             qsort.push_arg(rffi.cast(rffi.VOIDP, to_sort))
-            qsort.push_arg(rffi.cast(rffi.SIZE_T, rffi.sizeof(rffi.LONG)))
             qsort.push_arg(rffi.cast(rffi.SIZE_T, 4))
+            qsort.push_arg(rffi.cast(rffi.SIZE_T, rffi.sizeof(rffi.LONG)))
             qsort.push_arg(rffi.cast(rffi.VOIDP, ptr.ll_closure))
             qsort.call(lltype.Void)
             result = [to_sort[i] for i in range(4)] == [1,2,3,4]

Modified: pypy/trunk/pypy/translator/c/test/test_typed.py
==============================================================================
--- pypy/trunk/pypy/translator/c/test/test_typed.py	(original)
+++ pypy/trunk/pypy/translator/c/test/test_typed.py	Thu Mar 25 19:29:52 2010
@@ -28,6 +28,8 @@
     def compilefunc(self, t, func):
         from pypy.translator.c import genc
         self.builder = builder = genc.CExtModuleBuilder(t, func, config=t.config)
+        if hasattr(self, 'include_also_eci'):
+            builder.merge_eci(self.include_also_eci)
         builder.generate_source()
         builder.compile()
         return builder.get_entry_point()



More information about the Pypy-commit mailing list