From fed7b5318ba476701b7fac5073667bb8b41bd8a7 Mon Sep 17 00:00:00 2001 From: Dag Sverre Seljebotn Date: Fri, 15 Aug 2008 17:54:08 +0200 Subject: [PATCH] Buffers: Inplace operators. --- Cython/Compiler/ExprNodes.py | 65 +++++++++++++++++++----------------- Cython/Compiler/Nodes.py | 30 ++++++++++++++--- tests/run/bufaccess.pyx | 29 ++++++++++++++++ 3 files changed, 89 insertions(+), 35 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index a1b7982e..9ec23bcd 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -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)) diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index f10d278d..df2ac65e 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -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 diff --git a/tests/run/bufaccess.pyx b/tests/run/bufaccess.pyx index a4ad192c..5d7d32e1 100644 --- a/tests/run/bufaccess.pyx +++ b/tests/run/bufaccess.pyx @@ -596,12 +596,23 @@ TODO uc[0] = 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 # -- 2.26.2