Commit ac87fcc9 authored by Stefan Behnel's avatar Stefan Behnel

reduce redundant coercions in cascaded assignments

parent 86c39f80
...@@ -9,6 +9,9 @@ Latest ...@@ -9,6 +9,9 @@ Latest
Features added Features added
-------------- --------------
* Cascaded assignments (a = b = X) try to minimise the number of
type coercions.
* Generators have new properties ``__name__`` and ``__qualname__`` * Generators have new properties ``__name__`` and ``__qualname__``
that provide the plain/qualified name of the generator function that provide the plain/qualified name of the generator function
(following CPython 3.5). See http://bugs.python.org/issue21205 (following CPython 3.5). See http://bugs.python.org/issue21205
......
...@@ -11097,6 +11097,11 @@ class CloneNode(CoercionNode): ...@@ -11097,6 +11097,11 @@ class CloneNode(CoercionNode):
self.entry = self.arg.entry self.entry = self.arg.entry
return self 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): def is_simple(self):
return True # result is always in a temp (or a name) return True # result is always in a temp (or a name)
......
...@@ -4718,41 +4718,56 @@ class CascadedAssignmentNode(AssignmentNode): ...@@ -4718,41 +4718,56 @@ class CascadedAssignmentNode(AssignmentNode):
# #
# coerced_rhs_list [ExprNode] RHS coerced to type of each LHS # 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_rhs_list = None
coerced_values = None
def analyse_declarations(self, env): def analyse_declarations(self, env):
for lhs in self.lhs_list: for lhs in self.lhs_list:
lhs.analyse_target_declaration(env) 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 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) rhs = self.rhs.analyse_types(env)
if use_temp or rhs.is_attribute or ( if len(lhs_types) == 1:
not rhs.is_name and not rhs.is_literal and # common special case: only one type needed on the LHS => coerce only once
rhs.type.is_pyobject): 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) rhs = rhs.coerce_to_temp(env)
else: else:
rhs = rhs.coerce_to_simple(env) 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 = [] self.coerced_rhs_list = []
for lhs in self.lhs_list: for lhs in self.lhs_list:
lhs.analyse_target_types(env) rhs = coerced_values.get(lhs.type, self.rhs)
lhs.gil_assignment_check(env) self.coerced_rhs_list.append(CloneNode(rhs))
rhs = CloneNode(self.rhs)
rhs = rhs.coerce_to(lhs.type, env)
self.coerced_rhs_list.append(rhs)
return self return self
def generate_rhs_evaluation_code(self, code): def generate_rhs_evaluation_code(self, code):
self.rhs.generate_evaluation_code(code) self.rhs.generate_evaluation_code(code)
def generate_assignment_code(self, code): def generate_assignment_code(self, code):
for i in range(len(self.lhs_list)): for rhs in self.coerced_values:
lhs = self.lhs_list[i] rhs.generate_evaluation_code(code)
rhs = self.coerced_rhs_list[i] for lhs, rhs in zip(self.lhs_list, self.coerced_rhs_list):
rhs.generate_evaluation_code(code) rhs.generate_evaluation_code(code)
lhs.generate_assignment_code(rhs, code) lhs.generate_assignment_code(rhs, code)
# Assignment has disposed of the cloned RHS # Assignment has disposed of the cloned RHS
...@@ -4763,9 +4778,11 @@ class CascadedAssignmentNode(AssignmentNode): ...@@ -4763,9 +4778,11 @@ class CascadedAssignmentNode(AssignmentNode):
self.rhs.generate_function_definitions(env, code) self.rhs.generate_function_definitions(env, code)
def annotate(self, code): def annotate(self, code):
for i in range(len(self.lhs_list)): for rhs in self.coerced_values:
self.lhs_list[i].annotate(code) rhs.annotate(code)
self.coerced_rhs_list[i].annotate(code) for lhs, rhs in zip(self.lhs_list, self.coerced_rhs_list):
lhs.annotate(code)
rhs.annotate(code)
self.rhs.annotate(code) self.rhs.annotate(code)
......
...@@ -275,8 +275,6 @@ def cascaded_buffer_assignment(obj): ...@@ -275,8 +275,6 @@ def cascaded_buffer_assignment(obj):
>>> A = IntMockBuffer("A", range(6)) >>> A = IntMockBuffer("A", range(6))
>>> cascaded_buffer_assignment(A) >>> cascaded_buffer_assignment(A)
acquired A acquired A
acquired A
released A
released A released A
""" """
cdef int[:] a, b cdef int[:] a, b
......
...@@ -24,6 +24,18 @@ def simple_parallel_int_mix(): ...@@ -24,6 +24,18 @@ def simple_parallel_int_mix():
ai, bi = al, bl = ao, bo = c = d = [1,2] ai, bi = al, bl = ao, bo = c = d = [1,2]
return ao, bo, ai, bi, al, bl, c, d 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 int called = 0
cdef char* get_string(): cdef char* get_string():
......
import cython import cython
@cython.test_fail_if_path_exists(
'//CascadedAssignmentNode//CoerceFromPyTypeNode',
'//CascadedAssignmentNode//CoerceToPyTypeNode',
)
@cython.test_assert_path_exists('//CascadedAssignmentNode')
def test_cascaded_assignment_simple(): def test_cascaded_assignment_simple():
""" """
>>> test_cascaded_assignment_simple() >>> test_cascaded_assignment_simple()
...@@ -8,6 +13,11 @@ def test_cascaded_assignment_simple(): ...@@ -8,6 +13,11 @@ def test_cascaded_assignment_simple():
a = b = c = 5 a = b = c = 5
return a return a
@cython.test_fail_if_path_exists(
'//CascadedAssignmentNode//CoerceFromPyTypeNode',
'//CascadedAssignmentNode//CoerceToPyTypeNode',
)
@cython.test_assert_path_exists('//CascadedAssignmentNode')
def test_cascaded_assignment_typed(): def test_cascaded_assignment_typed():
""" """
>>> test_cascaded_assignment_typed() >>> test_cascaded_assignment_typed()
...@@ -46,4 +56,3 @@ def test_cascaded_assignment_evaluate_expr(): ...@@ -46,4 +56,3 @@ def test_cascaded_assignment_evaluate_expr():
""" """
a = b = c = float(expr()) a = b = c = float(expr())
return a, b, c 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