Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1aa03e9

Browse files
committed
py/parse: Add support for math module constants and float folding.
Add a new MICROPY_COMP_CONST_FLOAT feature, enabled by in mpy-cross and when compiling with MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES. The new feature leverages the code of MICROPY_COMP_CONST_FOLDING to support folding of floating point constants. If MICROPY_COMP_MODULE_CONST is defined as well, math module constants are made available at compile time. For example: _DEG_TO_GRADIANT = const(math.pi/180) _INVALID_VALUE = const(math.nan) A few corner cases had to be handled: - The float const folding code should not fold expressions resulting into complex results, as the mpy parser for complex immediates has limitations. - The constant generation code must distinguish between -0.0 and 0.0, which are different even if C consider them as == This change removes previous limitations on the use of const() expressions that would result in floating point number, so the test cases of micropython/const_error have to be updated. Additional test cases have been added to cover the new repr() code. A few other simple test cases have been added to handle the use of floats in const() expressions, but the float folding code itself is also tested when running general float test cases, as float expressions often get resolved at compile-time. Signed-off-by: Yoctopuce dev <[email protected]>
1 parent dbbaa95 commit 1aa03e9

File tree

12 files changed

+142
-31
lines changed

12 files changed

+142
-31
lines changed

mpy-cross/mpconfigport.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#define MICROPY_COMP_CONST_FOLDING (1)
5656
#define MICROPY_COMP_MODULE_CONST (1)
5757
#define MICROPY_COMP_CONST (1)
58+
#define MICROPY_COMP_CONST_FLOAT (1)
5859
#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1)
5960
#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1)
6061
#define MICROPY_COMP_RETURN_IF_EXPR (1)
@@ -88,7 +89,8 @@
8889
#define MICROPY_PY_ARRAY (0)
8990
#define MICROPY_PY_ATTRTUPLE (0)
9091
#define MICROPY_PY_COLLECTIONS (0)
91-
#define MICROPY_PY_MATH (0)
92+
#define MICROPY_PY_MATH (MICROPY_COMP_CONST_FLOAT)
93+
#define MICROPY_PY_MATH_CONSTANTS (MICROPY_COMP_CONST_FLOAT)
9294
#define MICROPY_PY_CMATH (0)
9395
#define MICROPY_PY_GC (0)
9496
#define MICROPY_PY_IO (0)

py/builtin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ extern const mp_obj_module_t mp_module_sys;
138138
extern const mp_obj_module_t mp_module_errno;
139139
extern const mp_obj_module_t mp_module_uctypes;
140140
extern const mp_obj_module_t mp_module_machine;
141+
extern const mp_obj_module_t mp_module_math;
141142

142143
extern const char MICROPY_PY_BUILTINS_HELP_TEXT[];
143144

py/emitcommon.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
*/
2626

2727
#include <assert.h>
28+
#include <math.h>
2829

2930
#include "py/emit.h"
3031
#include "py/nativeglue.h"
@@ -72,7 +73,21 @@ static bool strictly_equal(mp_obj_t a, mp_obj_t b) {
7273
}
7374
return true;
7475
} else {
75-
return mp_obj_equal(a, b);
76+
if (!mp_obj_equal(a, b)) {
77+
return false;
78+
}
79+
#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_COMP_CONST_FLOAT
80+
if (a_type == &mp_type_float) {
81+
mp_float_t a_val = mp_obj_float_get(a);
82+
if (a_val == (mp_float_t)0.0) {
83+
// Although 0.0 == -0.0, they are not strictly_equal and
84+
// must be stored as two different constants in .mpy files
85+
mp_float_t b_val = mp_obj_float_get(b);
86+
return signbit(a_val) == signbit(b_val);
87+
}
88+
}
89+
#endif
90+
return true;
7691
}
7792
}
7893

py/mpconfig.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,13 @@
490490
#define MICROPY_COMP_CONST (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
491491
#endif
492492

493+
// Whether to enable float constant folding like 1.2+3.4 (when MICROPY_COMP_CONST_FOLDING is also enabled)
494+
// and constant optimisation like id = const(1.2) (when MICROPY_COMP_CONST is also enabled)
495+
// and constant lookup like math.inf (when MICROPY_COMP_MODULE_CONST is also enabled)
496+
#ifndef MICROPY_COMP_CONST_FLOAT
497+
#define MICROPY_COMP_CONST_FLOAT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
498+
#endif
499+
493500
// Whether to enable optimisation of: a, b = c, d
494501
// Costs 124 bytes (Thumb2)
495502
#ifndef MICROPY_COMP_DOUBLE_TUPLE_ASSIGN

py/parse.c

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -336,18 +336,34 @@ static uint8_t peek_rule(parser_t *parser, size_t n) {
336336
}
337337
#endif
338338

339-
bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) {
339+
#if MICROPY_COMP_CONST_FOLDING || MICROPY_EMIT_INLINE_ASM
340+
static bool mp_parse_node_get_number_maybe(mp_parse_node_t pn, mp_obj_t *o) {
340341
if (MP_PARSE_NODE_IS_SMALL_INT(pn)) {
341342
*o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn));
342343
return true;
343344
} else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) {
344345
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn;
345346
*o = mp_parse_node_extract_const_object(pns);
346-
return mp_obj_is_int(*o);
347+
return mp_obj_is_int(*o)
348+
#if MICROPY_COMP_CONST_FLOAT
349+
|| mp_obj_is_float(*o)
350+
#endif
351+
;
347352
} else {
348353
return false;
349354
}
350355
}
356+
#endif
357+
358+
#if MICROPY_EMIT_INLINE_ASM
359+
bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) {
360+
return mp_parse_node_get_number_maybe(pn, o)
361+
#if MICROPY_COMP_CONST_FLOAT
362+
&& mp_obj_is_int(*o)
363+
#endif
364+
;
365+
}
366+
#endif
351367

352368
#if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST
353369
static bool mp_parse_node_is_const(mp_parse_node_t pn) {
@@ -642,12 +658,32 @@ static const mp_rom_map_elem_t mp_constants_table[] = {
642658
#if MICROPY_PY_UCTYPES
643659
{ MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) },
644660
#endif
661+
#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH && MICROPY_COMP_CONST_FLOAT
662+
{ MP_ROM_QSTR(MP_QSTR_math), MP_ROM_PTR(&mp_module_math) },
663+
#endif
645664
// Extra constants as defined by a port
646665
MICROPY_PORT_CONSTANTS
647666
};
648667
static MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table);
649668
#endif
650669

670+
static bool binary_op_maybe(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs, mp_obj_t *res) {
671+
nlr_buf_t nlr;
672+
if (nlr_push(&nlr) == 0) {
673+
mp_obj_t tmp = mp_binary_op(op, lhs, rhs);
674+
#if MICROPY_PY_BUILTINS_COMPLEX
675+
if (mp_obj_is_type(tmp, &mp_type_complex)) {
676+
return false;
677+
}
678+
#endif
679+
*res = tmp;
680+
nlr_pop();
681+
return true;
682+
} else {
683+
return false;
684+
}
685+
}
686+
651687
static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *num_args) {
652688
if (rule_id == RULE_or_test
653689
|| rule_id == RULE_and_test) {
@@ -706,7 +742,7 @@ static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *nu
706742
}
707743

708744
static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
709-
// this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4
745+
// this code does folding of arbitrary numeric expressions, eg 1 + 2 * 3 + 4
710746
// it does not do partial folding, eg 1 + 2 + x -> 3 + x
711747

712748
mp_obj_t arg0;
@@ -716,7 +752,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
716752
|| rule_id == RULE_power) {
717753
// folding for binary ops: | ^ & **
718754
mp_parse_node_t pn = peek_result(parser, num_args - 1);
719-
if (!mp_parse_node_get_int_maybe(pn, &arg0)) {
755+
if (!mp_parse_node_get_number_maybe(pn, &arg0)) {
720756
return false;
721757
}
722758
mp_binary_op_t op;
@@ -732,58 +768,61 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
732768
for (ssize_t i = num_args - 2; i >= 0; --i) {
733769
pn = peek_result(parser, i);
734770
mp_obj_t arg1;
735-
if (!mp_parse_node_get_int_maybe(pn, &arg1)) {
771+
if (!mp_parse_node_get_number_maybe(pn, &arg1)) {
736772
return false;
737773
}
774+
#if !MICROPY_COMP_CONST_FLOAT
738775
if (op == MP_BINARY_OP_POWER && mp_obj_int_sign(arg1) < 0) {
739776
// ** can't have negative rhs
740777
return false;
741778
}
742-
arg0 = mp_binary_op(op, arg0, arg1);
779+
#endif
780+
if (!binary_op_maybe(op, arg0, arg1, &arg0)) {
781+
return false;
782+
}
743783
}
744784
} else if (rule_id == RULE_shift_expr
745785
|| rule_id == RULE_arith_expr
746786
|| rule_id == RULE_term) {
747787
// folding for binary ops: << >> + - * @ / % //
748788
mp_parse_node_t pn = peek_result(parser, num_args - 1);
749-
if (!mp_parse_node_get_int_maybe(pn, &arg0)) {
789+
if (!mp_parse_node_get_number_maybe(pn, &arg0)) {
750790
return false;
751791
}
752792
for (ssize_t i = num_args - 2; i >= 1; i -= 2) {
753793
pn = peek_result(parser, i - 1);
754794
mp_obj_t arg1;
755-
if (!mp_parse_node_get_int_maybe(pn, &arg1)) {
795+
if (!mp_parse_node_get_number_maybe(pn, &arg1)) {
756796
return false;
757797
}
758798
mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i));
759-
if (tok == MP_TOKEN_OP_AT || tok == MP_TOKEN_OP_SLASH) {
760-
// Can't fold @ or /
799+
if (tok == MP_TOKEN_OP_AT) {
800+
// Can't fold @
801+
return false;
802+
}
803+
#if !MICROPY_COMP_CONST_FLOAT
804+
if (tok == MP_TOKEN_OP_SLASH) {
805+
// Can't fold /
761806
return false;
762807
}
808+
#endif
763809
mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS);
764-
int rhs_sign = mp_obj_int_sign(arg1);
765-
if (op <= MP_BINARY_OP_RSHIFT) {
766-
// << and >> can't have negative rhs
767-
if (rhs_sign < 0) {
768-
return false;
769-
}
770-
} else if (op >= MP_BINARY_OP_FLOOR_DIVIDE) {
771-
// % and // can't have zero rhs
772-
if (rhs_sign == 0) {
773-
return false;
774-
}
810+
if (!binary_op_maybe(op, arg0, arg1, &arg0)) {
811+
return false;
775812
}
776-
arg0 = mp_binary_op(op, arg0, arg1);
777813
}
778814
} else if (rule_id == RULE_factor_2) {
779815
// folding for unary ops: + - ~
780816
mp_parse_node_t pn = peek_result(parser, 0);
781-
if (!mp_parse_node_get_int_maybe(pn, &arg0)) {
817+
if (!mp_parse_node_get_number_maybe(pn, &arg0)) {
782818
return false;
783819
}
784820
mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, 1));
785821
mp_unary_op_t op;
786822
if (tok == MP_TOKEN_OP_TILDE) {
823+
if (!mp_obj_is_int(arg0)) {
824+
return false;
825+
}
787826
op = MP_UNARY_OP_INVERT;
788827
} else {
789828
assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); // should be
@@ -855,7 +894,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
855894
return false;
856895
}
857896
// id1.id2
858-
// look it up in constant table, see if it can be replaced with an integer
897+
// look it up in constant table, see if it can be replaced with an integer or a float
859898
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn1;
860899
assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0]));
861900
qstr q_base = MP_PARSE_NODE_LEAF_ARG(pn0);
@@ -866,7 +905,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) {
866905
}
867906
mp_obj_t dest[2];
868907
mp_load_method_maybe(elem->value, q_attr, dest);
869-
if (!(dest[0] != MP_OBJ_NULL && mp_obj_is_int(dest[0]) && dest[1] == MP_OBJ_NULL)) {
908+
if (!(dest[0] != MP_OBJ_NULL && (mp_obj_is_int(dest[0]) || mp_obj_is_float(dest[0])) && dest[1] == MP_OBJ_NULL)) {
870909
return false;
871910
}
872911
arg0 = dest[0];

tests/float/float_parse_doubleprec.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,9 @@
1919
print(float("1.00000000000000000000e-307"))
2020
print(float("10.0000000000000000000e-308"))
2121
print(float("100.000000000000000000e-309"))
22+
23+
# ensure repr() adds an extra digit when needed for accurate parsing
24+
print(float(repr(float("2.0") ** 100)) == float("2.0") ** 100)
25+
26+
# ensure repr does not add meaningless extra digits (1.234999999999)
27+
print(repr(1.2345))

tests/micropython/const_error.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ def test_syntax(code):
1818

1919
# these operations are not supported within const
2020
test_syntax("A = const(1 @ 2)")
21-
test_syntax("A = const(1 / 2)")
22-
test_syntax("A = const(1 ** -2)")
2321
test_syntax("A = const(1 << -2)")
2422
test_syntax("A = const(1 >> -2)")
2523
test_syntax("A = const(1 % 0)")

tests/micropython/const_error.py.exp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,3 @@ SyntaxError
55
SyntaxError
66
SyntaxError
77
SyntaxError
8-
SyntaxError
9-
SyntaxError

tests/micropython/const_float.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# test constant optimisation, with consts that are floats
2+
try:
3+
float("3.14")
4+
except NameError:
5+
print("SKIP")
6+
raise SystemExit
7+
8+
from micropython import const
9+
10+
# check we can make consts from floats
11+
F1 = const(2.5)
12+
F2 = const(-0.3)
13+
print(type(F1), F1)
14+
print(type(F2), F2)
15+
16+
# check arithmetic with floats
17+
F3 = const(F1 + F2)
18+
F4 = const(F1**2)
19+
print(F3, F4)
20+
21+
# check int operations with float results
22+
F5 = const(1 / 2)
23+
F6 = const(2**-2)
24+
print(F5, F6)
25+
26+
# note: we also test float expression folding when
27+
# we're compiling test cases in tests/float, as
28+
# many expressions are resolved at compile time.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<class 'float'> 2.5
2+
<class 'float'> -0.3
3+
2.2 6.25
4+
0.5 0.25

0 commit comments

Comments
 (0)