Skip to content

Commit 391db86

Browse files
committed
py: Add more compiler optimisations for constant if/while conditions.
1 parent 235f9b3 commit 391db86

3 files changed

Lines changed: 103 additions & 30 deletions

File tree

py/compile.c

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -603,12 +603,13 @@ void compile_generic_tuple(compiler_t *comp, mp_parse_node_struct_t *pns) {
603603
}
604604

605605
STATIC bool node_is_const_false(mp_parse_node_t pn) {
606-
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE);
607-
// untested: || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0);
606+
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE)
607+
|| (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0);
608608
}
609609

610610
STATIC bool node_is_const_true(mp_parse_node_t pn) {
611-
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE) || (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 1);
611+
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_TRUE)
612+
|| (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) != 0);
612613
}
613614

614615
#if MICROPY_EMIT_CPYTHON
@@ -1658,24 +1659,30 @@ void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
16581659

16591660
uint l_end = comp_next_label(comp);
16601661

1661-
uint l_fail = comp_next_label(comp);
1662-
c_if_cond(comp, pns->nodes[0], false, l_fail); // if condition
1662+
// optimisation: don't emit anything when "if False" (not in CPython)
1663+
if (MICROPY_EMIT_CPYTHON || !node_is_const_false(pns->nodes[0])) {
1664+
uint l_fail = comp_next_label(comp);
1665+
c_if_cond(comp, pns->nodes[0], false, l_fail); // if condition
16631666

1664-
compile_node(comp, pns->nodes[1]); // if block
1667+
compile_node(comp, pns->nodes[1]); // if block
16651668

1666-
if (
1667-
#if !MICROPY_EMIT_CPYTHON
1668-
// optimisation to not jump over non-existent elif/else blocks (this optimisation is not in CPython)
1669-
!(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3])) &&
1670-
#endif
1671-
// optimisation to not jump if last instruction was return
1672-
!EMIT(last_emit_was_return_value)
1673-
) {
1674-
// jump over elif/else blocks
1675-
EMIT_ARG(jump, l_end);
1676-
}
1669+
// optimisation: skip everything else when "if True" (not in CPython)
1670+
if (!MICROPY_EMIT_CPYTHON && node_is_const_true(pns->nodes[0])) {
1671+
goto done;
1672+
}
16771673

1678-
EMIT_ARG(label_assign, l_fail);
1674+
if (
1675+
// optimisation: don't jump over non-existent elif/else blocks (not in CPython)
1676+
(MICROPY_EMIT_CPYTHON || !(MP_PARSE_NODE_IS_NULL(pns->nodes[2]) && MP_PARSE_NODE_IS_NULL(pns->nodes[3])))
1677+
// optimisation: don't jump if last instruction was return
1678+
&& !EMIT(last_emit_was_return_value)
1679+
) {
1680+
// jump over elif/else blocks
1681+
EMIT_ARG(jump, l_end);
1682+
}
1683+
1684+
EMIT_ARG(label_assign, l_fail);
1685+
}
16791686

16801687
// compile elif blocks (if any)
16811688
mp_parse_node_t *pn_elif;
@@ -1684,19 +1691,30 @@ void compile_if_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
16841691
assert(MP_PARSE_NODE_IS_STRUCT_KIND(pn_elif[i], PN_if_stmt_elif)); // should be
16851692
mp_parse_node_struct_t *pns_elif = (mp_parse_node_struct_t*)pn_elif[i];
16861693

1687-
l_fail = comp_next_label(comp);
1688-
c_if_cond(comp, pns_elif->nodes[0], false, l_fail); // elif condition
1694+
// optimisation: don't emit anything when "if False" (not in CPython)
1695+
if (MICROPY_EMIT_CPYTHON || !node_is_const_false(pns_elif->nodes[0])) {
1696+
uint l_fail = comp_next_label(comp);
1697+
c_if_cond(comp, pns_elif->nodes[0], false, l_fail); // elif condition
16891698

1690-
compile_node(comp, pns_elif->nodes[1]); // elif block
1691-
if (!EMIT(last_emit_was_return_value)) { // simple optimisation to align with CPython
1692-
EMIT_ARG(jump, l_end);
1699+
compile_node(comp, pns_elif->nodes[1]); // elif block
1700+
1701+
// optimisation: skip everything else when "elif True" (not in CPython)
1702+
if (!MICROPY_EMIT_CPYTHON && node_is_const_true(pns_elif->nodes[0])) {
1703+
goto done;
1704+
}
1705+
1706+
// optimisation: don't jump if last instruction was return
1707+
if (!EMIT(last_emit_was_return_value)) {
1708+
EMIT_ARG(jump, l_end);
1709+
}
1710+
EMIT_ARG(label_assign, l_fail);
16931711
}
1694-
EMIT_ARG(label_assign, l_fail);
16951712
}
16961713

16971714
// compile else block
16981715
compile_node(comp, pns->nodes[3]); // can be null
16991716

1717+
done:
17001718
EMIT_ARG(label_assign, l_end);
17011719
}
17021720

@@ -1735,12 +1753,16 @@ void compile_while_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
17351753
EMIT(pop_block);
17361754
}
17371755
#else
1738-
uint top_label = comp_next_label(comp);
1739-
EMIT_ARG(jump, continue_label);
1740-
EMIT_ARG(label_assign, top_label);
1741-
compile_node(comp, pns->nodes[1]); // body
1742-
EMIT_ARG(label_assign, continue_label);
1743-
c_if_cond(comp, pns->nodes[0], true, top_label); // condition
1756+
if (!node_is_const_false(pns->nodes[0])) { // optimisation: don't emit anything for "while False"
1757+
uint top_label = comp_next_label(comp);
1758+
if (!node_is_const_true(pns->nodes[0])) { // optimisation: don't jump to cond for "while True"
1759+
EMIT_ARG(jump, continue_label);
1760+
}
1761+
EMIT_ARG(label_assign, top_label);
1762+
compile_node(comp, pns->nodes[1]); // body
1763+
EMIT_ARG(label_assign, continue_label);
1764+
c_if_cond(comp, pns->nodes[0], true, top_label); // condition
1765+
}
17441766
#endif
17451767

17461768
// break/continue apply to outer loop (if any) in the else block

tests/basics/ifcond.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
# test if conditions which are optimised by the compiler
22

3+
if 0:
4+
print(5)
5+
else:
6+
print(6)
7+
8+
if 1:
9+
print(7)
10+
11+
if 2:
12+
print(8)
13+
14+
if -1:
15+
print(9)
16+
elif 1:
17+
print(10)
18+
19+
if 0:
20+
print(11)
21+
else:
22+
print(12)
23+
24+
if 0:
25+
print(13)
26+
elif 1:
27+
print(14)
28+
29+
if 0:
30+
print(15)
31+
elif 0:
32+
print(16)
33+
else:
34+
print(17)
35+
336
f2 = 0
437

538
def f(t1, t2, f1):

tests/basics/while_cond.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# test while conditions which are optimised by the compiler
2+
3+
while 0:
4+
print(0)
5+
else:
6+
print(1)
7+
8+
while 1:
9+
print(2)
10+
break
11+
12+
while 2:
13+
print(3)
14+
break
15+
16+
while -1:
17+
print(4)
18+
break

0 commit comments

Comments
 (0)