Skip to content

Commit ffae48d

Browse files
committed
py, compiler: Add basic support for A=const(123).
You can now do: X = const(123) Y = const(456 + X) and the compiler will replace X and Y with their values. See discussion in issue adafruit#266 and issue adafruit#573.
1 parent c1c32d6 commit ffae48d

6 files changed

Lines changed: 91 additions & 38 deletions

File tree

py/compile.c

Lines changed: 71 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,77 @@ STATIC const mp_map_t mp_constants_map = {
115115
.table = (mp_map_elem_t*)mp_constants_table,
116116
};
117117

118-
STATIC mp_parse_node_t fold_constants(mp_parse_node_t pn) {
119-
if (MP_PARSE_NODE_IS_STRUCT(pn)) {
118+
// this function is essentially a simple preprocessor
119+
STATIC mp_parse_node_t fold_constants(compiler_t *comp, mp_parse_node_t pn, mp_map_t *consts) {
120+
if (0) {
121+
// dummy
122+
#if MICROPY_ENABLE_CONST
123+
} else if (MP_PARSE_NODE_IS_ID(pn)) {
124+
// lookup identifier in table of dynamic constants
125+
qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
126+
mp_map_elem_t *elem = mp_map_lookup(consts, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP);
127+
if (elem != NULL) {
128+
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, MP_OBJ_SMALL_INT_VALUE(elem->value));
129+
}
130+
#endif
131+
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
120132
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
121-
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
122133

123-
// fold arguments first
134+
// fold some parse nodes before folding their arguments
135+
switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
136+
#if MICROPY_ENABLE_CONST
137+
case PN_expr_stmt:
138+
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
139+
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
140+
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_expr_stmt_assign) {
141+
if (MP_PARSE_NODE_IS_ID(pns->nodes[0])
142+
&& MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_power)
143+
&& MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[0])
144+
&& MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[0]) == MP_QSTR_const
145+
&& MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[1], PN_trailer_paren)
146+
&& MP_PARSE_NODE_IS_NULL(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[2])
147+
) {
148+
// code to assign dynamic constants: id = const(value)
149+
150+
// get the id
151+
qstr id_qstr = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
152+
153+
// get the value
154+
mp_parse_node_t pn_value = ((mp_parse_node_struct_t*)((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[1])->nodes[0];
155+
pn_value = fold_constants(comp, pn_value, consts);
156+
if (!MP_PARSE_NODE_IS_SMALL_INT(pn_value)) {
157+
compile_syntax_error(comp, (mp_parse_node_t)pns, "constant must be an integer");
158+
break;
159+
}
160+
machine_int_t value = MP_PARSE_NODE_LEAF_SMALL_INT(pn_value);
161+
162+
// store the value in the table of dynamic constants
163+
mp_map_elem_t *elem = mp_map_lookup(consts, MP_OBJ_NEW_QSTR(id_qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
164+
if (elem->value != MP_OBJ_NULL) {
165+
compile_syntax_error(comp, (mp_parse_node_t)pns, "constant redefined");
166+
break;
167+
}
168+
elem->value = MP_OBJ_NEW_SMALL_INT(value);
169+
170+
// replace const(value) with value
171+
pns1->nodes[0] = pn_value;
172+
173+
// finished folding this assignment
174+
return pn;
175+
}
176+
}
177+
}
178+
break;
179+
#endif
180+
}
181+
182+
// fold arguments
183+
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
124184
for (int i = 0; i < n; i++) {
125-
pns->nodes[i] = fold_constants(pns->nodes[i]);
185+
pns->nodes[i] = fold_constants(comp, pns->nodes[i], consts);
126186
}
127187

128-
// now try to fold this parse node
188+
// try to fold this parse node
129189
switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
130190
case PN_atom_paren:
131191
if (n == 1 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0])) {
@@ -2045,36 +2105,7 @@ void compile_expr_stmt(compiler_t *comp, mp_parse_node_struct_t *pns) {
20452105
c_assign(comp, ((mp_parse_node_struct_t*)pns1->nodes[i])->nodes[0], ASSIGN_STORE); // middle store
20462106
}
20472107
} else if (kind == PN_expr_stmt_assign) {
2048-
if (0) {
2049-
// dummy
2050-
#if 0
2051-
// code to compile constants: id = const(...)
2052-
} else if (MP_PARSE_NODE_IS_ID(pns->nodes[0])
2053-
&& MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_power)
2054-
&& MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[0])
2055-
&& MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[0]) == MP_QSTR_const
2056-
&& MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[1], PN_trailer_paren)
2057-
&& MP_PARSE_NODE_IS_NULL(((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[2])
2058-
) {
2059-
if (comp->pass == MP_PASS_SCOPE) {
2060-
qstr const_id = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
2061-
2062-
if (!MP_PARSE_NODE_IS_SMALL_INT(((mp_parse_node_struct_t*)((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[1])->nodes[0])) {
2063-
compile_syntax_error(comp, (mp_parse_node_t)pns, "constant must be an integer");
2064-
}
2065-
machine_int_t value = MP_PARSE_NODE_LEAF_SMALL_INT(((mp_parse_node_struct_t*)((mp_parse_node_struct_t*)pns1->nodes[0])->nodes[1])->nodes[0]);
2066-
2067-
printf("assign const: %s = %ld\n", qstr_str(const_id), value);
2068-
mp_map_elem_t *elem = mp_map_lookup(&comp->module_consts, MP_OBJ_NEW_QSTR(const_id), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
2069-
if (elem->value != MP_OBJ_NULL) {
2070-
compile_syntax_error(comp, (mp_parse_node_t)pns, "constant redefined");
2071-
}
2072-
elem->value = MP_OBJ_NEW_SMALL_INT(value);
2073-
}
2074-
goto no_optimisation;
2075-
2076-
#endif
2077-
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
2108+
if (MP_PARSE_NODE_IS_STRUCT_KIND(pns1->nodes[0], PN_testlist_star_expr)
20782109
&& MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_star_expr)
20792110
&& MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns1->nodes[0]) == 2
20802111
&& MP_PARSE_NODE_STRUCT_NUM_NODES((mp_parse_node_struct_t*)pns->nodes[0]) == 2) {
@@ -3424,7 +3455,10 @@ mp_obj_t mp_compile(mp_parse_node_t pn, qstr source_file, uint emit_opt, bool is
34243455
comp->is_repl = is_repl;
34253456

34263457
// optimise constants
3427-
pn = fold_constants(pn);
3458+
mp_map_t consts;
3459+
mp_map_init(&consts, 0);
3460+
pn = fold_constants(comp, pn, &consts);
3461+
mp_map_deinit(&consts);
34283462

34293463
// set the outer scope
34303464
scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, pn, emit_opt);

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@
109109
/*****************************************************************************/
110110
/* Fine control over Python features */
111111

112+
// Whether to enable constant optimisation; id = const(value)
113+
#ifndef MICROPY_ENABLE_CONST
114+
#define MICROPY_ENABLE_CONST (1)
115+
#endif
116+
112117
// Whether to include the garbage collector
113118
#ifndef MICROPY_ENABLE_GC
114119
#define MICROPY_ENABLE_GC (0)

py/qstrdefs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Q(micropython)
6464
Q(byte_code)
6565
Q(native)
6666
Q(viper)
67+
Q(const)
6768

6869
#if MICROPY_EMIT_INLINE_THUMB
6970
Q(asm_thumb)

tests/micropython/const.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# test constant optimisation
2+
3+
X = const(123)
4+
Y = const(X + 456)
5+
6+
print(X, Y + 1)
7+
8+
def f():
9+
print(X, Y + 1)
10+
11+
f()

tests/micropython/const.py.exp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
123 580
2+
123 580

tests/run-tests

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def main():
112112
if len(args.files) == 0:
113113
if pyb is None:
114114
# run PC tests
115-
test_dirs = ('basics', 'float', 'import', 'io', 'misc')
115+
test_dirs = ('basics', 'micropython', 'float', 'import', 'io', 'misc')
116116
else:
117117
# run pyboard tests
118118
test_dirs = ('basics', 'float', 'pyb', 'pybnative', 'inlineasm')

0 commit comments

Comments
 (0)