Skip to content

Commit 0b62643

Browse files
committed
py/runtime: Add compact form of star arg calls.
This adds new bit flags to unum for the MP_BC_CALL_FUNCTION_VAR_KW and MP_BC_CALL_METHOD_VAR_KW op codes. The LSB of unum now indicates if the rest of the bits are in compact form or expanded form. The layout of the compact form is intended to be 7 bits so that it only emits one byte and handles the most common cases where there are a small number of arguments passed (up to 3 positional args - including *args and up to 3 kwargs - including **args). Additionally, the star_flags are now included in the unum. This allows the extra positional args bitmap to only be emitted if there are actually *args in the argument list. This will reduce the number of bytes emitted for calls like: example(**kwargs) by 2 bytes. One byte saved by using the compact form so that encoding n_kw doesn't require an extra byte and one saved by not pushing the positional arg bitmap on the stack. Signed-off-by: David Lechner <david@pybricks.com>
1 parent dbd5354 commit 0b62643

File tree

10 files changed

+104
-33
lines changed

10 files changed

+104
-33
lines changed

py/compile.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2465,7 +2465,7 @@ STATIC void compile_trailer_paren_helper(compiler_t *comp, mp_parse_node_t pn_ar
24652465
}
24662466
}
24672467

2468-
if (star_flags != 0) {
2468+
if (star_flags & MP_EMIT_STAR_FLAG_SINGLE) {
24692469
// one extra object that contains the star_args map
24702470
EMIT_ARG(load_const_small_int, star_args);
24712471
}

py/emitbc.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -711,9 +711,14 @@ void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_ov
711711
STATIC void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
712712
if (star_flags) {
713713
// each positional arg is one object, each kwarg is two objects, the key
714-
// and the value and one extra object for the star args bitmap.
715-
stack_adj -= (int)n_positional + 2 * (int)n_keyword + 1;
716-
emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints?
714+
// and the value and maybe one extra object for the star args bitmap.
715+
stack_adj -= (int)n_positional + 2 * (int)n_keyword + (int)(star_flags & MP_EMIT_STAR_FLAG_SINGLE);
716+
emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1,
717+
(n_keyword < 4 && n_positional < 4)
718+
// compact form (LSB is 1) - fits in one byte (7-bit) unum
719+
? ((n_keyword << 5) | (n_positional << 3) | (star_flags << 1) | 1)
720+
// expanded form (LSB is 0)
721+
: ((n_keyword << 11) | (n_positional << 3) | (star_flags << 1)));
717722
} else {
718723
stack_adj -= (int)n_positional + 2 * (int)n_keyword;
719724
emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints?

py/emitnative.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2744,8 +2744,11 @@ STATIC void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u
27442744
} else {
27452745
assert(vtype_fun == VTYPE_PYOBJ);
27462746
if (star_flags) {
2747-
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 2); // pointer to args
2748-
emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 0, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2);
2747+
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + (star_flags & MP_EMIT_STAR_FLAG_SINGLE) + 1); // pointer to args
2748+
emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 0, REG_ARG_1,
2749+
// expanded form (LSB is 0)
2750+
(n_keyword << 11) | (n_positional << 3) | (star_flags << 1),
2751+
REG_ARG_2);
27492752
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
27502753
} else {
27512754
if (n_positional != 0 || n_keyword != 0) {
@@ -2760,8 +2763,11 @@ STATIC void emit_native_call_function(emit_t *emit, mp_uint_t n_positional, mp_u
27602763

27612764
STATIC void emit_native_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
27622765
if (star_flags) {
2763-
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + 3); // pointer to args
2764-
emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 1, REG_ARG_1, n_positional | (n_keyword << 8), REG_ARG_2);
2766+
emit_get_stack_pointer_to_reg_for_pop(emit, REG_ARG_3, n_positional + 2 * n_keyword + (star_flags & MP_EMIT_STAR_FLAG_SINGLE) + 2); // pointer to args
2767+
emit_call_with_2_imm_args(emit, MP_F_CALL_METHOD_N_KW_VAR, 1, REG_ARG_1,
2768+
// expanded form (LSB is 0)
2769+
(n_keyword << 11) | (n_positional << 3) | (star_flags << 1),
2770+
REG_ARG_2);
27652771
emit_post_push_reg(emit, VTYPE_PYOBJ, REG_RET);
27662772
} else {
27672773
emit_native_pre(emit);

py/nativeglue.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ typedef struct _mp_fun_table_t {
115115
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);
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);
118-
mp_obj_t (*call_method_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args);
118+
mp_obj_t (*call_method_n_kw_var)(bool have_self, size_t n_args_n_kw_flags, const mp_obj_t *args);
119119
mp_obj_t (*getiter)(mp_obj_t obj, mp_obj_iter_buf_t *iter);
120120
mp_obj_t (*iternext)(mp_obj_iter_buf_t *iter);
121121
unsigned int (*nlr_push)(nlr_buf_t *);

py/profile.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -840,8 +840,15 @@ STATIC const byte *mp_prof_opcode_decode(const byte *ip, const mp_uint_t *const_
840840
case MP_BC_CALL_FUNCTION_VAR_KW:
841841
DECODE_UINT;
842842
instruction->qstr_opname = MP_QSTR_CALL_FUNCTION_VAR_KW;
843-
instruction->arg = unum & 0xff;
844-
instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff);
843+
if (unum & 0x1) {
844+
// compact form
845+
instruction->arg = (unum >> 3) & 0x3;
846+
instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 5) & 0x3);
847+
} else {
848+
// expanded form
849+
instruction->arg = (unum >> 3) & 0xff;
850+
instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 11) & 0xff);
851+
}
845852
break;
846853

847854
case MP_BC_CALL_METHOD:
@@ -854,8 +861,15 @@ STATIC const byte *mp_prof_opcode_decode(const byte *ip, const mp_uint_t *const_
854861
case MP_BC_CALL_METHOD_VAR_KW:
855862
DECODE_UINT;
856863
instruction->qstr_opname = MP_QSTR_CALL_METHOD_VAR_KW;
857-
instruction->arg = unum & 0xff;
858-
instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 8) & 0xff);
864+
if (unum & 0x1) {
865+
// compact form
866+
instruction->arg = (unum >> 3) & 0x3;
867+
instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 5) & 0x3);
868+
} else {
869+
// expanded form
870+
instruction->arg = (unum >> 3) & 0xff;
871+
instruction->argobjex_cache = MP_OBJ_NEW_SMALL_INT((unum >> 11) & 0xff);
872+
}
859873
break;
860874

861875
case MP_BC_RETURN_VALUE:

py/runtime.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -693,15 +693,25 @@ mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args) {
693693
#if !MICROPY_STACKLESS
694694
STATIC
695695
#endif
696-
void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args, mp_call_args_t *out_args) {
696+
void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw_flags, const mp_obj_t *args, mp_call_args_t *out_args) {
697697
mp_obj_t fun = *args++;
698698
mp_obj_t self = MP_OBJ_NULL;
699699
if (have_self) {
700700
self = *args++; // may be MP_OBJ_NULL
701701
}
702-
uint n_args = n_args_n_kw & 0xff;
703-
uint n_kw = (n_args_n_kw >> 8) & 0xff;
704-
mp_uint_t star_args = mp_obj_get_int_truncated(args[n_args + 2 * n_kw]);
702+
// layout of n_args_n_kw_flags is:
703+
// bit 0: compact form flag
704+
// bit 1: *args flag
705+
// bit 2: **args flag
706+
// compact form
707+
// bit 3-4: n_args
708+
// bit 5-6: n_kw
709+
// expanded form
710+
// bit 3-10: n_args
711+
// bit 11-18: n_kw
712+
uint n_args = ((n_args_n_kw_flags >> 3) & ((n_args_n_kw_flags & 0x1) ? 0x3 : 0xff));
713+
uint n_kw = (n_args_n_kw_flags & 0x1) ? ((n_args_n_kw_flags >> 5) & 0x3) : ((n_args_n_kw_flags >> 8) & 0xff);
714+
mp_uint_t star_args = (n_args_n_kw_flags & 0x2) ? mp_obj_get_int_truncated(args[n_args + 2 * n_kw]) : 0;
705715

706716
DEBUG_OP_printf("call method var (fun=%p, self=%p, n_args=%u, n_kw=%u, args=%p, map=%u)\n", fun, self, n_args, n_kw, args, star_args);
707717

@@ -895,9 +905,9 @@ void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_
895905
out_args->n_alloc = args2_alloc;
896906
}
897907

898-
mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args) {
908+
mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw_flags, const mp_obj_t *args) {
899909
mp_call_args_t out_args;
900-
mp_call_prepare_args_n_kw_var(have_self, n_args_n_kw, args, &out_args);
910+
mp_call_prepare_args_n_kw_var(have_self, n_args_n_kw_flags, args, &out_args);
901911

902912
mp_obj_t res = mp_call_function_n_kw(out_args.fun, out_args.n_args, out_args.n_kw, out_args.args);
903913
mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t));

py/runtime.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ mp_obj_t mp_call_function_1(mp_obj_t fun, mp_obj_t arg);
117117
mp_obj_t mp_call_function_2(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2);
118118
mp_obj_t mp_call_function_n_kw(mp_obj_t fun, size_t n_args, size_t n_kw, const mp_obj_t *args);
119119
mp_obj_t mp_call_method_n_kw(size_t n_args, size_t n_kw, const mp_obj_t *args);
120-
mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args);
120+
mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw_flags, const mp_obj_t *args);
121121
mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args);
122122
// Call function and catch/dump exception - for Python callbacks from C code
123123
// (return MP_OBJ_NULL in case of exception).
@@ -135,7 +135,7 @@ typedef struct _mp_call_args_t {
135135
// prepares argument array suitable for passing to ->call() method of a
136136
// function object (and mp_call_function_n_kw()).
137137
// (Only needed in stackless mode.)
138-
void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_obj_t *args, mp_call_args_t *out_args);
138+
void mp_call_prepare_args_n_kw_var(bool have_self, size_t n_args_n_kw_flags, const mp_obj_t *args, mp_call_args_t *out_args);
139139
#endif
140140

141141
void mp_unpack_sequence(mp_obj_t seq, size_t num, mp_obj_t *items);

py/showbc.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,13 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start,
455455

456456
case MP_BC_CALL_FUNCTION_VAR_KW:
457457
DECODE_UINT;
458-
mp_printf(print, "CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff);
458+
if (unum & 0x1) {
459+
// compact form
460+
mp_printf(print, "CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT " f=0x%x", (unum >> 3) & 0x3, (unum >> 5) & 0x3, unum & 0x7);
461+
} else {
462+
// expanded form
463+
mp_printf(print, "CALL_FUNCTION_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT " f=0x%x", (unum >> 3) & 0xff, (unum >> 11) & 0xff, unum & 0x7);
464+
}
459465
break;
460466

461467
case MP_BC_CALL_METHOD:
@@ -465,7 +471,13 @@ const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start,
465471

466472
case MP_BC_CALL_METHOD_VAR_KW:
467473
DECODE_UINT;
468-
mp_printf(print, "CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT, unum & 0xff, (unum >> 8) & 0xff);
474+
if (unum & 0x1) {
475+
// compact form
476+
mp_printf(print, "CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT " f=0x%x", (unum >> 3) & 0x3, (unum >> 5) & 0x3, unum & 0x7);
477+
} else {
478+
// expanded form
479+
mp_printf(print, "CALL_METHOD_VAR_KW n=" UINT_FMT " nkw=" UINT_FMT " f=0x%x", (unum >> 3) & 0xff, (unum >> 11) & 0xff, unum & 0x7);
480+
}
469481
break;
470482

471483
case MP_BC_RETURN_VALUE:

py/vm.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -924,11 +924,23 @@ unwind_jump:;
924924
FRAME_UPDATE();
925925
MARK_EXC_IP_SELECTIVE();
926926
DECODE_UINT;
927-
// unum & 0xff == n_positional
928-
// (unum >> 8) & 0xff == n_keyword
927+
// layout of unum is:
928+
// bit 0: compact form flag
929+
// bit 1: *args flag
930+
// bit 2: **args flag
931+
// compact form
932+
// bit 3-4: n_args
933+
// bit 5-6: n_kw
934+
// expanded form
935+
// bit 3-10: n_args
936+
// bit 11-18: n_kw
929937
// We have following stack layout here:
930-
// fun arg0 arg1 ... kw0 val0 kw1 val1 ... bitmap <- TOS
931-
sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 1;
938+
// fun arg0 arg1 ... kw0 val0 kw1 val1 ... [bitmap] <- TOS
939+
sp -= (
940+
(unum & 1)
941+
? (((unum >> 3) & 0x3) + ((unum >> 4) & 0x6))
942+
: (((unum >> 3) & 0xff) + ((unum >> 10) & 0x1fe))
943+
) + ((unum >> 1) & 0x1);
932944
#if MICROPY_STACKLESS
933945
if (mp_obj_get_type(*sp) == &mp_type_fun_bc) {
934946
code_state->ip = ip;
@@ -1009,11 +1021,23 @@ unwind_jump:;
10091021
FRAME_UPDATE();
10101022
MARK_EXC_IP_SELECTIVE();
10111023
DECODE_UINT;
1012-
// unum & 0xff == n_positional
1013-
// (unum >> 8) & 0xff == n_keyword
1024+
// layout of unum is:
1025+
// bit 0: compact form flag
1026+
// bit 1: *args flag
1027+
// bit 2: **args flag
1028+
// compact form
1029+
// bit 3-4: n_args
1030+
// bit 5-6: n_kw
1031+
// expanded form
1032+
// bit 3-10: n_args
1033+
// bit 11-18: n_kw
10141034
// We have following stack layout here:
1015-
// fun self arg0 arg1 ... kw0 val0 kw1 val1 ... bitmap <- TOS
1016-
sp -= (unum & 0xff) + ((unum >> 7) & 0x1fe) + 2;
1035+
// fun self arg0 arg1 ... kw0 val0 kw1 val1 ... [bitmap] <- TOS
1036+
sp -= 1 + (
1037+
(unum & 1)
1038+
? (((unum >> 3) & 0x3) + ((unum >> 4) & 0x6))
1039+
: (((unum >> 3) & 0xff) + ((unum >> 10) & 0x1fe))
1040+
) + ((unum >> 1) & 0x1);
10171041
#if MICROPY_STACKLESS
10181042
if (mp_obj_get_type(*sp) == &mp_type_fun_bc) {
10191043
code_state->ip = ip;

tests/cmdline/cmd_showbc.py.exp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ arg names:
298298
212 LOAD_FAST 0
299299
213 LOAD_DEREF 14
300300
215 LOAD_CONST_SMALL_INT 1
301-
216 CALL_FUNCTION_VAR_KW n=1 nkw=0
301+
216 CALL_FUNCTION_VAR_KW n=1 nkw=0 f=0x3
302302
218 POP_TOP
303303
219 LOAD_FAST 0
304304
220 LOAD_METHOD b
@@ -319,7 +319,7 @@ arg names:
319319
243 LOAD_METHOD b
320320
245 LOAD_FAST 1
321321
246 LOAD_CONST_SMALL_INT 1
322-
247 CALL_METHOD_VAR_KW n=1 nkw=0
322+
247 CALL_METHOD_VAR_KW n=1 nkw=0 f=0x3
323323
249 POP_TOP
324324
250 LOAD_FAST 0
325325
251 POP_JUMP_IF_FALSE 260

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy