Skip to content

Commit e2ff00e

Browse files
committed
py/emitglue: Introduce mp_proto_fun_t as a more general mp_raw_code_t.
Allows bytecode itself to be used instead of an mp_raw_code_t in the simple and common cases of a bytecode function without any children. This can be used to further reduce frozen code size, and has the potential to optimise other areas like importing. Signed-off-by: Damien George <[email protected]>
1 parent 5e3006f commit e2ff00e

15 files changed

+63
-39
lines changed

docs/develop/compiler.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ The most relevant method you should know about is this:
157157
mp_compile_to_raw_code(parse_tree, source_file, is_repl, &cm);
158158
159159
// Create and return a function object that executes the outer module.
160-
return mp_make_function_from_raw_code(cm.rc, cm.context, NULL);
160+
return mp_make_function_from_proto_fun(cm.rc, cm.context, NULL);
161161
}
162162
163163
The compiler compiles the code in four passes: scope, stack size, code size and emit.

ports/embed/port/embed_util.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ void mp_embed_exec_mpy(const uint8_t *mpy, size_t len) {
6969
mp_compiled_module_t cm;
7070
cm.context = ctx;
7171
mp_raw_code_load_mem(mpy, len, &cm);
72-
mp_obj_t f = mp_make_function_from_raw_code(cm.rc, ctx, MP_OBJ_NULL);
72+
mp_obj_t f = mp_make_function_from_proto_fun(cm.rc, ctx, MP_OBJ_NULL);
7373
mp_call_function_0(f);
7474
nlr_pop();
7575
} else {

py/bc.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@
4444
// prelude size : var uint
4545
// contains two values interleaved bit-wise as: xIIIIIIC repeated
4646
// x = extension another byte follows
47-
// I = n_info number of bytes in source info section
47+
// I = n_info number of bytes in source info section (always > 0)
4848
// C = n_cells number of bytes/cells in closure section
4949
//
5050
// source info section:
51-
// simple_name : var qstr
51+
// simple_name : var qstr always exists
5252
// argname0 : var qstr
5353
// ... : var qstr
5454
// argnameN : var qstr N = num_pos_args + num_kwonly_args - 1
@@ -226,7 +226,7 @@ typedef struct _mp_compiled_module_t {
226226
// Outer level struct defining a frozen module.
227227
typedef struct _mp_frozen_module_t {
228228
const mp_module_constants_t constants;
229-
const struct _mp_raw_code_t *rc;
229+
const void *proto_fun;
230230
} mp_frozen_module_t;
231231

232232
// State for an executing function.

py/builtinimport.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ STATIC void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) {
165165
#endif
166166

167167
#if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY
168-
STATIC void do_execute_raw_code(const mp_module_context_t *context, const mp_raw_code_t *rc, qstr source_name) {
168+
STATIC void do_execute_proto_fun(const mp_module_context_t *context, mp_proto_fun_t proto_fun, qstr source_name) {
169169
#if MICROPY_PY___FILE__
170170
mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
171171
#else
@@ -188,7 +188,7 @@ STATIC void do_execute_raw_code(const mp_module_context_t *context, const mp_raw
188188
nlr_push_jump_callback(&ctx.callback, mp_globals_locals_set_from_nlr_jump_callback);
189189

190190
// make and execute the function
191-
mp_obj_t module_fun = mp_make_function_from_raw_code(rc, context, NULL);
191+
mp_obj_t module_fun = mp_make_function_from_proto_fun(proto_fun, context, NULL);
192192
mp_call_function_0(module_fun);
193193

194194
// deregister exception handler and restore context
@@ -230,7 +230,7 @@ STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) {
230230
#else
231231
qstr frozen_file_qstr = MP_QSTRnull;
232232
#endif
233-
do_execute_raw_code(module_obj, frozen->rc, frozen_file_qstr);
233+
do_execute_proto_fun(module_obj, frozen->proto_fun, frozen_file_qstr);
234234
return;
235235
}
236236
#endif
@@ -247,7 +247,7 @@ STATIC void do_load(mp_module_context_t *module_obj, vstr_t *file) {
247247
mp_compiled_module_t cm;
248248
cm.context = module_obj;
249249
mp_raw_code_load_file(file_qstr, &cm);
250-
do_execute_raw_code(cm.context, cm.rc, file_qstr);
250+
do_execute_proto_fun(cm.context, cm.rc, file_qstr);
251251
return;
252252
}
253253
#endif

py/compile.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -3667,7 +3667,7 @@ mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl)
36673667
cm.context->module.globals = mp_globals_get();
36683668
mp_compile_to_raw_code(parse_tree, source_file, is_repl, &cm);
36693669
// return function that executes the outer module
3670-
return mp_make_function_from_raw_code(cm.rc, cm.context, NULL);
3670+
return mp_make_function_from_proto_fun(cm.rc, cm.context, NULL);
36713671
}
36723672

36733673
#endif // MICROPY_ENABLE_COMPILER

py/dynruntime.h

+5-3
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type
196196
#define mp_unary_op(op, obj) (mp_fun_table.unary_op((op), (obj)))
197197
#define mp_binary_op(op, lhs, rhs) (mp_fun_table.binary_op((op), (lhs), (rhs)))
198198

199-
#define mp_make_function_from_raw_code(rc, context, def_args) \
200-
(mp_fun_table.make_function_from_raw_code((rc), (context), (def_args)))
199+
#define mp_make_function_from_proto_fun(rc, context, def_args) \
200+
(mp_fun_table.make_function_from_proto_fun((rc), (context), (def_args)))
201201

202202
#define mp_call_function_n_kw(fun, n_args, n_kw, args) \
203203
(mp_fun_table.call_function_n_kw((fun), (n_args) | ((n_kw) << 8), args))
@@ -208,6 +208,8 @@ static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type
208208
#define MP_DYNRUNTIME_INIT_ENTRY \
209209
mp_obj_t old_globals = mp_fun_table.swap_globals(self->context->module.globals); \
210210
mp_raw_code_truncated_t rc; \
211+
rc.proto_fun_indicator[0] = MP_PROTO_FUN_INDICATOR_RAW_CODE_0; \
212+
rc.proto_fun_indicator[1] = MP_PROTO_FUN_INDICATOR_RAW_CODE_1; \
211213
rc.kind = MP_CODE_NATIVE_VIPER; \
212214
rc.is_generator = 0; \
213215
(void)rc;
@@ -217,7 +219,7 @@ static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type
217219
return mp_const_none;
218220

219221
#define MP_DYNRUNTIME_MAKE_FUNCTION(f) \
220-
(mp_make_function_from_raw_code((rc.fun_data = (f), (const mp_raw_code_t *)&rc), self->context, NULL))
222+
(mp_make_function_from_proto_fun((rc.fun_data = (f), (const mp_raw_code_t *)&rc), self->context, NULL))
221223

222224
#define mp_import_name(name, fromlist, level) \
223225
(mp_fun_table.import_name((name), (fromlist), (level)))

py/emitglue.c

+10-7
Original file line numberDiff line numberDiff line change
@@ -173,16 +173,19 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void
173173
}
174174
#endif
175175

176-
mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, const mp_obj_t *def_args) {
177-
DEBUG_OP_printf("make_function_from_raw_code %p\n", rc);
178-
assert(rc != NULL);
176+
mp_obj_t mp_make_function_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, const mp_obj_t *def_args) {
177+
DEBUG_OP_printf("make_function_from_proto_fun %p\n", proto_fun);
178+
assert(proto_fun != NULL);
179179

180180
// def_args must be MP_OBJ_NULL or a tuple
181181
assert(def_args == NULL || def_args[0] == MP_OBJ_NULL || mp_obj_is_type(def_args[0], &mp_type_tuple));
182182

183183
// def_kw_args must be MP_OBJ_NULL or a dict
184184
assert(def_args == NULL || def_args[1] == MP_OBJ_NULL || mp_obj_is_type(def_args[1], &mp_type_dict));
185185

186+
// the proto-function is a mp_raw_code_t
187+
const mp_raw_code_t *rc = proto_fun;
188+
186189
// make the function, depending on the raw code kind
187190
mp_obj_t fun;
188191
switch (rc->kind) {
@@ -221,16 +224,16 @@ mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module
221224
return fun;
222225
}
223226

224-
mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args) {
225-
DEBUG_OP_printf("make_closure_from_raw_code %p " UINT_FMT " %p\n", rc, n_closed_over, args);
227+
mp_obj_t mp_make_closure_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args) {
228+
DEBUG_OP_printf("make_closure_from_proto_fun %p " UINT_FMT " %p\n", proto_fun, n_closed_over, args);
226229
// make function object
227230
mp_obj_t ffun;
228231
if (n_closed_over & 0x100) {
229232
// default positional and keyword args given
230-
ffun = mp_make_function_from_raw_code(rc, context, args);
233+
ffun = mp_make_function_from_proto_fun(proto_fun, context, args);
231234
} else {
232235
// default positional and keyword args not given
233-
ffun = mp_make_function_from_raw_code(rc, context, NULL);
236+
ffun = mp_make_function_from_proto_fun(proto_fun, context, NULL);
234237
}
235238
// wrap function in closure object
236239
return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2));

py/emitglue.h

+22-5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@
3131

3232
// These variables and functions glue the code emitters to the runtime.
3333

34+
// Used with mp_raw_code_t::proto_fun_indicator to detect if a mp_proto_fun_t is a
35+
// mp_raw_code_t struct or a direct pointer to bytecode.
36+
#define MP_PROTO_FUN_INDICATOR_RAW_CODE_0 (0)
37+
#define MP_PROTO_FUN_INDICATOR_RAW_CODE_1 (0)
38+
3439
// These must fit in 8 bits; see scope.h
3540
enum {
3641
MP_EMIT_OPT_NONE,
@@ -49,14 +54,25 @@ typedef enum {
4954
MP_CODE_NATIVE_ASM,
5055
} mp_raw_code_kind_t;
5156

52-
// This mp_raw_code_t struct holds static information about a non-instantiated function.
57+
// An mp_proto_fun_t points to static information about a non-instantiated function.
5358
// A function object is created from this information, and that object can then be executed.
54-
//
55-
// This struct appears in the following places:
59+
// It points either to bytecode, or an mp_raw_code_t struct.
60+
typedef const void *mp_proto_fun_t;
61+
62+
// Bytecode is distinguished from an mp_raw_code_t struct by the first two bytes: bytecode
63+
// is guaranteed to have either its first or second byte non-zero. So if both bytes are
64+
// zero then the mp_proto_fun_t pointer must be an mp_raw_code_t.
65+
static inline bool mp_proto_fun_is_bytecode(mp_proto_fun_t proto_fun) {
66+
const uint8_t *header = proto_fun;
67+
return (header[0] | (header[1] << 8)) != (MP_PROTO_FUN_INDICATOR_RAW_CODE_0 | (MP_PROTO_FUN_INDICATOR_RAW_CODE_1 << 8));
68+
}
69+
70+
// The mp_raw_code_t struct appears in the following places:
5671
// compiled bytecode: instance in RAM, referenced by outer scope, usually freed after first (and only) use
5772
// mpy file: instance in RAM, created when .mpy file is loaded (same comments as above)
5873
// frozen: instance in ROM
5974
typedef struct _mp_raw_code_t {
75+
uint8_t proto_fun_indicator[2];
6076
uint8_t kind; // of type mp_raw_code_kind_t; only 3 bits used
6177
bool is_generator;
6278
const void *fun_data;
@@ -88,6 +104,7 @@ typedef struct _mp_raw_code_t {
88104
// only needed when the kind is MP_CODE_NATIVE_ASM. So this struct can be used when the
89105
// kind is MP_CODE_BYTECODE, MP_CODE_NATIVE_PY or MP_CODE_NATIVE_VIPER, to reduce its size.
90106
typedef struct _mp_raw_code_truncated_t {
107+
uint8_t proto_fun_indicator[2];
91108
uint8_t kind;
92109
bool is_generator;
93110
const void *fun_data;
@@ -127,7 +144,7 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void
127144
#endif
128145
uint16_t scope_flags, uint32_t asm_n_pos_args, uint32_t asm_type_sig);
129146

130-
mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, const mp_obj_t *def_args);
131-
mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args);
147+
mp_obj_t mp_make_function_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, const mp_obj_t *def_args);
148+
mp_obj_t mp_make_closure_from_proto_fun(mp_proto_fun_t proto_fun, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args);
132149

133150
#endif // MICROPY_INCLUDED_PY_EMITGLUE_H

py/emitnative.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -2657,7 +2657,7 @@ STATIC void emit_native_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_
26572657
need_reg_all(emit);
26582658
}
26592659
emit_load_reg_with_child(emit, REG_ARG_1, scope->raw_code);
2660-
ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_RAW_CODE);
2660+
ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_PROTO_FUN);
26612661
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
26622662
}
26632663

@@ -2675,7 +2675,7 @@ STATIC void emit_native_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_c
26752675
need_reg_all(emit);
26762676
}
26772677
emit_load_reg_with_child(emit, REG_ARG_1, scope->raw_code);
2678-
ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_RAW_CODE);
2678+
ASM_CALL_IND(emit->as, MP_F_MAKE_FUNCTION_FROM_PROTO_FUN);
26792679

26802680
// make closure
26812681
#if REG_ARG_1 != REG_RET

py/emitnx86.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
3737
[MP_F_STORE_SET] = 2,
3838
[MP_F_LIST_APPEND] = 2,
3939
[MP_F_STORE_MAP] = 3,
40-
[MP_F_MAKE_FUNCTION_FROM_RAW_CODE] = 3,
40+
[MP_F_MAKE_FUNCTION_FROM_PROTO_FUN] = 3,
4141
[MP_F_NATIVE_CALL_FUNCTION_N_KW] = 3,
4242
[MP_F_CALL_METHOD_N_KW] = 3,
4343
[MP_F_CALL_METHOD_N_KW_VAR] = 3,

py/nativeglue.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ const mp_fun_table_t mp_fun_table = {
281281
mp_obj_set_store,
282282
mp_obj_list_append,
283283
mp_obj_dict_store,
284-
mp_make_function_from_raw_code,
284+
mp_make_function_from_proto_fun,
285285
mp_native_call_function_n_kw,
286286
mp_call_method_n_kw,
287287
mp_call_method_n_kw_var,

py/nativeglue.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ typedef enum {
5858
MP_F_STORE_SET,
5959
MP_F_LIST_APPEND,
6060
MP_F_STORE_MAP,
61-
MP_F_MAKE_FUNCTION_FROM_RAW_CODE,
61+
MP_F_MAKE_FUNCTION_FROM_PROTO_FUN,
6262
MP_F_NATIVE_CALL_FUNCTION_N_KW,
6363
MP_F_CALL_METHOD_N_KW,
6464
MP_F_CALL_METHOD_N_KW_VAR,
@@ -112,7 +112,7 @@ typedef struct _mp_fun_table_t {
112112
void (*set_store)(mp_obj_t self_in, mp_obj_t item);
113113
mp_obj_t (*list_append)(mp_obj_t self_in, mp_obj_t arg);
114114
mp_obj_t (*dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value);
115-
mp_obj_t (*make_function_from_raw_code)(const mp_raw_code_t *rc, const mp_module_context_t *cm, const mp_obj_t *def_args);
115+
mp_obj_t (*make_function_from_proto_fun)(mp_proto_fun_t proto_fun, const mp_module_context_t *cm, const mp_obj_t *def_args);
116116
mp_obj_t (*call_function_n_kw)(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args);
117117
mp_obj_t (*call_method_n_kw)(size_t n_args, size_t n_kw, const mp_obj_t *args);
118118
mp_obj_t (*call_method_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args);

py/vm.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -893,15 +893,15 @@ unwind_jump:;
893893

894894
ENTRY(MP_BC_MAKE_FUNCTION): {
895895
DECODE_PTR;
896-
PUSH(mp_make_function_from_raw_code(ptr, code_state->fun_bc->context, NULL));
896+
PUSH(mp_make_function_from_proto_fun(ptr, code_state->fun_bc->context, NULL));
897897
DISPATCH();
898898
}
899899

900900
ENTRY(MP_BC_MAKE_FUNCTION_DEFARGS): {
901901
DECODE_PTR;
902902
// Stack layout: def_tuple def_dict <- TOS
903903
sp -= 1;
904-
SET_TOP(mp_make_function_from_raw_code(ptr, code_state->fun_bc->context, sp));
904+
SET_TOP(mp_make_function_from_proto_fun(ptr, code_state->fun_bc->context, sp));
905905
DISPATCH();
906906
}
907907

@@ -910,7 +910,7 @@ unwind_jump:;
910910
size_t n_closed_over = *ip++;
911911
// Stack layout: closed_overs <- TOS
912912
sp -= n_closed_over - 1;
913-
SET_TOP(mp_make_closure_from_raw_code(ptr, code_state->fun_bc->context, n_closed_over, sp));
913+
SET_TOP(mp_make_closure_from_proto_fun(ptr, code_state->fun_bc->context, n_closed_over, sp));
914914
DISPATCH();
915915
}
916916

@@ -919,7 +919,7 @@ unwind_jump:;
919919
size_t n_closed_over = *ip++;
920920
// Stack layout: def_tuple def_dict closed_overs <- TOS
921921
sp -= 2 + n_closed_over - 1;
922-
SET_TOP(mp_make_closure_from_raw_code(ptr, code_state->fun_bc->context, 0x100 | n_closed_over, sp));
922+
SET_TOP(mp_make_closure_from_proto_fun(ptr, code_state->fun_bc->context, 0x100 | n_closed_over, sp));
923923
DISPATCH();
924924
}
925925

shared/runtime/pyexec.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
8888
mp_module_context_t *ctx = m_new_obj(mp_module_context_t);
8989
ctx->module.globals = mp_globals_get();
9090
ctx->constants = frozen->constants;
91-
module_fun = mp_make_function_from_raw_code(frozen->rc, ctx, NULL);
91+
module_fun = mp_make_function_from_proto_fun(frozen->proto_fun, ctx, NULL);
9292
} else
9393
#endif
9494
{

tools/mpy-tool.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ def freeze(self, compiled_module_index):
684684
else:
685685
print(" .obj_table = NULL,")
686686
print(" },")
687-
print(" .rc = (const mp_raw_code_t *)&raw_code_%s," % self.raw_code.escaped_name)
687+
print(" .proto_fun = &proto_fun_%s," % self.raw_code.escaped_name)
688688
print("};")
689689

690690
def freeze_constant_obj(self, obj_name, obj):
@@ -899,7 +899,7 @@ def freeze_children(self, prelude_ptr=None):
899899
print()
900900
print("static const mp_raw_code_t *const children_%s[] = {" % self.escaped_name)
901901
for rc in self.children:
902-
print(" (const mp_raw_code_t *)&raw_code_%s," % rc.escaped_name)
902+
print(" (const mp_raw_code_t *)&proto_fun_%s," % rc.escaped_name)
903903
if prelude_ptr:
904904
print(" (void *)%s," % prelude_ptr)
905905
print("};")
@@ -911,7 +911,9 @@ def freeze_raw_code(self, prelude_ptr=None, type_sig=0):
911911
raw_code_type = "mp_raw_code_t"
912912
else:
913913
raw_code_type = "mp_raw_code_truncated_t"
914-
print("static const %s raw_code_%s = {" % (raw_code_type, self.escaped_name))
914+
print("static const %s proto_fun_%s = {" % (raw_code_type, self.escaped_name))
915+
print(" .proto_fun_indicator[0] = MP_PROTO_FUN_INDICATOR_RAW_CODE_0,")
916+
print(" .proto_fun_indicator[1] = MP_PROTO_FUN_INDICATOR_RAW_CODE_1,")
915917
print(" .kind = %s," % RawCode.code_kind_str[self.code_kind])
916918
print(" .is_generator = %d," % bool(self.scope_flags & MP_SCOPE_FLAG_GENERATOR))
917919
print(" .fun_data = fun_data_%s," % self.escaped_name)

0 commit comments

Comments
 (0)