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
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
a9072286
Commit
a9072286
authored
Sep 27, 2011
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
avoid unnecessary None checks based on control flow analysis (ticket #743)
parent
8ddf15a3
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
119 additions
and
10 deletions
+119
-10
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+10
-0
Cython/Compiler/FlowControl.py
Cython/Compiler/FlowControl.py
+16
-8
Cython/Compiler/Optimize.py
Cython/Compiler/Optimize.py
+9
-0
tests/run/cf_none.pyx
tests/run/cf_none.pyx
+84
-0
tests/run/list.pyx
tests/run/list.pyx
+0
-2
No files found.
Cython/Compiler/ExprNodes.py
View file @
a9072286
...
...
@@ -1481,6 +1481,16 @@ class NameNode(AtomicExprNode):
# If it's not a C variable, it'll be in a temp.
return
1
def
may_be_none
(
self
):
if
self
.
cf_state
:
# evaluate control flow state to see if there were any
# potential None values assigned to the node so far
for
assignment
in
self
.
cf_state
:
if
assignment
.
rhs
.
may_be_none
():
return
True
return
False
return
super
(
NameNode
,
self
).
may_be_none
()
def
nonlocally_immutable
(
self
):
if
ExprNode
.
nonlocally_immutable
(
self
):
return
True
...
...
Cython/Compiler/FlowControl.py
View file @
a9072286
...
...
@@ -14,11 +14,16 @@ from Visitor import TreeVisitor, CythonTransform
from
Errors
import
error
,
warning
,
CompileError
,
InternalError
class
TypedExprNode
(
ExprNodes
.
ExprNode
):
# Used for declaring assignments of a specified type w
h
ithout a known entry.
def
__init__
(
self
,
type
):
# Used for declaring assignments of a specified type without a known entry.
def
__init__
(
self
,
type
,
may_be_none
=
None
):
self
.
type
=
type
self
.
_may_be_none
=
may_be_none
object_expr
=
TypedExprNode
(
py_object_type
)
def
may_be_none
(
self
):
return
self
.
_may_be_none
!=
False
object_expr
=
TypedExprNode
(
py_object_type
,
may_be_none
=
True
)
object_expr_not_none
=
TypedExprNode
(
py_object_type
,
may_be_none
=
False
)
class
ControlBlock
(
object
):
"""Control flow graph node. Sequence of assignments and name references.
...
...
@@ -588,11 +593,13 @@ class CreateControlFlowGraph(CythonTransform):
if
node
.
star_arg
:
self
.
flow
.
mark_argument
(
node
.
star_arg
,
TypedExprNode
(
Builtin
.
tuple_type
),
TypedExprNode
(
Builtin
.
tuple_type
,
may_be_none
=
False
),
node
.
star_arg
.
entry
)
if
node
.
starstar_arg
:
self
.
flow
.
mark_argument
(
node
.
starstar_arg
,
TypedExprNode
(
Builtin
.
dict_type
),
TypedExprNode
(
Builtin
.
dict_type
,
may_be_none
=
False
),
node
.
starstar_arg
.
entry
)
self
.
visitchildren
(
node
)
# Workaround for generators
...
...
@@ -621,7 +628,7 @@ class CreateControlFlowGraph(CythonTransform):
if
entry
.
is_anonymous
:
entry
=
self
.
env
.
lookup
(
node
.
name
)
if
entry
:
self
.
flow
.
mark_assignment
(
node
,
object_expr
,
entry
)
self
.
flow
.
mark_assignment
(
node
,
object_expr
_not_none
,
entry
)
return
self
.
visit_FuncDefNode
(
node
)
def
visit_GeneratorBodyDefNode
(
self
,
node
):
...
...
@@ -717,7 +724,8 @@ class CreateControlFlowGraph(CythonTransform):
def
visit_CArgDeclNode
(
self
,
node
):
entry
=
self
.
env
.
lookup
(
node
.
name
)
if
entry
:
self
.
flow
.
mark_argument
(
node
,
TypedExprNode
(
entry
.
type
),
entry
)
may_be_none
=
not
node
.
not_none
self
.
flow
.
mark_argument
(
node
,
TypedExprNode
(
entry
.
type
,
may_be_none
),
entry
)
return
node
def
visit_NameNode
(
self
,
node
):
...
...
@@ -1091,7 +1099,7 @@ class CreateControlFlowGraph(CythonTransform):
def
visit_PyClassDefNode
(
self
,
node
):
self
.
flow
.
mark_assignment
(
node
.
target
,
object_expr
,
self
.
env
.
lookup
(
node
.
name
))
object_expr
_not_none
,
self
.
env
.
lookup
(
node
.
name
))
# TODO: add negative attribute list to "visitchildren"?
self
.
visitchildren
(
node
,
attrs
=
[
'dict'
,
'metaclass'
,
'mkw'
,
'bases'
,
'classobj'
])
...
...
Cython/Compiler/Optimize.py
View file @
a9072286
...
...
@@ -3530,3 +3530,12 @@ class FinalOptimizePhase(Visitor.CythonTransform):
if
not
node
.
arg
.
may_be_none
():
node
.
notnone
=
True
return
node
def
visit_NoneCheckNode
(
self
,
node
):
"""Remove None checks from expressions that definitely do not
carry a None value.
"""
self
.
visitchildren
(
node
)
if
not
node
.
arg
.
may_be_none
():
return
node
.
arg
return
node
tests/run/cf_none.pyx
0 → 100644
View file @
a9072286
cimport
cython
@
cython
.
test_fail_if_path_exists
(
'//NoneCheckNode'
)
def
none_checks
(
a
):
"""
>>> none_checks(1)
22
>>> none_checks(None)
True
"""
c
=
None
d
=
{
11
:
22
}
if
a
is
c
:
return
True
else
:
return
d
.
get
(
11
)
@
cython
.
test_assert_path_exists
(
'//NoneCheckNode'
)
def
dict_arg
(
dict
a
):
"""
>>> dict_arg({})
>>> dict_arg({1:2})
2
"""
return
a
.
get
(
1
)
@
cython
.
test_fail_if_path_exists
(
'//NoneCheckNode'
)
def
dict_arg_not_none
(
dict
a
not
None
):
"""
>>> dict_arg_not_none({})
>>> dict_arg_not_none({1:2})
2
"""
return
a
.
get
(
1
)
@
cython
.
test_assert_path_exists
(
'//NoneCheckNode'
)
def
reassignment
(
dict
d
):
"""
>>> reassignment({})
(None, 2)
>>> reassignment({1:3})
(3, 2)
"""
a
=
d
.
get
(
1
)
d
=
{
1
:
2
}
b
=
d
.
get
(
1
)
return
a
,
b
@
cython
.
test_fail_if_path_exists
(
'//NoneCheckNode'
)
def
conditional
(
a
):
"""
>>> conditional(True)
2
>>> conditional(False)
3
"""
if
a
:
d
=
{
1
:
2
}
else
:
d
=
{
1
:
3
}
return
d
.
get
(
1
)
@
cython
.
test_assert_path_exists
(
'//NoneCheckNode'
)
def
conditional_arg
(
a
,
dict
d
):
"""
>>> conditional_arg(True, {1:2})
>>> conditional_arg(False, {1:2})
2
"""
if
a
:
d
=
{}
return
d
.
get
(
1
)
@
cython
.
test_fail_if_path_exists
(
'//NoneCheckNode'
)
def
conditional_not_none
(
a
,
dict
d
not
None
):
"""
>>> conditional_not_none(True, {1:2})
>>> conditional_not_none(False, {1:2})
2
"""
if
a
:
d
=
{}
return
d
.
get
(
1
)
tests/run/list.pyx
View file @
a9072286
...
...
@@ -57,7 +57,6 @@ def test_list_sort_reversed():
l1
.
sort
(
reversed
=
True
)
return
l1
@
cython
.
test_assert_path_exists
(
"//SimpleCallNode//NoneCheckNode"
)
def
test_list_reverse
():
"""
>>> test_list_reverse()
...
...
@@ -68,7 +67,6 @@ def test_list_reverse():
l1
.
reverse
()
return
l1
@
cython
.
test_assert_path_exists
(
"//SimpleCallNode//NoneCheckNode"
)
def
test_list_append
():
"""
>>> test_list_append()
...
...
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