Buffers: Inplace operators.
authorDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Fri, 15 Aug 2008 15:54:08 +0000 (17:54 +0200)
committerDag Sverre Seljebotn <dagss@student.matnat.uio.no>
Fri, 15 Aug 2008 15:54:08 +0000 (17:54 +0200)
Cython/Compiler/ExprNodes.py
Cython/Compiler/Nodes.py
tests/run/bufaccess.pyx

index a1b7982e416dcd840ae3f24867cace5a2123ca63..9ec23bcdba4a4c32508548c4d5e9570e80fc72a9 100644 (file)
@@ -1379,7 +1379,7 @@ class IndexNode(ExprNode):
         skip_child_analysis = False
         buffer_access = False
         if self.base.type.is_buffer:
-            assert isinstance(self.base, NameNode)
+            assert hasattr(self.base, "entry") # Must be a NameNode-like node
             if isinstance(self.index, TupleNode):
                 indices = self.index.args
             else:
@@ -1394,7 +1394,7 @@ class IndexNode(ExprNode):
 
         if buffer_access:
             self.indices = indices
-            self.index = None
+            self.index = None # note that original is kept in _index, which is used for cloning
             self.type = self.base.type.dtype
             self.is_buffer_access = True
             self.buffer_type = self.base.entry.type
@@ -1469,19 +1469,19 @@ class IndexNode(ExprNode):
 
     def generate_subexpr_evaluation_code(self, code):
         self.base.generate_evaluation_code(code)
-        if self.index is not None:
-            self.index.generate_evaluation_code(code)
-        else:
+        if self.indices:
             for i in self.indices:
                 i.generate_evaluation_code(code)
-        
+        else:
+            self.index.generate_evaluation_code(code)       
+
     def generate_subexpr_disposal_code(self, code):
         self.base.generate_disposal_code(code)
-        if self.index is not None:
-            self.index.generate_disposal_code(code)
-        else:
+        if self.indices:
             for i in self.indices:
                 i.generate_disposal_code(code)
+        else:
+            self.index.generate_disposal_code(code)
 
     def generate_result_code(self, code):
         if self.is_buffer_access:
@@ -1525,30 +1525,34 @@ class IndexNode(ExprNode):
                 value_code,
                 self.index_unsigned_parameter(),
                 code.error_goto(self.pos)))
-    
+
+    def generate_buffer_assignment_code(self, rhs, code, op=""):
+        # Used from generate_assignment_code and InPlaceAssignmentNode
+        ptrexpr = self.buffer_lookup_code(code)
+        if self.buffer_type.dtype.is_pyobject:
+            # Must manage refcounts. Decref what is already there
+            # and incref what we put in.
+            ptr = code.funcstate.allocate_temp(self.buffer_type.buffer_ptr_type)
+            if rhs.is_temp: #TODO: REMOVE
+                rhs_code = code.funcstate.allocate_temp(rhs.type)
+            else:
+                rhs_code = rhs.result_code
+            code.putln("%s = %s;" % (ptr, ptrexpr))
+            code.putln("Py_DECREF(*%s); Py_INCREF(%s);" % (
+                ptr, rhs_code
+                ))
+            code.putln("*%s %s= %s;" % (ptr, op, rhs_code))
+            if rhs.is_temp:
+                code.funcstate.release_temp(rhs_code)
+            code.funcstate.release_temp(ptr)
+        else: 
+            # Simple case
+            code.putln("*%s %s= %s;" % (ptrexpr, op, rhs.result_code))
+
     def generate_assignment_code(self, rhs, code):
         self.generate_subexpr_evaluation_code(code)
         if self.is_buffer_access:
-            ptrexpr = self.buffer_lookup_code(code)
-            if self.buffer_type.dtype.is_pyobject:
-                # Must manage refcounts. Decref what is already there
-                # and incref what we put in.
-                ptr = code.funcstate.allocate_temp(self.buffer_type.buffer_ptr_type)
-                if rhs.is_temp:
-                    rhs_code = code.funcstate.allocate_temp(rhs.type)
-                else:
-                    rhs_code = rhs.result_code
-                code.putln("%s = %s;" % (ptr, ptrexpr))
-                code.putln("Py_DECREF(*%s); Py_INCREF(%s);" % (
-                    ptr, rhs_code
-                    ))
-                code.putln("*%s = %s;" % (ptr, rhs_code))
-                if rhs.is_temp:
-                    code.funcstate.release_temp(rhs_code)
-                code.funcstate.release_temp(ptr)
-            else: 
-                # Simple case
-                code.putln("*%s = %s;" % (ptrexpr, rhs.result_code))
+            self.generate_buffer_assignment_code(rhs, code)
         elif self.type.is_pyobject:
             self.generate_setitem_code(rhs.py_result(), code)
         else:
@@ -3937,6 +3941,7 @@ class CoercionNode(ExprNode):
     def __init__(self, arg):
         self.pos = arg.pos
         self.arg = arg
+        self.options = arg.options
         if debug_coercion:
             print("%s Coercing %s" % (self, self.arg))
             
index f10d278dd61ba034367e8b4380d1068e7c720f75..df2ac65ec76ad8140aca9940d2b21d3c9893ee5f 100644 (file)
@@ -71,10 +71,12 @@ class Node(object):
     #  pos         (string, int, int)   Source file position
     #  is_name     boolean              Is a NameNode
     #  is_literal  boolean              Is a ConstNode
+    #  options     dict                 Compiler directives for this node
     
     is_name = 0
     is_literal = 0
     temps = None
+    options = {}
 
     # All descandants should set child_attrs to a list of the attributes
     # containing nodes considered "children" in the tree. Each such attribute
@@ -2517,12 +2519,13 @@ class InPlaceAssignmentNode(AssignmentNode):
     def generate_execution_code(self, code):
         self.rhs.generate_evaluation_code(code)
         self.dup.generate_subexpr_evaluation_code(code)
-        self.dup.generate_result_code(code)
+        # self.dup.generate_result_code is run only if it is not buffer access
         if self.operator == "**":
             extra = ", Py_None"
         else:
             extra = ""
         if self.lhs.type.is_pyobject:
+            self.dup.generate_result_code(code)
             code.putln(
                 "%s = %s(%s, %s%s); %s" % (
                     self.result.result_code, 
@@ -2545,7 +2548,12 @@ class InPlaceAssignmentNode(AssignmentNode):
                 else:
                     error(self.pos, "No C inplace power operator")
             # have to do assignment directly to avoid side-effects
-            code.putln("%s %s= %s;" % (self.lhs.result_code, c_op, self.rhs.result_code) )
+            import ExprNodes
+            if isinstance(self.lhs, ExprNodes.IndexNode) and self.lhs.is_buffer_access:
+                self.lhs.generate_buffer_assignment_code(self.rhs, code, c_op)
+            else:
+                self.dup.generate_result_code(code)
+                code.putln("%s %s= %s;" % (self.lhs.result_code, c_op, self.rhs.result_code) )
             self.rhs.generate_disposal_code(code)
         if self.dup.is_temp:
             self.dup.generate_subexpr_disposal_code(code)
@@ -2555,11 +2563,23 @@ class InPlaceAssignmentNode(AssignmentNode):
         self.dup = self.lhs
         self.dup.analyse_types(env)
         if isinstance(self.lhs, ExprNodes.NameNode):
-            target_lhs = ExprNodes.NameNode(self.dup.pos, name = self.dup.name, is_temp = self.dup.is_temp, entry = self.dup.entry)
+            target_lhs = ExprNodes.NameNode(self.dup.pos,
+                                            name = self.dup.name,
+                                            is_temp = self.dup.is_temp,
+                                            entry = self.dup.entry,
+                                            options = self.dup.options)
         elif isinstance(self.lhs, ExprNodes.AttributeNode):
-            target_lhs = ExprNodes.AttributeNode(self.dup.pos, obj = ExprNodes.CloneNode(self.lhs.obj), attribute = self.dup.attribute, is_temp = self.dup.is_temp)
+            target_lhs = ExprNodes.AttributeNode(self.dup.pos,
+                                                 obj = ExprNodes.CloneNode(self.lhs.obj),
+                                                 attribute = self.dup.attribute,
+                                                 is_temp = self.dup.is_temp,
+                                                 options = self.dup.options)
         elif isinstance(self.lhs, ExprNodes.IndexNode):
-            target_lhs = ExprNodes.IndexNode(self.dup.pos, base = ExprNodes.CloneNode(self.dup.base), index = ExprNodes.CloneNode(self.lhs.index), is_temp = self.dup.is_temp)
+                target_lhs = ExprNodes.IndexNode(self.dup.pos,
+                                                 base = ExprNodes.CloneNode(self.dup.base),
+                                                 index = ExprNodes.CloneNode(self.lhs._index),
+                                                 is_temp = self.dup.is_temp,
+                                                 options = self.dup.options)
         self.lhs = target_lhs
         return self.dup
     
index a4ad192cf72ad83e22c3eb42300ba73ed7d8e921..5d7d32e19f95b4d252027a6a679dcd0a7285953c 100644 (file)
@@ -596,12 +596,23 @@ TODO
     uc[0] = <int>3.14
     print uc[0]
 
+    cdef char* ch = "asfd"
+    cdef object[object] objbuf
+    objbuf[3] = ch
+
 
 #
 # Testing that accessing data using various types of buffer access
 # all works.
 #
 
+def printbuf_int(object[int] buf, shape):
+    # Utility func
+    cdef int i
+    for i in range(shape[0]):
+        print buf[i],
+    print 'END'
+
 
 @testcase
 def printbuf_int_2d(o, shape):
@@ -654,6 +665,24 @@ def printbuf_float(o, shape):
     print "END"
 
 
+#
+# Test assignments
+#
+@testcase
+def inplace_operators(object[int] buf):
+    """
+    >>> buf = IntMockBuffer(None, [2, 2])
+    >>> inplace_operators(buf)
+    >>> printbuf_int(buf, (2,))
+    0 3 END
+    """
+    cdef int j = 0
+    buf[1] += 1
+    buf[j] *= 2
+    buf[0] -= 4
+
+
+
 #
 # Typedefs
 #