From 3ff2d038912cba10f856f47cefbabf68f84cb098 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 31 Mar 2014 18:02:22 +0100 Subject: [PATCH] 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. --- py/compile.c | 23 ++++-- tests/basics/for2.py | 8 +++ tests/misc/features.py | 159 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 tests/basics/for2.py create mode 100644 tests/misc/features.py diff --git a/py/compile.c b/py/compile.c index 43253a926b..25830eb6f9 100644 --- a/py/compile.c +++ b/py/compile.c @@ -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 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) { START_BREAK_CONTINUE_BLOCK int top_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); - c_assign(comp, pn_var, ASSIGN_STORE); + EMIT(dup_top); EMIT_ARG(jump, entry_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_node(comp, pn_body); EMIT_ARG(label_assign, continue_label); - // compile: var += step - c_assign(comp, pn_var, ASSIGN_AUG_LOAD); + // compile: var + step, duplicated on stack + compile_node(comp, pn_var); compile_node(comp, pn_step); 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); // compile: if var end: goto top - compile_node(comp, pn_var); compile_node(comp, pn_end); assert(MP_PARSE_NODE_IS_SMALL_INT(pn_step)); 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); + // 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 END_BREAK_CONTINUE_BLOCK diff --git a/tests/basics/for2.py b/tests/basics/for2.py new file mode 100644 index 0000000000..62f056e760 --- /dev/null +++ b/tests/basics/for2.py @@ -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 diff --git a/tests/misc/features.py b/tests/misc/features.py new file mode 100644 index 0000000000..d2fd8865c3 --- /dev/null +++ b/tests/misc/features.py @@ -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())