Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Gwenaël Samain
cython
Commits
1e94a834
Commit
1e94a834
authored
Jan 11, 2012
by
Vitja Makarov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Simple closure defnode call inlining
parent
c1c378a1
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
204 additions
and
0 deletions
+204
-0
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+100
-0
Cython/Compiler/Optimize.py
Cython/Compiler/Optimize.py
+22
-0
Cython/Compiler/Options.py
Cython/Compiler/Options.py
+3
-0
Cython/Compiler/Pipeline.py
Cython/Compiler/Pipeline.py
+2
-0
tests/run/closure_inlining.pyx
tests/run/closure_inlining.pyx
+77
-0
No files found.
Cython/Compiler/ExprNodes.py
View file @
1e94a834
...
@@ -3864,6 +3864,106 @@ class SimpleCallNode(CallNode):
...
@@ -3864,6 +3864,106 @@ class SimpleCallNode(CallNode):
code
.
funcstate
.
release_temp
(
self
.
opt_arg_struct
)
code
.
funcstate
.
release_temp
(
self
.
opt_arg_struct
)
class
InlinedDefNodeCallNode
(
CallNode
):
# Inline call to defnode
#
# function PyCFunctionNode
# function_name NameNode
# args [ExprNode]
subexprs
=
[
'args'
,
'function_name'
]
is_temp
=
1
type
=
py_object_type
function
=
None
function_name
=
None
def
can_be_inlined
(
self
):
func_type
=
self
.
function
.
def_node
if
func_type
.
star_arg
or
func_type
.
starstar_arg
:
return
False
if
len
(
func_type
.
args
)
!=
len
(
self
.
args
):
return
False
return
True
def
analyse_types
(
self
,
env
):
self
.
function_name
.
analyse_types
(
env
)
for
arg
in
self
.
args
:
arg
.
analyse_types
(
env
)
func_type
=
self
.
function
.
def_node
actual_nargs
=
len
(
self
.
args
)
# Coerce arguments
some_args_in_temps
=
False
for
i
in
xrange
(
actual_nargs
):
formal_type
=
func_type
.
args
[
i
].
type
arg
=
self
.
args
[
i
].
coerce_to
(
formal_type
,
env
)
if
arg
.
is_temp
:
if
i
>
0
:
# first argument in temp doesn't impact subsequent arguments
some_args_in_temps
=
True
elif
arg
.
type
.
is_pyobject
and
not
env
.
nogil
:
if
arg
.
nonlocally_immutable
():
# plain local variables are ok
pass
else
:
# we do not safely own the argument's reference,
# but we must make sure it cannot be collected
# before we return from the function, so we create
# an owned temp reference to it
if
i
>
0
:
# first argument doesn't matter
some_args_in_temps
=
True
arg
=
arg
.
coerce_to_temp
(
env
)
self
.
args
[
i
]
=
arg
if
some_args_in_temps
:
# if some args are temps and others are not, they may get
# constructed in the wrong order (temps first) => make
# sure they are either all temps or all not temps (except
# for the last argument, which is evaluated last in any
# case)
for
i
in
xrange
(
actual_nargs
-
1
):
arg
=
self
.
args
[
i
]
if
arg
.
nonlocally_immutable
():
# locals, C functions, unassignable types are safe.
pass
elif
arg
.
type
.
is_cpp_class
:
# Assignment has side effects, avoid.
pass
elif
env
.
nogil
and
arg
.
type
.
is_pyobject
:
# can't copy a Python reference into a temp in nogil
# env (this is safe: a construction would fail in
# nogil anyway)
pass
else
:
#self.args[i] = arg.coerce_to_temp(env)
# instead: issue a warning
if
i
>
0
:
warning
(
arg
.
pos
,
"Argument evaluation order in C function call is undefined and may not be as expected"
,
0
)
break
def
generate_result_code
(
self
,
code
):
arg_code
=
[
self
.
function_name
.
py_result
()]
func_type
=
self
.
function
.
def_node
for
arg
,
proto_arg
in
zip
(
self
.
args
,
func_type
.
args
):
if
arg
.
type
.
is_pyobject
:
if
proto_arg
.
hdr_type
:
arg_code
.
append
(
arg
.
result_as
(
proto_arg
.
hdr_type
))
else
:
arg_code
.
append
(
arg
.
result_as
(
proto_arg
.
type
))
else
:
arg_code
.
append
(
arg
.
result
())
arg_code
=
', '
.
join
(
arg_code
)
code
.
putln
(
"%s = %s(%s); %s"
%
(
self
.
result
(),
self
.
function
.
def_node
.
entry
.
pyfunc_cname
,
arg_code
,
code
.
error_goto_if_null
(
self
.
result
(),
self
.
pos
)))
code
.
put_gotref
(
self
.
py_result
())
class
PythonCapiFunctionNode
(
ExprNode
):
class
PythonCapiFunctionNode
(
ExprNode
):
subexprs
=
[]
subexprs
=
[]
def
__init__
(
self
,
pos
,
py_name
,
cname
,
func_type
,
utility_code
=
None
):
def
__init__
(
self
,
pos
,
py_name
,
cname
,
func_type
,
utility_code
=
None
):
...
...
Cython/Compiler/Optimize.py
View file @
1e94a834
...
@@ -1643,6 +1643,28 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
...
@@ -1643,6 +1643,28 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
return
node
return
node
return
kwargs
return
kwargs
class
InlineDefNodeCalls
(
Visitor
.
CythonTransform
):
visit_Node
=
Visitor
.
VisitorTransform
.
recurse_to_children
def
visit_SimpleCallNode
(
self
,
node
):
self
.
visitchildren
(
node
)
if
not
self
.
current_directives
.
get
(
'optimize.inline_defnode_calls'
):
return
node
function_name
=
node
.
function
if
not
function_name
.
is_name
:
return
node
if
len
(
function_name
.
cf_state
)
!=
1
:
return
node
function
=
list
(
function_name
.
cf_state
)[
0
].
rhs
if
not
isinstance
(
function
,
ExprNodes
.
PyCFunctionNode
):
return
node
inlined
=
ExprNodes
.
InlinedDefNodeCallNode
(
node
.
pos
,
function_name
=
function_name
,
function
=
function
,
args
=
node
.
args
)
if
inlined
.
can_be_inlined
():
return
inlined
return
node
class
OptimizeBuiltinCalls
(
Visitor
.
EnvTransform
):
class
OptimizeBuiltinCalls
(
Visitor
.
EnvTransform
):
"""Optimize some common methods calls and instantiation patterns
"""Optimize some common methods calls and instantiation patterns
...
...
Cython/Compiler/Options.py
View file @
1e94a834
...
@@ -106,6 +106,9 @@ directive_defaults = {
...
@@ -106,6 +106,9 @@ directive_defaults = {
'warn.unused_arg'
:
False
,
'warn.unused_arg'
:
False
,
'warn.unused_result'
:
False
,
'warn.unused_result'
:
False
,
# optimizations
'optimize.inline_defnode_calls'
:
False
,
# remove unreachable code
# remove unreachable code
'remove_unreachable'
:
True
,
'remove_unreachable'
:
True
,
...
...
Cython/Compiler/Pipeline.py
View file @
1e94a834
...
@@ -137,6 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()):
...
@@ -137,6 +137,7 @@ def create_pipeline(context, mode, exclude_classes=()):
from
AutoDocTransforms
import
EmbedSignature
from
AutoDocTransforms
import
EmbedSignature
from
Optimize
import
FlattenInListTransform
,
SwitchTransform
,
IterationTransform
from
Optimize
import
FlattenInListTransform
,
SwitchTransform
,
IterationTransform
from
Optimize
import
EarlyReplaceBuiltinCalls
,
OptimizeBuiltinCalls
from
Optimize
import
EarlyReplaceBuiltinCalls
,
OptimizeBuiltinCalls
from
Optimize
import
InlineDefNodeCalls
from
Optimize
import
ConstantFolding
,
FinalOptimizePhase
from
Optimize
import
ConstantFolding
,
FinalOptimizePhase
from
Optimize
import
DropRefcountingTransform
from
Optimize
import
DropRefcountingTransform
from
Buffer
import
IntroduceBufferAuxiliaryVars
from
Buffer
import
IntroduceBufferAuxiliaryVars
...
@@ -185,6 +186,7 @@ def create_pipeline(context, mode, exclude_classes=()):
...
@@ -185,6 +186,7 @@ def create_pipeline(context, mode, exclude_classes=()):
MarkOverflowingArithmetic
(
context
),
MarkOverflowingArithmetic
(
context
),
IntroduceBufferAuxiliaryVars
(
context
),
IntroduceBufferAuxiliaryVars
(
context
),
_check_c_declarations
,
_check_c_declarations
,
InlineDefNodeCalls
(
context
),
AnalyseExpressionsTransform
(
context
),
AnalyseExpressionsTransform
(
context
),
FindInvalidUseOfFusedTypes
(
context
),
FindInvalidUseOfFusedTypes
(
context
),
CreateClosureClasses
(
context
),
## After all lookups and type inference
CreateClosureClasses
(
context
),
## After all lookups and type inference
...
...
tests/run/closure_inlining.pyx
0 → 100644
View file @
1e94a834
# cython: optimize.inline_defnode_calls=True
# mode: run
cimport
cython
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
simple_noargs
():
"""
>>> simple_noargs()
123
"""
def
inner
():
return
123
return
inner
()
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
test_coerce
(
a
,
int
b
):
"""
>>> test_coerce(2, 2)
4
"""
def
inner
(
int
a
,
b
):
return
a
*
b
return
inner
(
a
,
b
)
cdef
class
Foo
(
object
):
def
__repr__
(
self
):
return
'<Foo>'
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
test_func_signature
(
a
):
"""
>>> test_func_signature(Foo())
<Foo>
"""
def
inner
(
Foo
a
):
return
a
return
inner
(
a
)
@
cython
.
test_fail_if_path_exists
(
'//SimpleCallNode'
)
@
cython
.
test_assert_path_exists
(
'//InlinedDefNodeCallNode'
)
def
test_func_signature2
(
a
,
b
):
"""
>>> test_func_signature2(Foo(), 123)
(<Foo>, 123)
"""
def
inner
(
Foo
a
,
b
):
return
a
,
b
return
inner
(
a
,
b
)
# Starred args and default values are not yet supported for inlining
@
cython
.
test_assert_path_exists
(
'//SimpleCallNode'
)
def
test_defaults
(
a
,
b
):
"""
>>> test_defaults(1, 2)
(1, 2, 123)
"""
def
inner
(
a
,
b
=
b
,
c
=
123
):
return
a
,
b
,
c
return
inner
(
a
)
@
cython
.
test_assert_path_exists
(
'//SimpleCallNode'
)
def
test_starred
(
a
):
"""
>>> test_starred(123)
(123, (), {})
"""
def
inner
(
a
,
*
args
,
**
kwargs
):
return
a
,
args
,
kwargs
return
inner
(
a
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment