Skip to content

[draft] Register machine - Add tmps in frame and compiler. Add RETURN_VALUE_R. #101208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Include/cpython/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ typedef struct {
* - co_posonlyargcount \
* - co_kwonlyargcount \
* - co_nlocals \
* - co_ntmps
* - co_stacksize \
* - co_flags \
* - co_firstlineno \
Expand All @@ -79,6 +80,7 @@ typedef struct {
int co_argcount; /* #arguments, except *args */ \
int co_posonlyargcount; /* #positional only arguments */ \
int co_kwonlyargcount; /* #keyword only arguments */ \
int co_ntmps; /* number of tmps needed for evaluation */ \
int co_stacksize; /* #entries needed for evaluation stack */ \
int co_firstlineno; /* first source line number */ \
\
Expand Down Expand Up @@ -166,13 +168,13 @@ static inline int PyCode_GetFirstFree(PyCodeObject *op) {

/* Public interface */
PyAPI_FUNC(PyCodeObject *) PyCode_New(
int, int, int, int, int, PyObject *, PyObject *,
int, int, int, int, int, int, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, int, PyObject *,
PyObject *);

PyAPI_FUNC(PyCodeObject *) PyCode_NewWithPosOnlyArgs(
int, int, int, int, int, int, PyObject *, PyObject *,
int, int, int, int, int, int, int, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, int, PyObject *,
PyObject *);
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ struct _PyCodeConstructor {
int kwonlyargcount;

/* needed to create the frame */
int ntmps;
int stacksize;

/* used by the eval loop */
Expand Down Expand Up @@ -466,6 +467,7 @@ _PyCode_LineNumberFromArray(PyCodeObject *co, int index)
typedef struct _PyShimCodeDef {
const uint8_t *code;
int codelen;
int ntmps;
int stacksize;
const char *cname;
} _PyShimCodeDef;
Expand Down
24 changes: 15 additions & 9 deletions Include/internal/pycore_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,23 @@ typedef struct _PyInterpreterFrame {
#define _PyInterpreterFrame_LASTI(IF) \
((int)((IF)->prev_instr - _PyCode_CODE((IF)->f_code)))

static inline int _PyFrame_StackbaseIndex(_PyInterpreterFrame *f) {
PyCodeObject *code = f->f_code;
return code->co_nlocalsplus + code->co_ntmps;
}

static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) {
return f->localsplus + f->f_code->co_nlocalsplus;
return f->localsplus + _PyFrame_StackbaseIndex(f);
}

static inline PyObject *_PyFrame_StackPeek(_PyInterpreterFrame *f) {
assert(f->stacktop > f->f_code->co_nlocalsplus);
assert(f->stacktop > _PyFrame_StackbaseIndex(f));
assert(f->localsplus[f->stacktop-1] != NULL);
return f->localsplus[f->stacktop-1];
}

static inline PyObject *_PyFrame_StackPop(_PyInterpreterFrame *f) {
assert(f->stacktop > f->f_code->co_nlocalsplus);
assert(f->stacktop > _PyFrame_StackbaseIndex(f));
f->stacktop--;
return f->localsplus[f->stacktop];
}
Expand All @@ -97,10 +102,10 @@ static inline void _PyFrame_StackPush(_PyInterpreterFrame *f, PyObject *value) {
static inline int
_PyFrame_NumSlotsForCodeObject(PyCodeObject *code)
{
/* This function needs to remain in sync with the calculation of
* co_framesize in Tools/build/deepfreeze.py */
assert(code->co_framesize >= FRAME_SPECIALS_SIZE);
return code->co_framesize - FRAME_SPECIALS_SIZE;
int nslots = code->co_framesize - FRAME_SPECIALS_SIZE;
assert(nslots == code->co_nlocalsplus + code->co_ntmps + code->co_stacksize);
return nslots;
}

void _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest);
Expand All @@ -119,13 +124,14 @@ _PyFrame_Initialize(
frame->f_builtins = func->func_builtins;
frame->f_globals = func->func_globals;
frame->f_locals = locals;
frame->stacktop = code->co_nlocalsplus;
frame->frame_obj = NULL;
frame->prev_instr = _PyCode_CODE(code) - 1;
frame->yield_offset = 0;
frame->owner = FRAME_OWNED_BY_THREAD;

for (int i = null_locals_from; i < code->co_nlocalsplus; i++) {
int stack_start = _PyFrame_StackbaseIndex(frame);
frame->stacktop = stack_start;
for (int i = null_locals_from; i < stack_start; i++) {
frame->localsplus[i] = NULL;
}
}
Expand All @@ -142,7 +148,7 @@ _PyFrame_GetLocalsArray(_PyInterpreterFrame *frame)
static inline PyObject**
_PyFrame_GetStackPointer(_PyInterpreterFrame *frame)
{
return frame->localsplus+frame->stacktop;
return frame->localsplus + frame->stacktop;
}

static inline void
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(co_name)
STRUCT_FOR_ID(co_names)
STRUCT_FOR_ID(co_nlocals)
STRUCT_FOR_ID(co_ntmps)
STRUCT_FOR_ID(co_posonlyargcount)
STRUCT_FOR_ID(co_qualname)
STRUCT_FOR_ID(co_stacksize)
Expand Down
8 changes: 4 additions & 4 deletions Include/internal/pycore_opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.12a1 3514 (Remove ASYNC_GEN_WRAP, LIST_TO_TUPLE, and UNARY_POSITIVE)
# Python 3.12a1 3515 (Embed jump mask in COMPARE_OP oparg)
# Python 3.12a1 3516 (Add COMAPRE_AND_BRANCH instruction)
# Python 3.12a1 3517 (Add ntmps to code object)
# Python 3.12a1 3518 (Add RETURN_VALUE_R)

# Python 3.13 will start with 3550

Expand All @@ -442,7 +444,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3516).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3518).to_bytes(2, 'little') + b'\r\n'

_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

Expand Down
2 changes: 2 additions & 0 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ def pseudo_op(name, op, real_ops):

def_op('CALL_FUNCTION_EX', 142) # Flags

def_op('RETURN_VALUE_R', 143)

def_op('EXTENDED_ARG', 144)
EXTENDED_ARG = 144
def_op('LIST_APPEND', 145)
Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,16 @@ def c_py_recurse(m):
finally:
sys.setrecursionlimit(depth)

class TestFunctionWithManyArgs(unittest.TestCase):
def test_function_with_many_args(self):
for N in (10, 500, 1000):
with self.subTest(N=N):
args = ",".join([f"a{i}" for i in range(N)])
src = f"def f({args}) : return a{N//2}"
l = {}
exec(src, {}, l)
self.assertEqual(l['f'](*range(N)), N//2)


if __name__ == "__main__":
unittest.main()
3 changes: 3 additions & 0 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ def func(): pass
co.co_posonlyargcount,
co.co_kwonlyargcount,
co.co_nlocals,
co.co_ntmps,
co.co_stacksize,
co.co_flags,
co.co_code,
Expand Down Expand Up @@ -264,6 +265,7 @@ def func2():
("co_posonlyargcount", 0),
("co_kwonlyargcount", 0),
("co_nlocals", 1),
("co_ntmps", 13),
("co_stacksize", 0),
("co_flags", code.co_flags | inspect.CO_COROUTINE),
("co_firstlineno", 100),
Expand Down Expand Up @@ -302,6 +304,7 @@ def func():
co.co_kwonlyargcount,
# This is the only change.
co.co_nlocals + diff,
co.co_ntmps,
co.co_stacksize,
co.co_flags,
co.co_code,
Expand Down
Loading