From 4d5e9a5542622bdd27bcd2902c39e4aa53dcbe6b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 24 Aug 2022 11:26:59 +0100 Subject: [PATCH 01/29] Handle _PyEval_EvalFrameDefault exit in bytecode. --- Include/internal/pycore_opcode.h | 40 ++++----- Include/opcode.h | 147 ++++++++++++++++--------------- Lib/opcode.py | 1 + Python/ceval.c | 35 +++++--- Python/opcode_targets.h | 38 ++++---- 5 files changed, 138 insertions(+), 123 deletions(-) diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 587590172b5615..392f560950108b 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -138,6 +138,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [IMPORT_FROM] = IMPORT_FROM, [IMPORT_NAME] = IMPORT_NAME, [IMPORT_STAR] = IMPORT_STAR, + [INTEPRETER_EXIT] = INTEPRETER_EXIT, [IS_OP] = IS_OP, [JUMP_BACKWARD] = JUMP_BACKWARD, [JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT, @@ -247,19 +248,20 @@ static const char *const _PyOpcode_OpName[267] = { [CACHE] = "CACHE", [POP_TOP] = "POP_TOP", [PUSH_NULL] = "PUSH_NULL", + [INTEPRETER_EXIT] = "INTEPRETER_EXIT", [BINARY_OP_ADAPTIVE] = "BINARY_OP_ADAPTIVE", [BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT", [BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT", [BINARY_OP_ADD_UNICODE] = "BINARY_OP_ADD_UNICODE", [BINARY_OP_INPLACE_ADD_UNICODE] = "BINARY_OP_INPLACE_ADD_UNICODE", - [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT", [NOP] = "NOP", [UNARY_POSITIVE] = "UNARY_POSITIVE", [UNARY_NEGATIVE] = "UNARY_NEGATIVE", [UNARY_NOT] = "UNARY_NOT", + [BINARY_OP_MULTIPLY_FLOAT] = "BINARY_OP_MULTIPLY_FLOAT", [BINARY_OP_MULTIPLY_INT] = "BINARY_OP_MULTIPLY_INT", - [BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT", [UNARY_INVERT] = "UNARY_INVERT", + [BINARY_OP_SUBTRACT_FLOAT] = "BINARY_OP_SUBTRACT_FLOAT", [BINARY_OP_SUBTRACT_INT] = "BINARY_OP_SUBTRACT_INT", [BINARY_SUBSCR_ADAPTIVE] = "BINARY_SUBSCR_ADAPTIVE", [BINARY_SUBSCR_DICT] = "BINARY_SUBSCR_DICT", @@ -268,20 +270,20 @@ static const char *const _PyOpcode_OpName[267] = { [BINARY_SUBSCR_TUPLE_INT] = "BINARY_SUBSCR_TUPLE_INT", [CALL_ADAPTIVE] = "CALL_ADAPTIVE", [CALL_PY_EXACT_ARGS] = "CALL_PY_EXACT_ARGS", - [CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS", [BINARY_SUBSCR] = "BINARY_SUBSCR", [BINARY_SLICE] = "BINARY_SLICE", [STORE_SLICE] = "STORE_SLICE", + [CALL_PY_WITH_DEFAULTS] = "CALL_PY_WITH_DEFAULTS", [CALL_BOUND_METHOD_EXACT_ARGS] = "CALL_BOUND_METHOD_EXACT_ARGS", - [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS", [GET_LEN] = "GET_LEN", [MATCH_MAPPING] = "MATCH_MAPPING", [MATCH_SEQUENCE] = "MATCH_SEQUENCE", [MATCH_KEYS] = "MATCH_KEYS", - [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS", + [CALL_BUILTIN_CLASS] = "CALL_BUILTIN_CLASS", [PUSH_EXC_INFO] = "PUSH_EXC_INFO", [CHECK_EXC_MATCH] = "CHECK_EXC_MATCH", [CHECK_EG_MATCH] = "CHECK_EG_MATCH", + [CALL_BUILTIN_FAST_WITH_KEYWORDS] = "CALL_BUILTIN_FAST_WITH_KEYWORDS", [CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS] = "CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS", [CALL_NO_KW_BUILTIN_FAST] = "CALL_NO_KW_BUILTIN_FAST", [CALL_NO_KW_BUILTIN_O] = "CALL_NO_KW_BUILTIN_O", @@ -292,7 +294,6 @@ static const char *const _PyOpcode_OpName[267] = { [CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS] = "CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS", [CALL_NO_KW_METHOD_DESCRIPTOR_O] = "CALL_NO_KW_METHOD_DESCRIPTOR_O", [CALL_NO_KW_STR_1] = "CALL_NO_KW_STR_1", - [CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1", [WITH_EXCEPT_START] = "WITH_EXCEPT_START", [GET_AITER] = "GET_AITER", [GET_ANEXT] = "GET_ANEXT", @@ -300,37 +301,37 @@ static const char *const _PyOpcode_OpName[267] = { [BEFORE_WITH] = "BEFORE_WITH", [END_ASYNC_FOR] = "END_ASYNC_FOR", [CLEANUP_THROW] = "CLEANUP_THROW", + [CALL_NO_KW_TUPLE_1] = "CALL_NO_KW_TUPLE_1", [CALL_NO_KW_TYPE_1] = "CALL_NO_KW_TYPE_1", [COMPARE_OP_ADAPTIVE] = "COMPARE_OP_ADAPTIVE", [COMPARE_OP_FLOAT_JUMP] = "COMPARE_OP_FLOAT_JUMP", - [COMPARE_OP_INT_JUMP] = "COMPARE_OP_INT_JUMP", [STORE_SUBSCR] = "STORE_SUBSCR", [DELETE_SUBSCR] = "DELETE_SUBSCR", + [COMPARE_OP_INT_JUMP] = "COMPARE_OP_INT_JUMP", [COMPARE_OP_STR_JUMP] = "COMPARE_OP_STR_JUMP", [EXTENDED_ARG_QUICK] = "EXTENDED_ARG_QUICK", [FOR_ITER_ADAPTIVE] = "FOR_ITER_ADAPTIVE", [FOR_ITER_LIST] = "FOR_ITER_LIST", [FOR_ITER_RANGE] = "FOR_ITER_RANGE", - [JUMP_BACKWARD_QUICK] = "JUMP_BACKWARD_QUICK", [GET_ITER] = "GET_ITER", [GET_YIELD_FROM_ITER] = "GET_YIELD_FROM_ITER", [PRINT_EXPR] = "PRINT_EXPR", [LOAD_BUILD_CLASS] = "LOAD_BUILD_CLASS", + [JUMP_BACKWARD_QUICK] = "JUMP_BACKWARD_QUICK", [LOAD_ATTR_ADAPTIVE] = "LOAD_ATTR_ADAPTIVE", - [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", [LOAD_ASSERTION_ERROR] = "LOAD_ASSERTION_ERROR", [RETURN_GENERATOR] = "RETURN_GENERATOR", + [LOAD_ATTR_CLASS] = "LOAD_ATTR_CLASS", [LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN] = "LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN", [LOAD_ATTR_INSTANCE_VALUE] = "LOAD_ATTR_INSTANCE_VALUE", [LOAD_ATTR_MODULE] = "LOAD_ATTR_MODULE", [LOAD_ATTR_PROPERTY] = "LOAD_ATTR_PROPERTY", [LOAD_ATTR_SLOT] = "LOAD_ATTR_SLOT", - [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [LIST_TO_TUPLE] = "LIST_TO_TUPLE", [RETURN_VALUE] = "RETURN_VALUE", [IMPORT_STAR] = "IMPORT_STAR", [SETUP_ANNOTATIONS] = "SETUP_ANNOTATIONS", - [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", + [LOAD_ATTR_WITH_HINT] = "LOAD_ATTR_WITH_HINT", [ASYNC_GEN_WRAP] = "ASYNC_GEN_WRAP", [PREP_RERAISE_STAR] = "PREP_RERAISE_STAR", [POP_EXCEPT] = "POP_EXCEPT", @@ -357,7 +358,7 @@ static const char *const _PyOpcode_OpName[267] = { [JUMP_FORWARD] = "JUMP_FORWARD", [JUMP_IF_FALSE_OR_POP] = "JUMP_IF_FALSE_OR_POP", [JUMP_IF_TRUE_OR_POP] = "JUMP_IF_TRUE_OR_POP", - [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", + [LOAD_ATTR_METHOD_LAZY_DICT] = "LOAD_ATTR_METHOD_LAZY_DICT", [POP_JUMP_FORWARD_IF_FALSE] = "POP_JUMP_FORWARD_IF_FALSE", [POP_JUMP_FORWARD_IF_TRUE] = "POP_JUMP_FORWARD_IF_TRUE", [LOAD_GLOBAL] = "LOAD_GLOBAL", @@ -365,7 +366,7 @@ static const char *const _PyOpcode_OpName[267] = { [CONTAINS_OP] = "CONTAINS_OP", [RERAISE] = "RERAISE", [COPY] = "COPY", - [LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT", + [LOAD_ATTR_METHOD_NO_DICT] = "LOAD_ATTR_METHOD_NO_DICT", [BINARY_OP] = "BINARY_OP", [SEND] = "SEND", [LOAD_FAST] = "LOAD_FAST", @@ -385,9 +386,9 @@ static const char *const _PyOpcode_OpName[267] = { [STORE_DEREF] = "STORE_DEREF", [DELETE_DEREF] = "DELETE_DEREF", [JUMP_BACKWARD] = "JUMP_BACKWARD", - [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", + [LOAD_ATTR_METHOD_WITH_DICT] = "LOAD_ATTR_METHOD_WITH_DICT", [CALL_FUNCTION_EX] = "CALL_FUNCTION_EX", - [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", + [LOAD_ATTR_METHOD_WITH_VALUES] = "LOAD_ATTR_METHOD_WITH_VALUES", [EXTENDED_ARG] = "EXTENDED_ARG", [LIST_APPEND] = "LIST_APPEND", [SET_ADD] = "SET_ADD", @@ -397,30 +398,31 @@ static const char *const _PyOpcode_OpName[267] = { [YIELD_VALUE] = "YIELD_VALUE", [RESUME] = "RESUME", [MATCH_CLASS] = "MATCH_CLASS", + [LOAD_CONST__LOAD_FAST] = "LOAD_CONST__LOAD_FAST", [LOAD_FAST__LOAD_CONST] = "LOAD_FAST__LOAD_CONST", - [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", [FORMAT_VALUE] = "FORMAT_VALUE", [BUILD_CONST_KEY_MAP] = "BUILD_CONST_KEY_MAP", [BUILD_STRING] = "BUILD_STRING", + [LOAD_FAST__LOAD_FAST] = "LOAD_FAST__LOAD_FAST", [LOAD_GLOBAL_ADAPTIVE] = "LOAD_GLOBAL_ADAPTIVE", [LOAD_GLOBAL_BUILTIN] = "LOAD_GLOBAL_BUILTIN", [LOAD_GLOBAL_MODULE] = "LOAD_GLOBAL_MODULE", - [RESUME_QUICK] = "RESUME_QUICK", [LIST_EXTEND] = "LIST_EXTEND", [SET_UPDATE] = "SET_UPDATE", [DICT_MERGE] = "DICT_MERGE", [DICT_UPDATE] = "DICT_UPDATE", + [RESUME_QUICK] = "RESUME_QUICK", [STORE_ATTR_ADAPTIVE] = "STORE_ATTR_ADAPTIVE", [STORE_ATTR_INSTANCE_VALUE] = "STORE_ATTR_INSTANCE_VALUE", [STORE_ATTR_SLOT] = "STORE_ATTR_SLOT", [STORE_ATTR_WITH_HINT] = "STORE_ATTR_WITH_HINT", - [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [CALL] = "CALL", [KW_NAMES] = "KW_NAMES", [POP_JUMP_BACKWARD_IF_NOT_NONE] = "POP_JUMP_BACKWARD_IF_NOT_NONE", [POP_JUMP_BACKWARD_IF_NONE] = "POP_JUMP_BACKWARD_IF_NONE", [POP_JUMP_BACKWARD_IF_FALSE] = "POP_JUMP_BACKWARD_IF_FALSE", [POP_JUMP_BACKWARD_IF_TRUE] = "POP_JUMP_BACKWARD_IF_TRUE", + [STORE_FAST__LOAD_FAST] = "STORE_FAST__LOAD_FAST", [STORE_FAST__STORE_FAST] = "STORE_FAST__STORE_FAST", [STORE_SUBSCR_ADAPTIVE] = "STORE_SUBSCR_ADAPTIVE", [STORE_SUBSCR_DICT] = "STORE_SUBSCR_DICT", @@ -429,7 +431,6 @@ static const char *const _PyOpcode_OpName[267] = { [UNPACK_SEQUENCE_LIST] = "UNPACK_SEQUENCE_LIST", [UNPACK_SEQUENCE_TUPLE] = "UNPACK_SEQUENCE_TUPLE", [UNPACK_SEQUENCE_TWO_TUPLE] = "UNPACK_SEQUENCE_TWO_TUPLE", - [185] = "<185>", [186] = "<186>", [187] = "<187>", [188] = "<188>", @@ -515,7 +516,6 @@ static const char *const _PyOpcode_OpName[267] = { #endif #define EXTRA_CASES \ - case 185: \ case 186: \ case 187: \ case 188: \ diff --git a/Include/opcode.h b/Include/opcode.h index cf11e5560674e1..1ca4b0d16f1914 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -11,6 +11,7 @@ extern "C" { #define CACHE 0 #define POP_TOP 1 #define PUSH_NULL 2 +#define INTEPRETER_EXIT 3 #define NOP 9 #define UNARY_POSITIVE 10 #define UNARY_NEGATIVE 11 @@ -134,79 +135,79 @@ extern "C" { #define POP_JUMP_IF_NOT_NONE 265 #define LOAD_METHOD 266 #define MAX_PSEUDO_OPCODE 266 -#define BINARY_OP_ADAPTIVE 3 -#define BINARY_OP_ADD_FLOAT 4 -#define BINARY_OP_ADD_INT 5 -#define BINARY_OP_ADD_UNICODE 6 -#define BINARY_OP_INPLACE_ADD_UNICODE 7 -#define BINARY_OP_MULTIPLY_FLOAT 8 -#define BINARY_OP_MULTIPLY_INT 13 -#define BINARY_OP_SUBTRACT_FLOAT 14 -#define BINARY_OP_SUBTRACT_INT 16 -#define BINARY_SUBSCR_ADAPTIVE 17 -#define BINARY_SUBSCR_DICT 18 -#define BINARY_SUBSCR_GETITEM 19 -#define BINARY_SUBSCR_LIST_INT 20 -#define BINARY_SUBSCR_TUPLE_INT 21 -#define CALL_ADAPTIVE 22 -#define CALL_PY_EXACT_ARGS 23 -#define CALL_PY_WITH_DEFAULTS 24 -#define CALL_BOUND_METHOD_EXACT_ARGS 28 -#define CALL_BUILTIN_CLASS 29 -#define CALL_BUILTIN_FAST_WITH_KEYWORDS 34 -#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 38 -#define CALL_NO_KW_BUILTIN_FAST 39 -#define CALL_NO_KW_BUILTIN_O 40 -#define CALL_NO_KW_ISINSTANCE 41 -#define CALL_NO_KW_LEN 42 -#define CALL_NO_KW_LIST_APPEND 43 -#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 44 -#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 45 -#define CALL_NO_KW_METHOD_DESCRIPTOR_O 46 -#define CALL_NO_KW_STR_1 47 -#define CALL_NO_KW_TUPLE_1 48 -#define CALL_NO_KW_TYPE_1 56 -#define COMPARE_OP_ADAPTIVE 57 -#define COMPARE_OP_FLOAT_JUMP 58 -#define COMPARE_OP_INT_JUMP 59 -#define COMPARE_OP_STR_JUMP 62 -#define EXTENDED_ARG_QUICK 63 -#define FOR_ITER_ADAPTIVE 64 -#define FOR_ITER_LIST 65 -#define FOR_ITER_RANGE 66 -#define JUMP_BACKWARD_QUICK 67 -#define LOAD_ATTR_ADAPTIVE 72 -#define LOAD_ATTR_CLASS 73 -#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 76 -#define LOAD_ATTR_INSTANCE_VALUE 77 -#define LOAD_ATTR_MODULE 78 -#define LOAD_ATTR_PROPERTY 79 -#define LOAD_ATTR_SLOT 80 -#define LOAD_ATTR_WITH_HINT 81 -#define LOAD_ATTR_METHOD_LAZY_DICT 86 -#define LOAD_ATTR_METHOD_NO_DICT 113 -#define LOAD_ATTR_METHOD_WITH_DICT 121 -#define LOAD_ATTR_METHOD_WITH_VALUES 141 -#define LOAD_CONST__LOAD_FAST 143 -#define LOAD_FAST__LOAD_CONST 153 -#define LOAD_FAST__LOAD_FAST 154 -#define LOAD_GLOBAL_ADAPTIVE 158 -#define LOAD_GLOBAL_BUILTIN 159 -#define LOAD_GLOBAL_MODULE 160 -#define RESUME_QUICK 161 -#define STORE_ATTR_ADAPTIVE 166 -#define STORE_ATTR_INSTANCE_VALUE 167 -#define STORE_ATTR_SLOT 168 -#define STORE_ATTR_WITH_HINT 169 -#define STORE_FAST__LOAD_FAST 170 -#define STORE_FAST__STORE_FAST 177 -#define STORE_SUBSCR_ADAPTIVE 178 -#define STORE_SUBSCR_DICT 179 -#define STORE_SUBSCR_LIST_INT 180 -#define UNPACK_SEQUENCE_ADAPTIVE 181 -#define UNPACK_SEQUENCE_LIST 182 -#define UNPACK_SEQUENCE_TUPLE 183 -#define UNPACK_SEQUENCE_TWO_TUPLE 184 +#define BINARY_OP_ADAPTIVE 4 +#define BINARY_OP_ADD_FLOAT 5 +#define BINARY_OP_ADD_INT 6 +#define BINARY_OP_ADD_UNICODE 7 +#define BINARY_OP_INPLACE_ADD_UNICODE 8 +#define BINARY_OP_MULTIPLY_FLOAT 13 +#define BINARY_OP_MULTIPLY_INT 14 +#define BINARY_OP_SUBTRACT_FLOAT 16 +#define BINARY_OP_SUBTRACT_INT 17 +#define BINARY_SUBSCR_ADAPTIVE 18 +#define BINARY_SUBSCR_DICT 19 +#define BINARY_SUBSCR_GETITEM 20 +#define BINARY_SUBSCR_LIST_INT 21 +#define BINARY_SUBSCR_TUPLE_INT 22 +#define CALL_ADAPTIVE 23 +#define CALL_PY_EXACT_ARGS 24 +#define CALL_PY_WITH_DEFAULTS 28 +#define CALL_BOUND_METHOD_EXACT_ARGS 29 +#define CALL_BUILTIN_CLASS 34 +#define CALL_BUILTIN_FAST_WITH_KEYWORDS 38 +#define CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS 39 +#define CALL_NO_KW_BUILTIN_FAST 40 +#define CALL_NO_KW_BUILTIN_O 41 +#define CALL_NO_KW_ISINSTANCE 42 +#define CALL_NO_KW_LEN 43 +#define CALL_NO_KW_LIST_APPEND 44 +#define CALL_NO_KW_METHOD_DESCRIPTOR_FAST 45 +#define CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS 46 +#define CALL_NO_KW_METHOD_DESCRIPTOR_O 47 +#define CALL_NO_KW_STR_1 48 +#define CALL_NO_KW_TUPLE_1 56 +#define CALL_NO_KW_TYPE_1 57 +#define COMPARE_OP_ADAPTIVE 58 +#define COMPARE_OP_FLOAT_JUMP 59 +#define COMPARE_OP_INT_JUMP 62 +#define COMPARE_OP_STR_JUMP 63 +#define EXTENDED_ARG_QUICK 64 +#define FOR_ITER_ADAPTIVE 65 +#define FOR_ITER_LIST 66 +#define FOR_ITER_RANGE 67 +#define JUMP_BACKWARD_QUICK 72 +#define LOAD_ATTR_ADAPTIVE 73 +#define LOAD_ATTR_CLASS 76 +#define LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN 77 +#define LOAD_ATTR_INSTANCE_VALUE 78 +#define LOAD_ATTR_MODULE 79 +#define LOAD_ATTR_PROPERTY 80 +#define LOAD_ATTR_SLOT 81 +#define LOAD_ATTR_WITH_HINT 86 +#define LOAD_ATTR_METHOD_LAZY_DICT 113 +#define LOAD_ATTR_METHOD_NO_DICT 121 +#define LOAD_ATTR_METHOD_WITH_DICT 141 +#define LOAD_ATTR_METHOD_WITH_VALUES 143 +#define LOAD_CONST__LOAD_FAST 153 +#define LOAD_FAST__LOAD_CONST 154 +#define LOAD_FAST__LOAD_FAST 158 +#define LOAD_GLOBAL_ADAPTIVE 159 +#define LOAD_GLOBAL_BUILTIN 160 +#define LOAD_GLOBAL_MODULE 161 +#define RESUME_QUICK 166 +#define STORE_ATTR_ADAPTIVE 167 +#define STORE_ATTR_INSTANCE_VALUE 168 +#define STORE_ATTR_SLOT 169 +#define STORE_ATTR_WITH_HINT 170 +#define STORE_FAST__LOAD_FAST 177 +#define STORE_FAST__STORE_FAST 178 +#define STORE_SUBSCR_ADAPTIVE 179 +#define STORE_SUBSCR_DICT 180 +#define STORE_SUBSCR_LIST_INT 181 +#define UNPACK_SEQUENCE_ADAPTIVE 182 +#define UNPACK_SEQUENCE_LIST 183 +#define UNPACK_SEQUENCE_TUPLE 184 +#define UNPACK_SEQUENCE_TWO_TUPLE 185 #define DO_TRACING 255 #define HAS_ARG(op) ((((op) >= HAVE_ARGUMENT) && (!IS_PSEUDO_OPCODE(op)))\ diff --git a/Lib/opcode.py b/Lib/opcode.py index 52c1271868e3ab..c3405aec02987e 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -77,6 +77,7 @@ def pseudo_op(name, op, real_ops): def_op('CACHE', 0) def_op('POP_TOP', 1) def_op('PUSH_NULL', 2) +def_op('INTEPRETER_EXIT', 3) def_op('NOP', 9) def_op('UNARY_POSITIVE', 10) diff --git a/Python/ceval.c b/Python/ceval.c index 7024addfe626cd..a98c7bd32516e4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1668,7 +1668,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int frame->is_entry = true; /* Push frame */ - frame->previous = prev_cframe->current_frame; + struct _PyInterpreterFrame py_frame; + py_frame.f_func = Py_INCREF(&_exit_func); + py_frame.f_locals = NULL; + py_frame.frame_obj = NULL; + py_frame.prev_instr = _PyCode_CODE(&exit_code); + py_frame.stacktop = 0; + py_frame.owner = -1; + py_frame.is_entry = 1; + py_frame.previous = prev_cframe->current_frame; + frame->previous = &py_frame; cframe.current_frame = frame; /* support for generator.throw() */ @@ -2456,18 +2465,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int goto error; } - TARGET(RETURN_VALUE) { + TARGET(INTERPRETER_EXIT) { PyObject *retval = POP(); assert(EMPTY()); - _PyFrame_SetStackPointer(frame, stack_pointer); - TRACE_FUNCTION_EXIT(); - DTRACE_FUNCTION_EXIT(); - _Py_LeaveRecursiveCallTstate(tstate); - if (!frame->is_entry) { - frame = cframe.current_frame = pop_frame(tstate, frame); - _PyFrame_StackPush(frame, retval); - goto resume_frame; - } /* Restore previous cframe and return. */ tstate->cframe = cframe.previous; tstate->cframe->use_tracing = cframe.use_tracing; @@ -2476,6 +2476,19 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int return retval; } + TARGET(RETURN_VALUE) { + PyObject *retval = POP(); + assert(EMPTY()); + _PyFrame_SetStackPointer(frame, stack_pointer); + TRACE_FUNCTION_EXIT(); + DTRACE_FUNCTION_EXIT(); + _Py_LeaveRecursiveCallTstate(tstate); + assert(!frame->is_entry); + frame = cframe.current_frame = pop_frame(tstate, frame); + _PyFrame_StackPush(frame, retval); + goto resume_frame; + } + TARGET(GET_AITER) { unaryfunc getter = NULL; PyObject *iter = NULL; diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 7c782d101c1b8c..6b113f4f10e806 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -2,19 +2,20 @@ static void *opcode_targets[256] = { &&TARGET_CACHE, &&TARGET_POP_TOP, &&TARGET_PUSH_NULL, + &&TARGET_INTEPRETER_EXIT, &&TARGET_BINARY_OP_ADAPTIVE, &&TARGET_BINARY_OP_ADD_FLOAT, &&TARGET_BINARY_OP_ADD_INT, &&TARGET_BINARY_OP_ADD_UNICODE, &&TARGET_BINARY_OP_INPLACE_ADD_UNICODE, - &&TARGET_BINARY_OP_MULTIPLY_FLOAT, &&TARGET_NOP, &&TARGET_UNARY_POSITIVE, &&TARGET_UNARY_NEGATIVE, &&TARGET_UNARY_NOT, + &&TARGET_BINARY_OP_MULTIPLY_FLOAT, &&TARGET_BINARY_OP_MULTIPLY_INT, - &&TARGET_BINARY_OP_SUBTRACT_FLOAT, &&TARGET_UNARY_INVERT, + &&TARGET_BINARY_OP_SUBTRACT_FLOAT, &&TARGET_BINARY_OP_SUBTRACT_INT, &&TARGET_BINARY_SUBSCR_ADAPTIVE, &&TARGET_BINARY_SUBSCR_DICT, @@ -23,20 +24,20 @@ static void *opcode_targets[256] = { &&TARGET_BINARY_SUBSCR_TUPLE_INT, &&TARGET_CALL_ADAPTIVE, &&TARGET_CALL_PY_EXACT_ARGS, - &&TARGET_CALL_PY_WITH_DEFAULTS, &&TARGET_BINARY_SUBSCR, &&TARGET_BINARY_SLICE, &&TARGET_STORE_SLICE, + &&TARGET_CALL_PY_WITH_DEFAULTS, &&TARGET_CALL_BOUND_METHOD_EXACT_ARGS, - &&TARGET_CALL_BUILTIN_CLASS, &&TARGET_GET_LEN, &&TARGET_MATCH_MAPPING, &&TARGET_MATCH_SEQUENCE, &&TARGET_MATCH_KEYS, - &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, + &&TARGET_CALL_BUILTIN_CLASS, &&TARGET_PUSH_EXC_INFO, &&TARGET_CHECK_EXC_MATCH, &&TARGET_CHECK_EG_MATCH, + &&TARGET_CALL_BUILTIN_FAST_WITH_KEYWORDS, &&TARGET_CALL_METHOD_DESCRIPTOR_FAST_WITH_KEYWORDS, &&TARGET_CALL_NO_KW_BUILTIN_FAST, &&TARGET_CALL_NO_KW_BUILTIN_O, @@ -47,7 +48,6 @@ static void *opcode_targets[256] = { &&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS, &&TARGET_CALL_NO_KW_METHOD_DESCRIPTOR_O, &&TARGET_CALL_NO_KW_STR_1, - &&TARGET_CALL_NO_KW_TUPLE_1, &&TARGET_WITH_EXCEPT_START, &&TARGET_GET_AITER, &&TARGET_GET_ANEXT, @@ -55,37 +55,37 @@ static void *opcode_targets[256] = { &&TARGET_BEFORE_WITH, &&TARGET_END_ASYNC_FOR, &&TARGET_CLEANUP_THROW, + &&TARGET_CALL_NO_KW_TUPLE_1, &&TARGET_CALL_NO_KW_TYPE_1, &&TARGET_COMPARE_OP_ADAPTIVE, &&TARGET_COMPARE_OP_FLOAT_JUMP, - &&TARGET_COMPARE_OP_INT_JUMP, &&TARGET_STORE_SUBSCR, &&TARGET_DELETE_SUBSCR, + &&TARGET_COMPARE_OP_INT_JUMP, &&TARGET_COMPARE_OP_STR_JUMP, &&TARGET_EXTENDED_ARG_QUICK, &&TARGET_FOR_ITER_ADAPTIVE, &&TARGET_FOR_ITER_LIST, &&TARGET_FOR_ITER_RANGE, - &&TARGET_JUMP_BACKWARD_QUICK, &&TARGET_GET_ITER, &&TARGET_GET_YIELD_FROM_ITER, &&TARGET_PRINT_EXPR, &&TARGET_LOAD_BUILD_CLASS, + &&TARGET_JUMP_BACKWARD_QUICK, &&TARGET_LOAD_ATTR_ADAPTIVE, - &&TARGET_LOAD_ATTR_CLASS, &&TARGET_LOAD_ASSERTION_ERROR, &&TARGET_RETURN_GENERATOR, + &&TARGET_LOAD_ATTR_CLASS, &&TARGET_LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN, &&TARGET_LOAD_ATTR_INSTANCE_VALUE, &&TARGET_LOAD_ATTR_MODULE, &&TARGET_LOAD_ATTR_PROPERTY, &&TARGET_LOAD_ATTR_SLOT, - &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_LIST_TO_TUPLE, &&TARGET_RETURN_VALUE, &&TARGET_IMPORT_STAR, &&TARGET_SETUP_ANNOTATIONS, - &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, + &&TARGET_LOAD_ATTR_WITH_HINT, &&TARGET_ASYNC_GEN_WRAP, &&TARGET_PREP_RERAISE_STAR, &&TARGET_POP_EXCEPT, @@ -112,7 +112,7 @@ static void *opcode_targets[256] = { &&TARGET_JUMP_FORWARD, &&TARGET_JUMP_IF_FALSE_OR_POP, &&TARGET_JUMP_IF_TRUE_OR_POP, - &&TARGET_LOAD_ATTR_METHOD_NO_DICT, + &&TARGET_LOAD_ATTR_METHOD_LAZY_DICT, &&TARGET_POP_JUMP_FORWARD_IF_FALSE, &&TARGET_POP_JUMP_FORWARD_IF_TRUE, &&TARGET_LOAD_GLOBAL, @@ -120,7 +120,7 @@ static void *opcode_targets[256] = { &&TARGET_CONTAINS_OP, &&TARGET_RERAISE, &&TARGET_COPY, - &&TARGET_LOAD_ATTR_METHOD_WITH_DICT, + &&TARGET_LOAD_ATTR_METHOD_NO_DICT, &&TARGET_BINARY_OP, &&TARGET_SEND, &&TARGET_LOAD_FAST, @@ -140,9 +140,9 @@ static void *opcode_targets[256] = { &&TARGET_STORE_DEREF, &&TARGET_DELETE_DEREF, &&TARGET_JUMP_BACKWARD, - &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, + &&TARGET_LOAD_ATTR_METHOD_WITH_DICT, &&TARGET_CALL_FUNCTION_EX, - &&TARGET_LOAD_CONST__LOAD_FAST, + &&TARGET_LOAD_ATTR_METHOD_WITH_VALUES, &&TARGET_EXTENDED_ARG, &&TARGET_LIST_APPEND, &&TARGET_SET_ADD, @@ -152,30 +152,31 @@ static void *opcode_targets[256] = { &&TARGET_YIELD_VALUE, &&TARGET_RESUME, &&TARGET_MATCH_CLASS, + &&TARGET_LOAD_CONST__LOAD_FAST, &&TARGET_LOAD_FAST__LOAD_CONST, - &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_FORMAT_VALUE, &&TARGET_BUILD_CONST_KEY_MAP, &&TARGET_BUILD_STRING, + &&TARGET_LOAD_FAST__LOAD_FAST, &&TARGET_LOAD_GLOBAL_ADAPTIVE, &&TARGET_LOAD_GLOBAL_BUILTIN, &&TARGET_LOAD_GLOBAL_MODULE, - &&TARGET_RESUME_QUICK, &&TARGET_LIST_EXTEND, &&TARGET_SET_UPDATE, &&TARGET_DICT_MERGE, &&TARGET_DICT_UPDATE, + &&TARGET_RESUME_QUICK, &&TARGET_STORE_ATTR_ADAPTIVE, &&TARGET_STORE_ATTR_INSTANCE_VALUE, &&TARGET_STORE_ATTR_SLOT, &&TARGET_STORE_ATTR_WITH_HINT, - &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_CALL, &&TARGET_KW_NAMES, &&TARGET_POP_JUMP_BACKWARD_IF_NOT_NONE, &&TARGET_POP_JUMP_BACKWARD_IF_NONE, &&TARGET_POP_JUMP_BACKWARD_IF_FALSE, &&TARGET_POP_JUMP_BACKWARD_IF_TRUE, + &&TARGET_STORE_FAST__LOAD_FAST, &&TARGET_STORE_FAST__STORE_FAST, &&TARGET_STORE_SUBSCR_ADAPTIVE, &&TARGET_STORE_SUBSCR_DICT, @@ -253,6 +254,5 @@ static void *opcode_targets[256] = { &&_unknown_opcode, &&_unknown_opcode, &&_unknown_opcode, - &&_unknown_opcode, &&TARGET_DO_TRACING }; From 7f5348ae127e4a48d395f14e9beacaf904d4aedc Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 26 Aug 2022 12:03:54 +0100 Subject: [PATCH 02/29] Get entry frame shim working. --- Include/internal/pycore_code.h | 3 + Include/internal/pycore_frame.h | 10 +-- Include/internal/pycore_interp.h | 1 + Include/internal/pycore_opcode.h | 4 +- Include/opcode.h | 2 +- Lib/opcode.py | 2 +- Modules/_tracemalloc.c | 3 + Objects/codeobject.c | 54 +++++++++++++++ Objects/frameobject.c | 10 ++- Objects/genobject.c | 4 +- Python/ceval.c | 114 +++++++++++++++---------------- Python/compile.c | 2 + Python/frame.c | 3 + Python/opcode_targets.h | 2 +- Python/pylifecycle.c | 12 +++- Python/pystate.c | 1 + Python/traceback.c | 7 ++ Tools/gdb/libpython.py | 14 ++-- 18 files changed, 167 insertions(+), 81 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 7d5fe941fcb4d0..a38be041107c51 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -510,6 +510,9 @@ _PyCode_LineNumberFromArray(PyCodeObject *co, int index) } } +extern PyCodeObject * +_Py_MakeTrampoline(const char *code, int codelen, const char *cname); + #ifdef __cplusplus } diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index decaafd141e9e1..aeada5b3e98575 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -42,7 +42,8 @@ typedef enum _framestate { enum _frameowner { FRAME_OWNED_BY_THREAD = 0, FRAME_OWNED_BY_GENERATOR = 1, - FRAME_OWNED_BY_FRAME_OBJECT = 2 + FRAME_OWNED_BY_FRAME_OBJECT = 2, + FRAME_OWNED_BY_CSTACK = 3, }; typedef struct _PyInterpreterFrame { @@ -60,9 +61,9 @@ typedef struct _PyInterpreterFrame { // example, it may be an inline CACHE entry, an instruction we just jumped // over, or (in the case of a newly-created frame) a totally invalid value: _Py_CODEUNIT *prev_instr; - int stacktop; /* Offset of TOS from localsplus */ - bool is_entry; // Whether this is the "root" frame for the current _PyCFrame. - char owner; + int stacktop; /* Offset of TOS from localsplus */ + int yield_offset: 28; + int owner: 4; /* Locals and stack */ PyObject *localsplus[1]; } _PyInterpreterFrame; @@ -109,7 +110,6 @@ _PyFrame_InitializeSpecials( frame->stacktop = code->co_nlocalsplus; frame->frame_obj = NULL; frame->prev_instr = _PyCode_CODE(code) - 1; - frame->is_entry = false; frame->owner = FRAME_OWNED_BY_THREAD; } diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index d71386953a0dd0..c049339c975386 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -175,6 +175,7 @@ struct _is { struct ast_state ast; struct types_state types; struct callable_cache callable_cache; + PyCodeObject *interpreter_trampoline; /* The following fields are here to avoid allocation during init. The data is exposed through PyInterpreterState pointer fields. diff --git a/Include/internal/pycore_opcode.h b/Include/internal/pycore_opcode.h index 392f560950108b..01e58c562963a1 100644 --- a/Include/internal/pycore_opcode.h +++ b/Include/internal/pycore_opcode.h @@ -138,7 +138,7 @@ const uint8_t _PyOpcode_Deopt[256] = { [IMPORT_FROM] = IMPORT_FROM, [IMPORT_NAME] = IMPORT_NAME, [IMPORT_STAR] = IMPORT_STAR, - [INTEPRETER_EXIT] = INTEPRETER_EXIT, + [INTERPRETER_EXIT] = INTERPRETER_EXIT, [IS_OP] = IS_OP, [JUMP_BACKWARD] = JUMP_BACKWARD, [JUMP_BACKWARD_NO_INTERRUPT] = JUMP_BACKWARD_NO_INTERRUPT, @@ -248,7 +248,7 @@ static const char *const _PyOpcode_OpName[267] = { [CACHE] = "CACHE", [POP_TOP] = "POP_TOP", [PUSH_NULL] = "PUSH_NULL", - [INTEPRETER_EXIT] = "INTEPRETER_EXIT", + [INTERPRETER_EXIT] = "INTERPRETER_EXIT", [BINARY_OP_ADAPTIVE] = "BINARY_OP_ADAPTIVE", [BINARY_OP_ADD_FLOAT] = "BINARY_OP_ADD_FLOAT", [BINARY_OP_ADD_INT] = "BINARY_OP_ADD_INT", diff --git a/Include/opcode.h b/Include/opcode.h index 1ca4b0d16f1914..3fd56a42e0dba3 100644 --- a/Include/opcode.h +++ b/Include/opcode.h @@ -11,7 +11,7 @@ extern "C" { #define CACHE 0 #define POP_TOP 1 #define PUSH_NULL 2 -#define INTEPRETER_EXIT 3 +#define INTERPRETER_EXIT 3 #define NOP 9 #define UNARY_POSITIVE 10 #define UNARY_NEGATIVE 11 diff --git a/Lib/opcode.py b/Lib/opcode.py index c3405aec02987e..9f09c97f0e65c1 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -77,7 +77,7 @@ def pseudo_op(name, op, real_ops): def_op('CACHE', 0) def_op('POP_TOP', 1) def_op('PUSH_NULL', 2) -def_op('INTEPRETER_EXIT', 3) +def_op('INTERPRETER_EXIT', 3) def_op('NOP', 9) def_op('UNARY_POSITIVE', 10) diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index ae09869deda704..436bcc5ba3dc44 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -411,6 +411,9 @@ traceback_get_frames(traceback_t *traceback) } _PyInterpreterFrame *back = pyframe->previous; + if (back && back->owner == FRAME_OWNED_BY_CSTACK) { + back = back->previous; + } pyframe = back; } } diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 72712f40e42c7c..49ee3c0edde1a2 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2221,3 +2221,57 @@ _PyStaticCode_InternStrings(PyCodeObject *co) } return 0; } + +PyCodeObject * +_Py_MakeTrampoline(const char *code, int codelen, const char *cname) +{ + PyObject *name = NULL; + PyObject *co_code = NULL; + PyObject *lines = NULL; + PyCodeObject *codeobj = NULL; + + name = _PyUnicode_FromASCII(cname, strlen(cname)); + if (name == NULL) { + goto cleanup; + } + co_code = PyBytes_FromStringAndSize(code, codelen); + if (co_code == NULL) { + goto cleanup; + } + const char loc[2] = { 0x80 | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) | 3, 0 }; + lines = PyBytes_FromStringAndSize(loc, 2); + if (lines == NULL) { + goto cleanup; + } + struct _PyCodeConstructor con = { + .filename = &_Py_STR(empty), + .name = name, + .qualname = name, + .flags = CO_NEWLOCALS | CO_OPTIMIZED, + + .code = co_code, + .firstlineno = 1, + .linetable = lines, + + .consts = (PyObject *)&_Py_SINGLETON(tuple_empty), + .names = (PyObject *)&_Py_SINGLETON(tuple_empty), + + .localsplusnames = (PyObject *)&_Py_SINGLETON(tuple_empty), + .localspluskinds = (PyObject *)&_Py_SINGLETON(bytes_empty), + + .argcount = 0, + .posonlyargcount = 0, + .kwonlyargcount = 0, + + .stacksize = 2, + + .exceptiontable = (PyObject *)&_Py_SINGLETON(bytes_empty), + }; + + codeobj = _PyCode_New(&con); +cleanup: + Py_XDECREF(name); + Py_XDECREF(co_code); + Py_XDECREF(lines); + return codeobj; +} diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d2647bd122888c..f7a467ba86ebbd 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1339,14 +1339,18 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) } } - int _PyFrame_IsEntryFrame(PyFrameObject *frame) { assert(frame != NULL); - return frame->f_frame->is_entry; + _PyInterpreterFrame *f = frame->f_frame; + assert(f->owner == FRAME_OWNED_BY_FRAME_OBJECT || + f->owner == FRAME_OWNED_BY_GENERATOR); + if (f->previous == NULL) { + return 0; + } + return f->previous->owner == FRAME_OWNED_BY_CSTACK; } - PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { diff --git a/Objects/genobject.c b/Objects/genobject.c index da4afecc69c8c1..248f2c7179bb0e 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -208,6 +208,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, Py_INCREF(result); _PyFrame_StackPush(frame, result); + _PyInterpreterFrame *prev = tstate->cframe->current_frame; frame->previous = tstate->cframe->current_frame; gen->gi_exc_state.previous_item = tstate->exc_info; @@ -227,7 +228,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; - assert(tstate->cframe->current_frame == frame->previous); + assert(tstate->cframe->current_frame == prev); /* Don't keep the reference to previous any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ @@ -273,7 +274,6 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ClearExcState(&gen->gi_exc_state); gen->gi_frame_state = FRAME_CLEARED; - _PyFrame_Clear(frame); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } diff --git a/Python/ceval.c b/Python/ceval.c index 2ba88571b0d236..2ea3effe790745 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1031,16 +1031,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int cframe.previous = prev_cframe; tstate->cframe = &cframe; - frame->is_entry = true; /* Push frame */ struct _PyInterpreterFrame py_frame; - py_frame.f_func = Py_INCREF(&_exit_func); + py_frame.f_funcobj = Py_None; py_frame.f_locals = NULL; py_frame.frame_obj = NULL; - py_frame.prev_instr = _PyCode_CODE(&exit_code); + py_frame.f_code = tstate->interp->interpreter_trampoline; + py_frame.prev_instr = _PyCode_CODE(tstate->interp->interpreter_trampoline); py_frame.stacktop = 0; - py_frame.owner = -1; - py_frame.is_entry = 1; + py_frame.owner = FRAME_OWNED_BY_CSTACK; + py_frame.yield_offset = 0; py_frame.previous = prev_cframe->current_frame; frame->previous = &py_frame; cframe.current_frame = frame; @@ -1097,14 +1097,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef LLTRACE { - int r = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__)); - if (r < 0) { - goto exit_unwind; + if (PyFunction_Check(frame->f_funcobj)) { + int r = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__)); + if (r < 0) { + goto exit_unwind; + } + lltrace = r; + } + if (lltrace) { + lltrace_resume_frame(frame); } - lltrace = r; - } - if (lltrace) { - lltrace_resume_frame(frame); } #endif @@ -1831,6 +1833,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(INTERPRETER_EXIT) { + assert(_PyFrame_IsIncomplete(frame)); PyObject *retval = POP(); assert(EMPTY()); /* Restore previous cframe and return. */ @@ -1848,7 +1851,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallTstate(tstate); - assert(!frame->is_entry); + assert(frame != &py_frame); frame = cframe.current_frame = pop_frame(tstate, frame); _PyFrame_StackPush(frame, retval); goto resume_frame; @@ -1985,7 +1988,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(SEND) { - assert(frame->is_entry); + assert(frame != &py_frame); assert(STACK_LEVEL() >= 2); PyObject *v = POP(); PyObject *receiver = TOP(); @@ -2050,19 +2053,19 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // The compiler treats any exception raised here as a failed close() // or throw() call. assert(oparg == STACK_LEVEL()); - assert(frame->is_entry); + assert(frame != &py_frame); PyObject *retval = POP(); _PyFrame_GetGenerator(frame)->gi_frame_state = FRAME_SUSPENDED; _PyFrame_SetStackPointer(frame, stack_pointer); TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallTstate(tstate); - /* Restore previous cframe and return. */ - tstate->cframe = cframe.previous; - tstate->cframe->use_tracing = cframe.use_tracing; - assert(tstate->cframe->current_frame == frame->previous); - assert(!_PyErr_Occurred(tstate)); - return retval; + _PyInterpreterFrame *gen_frame = frame; + frame = cframe.current_frame = gen_frame->previous; + gen_frame->previous = NULL; + frame->prev_instr += frame->yield_offset; + _PyFrame_StackPush(frame, retval); + goto resume_frame; } TARGET(POP_EXCEPT) { @@ -4926,24 +4929,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int gen->gi_frame_state = FRAME_CREATED; gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallTstate(tstate); - if (!frame->is_entry) { - _PyInterpreterFrame *prev = frame->previous; - _PyThreadState_PopFrame(tstate, frame); - frame = cframe.current_frame = prev; - _PyFrame_StackPush(frame, (PyObject *)gen); - goto resume_frame; - } - /* Make sure that frame is in a valid state */ - frame->stacktop = 0; - frame->f_locals = NULL; - Py_INCREF(frame->f_funcobj); - Py_INCREF(frame->f_code); - /* Restore previous cframe and return. */ - tstate->cframe = cframe.previous; - tstate->cframe->use_tracing = cframe.use_tracing; - assert(tstate->cframe->current_frame == frame->previous); - assert(!_PyErr_Occurred(tstate)); - return (PyObject *)gen; + assert(frame != &py_frame); + _PyInterpreterFrame *prev = frame->previous; + _PyThreadState_PopFrame(tstate, frame); + frame = cframe.current_frame = prev; + _PyFrame_StackPush(frame, (PyObject *)gen); + goto resume_frame; } TARGET(BUILD_SLICE) { @@ -5238,6 +5229,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif /* Log traceback info. */ + assert(frame != &py_frame); PyFrameObject *f = _PyFrame_GetFrameObject(frame); if (f != NULL) { PyTraceBack_Here(f); @@ -5308,14 +5300,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int exit_unwind: assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallTstate(tstate); - if (frame->is_entry) { + assert(frame != &py_frame); + frame = cframe.current_frame = pop_frame(tstate, frame); + if (frame == &py_frame) { + assert(_PyFrame_IsIncomplete(frame)); /* Restore previous cframe and exit */ tstate->cframe = cframe.previous; tstate->cframe->use_tracing = cframe.use_tracing; assert(tstate->cframe->current_frame == frame->previous); return NULL; } - frame = cframe.current_frame = pop_frame(tstate, frame); resume_with_error: SET_LOCALS_FROM_FRAME(); @@ -5874,16 +5868,26 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, static void _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame) { - // Make sure that this is, indeed, the top frame. We can't check this in - // _PyThreadState_PopFrame, since f_code is already cleared at that point: - assert((PyObject **)frame + frame->f_code->co_framesize == - tstate->datastack_top); - tstate->recursion_remaining--; - assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); - assert(frame->owner == FRAME_OWNED_BY_THREAD); - _PyFrame_Clear(frame); - tstate->recursion_remaining++; - _PyThreadState_PopFrame(tstate, frame); + if (frame->owner == FRAME_OWNED_BY_THREAD) { + // Make sure that this is, indeed, the top frame. We can't check this in + // _PyThreadState_PopFrame, since f_code is already cleared at that point: + assert((PyObject **)frame + frame->f_code->co_framesize == + tstate->datastack_top); + tstate->recursion_remaining--; + assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); + _PyFrame_Clear(frame); + tstate->recursion_remaining++; + _PyThreadState_PopFrame(tstate, frame); + } + else { + assert(frame->owner == FRAME_OWNED_BY_GENERATOR); + _PyFrame_GetGenerator(frame)->gi_frame_state = FRAME_CLEARED; + tstate->recursion_remaining--; + assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); + _PyFrame_Clear(frame); + tstate->recursion_remaining++; + frame->previous = NULL; + } } PyObject * @@ -5911,13 +5915,7 @@ _PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func, return NULL; } EVAL_CALL_STAT_INC(EVAL_CALL_VECTOR); - PyObject *retval = _PyEval_EvalFrame(tstate, frame, 0); - assert( - _PyFrame_GetStackPointer(frame) == _PyFrame_Stackbase(frame) || - _PyFrame_GetStackPointer(frame) == frame->localsplus - ); - _PyEvalFrameClearAndPop(tstate, frame); - return retval; + return _PyEval_EvalFrame(tstate, frame, 0); } /* Legacy API */ diff --git a/Python/compile.c b/Python/compile.c index 627f86a8ce9188..2eaa9f9e73e868 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1278,6 +1278,8 @@ stack_effect(int opcode, int oparg, int jump) return 1; case BINARY_OP: return -1; + case INTERPRETER_EXIT: + return -1; default: return PY_INVALID_STACK_EFFECT; } diff --git a/Python/frame.c b/Python/frame.c index 14464df0a8d506..3fb8cb1c4fabb2 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -98,6 +98,7 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) * to have cleared the enclosing generator, if any. */ assert(frame->owner != FRAME_OWNED_BY_GENERATOR || _PyFrame_GetGenerator(frame)->gi_frame_state == FRAME_CLEARED); + assert(frame->owner != FRAME_CLEARED); if (frame->frame_obj) { PyFrameObject *f = frame->frame_obj; frame->frame_obj = NULL; @@ -112,10 +113,12 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) for (int i = 0; i < frame->stacktop; i++) { Py_XDECREF(frame->localsplus[i]); } + frame->stacktop = 0; Py_XDECREF(frame->frame_obj); Py_XDECREF(frame->f_locals); Py_DECREF(frame->f_funcobj); Py_DECREF(frame->f_code); + frame->owner = FRAME_CLEARED; } int diff --git a/Python/opcode_targets.h b/Python/opcode_targets.h index 6b113f4f10e806..d307043e76c7fe 100644 --- a/Python/opcode_targets.h +++ b/Python/opcode_targets.h @@ -2,7 +2,7 @@ static void *opcode_targets[256] = { &&TARGET_CACHE, &&TARGET_POP_TOP, &&TARGET_PUSH_NULL, - &&TARGET_INTEPRETER_EXIT, + &&TARGET_INTERPRETER_EXIT, &&TARGET_BINARY_OP_ADAPTIVE, &&TARGET_BINARY_OP_ADD_FLOAT, &&TARGET_BINARY_OP_ADD_INT, diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index bb646f1a0ee2d0..2d09afd0203093 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -28,6 +28,7 @@ #include "pycore_tuple.h" // _PyTuple_InitTypes() #include "pycore_typeobject.h" // _PyTypes_InitTypes() #include "pycore_unicodeobject.h" // _PyUnicode_InitTypes() +#include "opcode.h" extern void _PyIO_Fini(void); @@ -756,6 +757,11 @@ pycore_init_types(PyInterpreterState *interp) return _PyStatus_OK(); } +const char INTERPRETER_TRAMPOLINE_CODE[] = { + 0, 0, + INTERPRETER_EXIT, 0, + RESUME, 0 +}; static PyStatus pycore_init_builtins(PyThreadState *tstate) @@ -790,7 +796,11 @@ pycore_init_builtins(PyThreadState *tstate) PyObject *object__getattribute__ = _PyType_Lookup(&PyBaseObject_Type, &_Py_ID(__getattribute__)); assert(object__getattribute__); interp->callable_cache.object__getattribute__ = object__getattribute__; - + interp->interpreter_trampoline = _Py_MakeTrampoline( + INTERPRETER_TRAMPOLINE_CODE, sizeof(INTERPRETER_TRAMPOLINE_CODE), ""); + if (interp->interpreter_trampoline == NULL) { + return _PyStatus_ERR("failed to create interpreter trampoline."); + } if (_PyBuiltins_AddExceptions(bimod) < 0) { return _PyStatus_ERR("failed to add exceptions to builtins"); } diff --git a/Python/pystate.c b/Python/pystate.c index a11f1622ecd4ab..49ccde4628316e 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -425,6 +425,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->importlib); Py_CLEAR(interp->import_func); Py_CLEAR(interp->dict); + Py_CLEAR(interp->interpreter_trampoline); #ifdef HAVE_FORK Py_CLEAR(interp->before_forkers); Py_CLEAR(interp->after_forkers_parent); diff --git a/Python/traceback.c b/Python/traceback.c index de658b9103180e..aeb4124a8bc2e7 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -1229,6 +1229,13 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) if (frame == NULL) { break; } + if (frame->owner == FRAME_OWNED_BY_CSTACK) { + /* Trampoline frame */ + frame = frame->previous; + } + if (frame == NULL) { + break; + } depth++; } } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index 303409cb0077d1..e7b22d12cfad24 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -1078,7 +1078,7 @@ def _f_lasti(self): return int(prev_instr - first_instr) def is_entry(self): - return self._f_special("is_entry", bool) + return self._f_special("owner", int) == 3 def previous(self): return self._f_special("previous", PyFramePtr) @@ -1821,14 +1821,14 @@ def print_summary(self): interp_frame = self.get_pyop() while True: if interp_frame: + if interp_frame.is_entry(): + break line = interp_frame.get_truncated_repr(MAX_OUTPUT_LEN) sys.stdout.write('#%i %s\n' % (self.get_index(), line)) if not interp_frame.is_optimized_out(): line = interp_frame.current_line() if line is not None: sys.stdout.write(' %s\n' % line.strip()) - if interp_frame.is_entry(): - break else: sys.stdout.write('#%i (unable to read python frame information)\n' % self.get_index()) break @@ -1845,13 +1845,13 @@ def print_traceback(self): interp_frame = self.get_pyop() while True: if interp_frame: + if interp_frame.is_entry(): + break interp_frame.print_traceback() if not interp_frame.is_optimized_out(): line = interp_frame.current_line() if line is not None: sys.stdout.write(' %s\n' % line.strip()) - if interp_frame.is_entry(): - break else: sys.stdout.write(' (unable to read python frame information)\n') break @@ -2106,6 +2106,8 @@ def invoke(self, args, from_tty): while True: if not pyop_frame: print(UNABLE_READ_INFO_PYTHON_FRAME) + if pyop_frame.is_entry(): + break sys.stdout.write('Locals for %s\n' % (pyop_frame.co_name.proxyval(set()))) @@ -2114,8 +2116,6 @@ def invoke(self, args, from_tty): % (pyop_name.proxyval(set()), pyop_value.get_truncated_repr(MAX_OUTPUT_LEN))) - if pyop_frame.is_entry(): - break pyop_frame = pyop_frame.previous() From 59e7727565d4ff44f97f24061faf99ab0a4021b3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 30 Aug 2022 10:37:17 +0100 Subject: [PATCH 03/29] Delay destruction of trampoline code object and make symbol private --- Python/ceval.c | 1 + Python/pylifecycle.c | 2 +- Python/pystate.c | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 2ea3effe790745..094cf5d7ea0110 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1037,6 +1037,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int py_frame.f_locals = NULL; py_frame.frame_obj = NULL; py_frame.f_code = tstate->interp->interpreter_trampoline; + assert(tstate->interp->interpreter_trampoline != NULL); py_frame.prev_instr = _PyCode_CODE(tstate->interp->interpreter_trampoline); py_frame.stacktop = 0; py_frame.owner = FRAME_OWNED_BY_CSTACK; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 2d09afd0203093..18b053526459dd 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -757,7 +757,7 @@ pycore_init_types(PyInterpreterState *interp) return _PyStatus_OK(); } -const char INTERPRETER_TRAMPOLINE_CODE[] = { +static const char INTERPRETER_TRAMPOLINE_CODE[] = { 0, 0, INTERPRETER_EXIT, 0, RESUME, 0 diff --git a/Python/pystate.c b/Python/pystate.c index 49ccde4628316e..b286e9c6196de7 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -425,7 +425,6 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->importlib); Py_CLEAR(interp->import_func); Py_CLEAR(interp->dict); - Py_CLEAR(interp->interpreter_trampoline); #ifdef HAVE_FORK Py_CLEAR(interp->before_forkers); Py_CLEAR(interp->after_forkers_parent); @@ -451,6 +450,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) PyDict_Clear(interp->builtins); Py_CLEAR(interp->sysdict); Py_CLEAR(interp->builtins); + Py_CLEAR(interp->interpreter_trampoline); // XXX Once we have one allocator per interpreter (i.e. // per-interpreter GC) we must ensure that all of the interpreter's From 9f710e63e4c069bae9cd99c2a4cc7531f833607d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 30 Aug 2022 16:20:11 +0100 Subject: [PATCH 04/29] Make sure that previous never points into the C stack after return. --- Python/ceval.c | 1 + Python/frame.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index 094cf5d7ea0110..e55a55079b8e4e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4932,6 +4932,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _Py_LeaveRecursiveCallTstate(tstate); assert(frame != &py_frame); _PyInterpreterFrame *prev = frame->previous; + gen_frame->previous = NULL; _PyThreadState_PopFrame(tstate, frame); frame = cframe.current_frame = prev; _PyFrame_StackPush(frame, (PyObject *)gen); diff --git a/Python/frame.c b/Python/frame.c index 3fb8cb1c4fabb2..53ab0cfa363414 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -60,6 +60,7 @@ _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest) static void take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) { + assert(frame->owner != FRAME_OWNED_BY_CSTACK); assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); assert(frame->owner != FRAME_CLEARED); Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame; @@ -72,7 +73,9 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) while (prev && _PyFrame_IsIncomplete(prev)) { prev = prev->previous; } + frame->previous = NULL; if (prev) { + assert(prev->owner != FRAME_OWNED_BY_CSTACK); /* Link PyFrameObjects.f_back and remove link through _PyInterpreterFrame.previous */ PyFrameObject *back = _PyFrame_GetFrameObject(prev); if (back == NULL) { @@ -84,7 +87,6 @@ take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) else { f->f_back = (PyFrameObject *)Py_NewRef(back); } - frame->previous = NULL; } if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { _PyObject_GC_TRACK((PyObject *)f); From 4ee84abd0cbd8d15fc585b5b2bb5bb799ec42a0a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 30 Aug 2022 18:18:37 +0100 Subject: [PATCH 05/29] Use explicit stacksize in _Py_MakeTrampoline. Allow interpreter to clear previous frame pointer. --- Include/internal/pycore_code.h | 2 +- Objects/codeobject.c | 4 ++-- Objects/genobject.c | 6 +----- Python/ceval.c | 3 ++- Python/pylifecycle.c | 2 +- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index a38be041107c51..905cd44e6a1c69 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -511,7 +511,7 @@ _PyCode_LineNumberFromArray(PyCodeObject *co, int index) } extern PyCodeObject * -_Py_MakeTrampoline(const char *code, int codelen, const char *cname); +_Py_MakeTrampoline(const char *code, int codelen, int stacksize, const char *cname); #ifdef __cplusplus diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 49ee3c0edde1a2..e3209b1eca6078 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2223,7 +2223,7 @@ _PyStaticCode_InternStrings(PyCodeObject *co) } PyCodeObject * -_Py_MakeTrampoline(const char *code, int codelen, const char *cname) +_Py_MakeTrampoline(const char *code, int codelen, int stacksize, const char *cname) { PyObject *name = NULL; PyObject *co_code = NULL; @@ -2263,7 +2263,7 @@ _Py_MakeTrampoline(const char *code, int codelen, const char *cname) .posonlyargcount = 0, .kwonlyargcount = 0, - .stacksize = 2, + .stacksize = stacksize, .exceptiontable = (PyObject *)&_Py_SINGLETON(bytes_empty), }; diff --git a/Objects/genobject.c b/Objects/genobject.c index 248f2c7179bb0e..1c6285111cd06c 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -208,9 +208,6 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, Py_INCREF(result); _PyFrame_StackPush(frame, result); - _PyInterpreterFrame *prev = tstate->cframe->current_frame; - frame->previous = tstate->cframe->current_frame; - gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; @@ -228,11 +225,10 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; - assert(tstate->cframe->current_frame == prev); /* Don't keep the reference to previous any longer than necessary. It * may keep a chain of frames alive or it could create a reference * cycle. */ - frame->previous = NULL; + assert(frame->previous == NULL); /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ diff --git a/Python/ceval.c b/Python/ceval.c index e55a55079b8e4e..ef958c76b770ff 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1038,7 +1038,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int py_frame.frame_obj = NULL; py_frame.f_code = tstate->interp->interpreter_trampoline; assert(tstate->interp->interpreter_trampoline != NULL); - py_frame.prev_instr = _PyCode_CODE(tstate->interp->interpreter_trampoline); + _Py_CODEUNIT *code = _PyCode_CODE(tstate->interp->interpreter_trampoline); + py_frame.prev_instr = code; py_frame.stacktop = 0; py_frame.owner = FRAME_OWNED_BY_CSTACK; py_frame.yield_offset = 0; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 18b053526459dd..15f73abcd6d753 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -797,7 +797,7 @@ pycore_init_builtins(PyThreadState *tstate) assert(object__getattribute__); interp->callable_cache.object__getattribute__ = object__getattribute__; interp->interpreter_trampoline = _Py_MakeTrampoline( - INTERPRETER_TRAMPOLINE_CODE, sizeof(INTERPRETER_TRAMPOLINE_CODE), ""); + INTERPRETER_TRAMPOLINE_CODE, sizeof(INTERPRETER_TRAMPOLINE_CODE), 1, ""); if (interp->interpreter_trampoline == NULL) { return _PyStatus_ERR("failed to create interpreter trampoline."); } From f5161e31f01e9ebc9efb5d4be96e889d40e38cbc Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 31 Aug 2022 11:13:25 +0100 Subject: [PATCH 06/29] Move assert before use. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index ef958c76b770ff..1bb3dae6c06392 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1036,8 +1036,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int py_frame.f_funcobj = Py_None; py_frame.f_locals = NULL; py_frame.frame_obj = NULL; - py_frame.f_code = tstate->interp->interpreter_trampoline; assert(tstate->interp->interpreter_trampoline != NULL); + py_frame.f_code = tstate->interp->interpreter_trampoline; _Py_CODEUNIT *code = _PyCode_CODE(tstate->interp->interpreter_trampoline); py_frame.prev_instr = code; py_frame.stacktop = 0; From 06c2ad66228ddfdda89bb41c44d85230ab931d9d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 31 Aug 2022 12:34:02 +0100 Subject: [PATCH 07/29] Use typedef name. --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index 1bb3dae6c06392..b466d565a02d1a 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1032,7 +1032,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int tstate->cframe = &cframe; /* Push frame */ - struct _PyInterpreterFrame py_frame; + _PyInterpreterFrame py_frame; py_frame.f_funcobj = Py_None; py_frame.f_locals = NULL; py_frame.frame_obj = NULL; From 053f48844ba511dc2557fe4bcb1830bc9293925b Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 8 Sep 2022 15:45:08 +0100 Subject: [PATCH 08/29] More on-stack C structs into a big struct and add sentinels, as we want safety checks but ASAN gets tripped up in some cases. --- Python/ceval.c | 302 ++++++++++++++++++++++++++----------------------- 1 file changed, 163 insertions(+), 139 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 20a9bb6eb39633..46f89eca25aaec 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -711,8 +711,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { \ NEXTOPARG(); \ PRE_DISPATCH_GOTO(); \ - assert(cframe.use_tracing == 0 || cframe.use_tracing == 255); \ - opcode |= cframe.use_tracing OR_DTRACE_LINE; \ + assert(cframe.cframe.use_tracing == 0 || cframe.cframe.use_tracing == 255); \ + opcode |= cframe.cframe.use_tracing OR_DTRACE_LINE; \ DISPATCH_GOTO(); \ } @@ -796,7 +796,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define PREDICT(op) \ do { \ _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word) | cframe.use_tracing OR_DTRACE_LINE; \ + opcode = _Py_OPCODE(word) | cframe.cframe.use_tracing OR_DTRACE_LINE; \ if (opcode == op) { \ oparg = _Py_OPARG(word); \ INSTRUCTION_START(op); \ @@ -875,7 +875,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { /* Shared opcode macros */ #define TRACE_FUNCTION_EXIT() \ - if (cframe.use_tracing) { \ + if (cframe.cframe.use_tracing) { \ if (trace_function_exit(tstate, frame, retval)) { \ Py_DECREF(retval); \ goto exit_unwind; \ @@ -888,14 +888,14 @@ GETITEM(PyObject *v, Py_ssize_t i) { } #define TRACE_FUNCTION_UNWIND() \ - if (cframe.use_tracing) { \ + if (cframe.cframe.use_tracing) { \ /* Since we are already unwinding, \ * we don't care if this raises */ \ trace_function_exit(tstate, frame, NULL); \ } #define TRACE_FUNCTION_ENTRY() \ - if (cframe.use_tracing) { \ + if (cframe.cframe.use_tracing) { \ _PyFrame_SetStackPointer(frame, stack_pointer); \ int err = trace_function_entry(tstate, frame); \ stack_pointer = _PyFrame_GetStackPointer(frame); \ @@ -905,7 +905,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { } #define TRACE_FUNCTION_THROW_ENTRY() \ - if (cframe.use_tracing) { \ + if (cframe.cframe.use_tracing) { \ assert(frame->stacktop >= 0); \ if (trace_function_entry(tstate, frame)) { \ goto exit_unwind; \ @@ -988,18 +988,35 @@ pop_frame(PyThreadState *tstate, _PyInterpreterFrame *frame) return prev_frame; } -/* It is only between the KW_NAMES instruction and the following CALL, - * that this has any meaning. - */ -typedef struct { - PyObject *kwnames; -} CallShape; - // GH-89279: Must be a macro to be sure it's inlined by MSVC. #define is_method(stack_pointer, args) (PEEK((args)+2) != NULL) #define KWNAMES_LEN() \ - (call_shape.kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(call_shape.kwnames))) + (cframe.kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(cframe.kwnames))) + +#ifdef Py_DEBUG +#define SENTINEL(N) char sentinel_##N[8]; +#define CHECK_SENTINEL(N) \ + assert(strncmp(cframe.sentinel_##N, "Sentinel", 8) == 0); +#define SET_SENTINEL(N) \ + memcpy(cframe.sentinel_##N, "Sentinel", 8); +#else +#define SENTINEL(N) +#define CHECK_SENTINEL(N) +#define SET_SENTINEL(N) +#endif + +typedef struct _eval_frame_data { + SENTINEL(0); + _PyCFrame cframe; + SENTINEL(1); + _PyInterpreterFrame pyframe; + SENTINEL(2); +/* It is only between the KW_NAMES instruction and the following CALL, + * that this has any meaning. + */ + PyObject *kwnames; +} EvalFrameData; PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) @@ -1024,34 +1041,37 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int int lltrace = 0; #endif - _PyCFrame cframe; - CallShape call_shape; - call_shape.kwnames = NULL; // Borrowed reference. Reset by CALL instructions. + EvalFrameData cframe; + SET_SENTINEL(0); + SET_SENTINEL(1); + SET_SENTINEL(2); + cframe.kwnames = NULL; // Borrowed reference. Reset by CALL instructions. /* WARNING: Because the _PyCFrame lives on the C stack, * but can be accessed from a heap allocated object (tstate) * strict stack discipline must be maintained. */ _PyCFrame *prev_cframe = tstate->cframe; - cframe.use_tracing = prev_cframe->use_tracing; - cframe.previous = prev_cframe; - tstate->cframe = &cframe; + cframe.cframe.use_tracing = prev_cframe->use_tracing; + cframe.cframe.previous = prev_cframe; + tstate->cframe = &cframe.cframe; /* Push frame */ - _PyInterpreterFrame py_frame; - py_frame.f_funcobj = Py_None; - py_frame.f_locals = NULL; - py_frame.frame_obj = NULL; + cframe.pyframe.f_funcobj = Py_None; + cframe.pyframe.f_globals = NULL; + cframe.pyframe.f_builtins = NULL; + cframe.pyframe.f_locals = NULL; + cframe.pyframe.frame_obj = NULL; assert(tstate->interp->interpreter_trampoline != NULL); - py_frame.f_code = tstate->interp->interpreter_trampoline; + cframe.pyframe.f_code = tstate->interp->interpreter_trampoline; _Py_CODEUNIT *code = _PyCode_CODE(tstate->interp->interpreter_trampoline); - py_frame.prev_instr = code; - py_frame.stacktop = 0; - py_frame.owner = FRAME_OWNED_BY_CSTACK; - py_frame.yield_offset = 0; - py_frame.previous = prev_cframe->current_frame; - frame->previous = &py_frame; - cframe.current_frame = frame; + cframe.pyframe.prev_instr = code; + cframe.pyframe.stacktop = 0; + cframe.pyframe.owner = FRAME_OWNED_BY_CSTACK; + cframe.pyframe.yield_offset = 0; + cframe.pyframe.previous = prev_cframe->current_frame; + frame->previous = &cframe.pyframe; + cframe.cframe.current_frame = frame; /* support for generator.throw() */ if (throwflag) { @@ -1105,7 +1125,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef LLTRACE { - if (PyFunction_Check(frame->f_funcobj)) { + if (frame != &cframe.pyframe) { int r = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__)); if (r < 0) { goto exit_unwind; @@ -1163,8 +1183,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(RESUME_QUICK) { PREDICTED(RESUME_QUICK); - assert(tstate->cframe == &cframe); - assert(frame == cframe.current_frame); + assert(tstate->cframe == &cframe.cframe); + assert(frame == cframe.cframe.current_frame); if (_Py_atomic_load_relaxed_int32(eval_breaker) && oparg < 2) { goto handle_eval_breaker; } @@ -1337,7 +1357,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_MULTIPLY_INT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -1356,7 +1376,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_MULTIPLY_FLOAT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -1377,7 +1397,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_SUBTRACT_INT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -1396,7 +1416,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_SUBTRACT_FLOAT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -1416,7 +1436,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADD_UNICODE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); @@ -1435,7 +1455,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); @@ -1471,7 +1491,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADD_FLOAT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -1492,7 +1512,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADD_INT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -1583,7 +1603,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_SUBSCR_LIST_INT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *list = SECOND(); DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); @@ -1608,7 +1628,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_SUBSCR_TUPLE_INT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *tuple = SECOND(); DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); @@ -1633,7 +1653,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_SUBSCR_DICT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *dict = SECOND(); DEOPT_IF(!PyDict_CheckExact(SECOND()), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); @@ -1683,7 +1703,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.current_frame = new_frame; + frame = cframe.cframe.current_frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } @@ -1747,7 +1767,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_SUBSCR_LIST_INT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *list = SECOND(); PyObject *value = THIRD(); @@ -1773,7 +1793,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_SUBSCR_DICT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *dict = SECOND(); PyObject *value = THIRD(); @@ -1844,12 +1864,16 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(INTERPRETER_EXIT) { + assert(frame == &cframe.pyframe); assert(_PyFrame_IsIncomplete(frame)); + CHECK_SENTINEL(0); + CHECK_SENTINEL(1); + CHECK_SENTINEL(2); PyObject *retval = POP(); assert(EMPTY()); /* Restore previous cframe and return. */ - tstate->cframe = cframe.previous; - tstate->cframe->use_tracing = cframe.use_tracing; + tstate->cframe = cframe.cframe.previous; + tstate->cframe->use_tracing = cframe.cframe.use_tracing; assert(tstate->cframe->current_frame == frame->previous); assert(!_PyErr_Occurred(tstate)); return retval; @@ -1862,8 +1886,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallTstate(tstate); - assert(frame != &py_frame); - frame = cframe.current_frame = pop_frame(tstate, frame); + assert(frame != &cframe.pyframe); + frame = cframe.cframe.current_frame = pop_frame(tstate, frame); _PyFrame_StackPush(frame, retval); goto resume_frame; } @@ -1999,7 +2023,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(SEND) { - assert(frame != &py_frame); + assert(frame != &cframe.pyframe); assert(STACK_LEVEL() >= 2); PyObject *v = POP(); PyObject *receiver = TOP(); @@ -2064,7 +2088,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // The compiler treats any exception raised here as a failed close() // or throw() call. assert(oparg == STACK_LEVEL()); - assert(frame != &py_frame); + assert(frame != &cframe.pyframe); PyObject *retval = POP(); _PyFrame_GetGenerator(frame)->gi_frame_state = FRAME_SUSPENDED; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2072,7 +2096,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallTstate(tstate); _PyInterpreterFrame *gen_frame = frame; - frame = cframe.current_frame = gen_frame->previous; + frame = cframe.cframe.current_frame = gen_frame->previous; gen_frame->previous = NULL; frame->prev_instr += frame->yield_offset; _PyFrame_StackPush(frame, retval); @@ -2250,7 +2274,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(UNPACK_SEQUENCE_ADAPTIVE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *seq = TOP(); @@ -2491,7 +2515,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_GLOBAL_ADAPTIVE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *name = GETITEM(names, oparg>>1); @@ -2509,7 +2533,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_GLOBAL_MODULE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); PyDictObject *dict = (PyDictObject *)GLOBALS(); _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; @@ -2530,7 +2554,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_GLOBAL_BUILTIN) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); DEOPT_IF(!PyDict_CheckExact(BUILTINS()), LOAD_GLOBAL); PyDictObject *mdict = (PyDictObject *)GLOBALS(); @@ -2951,7 +2975,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_ADAPTIVE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *owner = TOP(); @@ -2970,7 +2994,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_INSTANCE_VALUE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); @@ -2995,7 +3019,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_MODULE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3020,7 +3044,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_WITH_HINT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); @@ -3059,7 +3083,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_SLOT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); @@ -3081,7 +3105,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_CLASS) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; PyObject *cls = TOP(); @@ -3104,7 +3128,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_PROPERTY) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -3136,13 +3160,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.current_frame = new_frame; + frame = cframe.cframe.current_frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; PyObject *owner = TOP(); @@ -3174,13 +3198,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.current_frame = new_frame; + frame = cframe.cframe.current_frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } TARGET(STORE_ATTR_ADAPTIVE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *owner = TOP(); @@ -3199,7 +3223,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_ATTR_INSTANCE_VALUE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3228,7 +3252,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_ATTR_WITH_HINT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3277,7 +3301,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_ATTR_SLOT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3313,7 +3337,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_ADAPTIVE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *right = TOP(); @@ -3330,7 +3354,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_FLOAT_JUMP) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); // Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false) _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; int when_to_jump_mask = cache->mask; @@ -3361,7 +3385,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_INT_JUMP) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); // Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false) _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; int when_to_jump_mask = cache->mask; @@ -3393,7 +3417,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_STR_JUMP) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); // Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false) _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; int invert = cache->mask; @@ -3842,7 +3866,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(FOR_ITER_ADAPTIVE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { next_instr--; @@ -3857,7 +3881,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(FOR_ITER_LIST) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyListIterObject *it = (_PyListIterObject *)TOP(); DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); @@ -3880,7 +3904,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(FOR_ITER_RANGE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); @@ -4025,7 +4049,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { /* Cached method object */ - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4051,7 +4075,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(LOAD_ATTR_METHOD_WITH_DICT) { /* Can be either a managed dict, or a tp_dictoffset offset.*/ - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4078,7 +4102,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_METHOD_NO_DICT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4097,7 +4121,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4135,9 +4159,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(KW_NAMES) { - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); - call_shape.kwnames = GETITEM(consts, oparg); + cframe.kwnames = GETITEM(consts, oparg); DISPATCH(); } @@ -4166,9 +4190,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int STACK_SHRINK(total_args); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, (PyFunctionObject *)function, locals, - stack_pointer, positional_args, call_shape.kwnames + stack_pointer, positional_args, cframe.kwnames ); - call_shape.kwnames = NULL; + cframe.kwnames = NULL; STACK_SHRINK(2-is_meth); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. @@ -4179,24 +4203,24 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - cframe.current_frame = frame = new_frame; + cframe.cframe.current_frame = frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } /* Callable is not a normal Python function */ PyObject *res; - if (cframe.use_tracing) { + if (cframe.cframe.use_tracing) { res = trace_call_function( tstate, function, stack_pointer-total_args, - positional_args, call_shape.kwnames); + positional_args, cframe.kwnames); } else { res = PyObject_Vectorcall( function, stack_pointer-total_args, positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - call_shape.kwnames); + cframe.kwnames); } - call_shape.kwnames = NULL; + cframe.kwnames = NULL; assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(function); /* Clear the stack */ @@ -4222,7 +4246,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int int nargs = oparg + is_meth; PyObject *callable = PEEK(nargs + 1); int err = _Py_Specialize_Call(callable, next_instr, nargs, - call_shape.kwnames); + cframe.kwnames); if (err < 0) { goto error; } @@ -4237,7 +4261,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(CALL_PY_EXACT_ARGS) { call_exact_args: - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); _PyCallCache *cache = (_PyCallCache *)next_instr; int is_meth = is_method(stack_pointer, oparg); @@ -4264,12 +4288,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.current_frame = new_frame; + frame = cframe.cframe.current_frame = new_frame; goto start_frame; } TARGET(CALL_PY_WITH_DEFAULTS) { - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); _PyCallCache *cache = (_PyCallCache *)next_instr; int is_meth = is_method(stack_pointer, oparg); @@ -4304,13 +4328,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.current_frame = new_frame; + frame = cframe.cframe.current_frame = new_frame; goto start_frame; } TARGET(CALL_NO_KW_TYPE_1) { - assert(call_shape.kwnames == NULL); - assert(cframe.use_tracing == 0); + assert(cframe.kwnames == NULL); + assert(cframe.cframe.use_tracing == 0); assert(oparg == 1); DEOPT_IF(is_method(stack_pointer, 1), CALL); PyObject *obj = TOP(); @@ -4327,8 +4351,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_STR_1) { - assert(call_shape.kwnames == NULL); - assert(cframe.use_tracing == 0); + assert(cframe.kwnames == NULL); + assert(cframe.cframe.use_tracing == 0); assert(oparg == 1); DEOPT_IF(is_method(stack_pointer, 1), CALL); PyObject *callable = PEEK(2); @@ -4349,7 +4373,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_TUPLE_1) { - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); assert(oparg == 1); DEOPT_IF(is_method(stack_pointer, 1), CALL); PyObject *callable = PEEK(2); @@ -4381,8 +4405,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); STACK_SHRINK(total_args); PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer, - total_args-kwnames_len, call_shape.kwnames); - call_shape.kwnames = NULL; + total_args-kwnames_len, cframe.kwnames); + cframe.kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { Py_DECREF(stack_pointer[i]); @@ -4398,9 +4422,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_BUILTIN_O) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); /* Builtin METH_O functions */ - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; DEOPT_IF(total_args != 1, CALL); @@ -4432,9 +4456,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_BUILTIN_FAST) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); /* Builtin METH_FASTCALL functions, without keywords */ - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; PyObject *callable = PEEK(total_args + 1); @@ -4472,7 +4496,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4491,10 +4515,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PyCFunction_GET_SELF(callable), stack_pointer, total_args - KWNAMES_LEN(), - call_shape.kwnames + cframe.kwnames ); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - call_shape.kwnames = NULL; + cframe.kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { @@ -4511,8 +4535,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_LEN) { - assert(cframe.use_tracing == 0); - assert(call_shape.kwnames == NULL); + assert(cframe.cframe.use_tracing == 0); + assert(cframe.kwnames == NULL); /* len(o) */ int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4541,8 +4565,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_ISINSTANCE) { - assert(cframe.use_tracing == 0); - assert(call_shape.kwnames == NULL); + assert(cframe.cframe.use_tracing == 0); + assert(cframe.kwnames == NULL); /* isinstance(o, o2) */ int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4574,8 +4598,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_LIST_APPEND) { - assert(cframe.use_tracing == 0); - assert(call_shape.kwnames == NULL); + assert(cframe.cframe.use_tracing == 0); + assert(cframe.kwnames == NULL); assert(oparg == 1); PyObject *callable = PEEK(3); PyInterpreterState *interp = _PyInterpreterState_GET(); @@ -4597,7 +4621,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) { - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; PyMethodDescrObject *callable = @@ -4650,9 +4674,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), - call_shape.kwnames); + cframe.kwnames); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - call_shape.kwnames = NULL; + cframe.kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < nargs; i++) { @@ -4670,7 +4694,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) { - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); assert(oparg == 0 || oparg == 1); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4704,7 +4728,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) { - assert(call_shape.kwnames == NULL); + assert(cframe.kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; PyMethodDescrObject *callable = @@ -4772,7 +4796,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } assert(PyTuple_CheckExact(callargs)); - result = do_call_core(tstate, func, callargs, kwargs, cframe.use_tracing); + result = do_call_core(tstate, func, callargs, kwargs, cframe.cframe.use_tracing); Py_DECREF(func); Py_DECREF(callargs); Py_XDECREF(kwargs); @@ -4833,11 +4857,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int gen->gi_frame_state = FRAME_CREATED; gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallTstate(tstate); - assert(frame != &py_frame); + assert(frame != &cframe.pyframe); _PyInterpreterFrame *prev = frame->previous; gen_frame->previous = NULL; _PyThreadState_PopFrame(tstate, frame); - frame = cframe.current_frame = prev; + frame = cframe.cframe.current_frame = prev; _PyFrame_StackPush(frame, (PyObject *)gen); goto resume_frame; } @@ -4947,7 +4971,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADAPTIVE) { - assert(cframe.use_tracing == 0); + assert(cframe.cframe.use_tracing == 0); _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *lhs = SECOND(); @@ -4981,7 +5005,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PRE_DISPATCH_GOTO(); // CPython hasn't traced the following instruction historically // (DO_TRACING would clobber our extended oparg anyways), so just - // skip our usual cframe.use_tracing check before dispatch. Also, + // skip our usual cframe.cframe.use_tracing check before dispatch. Also, // make sure the next instruction isn't a RESUME, since that needs // to trace properly (and shouldn't have an extended arg anyways): assert(opcode != RESUME); @@ -5005,7 +5029,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int case DO_TRACING: #endif { - assert(cframe.use_tracing); + assert(cframe.cframe.use_tracing); assert(tstate->tracing == 0); if (INSTR_OFFSET() >= frame->f_code->_co_firsttraceable) { int instr_prev = _PyInterpreterFrame_LASTI(frame); @@ -5025,7 +5049,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int maybe_dtrace_line(frame, &tstate->trace_info, instr_prev); } - if (cframe.use_tracing && + if (cframe.cframe.use_tracing && tstate->c_tracefunc != NULL && !tstate->tracing) { int err; /* see maybe_call_line_trace() @@ -5112,7 +5136,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } error: - call_shape.kwnames = NULL; + cframe.kwnames = NULL; /* Double-check exception status. */ #ifdef NDEBUG if (!_PyErr_Occurred(tstate)) { @@ -5124,7 +5148,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif /* Log traceback info. */ - assert(frame != &py_frame); + assert(frame != &cframe.pyframe); PyFrameObject *f = _PyFrame_GetFrameObject(frame); if (f != NULL) { PyTraceBack_Here(f); @@ -5195,13 +5219,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int exit_unwind: assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallTstate(tstate); - assert(frame != &py_frame); - frame = cframe.current_frame = pop_frame(tstate, frame); - if (frame == &py_frame) { + assert(frame != &cframe.pyframe); + frame = cframe.cframe.current_frame = pop_frame(tstate, frame); + if (frame == &cframe.pyframe) { assert(_PyFrame_IsIncomplete(frame)); /* Restore previous cframe and exit */ - tstate->cframe = cframe.previous; - tstate->cframe->use_tracing = cframe.use_tracing; + tstate->cframe = cframe.cframe.previous; + tstate->cframe->use_tracing = cframe.cframe.use_tracing; assert(tstate->cframe->current_frame == frame->previous); return NULL; } From 4dad69a1d7a090721706bbbc33ce685c743ef948 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 8 Sep 2022 15:56:01 +0100 Subject: [PATCH 09/29] Skip a test if ASAN is turned on. --- Lib/test/test_subprocess.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index f6854922a5b878..fbef5adaa9d5e4 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -25,6 +25,7 @@ import json import pathlib from test.support.os_helper import FakePath +from test.support import skip_if_sanitizer try: import _testcapi @@ -1875,6 +1876,8 @@ def bad_error(*args): @unittest.skipIf(not os.path.exists('/proc/self/status'), "need /proc/self/status") + @skip_if_sanitizer(memory=True, address=True, + reason= "Spurious error when assigning to stack variable.") def test_restore_signals(self): # Blindly assume that cat exists on systems with /proc/self/status... default_proc_status = subprocess.check_output( From 0db3b25d0d5a07965f865d804fa12a9b93843c40 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Sep 2022 10:28:20 +0100 Subject: [PATCH 10/29] Remove extra semicolons. --- Python/ceval.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 46f89eca25aaec..e706b96c81018c 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1007,11 +1007,11 @@ pop_frame(PyThreadState *tstate, _PyInterpreterFrame *frame) #endif typedef struct _eval_frame_data { - SENTINEL(0); + SENTINEL(0) _PyCFrame cframe; - SENTINEL(1); + SENTINEL(1) _PyInterpreterFrame pyframe; - SENTINEL(2); + SENTINEL(2) /* It is only between the KW_NAMES instruction and the following CALL, * that this has any meaning. */ @@ -1042,9 +1042,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif EvalFrameData cframe; - SET_SENTINEL(0); - SET_SENTINEL(1); - SET_SENTINEL(2); + SET_SENTINEL(0) + SET_SENTINEL(1) + SET_SENTINEL(2) cframe.kwnames = NULL; // Borrowed reference. Reset by CALL instructions. /* WARNING: Because the _PyCFrame lives on the C stack, @@ -1866,9 +1866,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(INTERPRETER_EXIT) { assert(frame == &cframe.pyframe); assert(_PyFrame_IsIncomplete(frame)); - CHECK_SENTINEL(0); - CHECK_SENTINEL(1); - CHECK_SENTINEL(2); + CHECK_SENTINEL(0) + CHECK_SENTINEL(1) + CHECK_SENTINEL(2) PyObject *retval = POP(); assert(EMPTY()); /* Restore previous cframe and return. */ From 05b4f68d264ac47994b23daa0b471956399ceb3d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Sep 2022 10:37:43 +0100 Subject: [PATCH 11/29] Add news --- .../2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst new file mode 100644 index 00000000000000..c66c921e95c577 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst @@ -0,0 +1,4 @@ +When calling into Python code from C code, though PyEval_EvalFrame or +related C-API function, a shim frame in inserted into the call stack. This +should be invisible to all Python and most C extensions, but out-of-process +debuggers, profilers and debuggers need to be aware of this. From 4015c72847c9851265880df6e2a13cbd3f472b7f Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 14 Sep 2022 16:23:37 +0100 Subject: [PATCH 12/29] Halve the number of writes for shim frame. --- Python/ceval.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 943f23ebb124be..d443a34f3107e4 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1053,11 +1053,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int /* Push frame */ cframe.pyframe.f_funcobj = Py_None; - cframe.pyframe.f_globals = NULL; - cframe.pyframe.f_builtins = NULL; - cframe.pyframe.f_locals = NULL; - cframe.pyframe.frame_obj = NULL; assert(tstate->interp->interpreter_trampoline != NULL); +#ifdef Py_DEBUG + cframe.pyframe.f_locals = (PyObject*)0xaaa1; + cframe.pyframe.frame_obj = (PyFrameObject*)0xaaa2; + cframe.pyframe.f_globals = (PyObject*)0xaaa3; + cframe.pyframe.f_builtins = (PyObject*)0xaaa4; +#endif cframe.pyframe.f_code = tstate->interp->interpreter_trampoline; _Py_CODEUNIT *code = _PyCode_CODE(tstate->interp->interpreter_trampoline); cframe.pyframe.prev_instr = code; From fb19e94edcf8b5b9e4d67c9a1d2390b89d7de86d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 14 Sep 2022 16:51:15 +0100 Subject: [PATCH 13/29] Remove one more write to shim frame. --- Include/internal/pycore_frame.h | 10 +++++----- Python/ceval.c | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 35414a236d44ad..b86430cfeb4c6e 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -48,12 +48,12 @@ enum _frameowner { typedef struct _PyInterpreterFrame { /* "Specials" section */ - PyObject *f_funcobj; /* Strong reference */ - PyObject *f_globals; /* Borrowed reference */ - PyObject *f_builtins; /* Borrowed reference */ - PyObject *f_locals; /* Strong reference, may be NULL */ + PyObject *f_funcobj; /* Strong reference. Only valid for Python frames */ + PyObject *f_globals; /* Borrowed reference. Only valid for Python frames */ + PyObject *f_builtins; /* Borrowed reference. Only valid for Python frames */ + PyObject *f_locals; /* Strong reference, may be NULL. Only valid for Python frames */ PyCodeObject *f_code; /* Strong reference */ - PyFrameObject *frame_obj; /* Strong reference, may be NULL */ + PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid for Python frames */ /* Linkage section */ struct _PyInterpreterFrame *previous; // NOTE: This is not necessarily the last instruction started in the given diff --git a/Python/ceval.c b/Python/ceval.c index d443a34f3107e4..efbc17144c8c90 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1051,10 +1051,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int cframe.cframe.previous = prev_cframe; tstate->cframe = &cframe.cframe; - /* Push frame */ - cframe.pyframe.f_funcobj = Py_None; assert(tstate->interp->interpreter_trampoline != NULL); #ifdef Py_DEBUG + cframe.pyframe.f_funcobj = (PyObject*)0xaaa0; cframe.pyframe.f_locals = (PyObject*)0xaaa1; cframe.pyframe.frame_obj = (PyFrameObject*)0xaaa2; cframe.pyframe.f_globals = (PyObject*)0xaaa3; @@ -1066,6 +1065,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int cframe.pyframe.stacktop = 0; cframe.pyframe.owner = FRAME_OWNED_BY_CSTACK; cframe.pyframe.yield_offset = 0; + /* Push frame */ cframe.pyframe.previous = prev_cframe->current_frame; frame->previous = &cframe.pyframe; cframe.cframe.current_frame = frame; From 95ef81b79a6b9a7349dd69601b87262059f46424 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 14 Sep 2022 16:58:00 +0100 Subject: [PATCH 14/29] Fix lltrace --- Python/ceval.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index efbc17144c8c90..9841164a6456fb 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -155,7 +155,10 @@ static void lltrace_resume_frame(_PyInterpreterFrame *frame) { PyObject *fobj = frame->f_funcobj; - if (fobj == NULL || !PyFunction_Check(fobj)) { + if (frame->owner == FRAME_OWNED_BY_CSTACK || + fobj == NULL || + !PyFunction_Check(fobj) + ) { printf("\nResuming frame."); return; } From aa8bd73fc1f8b7f404dae139a4a9951d22659919 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 19 Oct 2022 15:44:52 +0100 Subject: [PATCH 15/29] Remove first_instr local variable. --- Python/ceval.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index ce6f91d66df21e..16b3fcf9658341 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -737,13 +737,13 @@ GETITEM(PyObject *v, Py_ssize_t i) { /* Code access macros */ /* The integer overflow is checked by an assertion below. */ -#define INSTR_OFFSET() ((int)(next_instr - first_instr)) +#define INSTR_OFFSET() ((int)(next_instr - _PyCode_CODE(frame->f_code))) #define NEXTOPARG() do { \ _Py_CODEUNIT word = *next_instr; \ opcode = _Py_OPCODE(word); \ oparg = _Py_OPARG(word); \ } while (0) -#define JUMPTO(x) (next_instr = first_instr + (x)) +#define JUMPTO(x) (next_instr = _PyCode_CODE(frame->f_code) + (x)) #define JUMPBY(x) (next_instr += (x)) /* Get opcode and oparg from original instructions, not quickened form. */ @@ -1119,7 +1119,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PyObject *names; PyObject *consts; - _Py_CODEUNIT *first_instr; _Py_CODEUNIT *next_instr; PyObject **stack_pointer; @@ -1129,7 +1128,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PyCodeObject *co = frame->f_code; \ names = co->co_names; \ consts = co->co_consts; \ - first_instr = _PyCode_CODE(co); \ } \ assert(_PyInterpreterFrame_LASTI(frame) >= -1); \ /* Jump back to the last instruction executed... */ \ @@ -2143,7 +2141,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int if (oparg) { PyObject *lasti = PEEK(oparg + 1); if (PyLong_Check(lasti)) { - frame->prev_instr = first_instr + PyLong_AsLong(lasti); + frame->prev_instr = _PyCode_CODE(frame->f_code) + PyLong_AsLong(lasti); assert(!_PyErr_Occurred(tstate)); } else { From f7072e16cbf8f3625b4b5717e475497544eac597 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 19 Oct 2022 15:59:19 +0100 Subject: [PATCH 16/29] Add news and update frame_layout.md --- ...2-10-19-15-59-08.gh-issue-96421.e22y3r.rst | 4 ++++ Objects/frame_layout.md | 22 ++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst new file mode 100644 index 00000000000000..b2ef8b7b0e793b --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst @@ -0,0 +1,4 @@ +On entry to ``PyEval_EvalFrameDefault()`` an additional, internal, shim +frame is pushed. This simplifies the return and yield bytecode instructions. +Only debuggers and profiles that inspect the stack of frames should be +impacted. diff --git a/Objects/frame_layout.md b/Objects/frame_layout.md index 11688f68e42e3d..2f95214db56498 100644 --- a/Objects/frame_layout.md +++ b/Objects/frame_layout.md @@ -9,10 +9,10 @@ results in poor locality of reference. In 3.11, rather than have these frames scattered about memory, as happens for heap-allocated objects, frames are allocated -contiguously in a per-thread stack. +contiguously in a per-thread stack. This improves performance significantly for two reasons: * It reduces allocation overhead to a pointer comparison and increment. -* Stack allocated data has the best possible locality and will always be in +* Stack allocated data has the best possible locality and will always be in CPU cache. Generator and coroutines still need heap allocated activation records, but @@ -63,7 +63,7 @@ We may implement this in the future. > In a contiguous stack, we would need to save one fewer registers, as the > top of the caller's activation record would be the same at the base of the -> callee's. However, since some activation records are kept on the heap we +> callee's. However, since some activation records are kept on the heap we > cannot do this. ### Generators and Coroutines @@ -85,7 +85,7 @@ and builtins, than strong references to both globals and builtins. ### Frame objects When creating a backtrace or when calling `sys._getframe()` the frame becomes -visible to Python code. When this happens a new `PyFrameObject` is created +visible to Python code. When this happens a new `PyFrameObject` is created and a strong reference to it placed in the `frame_obj` field of the specials section. The `frame_obj` field is initially `NULL`. @@ -104,7 +104,7 @@ Generator objects have a `_PyInterpreterFrame` embedded in them. This means that creating a generator requires only a single allocation, reducing allocation overhead and improving locality of reference. The embedded frame is linked into the per-thread frame when iterated or -awaited. +awaited. If a frame object associated with a generator outlives the generator, then the embedded `_PyInterpreterFrame` is copied into the frame object. @@ -119,4 +119,14 @@ Thus, some of the field names may be a bit misleading. For example the `f_globals` field has a `f_` prefix implying it belongs to the `PyFrameObject` struct, although it belongs to the `_PyInterpreterFrame` struct. -We may rationalize this naming scheme for 3.12. \ No newline at end of file +We may rationalize this naming scheme for 3.12. + + +### Shim frames + +On entry to `_PyEval_EvalFrameDefault()` a shim `_PyInterpreterFrame` is pushed. +This frame is stored on the C stack, and popped when `_PyEval_EvalFrameDefault()` +returns. This extra frame is inserted so that `RETURN_VALUE`, `YIELD_VALUE`, and +`RETURN_GENERATOR` do not need to check whether the current frame is the entry frame. +The shim frame points to a special code object containing the `INTERPRETER_EXIT` +instruction which cleans up the shim frame and returns. From bd2b0eed3f10ecd527e1b8b6266ae6fc06f2a5a7 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Oct 2022 13:31:05 +0100 Subject: [PATCH 17/29] Remove debugging scaffolding and give C compiler more freedom to layout frame. --- Python/ceval.c | 301 ++++++++++++++++++++++--------------------------- 1 file changed, 136 insertions(+), 165 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 16b3fcf9658341..e7f3d5e91debcd 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -700,8 +700,8 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { \ NEXTOPARG(); \ PRE_DISPATCH_GOTO(); \ - assert(cframe.cframe.use_tracing == 0 || cframe.cframe.use_tracing == 255); \ - opcode |= cframe.cframe.use_tracing OR_DTRACE_LINE; \ + assert(cframe.use_tracing == 0 || cframe.use_tracing == 255); \ + opcode |= cframe.use_tracing OR_DTRACE_LINE; \ DISPATCH_GOTO(); \ } @@ -709,7 +709,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) { \ opcode = _Py_OPCODE(*next_instr); \ PRE_DISPATCH_GOTO(); \ - opcode |= cframe.cframe.use_tracing OR_DTRACE_LINE; \ + opcode |= cframe.use_tracing OR_DTRACE_LINE; \ DISPATCH_GOTO(); \ } @@ -786,7 +786,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { #define PREDICT(op) \ do { \ _Py_CODEUNIT word = *next_instr; \ - opcode = _Py_OPCODE(word) | cframe.cframe.use_tracing OR_DTRACE_LINE; \ + opcode = _Py_OPCODE(word) | cframe.use_tracing OR_DTRACE_LINE; \ if (opcode == op) { \ oparg = _Py_OPARG(word); \ INSTRUCTION_START(op); \ @@ -865,7 +865,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { /* Shared opcode macros */ #define TRACE_FUNCTION_EXIT() \ - if (cframe.cframe.use_tracing) { \ + if (cframe.use_tracing) { \ if (trace_function_exit(tstate, frame, retval)) { \ Py_DECREF(retval); \ goto exit_unwind; \ @@ -878,14 +878,14 @@ GETITEM(PyObject *v, Py_ssize_t i) { } #define TRACE_FUNCTION_UNWIND() \ - if (cframe.cframe.use_tracing) { \ + if (cframe.use_tracing) { \ /* Since we are already unwinding, \ * we don't care if this raises */ \ trace_function_exit(tstate, frame, NULL); \ } #define TRACE_FUNCTION_ENTRY() \ - if (cframe.cframe.use_tracing) { \ + if (cframe.use_tracing) { \ _PyFrame_SetStackPointer(frame, stack_pointer); \ int err = trace_function_entry(tstate, frame); \ stack_pointer = _PyFrame_GetStackPointer(frame); \ @@ -895,7 +895,7 @@ GETITEM(PyObject *v, Py_ssize_t i) { } #define TRACE_FUNCTION_THROW_ENTRY() \ - if (cframe.cframe.use_tracing) { \ + if (cframe.use_tracing) { \ assert(frame->stacktop >= 0); \ if (trace_function_entry(tstate, frame)) { \ goto exit_unwind; \ @@ -1015,31 +1015,7 @@ static inline void _Py_LeaveRecursiveCallPy(PyThreadState *tstate) { #define is_method(stack_pointer, args) (PEEK((args)+2) != NULL) #define KWNAMES_LEN() \ - (cframe.kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(cframe.kwnames))) - -#ifdef Py_DEBUG -#define SENTINEL(N) char sentinel_##N[8]; -#define CHECK_SENTINEL(N) \ - assert(strncmp(cframe.sentinel_##N, "Sentinel", 8) == 0); -#define SET_SENTINEL(N) \ - memcpy(cframe.sentinel_##N, "Sentinel", 8); -#else -#define SENTINEL(N) -#define CHECK_SENTINEL(N) -#define SET_SENTINEL(N) -#endif - -typedef struct _eval_frame_data { - SENTINEL(0) - _PyCFrame cframe; - SENTINEL(1) - _PyInterpreterFrame pyframe; - SENTINEL(2) -/* It is only between the KW_NAMES instruction and the following CALL, - * that this has any meaning. - */ - PyObject *kwnames; -} EvalFrameData; + (kwnames == NULL ? 0 : ((int)PyTuple_GET_SIZE(kwnames))) PyObject* _Py_HOT_FUNCTION _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag) @@ -1064,39 +1040,37 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int int lltrace = 0; #endif - EvalFrameData cframe; - SET_SENTINEL(0) - SET_SENTINEL(1) - SET_SENTINEL(2) - cframe.kwnames = NULL; // Borrowed reference. Reset by CALL instructions. + _PyCFrame cframe; + _PyInterpreterFrame pyframe; + PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions. /* WARNING: Because the _PyCFrame lives on the C stack, * but can be accessed from a heap allocated object (tstate) * strict stack discipline must be maintained. */ _PyCFrame *prev_cframe = tstate->cframe; - cframe.cframe.use_tracing = prev_cframe->use_tracing; - cframe.cframe.previous = prev_cframe; - tstate->cframe = &cframe.cframe; + cframe.use_tracing = prev_cframe->use_tracing; + cframe.previous = prev_cframe; + tstate->cframe = &cframe; assert(tstate->interp->interpreter_trampoline != NULL); #ifdef Py_DEBUG - cframe.pyframe.f_funcobj = (PyObject*)0xaaa0; - cframe.pyframe.f_locals = (PyObject*)0xaaa1; - cframe.pyframe.frame_obj = (PyFrameObject*)0xaaa2; - cframe.pyframe.f_globals = (PyObject*)0xaaa3; - cframe.pyframe.f_builtins = (PyObject*)0xaaa4; + pyframe.f_funcobj = (PyObject*)0xaaa0; + pyframe.f_locals = (PyObject*)0xaaa1; + pyframe.frame_obj = (PyFrameObject*)0xaaa2; + pyframe.f_globals = (PyObject*)0xaaa3; + pyframe.f_builtins = (PyObject*)0xaaa4; #endif - cframe.pyframe.f_code = tstate->interp->interpreter_trampoline; + pyframe.f_code = tstate->interp->interpreter_trampoline; _Py_CODEUNIT *code = _PyCode_CODE(tstate->interp->interpreter_trampoline); - cframe.pyframe.prev_instr = code; - cframe.pyframe.stacktop = 0; - cframe.pyframe.owner = FRAME_OWNED_BY_CSTACK; - cframe.pyframe.yield_offset = 0; + pyframe.prev_instr = code; + pyframe.stacktop = 0; + pyframe.owner = FRAME_OWNED_BY_CSTACK; + pyframe.yield_offset = 0; /* Push frame */ - cframe.pyframe.previous = prev_cframe->current_frame; - frame->previous = &cframe.pyframe; - cframe.cframe.current_frame = frame; + pyframe.previous = prev_cframe->current_frame; + frame->previous = &pyframe; + cframe.current_frame = frame; if (_Py_EnterRecursiveCallTstate(tstate, "")) { tstate->c_recursion_remaining--; @@ -1152,7 +1126,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef LLTRACE { - if (frame != &cframe.pyframe) { + if (frame != &pyframe) { int r = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__)); if (r < 0) { goto exit_unwind; @@ -1209,8 +1183,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(RESUME_QUICK) { PREDICTED(RESUME_QUICK); - assert(tstate->cframe == &cframe.cframe); - assert(frame == cframe.cframe.current_frame); + assert(tstate->cframe == &cframe); + assert(frame == cframe.current_frame); if (_Py_atomic_load_relaxed_int32(eval_breaker) && oparg < 2) { goto handle_eval_breaker; } @@ -1383,7 +1357,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_MULTIPLY_INT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -1402,7 +1376,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_MULTIPLY_FLOAT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -1423,7 +1397,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_SUBTRACT_INT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -1442,7 +1416,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_SUBTRACT_FLOAT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -1462,7 +1436,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADD_UNICODE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); @@ -1481,7 +1455,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_INPLACE_ADD_UNICODE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyUnicode_CheckExact(left), BINARY_OP); @@ -1517,7 +1491,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADD_FLOAT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyFloat_CheckExact(left), BINARY_OP); @@ -1538,7 +1512,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADD_INT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *left = SECOND(); PyObject *right = TOP(); DEOPT_IF(!PyLong_CheckExact(left), BINARY_OP); @@ -1629,7 +1603,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_SUBSCR_LIST_INT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *list = SECOND(); DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); @@ -1654,7 +1628,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_SUBSCR_TUPLE_INT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *tuple = SECOND(); DEOPT_IF(!PyLong_CheckExact(sub), BINARY_SUBSCR); @@ -1679,7 +1653,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_SUBSCR_DICT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *dict = SECOND(); DEOPT_IF(!PyDict_CheckExact(SECOND()), BINARY_SUBSCR); STAT_INC(BINARY_SUBSCR, hit); @@ -1728,7 +1702,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_BINARY_SUBSCR); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.cframe.current_frame = new_frame; + frame = cframe.current_frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } @@ -1792,7 +1766,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_SUBSCR_LIST_INT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *list = SECOND(); PyObject *value = THIRD(); @@ -1818,7 +1792,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_SUBSCR_DICT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *sub = TOP(); PyObject *dict = SECOND(); PyObject *value = THIRD(); @@ -1889,16 +1863,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(INTERPRETER_EXIT) { - assert(frame == &cframe.pyframe); + assert(frame == &pyframe); assert(_PyFrame_IsIncomplete(frame)); - CHECK_SENTINEL(0) - CHECK_SENTINEL(1) - CHECK_SENTINEL(2) PyObject *retval = POP(); assert(EMPTY()); /* Restore previous cframe and return. */ - tstate->cframe = cframe.cframe.previous; - tstate->cframe->use_tracing = cframe.cframe.use_tracing; + tstate->cframe = cframe.previous; + tstate->cframe->use_tracing = cframe.use_tracing; assert(tstate->cframe->current_frame == frame->previous); assert(!_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallTstate(tstate); @@ -1912,8 +1883,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &cframe.pyframe); - frame = cframe.cframe.current_frame = pop_frame(tstate, frame); + assert(frame != &pyframe); + frame = cframe.current_frame = pop_frame(tstate, frame); _PyFrame_StackPush(frame, retval); goto resume_frame; } @@ -2049,7 +2020,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(SEND) { - assert(frame != &cframe.pyframe); + assert(frame != &pyframe); assert(STACK_LEVEL() >= 2); PyObject *v = POP(); PyObject *receiver = TOP(); @@ -2114,7 +2085,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // The compiler treats any exception raised here as a failed close() // or throw() call.R assert(oparg == STACK_LEVEL()); - assert(frame != &cframe.pyframe); + assert(frame != &pyframe); PyObject *retval = POP(); _PyFrame_GetGenerator(frame)->gi_frame_state = FRAME_SUSPENDED; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -2122,7 +2093,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallPy(tstate); _PyInterpreterFrame *gen_frame = frame; - frame = cframe.cframe.current_frame = gen_frame->previous; + frame = cframe.current_frame = gen_frame->previous; gen_frame->previous = NULL; frame->prev_instr += frame->yield_offset; _PyFrame_StackPush(frame, retval); @@ -2300,7 +2271,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(UNPACK_SEQUENCE_ADAPTIVE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *seq = TOP(); @@ -2541,7 +2512,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_GLOBAL_ADAPTIVE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *name = GETITEM(names, oparg>>1); @@ -2559,7 +2530,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_GLOBAL_MODULE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); PyDictObject *dict = (PyDictObject *)GLOBALS(); _PyLoadGlobalCache *cache = (_PyLoadGlobalCache *)next_instr; @@ -2580,7 +2551,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_GLOBAL_BUILTIN) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); DEOPT_IF(!PyDict_CheckExact(GLOBALS()), LOAD_GLOBAL); DEOPT_IF(!PyDict_CheckExact(BUILTINS()), LOAD_GLOBAL); PyDictObject *mdict = (PyDictObject *)GLOBALS(); @@ -2998,7 +2969,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_ADAPTIVE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *owner = TOP(); @@ -3017,7 +2988,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_INSTANCE_VALUE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); @@ -3042,7 +3013,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_MODULE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3067,7 +3038,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_WITH_HINT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); @@ -3106,7 +3077,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_SLOT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyObject *res; PyTypeObject *tp = Py_TYPE(owner); @@ -3128,7 +3099,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_CLASS) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; PyObject *cls = TOP(); @@ -3151,7 +3122,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_PROPERTY) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -3183,13 +3154,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.cframe.current_frame = new_frame; + frame = cframe.current_frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } TARGET(LOAD_ATTR_GETATTRIBUTE_OVERRIDDEN) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); DEOPT_IF(tstate->interp->eval_frame, LOAD_ATTR); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; PyObject *owner = TOP(); @@ -3224,13 +3195,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.cframe.current_frame = new_frame; + frame = cframe.current_frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } TARGET(STORE_ATTR_ADAPTIVE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyAttrCache *cache = (_PyAttrCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *owner = TOP(); @@ -3249,7 +3220,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_ATTR_INSTANCE_VALUE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3278,7 +3249,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_ATTR_WITH_HINT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3330,7 +3301,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(STORE_ATTR_SLOT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *owner = TOP(); PyTypeObject *tp = Py_TYPE(owner); _PyAttrCache *cache = (_PyAttrCache *)next_instr; @@ -3366,7 +3337,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_ADAPTIVE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *right = TOP(); @@ -3383,7 +3354,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_FLOAT_JUMP) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (float ? float) + POP_JUMP_IF_(true/false) _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; int when_to_jump_mask = cache->mask; @@ -3414,7 +3385,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_INT_JUMP) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (int ? int) + POP_JUMP_IF_(true/false) _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; int when_to_jump_mask = cache->mask; @@ -3446,7 +3417,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(COMPARE_OP_STR_JUMP) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); // Combined: COMPARE_OP (str == str or str != str) + POP_JUMP_IF_(true/false) _PyCompareOpCache *cache = (_PyCompareOpCache *)next_instr; int invert = cache->mask; @@ -3895,7 +3866,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(FOR_ITER_ADAPTIVE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyForIterCache *cache = (_PyForIterCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { next_instr--; @@ -3910,7 +3881,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(FOR_ITER_LIST) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyListIterObject *it = (_PyListIterObject *)TOP(); DEOPT_IF(Py_TYPE(it) != &PyListIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); @@ -3933,7 +3904,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(FOR_ITER_RANGE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyRangeIterObject *r = (_PyRangeIterObject *)TOP(); DEOPT_IF(Py_TYPE(r) != &PyRangeIter_Type, FOR_ITER); STAT_INC(FOR_ITER, hit); @@ -4078,7 +4049,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(LOAD_ATTR_METHOD_WITH_VALUES) { /* Cached method object */ - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4104,7 +4075,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(LOAD_ATTR_METHOD_WITH_DICT) { /* Can be either a managed dict, or a tp_dictoffset offset.*/ - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4131,7 +4102,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_METHOD_NO_DICT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4150,7 +4121,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(LOAD_ATTR_METHOD_LAZY_DICT) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); PyObject *self = TOP(); PyTypeObject *self_cls = Py_TYPE(self); _PyLoadMethodCache *cache = (_PyLoadMethodCache *)next_instr; @@ -4188,9 +4159,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(KW_NAMES) { - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); assert(oparg < PyTuple_GET_SIZE(consts)); - cframe.kwnames = GETITEM(consts, oparg); + kwnames = GETITEM(consts, oparg); DISPATCH(); } @@ -4222,9 +4193,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int STACK_SHRINK(total_args); _PyInterpreterFrame *new_frame = _PyEvalFramePushAndInit( tstate, (PyFunctionObject *)function, locals, - stack_pointer, positional_args, cframe.kwnames + stack_pointer, positional_args, kwnames ); - cframe.kwnames = NULL; + kwnames = NULL; STACK_SHRINK(2-is_meth); // The frame has stolen all the arguments from the stack, // so there is no need to clean them up. @@ -4235,24 +4206,24 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - cframe.cframe.current_frame = frame = new_frame; + cframe.current_frame = frame = new_frame; CALL_STAT_INC(inlined_py_calls); goto start_frame; } /* Callable is not a normal Python function */ PyObject *res; - if (cframe.cframe.use_tracing) { + if (cframe.use_tracing) { res = trace_call_function( tstate, function, stack_pointer-total_args, - positional_args, cframe.kwnames); + positional_args, kwnames); } else { res = PyObject_Vectorcall( function, stack_pointer-total_args, positional_args | PY_VECTORCALL_ARGUMENTS_OFFSET, - cframe.kwnames); + kwnames); } - cframe.kwnames = NULL; + kwnames = NULL; assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); Py_DECREF(function); /* Clear the stack */ @@ -4278,7 +4249,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int int nargs = oparg + is_meth; PyObject *callable = PEEK(nargs + 1); int err = _Py_Specialize_Call(callable, next_instr, nargs, - cframe.kwnames); + kwnames); if (err < 0) { goto error; } @@ -4293,7 +4264,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(CALL_PY_EXACT_ARGS) { PREDICTED(CALL_PY_EXACT_ARGS); - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); _PyCallCache *cache = (_PyCallCache *)next_instr; int is_meth = is_method(stack_pointer, oparg); @@ -4320,12 +4291,12 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.cframe.current_frame = new_frame; + frame = cframe.current_frame = new_frame; goto start_frame; } TARGET(CALL_PY_WITH_DEFAULTS) { - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); DEOPT_IF(tstate->interp->eval_frame, CALL); _PyCallCache *cache = (_PyCallCache *)next_instr; int is_meth = is_method(stack_pointer, oparg); @@ -4360,13 +4331,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); frame->prev_instr = next_instr - 1; new_frame->previous = frame; - frame = cframe.cframe.current_frame = new_frame; + frame = cframe.current_frame = new_frame; goto start_frame; } TARGET(CALL_NO_KW_TYPE_1) { - assert(cframe.kwnames == NULL); - assert(cframe.cframe.use_tracing == 0); + assert(kwnames == NULL); + assert(cframe.use_tracing == 0); assert(oparg == 1); DEOPT_IF(is_method(stack_pointer, 1), CALL); PyObject *obj = TOP(); @@ -4383,8 +4354,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_STR_1) { - assert(cframe.kwnames == NULL); - assert(cframe.cframe.use_tracing == 0); + assert(kwnames == NULL); + assert(cframe.use_tracing == 0); assert(oparg == 1); DEOPT_IF(is_method(stack_pointer, 1), CALL); PyObject *callable = PEEK(2); @@ -4405,7 +4376,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_TUPLE_1) { - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); assert(oparg == 1); DEOPT_IF(is_method(stack_pointer, 1), CALL); PyObject *callable = PEEK(2); @@ -4437,8 +4408,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int JUMPBY(INLINE_CACHE_ENTRIES_CALL); STACK_SHRINK(total_args); PyObject *res = tp->tp_vectorcall((PyObject *)tp, stack_pointer, - total_args-kwnames_len, cframe.kwnames); - cframe.kwnames = NULL; + total_args-kwnames_len, kwnames); + kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { Py_DECREF(stack_pointer[i]); @@ -4454,9 +4425,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_BUILTIN_O) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); /* Builtin METH_O functions */ - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; DEOPT_IF(total_args != 1, CALL); @@ -4488,9 +4459,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_BUILTIN_FAST) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL functions, without keywords */ - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; PyObject *callable = PEEK(total_args + 1); @@ -4528,7 +4499,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_BUILTIN_FAST_WITH_KEYWORDS) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); /* Builtin METH_FASTCALL | METH_KEYWORDS functions */ int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4547,10 +4518,10 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PyCFunction_GET_SELF(callable), stack_pointer, total_args - KWNAMES_LEN(), - cframe.kwnames + kwnames ); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - cframe.kwnames = NULL; + kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < total_args; i++) { @@ -4567,8 +4538,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_LEN) { - assert(cframe.cframe.use_tracing == 0); - assert(cframe.kwnames == NULL); + assert(cframe.use_tracing == 0); + assert(kwnames == NULL); /* len(o) */ int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4597,8 +4568,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_ISINSTANCE) { - assert(cframe.cframe.use_tracing == 0); - assert(cframe.kwnames == NULL); + assert(cframe.use_tracing == 0); + assert(kwnames == NULL); /* isinstance(o, o2) */ int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4630,8 +4601,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_LIST_APPEND) { - assert(cframe.cframe.use_tracing == 0); - assert(cframe.kwnames == NULL); + assert(cframe.use_tracing == 0); + assert(kwnames == NULL); assert(oparg == 1); PyObject *callable = PEEK(3); PyInterpreterState *interp = _PyInterpreterState_GET(); @@ -4653,7 +4624,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_O) { - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; PyMethodDescrObject *callable = @@ -4706,9 +4677,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyCFunctionFastWithKeywords cfunc = (_PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth; PyObject *res = cfunc(self, stack_pointer, nargs - KWNAMES_LEN(), - cframe.kwnames); + kwnames); assert((res != NULL) ^ (_PyErr_Occurred(tstate) != NULL)); - cframe.kwnames = NULL; + kwnames = NULL; /* Free the arguments. */ for (int i = 0; i < nargs; i++) { @@ -4726,7 +4697,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_NOARGS) { - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); assert(oparg == 0 || oparg == 1); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; @@ -4760,7 +4731,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(CALL_NO_KW_METHOD_DESCRIPTOR_FAST) { - assert(cframe.kwnames == NULL); + assert(kwnames == NULL); int is_meth = is_method(stack_pointer, oparg); int total_args = oparg + is_meth; PyMethodDescrObject *callable = @@ -4817,7 +4788,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } assert(PyTuple_CheckExact(callargs)); - result = do_call_core(tstate, func, callargs, kwargs, cframe.cframe.use_tracing); + result = do_call_core(tstate, func, callargs, kwargs, cframe.use_tracing); Py_DECREF(func); Py_DECREF(callargs); Py_XDECREF(kwargs); @@ -4878,11 +4849,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int gen->gi_frame_state = FRAME_CREATED; gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &cframe.pyframe); + assert(frame != &pyframe); _PyInterpreterFrame *prev = frame->previous; gen_frame->previous = NULL; _PyThreadState_PopFrame(tstate, frame); - frame = cframe.cframe.current_frame = prev; + frame = cframe.current_frame = prev; _PyFrame_StackPush(frame, (PyObject *)gen); goto resume_frame; } @@ -4992,7 +4963,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(BINARY_OP_ADAPTIVE) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); _PyBinaryOpCache *cache = (_PyBinaryOpCache *)next_instr; if (ADAPTIVE_COUNTER_IS_ZERO(cache)) { PyObject *lhs = SECOND(); @@ -5026,7 +4997,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PRE_DISPATCH_GOTO(); // CPython hasn't traced the following instruction historically // (DO_TRACING would clobber our extended oparg anyways), so just - // skip our usual cframe.cframe.use_tracing check before dispatch. Also, + // skip our usual cframe.use_tracing check before dispatch. Also, // make sure the next instruction isn't a RESUME, since that needs // to trace properly (and shouldn't have an extended arg anyways): assert(opcode != RESUME); @@ -5034,7 +5005,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(EXTENDED_ARG_QUICK) { - assert(cframe.cframe.use_tracing == 0); + assert(cframe.use_tracing == 0); assert(oparg); int oldoparg = oparg; NEXTOPARG(); @@ -5052,7 +5023,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int case DO_TRACING: #endif { - assert(cframe.cframe.use_tracing); + assert(cframe.use_tracing); assert(tstate->tracing == 0); if (INSTR_OFFSET() >= frame->f_code->_co_firsttraceable) { int instr_prev = _PyInterpreterFrame_LASTI(frame); @@ -5072,7 +5043,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int maybe_dtrace_line(frame, &tstate->trace_info, instr_prev); } - if (cframe.cframe.use_tracing && + if (cframe.use_tracing && tstate->c_tracefunc != NULL && !tstate->tracing) { int err; /* see maybe_call_line_trace() @@ -5161,7 +5132,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } error: - cframe.kwnames = NULL; + kwnames = NULL; /* Double-check exception status. */ #ifdef NDEBUG if (!_PyErr_Occurred(tstate)) { @@ -5173,7 +5144,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif /* Log traceback info. */ - assert(frame != &cframe.pyframe); + assert(frame != &pyframe); if (!_PyFrame_IsIncomplete(frame)) { PyFrameObject *f = _PyFrame_GetFrameObject(frame); if (f != NULL) { @@ -5246,13 +5217,13 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int exit_unwind: assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &cframe.pyframe); - frame = cframe.cframe.current_frame = pop_frame(tstate, frame); - if (frame == &cframe.pyframe) { + assert(frame != &pyframe); + frame = cframe.current_frame = pop_frame(tstate, frame); + if (frame == &pyframe) { assert(_PyFrame_IsIncomplete(frame)); /* Restore previous cframe and exit */ - tstate->cframe = cframe.cframe.previous; - tstate->cframe->use_tracing = cframe.cframe.use_tracing; + tstate->cframe = cframe.previous; + tstate->cframe->use_tracing = cframe.use_tracing; assert(tstate->cframe->current_frame == frame->previous); _Py_LeaveRecursiveCallTstate(tstate); return NULL; From 10b03c8688109db4bee30e11fcbb6c66e2048dc2 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 20 Oct 2022 13:51:46 +0100 Subject: [PATCH 18/29] Remove typo --- Python/ceval.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/ceval.c b/Python/ceval.c index e7f3d5e91debcd..b235814c98b78b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2083,7 +2083,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(YIELD_VALUE) { // NOTE: It's important that YIELD_VALUE never raises an exception! // The compiler treats any exception raised here as a failed close() - // or throw() call.R + // or throw() call. assert(oparg == STACK_LEVEL()); assert(frame != &pyframe); PyObject *retval = POP(); From b62f5f3506b86c4ccb555ad76def3823d79feb3c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 11:19:13 +0100 Subject: [PATCH 19/29] Address code review comments. --- Include/internal/pycore_code.h | 2 +- Include/internal/pycore_frame.h | 13 ++++++------- ...22-09-09-10-37-34.gh-issue-96421.cyg33z.rst | 4 ---- ...22-10-19-15-59-08.gh-issue-96421.e22y3r.rst | 18 ++++++++++++++---- Objects/codeobject.c | 2 +- Python/ceval.c | 2 -- Python/frame.c | 3 --- Python/pylifecycle.c | 11 ++++++++--- Python/traceback.c | 2 ++ Tools/gdb/libpython.py | 12 +++++++----- 10 files changed, 39 insertions(+), 30 deletions(-) delete mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index fd33fd2d02f86e..302fd64f2771d5 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -456,7 +456,7 @@ _PyCode_LineNumberFromArray(PyCodeObject *co, int index) } extern PyCodeObject * -_Py_MakeTrampoline(const char *code, int codelen, int stacksize, const char *cname); +_Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char *cname); #ifdef __cplusplus diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index b86430cfeb4c6e..2b0f161747e75f 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -48,12 +48,12 @@ enum _frameowner { typedef struct _PyInterpreterFrame { /* "Specials" section */ - PyObject *f_funcobj; /* Strong reference. Only valid for Python frames */ - PyObject *f_globals; /* Borrowed reference. Only valid for Python frames */ - PyObject *f_builtins; /* Borrowed reference. Only valid for Python frames */ - PyObject *f_locals; /* Strong reference, may be NULL. Only valid for Python frames */ + PyObject *f_funcobj; /* Strong reference. Only valid if not on C stack */ + PyObject *f_globals; /* Borrowed reference. Only valid if not on C stack */ + PyObject *f_builtins; /* Borrowed reference. Only valid if not on C stack */ + PyObject *f_locals; /* Strong reference, may be NULL. Only valid if not on C stack */ PyCodeObject *f_code; /* Strong reference */ - PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid for Python frames */ + PyFrameObject *frame_obj; /* Strong reference, may be NULL. Only valid if not on C stack */ /* Linkage section */ struct _PyInterpreterFrame *previous; // NOTE: This is not necessarily the last instruction started in the given @@ -62,8 +62,7 @@ typedef struct _PyInterpreterFrame { // over, or (in the case of a newly-created frame) a totally invalid value: _Py_CODEUNIT *prev_instr; int stacktop; /* Offset of TOS from localsplus */ - int yield_offset: 28; - int owner: 4; + char owner; /* Locals and stack */ PyObject *localsplus[1]; } _PyInterpreterFrame; diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst deleted file mode 100644 index c66c921e95c577..00000000000000 --- a/Misc/NEWS.d/next/Core and Builtins/2022-09-09-10-37-34.gh-issue-96421.cyg33z.rst +++ /dev/null @@ -1,4 +0,0 @@ -When calling into Python code from C code, though PyEval_EvalFrame or -related C-API function, a shim frame in inserted into the call stack. This -should be invisible to all Python and most C extensions, but out-of-process -debuggers, profilers and debuggers need to be aware of this. diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst index b2ef8b7b0e793b..21160f00d309b0 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst @@ -1,4 +1,14 @@ -On entry to ``PyEval_EvalFrameDefault()`` an additional, internal, shim -frame is pushed. This simplifies the return and yield bytecode instructions. -Only debuggers and profiles that inspect the stack of frames should be -impacted. +When calling into Python code from C code, though ``PyEval_EvalFrame`` or +related C-API function, a shim frame in inserted into the call stack. +This occurs in the ``_PyEval_EvalFrameDefault()`` function. +The extra frame should be invisible to all Python and most C extensions, +but out-of-process debuggers, profilers and debuggers need to be aware of +it. +These shim frames can be detected by checking +``frame->owner == FRAME_OWNED_BY_CSTACK``. + +Extensions implementing their own interpreters using PEP 523 need to be +aware of this shim frame and the changes to the semantics of +``RETURN_VALUE``, ``YIELD_VALUE``, and ``RETURN_GENERATOR``, +which now clear the frame. + diff --git a/Objects/codeobject.c b/Objects/codeobject.c index b969733c039869..df801c1d281f8f 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2270,7 +2270,7 @@ _PyStaticCode_InternStrings(PyCodeObject *co) } PyCodeObject * -_Py_MakeTrampoline(const char *code, int codelen, int stacksize, const char *cname) +_Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char *cname) { PyObject *name = NULL; PyObject *co_code = NULL; diff --git a/Python/ceval.c b/Python/ceval.c index b235814c98b78b..678a41b42c61e0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1066,7 +1066,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int pyframe.prev_instr = code; pyframe.stacktop = 0; pyframe.owner = FRAME_OWNED_BY_CSTACK; - pyframe.yield_offset = 0; /* Push frame */ pyframe.previous = prev_cframe->current_frame; frame->previous = &pyframe; @@ -2095,7 +2094,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyInterpreterFrame *gen_frame = frame; frame = cframe.current_frame = gen_frame->previous; gen_frame->previous = NULL; - frame->prev_instr += frame->yield_offset; _PyFrame_StackPush(frame, retval); goto resume_frame; } diff --git a/Python/frame.c b/Python/frame.c index d6bf020764f3e0..52f6ef428291c5 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -127,7 +127,6 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) * to have cleared the enclosing generator, if any. */ assert(frame->owner != FRAME_OWNED_BY_GENERATOR || _PyFrame_GetGenerator(frame)->gi_frame_state == FRAME_CLEARED); - assert(frame->owner != FRAME_CLEARED); if (frame->frame_obj) { PyFrameObject *f = frame->frame_obj; frame->frame_obj = NULL; @@ -142,12 +141,10 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) for (int i = 0; i < frame->stacktop; i++) { Py_XDECREF(frame->localsplus[i]); } - frame->stacktop = 0; Py_XDECREF(frame->frame_obj); Py_XDECREF(frame->f_locals); Py_DECREF(frame->f_funcobj); Py_DECREF(frame->f_code); - frame->owner = FRAME_CLEARED; } int diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index cc22caa493bcd0..8991464573006d 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -752,9 +752,12 @@ pycore_init_types(PyInterpreterState *interp) return _PyStatus_OK(); } -static const char INTERPRETER_TRAMPOLINE_CODE[] = { - 0, 0, +static const uint8_t INTERPRETER_TRAMPOLINE_CODE[] = { + /* Put a NOP at the start, so that the IP points into + * the code, rather than before it */ + NOP, 0, INTERPRETER_EXIT, 0, + /* RESUME at end makes sure that the frame appears incomplete */ RESUME, 0 }; @@ -792,7 +795,9 @@ pycore_init_builtins(PyThreadState *tstate) assert(object__getattribute__); interp->callable_cache.object__getattribute__ = object__getattribute__; interp->interpreter_trampoline = _Py_MakeTrampoline( - INTERPRETER_TRAMPOLINE_CODE, sizeof(INTERPRETER_TRAMPOLINE_CODE), 1, ""); + INTERPRETER_TRAMPOLINE_CODE, + sizeof(INTERPRETER_TRAMPOLINE_CODE), + 1, ""); if (interp->interpreter_trampoline == NULL) { return _PyStatus_ERR("failed to create interpreter trampoline."); } diff --git a/Python/traceback.c b/Python/traceback.c index aeb4124a8bc2e7..ae14cf061bb1a1 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -1236,6 +1236,8 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header) if (frame == NULL) { break; } + /* Can't have more than one shim frame in a row */ + assert(frame->owner != FRAME_OWNED_BY_CSTACK); depth++; } } diff --git a/Tools/gdb/libpython.py b/Tools/gdb/libpython.py index e7b22d12cfad24..c003c1ab4a23bd 100755 --- a/Tools/gdb/libpython.py +++ b/Tools/gdb/libpython.py @@ -82,6 +82,8 @@ def _sizeof_void_p(): Py_TPFLAGS_BASE_EXC_SUBCLASS = (1 << 30) Py_TPFLAGS_TYPE_SUBCLASS = (1 << 31) +#From pycore_frame.h +FRAME_OWNED_BY_CSTACK = 3 MAX_OUTPUT_LEN=1024 @@ -1077,8 +1079,8 @@ def _f_lasti(self): first_instr = self._f_code().field("co_code_adaptive").cast(codeunit_p) return int(prev_instr - first_instr) - def is_entry(self): - return self._f_special("owner", int) == 3 + def is_shim(self): + return self._f_special("owner", int) == FRAME_OWNED_BY_CSTACK def previous(self): return self._f_special("previous", PyFramePtr) @@ -1821,7 +1823,7 @@ def print_summary(self): interp_frame = self.get_pyop() while True: if interp_frame: - if interp_frame.is_entry(): + if interp_frame.is_shim(): break line = interp_frame.get_truncated_repr(MAX_OUTPUT_LEN) sys.stdout.write('#%i %s\n' % (self.get_index(), line)) @@ -1845,7 +1847,7 @@ def print_traceback(self): interp_frame = self.get_pyop() while True: if interp_frame: - if interp_frame.is_entry(): + if interp_frame.is_shim(): break interp_frame.print_traceback() if not interp_frame.is_optimized_out(): @@ -2106,7 +2108,7 @@ def invoke(self, args, from_tty): while True: if not pyop_frame: print(UNABLE_READ_INFO_PYTHON_FRAME) - if pyop_frame.is_entry(): + if pyop_frame.is_shim(): break sys.stdout.write('Locals for %s\n' % (pyop_frame.co_name.proxyval(set()))) From 1a0b08f891428aa1e1724cad22f07aa146a6e819 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 11:40:16 +0100 Subject: [PATCH 20/29] Generate linetable when creating shim code. --- Objects/codeobject.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index df801c1d281f8f..e64f41e6a42c1f 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2281,12 +2281,26 @@ _Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char * if (name == NULL) { goto cleanup; } - co_code = PyBytes_FromStringAndSize(code, codelen); + co_code = PyBytes_FromStringAndSize((const char *)code, codelen); if (co_code == NULL) { goto cleanup; } - const char loc[2] = { 0x80 | (PY_CODE_LOCATION_INFO_NO_COLUMNS << 3) | 3, 0 }; - lines = PyBytes_FromStringAndSize(loc, 2); + int code_units = codelen/2; + int loc_entries = (code_units + 7)/8; + uint8_t *loc_table = PyMem_Malloc(loc_entries); + if (loc_table == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (int i = 0; i < loc_entries-1; i++) { + loc_table[i] = 0x80 | (PY_CODE_LOCATION_INFO_NONE << 3) | 7; + code_units -= 8; + } + assert(loc_entries > 0); + assert(code_units > 0 && code_units <= 8); + loc_table[loc_entries-1] = 0x80 | + (PY_CODE_LOCATION_INFO_NONE << 3) | (code_units-1); + lines = PyBytes_FromStringAndSize((const char *)loc_table, 2); if (lines == NULL) { goto cleanup; } @@ -2317,6 +2331,7 @@ _Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char * codeobj = _PyCode_New(&con); cleanup: + PyMem_Free(loc_table); Py_XDECREF(name); Py_XDECREF(co_code); Py_XDECREF(lines); From 76e437a0d139ec02ebf47000620fbbe6437750c0 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 11:54:28 +0100 Subject: [PATCH 21/29] Bundle shim code definition into single struct. --- Include/internal/pycore_code.h | 9 ++++++++- Objects/codeobject.c | 18 +++++++++++------- Python/pylifecycle.c | 16 ++++++++++------ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Include/internal/pycore_code.h b/Include/internal/pycore_code.h index 302fd64f2771d5..9fb2c527bdfc55 100644 --- a/Include/internal/pycore_code.h +++ b/Include/internal/pycore_code.h @@ -455,8 +455,15 @@ _PyCode_LineNumberFromArray(PyCodeObject *co, int index) } } +typedef struct _PyShimCodeDef { + const uint8_t *code; + int codelen; + int stacksize; + const char *cname; +} _PyShimCodeDef; + extern PyCodeObject * -_Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char *cname); +_Py_MakeShimCode(const _PyShimCodeDef *code); #ifdef __cplusplus diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e64f41e6a42c1f..d7af1a21fcc91d 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2270,24 +2270,26 @@ _PyStaticCode_InternStrings(PyCodeObject *co) } PyCodeObject * -_Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char *cname) +_Py_MakeShimCode(const _PyShimCodeDef *codedef) { PyObject *name = NULL; PyObject *co_code = NULL; PyObject *lines = NULL; PyCodeObject *codeobj = NULL; + uint8_t *loc_table = NULL; - name = _PyUnicode_FromASCII(cname, strlen(cname)); + name = _PyUnicode_FromASCII(codedef->cname, strlen(codedef->cname)); if (name == NULL) { goto cleanup; } - co_code = PyBytes_FromStringAndSize((const char *)code, codelen); + co_code = PyBytes_FromStringAndSize( + (const char *)codedef->code, codedef->codelen); if (co_code == NULL) { goto cleanup; } - int code_units = codelen/2; + int code_units = codedef->codelen/2; int loc_entries = (code_units + 7)/8; - uint8_t *loc_table = PyMem_Malloc(loc_entries); + loc_table = PyMem_Malloc(loc_entries); if (loc_table == NULL) { PyErr_NoMemory(); return NULL; @@ -2324,14 +2326,16 @@ _Py_MakeTrampoline(const uint8_t *code, int codelen, int stacksize, const char * .posonlyargcount = 0, .kwonlyargcount = 0, - .stacksize = stacksize, + .stacksize = codedef->stacksize, .exceptiontable = (PyObject *)&_Py_SINGLETON(bytes_empty), }; codeobj = _PyCode_New(&con); cleanup: - PyMem_Free(loc_table); + if (loc_table) { + PyMem_Free(loc_table); + } Py_XDECREF(name); Py_XDECREF(co_code); Py_XDECREF(lines); diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 8991464573006d..858b5926f1fc77 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -752,15 +752,22 @@ pycore_init_types(PyInterpreterState *interp) return _PyStatus_OK(); } -static const uint8_t INTERPRETER_TRAMPOLINE_CODE[] = { +static const uint8_t INTERPRETER_TRAMPOLINE_INSTRUCTIONS[] = { /* Put a NOP at the start, so that the IP points into - * the code, rather than before it */ + * the code, rather than before it */ NOP, 0, INTERPRETER_EXIT, 0, /* RESUME at end makes sure that the frame appears incomplete */ RESUME, 0 }; +static const _PyShimCodeDef INTERPRETER_TRAMPOLINE_CODEDEF = { + INTERPRETER_TRAMPOLINE_INSTRUCTIONS, + sizeof(INTERPRETER_TRAMPOLINE_INSTRUCTIONS), + 1, + "" +}; + static PyStatus pycore_init_builtins(PyThreadState *tstate) { @@ -794,10 +801,7 @@ pycore_init_builtins(PyThreadState *tstate) PyObject *object__getattribute__ = _PyType_Lookup(&PyBaseObject_Type, &_Py_ID(__getattribute__)); assert(object__getattribute__); interp->callable_cache.object__getattribute__ = object__getattribute__; - interp->interpreter_trampoline = _Py_MakeTrampoline( - INTERPRETER_TRAMPOLINE_CODE, - sizeof(INTERPRETER_TRAMPOLINE_CODE), - 1, ""); + interp->interpreter_trampoline = _Py_MakeShimCode(&INTERPRETER_TRAMPOLINE_CODEDEF); if (interp->interpreter_trampoline == NULL) { return _PyStatus_ERR("failed to create interpreter trampoline."); } From 945e26a1e33cd5a92d9eb9808545909367c2acb2 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 12:30:02 +0100 Subject: [PATCH 22/29] Remove incorrect assert. --- Objects/frameobject.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index e01d4b21ac4dc0..4a772ead662255 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1342,8 +1342,6 @@ int _PyFrame_IsEntryFrame(PyFrameObject *frame) assert(frame != NULL); _PyInterpreterFrame *f = frame->f_frame; assert(!_PyFrame_IsIncomplete(f)); - assert(f->owner == FRAME_OWNED_BY_FRAME_OBJECT || - f->owner == FRAME_OWNED_BY_GENERATOR); if (f->previous == NULL) { return 0; } From 676321aa014f5e6506bdfa4cd7020a19635cd4a9 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 12:57:17 +0100 Subject: [PATCH 23/29] assert generator is cleared after returning or raising. --- Objects/genobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/genobject.c b/Objects/genobject.c index a4e01484f9e5ec..ed9de00fac1223 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -269,7 +269,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* first clean reference cycle through stored exception traceback */ _PyErr_ClearExcState(&gen->gi_exc_state); - gen->gi_frame_state = FRAME_CLEARED; + assert(gen->gi_frame_state == FRAME_CLEARED); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; } From 3cb22e60ec709ae56500e8ce5f9d6198846be8f7 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 13:18:11 +0100 Subject: [PATCH 24/29] Rename pyframe to entry_frame. --- Python/ceval.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/Python/ceval.c b/Python/ceval.c index 678a41b42c61e0..9a29df397dc98e 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1041,7 +1041,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif _PyCFrame cframe; - _PyInterpreterFrame pyframe; + _PyInterpreterFrame entry_frame; PyObject *kwnames = NULL; // Borrowed reference. Reset by CALL instructions. /* WARNING: Because the _PyCFrame lives on the C stack, @@ -1055,20 +1055,21 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int assert(tstate->interp->interpreter_trampoline != NULL); #ifdef Py_DEBUG - pyframe.f_funcobj = (PyObject*)0xaaa0; - pyframe.f_locals = (PyObject*)0xaaa1; - pyframe.frame_obj = (PyFrameObject*)0xaaa2; - pyframe.f_globals = (PyObject*)0xaaa3; - pyframe.f_builtins = (PyObject*)0xaaa4; + /* Set these to invalid but identifiable values for debugging. */ + entry_frame.f_funcobj = (PyObject*)0xaaa0; + entry_frame.f_locals = (PyObject*)0xaaa1; + entry_frame.frame_obj = (PyFrameObject*)0xaaa2; + entry_frame.f_globals = (PyObject*)0xaaa3; + entry_frame.f_builtins = (PyObject*)0xaaa4; #endif - pyframe.f_code = tstate->interp->interpreter_trampoline; - _Py_CODEUNIT *code = _PyCode_CODE(tstate->interp->interpreter_trampoline); - pyframe.prev_instr = code; - pyframe.stacktop = 0; - pyframe.owner = FRAME_OWNED_BY_CSTACK; + entry_frame.f_code = tstate->interp->interpreter_trampoline; + entry_frame.prev_instr = + _PyCode_CODE(tstate->interp->interpreter_trampoline); + entry_frame.stacktop = 0; + entry_frame.owner = FRAME_OWNED_BY_CSTACK; /* Push frame */ - pyframe.previous = prev_cframe->current_frame; - frame->previous = &pyframe; + entry_frame.previous = prev_cframe->current_frame; + frame->previous = &entry_frame; cframe.current_frame = frame; if (_Py_EnterRecursiveCallTstate(tstate, "")) { @@ -1125,7 +1126,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #ifdef LLTRACE { - if (frame != &pyframe) { + if (frame != &entry_frame) { int r = PyDict_Contains(GLOBALS(), &_Py_ID(__lltrace__)); if (r < 0) { goto exit_unwind; @@ -1862,7 +1863,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(INTERPRETER_EXIT) { - assert(frame == &pyframe); + assert(frame == &entry_frame); assert(_PyFrame_IsIncomplete(frame)); PyObject *retval = POP(); assert(EMPTY()); @@ -1882,7 +1883,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &pyframe); + assert(frame != &entry_frame); frame = cframe.current_frame = pop_frame(tstate, frame); _PyFrame_StackPush(frame, retval); goto resume_frame; @@ -2019,7 +2020,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int } TARGET(SEND) { - assert(frame != &pyframe); + assert(frame != &entry_frame); assert(STACK_LEVEL() >= 2); PyObject *v = POP(); PyObject *receiver = TOP(); @@ -2084,7 +2085,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int // The compiler treats any exception raised here as a failed close() // or throw() call. assert(oparg == STACK_LEVEL()); - assert(frame != &pyframe); + assert(frame != &entry_frame); PyObject *retval = POP(); _PyFrame_GetGenerator(frame)->gi_frame_state = FRAME_SUSPENDED; _PyFrame_SetStackPointer(frame, stack_pointer); @@ -4847,7 +4848,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int gen->gi_frame_state = FRAME_CREATED; gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &pyframe); + assert(frame != &entry_frame); _PyInterpreterFrame *prev = frame->previous; gen_frame->previous = NULL; _PyThreadState_PopFrame(tstate, frame); @@ -5142,7 +5143,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int #endif /* Log traceback info. */ - assert(frame != &pyframe); + assert(frame != &entry_frame); if (!_PyFrame_IsIncomplete(frame)) { PyFrameObject *f = _PyFrame_GetFrameObject(frame); if (f != NULL) { @@ -5215,9 +5216,9 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int exit_unwind: assert(_PyErr_Occurred(tstate)); _Py_LeaveRecursiveCallPy(tstate); - assert(frame != &pyframe); + assert(frame != &entry_frame); frame = cframe.current_frame = pop_frame(tstate, frame); - if (frame == &pyframe) { + if (frame == &entry_frame) { assert(_PyFrame_IsIncomplete(frame)); /* Restore previous cframe and exit */ tstate->cframe = cframe.previous; From e5dcbd91ba3cdc09c26e359d27330d7a5fc47f0f Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 21 Oct 2022 14:11:09 +0100 Subject: [PATCH 25/29] Pass correct length. --- Objects/codeobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/codeobject.c b/Objects/codeobject.c index d7af1a21fcc91d..121bdee6f0d5e4 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2302,7 +2302,7 @@ _Py_MakeShimCode(const _PyShimCodeDef *codedef) assert(code_units > 0 && code_units <= 8); loc_table[loc_entries-1] = 0x80 | (PY_CODE_LOCATION_INFO_NONE << 3) | (code_units-1); - lines = PyBytes_FromStringAndSize((const char *)loc_table, 2); + lines = PyBytes_FromStringAndSize((const char *)loc_table, loc_entries); if (lines == NULL) { goto cleanup; } From bd293567d15857f0b53dd51a8a6e908fe56ceb9a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 7 Nov 2022 16:04:22 +0000 Subject: [PATCH 26/29] Address review comments --- Include/internal/pycore_global_strings.h | 4 +++ .../internal/pycore_runtime_init_generated.h | 26 +++++++++++++++++++ ...2-10-19-15-59-08.gh-issue-96421.e22y3r.rst | 3 +-- Objects/codeobject.c | 20 +++++++------- Objects/frameobject.c | 8 +++--- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 811cfc147fcf6b..bcc441b97ca852 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -48,6 +48,7 @@ struct _Py_global_strings { STRUCT_FOR_STR(newline, "\n") STRUCT_FOR_STR(open_br, "{") STRUCT_FOR_STR(percent, "%") + STRUCT_FOR_STR(shim_name, "") STRUCT_FOR_STR(utf_8, "utf-8") } literals; @@ -324,6 +325,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(digest_size) STRUCT_FOR_ID(digestmod) STRUCT_FOR_ID(dir_fd) + STRUCT_FOR_ID(discard) STRUCT_FOR_ID(dispatch_table) STRUCT_FOR_ID(displayhook) STRUCT_FOR_ID(dklen) @@ -564,6 +566,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(seek) STRUCT_FOR_ID(seekable) STRUCT_FOR_ID(selectors) + STRUCT_FOR_ID(self) STRUCT_FOR_ID(send) STRUCT_FOR_ID(sep) STRUCT_FOR_ID(sequence) @@ -625,6 +628,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(traceback) STRUCT_FOR_ID(trailers) STRUCT_FOR_ID(translate) + STRUCT_FOR_ID(true) STRUCT_FOR_ID(truncate) STRUCT_FOR_ID(twice) STRUCT_FOR_ID(txt) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 8ce95884ccdd41..9bacca9f2b6b9d 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -558,6 +558,7 @@ extern "C" { INIT_STR(newline, "\n"), \ INIT_STR(open_br, "{"), \ INIT_STR(percent, "%"), \ + INIT_STR(shim_name, ""), \ INIT_STR(utf_8, "utf-8"), \ }, \ .identifiers = { \ @@ -833,6 +834,7 @@ extern "C" { INIT_ID(digest_size), \ INIT_ID(digestmod), \ INIT_ID(dir_fd), \ + INIT_ID(discard), \ INIT_ID(dispatch_table), \ INIT_ID(displayhook), \ INIT_ID(dklen), \ @@ -1073,6 +1075,7 @@ extern "C" { INIT_ID(seek), \ INIT_ID(seekable), \ INIT_ID(selectors), \ + INIT_ID(self), \ INIT_ID(send), \ INIT_ID(sep), \ INIT_ID(sequence), \ @@ -1134,6 +1137,7 @@ extern "C" { INIT_ID(traceback), \ INIT_ID(trailers), \ INIT_ID(translate), \ + INIT_ID(true), \ INIT_ID(truncate), \ INIT_ID(twice), \ INIT_ID(txt), \ @@ -1977,6 +1981,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(dir_fd); PyUnicode_InternInPlace(&string); + string = &_Py_ID(discard); + PyUnicode_InternInPlace(&string); string = &_Py_ID(dispatch_table); PyUnicode_InternInPlace(&string); string = &_Py_ID(displayhook); @@ -2457,6 +2463,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(selectors); PyUnicode_InternInPlace(&string); + string = &_Py_ID(self); + PyUnicode_InternInPlace(&string); string = &_Py_ID(send); PyUnicode_InternInPlace(&string); string = &_Py_ID(sep); @@ -2579,6 +2587,8 @@ _PyUnicode_InitStaticStrings(void) { PyUnicode_InternInPlace(&string); string = &_Py_ID(translate); PyUnicode_InternInPlace(&string); + string = &_Py_ID(true); + PyUnicode_InternInPlace(&string); string = &_Py_ID(truncate); PyUnicode_InternInPlace(&string); string = &_Py_ID(twice); @@ -4794,6 +4804,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_STR(percent)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_STR(shim_name)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_STR(shim_name)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_STR(utf_8)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_STR(utf_8)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); @@ -5886,6 +5900,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(dir_fd)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(discard)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(discard)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(dispatch_table)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(dispatch_table)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); @@ -6846,6 +6864,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(selectors)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(self)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(self)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(send)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(send)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); @@ -7090,6 +7112,10 @@ _PyStaticObjects_CheckRefcnt(void) { _PyObject_Dump((PyObject *)&_Py_ID(translate)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); }; + if (Py_REFCNT((PyObject *)&_Py_ID(true)) < _PyObject_IMMORTAL_REFCNT) { + _PyObject_Dump((PyObject *)&_Py_ID(true)); + Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); + }; if (Py_REFCNT((PyObject *)&_Py_ID(truncate)) < _PyObject_IMMORTAL_REFCNT) { _PyObject_Dump((PyObject *)&_Py_ID(truncate)); Py_FatalError("immortal object has less refcnt than expected _PyObject_IMMORTAL_REFCNT"); diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst index 21160f00d309b0..2a189866c3aee7 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst @@ -2,8 +2,7 @@ When calling into Python code from C code, though ``PyEval_EvalFrame`` or related C-API function, a shim frame in inserted into the call stack. This occurs in the ``_PyEval_EvalFrameDefault()`` function. The extra frame should be invisible to all Python and most C extensions, -but out-of-process debuggers, profilers and debuggers need to be aware of -it. +but out-of-process profilers and debuggers need to be aware of it. These shim frames can be detected by checking ``frame->owner == FRAME_OWNED_BY_CSTACK``. diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 121bdee6f0d5e4..aa53a42b28eef5 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2269,6 +2269,8 @@ _PyStaticCode_InternStrings(PyCodeObject *co) return 0; } +#define MAX_CODE_UNITS_PER_LOC_ENTRY 8 + PyCodeObject * _Py_MakeShimCode(const _PyShimCodeDef *codedef) { @@ -2287,27 +2289,30 @@ _Py_MakeShimCode(const _PyShimCodeDef *codedef) if (co_code == NULL) { goto cleanup; } - int code_units = codedef->codelen/2; - int loc_entries = (code_units + 7)/8; + int code_units = codedef->codelen / sizeof(_Py_CODEUNIT); + int loc_entries = (code_units + MAX_CODE_UNITS_PER_LOC_ENTRY - 1) / + MAX_CODE_UNITS_PER_LOC_ENTRY; loc_table = PyMem_Malloc(loc_entries); if (loc_table == NULL) { PyErr_NoMemory(); - return NULL; + goto cleanup; } for (int i = 0; i < loc_entries-1; i++) { loc_table[i] = 0x80 | (PY_CODE_LOCATION_INFO_NONE << 3) | 7; - code_units -= 8; + code_units -= MAX_CODE_UNITS_PER_LOC_ENTRY; } assert(loc_entries > 0); - assert(code_units > 0 && code_units <= 8); + assert(code_units > 0 && code_units <= MAX_CODE_UNITS_PER_LOC_ENTRY); loc_table[loc_entries-1] = 0x80 | (PY_CODE_LOCATION_INFO_NONE << 3) | (code_units-1); lines = PyBytes_FromStringAndSize((const char *)loc_table, loc_entries); + PyMem_Free(loc_table); if (lines == NULL) { goto cleanup; } + _Py_DECLARE_STR(shim_name, ""); struct _PyCodeConstructor con = { - .filename = &_Py_STR(empty), + .filename = &_Py_STR(shim_name), .name = name, .qualname = name, .flags = CO_NEWLOCALS | CO_OPTIMIZED, @@ -2333,9 +2338,6 @@ _Py_MakeShimCode(const _PyShimCodeDef *codedef) codeobj = _PyCode_New(&con); cleanup: - if (loc_table) { - PyMem_Free(loc_table); - } Py_XDECREF(name); Py_XDECREF(co_code); Py_XDECREF(lines); diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 4a772ead662255..5b8dd6e38fa465 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1337,15 +1337,13 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) } } -int _PyFrame_IsEntryFrame(PyFrameObject *frame) +int +_PyFrame_IsEntryFrame(PyFrameObject *frame) { assert(frame != NULL); _PyInterpreterFrame *f = frame->f_frame; assert(!_PyFrame_IsIncomplete(f)); - if (f->previous == NULL) { - return 0; - } - return f->previous->owner == FRAME_OWNED_BY_CSTACK; + return f->previous && f->previous->owner == FRAME_OWNED_BY_CSTACK; } PyCodeObject * From f6a6457e0009f1121600bdd56463081cf2272961 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 8 Nov 2022 09:47:37 +0000 Subject: [PATCH 27/29] Post merge cleanup --- Objects/genobject.c | 7 +------ Python/bytecodes.c | 7 ++----- Python/ceval.c | 6 +----- Python/generated_cases.c.h | 7 ++----- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/Objects/genobject.c b/Objects/genobject.c index 753352e37d1119..3886e72add2dd2 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -221,12 +221,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, result = _PyEval_EvalFrame(tstate, frame, exc); assert(tstate->exc_info == prev_exc_info); assert(gen->gi_exc_state.previous_item == NULL); - if (gen->gi_frame_state == FRAME_EXECUTING) { - gen->gi_frame_state = FRAME_COMPLETED; - } - /* Don't keep the reference to previous any longer than necessary. It - * may keep a chain of frames alive or it could create a reference - * cycle. */ + assert(gen->gi_frame_state != FRAME_EXECUTING); assert(frame->previous == NULL); /* If the generator just returned (as opposed to yielding), signal diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 12ea05e195f6ed..3aa7de6fd954a9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -764,11 +764,6 @@ dummy_func( TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallPy(tstate); - if (frame->owner == FRAME_OWNED_BY_GENERATOR) { - PyGenObject *gen = _PyFrame_GetGenerator(frame); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - } assert(frame != &entry_frame); frame = cframe.current_frame = pop_frame(tstate, frame); _PyFrame_StackPush(frame, retval); @@ -981,7 +976,9 @@ dummy_func( tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; frame = cframe.current_frame = frame->previous; + gen_frame->previous = NULL; frame->prev_instr -= frame->yield_offset; _PyFrame_StackPush(frame, retval); goto resume_frame; diff --git a/Python/ceval.c b/Python/ceval.c index 811584e460de19..9f529d8880b4af 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1067,6 +1067,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyCode_CODE(tstate->interp->interpreter_trampoline); entry_frame.stacktop = 0; entry_frame.owner = FRAME_OWNED_BY_CSTACK; + entry_frame.yield_offset = 0; /* Push frame */ entry_frame.previous = prev_cframe->current_frame; frame->previous = &entry_frame; @@ -1372,11 +1373,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _Py_LeaveRecursiveCallPy(tstate); assert(frame != &entry_frame); frame = cframe.current_frame = pop_frame(tstate, frame); - if (frame->owner == FRAME_OWNED_BY_GENERATOR) { - PyGenObject *gen = _PyFrame_GetGenerator(frame); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - } if (frame == &entry_frame) { /* Restore previous cframe and exit */ tstate->cframe = cframe.previous; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index b41bdee4ba80e6..36e1a9b1545117 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -654,11 +654,6 @@ TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); _Py_LeaveRecursiveCallPy(tstate); - if (frame->owner == FRAME_OWNED_BY_GENERATOR) { - PyGenObject *gen = _PyFrame_GetGenerator(frame); - tstate->exc_info = gen->gi_exc_state.previous_item; - gen->gi_exc_state.previous_item = NULL; - } assert(frame != &entry_frame); frame = cframe.current_frame = pop_frame(tstate, frame); _PyFrame_StackPush(frame, retval); @@ -871,7 +866,9 @@ tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; _Py_LeaveRecursiveCallPy(tstate); + _PyInterpreterFrame *gen_frame = frame; frame = cframe.current_frame = frame->previous; + gen_frame->previous = NULL; frame->prev_instr -= frame->yield_offset; _PyFrame_StackPush(frame, retval); goto resume_frame; From d1136553cdb64119da4cf890785eda8675ce2963 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 8 Nov 2022 10:41:03 +0000 Subject: [PATCH 28/29] Turn ASAN back on for subprocess test. --- Lib/test/test_subprocess.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 553d9fa7008b93..32a6afee3aa2fe 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1882,8 +1882,6 @@ def bad_error(*args): @unittest.skipIf(not os.path.exists('/proc/self/status'), "need /proc/self/status") - @skip_if_sanitizer(memory=True, address=True, - reason= "Spurious error when assigning to stack variable.") def test_restore_signals(self): # Blindly assume that cat exists on systems with /proc/self/status... default_proc_status = subprocess.check_output( From ed7af1eee183b2a2c4f97dcc46594c8c7eafa637 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 9 Nov 2022 16:39:39 +0000 Subject: [PATCH 29/29] Apply suggestions from code review Co-authored-by: Brandt Bucher --- Lib/test/test_subprocess.py | 1 - .../2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Lib/test/test_subprocess.py b/Lib/test/test_subprocess.py index 32a6afee3aa2fe..424a4a93b6f972 100644 --- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -25,7 +25,6 @@ import json import pathlib from test.support.os_helper import FakePath -from test.support import skip_if_sanitizer try: import _testcapi diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst index 2a189866c3aee7..857ce7decea285 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-10-19-15-59-08.gh-issue-96421.e22y3r.rst @@ -1,5 +1,5 @@ -When calling into Python code from C code, though ``PyEval_EvalFrame`` or -related C-API function, a shim frame in inserted into the call stack. +When calling into Python code from C code, through :c:func:`PyEval_EvalFrameEx` or +a related C-API function, a shim frame in inserted into the call stack. This occurs in the ``_PyEval_EvalFrameDefault()`` function. The extra frame should be invisible to all Python and most C extensions, but out-of-process profilers and debuggers need to be aware of it. @@ -8,6 +8,6 @@ These shim frames can be detected by checking Extensions implementing their own interpreters using PEP 523 need to be aware of this shim frame and the changes to the semantics of -``RETURN_VALUE``, ``YIELD_VALUE``, and ``RETURN_GENERATOR``, +:opcode:`RETURN_VALUE`, :opcode:`YIELD_VALUE`, and :opcode:`RETURN_GENERATOR`, which now clear the frame.