Commit ac87fcc9 authored by Stefan Behnel's avatar Stefan Behnel

reduce redundant coercions in cascaded assignments

parent 86c39f80
......@@ -9,6 +9,9 @@ Latest
Features added
--------------
* Cascaded assignments (a = b = X) try to minimise the number of
type coercions.
* Generators have new properties ``__name__`` and ``__qualname__``
that provide the plain/qualified name of the generator function
(following CPython 3.5). See http://bugs.python.org/issue21205
......
......@@ -11097,6 +11097,11 @@ class CloneNode(CoercionNode):
self.entry = self.arg.entry
return self
def coerce_to(self, dest_type, env):
if self.arg.is_literal:
return self.arg.coerce_to(dest_type, env)
return super(CloneNode, self).coerce_to(dest_type, env)
def is_simple(self):
return True # result is always in a temp (or a name)
......
......@@ -4718,41 +4718,56 @@ class CascadedAssignmentNode(AssignmentNode):
#
# coerced_rhs_list [ExprNode] RHS coerced to type of each LHS
child_attrs = ["lhs_list", "rhs", "coerced_rhs_list"]
child_attrs = ["lhs_list", "rhs", "coerced_values", "coerced_rhs_list"]
coerced_rhs_list = None
coerced_values = None
def analyse_declarations(self, env):
for lhs in self.lhs_list:
lhs.analyse_target_declaration(env)
def analyse_types(self, env, use_temp = 0):
def analyse_types(self, env, use_temp=0):
from .ExprNodes import CloneNode, ProxyNode
lhs_types = set()
for lhs in self.lhs_list:
lhs.analyse_target_types(env)
lhs.gil_assignment_check(env)
lhs_types.add(lhs.type)
rhs = self.rhs.analyse_types(env)
if use_temp or rhs.is_attribute or (
not rhs.is_name and not rhs.is_literal and
rhs.type.is_pyobject):
if len(lhs_types) == 1:
# common special case: only one type needed on the LHS => coerce only once
rhs = rhs.coerce_to(lhs_types.pop(), env)
if not rhs.is_name and not rhs.is_literal and (
use_temp or rhs.is_attribute or rhs.type.is_pyobject):
rhs = rhs.coerce_to_temp(env)
else:
rhs = rhs.coerce_to_simple(env)
self.rhs = ProxyNode(rhs)
self.rhs = ProxyNode(rhs) if rhs.is_temp else rhs
self.coerced_values = []
coerced_values = {}
for lhs in self.lhs_list:
if lhs.type not in coerced_values and lhs.type != rhs.type:
rhs = CloneNode(self.rhs).coerce_to(lhs.type, env)
self.coerced_values.append(rhs)
coerced_values[lhs.type] = rhs
self.coerced_rhs_list = []
for lhs in self.lhs_list:
lhs.analyse_target_types(env)
lhs.gil_assignment_check(env)
rhs = CloneNode(self.rhs)
rhs = rhs.coerce_to(lhs.type, env)
self.coerced_rhs_list.append(rhs)
rhs = coerced_values.get(lhs.type, self.rhs)
self.coerced_rhs_list.append(CloneNode(rhs))
return self
def generate_rhs_evaluation_code(self, code):
self.rhs.generate_evaluation_code(code)
def generate_assignment_code(self, code):
for i in range(len(self.lhs_list)):
lhs = self.lhs_list[i]
rhs = self.coerced_rhs_list[i]
for rhs in self.coerced_values:
rhs.generate_evaluation_code(code)
for lhs, rhs in zip(self.lhs_list, self.coerced_rhs_list):
rhs.generate_evaluation_code(code)
lhs.generate_assignment_code(rhs, code)
# Assignment has disposed of the cloned RHS
......@@ -4763,9 +4778,11 @@ class CascadedAssignmentNode(AssignmentNode):
self.rhs.generate_function_definitions(env, code)
def annotate(self, code):
for i in range(len(self.lhs_list)):
self.lhs_list[i].annotate(code)
self.coerced_rhs_list[i].annotate(code)
for rhs in self.coerced_values:
rhs.annotate(code)
for lhs, rhs in zip(self.lhs_list, self.coerced_rhs_list):
lhs.annotate(code)
rhs.annotate(code)
self.rhs.annotate(code)
......
......@@ -275,8 +275,6 @@ def cascaded_buffer_assignment(obj):
>>> A = IntMockBuffer("A", range(6))
>>> cascaded_buffer_assignment(A)
acquired A
acquired A
released A
released A
"""
cdef int[:] a, b
......
......@@ -24,6 +24,18 @@ def simple_parallel_int_mix():
ai, bi = al, bl = ao, bo = c = d = [1,2]
return ao, bo, ai, bi, al, bl, c, d
def simple_parallel_int_mix_recursive():
"""
>>> simple_parallel_int_mix_recursive()
(1, 2, 3, 1, [2, 3], 1, 2, 3, 1, 2, 3, [1, [2, 3]], [1, [2, 3]])
"""
cdef int ai, bi, ci
cdef long al, bl, cl
cdef object ao, bo, co
cdef object xo, yo
ai, [bi, ci] = al, [bl, cl] = xo, yo = ao, [bo, co] = c = d = [1, [2, 3]]
return ao, bo, co, xo, yo, ai, bi, ci, al, bl, cl, c, d
cdef int called = 0
cdef char* get_string():
......
import cython
@cython.test_fail_if_path_exists(
'//CascadedAssignmentNode//CoerceFromPyTypeNode',
'//CascadedAssignmentNode//CoerceToPyTypeNode',
)
@cython.test_assert_path_exists('//CascadedAssignmentNode')
def test_cascaded_assignment_simple():
"""
>>> test_cascaded_assignment_simple()
......@@ -8,6 +13,11 @@ def test_cascaded_assignment_simple():
a = b = c = 5
return a
@cython.test_fail_if_path_exists(
'//CascadedAssignmentNode//CoerceFromPyTypeNode',
'//CascadedAssignmentNode//CoerceToPyTypeNode',
)
@cython.test_assert_path_exists('//CascadedAssignmentNode')
def test_cascaded_assignment_typed():
"""
>>> test_cascaded_assignment_typed()
......@@ -46,4 +56,3 @@ def test_cascaded_assignment_evaluate_expr():
"""
a = b = c = float(expr())
return a, b, c
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment