Skip to content

Commit 64f2b21

Browse files
committed
py: Move constant folding from compiler to parser.
It makes much more sense to do constant folding in the parser while the parse tree is being built. This eliminates the need to create parse nodes that will just be folded away. The code is slightly simpler and a bit smaller as well. Constant folding now has a configuration option, MICROPY_COMP_CONST_FOLDING, which is enabled by default.
1 parent 91fc075 commit 64f2b21

3 files changed

Lines changed: 286 additions & 264 deletions

File tree

py/compile.c

Lines changed: 0 additions & 241 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@
3333
#include "py/scope.h"
3434
#include "py/emit.h"
3535
#include "py/compile.h"
36-
#include "py/smallint.h"
3736
#include "py/runtime.h"
38-
#include "py/builtin.h"
3937

4038
// TODO need to mangle __attr names
4139

@@ -124,238 +122,6 @@ STATIC void compile_syntax_error(compiler_t *comp, mp_parse_node_t pn, const cha
124122
}
125123
}
126124

127-
#if MICROPY_COMP_MODULE_CONST
128-
STATIC const mp_map_elem_t mp_constants_table[] = {
129-
#if MICROPY_PY_UCTYPES
130-
{ MP_OBJ_NEW_QSTR(MP_QSTR_uctypes), (mp_obj_t)&mp_module_uctypes },
131-
#endif
132-
// Extra constants as defined by a port
133-
MICROPY_PORT_CONSTANTS
134-
};
135-
STATIC MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table);
136-
#endif
137-
138-
// this function is essentially a simple preprocessor
139-
STATIC mp_parse_node_t fold_constants(compiler_t *comp, mp_parse_node_t pn, mp_map_t *consts) {
140-
if (0) {
141-
// dummy
142-
#if MICROPY_COMP_CONST
143-
} else if (MP_PARSE_NODE_IS_ID(pn)) {
144-
// lookup identifier in table of dynamic constants
145-
qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
146-
mp_map_elem_t *elem = mp_map_lookup(consts, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP);
147-
if (elem != NULL) {
148-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, MP_OBJ_SMALL_INT_VALUE(elem->value));
149-
}
150-
#endif
151-
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
152-
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t*)pn;
153-
154-
// fold some parse nodes before folding their arguments
155-
switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
156-
#if MICROPY_COMP_CONST
157-
case PN_expr_stmt:
158-
if (!MP_PARSE_NODE_IS_NULL(pns->nodes[1])) {
159-
if (!(MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_expr_stmt_augassign)
160-
|| MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_expr_stmt_assign_list))) {
161-
// this node is of the form <x> = <y>
162-
if (MP_PARSE_NODE_IS_ID(pns->nodes[0])
163-
&& MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_power)
164-
&& MP_PARSE_NODE_IS_ID(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[0])
165-
&& MP_PARSE_NODE_LEAF_ARG(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[0]) == MP_QSTR_const
166-
&& MP_PARSE_NODE_IS_STRUCT_KIND(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[1], PN_trailer_paren)
167-
&& MP_PARSE_NODE_IS_NULL(((mp_parse_node_struct_t*)pns->nodes[1])->nodes[2])
168-
) {
169-
// code to assign dynamic constants: id = const(value)
170-
171-
// get the id
172-
qstr id_qstr = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
173-
174-
// get the value
175-
mp_parse_node_t pn_value = ((mp_parse_node_struct_t*)((mp_parse_node_struct_t*)pns->nodes[1])->nodes[1])->nodes[0];
176-
pn_value = fold_constants(comp, pn_value, consts);
177-
if (!MP_PARSE_NODE_IS_SMALL_INT(pn_value)) {
178-
compile_syntax_error(comp, (mp_parse_node_t)pns, "constant must be an integer");
179-
break;
180-
}
181-
mp_int_t value = MP_PARSE_NODE_LEAF_SMALL_INT(pn_value);
182-
183-
// store the value in the table of dynamic constants
184-
mp_map_elem_t *elem = mp_map_lookup(consts, MP_OBJ_NEW_QSTR(id_qstr), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
185-
if (elem->value != MP_OBJ_NULL) {
186-
compile_syntax_error(comp, (mp_parse_node_t)pns, "constant redefined");
187-
break;
188-
}
189-
elem->value = MP_OBJ_NEW_SMALL_INT(value);
190-
191-
// replace const(value) with value
192-
pns->nodes[1] = pn_value;
193-
194-
// finished folding this assignment
195-
return pn;
196-
}
197-
}
198-
}
199-
break;
200-
#endif
201-
case PN_string:
202-
case PN_bytes:
203-
case PN_const_object:
204-
return pn;
205-
}
206-
207-
// fold arguments
208-
int n = MP_PARSE_NODE_STRUCT_NUM_NODES(pns);
209-
for (int i = 0; i < n; i++) {
210-
pns->nodes[i] = fold_constants(comp, pns->nodes[i], consts);
211-
}
212-
213-
// try to fold this parse node
214-
switch (MP_PARSE_NODE_STRUCT_KIND(pns)) {
215-
case PN_atom_paren:
216-
if (n == 1 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0])) {
217-
// (int)
218-
pn = pns->nodes[0];
219-
}
220-
break;
221-
222-
case PN_expr:
223-
if (n == 2 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) {
224-
// int | int
225-
mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
226-
mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[1]);
227-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 | arg1);
228-
}
229-
break;
230-
231-
case PN_and_expr:
232-
if (n == 2 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) {
233-
// int & int
234-
mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
235-
mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[1]);
236-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 & arg1);
237-
}
238-
break;
239-
240-
case PN_shift_expr:
241-
if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
242-
mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
243-
mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]);
244-
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_LESS)) {
245-
// int << int
246-
if (!(arg1 >= (mp_int_t)BITS_PER_WORD || arg0 > (MP_SMALL_INT_MAX >> arg1) || arg0 < (MP_SMALL_INT_MIN >> arg1))) {
247-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 << arg1);
248-
}
249-
} else {
250-
assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_MORE)); // should be
251-
// int >> int
252-
if (arg1 >= (mp_int_t)BITS_PER_WORD) {
253-
// Shifting to big amounts is underfined behavior
254-
// in C and is CPU-dependent; propagate sign bit.
255-
arg1 = BITS_PER_WORD - 1;
256-
}
257-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0 >> arg1);
258-
}
259-
}
260-
break;
261-
262-
case PN_arith_expr:
263-
// overflow checking here relies on SMALL_INT being strictly smaller than mp_int_t
264-
if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
265-
mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
266-
mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]);
267-
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PLUS)) {
268-
// int + int
269-
arg0 += arg1;
270-
} else {
271-
assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_MINUS)); // should be
272-
// int - int
273-
arg0 -= arg1;
274-
}
275-
if (MP_SMALL_INT_FITS(arg0)) {
276-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0);
277-
}
278-
}
279-
break;
280-
281-
case PN_term:
282-
if (n == 3 && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[0]) && MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[2])) {
283-
mp_int_t arg0 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[0]);
284-
mp_int_t arg1 = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[2]);
285-
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_STAR)) {
286-
// int * int
287-
if (!mp_small_int_mul_overflow(arg0, arg1)) {
288-
arg0 *= arg1;
289-
if (MP_SMALL_INT_FITS(arg0)) {
290-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg0);
291-
}
292-
}
293-
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_SLASH)) {
294-
// int / int
295-
// pass
296-
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_PERCENT)) {
297-
// int%int
298-
if (arg1 != 0) {
299-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_modulo(arg0, arg1));
300-
}
301-
} else {
302-
assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[1], MP_TOKEN_OP_DBL_SLASH)); // should be
303-
if (arg1 != 0) {
304-
// int // int
305-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, mp_small_int_floor_divide(arg0, arg1));
306-
}
307-
}
308-
}
309-
break;
310-
311-
case PN_factor_2:
312-
if (MP_PARSE_NODE_IS_SMALL_INT(pns->nodes[1])) {
313-
mp_int_t arg = MP_PARSE_NODE_LEAF_SMALL_INT(pns->nodes[1]);
314-
if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_PLUS)) {
315-
// +int
316-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg);
317-
} else if (MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_MINUS)) {
318-
// -int
319-
arg = -arg;
320-
if (MP_SMALL_INT_FITS(arg)) {
321-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, arg);
322-
}
323-
} else {
324-
assert(MP_PARSE_NODE_IS_TOKEN_KIND(pns->nodes[0], MP_TOKEN_OP_TILDE)); // should be
325-
// ~int
326-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, ~arg);
327-
}
328-
}
329-
break;
330-
331-
case PN_power:
332-
if (0) {
333-
#if MICROPY_COMP_MODULE_CONST
334-
} else if (MP_PARSE_NODE_IS_ID(pns->nodes[0]) && MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[1], PN_trailer_period) && MP_PARSE_NODE_IS_NULL(pns->nodes[2])) {
335-
// id.id
336-
// look it up in constant table, see if it can be replaced with an integer
337-
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t*)pns->nodes[1];
338-
assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
339-
qstr q_base = MP_PARSE_NODE_LEAF_ARG(pns->nodes[0]);
340-
qstr q_attr = MP_PARSE_NODE_LEAF_ARG(pns1->nodes[0]);
341-
mp_map_elem_t *elem = mp_map_lookup((mp_map_t*)&mp_constants_map, MP_OBJ_NEW_QSTR(q_base), MP_MAP_LOOKUP);
342-
if (elem != NULL) {
343-
mp_obj_t dest[2];
344-
mp_load_method_maybe(elem->value, q_attr, dest);
345-
if (MP_OBJ_IS_SMALL_INT(dest[0]) && dest[1] == NULL) {
346-
mp_int_t val = MP_OBJ_SMALL_INT_VALUE(dest[0]);
347-
pn = mp_parse_node_new_leaf(MP_PARSE_NODE_SMALL_INT, val);
348-
}
349-
}
350-
#endif
351-
}
352-
break;
353-
}
354-
}
355-
356-
return pn;
357-
}
358-
359125
STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_arglist, bool is_method_call, int n_positional_extra);
360126
STATIC void compile_comprehension(compiler_t *comp, mp_parse_node_struct_t *pns, scope_kind_t kind);
361127
STATIC void compile_node(compiler_t *comp, mp_parse_node_t pn);
@@ -3332,13 +3098,6 @@ mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, uint emit_opt
33323098
// create the module scope
33333099
scope_t *module_scope = scope_new_and_link(comp, SCOPE_MODULE, parse_tree->root, emit_opt);
33343100

3335-
// optimise constants (scope must be set for error messages to work)
3336-
comp->scope_cur = module_scope;
3337-
mp_map_t consts;
3338-
mp_map_init(&consts, 0);
3339-
module_scope->pn = fold_constants(comp, module_scope->pn, &consts);
3340-
mp_map_deinit(&consts);
3341-
33423101
// create standard emitter; it's used at least for MP_PASS_SCOPE
33433102
emit_t *emit_bc = emit_bc_new();
33443103

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@
215215
/*****************************************************************************/
216216
/* Compiler configuration */
217217

218+
// Whether to enable constant folding; eg 1+2 rewritten as 3
219+
#ifndef MICROPY_COMP_CONST_FOLDING
220+
#define MICROPY_COMP_CONST_FOLDING (1)
221+
#endif
222+
218223
// Whether to enable lookup of constants in modules; eg module.CONST
219224
#ifndef MICROPY_COMP_MODULE_CONST
220225
#define MICROPY_COMP_MODULE_CONST (0)

0 commit comments

Comments
 (0)