Permalink
Browse files

Support non-assignment array expressions

  • Loading branch information...
markflorisson committed Jun 24, 2012
1 parent 62f8783 commit ce10532e35a5f9a9a49f7cbe8717553f90e29297
Showing with 96 additions and 77 deletions.
  1. +0 −1 Cython/Compiler/ExprNodes.py
  2. +84 −69 Cython/Compiler/Vector.py
  3. +12 −7 tests/array_expressions/new_memory.pyx
@@ -9643,7 +9643,6 @@ def __init__(self, arg, env, type=py_object_type):
if self.arg.type.is_memoryviewslice:
# register utility codes for conversion to/from the memoryview dtype
self.arg.type.dtype_object_conversion_funcs(env)
- self.is_elemental = self.arg.is_elemental
self.env = env
View
@@ -673,7 +673,7 @@ def offsets(lhs, rhs):
rhs_offset = max(rhs_ndim - lhs_ndim, 0)
return lhs_offset, rhs_offset
-class ElementalNode(Nodes.StatNode):
+class ElementalNode(ExprNodes.ExprNode):
"""
Evaluate the expression on the right hand side before assigning to the
expression on the left hand side. This is needed in two situations:
@@ -692,15 +692,12 @@ class ElementalNode(Nodes.StatNode):
acquire_slice:
This assignment creates a cython.view.array and acquires a
memoryview slice from that in self.lhs (a temporary)
- final_lhs_assignment:
- Assignment of temporary memoryview slice of a
- cython.view.array to the final lhs ('c').
"""
- child_attrs = ['operands', 'scalar_operands', 'temp_nodes', 'lhs',
- 'check_overlap', 'rhs', 'final_assignment_node',
- 'broadcast', 'final_broadcast', 'temp_dst',
- 'acquire_slice', 'final_lhs_assignment']
+ subexprs = ['operands', 'scalar_operands', 'temp_nodes', 'lhs',
+ 'check_overlap', 'rhs', 'final_assignment_node',
+ 'broadcast', 'final_broadcast', 'temp_dst',
+ 'acquire_slice', 'final_lhs_assignment']
check_overlap = None
temp_dst = None
@@ -837,7 +834,7 @@ def verify_final_shape(self, code):
self.lhs.type.ndim, self.rhs.type.ndim)
code.putln(code.error_goto_if_neg(call, self.pos))
- def generate_execution_code(self, code):
+ def generate_evaluation_code(self, code):
code.mark_pos(self.pos)
code.putln("/* LHS */")
@@ -906,16 +903,12 @@ def generate_execution_code(self, code):
code.putln("if (%s) {" % self.overlap())
self.temp_dst.generate_disposal_code(code)
self.temp_dst.free_temps(code)
+ self.temp_dst = self.temp_dst.wrap_in_clone_node()
code.putln("}")
- else:
- self.final_lhs_assignment.generate_execution_code(code)
- self.dispose(code)
-
- def dispose(self, code):
+ def generate_disposal_code(self, code):
for child_attr in self.child_attrs:
- if child_attr in ('temp_dst', 'acquire_slice',
- 'final_lhs_assignment'):
+ if child_attr == 'acquire_slice':
continue
value_list = getattr(self, child_attr)
@@ -927,6 +920,36 @@ def dispose(self, code):
node.generate_disposal_code(code)
node.free_temps(code)
+ def free_temps(self, code):
+ "We already released temps during disposal code generation."
+
+ def calculate_result_code(self):
+ return ""
+
+class ElementalNodeWrapper(ExprNodes.ExprNode):
+ """
+ This node is used in case of no slice assignment.
+
+ Attributes:
+ elemental_node:
+ The ElementalNode being wrapped
+ slice_result:
+ The expression holding the final memoryview slice with the
+ result.
+ """
+ subexprs = ['elemental_node']
+ def analyse_types(self, env):
+ self.type = self.slice_result.type
+
+ def generate_assignment_code(self, rhs, code):
+ self.slice_result.generate_assignment_code(self, rhs, code)
+
+ def generate_result_code(self, code):
+ pass
+
+ def result(self):
+ return self.slice_result.result()
+
def need_wrapper_node(node):
"""
Return whether a Cython node that needs to be mapped to a miniast Node,
@@ -1162,98 +1185,90 @@ def visit_elemental(self, node, lhs=None, acquire_slice=None):
#node = Nodes.ExprStatNode(node.pos, expr=node)
return node
- def visit_ExprNode(self, node):
- if node.is_elemental:
- node = self.visit_elemental(node)
+ def visit_SingleAssignmentNode(self, node):
+ env = self.current_env()
+ if (isinstance(node.lhs, ExprNodes.MemoryCopySlice) and
+ node.lhs.is_elemental):
+ assert not self.in_elemental
+ node.is_elemental = True
+
+ node.lhs.dst = UnbroadcastDestNode(
+ node.pos, lhs=node.lhs.dst.coerce_to_temp(env),
+ rhs=node.rhs)
+ node.lhs.dst.analyse_types(env)
+
+ elemental_node = self.visit_elemental(node, node.lhs.dst)
+ return Nodes.ExprStatNode(node.pos, expr=elemental_node)
else:
+ is_elemental = node.rhs.is_elemental
self.visitchildren(node)
+ if is_elemental:
+ node.analyse_types(env)
+ return node
+
+ def visit_ExprNode(self, node):
+ if node.is_elemental:
+ if self.in_elemental:
+ return self.visit_elemental(node)
+
+ env = self.current_env()
+ tmp_lhs, elemental_node = self._create_new_array_node(node, env)
+ result = ElementalNodeWrapper(node.pos, slice_result=tmp_lhs,
+ elemental_node=elemental_node)
+ result.analyse_types(env)
+ return result
+ self.visitchildren(node)
return node
- def _acquire_new_memview(self, lhs, rhs, lazy_rhs, env):
+ def _acquire_new_memview(self, rhs, lazy_rhs, env):
"""
- Acquire a new memoryview slice, assigning to an lhs variable which
- may be a memoryview or an object. The rhs is the broadcasted rhs
- expression.
+ Acquire a new memoryview slice in a temp.
+ The rhs is the broadcasted rhs expression (which is a ProxyNode since
+ we don't have its result yet).
"""
- if not lhs.type.is_pyobject:
- type = lhs.type
- else:
- type = rhs.type
+ type = rhs.type
# cyarray = <dtype[:broadcasted_shape[0]]> malloc(broadcasted_shape[0])
temp_cy_array = TempCythonArrayNode(
rhs.pos, dest_array_type=type, rhs=lazy_rhs)
# cdef dtype[:] tmp
tmp_lhs = TempSliceStruct(
- lhs.pos, dtype=type.dtype, axes=type.axes, ndim=type.ndim)
+ rhs.pos, dtype=type.dtype, axes=type.axes, ndim=type.ndim)
tmp_lhs.analyse_types(env)
tmp_lhs = tmp_lhs.wrap_in_clone_node()
# tmp = cyarray
acquire_assignment = Nodes.SingleAssignmentNode(
- lhs.pos, lhs=tmp_lhs.arg, rhs=temp_cy_array)
+ rhs.pos, lhs=tmp_lhs.arg, rhs=temp_cy_array)
acquire_assignment.analyse_expressions(env)
- # Final assignment, after elemental expression is executed:
- # lhs = tmp
- final_assignment = Nodes.SingleAssignmentNode(lhs.pos,
- lhs=lhs, rhs=tmp_lhs)
- final_assignment.analyse_expressions(env)
- return lhs, rhs, tmp_lhs, acquire_assignment, final_assignment
+ return tmp_lhs, acquire_assignment
- def _create_new_array_node(self, lhs, rhs, env):
+ def _create_new_array_node(self, rhs, env):
"""
In an assignment like 'b = a + a', we need to create a new array 'b'.
We create a cython.view.array, and assign it to variable 'b'.
Todo: implement temporary memoryview slices without needing the GIL!
"""
lazy_rhs = ExprNodes.ProxyNode(rhs)
- lhs, rhs, tmp_lhs, acquire_assignment, final_assignment = \
- self._acquire_new_memview(lhs, rhs, lazy_rhs, env)
+ tmp_lhs, acquire_assignment = self._acquire_new_memview(
+ rhs, lazy_rhs, env)
# create elemental assignment
elemental_assignment = Nodes.SingleAssignmentNode(
- lhs.pos, lhs=tmp_lhs.arg, rhs=rhs)
+ rhs.pos, lhs=tmp_lhs.arg, rhs=rhs)
elemental_node = self.visit_elemental(elemental_assignment,
lhs=tmp_lhs.arg,
acquire_slice=acquire_assignment)
lazy_rhs.arg = elemental_node.rhs
lazy_rhs.proxy_type()
- elemental_node.final_lhs_assignment = final_assignment
- return elemental_node
- #return [elemental_node, final_assignment]
-
- def visit_SingleAssignmentNode(self, node):
- if not (node.lhs.is_elemental or node.rhs.is_elemental):
- self.visitchildren(node)
- return node
-
- env = self.current_env()
-
- if (isinstance(node.lhs, ExprNodes.MemoryCopySlice) and
- node.lhs.is_elemental):
- assert not self.in_elemental
- node.is_elemental = True
-
- node.lhs.dst = UnbroadcastDestNode(
- node.pos, lhs=node.lhs.dst.coerce_to_temp(env),
- rhs=node.rhs)
- node.lhs.dst.analyse_types(env)
-
- return self.visit_elemental(node, node.lhs.dst)
- elif node.lhs.is_name and node.rhs.is_elemental:
- if isinstance(node.rhs, ExprNodes.CoerceToPyTypeNode):
- node.rhs = node.rhs.arg
- return self._create_new_array_node(node.lhs, node.rhs, env)
- else:
- error(node.pos, "Invalid elementwise assignment")
- return
-
+ #elemental_node.final_lhs_assignment = final_assignment
+ return tmp_lhs, elemental_node
def load_utilities(env):
from Cython.Compiler import CythonScope
@@ -30,15 +30,20 @@ def test_allocate_new_memory_simple(fused_dtype_t[:] m):
result = m + m
return np.asarray(result)
-@testcase
+@testcase_like(test_allocate_new_memory_simple)
def test_allocate_new_memory_typed(fused_dtype_t[:] m):
- """
- >>> test_allocate_new_memory_typed(np.arange(10, dtype=np.longdouble))
- array([ 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0], dtype=float128)
- >>> test_allocate_new_memory_typed(object_range(10))
- array([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], dtype=object)
- """
cdef fused_dtype_t[:] result
result = m + m
return np.asarray(result)
+
+@testcase_like(test_allocate_new_memory_simple)
+def test_allocate_new_memory_expr(fused_dtype_t[:] m):
+ return np.asarray(m + m)
+
+cdef fused_dtype_t[:] func(fused_dtype_t[:] m):
+ return m
+
+@testcase_like(test_allocate_new_memory_simple)
+def test_allocate_new_memory_expr_typed_call(fused_dtype_t[:] m):
+ return np.asarray(func(m + m))

0 comments on commit ce10532

Please sign in to comment.