Skip to content

Commit

Permalink
py: Add LOAD_SUPER_METHOD bytecode to allow heap-free super meth calls.
Browse files Browse the repository at this point in the history
This patch allows the following code to run without allocating on the heap:

    super().foo(...)

Before this patch such a call would allocate a super object on the heap and
then load the foo method and call it right away.  The super object is only
needed to perform the lookup of the method and not needed after that.  This
patch makes an optimisation to allocate the super object on the C stack and
discard it right after use.

Changes in code size due to this patch are:

   bare-arm: +128
    minimal: +232
   unix x64: +416
unix nanbox: +364
     stmhal: +184
    esp8266: +340
     cc3200: +128
  • Loading branch information
dpgeorge committed Apr 22, 2017
1 parent 5335942 commit dd11af2
Show file tree
Hide file tree
Showing 16 changed files with 68 additions and 27 deletions.
Binary file modified minimal/frozentest.mpy
Binary file not shown.
2 changes: 1 addition & 1 deletion py/bc.c
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ STATIC const byte opcode_format_table[64] = {
OC4(U, U, U, U), // 0x0c-0x0f
OC4(B, B, B, U), // 0x10-0x13
OC4(V, U, Q, V), // 0x14-0x17
OC4(B, U, V, V), // 0x18-0x1b
OC4(B, V, V, Q), // 0x18-0x1b
OC4(Q, Q, Q, Q), // 0x1c-0x1f
OC4(B, B, V, V), // 0x20-0x23
OC4(Q, Q, Q, B), // 0x24-0x27
Expand Down
13 changes: 7 additions & 6 deletions py/bc0.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,13 @@
#define MP_BC_LOAD_CONST_OBJ (0x17) // ptr
#define MP_BC_LOAD_NULL (0x18)

#define MP_BC_LOAD_FAST_N (0x1a) // uint
#define MP_BC_LOAD_DEREF (0x1b) // uint
#define MP_BC_LOAD_NAME (0x1c) // qstr
#define MP_BC_LOAD_GLOBAL (0x1d) // qstr
#define MP_BC_LOAD_ATTR (0x1e) // qstr
#define MP_BC_LOAD_METHOD (0x1f) // qstr
#define MP_BC_LOAD_FAST_N (0x19) // uint
#define MP_BC_LOAD_DEREF (0x1a) // uint
#define MP_BC_LOAD_NAME (0x1b) // qstr
#define MP_BC_LOAD_GLOBAL (0x1c) // qstr
#define MP_BC_LOAD_ATTR (0x1d) // qstr
#define MP_BC_LOAD_METHOD (0x1e) // qstr
#define MP_BC_LOAD_SUPER_METHOD (0x1f) // qstr
#define MP_BC_LOAD_BUILD_CLASS (0x20)
#define MP_BC_LOAD_SUBSCR (0x21)

Expand Down
23 changes: 17 additions & 6 deletions py/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1694,7 +1694,7 @@ STATIC void compile_yield_from(compiler_t *comp) {

#if MICROPY_PY_ASYNC_AWAIT
STATIC void compile_await_object_method(compiler_t *comp, qstr method) {
EMIT_ARG(load_method, method);
EMIT_ARG(load_method, method, false);
EMIT_ARG(call_method, 0, 0, 0);
compile_yield_from(comp);
}
Expand Down Expand Up @@ -1785,7 +1785,7 @@ STATIC void compile_async_with_stmt_helper(compiler_t *comp, int n, mp_parse_nod
}

compile_load_id(comp, context);
EMIT_ARG(load_method, MP_QSTR___aexit__);
EMIT_ARG(load_method, MP_QSTR___aexit__, false);

EMIT_ARG(setup_except, try_exception_label);
compile_increase_except_level(comp);
Expand Down Expand Up @@ -2219,9 +2219,20 @@ STATIC void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *p
return;
}

// a super() call
EMIT_ARG(call_function, 2, 0, 0);
i = 1;
if (num_trail >= 3
&& MP_PARSE_NODE_STRUCT_KIND(pns_trail[1]) == PN_trailer_period
&& MP_PARSE_NODE_STRUCT_KIND(pns_trail[2]) == PN_trailer_paren) {
// optimisation for method calls super().f(...), to eliminate heap allocation
mp_parse_node_struct_t *pns_period = pns_trail[1];
mp_parse_node_struct_t *pns_paren = pns_trail[2];
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), true);
compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
i = 3;
} else {
// a super() call
EMIT_ARG(call_function, 2, 0, 0);
i = 1;
}
}

// compile the remaining trailers
Expand All @@ -2232,7 +2243,7 @@ STATIC void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *p
// optimisation for method calls a.f(...), following PyPy
mp_parse_node_struct_t *pns_period = pns_trail[i];
mp_parse_node_struct_t *pns_paren = pns_trail[i + 1];
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]));
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), false);
compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
i += 1;
} else {
Expand Down
4 changes: 2 additions & 2 deletions py/emit.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ typedef struct _emit_method_table_t {
void (*load_const_obj)(emit_t *emit, mp_obj_t obj);
void (*load_null)(emit_t *emit);
void (*load_attr)(emit_t *emit, qstr qst);
void (*load_method)(emit_t *emit, qstr qst);
void (*load_method)(emit_t *emit, qstr qst, bool is_super);
void (*load_build_class)(emit_t *emit);
void (*load_subscr)(emit_t *emit);
void (*store_attr)(emit_t *emit, qstr qst);
Expand Down Expand Up @@ -205,7 +205,7 @@ void mp_emit_bc_load_const_str(emit_t *emit, qstr qst);
void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj);
void mp_emit_bc_load_null(emit_t *emit);
void mp_emit_bc_load_attr(emit_t *emit, qstr qst);
void mp_emit_bc_load_method(emit_t *emit, qstr qst);
void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super);
void mp_emit_bc_load_build_class(emit_t *emit);
void mp_emit_bc_load_subscr(emit_t *emit);
void mp_emit_bc_store_attr(emit_t *emit, qstr qst);
Expand Down
6 changes: 3 additions & 3 deletions py/emitbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,9 +594,9 @@ void mp_emit_bc_load_attr(emit_t *emit, qstr qst) {
}
}

void mp_emit_bc_load_method(emit_t *emit, qstr qst) {
emit_bc_pre(emit, 1);
emit_write_bytecode_byte_qstr(emit, MP_BC_LOAD_METHOD, qst);
void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super) {
emit_bc_pre(emit, 1 - 2 * is_super);
emit_write_bytecode_byte_qstr(emit, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst);
}

void mp_emit_bc_load_build_class(emit_t *emit) {
Expand Down
19 changes: 13 additions & 6 deletions py/emitnative.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
[MP_F_LOAD_BUILD_CLASS] = 0,
[MP_F_LOAD_ATTR] = 2,
[MP_F_LOAD_METHOD] = 3,
[MP_F_LOAD_SUPER_METHOD] = 2,
[MP_F_STORE_NAME] = 2,
[MP_F_STORE_GLOBAL] = 2,
[MP_F_STORE_ATTR] = 3,
Expand Down Expand Up @@ -1065,12 +1066,18 @@ STATIC void emit_native_load_attr(emit_t *emit, qstr qst) {
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
}

STATIC void emit_native_load_method(emit_t *emit, qstr qst) {
vtype_kind_t vtype_base;
emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base
assert(vtype_base == VTYPE_PYOBJ);
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr
emit_call_with_imm_arg(emit, MP_F_LOAD_METHOD, qst, REG_ARG_2); // arg2 = method name
STATIC void emit_native_load_method(emit_t *emit, qstr qst, bool is_super) {
if (is_super) {
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_2, 3); // arg2 = dest ptr
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_2, 2); // arg2 = dest ptr
emit_call_with_imm_arg(emit, MP_F_LOAD_SUPER_METHOD, qst, REG_ARG_1); // arg1 = method name
} else {
vtype_kind_t vtype_base;
emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base
assert(vtype_base == VTYPE_PYOBJ);
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr
emit_call_with_imm_arg(emit, MP_F_LOAD_METHOD, qst, REG_ARG_2); // arg2 = method name
}
}

STATIC void emit_native_load_build_class(emit_t *emit) {
Expand Down
1 change: 1 addition & 0 deletions py/nativeglue.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = {
mp_load_build_class,
mp_load_attr,
mp_load_method,
mp_load_super_method,
mp_store_name,
mp_store_global,
mp_store_attr,
Expand Down
5 changes: 5 additions & 0 deletions py/objtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -1070,6 +1070,11 @@ const mp_obj_type_t mp_type_super = {
.attr = super_attr,
};

void mp_load_super_method(qstr attr, mp_obj_t *dest) {
mp_obj_super_t super = {{&mp_type_super}, dest[1], dest[2]};
mp_load_method(MP_OBJ_FROM_PTR(&super), attr, dest);
}

/******************************************************************************/
// subclassing and built-ins specific to types

Expand Down
2 changes: 1 addition & 1 deletion py/persistentcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#include "py/smallint.h"

// The current version of .mpy files
#define MPY_VERSION (1)
#define MPY_VERSION (2)

// The feature flags byte encodes the compile-time config options that
// affect the generate bytecode.
Expand Down
1 change: 1 addition & 0 deletions py/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ mp_obj_t mp_load_attr(mp_obj_t base, qstr attr);
void mp_convert_member_lookup(mp_obj_t obj, const mp_obj_type_t *type, mp_obj_t member, mp_obj_t *dest);
void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest);
void mp_load_super_method(qstr attr, mp_obj_t *dest);
void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);

mp_obj_t mp_getiter(mp_obj_t o, mp_obj_iter_buf_t *iter_buf);
Expand Down
1 change: 1 addition & 0 deletions py/runtime0.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ typedef enum {
MP_F_LOAD_BUILD_CLASS,
MP_F_LOAD_ATTR,
MP_F_LOAD_METHOD,
MP_F_LOAD_SUPER_METHOD,
MP_F_STORE_NAME,
MP_F_STORE_GLOBAL,
MP_F_STORE_ATTR,
Expand Down
5 changes: 5 additions & 0 deletions py/showbc.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ const byte *mp_bytecode_print_str(const byte *ip) {
printf("LOAD_METHOD %s", qstr_str(qst));
break;

case MP_BC_LOAD_SUPER_METHOD:
DECODE_QSTR;
printf("LOAD_SUPER_METHOD %s", qstr_str(qst));
break;

case MP_BC_LOAD_BUILD_CLASS:
printf("LOAD_BUILD_CLASS");
break;
Expand Down
8 changes: 8 additions & 0 deletions py/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,14 @@ run_code_state: ;
DISPATCH();
}

ENTRY(MP_BC_LOAD_SUPER_METHOD): {
MARK_EXC_IP_SELECTIVE();
DECODE_QSTR;
sp -= 1;
mp_load_super_method(qst, sp - 1);
DISPATCH();
}

ENTRY(MP_BC_LOAD_BUILD_CLASS):
MARK_EXC_IP_SELECTIVE();
PUSH(mp_load_build_class());
Expand Down
1 change: 1 addition & 0 deletions py/vmentrytable.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ static const void *const entry_table[256] = {
[MP_BC_LOAD_GLOBAL] = &&entry_MP_BC_LOAD_GLOBAL,
[MP_BC_LOAD_ATTR] = &&entry_MP_BC_LOAD_ATTR,
[MP_BC_LOAD_METHOD] = &&entry_MP_BC_LOAD_METHOD,
[MP_BC_LOAD_SUPER_METHOD] = &&entry_MP_BC_LOAD_SUPER_METHOD,
[MP_BC_LOAD_BUILD_CLASS] = &&entry_MP_BC_LOAD_BUILD_CLASS,
[MP_BC_LOAD_SUBSCR] = &&entry_MP_BC_LOAD_SUBSCR,
[MP_BC_STORE_FAST_N] = &&entry_MP_BC_STORE_FAST_N,
Expand Down
4 changes: 2 additions & 2 deletions tools/mpy-tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __str__(self):
return 'error while freezing %s: %s' % (self.rawcode.source_file, self.msg)

class Config:
MPY_VERSION = 1
MPY_VERSION = 2
MICROPY_LONGINT_IMPL_NONE = 0
MICROPY_LONGINT_IMPL_LONGLONG = 1
MICROPY_LONGINT_IMPL_MPZ = 2
Expand Down Expand Up @@ -94,7 +94,7 @@ def OC4(a, b, c, d):
OC4(U, U, U, U), # 0x0c-0x0f
OC4(B, B, B, U), # 0x10-0x13
OC4(V, U, Q, V), # 0x14-0x17
OC4(B, U, V, V), # 0x18-0x1b
OC4(B, V, V, Q), # 0x18-0x1b
OC4(Q, Q, Q, Q), # 0x1c-0x1f
OC4(B, B, V, V), # 0x20-0x23
OC4(Q, Q, Q, B), # 0x24-0x27
Expand Down

0 comments on commit dd11af2

Please sign in to comment.