Skip to content

Commit

Permalink
py/obj: Add MICROPY_OBJ_IMMEDIATE_OBJS option to reduce code size.
Browse files Browse the repository at this point in the history
This option (enabled by default for object representation A, B, C) makes
None/False/True objects immediate objects, ie they are no longer a concrete
object in ROM but are rather just values, eg None=0x6 for representation A.

Doing this saves a considerable amount of code size, due to these objects
being widely used:

   bare-arm:  -392 -0.591%
minimal x86:  -252 -0.170% [incl +52(data)]
   unix x64:  -624 -0.125% [incl -128(data)]
unix nanbox:    +0 +0.000%
      stm32: -1940 -0.510% PYBV10
     cc3200: -1216 -0.659%
    esp8266:  -404 -0.062% GENERIC
      esp32:  -732 -0.064% GENERIC[incl +48(data)]
        nrf:  -988 -0.675% pca10040
       samd:  -564 -0.556% ADAFRUIT_ITSYBITSY_M4_EXPRESS

Thanks go to @Jongy aka Yonatan Goldschmidt for the idea.
  • Loading branch information
dpgeorge committed Jan 12, 2020
1 parent 6f0c83f commit d96cfd1
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 12 deletions.
7 changes: 7 additions & 0 deletions py/mpconfig.h
Expand Up @@ -113,6 +113,13 @@
#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A)
#endif

// Whether to encode None/False/True as immediate objects instead of pointers to
// real objects. Reduces code size by a decent amount without hurting
// performance, for all representations except D on some architectures.
#ifndef MICROPY_OBJ_IMMEDIATE_OBJS
#define MICROPY_OBJ_IMMEDIATE_OBJS (MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D)
#endif

/*****************************************************************************/
/* Memory allocation policy */

Expand Down
5 changes: 5 additions & 0 deletions py/obj.c
Expand Up @@ -46,6 +46,11 @@ const mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in) {
} else if (mp_obj_is_float(o_in)) {
return &mp_type_float;
#endif
#if MICROPY_OBJ_IMMEDIATE_OBJS
} else if (mp_obj_is_immediate_obj(o_in)) {
static const mp_obj_type_t *const types[2] = {&mp_type_NoneType, &mp_type_bool};
return types[MP_OBJ_IMMEDIATE_OBJ_VALUE(o_in) & 1];
#endif
} else {
const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in);
return o->type;
Expand Down
29 changes: 24 additions & 5 deletions py/obj.h
Expand Up @@ -263,13 +263,22 @@ typedef union _mp_rom_obj_t { uint64_t u64; struct { const void *lo, *hi; } u32;
// Macros to create objects that are stored in ROM.

#ifndef MP_ROM_NONE
#if MICROPY_OBJ_IMMEDIATE_OBJS
#define MP_ROM_NONE mp_const_none
#else
#define MP_ROM_NONE MP_ROM_PTR(&mp_const_none_obj)
#endif
#endif

#ifndef MP_ROM_FALSE
#if MICROPY_OBJ_IMMEDIATE_OBJS
#define MP_ROM_FALSE mp_const_false
#define MP_ROM_TRUE mp_const_true
#else
#define MP_ROM_FALSE MP_ROM_PTR(&mp_const_false_obj)
#define MP_ROM_TRUE MP_ROM_PTR(&mp_const_true_obj)
#endif
#endif

#ifndef MP_ROM_INT
typedef mp_const_obj_t mp_rom_obj_t;
Expand Down Expand Up @@ -622,17 +631,27 @@ extern const mp_obj_type_t mp_type_ValueError;
extern const mp_obj_type_t mp_type_ViperTypeError;
extern const mp_obj_type_t mp_type_ZeroDivisionError;

// Constant objects, globally accessible
// The macros are for convenience only
// Constant objects, globally accessible: None, False, True
// These should always be accessed via the below macros.
#if MICROPY_OBJ_IMMEDIATE_OBJS
// None is even while False/True are odd so their types can be distinguished with 1 bit.
#define mp_const_none MP_OBJ_NEW_IMMEDIATE_OBJ(0)
#define mp_const_false MP_OBJ_NEW_IMMEDIATE_OBJ(1)
#define mp_const_true MP_OBJ_NEW_IMMEDIATE_OBJ(3)
#else
#define mp_const_none (MP_OBJ_FROM_PTR(&mp_const_none_obj))
#define mp_const_false (MP_OBJ_FROM_PTR(&mp_const_false_obj))
#define mp_const_true (MP_OBJ_FROM_PTR(&mp_const_true_obj))
#define mp_const_empty_bytes (MP_OBJ_FROM_PTR(&mp_const_empty_bytes_obj))
#define mp_const_empty_tuple (MP_OBJ_FROM_PTR(&mp_const_empty_tuple_obj))
#define mp_const_notimplemented (MP_OBJ_FROM_PTR(&mp_const_notimplemented_obj))
extern const struct _mp_obj_none_t mp_const_none_obj;
extern const struct _mp_obj_bool_t mp_const_false_obj;
extern const struct _mp_obj_bool_t mp_const_true_obj;
#endif

// Constant objects, globally accessible: b'', (), Ellipsis, NotImplemented, GeneratorExit()
// The below macros are for convenience only.
#define mp_const_empty_bytes (MP_OBJ_FROM_PTR(&mp_const_empty_bytes_obj))
#define mp_const_empty_tuple (MP_OBJ_FROM_PTR(&mp_const_empty_tuple_obj))
#define mp_const_notimplemented (MP_OBJ_FROM_PTR(&mp_const_notimplemented_obj))
extern const struct _mp_obj_str_t mp_const_empty_bytes_obj;
extern const struct _mp_obj_tuple_t mp_const_empty_tuple_obj;
extern const struct _mp_obj_singleton_t mp_const_ellipsis_obj;
Expand Down
26 changes: 19 additions & 7 deletions py/objbool.c
Expand Up @@ -28,21 +28,31 @@

#include "py/runtime.h"

#if MICROPY_OBJ_IMMEDIATE_OBJS

#define BOOL_VALUE(o) ((o) == mp_const_false ? 0 : 1)

#else

#define BOOL_VALUE(o) (((mp_obj_bool_t*)MP_OBJ_TO_PTR(o))->value)

typedef struct _mp_obj_bool_t {
mp_obj_base_t base;
bool value;
} mp_obj_bool_t;

#endif

STATIC void bool_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
mp_obj_bool_t *self = MP_OBJ_TO_PTR(self_in);
bool value = BOOL_VALUE(self_in);
if (MICROPY_PY_UJSON && kind == PRINT_JSON) {
if (self->value) {
if (value) {
mp_print_str(print, "true");
} else {
mp_print_str(print, "false");
}
} else {
if (self->value) {
if (value) {
mp_print_str(print, "True");
} else {
mp_print_str(print, "False");
Expand All @@ -65,13 +75,13 @@ STATIC mp_obj_t bool_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
if (op == MP_UNARY_OP_LEN) {
return MP_OBJ_NULL;
}
mp_obj_bool_t *self = MP_OBJ_TO_PTR(o_in);
return mp_unary_op(op, MP_OBJ_NEW_SMALL_INT(self->value));
bool value = BOOL_VALUE(o_in);
return mp_unary_op(op, MP_OBJ_NEW_SMALL_INT(value));
}

STATIC mp_obj_t bool_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
mp_obj_bool_t *self = MP_OBJ_TO_PTR(lhs_in);
return mp_binary_op(op, MP_OBJ_NEW_SMALL_INT(self->value), rhs_in);
bool value = BOOL_VALUE(lhs_in);
return mp_binary_op(op, MP_OBJ_NEW_SMALL_INT(value), rhs_in);
}

const mp_obj_type_t mp_type_bool = {
Expand All @@ -83,5 +93,7 @@ const mp_obj_type_t mp_type_bool = {
.binary_op = bool_binary_op,
};

#if !MICROPY_OBJ_IMMEDIATE_OBJS
const mp_obj_bool_t mp_const_false_obj = {{&mp_type_bool}, false};
const mp_obj_bool_t mp_const_true_obj = {{&mp_type_bool}, true};
#endif
4 changes: 4 additions & 0 deletions py/objnone.c
Expand Up @@ -28,9 +28,11 @@

#include "py/obj.h"

#if !MICROPY_OBJ_IMMEDIATE_OBJS
typedef struct _mp_obj_none_t {
mp_obj_base_t base;
} mp_obj_none_t;
#endif

STATIC void none_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)self_in;
Expand All @@ -48,4 +50,6 @@ const mp_obj_type_t mp_type_NoneType = {
.unary_op = mp_generic_unary_op,
};

#if !MICROPY_OBJ_IMMEDIATE_OBJS
const mp_obj_none_t mp_const_none_obj = {{&mp_type_NoneType}};
#endif

0 comments on commit d96cfd1

Please sign in to comment.