py/parse: Allow const types other than int to optimise as true/false.
Allows optimisation of cases like: import micropython _DEBUG = micropython.const(False) if _DEBUG: print('Debugging info') Previously the 'if' statement was only optimised out if the type of the const() argument was integer. The change is implemented in a way that makes the compiler slightly smaller (-16 bytes on PYBV11) but compilation will also be very slightly slower. As a bonus, if const support is enabled then the compiler can now optimise const truthy/falsey expressions of other types, like: while "something": pass ... unclear if that is useful, but perhaps it could be. Signed-off-by: Angus Gratton <angus@redyak.com.au>
This commit is contained in:
parent
f91ebf6fa9
commit
25ff5b52d9
28
py/parse.c
28
py/parse.c
@ -334,16 +334,6 @@ STATIC uint8_t peek_rule(parser_t *parser, size_t n) {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool mp_parse_node_is_const_false(mp_parse_node_t pn) {
|
||||
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, MP_TOKEN_KW_FALSE)
|
||||
|| (MP_PARSE_NODE_IS_SMALL_INT(pn) && MP_PARSE_NODE_LEAF_SMALL_INT(pn) == 0);
|
||||
}
|
||||
|
||||
bool mp_parse_node_is_const_true(mp_parse_node_t pn) {
|
||||
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) != 0);
|
||||
}
|
||||
|
||||
bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) {
|
||||
if (MP_PARSE_NODE_IS_SMALL_INT(pn)) {
|
||||
*o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn));
|
||||
@ -427,6 +417,24 @@ STATIC mp_obj_t mp_parse_node_convert_to_obj(mp_parse_node_t pn) {
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC bool parse_node_is_const_bool(mp_parse_node_t pn, bool value) {
|
||||
// Returns true if 'pn' is a constant whose boolean value is equivalent to 'value'
|
||||
#if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST
|
||||
return mp_parse_node_is_const(pn) && mp_obj_is_true(mp_parse_node_convert_to_obj(pn)) == value;
|
||||
#else
|
||||
return MP_PARSE_NODE_IS_TOKEN_KIND(pn, value ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE)
|
||||
|| (MP_PARSE_NODE_IS_SMALL_INT(pn) && !!MP_PARSE_NODE_LEAF_SMALL_INT(pn) == value);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool mp_parse_node_is_const_false(mp_parse_node_t pn) {
|
||||
return parse_node_is_const_bool(pn, false);
|
||||
}
|
||||
|
||||
bool mp_parse_node_is_const_true(mp_parse_node_t pn) {
|
||||
return parse_node_is_const_bool(pn, true);
|
||||
}
|
||||
|
||||
size_t mp_parse_node_extract_list(mp_parse_node_t *pn, size_t pn_kind, mp_parse_node_t **nodes) {
|
||||
if (MP_PARSE_NODE_IS_NULL(*pn)) {
|
||||
*nodes = NULL;
|
||||
|
69
tests/cmdline/cmd_showbc_const.py
Normal file
69
tests/cmdline/cmd_showbc_const.py
Normal file
@ -0,0 +1,69 @@
|
||||
# cmdline: -v -v
|
||||
# Test constant-related bytecode optimisations
|
||||
# (constant folding, compile-time if/while evaluation, etc.)
|
||||
from micropython import const
|
||||
import sys
|
||||
|
||||
try:
|
||||
sys.settrace
|
||||
# if MICROPY_PY_SYS_SETTRACE is enabled, compile-time const optimizations
|
||||
# are disabled so the bytecode output is very different
|
||||
print("SKIP")
|
||||
raise SystemExit
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
_STR = const("foo")
|
||||
_EMPTY_TUPLE = const(())
|
||||
_TRUE = const(True)
|
||||
_FALSE = const(False)
|
||||
_SMALLINT = const(33)
|
||||
_ZERO = const(0)
|
||||
|
||||
# Bytecode generated for these if/while statements should contain no JUMP_IF
|
||||
# and no instances of string 'Eliminated'
|
||||
|
||||
if _STR or _EMPTY_TUPLE:
|
||||
print("Kept")
|
||||
if _STR and _EMPTY_TUPLE:
|
||||
print("Eliminated")
|
||||
if _TRUE:
|
||||
print("Kept")
|
||||
if _SMALLINT:
|
||||
print("Kept")
|
||||
if _ZERO and _SMALLINT:
|
||||
print("Eliminated")
|
||||
if _FALSE:
|
||||
print("Eliminated")
|
||||
|
||||
while _SMALLINT:
|
||||
print("Kept")
|
||||
break
|
||||
while _ZERO:
|
||||
print("Eliminated")
|
||||
while _FALSE:
|
||||
print("Eliminated")
|
||||
|
||||
# These values are stored in variables, and therefore bytecode will contain JUMP_IF
|
||||
|
||||
a = _EMPTY_TUPLE or _STR
|
||||
if a == _STR:
|
||||
print("Kept")
|
||||
|
||||
b = _SMALLINT and _STR
|
||||
if b == _STR:
|
||||
print("Kept")
|
||||
|
||||
# The compiler is also unable to optimise these expressions, even though the arguments are const,
|
||||
# so these also contain JUMP_IF
|
||||
|
||||
if (_EMPTY_TUPLE or _STR) == _STR:
|
||||
print("Kept")
|
||||
|
||||
if (_EMPTY_TUPLE and _STR) == _STR:
|
||||
print("Not Eliminated")
|
||||
|
||||
if (not _STR) == _FALSE:
|
||||
print("Kept")
|
||||
|
||||
assert True
|
160
tests/cmdline/cmd_showbc_const.py.exp
Normal file
160
tests/cmdline/cmd_showbc_const.py.exp
Normal file
@ -0,0 +1,160 @@
|
||||
File cmdline/cmd_showbc_const.py, code block '<module>' (descriptor: \.\+, bytecode @\.\+ 198 bytes)
|
||||
Raw bytecode (code_info_size=40, bytecode_size=158):
|
||||
2c 4c 01 60 2c 46 22 65 27 4a 83 0c 20 27 40 20
|
||||
27 20 27 40 60 20 27 24 40 60 40 24 27 47 24 27
|
||||
67 40 27 47 27 47 26 47 80 10 02 2a 01 1b 03 1c
|
||||
02 16 02 59 80 51 1b 04 16 04 48 0f 11 04 13 05
|
||||
59 11 09 10 06 34 01 59 11 0a 65 57 11 0b df 44
|
||||
43 59 4a 01 5d 11 09 10 07 34 01 59 11 09 10 07
|
||||
34 01 59 11 09 10 07 34 01 59 11 09 10 07 34 01
|
||||
59 42 42 42 35 23 00 16 0c 11 0c 23 00 d9 44 47
|
||||
11 09 10 07 34 01 59 23 00 16 0d 11 0d 23 00 d9
|
||||
44 47 11 09 10 07 34 01 59 23 00 23 00 d9 44 47
|
||||
11 09 10 07 34 01 59 23 01 23 00 d9 44 47 11 09
|
||||
23 02 34 01 59 50 23 03 d9 44 47 11 09 10 07 34
|
||||
01 59 42 40 51 63
|
||||
arg names:
|
||||
(N_STATE 6)
|
||||
(N_EXC_STACK 1)
|
||||
bc=0 line=1
|
||||
bc=0 line=4
|
||||
bc=12 line=5
|
||||
bc=18 line=7
|
||||
bc=20 line=8
|
||||
bc=25 line=11
|
||||
bc=32 line=12
|
||||
bc=42 line=14
|
||||
bc=45 line=26
|
||||
bc=45 line=27
|
||||
bc=52 line=28
|
||||
bc=52 line=30
|
||||
bc=52 line=31
|
||||
bc=59 line=32
|
||||
bc=59 line=33
|
||||
bc=66 line=34
|
||||
bc=66 line=36
|
||||
bc=66 line=39
|
||||
bc=66 line=40
|
||||
bc=73 line=41
|
||||
bc=77 line=42
|
||||
bc=77 line=44
|
||||
bc=77 line=47
|
||||
bc=77 line=49
|
||||
bc=81 line=50
|
||||
bc=88 line=51
|
||||
bc=95 line=53
|
||||
bc=99 line=54
|
||||
bc=106 line=55
|
||||
bc=113 line=58
|
||||
bc=113 line=60
|
||||
bc=120 line=61
|
||||
bc=127 line=63
|
||||
bc=134 line=64
|
||||
bc=141 line=66
|
||||
bc=147 line=67
|
||||
bc=154 line=69
|
||||
00 LOAD_CONST_SMALL_INT 0
|
||||
01 LOAD_CONST_STRING 'const'
|
||||
03 BUILD_TUPLE 1
|
||||
05 IMPORT_NAME 'micropython'
|
||||
07 IMPORT_FROM 'const'
|
||||
09 STORE_NAME const
|
||||
11 POP_TOP
|
||||
12 LOAD_CONST_SMALL_INT 0
|
||||
13 LOAD_CONST_NONE
|
||||
14 IMPORT_NAME 'sys'
|
||||
16 STORE_NAME sys
|
||||
18 SETUP_EXCEPT 35
|
||||
20 LOAD_NAME sys
|
||||
22 LOAD_ATTR settrace
|
||||
24 POP_TOP
|
||||
25 LOAD_NAME print
|
||||
27 LOAD_CONST_STRING 'SKIP'
|
||||
29 CALL_FUNCTION n=1 nkw=0
|
||||
31 POP_TOP
|
||||
32 LOAD_NAME SystemExit
|
||||
34 RAISE_OBJ
|
||||
35 DUP_TOP
|
||||
36 LOAD_NAME AttributeError
|
||||
38 BINARY_OP 8
|
||||
39 POP_JUMP_IF_FALSE 44
|
||||
41 POP_TOP
|
||||
42 POP_EXCEPT_JUMP 45
|
||||
44 END_FINALLY
|
||||
45 LOAD_NAME print
|
||||
47 LOAD_CONST_STRING 'Kept'
|
||||
49 CALL_FUNCTION n=1 nkw=0
|
||||
51 POP_TOP
|
||||
52 LOAD_NAME print
|
||||
54 LOAD_CONST_STRING 'Kept'
|
||||
56 CALL_FUNCTION n=1 nkw=0
|
||||
58 POP_TOP
|
||||
59 LOAD_NAME print
|
||||
61 LOAD_CONST_STRING 'Kept'
|
||||
63 CALL_FUNCTION n=1 nkw=0
|
||||
65 POP_TOP
|
||||
66 LOAD_NAME print
|
||||
68 LOAD_CONST_STRING 'Kept'
|
||||
70 CALL_FUNCTION n=1 nkw=0
|
||||
72 POP_TOP
|
||||
73 JUMP 77
|
||||
75 JUMP 66
|
||||
77 LOAD_CONST_OBJ \.\+='foo'
|
||||
79 STORE_NAME a
|
||||
81 LOAD_NAME a
|
||||
83 LOAD_CONST_OBJ \.\+='foo'
|
||||
85 BINARY_OP 2 __eq__
|
||||
86 POP_JUMP_IF_FALSE 95
|
||||
88 LOAD_NAME print
|
||||
90 LOAD_CONST_STRING 'Kept'
|
||||
92 CALL_FUNCTION n=1 nkw=0
|
||||
94 POP_TOP
|
||||
95 LOAD_CONST_OBJ \.\+='foo'
|
||||
97 STORE_NAME b
|
||||
99 LOAD_NAME b
|
||||
101 LOAD_CONST_OBJ \.\+='foo'
|
||||
103 BINARY_OP 2 __eq__
|
||||
104 POP_JUMP_IF_FALSE 113
|
||||
106 LOAD_NAME print
|
||||
108 LOAD_CONST_STRING 'Kept'
|
||||
110 CALL_FUNCTION n=1 nkw=0
|
||||
112 POP_TOP
|
||||
113 LOAD_CONST_OBJ \.\+='foo'
|
||||
115 LOAD_CONST_OBJ \.\+='foo'
|
||||
117 BINARY_OP 2 __eq__
|
||||
118 POP_JUMP_IF_FALSE 127
|
||||
120 LOAD_NAME print
|
||||
122 LOAD_CONST_STRING 'Kept'
|
||||
124 CALL_FUNCTION n=1 nkw=0
|
||||
126 POP_TOP
|
||||
127 LOAD_CONST_OBJ \.\+=()
|
||||
129 LOAD_CONST_OBJ \.\+='foo'
|
||||
131 BINARY_OP 2 __eq__
|
||||
132 POP_JUMP_IF_FALSE 141
|
||||
134 LOAD_NAME print
|
||||
136 LOAD_CONST_OBJ \.\+='Not Eliminated'
|
||||
138 CALL_FUNCTION n=1 nkw=0
|
||||
140 POP_TOP
|
||||
141 LOAD_CONST_FALSE
|
||||
142 LOAD_CONST_OBJ \.\+=False
|
||||
144 BINARY_OP 2 __eq__
|
||||
145 POP_JUMP_IF_FALSE 154
|
||||
147 LOAD_NAME print
|
||||
149 LOAD_CONST_STRING 'Kept'
|
||||
151 CALL_FUNCTION n=1 nkw=0
|
||||
153 POP_TOP
|
||||
154 JUMP 156
|
||||
156 LOAD_CONST_NONE
|
||||
157 RETURN_VALUE
|
||||
Kept
|
||||
Kept
|
||||
Kept
|
||||
Kept
|
||||
Kept
|
||||
Kept
|
||||
Kept
|
||||
Kept
|
||||
mem: total=\\d\+, current=\\d\+, peak=\\d\+
|
||||
stack: \\d\+ out of \\d\+
|
||||
GC: total: \\d\+, used: \\d\+, free: \\d\+
|
||||
No. of 1-blocks: \\d\+, 2-blocks: \\d\+, max blk sz: \\d\+, max free sz: \\d\+
|
Loading…
Reference in New Issue
Block a user