Skip to content

Commit 3558f62

Browse files
committed
py: Making closures now passes pointer to stack, not a tuple for vars.
Closed over variables are now passed on the stack, instead of creating a tuple and passing that. This way memory for the closed over variables can be allocated within the closure object itself. See issue micropython#510 for background.
1 parent bc5f0c1 commit 3558f62

11 files changed

Lines changed: 71 additions & 56 deletions

File tree

py/compile.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -889,8 +889,7 @@ void close_over_variables_etc(compiler_t *comp, scope_t *this_scope, int n_pos_d
889889
if (nfree == 0) {
890890
EMIT_ARG(make_function, this_scope, n_pos_defaults, n_kw_defaults);
891891
} else {
892-
EMIT_ARG(build_tuple, nfree);
893-
EMIT_ARG(make_closure, this_scope, n_pos_defaults, n_kw_defaults);
892+
EMIT_ARG(make_closure, this_scope, nfree, n_pos_defaults, n_kw_defaults);
894893
}
895894
}
896895

@@ -957,6 +956,8 @@ void compile_funcdef_param(compiler_t *comp, mp_parse_node_t pn) {
957956
// we need to do this here before we start building the map for the default keywords
958957
if (comp->num_default_params > 0) {
959958
EMIT_ARG(build_tuple, comp->num_default_params);
959+
} else {
960+
EMIT(load_null); // sentinel indicating empty default positional args
960961
}
961962
// first default dict param, so make the map
962963
EMIT_ARG(build_map, 0);
@@ -1009,6 +1010,7 @@ qstr compile_funcdef_helper(compiler_t *comp, mp_parse_node_struct_t *pns, uint
10091010
// the default keywords args may have already made the tuple; if not, do it now
10101011
if (comp->num_default_params > 0 && comp->num_dict_params == 0) {
10111012
EMIT_ARG(build_tuple, comp->num_default_params);
1013+
EMIT(load_null); // sentinel indicating empty default keyword args
10121014
}
10131015
#endif
10141016

py/emit.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ typedef struct _emit_method_table_t {
4242
void (*load_const_id)(emit_t *emit, qstr qstr);
4343
void (*load_const_str)(emit_t *emit, qstr qstr, bool bytes);
4444
void (*load_const_verbatim_str)(emit_t *emit, const char *str); // only needed for emitcpy
45+
void (*load_null)(emit_t *emit);
4546
void (*load_fast)(emit_t *emit, qstr qstr, uint id_flags, int local_num);
4647
void (*load_deref)(emit_t *emit, qstr qstr, int local_num);
4748
void (*load_closure)(emit_t *emit, qstr qstr, int local_num); // only needed for emitcpy
@@ -100,7 +101,7 @@ typedef struct _emit_method_table_t {
100101
void (*unpack_sequence)(emit_t *emit, int n_args);
101102
void (*unpack_ex)(emit_t *emit, int n_left, int n_right);
102103
void (*make_function)(emit_t *emit, scope_t *scope, uint n_pos_defaults, uint n_kw_defaults);
103-
void (*make_closure)(emit_t *emit, scope_t *scope, uint n_pos_defaults, uint n_kw_defaults);
104+
void (*make_closure)(emit_t *emit, scope_t *scope, uint n_closed_over, uint n_pos_defaults, uint n_kw_defaults);
104105
void (*call_function)(emit_t *emit, int n_positional, int n_keyword, uint star_flags);
105106
void (*call_method)(emit_t *emit, int n_positional, int n_keyword, uint star_flags);
106107
void (*return_value)(emit_t *emit);

py/emitbc.c

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -761,35 +761,21 @@ STATIC void emit_bc_make_function(emit_t *emit, scope_t *scope, uint n_pos_defau
761761
emit_bc_pre(emit, 1);
762762
emit_write_byte_code_byte_ptr(emit, MP_BC_MAKE_FUNCTION, scope->raw_code);
763763
} else {
764-
if (n_pos_defaults == 0) {
765-
// load dummy entry for non-existent positional default tuple
766-
emit_bc_load_null(emit);
767-
emit_bc_rot_two(emit);
768-
} else if (n_kw_defaults == 0) {
769-
// load dummy entry for non-existent keyword default dict
770-
emit_bc_load_null(emit);
771-
}
772764
emit_bc_pre(emit, -1);
773765
emit_write_byte_code_byte_ptr(emit, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code);
774766
}
775767
}
776768

777-
STATIC void emit_bc_make_closure(emit_t *emit, scope_t *scope, uint n_pos_defaults, uint n_kw_defaults) {
769+
STATIC void emit_bc_make_closure(emit_t *emit, scope_t *scope, uint n_closed_over, uint n_pos_defaults, uint n_kw_defaults) {
778770
if (n_pos_defaults == 0 && n_kw_defaults == 0) {
779-
emit_bc_pre(emit, 0);
771+
emit_bc_pre(emit, -n_closed_over + 1);
780772
emit_write_byte_code_byte_ptr(emit, MP_BC_MAKE_CLOSURE, scope->raw_code);
773+
emit_write_byte_code_byte(emit, n_closed_over);
781774
} else {
782-
if (n_pos_defaults == 0) {
783-
// load dummy entry for non-existent positional default tuple
784-
emit_bc_load_null(emit);
785-
emit_bc_rot_three(emit);
786-
} else if (n_kw_defaults == 0) {
787-
// load dummy entry for non-existent keyword default dict
788-
emit_bc_load_null(emit);
789-
emit_bc_rot_two(emit);
790-
}
791-
emit_bc_pre(emit, -2);
775+
assert(n_closed_over <= 255);
776+
emit_bc_pre(emit, -2 - n_closed_over + 1);
792777
emit_write_byte_code_byte_ptr(emit, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code);
778+
emit_write_byte_code_byte(emit, n_closed_over);
793779
}
794780
}
795781

@@ -870,6 +856,7 @@ const emit_method_table_t emit_bc_method_table = {
870856
emit_bc_load_const_id,
871857
emit_bc_load_const_str,
872858
emit_bc_load_const_verbatim_str,
859+
emit_bc_load_null,
873860
emit_bc_load_fast,
874861
emit_bc_load_deref,
875862
emit_bc_load_closure,

py/emitcpy.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,11 @@ STATIC void emit_cpy_load_const_verbatim_str(emit_t *emit, const char *str) {
228228
}
229229
}
230230

231+
STATIC void emit_cpy_load_null(emit_t *emit) {
232+
// unused for cpy
233+
assert(0);
234+
}
235+
231236
STATIC void emit_cpy_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
232237
emit_pre(emit, 1, 3);
233238
if (emit->pass == PASS_3) {
@@ -764,7 +769,8 @@ STATIC void emit_cpy_make_function(emit_t *emit, scope_t *scope, uint n_pos_defa
764769
}
765770
}
766771

767-
STATIC void emit_cpy_make_closure(emit_t *emit, scope_t *scope, uint n_pos_defaults, uint n_kw_defaults) {
772+
STATIC void emit_cpy_make_closure(emit_t *emit, scope_t *scope, uint n_closed_over, uint n_pos_defaults, uint n_kw_defaults) {
773+
emit_cpy_build_tuple(emit, n_closed_over);
768774
load_cpy_const_code_and_name(emit, scope->simple_name);
769775
emit_pre(emit, -2 - n_pos_defaults - 2 * n_kw_defaults, 3);
770776
if (emit->pass == PASS_3) {
@@ -815,6 +821,7 @@ const emit_method_table_t emit_cpython_method_table = {
815821
emit_cpy_load_const_id,
816822
emit_cpy_load_const_str,
817823
emit_cpy_load_const_verbatim_str,
824+
emit_cpy_load_null,
818825
emit_cpy_load_fast,
819826
emit_cpy_load_deref,
820827
emit_cpy_load_closure,

py/emitglue.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,17 @@ mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp
158158
return fun;
159159
}
160160

161-
mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, mp_obj_t closure_tuple, mp_obj_t def_args, mp_obj_t def_kw_args) {
162-
DEBUG_OP_printf("make_closure_from_raw_code %p\n", rc);
161+
mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, uint n_closed_over, const mp_obj_t *args) {
162+
DEBUG_OP_printf("make_closure_from_raw_code %p %u %p\n", rc, n_closed_over, argrs);
163163
// make function object
164-
mp_obj_t ffun = mp_make_function_from_raw_code(rc, def_args, def_kw_args);
164+
mp_obj_t ffun;
165+
if (n_closed_over & 0x100) {
166+
// default positional and keyword args given
167+
ffun = mp_make_function_from_raw_code(rc, args[0], args[1]);
168+
} else {
169+
// default positional and keyword args not given
170+
ffun = mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL);
171+
}
165172
// wrap function in closure object
166-
return mp_obj_new_closure(ffun, closure_tuple);
173+
return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2));
167174
}

py/emitglue.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@ void mp_emit_glue_assign_native_code(mp_raw_code_t *rc, void *f, uint len, int n
3737
void mp_emit_glue_assign_inline_asm_code(mp_raw_code_t *rc, void *f, uint len, int n_args);
3838

3939
mp_obj_t mp_make_function_from_raw_code(mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
40-
mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, mp_obj_t closure_tuple, mp_obj_t def_args, mp_obj_t def_kw_args);
40+
mp_obj_t mp_make_closure_from_raw_code(mp_raw_code_t *rc, uint n_closed_over, const mp_obj_t *args);

py/emitnative.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,11 @@ STATIC void emit_native_load_const_verbatim_str(emit_t *emit, const char *str) {
700700
assert(0);
701701
}
702702

703+
STATIC void emit_native_load_null(emit_t *emit) {
704+
emit_native_pre(emit);
705+
emit_post_push_imm(emit, VTYPE_PYOBJ, 0);
706+
}
707+
703708
STATIC void emit_native_load_fast(emit_t *emit, qstr qstr, uint id_flags, int local_num) {
704709
vtype_kind_t vtype = emit->local_vtype[local_num];
705710
if (vtype == VTYPE_UNBOUND) {
@@ -1209,7 +1214,7 @@ STATIC void emit_native_make_function(emit_t *emit, scope_t *scope, uint n_pos_d
12091214
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
12101215
}
12111216

1212-
STATIC void emit_native_make_closure(emit_t *emit, scope_t *scope, uint n_pos_defaults, uint n_kw_defaults) {
1217+
STATIC void emit_native_make_closure(emit_t *emit, scope_t *scope, uint n_closed_over, uint n_pos_defaults, uint n_kw_defaults) {
12131218
assert(0);
12141219
}
12151220

@@ -1335,6 +1340,7 @@ const emit_method_table_t EXPORT_FUN(method_table) = {
13351340
emit_native_load_const_id,
13361341
emit_native_load_const_str,
13371342
emit_native_load_const_verbatim_str,
1343+
emit_native_load_null,
13381344
emit_native_load_fast,
13391345
emit_native_load_deref,
13401346
emit_native_load_closure,

py/emitpass1.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,5 @@ const emit_method_table_t emit_pass1_method_table = {
191191
(void*)emit_pass1_dummy,
192192
(void*)emit_pass1_dummy,
193193
(void*)emit_pass1_dummy,
194+
(void*)emit_pass1_dummy,
194195
};

py/obj.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char
357357
mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t def_args, const byte *code);
358358
mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun);
359359
mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
360-
mp_obj_t mp_obj_new_closure(mp_obj_t fun, mp_obj_t closure_tuple);
360+
mp_obj_t mp_obj_new_closure(mp_obj_t fun, uint n_closed, const mp_obj_t *closed);
361361
mp_obj_t mp_obj_new_tuple(uint n, const mp_obj_t *items);
362362
mp_obj_t mp_obj_new_list(uint n, mp_obj_t *items);
363363
mp_obj_t mp_obj_new_dict(int n_args);

py/objclosure.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,28 @@
1111
typedef struct _mp_obj_closure_t {
1212
mp_obj_base_t base;
1313
mp_obj_t fun;
14-
mp_obj_tuple_t *closure_tuple;
14+
machine_uint_t n_closed;
15+
mp_obj_t closed[];
1516
} mp_obj_closure_t;
1617

1718
mp_obj_t closure_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *args) {
1819
mp_obj_closure_t *self = self_in;
19-
mp_obj_tuple_t *t = self->closure_tuple;
2020

2121
// need to concatenate closed-over-vars and args
2222

23-
int n_total = t->len + n_args + 2 * n_kw;
23+
int n_total = self->n_closed + n_args + 2 * n_kw;
2424
if (n_total <= 5) {
2525
// use stack to allocate temporary args array
2626
mp_obj_t args2[5];
27-
memcpy(args2, t->items, t->len * sizeof(mp_obj_t));
28-
memcpy(args2 + t->len, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t));
29-
return mp_call_function_n_kw(self->fun, t->len + n_args, n_kw, args2);
27+
memcpy(args2, self->closed, self->n_closed * sizeof(mp_obj_t));
28+
memcpy(args2 + self->n_closed, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t));
29+
return mp_call_function_n_kw(self->fun, self->n_closed + n_args, n_kw, args2);
3030
} else {
3131
// use heap to allocate temporary args array
3232
mp_obj_t *args2 = m_new(mp_obj_t, n_total);
33-
memcpy(args2, t->items, t->len * sizeof(mp_obj_t));
34-
memcpy(args2 + t->len, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t));
35-
mp_obj_t res = mp_call_function_n_kw(self->fun, t->len + n_args, n_kw, args2);
33+
memcpy(args2, self->closed, self->n_closed * sizeof(mp_obj_t));
34+
memcpy(args2 + self->n_closed, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t));
35+
mp_obj_t res = mp_call_function_n_kw(self->fun, self->n_closed + n_args, n_kw, args2);
3636
m_del(mp_obj_t, args2, n_total);
3737
return res;
3838
}
@@ -41,13 +41,12 @@ mp_obj_t closure_call(mp_obj_t self_in, uint n_args, uint n_kw, const mp_obj_t *
4141
#if 0
4242
STATIC void closure_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in, mp_print_kind_t kind) {
4343
mp_obj_closure_t *o = o_in;
44-
print(env, "<closure %p (closed: %p) ", o, o->closure_tuple);
45-
mp_obj_tuple_t *t = o->closure_tuple;
46-
for (int i = 0; i < t->len; i++) {
47-
if (t->items[i] == MP_OBJ_NULL) {
44+
print(env, "<closure %p, n_closed=%u ", o, o->n_closed);
45+
for (int i = 0; i < o->n_closed; i++) {
46+
if (o->closed[i] == MP_OBJ_NULL) {
4847
print(env, "(nil)");
4948
} else {
50-
mp_obj_print_helper(print, env, t->items[i], PRINT_REPR);
49+
mp_obj_print_helper(print, env, o->closed[i], PRINT_REPR);
5150
}
5251
print(env, " ");
5352
}
@@ -62,10 +61,11 @@ const mp_obj_type_t closure_type = {
6261
.call = closure_call,
6362
};
6463

65-
mp_obj_t mp_obj_new_closure(mp_obj_t fun, mp_obj_t closure_tuple) {
66-
mp_obj_closure_t *o = m_new_obj(mp_obj_closure_t);
64+
mp_obj_t mp_obj_new_closure(mp_obj_t fun, uint n_closed_over, const mp_obj_t *closed) {
65+
mp_obj_closure_t *o = m_new_obj_var(mp_obj_closure_t, mp_obj_t, n_closed_over);
6766
o->base.type = &closure_type;
6867
o->fun = fun;
69-
o->closure_tuple = closure_tuple;
68+
o->n_closed = n_closed_over;
69+
memcpy(o->closed, closed, n_closed_over * sizeof(mp_obj_t));
7070
return o;
7171
}

0 commit comments

Comments
 (0)