py: Fix bug in optimised for .. range.

Don't store final, failing value to the loop variable.  This fix also
makes for .. range a bit more efficient, as it uses less store/load
pairs for the loop variable.
This commit is contained in:
Damien George 2014-03-31 18:02:22 +01:00
parent 8cd72bdf92
commit 3ff2d03891
3 changed files with 183 additions and 7 deletions

View File

@ -1534,35 +1534,41 @@ void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
} }
// TODO preload end and step onto stack if they are not constants // TODO preload end and step onto stack if they are not constants
// TODO check if step is negative and do opposite test // Note that, as per semantics of for .. range, the final failing value should not be stored in the loop variable
// And, if the loop never runs, the loop variable should never be assigned
void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var, mp_parse_node_t pn_start, mp_parse_node_t pn_end, mp_parse_node_t pn_step, mp_parse_node_t pn_body, mp_parse_node_t pn_else) { void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var, mp_parse_node_t pn_start, mp_parse_node_t pn_end, mp_parse_node_t pn_step, mp_parse_node_t pn_body, mp_parse_node_t pn_else) {
START_BREAK_CONTINUE_BLOCK START_BREAK_CONTINUE_BLOCK
int top_label = comp_next_label(comp); int top_label = comp_next_label(comp);
int entry_label = comp_next_label(comp); int entry_label = comp_next_label(comp);
// compile: var = start // compile: start, duplicated on stack
compile_node(comp, pn_start); compile_node(comp, pn_start);
c_assign(comp, pn_var, ASSIGN_STORE); EMIT(dup_top);
EMIT_ARG(jump, entry_label); EMIT_ARG(jump, entry_label);
EMIT_ARG(label_assign, top_label); EMIT_ARG(label_assign, top_label);
// at this point we actually have 1 less element on the stack
EMIT_ARG(set_stack_size, EMIT(get_stack_size) - 1);
// store next value to var
c_assign(comp, pn_var, ASSIGN_STORE);
// compile body // compile body
compile_node(comp, pn_body); compile_node(comp, pn_body);
EMIT_ARG(label_assign, continue_label); EMIT_ARG(label_assign, continue_label);
// compile: var += step // compile: var + step, duplicated on stack
c_assign(comp, pn_var, ASSIGN_AUG_LOAD); compile_node(comp, pn_var);
compile_node(comp, pn_step); compile_node(comp, pn_step);
EMIT_ARG(binary_op, MP_BINARY_OP_INPLACE_ADD); EMIT_ARG(binary_op, MP_BINARY_OP_INPLACE_ADD);
c_assign(comp, pn_var, ASSIGN_AUG_STORE); EMIT(dup_top);
EMIT_ARG(label_assign, entry_label); EMIT_ARG(label_assign, entry_label);
// compile: if var <cond> end: goto top // compile: if var <cond> end: goto top
compile_node(comp, pn_var);
compile_node(comp, pn_end); compile_node(comp, pn_end);
assert(MP_PARSE_NODE_IS_SMALL_INT(pn_step)); assert(MP_PARSE_NODE_IS_SMALL_INT(pn_step));
if (MP_PARSE_NODE_LEAF_SMALL_INT(pn_step) >= 0) { if (MP_PARSE_NODE_LEAF_SMALL_INT(pn_step) >= 0) {
@ -1572,6 +1578,9 @@ void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var,
} }
EMIT_ARG(pop_jump_if_true, top_label); EMIT_ARG(pop_jump_if_true, top_label);
// discard final value of var that failed the loop condition
EMIT(pop_top);
// break/continue apply to outer loop (if any) in the else block // break/continue apply to outer loop (if any) in the else block
END_BREAK_CONTINUE_BLOCK END_BREAK_CONTINUE_BLOCK

8
tests/basics/for2.py Normal file
View File

@ -0,0 +1,8 @@
i = 'init'
for i in range(0):
pass
print(i) # should not have been modified
for i in range(10):
pass
print(i) # should be last successful value of loop

159
tests/misc/features.py Normal file
View File

@ -0,0 +1,159 @@
# mad.py
# Alf Clement 27-Mar-2014
#
zero=0
three=3
print("1")
print("2")
print(three)
print("{}".format(4))
five=25/5
print(int(five))
j=0
for i in range(4):
j += i
print(j)
print(3+4)
try:
a=4/zero
except:
print(8)
print("xxxxxxxxx".count("x"))
def ten():
return 10
print(ten())
a=[]
for i in range(13):
a.append(i)
print(a[11])
print(a[-1])
str="0123456789"
print(str[1]+str[3])
def p(s):
print(s)
p("14")
p(15)
class A:
def __init__(self):
self.a=16
def print(self):
print(self.a)
def set(self,b):
self.a=b
a=A()
a.print()
a.set(17)
a.print()
b=A()
b.set(a.a + 1)
b.print()
for i in range(20):
pass
print(i)
if 20 > 30:
a="1"
else:
a="2"
if 0 < 4:
print(a+"0")
else:
print(a+"1")
a=[20,21,22,23,24]
for i in a:
if i < 21:
continue
if i > 21:
break
print(i)
b=[a,a,a]
print(b[1][2])
print(161//7)
a=24
while True:
try:
def gcheck():
global a
print(a)
gcheck()
class c25():
x=25
x=c25()
print(x.x)
raise
except:
print(26)
print(27+zero)
break
print(28)
k=29
def f():
global k
k = yield k
print(next(f()))
while True:
k+= 1
if k < 30:
continue
break
print(k)
for i in [1,2,3]:
class A():
def __init__(self, c):
self.a = i+10*c
b = A(3)
print(b.a)
print(34)
p=0
for i in range(35, -1, -1):
print(i)
p = p + 1
if p > 0:
break
p=36
while p == 36:
print(p)
p=37
print(p)
for i in [38]:
print(i)
print(int(exec("def foo(): return 38") == None)+foo())
d = {}
exec("def bar(): return 40", d)
print(d["bar"]())
def fib2(n):
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
print(fib2(100)[-2]-14)
Answer={}
Answer["ForAll"]=42
print(Answer["ForAll"])
i = 43
def f(i=i):
print(i)
i = 44
f()
print(i)
while True:
try:
if None != True:
print(45)
break
else:
print(0)
except:
print(0)
print(46)
print(46+1)
def u(p):
if p > 3:
return 3*p
else:
return u(2*p)-3*u(p)
print(u(16))
def u49():
return 49
print(u49())