Skip to content

Commit dd11af2

Browse files
committed
py: Add LOAD_SUPER_METHOD bytecode to allow heap-free super meth calls.
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
1 parent 5335942 commit dd11af2

16 files changed

Lines changed: 68 additions & 27 deletions

minimal/frozentest.mpy

0 Bytes
Binary file not shown.

py/bc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ STATIC const byte opcode_format_table[64] = {
304304
OC4(U, U, U, U), // 0x0c-0x0f
305305
OC4(B, B, B, U), // 0x10-0x13
306306
OC4(V, U, Q, V), // 0x14-0x17
307-
OC4(B, U, V, V), // 0x18-0x1b
307+
OC4(B, V, V, Q), // 0x18-0x1b
308308
OC4(Q, Q, Q, Q), // 0x1c-0x1f
309309
OC4(B, B, V, V), // 0x20-0x23
310310
OC4(Q, Q, Q, B), // 0x24-0x27

py/bc0.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@
3737
#define MP_BC_LOAD_CONST_OBJ (0x17) // ptr
3838
#define MP_BC_LOAD_NULL (0x18)
3939

40-
#define MP_BC_LOAD_FAST_N (0x1a) // uint
41-
#define MP_BC_LOAD_DEREF (0x1b) // uint
42-
#define MP_BC_LOAD_NAME (0x1c) // qstr
43-
#define MP_BC_LOAD_GLOBAL (0x1d) // qstr
44-
#define MP_BC_LOAD_ATTR (0x1e) // qstr
45-
#define MP_BC_LOAD_METHOD (0x1f) // qstr
40+
#define MP_BC_LOAD_FAST_N (0x19) // uint
41+
#define MP_BC_LOAD_DEREF (0x1a) // uint
42+
#define MP_BC_LOAD_NAME (0x1b) // qstr
43+
#define MP_BC_LOAD_GLOBAL (0x1c) // qstr
44+
#define MP_BC_LOAD_ATTR (0x1d) // qstr
45+
#define MP_BC_LOAD_METHOD (0x1e) // qstr
46+
#define MP_BC_LOAD_SUPER_METHOD (0x1f) // qstr
4647
#define MP_BC_LOAD_BUILD_CLASS (0x20)
4748
#define MP_BC_LOAD_SUBSCR (0x21)
4849

py/compile.c

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,7 +1694,7 @@ STATIC void compile_yield_from(compiler_t *comp) {
16941694

16951695
#if MICROPY_PY_ASYNC_AWAIT
16961696
STATIC void compile_await_object_method(compiler_t *comp, qstr method) {
1697-
EMIT_ARG(load_method, method);
1697+
EMIT_ARG(load_method, method, false);
16981698
EMIT_ARG(call_method, 0, 0, 0);
16991699
compile_yield_from(comp);
17001700
}
@@ -1785,7 +1785,7 @@ STATIC void compile_async_with_stmt_helper(compiler_t *comp, int n, mp_parse_nod
17851785
}
17861786

17871787
compile_load_id(comp, context);
1788-
EMIT_ARG(load_method, MP_QSTR___aexit__);
1788+
EMIT_ARG(load_method, MP_QSTR___aexit__, false);
17891789

17901790
EMIT_ARG(setup_except, try_exception_label);
17911791
compile_increase_except_level(comp);
@@ -2219,9 +2219,20 @@ STATIC void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *p
22192219
return;
22202220
}
22212221

2222-
// a super() call
2223-
EMIT_ARG(call_function, 2, 0, 0);
2224-
i = 1;
2222+
if (num_trail >= 3
2223+
&& MP_PARSE_NODE_STRUCT_KIND(pns_trail[1]) == PN_trailer_period
2224+
&& MP_PARSE_NODE_STRUCT_KIND(pns_trail[2]) == PN_trailer_paren) {
2225+
// optimisation for method calls super().f(...), to eliminate heap allocation
2226+
mp_parse_node_struct_t *pns_period = pns_trail[1];
2227+
mp_parse_node_struct_t *pns_paren = pns_trail[2];
2228+
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), true);
2229+
compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
2230+
i = 3;
2231+
} else {
2232+
// a super() call
2233+
EMIT_ARG(call_function, 2, 0, 0);
2234+
i = 1;
2235+
}
22252236
}
22262237

22272238
// compile the remaining trailers
@@ -2232,7 +2243,7 @@ STATIC void compile_atom_expr_normal(compiler_t *comp, mp_parse_node_struct_t *p
22322243
// optimisation for method calls a.f(...), following PyPy
22332244
mp_parse_node_struct_t *pns_period = pns_trail[i];
22342245
mp_parse_node_struct_t *pns_paren = pns_trail[i + 1];
2235-
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]));
2246+
EMIT_ARG(load_method, MP_PARSE_NODE_LEAF_ARG(pns_period->nodes[0]), false);
22362247
compile_trailer_paren_helper(comp, pns_paren->nodes[0], true, 0);
22372248
i += 1;
22382249
} else {

py/emit.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ typedef struct _emit_method_table_t {
8888
void (*load_const_obj)(emit_t *emit, mp_obj_t obj);
8989
void (*load_null)(emit_t *emit);
9090
void (*load_attr)(emit_t *emit, qstr qst);
91-
void (*load_method)(emit_t *emit, qstr qst);
91+
void (*load_method)(emit_t *emit, qstr qst, bool is_super);
9292
void (*load_build_class)(emit_t *emit);
9393
void (*load_subscr)(emit_t *emit);
9494
void (*store_attr)(emit_t *emit, qstr qst);
@@ -205,7 +205,7 @@ void mp_emit_bc_load_const_str(emit_t *emit, qstr qst);
205205
void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj);
206206
void mp_emit_bc_load_null(emit_t *emit);
207207
void mp_emit_bc_load_attr(emit_t *emit, qstr qst);
208-
void mp_emit_bc_load_method(emit_t *emit, qstr qst);
208+
void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super);
209209
void mp_emit_bc_load_build_class(emit_t *emit);
210210
void mp_emit_bc_load_subscr(emit_t *emit);
211211
void mp_emit_bc_store_attr(emit_t *emit, qstr qst);

py/emitbc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -594,9 +594,9 @@ void mp_emit_bc_load_attr(emit_t *emit, qstr qst) {
594594
}
595595
}
596596

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

602602
void mp_emit_bc_load_build_class(emit_t *emit) {

py/emitnative.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
8585
[MP_F_LOAD_BUILD_CLASS] = 0,
8686
[MP_F_LOAD_ATTR] = 2,
8787
[MP_F_LOAD_METHOD] = 3,
88+
[MP_F_LOAD_SUPER_METHOD] = 2,
8889
[MP_F_STORE_NAME] = 2,
8990
[MP_F_STORE_GLOBAL] = 2,
9091
[MP_F_STORE_ATTR] = 3,
@@ -1065,12 +1066,18 @@ STATIC void emit_native_load_attr(emit_t *emit, qstr qst) {
10651066
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
10661067
}
10671068

1068-
STATIC void emit_native_load_method(emit_t *emit, qstr qst) {
1069-
vtype_kind_t vtype_base;
1070-
emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base
1071-
assert(vtype_base == VTYPE_PYOBJ);
1072-
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr
1073-
emit_call_with_imm_arg(emit, MP_F_LOAD_METHOD, qst, REG_ARG_2); // arg2 = method name
1069+
STATIC void emit_native_load_method(emit_t *emit, qstr qst, bool is_super) {
1070+
if (is_super) {
1071+
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_2, 3); // arg2 = dest ptr
1072+
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_2, 2); // arg2 = dest ptr
1073+
emit_call_with_imm_arg(emit, MP_F_LOAD_SUPER_METHOD, qst, REG_ARG_1); // arg1 = method name
1074+
} else {
1075+
vtype_kind_t vtype_base;
1076+
emit_pre_pop_reg(emit, &vtype_base, REG_ARG_1); // arg1 = base
1077+
assert(vtype_base == VTYPE_PYOBJ);
1078+
emit_get_stack_pointer_to_reg_for_push(emit, REG_ARG_3, 2); // arg3 = dest ptr
1079+
emit_call_with_imm_arg(emit, MP_F_LOAD_METHOD, qst, REG_ARG_2); // arg2 = method name
1080+
}
10741081
}
10751082

10761083
STATIC void emit_native_load_build_class(emit_t *emit) {

py/nativeglue.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ void *const mp_fun_table[MP_F_NUMBER_OF] = {
133133
mp_load_build_class,
134134
mp_load_attr,
135135
mp_load_method,
136+
mp_load_super_method,
136137
mp_store_name,
137138
mp_store_global,
138139
mp_store_attr,

py/objtype.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,11 @@ const mp_obj_type_t mp_type_super = {
10701070
.attr = super_attr,
10711071
};
10721072

1073+
void mp_load_super_method(qstr attr, mp_obj_t *dest) {
1074+
mp_obj_super_t super = {{&mp_type_super}, dest[1], dest[2]};
1075+
mp_load_method(MP_OBJ_FROM_PTR(&super), attr, dest);
1076+
}
1077+
10731078
/******************************************************************************/
10741079
// subclassing and built-ins specific to types
10751080

py/persistentcode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
#include "py/smallint.h"
4040

4141
// The current version of .mpy files
42-
#define MPY_VERSION (1)
42+
#define MPY_VERSION (2)
4343

4444
// The feature flags byte encodes the compile-time config options that
4545
// affect the generate bytecode.

0 commit comments

Comments
 (0)