-
Notifications
You must be signed in to change notification settings - Fork 198
Description
Given this input:
def is_even(n: int) -> int:
return (n & 1) == 0
#pythran export trailing_zero_bits(int)
def trailing_zero_bits(n: int) -> int:
result = 0
while is_even(n):
n >>= 1
result += 1
return result
pythran -P
produces
def is_even(n):
return ((n & 1) == 0)
def trailing_zero_bits(n):
n_ = n
result = 0
__pythran_inlineis_evenn0 = n_
while ((__pythran_inlineis_evenn0 & 1) == 0):
n_ >>= 1
result += 1
return result
The problem is the creation of the __pythran_inlineis_evenn0
, specifically the loop whose condition it is used in.
I suspect this catches a corner case on the function inliner: to avoid duplicate side effects from evaluating an expression every place the argument appears in the inlined function, it creates a variable to hold the value of that expression, assigns that value to that variable on the previous line, and uses the new variable in the inlined function body.
If the function is used anywhere other than an elif
or while
condition, this works very well. If it occurs in an elif
condition, one need merely move the assignment before the corresponding if
(and ensure there are no name collisions with functions inlined in the if
or preceeding elif
conditions) or separate out elif
into else
and if
, providing a space for the assignment to happen between the two.
If a function is to be inlined into a while
condition, the assignment must be duplicated: placed not merely on the previous line, but also as the last line of the while loop body. It seems pythran 0.17.0, at least on Cygwin, does not perform that second assignment.
I don't know enough of the pythran internals to be sure where in the optimizers the inliner lives, nor with Python ASTs to be able to insert the second assignment at the required location.