[pypy-commit] pypy faster-rstruct-2: delegate the alignment check to Buffer.typed_{read, write}, to take in account also the SubBuffer's offset. Moreover, reuse the logic in rawstorage to determine whether it is fine to do an unaligned access, depending on the CPU

antocuni pypy.commits at gmail.com
Thu May 18 11:13:40 EDT 2017


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: faster-rstruct-2
Changeset: r91335:7d8f1bbfc0b1
Date: 2017-05-18 17:12 +0200
http://bitbucket.org/pypy/pypy/changeset/7d8f1bbfc0b1/

Log:	delegate the alignment check to Buffer.typed_{read,write}, to take
	in account also the SubBuffer's offset. Moreover, reuse the logic in
	rawstorage to determine whether it is fine to do an unaligned
	access, depending on the CPU

diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -13,6 +13,21 @@
                               ll_for_resizable_list)
 from rpython.rlib.signature import signature
 from rpython.rlib import types
+from rpython.rlib import rawstorage
+
+ALLOW_UNALIGNED_ACCESS = rawstorage.misaligned_is_fine
+
+ at specialize.ll()
+def is_alignment_correct(TYPE, index):
+    if ALLOW_UNALIGNED_ACCESS:
+        return True
+    try:
+        rawstorage._check_alignment(TYPE, index)
+    except rawstorage.AlignmentError:
+        return False
+    else:
+        return True
+
 
 class CannotRead(Exception):
     """
@@ -117,6 +132,8 @@
         """
         Read the value of type TP starting at byte_offset. No bounds checks
         """
+        if not is_alignment_correct(TP, byte_offset):
+            raise CannotRead
         ptr = self.get_raw_address()
         return llop.raw_load(TP, ptr, byte_offset)
 
@@ -125,7 +142,7 @@
         """
         Write the value of type TP at byte_offset. No bounds checks
         """
-        if self.readonly:
+        if self.readonly or not is_alignment_correct(TP, byte_offset):
             raise CannotWrite
         ptr = self.get_raw_address()
         value = lltype.cast_primitive(TP, value)
@@ -158,6 +175,8 @@
 
         @specialize.ll_and_arg(1)
         def typed_read(self, TP, byte_offset):
+            if not is_alignment_correct(TP, byte_offset):
+                raise CannotRead
             lldata = self._get_gc_data()
             byte_offset += self._get_gc_data_extra_offset()
             return llop.gc_load_indexed(TP, lldata, byte_offset,
@@ -165,7 +184,7 @@
 
         @specialize.ll_and_arg(1)
         def typed_write(self, TP, byte_offset, value):
-            if self.readonly:
+            if self.readonly or not is_alignment_correct(TP, byte_offset):
                 raise CannotWrite
             lldata = self._get_gc_data()
             byte_offset += self._get_gc_data_extra_offset()
diff --git a/rpython/rlib/rstruct/standardfmttable.py b/rpython/rlib/rstruct/standardfmttable.py
--- a/rpython/rlib/rstruct/standardfmttable.py
+++ b/rpython/rlib/rstruct/standardfmttable.py
@@ -33,17 +33,17 @@
     @specialize.argtype(0)
     def do_pack_fastpath(fmtiter, value):
         size = rffi.sizeof(TYPE)
-        pos = fmtiter.pos
         if (not USE_FASTPATH or
             fmtiter.bigendian != native_is_bigendian or
-            not native_is_ieee754 or
-            pos % size != 0):
+            not native_is_ieee754):
             raise CannotWrite
         #
-        if not ALLOW_FASTPATH:
-            raise ValueError("fastpath not allowed :(")
         # typed_write() might raise CannotWrite
         fmtiter.wbuf.typed_write(TYPE, fmtiter.pos, value)
+        if not ALLOW_FASTPATH:
+            # if we are here it means that typed_write did not raise, and thus
+            # the fast path was actually taken
+            raise ValueError("fastpath not allowed :(")
         fmtiter.advance(size)
     #
     @specialize.argtype(0)
@@ -203,12 +203,7 @@
     def do_unpack_fastpath(fmtiter):
         size = rffi.sizeof(TYPE)
         buf, pos = fmtiter.get_buffer_and_pos()
-        if pos % size != 0 or not USE_FASTPATH:
-            # XXX: maybe we are too conservative here? On most architectures,
-            # it is possible to read the data even if pos is not
-            # aligned. Also, probably it should responsibility of
-            # buf.typed_read to raise CannotRead in case it is not aligned
-            # *and* it is not supported.
+        if not USE_FASTPATH:
             raise CannotRead
         #
         if not ALLOW_FASTPATH:
diff --git a/rpython/rlib/rstruct/test/test_pack.py b/rpython/rlib/rstruct/test/test_pack.py
--- a/rpython/rlib/rstruct/test/test_pack.py
+++ b/rpython/rlib/rstruct/test/test_pack.py
@@ -2,8 +2,10 @@
 from rpython.rlib.rarithmetic import r_ulonglong
 from rpython.rlib.rstruct import standardfmttable, nativefmttable
 from rpython.rlib.rstruct.error import StructOverflowError
+from rpython.rlib import buffer
 from rpython.rlib.buffer import SubBuffer
 from rpython.rlib.mutbuffer import MutableStringBuffer
+from rpython.rlib import rawstorage
 import struct
 
 class FakeFormatIter(object):
@@ -40,16 +42,19 @@
     USE_FASTPATH = True
     ALLOW_SLOWPATH = True
     ALLOW_FASTPATH = True
+    ALLOW_UNALIGNED_ACCESS = rawstorage.misaligned_is_fine
     
     def setup_method(self, meth):
         standardfmttable.USE_FASTPATH = self.USE_FASTPATH
         standardfmttable.ALLOW_SLOWPATH = self.ALLOW_SLOWPATH
         standardfmttable.ALLOW_FASTPATH = self.ALLOW_FASTPATH
+        buffer.ALLOW_UNALIGNED_ACCESS = self.ALLOW_UNALIGNED_ACCESS
 
     def teardown_method(self, meth):
         standardfmttable.USE_FASTPATH = True
         standardfmttable.ALLOW_SLOWPATH = True
         standardfmttable.ALLOW_FASTPATH = True
+        buffer.ALLOW_UNALIGNED_ACCESS = rawstorage.misaligned_is_fine
 
     def mypack(self, fmt, value):
         size = struct.calcsize(fmt)
@@ -202,6 +207,7 @@
 
 class TestUnaligned(PackSupport):
     ALLOW_FASTPATH = False
+    ALLOW_UNALIGNED_ACCESS = False
     bigendian = nativefmttable.native_is_bigendian
     fmttable = nativefmttable.native_fmttable
 
@@ -227,6 +233,6 @@
         wbuf.setitem(0, chr(0xAB))
         wbuf.setitem(1, chr(0xCD))
         fake_fmtiter = self.mypack_into('i', wsubbuf, 0x1234)
-        assert fake_fmtiter.pos == wbuf.getlength()
+        assert fake_fmtiter.pos == wbuf.getlength()-2 # -2 since it's a SubBuffer
         got = wbuf.finish()
         assert got == expected


More information about the pypy-commit mailing list