Skip to content

Commit 510890b

Browse files
committed
Enable squeezing property storage in flash
.. and enable it on atmel-samd and raspberrypi. On trinket_m0 this saves 96 net bytes of flash. There are 216 bytes actually saved by reducing the flash storage size of the property descriptors, but added code in several paths takes back over half of the 'raw savings'. By organizing the "get-only" and "get-set" (but no delete) properties each in a different section, we can represent then more efficiently. Testing performed: that a get-only property can still be gotten but can't be set or deleted; that a get-set property can sill be gotten or set but can't be deleted. Tested on pygamer. Because this requires linker file support, I only enabled it on two of the ports.
1 parent 78cf0a9 commit 510890b

File tree

11 files changed

+75
-10
lines changed

11 files changed

+75
-10
lines changed

ports/atmel-samd/boards/common.template.ld

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ SECTIONS
2828
_sfixed = .;
2929
KEEP(*(.vectors)) /* isr vector table */
3030

31+
__property_getter_start = .;
32+
*(.property_getter)
33+
__property_getter_end = .;
34+
__property_getset_start = .;
35+
*(.property_getset)
36+
__property_getset_end = .;
37+
3138
/* Sort text sections so that they have fewer *fill* bytes needed. */
3239
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.text))) /* .text sections (code) */
3340
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.text*))) /* .text* sections (code) */

ports/atmel-samd/mpconfigport.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ INTERNAL_LIBM = 1
88
USB_NUM_ENDPOINT_PAIRS = 8
99

1010
CIRCUITPY_ROTARYIO_SOFTENCODER = 1
11+
CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE ?= 1
1112

1213
######################################################################
1314
# Put samd21-only choices here.

ports/raspberrypi/link.ld

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ SECTIONS
6868
/* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
6969
* FLASH ... we will include any thing excluded here in .data below by default */
7070
*(.init)
71+
72+
__property_getter_start = .;
73+
*(.property_getter)
74+
__property_getter_end = .;
75+
__property_getset_start = .;
76+
*(.property_getset)
77+
__property_getset_end = .;
78+
7179
*(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
7280
*(.fini)
7381
/* Pull all c'tors into .text */

ports/raspberrypi/mpconfigport.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# All raspberrypi ports have longints.
22
LONGINT_IMPL = MPZ
33

4+
CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE ?= 1
45
CIRCUITPY_ALARM ?= 1
56

67
CIRCUITPY_RP2PIO ?= 1

py/circuitpy_mpconfig.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,4 +573,6 @@ void supervisor_run_background_tasks_if_tick(void);
573573
#define MICROPY_WRAP_MP_EXECUTE_BYTECODE PLACE_IN_ITCM
574574
#endif
575575

576+
#define MICROPY_PY_OPTIMIZE_PROPERTY_FLASH_SIZE (CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE)
577+
576578
#endif // __INCLUDED_MPCONFIG_CIRCUITPY_H

py/circuitpy_mpconfig.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232
CIRCUITPY_FULL_BUILD ?= 1
3333
CFLAGS += -DCIRCUITPY_FULL_BUILD=$(CIRCUITPY_FULL_BUILD)
3434

35+
# Reduce the size of in-flash properties. Requires support in the .ld linker
36+
# file, so not enabled by default.
37+
CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE ?= 0
38+
CFLAGS += -DCIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE=$(CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE)
39+
3540
# async/await language keyword support
3641
MICROPY_PY_ASYNC_AWAIT ?= $(CIRCUITPY_FULL_BUILD)
3742
CFLAGS += -DMICROPY_PY_ASYNC_AWAIT=$(MICROPY_PY_ASYNC_AWAIT)

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,11 @@ typedef double mp_float_t;
10781078
#define MICROPY_PY_BUILTINS_PROPERTY (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES)
10791079
#endif
10801080

1081+
// Whether to optimize property flash storage size (requires linker script support)
1082+
#ifndef MICROPY_PY_BUILTINS_PROPERTY
1083+
#define MICROPY_PY_BUILTINS_PROPERTY (0)
1084+
#endif
1085+
10811086
// Whether to implement the start/stop/step attributes (readback) on
10821087
// the "range" builtin type. Rarely used, and costs ~60 bytes (x86).
10831088
#ifndef MICROPY_PY_BUILTINS_RANGE_ATTRS

py/objproperty.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,24 @@ const mp_obj_type_t mp_type_property = {
9595
.locals_dict = (mp_obj_dict_t *)&property_locals_dict,
9696
};
9797

98-
const mp_obj_t *mp_obj_property_get(mp_obj_t self_in) {
98+
#if MICROPY_PY_OPTIMIZE_PROPERTY_FLASH_SIZE
99+
extern const mp_obj_property_t __property_getter_start, __property_getter_end, __property_getset_start, __property_getset_end;
100+
#endif
101+
102+
const mp_obj_t *mp_obj_property_get(mp_obj_t self_in, size_t *n_proxy) {
99103
mp_check_self(mp_obj_is_type(self_in, &mp_type_property));
100104
mp_obj_property_t *self = MP_OBJ_TO_PTR(self_in);
105+
#if MICROPY_PY_OPTIMIZE_PROPERTY_FLASH_SIZE
106+
if (self >= &__property_getter_start && self < &__property_getter_end) {
107+
*n_proxy = 1;
108+
} else if (self >= &__property_getset_start && self < &__property_getset_end) {
109+
*n_proxy = 2;
110+
} else {
111+
*n_proxy = 3;
112+
}
113+
#else
114+
*n_proxy = 3;
115+
#endif
101116
return self->proxy;
102117
}
103118

py/objproperty.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,27 @@ typedef struct _mp_obj_property_t {
3535
mp_obj_t proxy[3]; // getter, setter, deleter
3636
} mp_obj_property_t;
3737

38+
#if MICROPY_PY_OPTIMIZE_PROPERTY_FLASH_SIZE
39+
typedef struct _mp_obj_property_getter_t {
40+
mp_obj_base_t base;
41+
mp_obj_t proxy[1]; // getter
42+
} mp_obj_property_getter_t;
43+
44+
typedef struct _mp_obj_property_getset_t {
45+
mp_obj_base_t base;
46+
mp_obj_t proxy[2]; // getter, setter
47+
} mp_obj_property_getset_t;
48+
49+
#define MP_PROPERTY_GETTER(P, G) const mp_obj_property_getter_t P __attribute((section(".property_getter"))) = {.base.type = &mp_type_property, .proxy = {G}}
50+
#define MP_PROPERTY_GETSET(P, G, S) const mp_obj_property_getset_t P __attribute((section(".property_getset"))) = {.base.type = &mp_type_property, .proxy = {G, S}}
51+
52+
#else
3853
typedef struct _mp_obj_property_t mp_obj_property_getter_t;
3954
typedef struct _mp_obj_property_t mp_obj_property_getset_t;
4055

4156
#define MP_PROPERTY_GETTER(P, G) const mp_obj_property_t P = {.base.type = &mp_type_property, .proxy = {G, MP_ROM_NONE, MP_ROM_NONE}}
4257
#define MP_PROPERTY_GETSET(P, G, S) const mp_obj_property_t P = {.base.type = &mp_type_property, .proxy = {G, S, MP_ROM_NONE}}
58+
#endif
4359

4460
#endif // MICROPY_PY_BUILTINS_PROPERTY
4561

py/objtype.c

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,8 @@ STATIC void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des
660660
// be called by the descriptor code down below. But that way
661661
// requires overhead for the nested mp_call's and overhead for
662662
// the code.
663-
const mp_obj_t *proxy = mp_obj_property_get(member);
663+
size_t n_proxy;
664+
const mp_obj_t *proxy = mp_obj_property_get(member, &n_proxy);
664665
if (proxy[0] == mp_const_none) {
665666
mp_raise_AttributeError(MP_ERROR_TEXT("unreadable attribute"));
666667
} else {
@@ -740,11 +741,12 @@ STATIC bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t val
740741
// would be called by the descriptor code down below. But that way
741742
// requires overhead for the nested mp_call's and overhead for
742743
// the code.
743-
const mp_obj_t *proxy = mp_obj_property_get(member[0]);
744+
size_t n_proxy;
745+
const mp_obj_t *proxy = mp_obj_property_get(member[0], &n_proxy);
744746
mp_obj_t dest[2] = {self_in, value};
745747
if (value == MP_OBJ_NULL) {
746748
// delete attribute
747-
if (proxy[2] == mp_const_none) {
749+
if (n_proxy < 3 || proxy[2] == mp_const_none) {
748750
// TODO better error message?
749751
return false;
750752
} else {
@@ -753,7 +755,7 @@ STATIC bool mp_obj_instance_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t val
753755
}
754756
} else {
755757
// store attribute
756-
if (proxy[1] == mp_const_none) {
758+
if (n_proxy < 2 || proxy[1] == mp_const_none) {
757759
// TODO better error message?
758760
return false;
759761
} else {
@@ -1374,7 +1376,8 @@ STATIC void super_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
13741376
// here...
13751377
#if MICROPY_PY_BUILTINS_PROPERTY
13761378
if (mp_obj_is_type(member, &mp_type_property)) {
1377-
const mp_obj_t *proxy = mp_obj_property_get(member);
1379+
size_t n_proxy;
1380+
const mp_obj_t *proxy = mp_obj_property_get(member, &n_proxy);
13781381
if (proxy[0] == mp_const_none) {
13791382
mp_raise_AttributeError(MP_ERROR_TEXT("unreadable attribute"));
13801383
} else {

0 commit comments

Comments
 (0)