From 4a6c25172efc4c4944771d142668ec4220be085f Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 5 Jan 2024 01:59:45 +0800 Subject: [PATCH 01/19] Add types to the DSL --- Include/internal/pycore_opcode_metadata.h | 53 +++++++++++++ Include/internal/pycore_uop_ids.h | 5 +- Include/internal/pycore_uop_metadata.h | 2 - Python/bytecodes.c | 79 +++++++++---------- Python/executor_cases.c.h | 10 --- Python/generated_cases.c.h | 31 +++----- Tools/cases_generator/analyzer.py | 22 +++++- Tools/cases_generator/lexer.py | 10 ++- .../opcode_metadata_generator.py | 28 +++++++ Tools/cases_generator/parsing.py | 23 ++++-- Tools/cases_generator/stack.py | 11 +-- 11 files changed, 184 insertions(+), 90 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 7d39e4bc03099c..b3034db47f1433 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1877,6 +1877,59 @@ is_pseudo_target(int pseudo, int target) { return false; } +extern const uint8_t _PyOpcode_ispure[462]; +#ifdef NEED_OPCODE_METADATA +const uint8_t _PyOpcode_ispure[462] = { + [LOAD_FAST] = 1, + [LOAD_FAST_AND_CLEAR] = 1, + [LOAD_CONST] = 1, + [STORE_FAST] = 1, + [POP_TOP] = 1, + [PUSH_NULL] = 1, + [END_SEND] = 1, + [UNARY_NOT] = 1, + [COPY] = 1, + [SWAP] = 1, + [END_FOR] = 1, + [RETURN_VALUE] = 1, + [RETURN_CONST] = 1, + [_BINARY_OP_MULTIPLY_INT] = 1, + [_BINARY_OP_ADD_INT] = 1, + [_BINARY_OP_SUBTRACT_INT] = 1, + [_BINARY_OP_MULTIPLY_FLOAT] = 1, + [_BINARY_OP_ADD_FLOAT] = 1, + [_BINARY_OP_SUBTRACT_FLOAT] = 1, + [_BINARY_OP_ADD_UNICODE] = 1, + [_POP_FRAME] = 1, + [_INIT_CALL_PY_EXACT_ARGS] = 1, + [_PUSH_FRAME] = 1, +}; + +#endif // NEED_OPCODE_METADATA + +extern const uint8_t _PyOpcode_isguard[462]; +#ifdef NEED_OPCODE_METADATA +const uint8_t _PyOpcode_isguard[462] = { + [RESUME_CHECK] = 1, + [_GUARD_BOTH_INT] = 1, + [_GUARD_BOTH_FLOAT] = 1, + [_GUARD_BOTH_UNICODE] = 1, + [_GUARD_GLOBALS_VERSION] = 1, + [_GUARD_BUILTINS_VERSION] = 1, + [_GUARD_TYPE_VERSION] = 1, + [_CHECK_MANAGED_OBJECT_HAS_VALUES] = 1, + [_GUARD_DORV_VALUES] = 1, + [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = 1, + [_GUARD_KEYS_VERSION] = 1, + [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = 1, + [_CHECK_PEP_523] = 1, + [_CHECK_FUNCTION_EXACT_ARGS] = 1, + [_CHECK_STACK_SPACE] = 1, + [_SAVE_RETURN_OFFSET] = 1, +}; + +#endif // NEED_OPCODE_METADATA + #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_ids.h b/Include/internal/pycore_uop_ids.h index 4a9a00ba352d33..71d167e195b34e 100644 --- a/Include/internal/pycore_uop_ids.h +++ b/Include/internal/pycore_uop_ids.h @@ -229,9 +229,8 @@ extern "C" { #define _GUARD_IS_NOT_NONE_POP 376 #define _JUMP_TO_TOP 377 #define _SAVE_RETURN_OFFSET 378 -#define _INSERT 379 -#define _CHECK_VALIDITY 380 -#define MAX_UOP_ID 380 +#define _CHECK_VALIDITY 379 +#define MAX_UOP_ID 379 #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 300bd3baa7b377..c20829ca002c33 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -201,7 +201,6 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_SET_IP] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG, [_EXIT_TRACE] = HAS_DEOPT_FLAG, - [_INSERT] = HAS_ARG_FLAG, [_CHECK_VALIDITY] = HAS_DEOPT_FLAG, }; @@ -302,7 +301,6 @@ const char *const _PyOpcode_uop_name[MAX_UOP_ID+1] = { [_GUARD_TYPE_VERSION] = "_GUARD_TYPE_VERSION", [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = "_INIT_CALL_BOUND_METHOD_EXACT_ARGS", [_INIT_CALL_PY_EXACT_ARGS] = "_INIT_CALL_PY_EXACT_ARGS", - [_INSERT] = "_INSERT", [_IS_NONE] = "_IS_NONE", [_IS_OP] = "_IS_OP", [_ITER_CHECK_LIST] = "_ITER_CHECK_LIST", diff --git a/Python/bytecodes.c b/Python/bytecodes.c index e1a6a256fbdf96..5a2f727ded59ff 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -161,7 +161,7 @@ dummy_func( } } - inst(RESUME_CHECK, (--)) { + mandatory guard inst(RESUME_CHECK, (--)) { #if defined(__EMSCRIPTEN__) DEOPT_IF(_Py_emscripten_signal_clock == 0); _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; @@ -208,13 +208,13 @@ dummy_func( Py_INCREF(value); } - inst(LOAD_FAST, (-- value)) { + pure inst(LOAD_FAST, (-- value)) { value = GETLOCAL(oparg); assert(value != NULL); Py_INCREF(value); } - inst(LOAD_FAST_AND_CLEAR, (-- value)) { + pure inst(LOAD_FAST_AND_CLEAR, (-- value)) { value = GETLOCAL(oparg); // do not use SETLOCAL here, it decrefs the old value GETLOCAL(oparg) = NULL; @@ -229,12 +229,12 @@ dummy_func( Py_INCREF(value2); } - inst(LOAD_CONST, (-- value)) { + pure inst(LOAD_CONST, (-- value)) { value = GETITEM(FRAME_CO_CONSTS, oparg); Py_INCREF(value); } - inst(STORE_FAST, (value --)) { + pure inst(STORE_FAST, (value --)) { SETLOCAL(oparg, value); } @@ -257,11 +257,11 @@ dummy_func( SETLOCAL(oparg2, value2); } - inst(POP_TOP, (value --)) { + pure inst(POP_TOP, (value --)) { DECREF_INPUTS(); } - inst(PUSH_NULL, (-- res)) { + pure inst(PUSH_NULL, (-- res)) { res = NULL; } @@ -281,7 +281,7 @@ dummy_func( DECREF_INPUTS(); } - inst(END_SEND, (receiver, value -- value)) { + pure inst(END_SEND, (receiver, value -- value)) { Py_DECREF(receiver); } @@ -303,7 +303,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - inst(UNARY_NOT, (value -- res)) { + pure inst(UNARY_NOT, (value -- res)) { assert(PyBool_Check(value)); res = Py_IsFalse(value) ? Py_True : Py_False; } @@ -411,12 +411,12 @@ dummy_func( // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. }; - op(_GUARD_BOTH_INT, (left, right -- left, right)) { + guard op(_GUARD_BOTH_INT, (left, right -- left: ~(PYINT_TYPE), right: ~(PYINT_TYPE))) { DEOPT_IF(!PyLong_CheckExact(left)); DEOPT_IF(!PyLong_CheckExact(right)); } - op(_BINARY_OP_MULTIPLY_INT, (left, right -- res)) { + pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res: ~(PYINT_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -424,7 +424,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - op(_BINARY_OP_ADD_INT, (left, right -- res)) { + pure op(_BINARY_OP_ADD_INT, (left, right -- res: ~(PYINT_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -432,7 +432,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - op(_BINARY_OP_SUBTRACT_INT, (left, right -- res)) { + pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res: ~(PYINT_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -447,12 +447,12 @@ dummy_func( macro(BINARY_OP_SUBTRACT_INT) = _GUARD_BOTH_INT + unused/1 + _BINARY_OP_SUBTRACT_INT; - op(_GUARD_BOTH_FLOAT, (left, right -- left, right)) { + guard op(_GUARD_BOTH_FLOAT, (left, right -- left: ~(PYFLOAT_TYPE), right: ~(PYFLOAT_TYPE))) { DEOPT_IF(!PyFloat_CheckExact(left)); DEOPT_IF(!PyFloat_CheckExact(right)); } - op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res)) { + pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res: ~(PYFLOAT_TYPE))) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval * @@ -460,7 +460,7 @@ dummy_func( DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - op(_BINARY_OP_ADD_FLOAT, (left, right -- res)) { + pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res: ~(PYFLOAT_TYPE))) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval + @@ -468,7 +468,7 @@ dummy_func( DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res)) { + pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res: ~(PYFLOAT_TYPE))) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval - @@ -483,12 +483,12 @@ dummy_func( macro(BINARY_OP_SUBTRACT_FLOAT) = _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_SUBTRACT_FLOAT; - op(_GUARD_BOTH_UNICODE, (left, right -- left, right)) { + guard op(_GUARD_BOTH_UNICODE, (left, right -- left: ~(PYUNICODE_TYPE), right: ~(PYUNICODE_TYPE))) { DEOPT_IF(!PyUnicode_CheckExact(left)); DEOPT_IF(!PyUnicode_CheckExact(right)); } - op(_BINARY_OP_ADD_UNICODE, (left, right -- res)) { + pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res: ~(PYUNICODE_TYPE))) { STAT_INC(BINARY_OP, hit); res = PyUnicode_Concat(left, right); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); @@ -801,7 +801,7 @@ dummy_func( // We definitely pop the return value off the stack on entry. // We also push it onto the stack on exit, but that's a // different frame, and it's accounted for by _PUSH_FRAME. - op(_POP_FRAME, (retval --)) { + pure op(_POP_FRAME, (retval --)) { #if TIER_ONE assert(frame != &entry_frame); #endif @@ -1458,14 +1458,14 @@ dummy_func( builtins_version/1 + _LOAD_GLOBAL; - op(_GUARD_GLOBALS_VERSION, (version/1 --)) { + mandatory guard op(_GUARD_GLOBALS_VERSION, (version/1 --)) { PyDictObject *dict = (PyDictObject *)GLOBALS(); DEOPT_IF(!PyDict_CheckExact(dict)); DEOPT_IF(dict->ma_keys->dk_version != version); assert(DK_IS_UNICODE(dict->ma_keys)); } - op(_GUARD_BUILTINS_VERSION, (version/1 --)) { + mandatory guard op(_GUARD_BUILTINS_VERSION, (version/1 --)) { PyDictObject *dict = (PyDictObject *)BUILTINS(); DEOPT_IF(!PyDict_CheckExact(dict)); DEOPT_IF(dict->ma_keys->dk_version != version); @@ -1900,13 +1900,13 @@ dummy_func( LOAD_ATTR, }; - op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner)) { + guard op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner: ~(GUARD_TYPE_VERSION_TYPE + type_version))) { PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version); } - op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { + mandatory guard op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { assert(Py_TYPE(owner)->tp_dictoffset < 0); assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); @@ -2081,7 +2081,7 @@ dummy_func( DISPATCH_INLINED(new_frame); } - op(_GUARD_DORV_VALUES, (owner -- owner)) { + guard op(_GUARD_DORV_VALUES, (owner -- owner: ~(GUARD_DORV_VALUES_TYPE))) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); @@ -2721,7 +2721,7 @@ dummy_func( DEOPT_IF(r->len <= 0); } - op(_ITER_NEXT_RANGE, (iter -- iter, next)) { + op(_ITER_NEXT_RANGE, (iter -- iter, next: ~(PYINT_TYPE))) { _PyRangeIterObject *r = (_PyRangeIterObject *)iter; assert(Py_TYPE(r) == &PyRangeIter_Type); assert(r->len > 0); @@ -2879,13 +2879,13 @@ dummy_func( exc_info->exc_value = Py_NewRef(new_exc); } - op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner)) { + guard op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner: ~(GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE))) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); } - op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner)) { + guard op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner: ~(GUARD_KEYS_VERSION_TYPE + keys_version))) { PyTypeObject *owner_cls = Py_TYPE(owner); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version); @@ -3100,7 +3100,7 @@ dummy_func( macro(CALL) = _SPECIALIZE_CALL + unused/2 + _CALL; - op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable, null, unused[oparg])) { + guard op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable: ~(PYMETHOD_TYPE), null: ~(NULL_TYPE), unused[oparg])) { DEOPT_IF(null != NULL); DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type); } @@ -3114,11 +3114,11 @@ dummy_func( Py_DECREF(callable); } - op(_CHECK_PEP_523, (--)) { + mandatory guard op(_CHECK_PEP_523, (--)) { DEOPT_IF(tstate->interp->eval_frame); } - op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable, self_or_null, unused[oparg])) { + guard op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable: ~(PYFUNCTION_TYPE_VERSION_TYPE + func_version), self_or_null, unused[oparg])) { DEOPT_IF(!PyFunction_Check(callable)); PyFunctionObject *func = (PyFunctionObject *)callable; DEOPT_IF(func->func_version != func_version); @@ -3126,14 +3126,14 @@ dummy_func( DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL)); } - op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + mandatory guard op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { PyFunctionObject *func = (PyFunctionObject *)callable; PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); DEOPT_IF(tstate->py_recursion_remaining <= 1); } - op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) { + mandatory pure op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) { int argcount = oparg; if (self_or_null != NULL) { args--; @@ -3150,7 +3150,7 @@ dummy_func( // The 'unused' output effect represents the return value // (which will be pushed when the frame returns). // It is needed so CALL_PY_EXACT_ARGS matches its family. - op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused if (0))) { + pure op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused if (0))) { // Write it out explicitly because it's subtly different. // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); @@ -3877,7 +3877,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { + pure inst(COPY, (bottom, unused[oparg-1] -- bottom, unused[oparg-1], top)) { assert(oparg > 0); top = Py_NewRef(bottom); } @@ -3906,7 +3906,7 @@ dummy_func( macro(BINARY_OP) = _SPECIALIZE_BINARY_OP + _BINARY_OP; - inst(SWAP, (bottom, unused[oparg-2], top -- + pure inst(SWAP, (bottom, unused[oparg-2], top -- top, unused[oparg-2], bottom)) { assert(oparg >= 2); } @@ -4042,7 +4042,8 @@ dummy_func( frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + oparg; } - op(_SAVE_RETURN_OFFSET, (--)) { + // Not exactly a guard, but not pure either, is just required. + mandatory guard op(_SAVE_RETURN_OFFSET, (--)) { #if TIER_ONE frame->return_offset = (uint16_t)(next_instr - this_instr); #endif @@ -4056,10 +4057,6 @@ dummy_func( DEOPT_IF(1); } - op(_INSERT, (unused[oparg], top -- top, unused[oparg])) { - // Inserts TOS at position specified by oparg; - memmove(&stack_pointer[-1 - oparg], &stack_pointer[-oparg], oparg * sizeof(stack_pointer[0])); - } op(_CHECK_VALIDITY, (--)) { TIER_TWO_ONLY diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 14fb3a05a9f674..3df108a2541fd8 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -3381,16 +3381,6 @@ break; } - case _INSERT: { - PyObject *top; - oparg = CURRENT_OPARG(); - top = stack_pointer[-1]; - // Inserts TOS at position specified by oparg; - memmove(&stack_pointer[-1 - oparg], &stack_pointer[-oparg], oparg * sizeof(stack_pointer[0])); - stack_pointer[-1 - oparg] = top; - break; - } - case _CHECK_VALIDITY: { TIER_TWO_ONLY if (!current_executor->base.vm_data.valid) goto deoptimize; diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 8226d827cde514..ce31967b7912d7 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2371,35 +2371,24 @@ } TARGET(ENTER_EXECUTOR) { - _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; + frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); TIER_ONE_ONLY CHECK_EVAL_BREAKER(); PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255]; - if (executor->vm_data.valid) { - Py_INCREF(executor); - if (executor->execute == _PyUOpExecute) { - current_executor = (_PyUOpExecutorObject *)executor; - GOTO_TIER_TWO(); - } - next_instr = executor->execute(executor, frame, stack_pointer); - frame = tstate->current_frame; - if (next_instr == NULL) { - goto resume_with_error; - } - stack_pointer = _PyFrame_GetStackPointer(frame); + Py_INCREF(executor); + if (executor->execute == _PyUOpExecute) { + current_executor = (_PyUOpExecutorObject *)executor; + GOTO_TIER_TWO(); } - else { - code->co_executors->executors[oparg & 255] = NULL; - opcode = this_instr->op.code = executor->vm_data.opcode; - this_instr->op.arg = executor->vm_data.oparg; - oparg = (oparg & (~255)) | executor->vm_data.oparg; - Py_DECREF(executor); - next_instr = this_instr; - DISPATCH_GOTO(); + next_instr = executor->execute(executor, frame, stack_pointer); + frame = tstate->current_frame; + if (next_instr == NULL) { + goto resume_with_error; } + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 82ef8888bfcee5..3dd1dd8f72d548 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field import lexer import parser from typing import Optional @@ -22,6 +22,10 @@ class Properties: uses_locals: bool has_free: bool + pure: bool + guard: bool + mandatory: bool + def dump(self, indent: str) -> None: print(indent, end="") text = ", ".join([f"{key}: {value}" for (key, value) in self.__dict__.items()]) @@ -45,6 +49,10 @@ def from_list(properties: list["Properties"]) -> "Properties": uses_co_names=any(p.uses_co_names for p in properties), uses_locals=any(p.uses_locals for p in properties), has_free=any(p.has_free for p in properties), + + pure=all(p.pure for p in properties), + guard=all(p.guard for p in properties), + mandatory=any(p.mandatory for p in properties), ) @@ -64,6 +72,10 @@ def from_list(properties: list["Properties"]) -> "Properties": uses_co_names=False, uses_locals=False, has_free=False, + + pure=False, + guard=False, + mandatory=False, ) @@ -88,6 +100,8 @@ class StackItem: condition: str | None size: str peek: bool = False + type_prop: None | tuple[str, None | str] = \ + field(default_factory=lambda: None, init=True, compare=False, hash=False) def __str__(self) -> str: cond = f" if ({self.condition})" if self.condition else "" @@ -259,7 +273,7 @@ def override_error( def convert_stack_item(item: parser.StackEffect) -> StackItem: - return StackItem(item.name, item.type, item.cond, (item.size or "1")) + return StackItem(item.name, item.type, item.cond, (item.size or "1"), type_prop=item.type_prop) def analyze_stack(op: parser.InstDef) -> StackEffect: @@ -440,6 +454,10 @@ def compute_properties(op: parser.InstDef) -> Properties: uses_locals=(variable_used(op, "GETLOCAL") or variable_used(op, "SETLOCAL")) and not has_free, has_free=has_free, + + pure="pure" in op.annotations, + guard="guard" in op.annotations, + mandatory="mandatory" in op.annotations, ) diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index c3c2954a42083f..b81e31f1c955ac 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -216,7 +216,15 @@ def choice(*opts: str) -> str: keywords = {name.lower(): name for name in kwds} ANNOTATION = "ANNOTATION" -annotations = {"specializing", "guard", "override", "register", "replaced"} +annotations = { + "specializing", + "guard", + "override", + "register", + "replaced", + "pure", + "mandatory" +} __all__ = [] __all__.extend(kwds) diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py index 9b7df9a54c7b3b..210cb0b0252f31 100644 --- a/Tools/cases_generator/opcode_metadata_generator.py +++ b/Tools/cases_generator/opcode_metadata_generator.py @@ -330,6 +330,32 @@ def generate_pseudo_targets(analysis: Analysis, out: CWriter) -> None: out.emit("}\n\n") +def generate_tier2_properties(analysis: Analysis, out: CWriter) -> None: + table_size = len(analysis.instructions) + len(analysis.uops) + + out.emit(f"extern const uint8_t _PyOpcode_ispure[{table_size}];\n") + out.emit("#ifdef NEED_OPCODE_METADATA\n") + out.emit(f"const uint8_t _PyOpcode_ispure[{table_size}] = {{\n") + + for name, inst in (analysis.instructions | analysis.uops).items(): + if inst.properties.pure: + out.emit(f"[{name}] = 1,\n") + + out.emit("};\n\n") + out.emit("#endif // NEED_OPCODE_METADATA\n\n") + + out.emit(f"extern const uint8_t _PyOpcode_isguard[{table_size}];\n") + out.emit("#ifdef NEED_OPCODE_METADATA\n") + out.emit(f"const uint8_t _PyOpcode_isguard[{table_size}] = {{\n") + + for name, inst in (analysis.instructions | analysis.uops).items(): + if inst.properties.guard: + out.emit(f"[{name}] = 1,\n") + + out.emit("};\n\n") + out.emit("#endif // NEED_OPCODE_METADATA\n\n") + + def generate_opcode_metadata( filenames: list[str], analysis: Analysis, outfile: TextIO ) -> None: @@ -358,6 +384,8 @@ def generate_opcode_metadata( generate_deopt_table(analysis, out) generate_extra_cases(analysis, out) generate_pseudo_targets(analysis, out) + generate_tier2_properties(analysis, out) + arg_parser = argparse.ArgumentParser( diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index 60c185dcef58e9..b7338956ff5664 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -71,6 +71,9 @@ class Block(Node): class StackEffect(Node): name: str = field(compare=False) # __eq__ only uses type, cond, size type: str = "" # Optional `:type` + # Optional `(type, aux)` + type_prop: None | tuple[str, None | str] = \ + field(default_factory=lambda: None, init=True, compare=False, hash=False) cond: str = "" # Optional `if (cond)` size: str = "" # Optional `[size]` # Note: size cannot be combined with type or cond @@ -253,14 +256,24 @@ def cache_effect(self) -> CacheEffect | None: @contextual def stack_effect(self) -> StackEffect | None: - # IDENTIFIER [':' IDENTIFIER [TIMES]] ['if' '(' expression ')'] + # IDENTIFIER [':' [IDENTIFIER [TIMES]] ['~' '(' IDENTIFIER ['+' IDENTIFIER] ')']] ['if' '(' expression ')'] # | IDENTIFIER '[' expression ']' if tkn := self.expect(lx.IDENTIFIER): type_text = "" + type_prop = None if self.expect(lx.COLON): - type_text = self.require(lx.IDENTIFIER).text.strip() - if self.expect(lx.TIMES): - type_text += " *" + if i := self.expect(lx.IDENTIFIER): + type_text = i.text.strip() + if self.expect(lx.TIMES): + type_text += " *" + if self.expect(lx.NOT): + self.require(lx.LPAREN) + type_prop_text = self.require(lx.IDENTIFIER).text.strip() + aux = None + if self.expect(lx.PLUS): + aux = self.require(lx.IDENTIFIER).text.strip() + type_prop = (type_prop_text, aux) + self.require(lx.RPAREN) cond_text = "" if self.expect(lx.IF): self.require(lx.LPAREN) @@ -277,7 +290,7 @@ def stack_effect(self) -> StackEffect | None: self.require(lx.RBRACKET) type_text = "PyObject **" size_text = size.text.strip() - return StackEffect(tkn.text, type_text, cond_text, size_text) + return StackEffect(tkn.text, type_text, type_prop, cond_text, size_text) return None @contextual diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index d351037a663ca2..7328b09ddc4bb6 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from cwriter import CWriter +UNUSED = {"unused", "__unused_"} def maybe_parenthesize(sym: str) -> str: """Add parentheses around a string if it contains an operator @@ -134,18 +135,18 @@ def pop(self, var: StackItem) -> str: ) if popped.name == var.name: return "" - elif popped.name == "unused": + elif popped.name in UNUSED: self.defined.add(var.name) return ( f"{var.name} = {indirect}stack_pointer[{self.top_offset.to_c()}];\n" ) - elif var.name == "unused": + elif var.name in UNUSED: return "" else: self.defined.add(var.name) return f"{var.name} = {popped.name};\n" self.base_offset.pop(var) - if var.name == "unused": + if var.name in UNUSED: return "" else: self.defined.add(var.name) @@ -159,7 +160,7 @@ def pop(self, var: StackItem) -> str: def push(self, var: StackItem) -> str: self.variables.append(var) - if var.is_array() and var.name not in self.defined and var.name != "unused": + if var.is_array() and var.name not in self.defined and var.name not in UNUSED: c_offset = self.top_offset.to_c() self.top_offset.push(var) self.defined.add(var.name) @@ -172,7 +173,7 @@ def flush(self, out: CWriter) -> None: for var in self.variables: if not var.peek: cast = "(PyObject *)" if var.type else "" - if var.name != "unused" and not var.is_array(): + if var.name not in UNUSED and not var.is_array(): if var.condition: out.emit(f"if ({var.condition}) ") out.emit( From 70e42b08170759780b9679c817b257672ac62164 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 5 Jan 2024 02:19:30 +0800 Subject: [PATCH 02/19] Update generated_cases.c.h Co-Authored-By: Jules <57632293+JuliaPoo@users.noreply.github.com> --- Python/generated_cases.c.h | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index ce31967b7912d7..8226d827cde514 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2371,24 +2371,35 @@ } TARGET(ENTER_EXECUTOR) { - frame->instr_ptr = next_instr; + _Py_CODEUNIT *this_instr = frame->instr_ptr = next_instr; next_instr += 1; INSTRUCTION_STATS(ENTER_EXECUTOR); TIER_ONE_ONLY CHECK_EVAL_BREAKER(); PyCodeObject *code = _PyFrame_GetCode(frame); _PyExecutorObject *executor = (_PyExecutorObject *)code->co_executors->executors[oparg&255]; - Py_INCREF(executor); - if (executor->execute == _PyUOpExecute) { - current_executor = (_PyUOpExecutorObject *)executor; - GOTO_TIER_TWO(); + if (executor->vm_data.valid) { + Py_INCREF(executor); + if (executor->execute == _PyUOpExecute) { + current_executor = (_PyUOpExecutorObject *)executor; + GOTO_TIER_TWO(); + } + next_instr = executor->execute(executor, frame, stack_pointer); + frame = tstate->current_frame; + if (next_instr == NULL) { + goto resume_with_error; + } + stack_pointer = _PyFrame_GetStackPointer(frame); } - next_instr = executor->execute(executor, frame, stack_pointer); - frame = tstate->current_frame; - if (next_instr == NULL) { - goto resume_with_error; + else { + code->co_executors->executors[oparg & 255] = NULL; + opcode = this_instr->op.code = executor->vm_data.opcode; + this_instr->op.arg = executor->vm_data.oparg; + oparg = (oparg & (~255)) | executor->vm_data.oparg; + Py_DECREF(executor); + next_instr = this_instr; + DISPATCH_GOTO(); } - stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH(); } From 37f87cc486bebd5c2c1da145f00c01d5c2d6bef7 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 9 Jan 2024 11:53:14 +0800 Subject: [PATCH 03/19] 1st round of reviews (TODO: black, metadata, doc) --- Include/internal/pycore_opcode_metadata.h | 2 - Python/bytecodes.c | 54 +++++++++---------- Tools/cases_generator/analyzer.py | 16 +++--- Tools/cases_generator/lexer.py | 4 +- .../opcode_metadata_generator.py | 2 +- Tools/cases_generator/parsing.py | 13 ++--- Tools/cases_generator/stack.py | 2 +- 7 files changed, 45 insertions(+), 48 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index b3034db47f1433..4a9941ee8130c9 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1883,7 +1883,6 @@ const uint8_t _PyOpcode_ispure[462] = { [LOAD_FAST] = 1, [LOAD_FAST_AND_CLEAR] = 1, [LOAD_CONST] = 1, - [STORE_FAST] = 1, [POP_TOP] = 1, [PUSH_NULL] = 1, [END_SEND] = 1, @@ -1913,7 +1912,6 @@ const uint8_t _PyOpcode_isguard[462] = { [RESUME_CHECK] = 1, [_GUARD_BOTH_INT] = 1, [_GUARD_BOTH_FLOAT] = 1, - [_GUARD_BOTH_UNICODE] = 1, [_GUARD_GLOBALS_VERSION] = 1, [_GUARD_BUILTINS_VERSION] = 1, [_GUARD_TYPE_VERSION] = 1, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 5a2f727ded59ff..23edec6b6e88e0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -161,7 +161,7 @@ dummy_func( } } - mandatory guard inst(RESUME_CHECK, (--)) { + no_trivial_elimination passthrough inst(RESUME_CHECK, (--)) { #if defined(__EMSCRIPTEN__) DEOPT_IF(_Py_emscripten_signal_clock == 0); _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; @@ -234,7 +234,7 @@ dummy_func( Py_INCREF(value); } - pure inst(STORE_FAST, (value --)) { + inst(STORE_FAST, (value --)) { SETLOCAL(oparg, value); } @@ -411,12 +411,12 @@ dummy_func( // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. }; - guard op(_GUARD_BOTH_INT, (left, right -- left: ~(PYINT_TYPE), right: ~(PYINT_TYPE))) { + passthrough op(_GUARD_BOTH_INT, (left, right -- left: &(PYINT_TYPE), right: &(PYINT_TYPE))) { DEOPT_IF(!PyLong_CheckExact(left)); DEOPT_IF(!PyLong_CheckExact(right)); } - pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res: ~(PYINT_TYPE))) { + pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res: &(PYINT_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -424,7 +424,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - pure op(_BINARY_OP_ADD_INT, (left, right -- res: ~(PYINT_TYPE))) { + pure op(_BINARY_OP_ADD_INT, (left, right -- res: &(PYINT_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -432,7 +432,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res: ~(PYINT_TYPE))) { + pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res: &(PYINT_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -447,12 +447,12 @@ dummy_func( macro(BINARY_OP_SUBTRACT_INT) = _GUARD_BOTH_INT + unused/1 + _BINARY_OP_SUBTRACT_INT; - guard op(_GUARD_BOTH_FLOAT, (left, right -- left: ~(PYFLOAT_TYPE), right: ~(PYFLOAT_TYPE))) { + passthrough op(_GUARD_BOTH_FLOAT, (left, right -- left: &(PYFLOAT_TYPE), right: &(PYFLOAT_TYPE))) { DEOPT_IF(!PyFloat_CheckExact(left)); DEOPT_IF(!PyFloat_CheckExact(right)); } - pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res: ~(PYFLOAT_TYPE))) { + pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res: &(PYFLOAT_TYPE))) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval * @@ -460,7 +460,7 @@ dummy_func( DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res: ~(PYFLOAT_TYPE))) { + pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res: &(PYFLOAT_TYPE))) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval + @@ -468,7 +468,7 @@ dummy_func( DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res: ~(PYFLOAT_TYPE))) { + pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res: &(PYFLOAT_TYPE))) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval - @@ -483,12 +483,12 @@ dummy_func( macro(BINARY_OP_SUBTRACT_FLOAT) = _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_SUBTRACT_FLOAT; - guard op(_GUARD_BOTH_UNICODE, (left, right -- left: ~(PYUNICODE_TYPE), right: ~(PYUNICODE_TYPE))) { + no_trivial_elimination op(_GUARD_BOTH_UNICODE, (left, right -- left: &(PYUNICODE_TYPE), right: &(PYUNICODE_TYPE))) { DEOPT_IF(!PyUnicode_CheckExact(left)); DEOPT_IF(!PyUnicode_CheckExact(right)); } - pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res: ~(PYUNICODE_TYPE))) { + pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res: &(PYUNICODE_TYPE))) { STAT_INC(BINARY_OP, hit); res = PyUnicode_Concat(left, right); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); @@ -1458,14 +1458,14 @@ dummy_func( builtins_version/1 + _LOAD_GLOBAL; - mandatory guard op(_GUARD_GLOBALS_VERSION, (version/1 --)) { + no_trivial_elimination passthrough op(_GUARD_GLOBALS_VERSION, (version/1 --)) { PyDictObject *dict = (PyDictObject *)GLOBALS(); DEOPT_IF(!PyDict_CheckExact(dict)); DEOPT_IF(dict->ma_keys->dk_version != version); assert(DK_IS_UNICODE(dict->ma_keys)); } - mandatory guard op(_GUARD_BUILTINS_VERSION, (version/1 --)) { + no_trivial_elimination passthrough op(_GUARD_BUILTINS_VERSION, (version/1 --)) { PyDictObject *dict = (PyDictObject *)BUILTINS(); DEOPT_IF(!PyDict_CheckExact(dict)); DEOPT_IF(dict->ma_keys->dk_version != version); @@ -1900,13 +1900,13 @@ dummy_func( LOAD_ATTR, }; - guard op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner: ~(GUARD_TYPE_VERSION_TYPE + type_version))) { + passthrough op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner: &(GUARD_TYPE_VERSION_TYPE + type_version))) { PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version); } - mandatory guard op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { + no_trivial_elimination passthrough op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { assert(Py_TYPE(owner)->tp_dictoffset < 0); assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); @@ -2081,7 +2081,7 @@ dummy_func( DISPATCH_INLINED(new_frame); } - guard op(_GUARD_DORV_VALUES, (owner -- owner: ~(GUARD_DORV_VALUES_TYPE))) { + passthrough op(_GUARD_DORV_VALUES, (owner -- owner: &(GUARD_DORV_VALUES_TYPE))) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); @@ -2721,7 +2721,7 @@ dummy_func( DEOPT_IF(r->len <= 0); } - op(_ITER_NEXT_RANGE, (iter -- iter, next: ~(PYINT_TYPE))) { + op(_ITER_NEXT_RANGE, (iter -- iter, next: &(PYINT_TYPE))) { _PyRangeIterObject *r = (_PyRangeIterObject *)iter; assert(Py_TYPE(r) == &PyRangeIter_Type); assert(r->len > 0); @@ -2879,13 +2879,13 @@ dummy_func( exc_info->exc_value = Py_NewRef(new_exc); } - guard op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner: ~(GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE))) { + passthrough op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner: &(GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE))) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); } - guard op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner: ~(GUARD_KEYS_VERSION_TYPE + keys_version))) { + passthrough op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner: &(GUARD_KEYS_VERSION_TYPE + keys_version))) { PyTypeObject *owner_cls = Py_TYPE(owner); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version); @@ -3100,7 +3100,7 @@ dummy_func( macro(CALL) = _SPECIALIZE_CALL + unused/2 + _CALL; - guard op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable: ~(PYMETHOD_TYPE), null: ~(NULL_TYPE), unused[oparg])) { + passthrough op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable: &(PYMETHOD_TYPE), null: &(NULL_TYPE), unused[oparg])) { DEOPT_IF(null != NULL); DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type); } @@ -3114,11 +3114,11 @@ dummy_func( Py_DECREF(callable); } - mandatory guard op(_CHECK_PEP_523, (--)) { + no_trivial_elimination passthrough op(_CHECK_PEP_523, (--)) { DEOPT_IF(tstate->interp->eval_frame); } - guard op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable: ~(PYFUNCTION_TYPE_VERSION_TYPE + func_version), self_or_null, unused[oparg])) { + passthrough op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable: &(PYFUNCTION_TYPE_VERSION_TYPE + func_version), self_or_null, unused[oparg])) { DEOPT_IF(!PyFunction_Check(callable)); PyFunctionObject *func = (PyFunctionObject *)callable; DEOPT_IF(func->func_version != func_version); @@ -3126,14 +3126,14 @@ dummy_func( DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL)); } - mandatory guard op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + no_trivial_elimination passthrough op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { PyFunctionObject *func = (PyFunctionObject *)callable; PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); DEOPT_IF(tstate->py_recursion_remaining <= 1); } - mandatory pure op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) { + no_trivial_elimination pure op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) { int argcount = oparg; if (self_or_null != NULL) { args--; @@ -4042,8 +4042,7 @@ dummy_func( frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + oparg; } - // Not exactly a guard, but not pure either, is just required. - mandatory guard op(_SAVE_RETURN_OFFSET, (--)) { + no_trivial_elimination passthrough op(_SAVE_RETURN_OFFSET, (--)) { #if TIER_ONE frame->return_offset = (uint16_t)(next_instr - this_instr); #endif @@ -4057,7 +4056,6 @@ dummy_func( DEOPT_IF(1); } - op(_CHECK_VALIDITY, (--)) { TIER_TWO_ONLY DEOPT_IF(!current_executor->base.vm_data.valid); diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 3dd1dd8f72d548..dac626124ada38 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -23,8 +23,8 @@ class Properties: has_free: bool pure: bool - guard: bool - mandatory: bool + passthrough: bool + no_trivial_elimination: bool def dump(self, indent: str) -> None: print(indent, end="") @@ -51,8 +51,8 @@ def from_list(properties: list["Properties"]) -> "Properties": has_free=any(p.has_free for p in properties), pure=all(p.pure for p in properties), - guard=all(p.guard for p in properties), - mandatory=any(p.mandatory for p in properties), + passthrough=all(p.passthrough for p in properties), + no_trivial_elimination=any(p.no_trivial_elimination for p in properties), ) @@ -74,8 +74,8 @@ def from_list(properties: list["Properties"]) -> "Properties": has_free=False, pure=False, - guard=False, - mandatory=False, + passthrough=False, + no_trivial_elimination=False, ) @@ -456,8 +456,8 @@ def compute_properties(op: parser.InstDef) -> Properties: has_free=has_free, pure="pure" in op.annotations, - guard="guard" in op.annotations, - mandatory="mandatory" in op.annotations, + passthrough="passthrough" in op.annotations, + no_trivial_elimination="no_trivial_elimination" in op.annotations, ) diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index b81e31f1c955ac..70989c86355a46 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -218,12 +218,12 @@ def choice(*opts: str) -> str: ANNOTATION = "ANNOTATION" annotations = { "specializing", - "guard", + "passthrough", "override", "register", "replaced", "pure", - "mandatory" + "no_trivial_elimination", } __all__ = [] diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py index 210cb0b0252f31..f3361e3554e04c 100644 --- a/Tools/cases_generator/opcode_metadata_generator.py +++ b/Tools/cases_generator/opcode_metadata_generator.py @@ -349,7 +349,7 @@ def generate_tier2_properties(analysis: Analysis, out: CWriter) -> None: out.emit(f"const uint8_t _PyOpcode_isguard[{table_size}] = {{\n") for name, inst in (analysis.instructions | analysis.uops).items(): - if inst.properties.guard: + if inst.properties.passthrough: out.emit(f"[{name}] = 1,\n") out.emit("};\n\n") diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index b7338956ff5664..d594f65b077488 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -71,13 +71,14 @@ class Block(Node): class StackEffect(Node): name: str = field(compare=False) # __eq__ only uses type, cond, size type: str = "" # Optional `:type` - # Optional `(type, aux)` - type_prop: None | tuple[str, None | str] = \ - field(default_factory=lambda: None, init=True, compare=False, hash=False) cond: str = "" # Optional `if (cond)` size: str = "" # Optional `[size]` # Note: size cannot be combined with type or cond + # Optional `(type, aux)` + type_prop: None | tuple[str, None | str] = \ + field(default_factory=lambda: None, init=True, compare=False, hash=False) + def __repr__(self) -> str: items = [self.name, self.type, self.cond, self.size] while items and items[-1] == "": @@ -256,7 +257,7 @@ def cache_effect(self) -> CacheEffect | None: @contextual def stack_effect(self) -> StackEffect | None: - # IDENTIFIER [':' [IDENTIFIER [TIMES]] ['~' '(' IDENTIFIER ['+' IDENTIFIER] ')']] ['if' '(' expression ')'] + # IDENTIFIER [':' [IDENTIFIER [TIMES]] ['&' '(' IDENTIFIER ['+' IDENTIFIER] ')']] ['if' '(' expression ')'] # | IDENTIFIER '[' expression ']' if tkn := self.expect(lx.IDENTIFIER): type_text = "" @@ -266,7 +267,7 @@ def stack_effect(self) -> StackEffect | None: type_text = i.text.strip() if self.expect(lx.TIMES): type_text += " *" - if self.expect(lx.NOT): + if self.expect(lx.AND): self.require(lx.LPAREN) type_prop_text = self.require(lx.IDENTIFIER).text.strip() aux = None @@ -290,7 +291,7 @@ def stack_effect(self) -> StackEffect | None: self.require(lx.RBRACKET) type_text = "PyObject **" size_text = size.text.strip() - return StackEffect(tkn.text, type_text, type_prop, cond_text, size_text) + return StackEffect(tkn.text, type_text, cond_text, size_text, type_prop) return None @contextual diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 7328b09ddc4bb6..08cccabff2f9d4 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -3,7 +3,7 @@ from dataclasses import dataclass from cwriter import CWriter -UNUSED = {"unused", "__unused_"} +UNUSED = {"unused"} def maybe_parenthesize(sym: str) -> str: """Add parentheses around a string if it contains an operator From 577588cb31dcc8661dd226b87162f7c466612b86 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:46:18 +0800 Subject: [PATCH 04/19] fix tests --- Lib/test/test_generated_cases.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 3b2f579be684b7..a865d44c1410c8 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -750,7 +750,7 @@ def test_override_op(self): def test_annotated_inst(self): input = """ - guard inst(OP, (--)) { + passthrough inst(OP, (--)) { ham(); } """ @@ -767,7 +767,7 @@ def test_annotated_inst(self): def test_annotated_op(self): input = """ - guard op(OP, (--)) { + passthrough op(OP, (--)) { spam(); } macro(M) = OP; @@ -784,7 +784,7 @@ def test_annotated_op(self): self.run_cases_test(input, output) input = """ - guard register specializing op(OP, (--)) { + passthrough register specializing op(OP, (--)) { spam(); } macro(M) = OP; From 0c1d0b9bf93340061c4026a7982cfac00c378b5a Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:59:22 +0800 Subject: [PATCH 05/19] use the table instead --- Include/internal/pycore_opcode_metadata.h | 83 ++++--------------- Include/internal/pycore_uop_metadata.h | 68 +++++++-------- Tools/cases_generator/generators_common.py | 8 +- .../opcode_metadata_generator.py | 30 +------ 4 files changed, 61 insertions(+), 128 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 4a9941ee8130c9..bd91dac9c49995 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -909,6 +909,8 @@ enum InstructionFormat { #define HAS_DEOPT_FLAG (128) #define HAS_ERROR_FLAG (256) #define HAS_ESCAPES_FLAG (512) +#define HAS_PURE_FLAG (1024) +#define HAS_PASSTHROUGH_FLAG (2048) #define OPCODE_HAS_ARG(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_ARG_FLAG)) #define OPCODE_HAS_CONST(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_CONST_FLAG)) #define OPCODE_HAS_NAME(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_NAME_FLAG)) @@ -919,6 +921,8 @@ enum InstructionFormat { #define OPCODE_HAS_DEOPT(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_DEOPT_FLAG)) #define OPCODE_HAS_ERROR(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_ERROR_FLAG)) #define OPCODE_HAS_ESCAPES(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_ESCAPES_FLAG)) +#define OPCODE_HAS_PURE(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_PURE_FLAG)) +#define OPCODE_HAS_PASSTHROUGH(OP) (_PyOpcode_opcode_metadata[OP].flags & (HAS_PASSTHROUGH_FLAG)) #define OPARG_FULL 0 #define OPARG_CACHE_1 1 @@ -996,7 +1000,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [COMPARE_OP_STR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [CONTAINS_OP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CONVERT_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, - [COPY] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, + [COPY] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | PURE }, [COPY_FREE_VARS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [DELETE_ATTR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [DELETE_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1007,8 +1011,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [DICT_MERGE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [DICT_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [END_FOR] = { true, INSTR_FMT_IX, 0 }, - [END_SEND] = { true, INSTR_FMT_IX, 0 }, + [END_FOR] = { true, INSTR_FMT_IX, PURE }, + [END_SEND] = { true, INSTR_FMT_IX, PURE }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [EXIT_INIT_CHECK] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [EXTENDED_ARG] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, @@ -1067,10 +1071,10 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG }, + [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | PURE }, [LOAD_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, - [LOAD_FAST_AND_CLEAR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, + [LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE }, + [LOAD_FAST_AND_CLEAR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE }, [LOAD_FAST_CHECK] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG }, [LOAD_FAST_LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, [LOAD_FROM_DICT_OR_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1096,17 +1100,17 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, - [POP_TOP] = { true, INSTR_FMT_IX, 0 }, + [POP_TOP] = { true, INSTR_FMT_IX, PURE }, [PUSH_EXC_INFO] = { true, INSTR_FMT_IX, 0 }, - [PUSH_NULL] = { true, INSTR_FMT_IX, 0 }, + [PUSH_NULL] = { true, INSTR_FMT_IX, PURE }, [RAISE_VARARGS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RERAISE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RESERVED] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, - [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_ESCAPES_FLAG }, + [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG | PASSTHROUGH }, + [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_ESCAPES_FLAG | PURE }, [RETURN_GENERATOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, + [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | PURE }, [SEND] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [SEND_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [SETUP_ANNOTATIONS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1127,7 +1131,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [STORE_SUBSCR] = { true, INSTR_FMT_IXC, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_SUBSCR_DICT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, - [SWAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, + [SWAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | PURE }, [TO_BOOL] = { true, INSTR_FMT_IXC00, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [TO_BOOL_ALWAYS_TRUE] = { true, INSTR_FMT_IXC00, HAS_DEOPT_FLAG }, [TO_BOOL_BOOL] = { true, INSTR_FMT_IXC00, HAS_DEOPT_FLAG }, @@ -1137,7 +1141,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [TO_BOOL_STR] = { true, INSTR_FMT_IXC00, HAS_DEOPT_FLAG }, [UNARY_INVERT] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [UNARY_NEGATIVE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [UNARY_NOT] = { true, INSTR_FMT_IX, 0 }, + [UNARY_NOT] = { true, INSTR_FMT_IX, PURE }, [UNPACK_EX] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [UNPACK_SEQUENCE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [UNPACK_SEQUENCE_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1147,7 +1151,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, [JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_NO_INTERRUPT] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG }, - [LOAD_CLOSURE] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, + [LOAD_CLOSURE] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE }, [LOAD_METHOD] = { true, -1, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_SUPER_METHOD] = { true, -1, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ZERO_SUPER_ATTR] = { true, -1, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1877,57 +1881,6 @@ is_pseudo_target(int pseudo, int target) { return false; } -extern const uint8_t _PyOpcode_ispure[462]; -#ifdef NEED_OPCODE_METADATA -const uint8_t _PyOpcode_ispure[462] = { - [LOAD_FAST] = 1, - [LOAD_FAST_AND_CLEAR] = 1, - [LOAD_CONST] = 1, - [POP_TOP] = 1, - [PUSH_NULL] = 1, - [END_SEND] = 1, - [UNARY_NOT] = 1, - [COPY] = 1, - [SWAP] = 1, - [END_FOR] = 1, - [RETURN_VALUE] = 1, - [RETURN_CONST] = 1, - [_BINARY_OP_MULTIPLY_INT] = 1, - [_BINARY_OP_ADD_INT] = 1, - [_BINARY_OP_SUBTRACT_INT] = 1, - [_BINARY_OP_MULTIPLY_FLOAT] = 1, - [_BINARY_OP_ADD_FLOAT] = 1, - [_BINARY_OP_SUBTRACT_FLOAT] = 1, - [_BINARY_OP_ADD_UNICODE] = 1, - [_POP_FRAME] = 1, - [_INIT_CALL_PY_EXACT_ARGS] = 1, - [_PUSH_FRAME] = 1, -}; - -#endif // NEED_OPCODE_METADATA - -extern const uint8_t _PyOpcode_isguard[462]; -#ifdef NEED_OPCODE_METADATA -const uint8_t _PyOpcode_isguard[462] = { - [RESUME_CHECK] = 1, - [_GUARD_BOTH_INT] = 1, - [_GUARD_BOTH_FLOAT] = 1, - [_GUARD_GLOBALS_VERSION] = 1, - [_GUARD_BUILTINS_VERSION] = 1, - [_GUARD_TYPE_VERSION] = 1, - [_CHECK_MANAGED_OBJECT_HAS_VALUES] = 1, - [_GUARD_DORV_VALUES] = 1, - [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = 1, - [_GUARD_KEYS_VERSION] = 1, - [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = 1, - [_CHECK_PEP_523] = 1, - [_CHECK_FUNCTION_EXACT_ARGS] = 1, - [_CHECK_STACK_SPACE] = 1, - [_SAVE_RETURN_OFFSET] = 1, -}; - -#endif // NEED_OPCODE_METADATA - #ifdef __cplusplus } diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index c20829ca002c33..96ba1a097439e9 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -17,20 +17,20 @@ extern const char * const _PyOpcode_uop_name[MAX_UOP_ID+1]; #ifdef NEED_OPCODE_METADATA const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_NOP] = 0, - [_RESUME_CHECK] = HAS_DEOPT_FLAG, + [_RESUME_CHECK] = HAS_DEOPT_FLAG | PASSTHROUGH, [_LOAD_FAST_CHECK] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG, - [_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, + [_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE, + [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE, [_LOAD_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_LOAD_CONST] = HAS_ARG_FLAG | HAS_CONST_FLAG, + [_LOAD_CONST] = HAS_ARG_FLAG | HAS_CONST_FLAG | PURE, [_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_STORE_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_STORE_FAST_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_POP_TOP] = 0, - [_PUSH_NULL] = 0, - [_END_SEND] = 0, + [_POP_TOP] = PURE, + [_PUSH_NULL] = PURE, + [_END_SEND] = PURE, [_UNARY_NEGATIVE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_UNARY_NOT] = 0, + [_UNARY_NOT] = PURE, [_TO_BOOL] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_TO_BOOL_BOOL] = HAS_DEOPT_FLAG, [_TO_BOOL_INT] = HAS_DEOPT_FLAG, @@ -39,16 +39,16 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_TO_BOOL_STR] = HAS_DEOPT_FLAG, [_TO_BOOL_ALWAYS_TRUE] = HAS_DEOPT_FLAG, [_UNARY_INVERT] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_GUARD_BOTH_INT] = HAS_DEOPT_FLAG, - [_BINARY_OP_MULTIPLY_INT] = HAS_ERROR_FLAG, - [_BINARY_OP_ADD_INT] = HAS_ERROR_FLAG, - [_BINARY_OP_SUBTRACT_INT] = HAS_ERROR_FLAG, - [_GUARD_BOTH_FLOAT] = HAS_DEOPT_FLAG, - [_BINARY_OP_MULTIPLY_FLOAT] = 0, - [_BINARY_OP_ADD_FLOAT] = 0, - [_BINARY_OP_SUBTRACT_FLOAT] = 0, + [_GUARD_BOTH_INT] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_BINARY_OP_MULTIPLY_INT] = HAS_ERROR_FLAG | PURE, + [_BINARY_OP_ADD_INT] = HAS_ERROR_FLAG | PURE, + [_BINARY_OP_SUBTRACT_INT] = HAS_ERROR_FLAG | PURE, + [_GUARD_BOTH_FLOAT] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_BINARY_OP_MULTIPLY_FLOAT] = PURE, + [_BINARY_OP_ADD_FLOAT] = PURE, + [_BINARY_OP_SUBTRACT_FLOAT] = PURE, [_GUARD_BOTH_UNICODE] = HAS_DEOPT_FLAG, - [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, + [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | PURE, [_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -64,7 +64,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_DELETE_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_INTRINSIC_1] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_INTRINSIC_2] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_POP_FRAME] = HAS_ESCAPES_FLAG, + [_POP_FRAME] = HAS_ESCAPES_FLAG | PURE, [_GET_AITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_ANEXT] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_AWAITABLE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -86,8 +86,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_FROM_DICT_OR_GLOBALS] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_GLOBAL] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_GUARD_GLOBALS_VERSION] = HAS_DEOPT_FLAG, - [_GUARD_BUILTINS_VERSION] = HAS_DEOPT_FLAG, + [_GUARD_GLOBALS_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_GUARD_BUILTINS_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, [_LOAD_GLOBAL_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_LOAD_GLOBAL_BUILTINS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_DELETE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG, @@ -112,8 +112,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_SUPER_ATTR_ATTR] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_SUPER_ATTR_METHOD] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_GUARD_TYPE_VERSION] = HAS_DEOPT_FLAG, - [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG, + [_GUARD_TYPE_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG | PASSTHROUGH, [_LOAD_ATTR_INSTANCE_VALUE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CHECK_ATTR_MODULE] = HAS_DEOPT_FLAG, [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, @@ -122,7 +122,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_SLOT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CHECK_ATTR_CLASS] = HAS_DEOPT_FLAG, [_LOAD_ATTR_CLASS] = HAS_ARG_FLAG, - [_GUARD_DORV_VALUES] = HAS_DEOPT_FLAG, + [_GUARD_DORV_VALUES] = HAS_DEOPT_FLAG | PASSTHROUGH, [_STORE_ATTR_INSTANCE_VALUE] = HAS_ESCAPES_FLAG, [_STORE_ATTR_SLOT] = HAS_ESCAPES_FLAG, [_COMPARE_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -155,21 +155,21 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BEFORE_WITH] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_WITH_EXCEPT_START] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_PUSH_EXC_INFO] = 0, - [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = HAS_DEOPT_FLAG, - [_GUARD_KEYS_VERSION] = HAS_DEOPT_FLAG, + [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_GUARD_KEYS_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, [_LOAD_ATTR_METHOD_WITH_VALUES] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR_METHOD_NO_DICT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = HAS_ARG_FLAG, [_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = HAS_ARG_FLAG, [_CHECK_ATTR_METHOD_LAZY_DICT] = HAS_DEOPT_FLAG, [_LOAD_ATTR_METHOD_LAZY_DICT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, - [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | PASSTHROUGH, [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG, - [_CHECK_PEP_523] = HAS_DEOPT_FLAG, - [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, - [_PUSH_FRAME] = 0, + [_CHECK_PEP_523] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | PASSTHROUGH, + [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | PASSTHROUGH, + [_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG | PURE, + [_PUSH_FRAME] = PURE, [_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CALL_STR_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_TUPLE_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -190,16 +190,16 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CONVERT_VALUE] = HAS_ARG_FLAG | HAS_ERROR_FLAG, [_FORMAT_SIMPLE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_FORMAT_WITH_SPEC] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_COPY] = HAS_ARG_FLAG, + [_COPY] = HAS_ARG_FLAG | PURE, [_BINARY_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG, - [_SWAP] = HAS_ARG_FLAG, + [_SWAP] = HAS_ARG_FLAG | PURE, [_GUARD_IS_TRUE_POP] = HAS_DEOPT_FLAG, [_GUARD_IS_FALSE_POP] = HAS_DEOPT_FLAG, [_GUARD_IS_NONE_POP] = HAS_DEOPT_FLAG, [_GUARD_IS_NOT_NONE_POP] = HAS_DEOPT_FLAG, [_JUMP_TO_TOP] = HAS_EVAL_BREAK_FLAG, [_SET_IP] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, - [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG, + [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG | PASSTHROUGH, [_EXIT_TRACE] = HAS_DEOPT_FLAG, [_CHECK_VALIDITY] = HAS_DEOPT_FLAG, }; diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index 5a42a05c5c2ef2..c9e849884e1439 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -26,7 +26,9 @@ def root_relative_path(filename: str) -> str: return filename -def write_header(generator: str, sources: list[str], outfile: TextIO, comment: str = "//") -> None: +def write_header( + generator: str, sources: list[str], outfile: TextIO, comment: str = "//" +) -> None: outfile.write( f"""{comment} This file is generated by {root_relative_path(generator)} {comment} from: @@ -209,6 +211,10 @@ def cflags(p: Properties) -> str: flags.append("HAS_ERROR_FLAG") if p.escapes: flags.append("HAS_ESCAPES_FLAG") + if p.pure: + flags.append("PURE") + if p.passthrough: + flags.append("PASSTHROUGH") if flags: return " | ".join(flags) else: diff --git a/Tools/cases_generator/opcode_metadata_generator.py b/Tools/cases_generator/opcode_metadata_generator.py index f3361e3554e04c..1826a0b645c3b8 100644 --- a/Tools/cases_generator/opcode_metadata_generator.py +++ b/Tools/cases_generator/opcode_metadata_generator.py @@ -50,6 +50,8 @@ "DEOPT", "ERROR", "ESCAPES", + "PURE", + "PASSTHROUGH", ] @@ -330,32 +332,6 @@ def generate_pseudo_targets(analysis: Analysis, out: CWriter) -> None: out.emit("}\n\n") -def generate_tier2_properties(analysis: Analysis, out: CWriter) -> None: - table_size = len(analysis.instructions) + len(analysis.uops) - - out.emit(f"extern const uint8_t _PyOpcode_ispure[{table_size}];\n") - out.emit("#ifdef NEED_OPCODE_METADATA\n") - out.emit(f"const uint8_t _PyOpcode_ispure[{table_size}] = {{\n") - - for name, inst in (analysis.instructions | analysis.uops).items(): - if inst.properties.pure: - out.emit(f"[{name}] = 1,\n") - - out.emit("};\n\n") - out.emit("#endif // NEED_OPCODE_METADATA\n\n") - - out.emit(f"extern const uint8_t _PyOpcode_isguard[{table_size}];\n") - out.emit("#ifdef NEED_OPCODE_METADATA\n") - out.emit(f"const uint8_t _PyOpcode_isguard[{table_size}] = {{\n") - - for name, inst in (analysis.instructions | analysis.uops).items(): - if inst.properties.passthrough: - out.emit(f"[{name}] = 1,\n") - - out.emit("};\n\n") - out.emit("#endif // NEED_OPCODE_METADATA\n\n") - - def generate_opcode_metadata( filenames: list[str], analysis: Analysis, outfile: TextIO ) -> None: @@ -384,8 +360,6 @@ def generate_opcode_metadata( generate_deopt_table(analysis, out) generate_extra_cases(analysis, out) generate_pseudo_targets(analysis, out) - generate_tier2_properties(analysis, out) - arg_parser = argparse.ArgumentParser( From f8c5cd1ed4b807460885e9c1a7e98a5ad780e013 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:02:00 +0800 Subject: [PATCH 06/19] run black --- Lib/test/test_generated_cases.py | 19 ++++++++++--------- Tools/cases_generator/analyzer.py | 17 +++++++---------- Tools/cases_generator/lexer.py | 4 +++- Tools/cases_generator/parsing.py | 15 ++++++++++----- Tools/cases_generator/stack.py | 7 +++---- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index a865d44c1410c8..a9cc10a684d501 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -9,7 +9,7 @@ def skip_if_different_mount_drives(): - if sys.platform != 'win32': + if sys.platform != "win32": return ROOT = os.path.dirname(os.path.dirname(__file__)) root_drive = os.path.splitroot(ROOT)[0] @@ -22,11 +22,13 @@ def skip_if_different_mount_drives(): f"directory have different mount drives " f"({cwd_drive} and {root_drive})" ) + + skip_if_different_mount_drives() -test_tools.skip_if_missing('cases_generator') -with test_tools.imports_under_tool('cases_generator'): +test_tools.skip_if_missing("cases_generator") +with test_tools.imports_under_tool("cases_generator"): from analyzer import StackItem import parser from stack import Stack @@ -39,13 +41,14 @@ def handle_stderr(): else: return support.captured_stderr() + class TestEffects(unittest.TestCase): def test_effect_sizes(self): stack = Stack() inputs = [ - x:= StackItem("x", None, "", "1"), - y:= StackItem("y", None, "", "oparg"), - z:= StackItem("z", None, "", "oparg*2"), + x := StackItem("x", None, "", "1"), + y := StackItem("y", None, "", "oparg"), + z := StackItem("z", None, "", "oparg*2"), ] outputs = [ StackItem("x", None, "", "1"), @@ -96,9 +99,7 @@ def run_cases_test(self, input: str, expected: str): with handle_stderr(): tier1_generator.generate_tier1_from_files( - [self.temp_input_filename], - self.temp_output_filename, - False + [self.temp_input_filename], self.temp_output_filename, False ) with open(self.temp_output_filename) as temp_output: diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index dac626124ada38..b6c6f5ee866cd8 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -49,7 +49,6 @@ def from_list(properties: list["Properties"]) -> "Properties": uses_co_names=any(p.uses_co_names for p in properties), uses_locals=any(p.uses_locals for p in properties), has_free=any(p.has_free for p in properties), - pure=all(p.pure for p in properties), passthrough=all(p.passthrough for p in properties), no_trivial_elimination=any(p.no_trivial_elimination for p in properties), @@ -72,7 +71,6 @@ def from_list(properties: list["Properties"]) -> "Properties": uses_co_names=False, uses_locals=False, has_free=False, - pure=False, passthrough=False, no_trivial_elimination=False, @@ -100,8 +98,9 @@ class StackItem: condition: str | None size: str peek: bool = False - type_prop: None | tuple[str, None | str] = \ - field(default_factory=lambda: None, init=True, compare=False, hash=False) + type_prop: None | tuple[str, None | str] = field( + default_factory=lambda: None, init=True, compare=False, hash=False + ) def __str__(self) -> str: cond = f" if ({self.condition})" if self.condition else "" @@ -273,7 +272,9 @@ def override_error( def convert_stack_item(item: parser.StackEffect) -> StackItem: - return StackItem(item.name, item.type, item.cond, (item.size or "1"), type_prop=item.type_prop) + return StackItem( + item.name, item.type, item.cond, (item.size or "1"), type_prop=item.type_prop + ) def analyze_stack(op: parser.InstDef) -> StackEffect: @@ -391,7 +392,6 @@ def makes_escaping_api_call(instr: parser.InstDef) -> bool: return False - EXITS = { "DISPATCH", "GO_TO_INSTRUCTION", @@ -454,7 +454,6 @@ def compute_properties(op: parser.InstDef) -> Properties: uses_locals=(variable_used(op, "GETLOCAL") or variable_used(op, "SETLOCAL")) and not has_free, has_free=has_free, - pure="pure" in op.annotations, passthrough="passthrough" in op.annotations, no_trivial_elimination="no_trivial_elimination" in op.annotations, @@ -704,9 +703,7 @@ def analyze_forest(forest: list[parser.AstNode]) -> Analysis: inst = instructions["BINARY_OP_INPLACE_ADD_UNICODE"] inst.family = families["BINARY_OP"] families["BINARY_OP"].members.append(inst) - opmap, first_arg, min_instrumented = assign_opcodes( - instructions, families, pseudos - ) + opmap, first_arg, min_instrumented = assign_opcodes(instructions, families, pseudos) return Analysis( instructions, uops, families, pseudos, opmap, first_arg, min_instrumented ) diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index 70989c86355a46..6f21a9f529933a 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -332,7 +332,9 @@ def tokenize(src: str, line: int = 1, filename: str = "") -> Iterator[Token]: else: begin = line, start - linestart if kind != "\n": - yield Token(filename, kind, text, begin, (line, start - linestart + len(text))) + yield Token( + filename, kind, text, begin, (line, start - linestart + len(text)) + ) def to_text(tkns: list[Token], dedent: int = 0) -> str: diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index d594f65b077488..6dab32e36235d4 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -76,8 +76,9 @@ class StackEffect(Node): # Note: size cannot be combined with type or cond # Optional `(type, aux)` - type_prop: None | tuple[str, None | str] = \ - field(default_factory=lambda: None, init=True, compare=False, hash=False) + type_prop: None | tuple[str, None | str] = field( + default_factory=lambda: None, init=True, compare=False, hash=False + ) def __repr__(self) -> str: items = [self.name, self.type, self.cond, self.size] @@ -142,11 +143,13 @@ class Family(Node): @dataclass class Pseudo(Node): name: str - flags: list[str] # instr flags to set on the pseudo instruction - targets: list[str] # opcodes this can be replaced by + flags: list[str] # instr flags to set on the pseudo instruction + targets: list[str] # opcodes this can be replaced by + AstNode = InstDef | Macro | Pseudo | Family + class Parser(PLexer): @contextual def definition(self) -> AstNode | None: @@ -378,7 +381,9 @@ def family_def(self) -> Family | None: if self.expect(lx.COMMA): if not (size := self.expect(lx.IDENTIFIER)): if not (size := self.expect(lx.NUMBER)): - raise self.make_syntax_error("Expected identifier or number") + raise self.make_syntax_error( + "Expected identifier or number" + ) if self.expect(lx.RPAREN): if self.expect(lx.EQUALS): if not self.expect(lx.LBRACE): diff --git a/Tools/cases_generator/stack.py b/Tools/cases_generator/stack.py index 08cccabff2f9d4..6633950aada002 100644 --- a/Tools/cases_generator/stack.py +++ b/Tools/cases_generator/stack.py @@ -5,6 +5,7 @@ UNUSED = {"unused"} + def maybe_parenthesize(sym: str) -> str: """Add parentheses around a string if it contains an operator and is not already parenthesized. @@ -30,6 +31,7 @@ def var_size(var: StackItem) -> str: else: return var.size + @dataclass class StackOffset: "The stack offset of the virtual base of the stack from the physical stack pointer" @@ -48,10 +50,7 @@ def push(self, item: StackItem) -> None: self.pushed.append(var_size(item)) def __sub__(self, other: "StackOffset") -> "StackOffset": - return StackOffset( - self.popped + other.pushed, - self.pushed + other.popped - ) + return StackOffset(self.popped + other.pushed, self.pushed + other.popped) def __neg__(self) -> "StackOffset": return StackOffset(self.pushed, self.popped) From 2d23474004f74100e7492ae65a736c9b2633cfd2 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:15:43 +0800 Subject: [PATCH 07/19] documentation --- .../cases_generator/interpreter_definition.md | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index 5c4238756748a7..bd8d64040256f5 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -15,6 +15,7 @@ These tools would be used to: * Generate the tier 2 interpreter * Generate documentation for instructions * Generate metadata about instructions, such as stack use (done). +* Generate the tier 2 optimizer's abstract interpreter. Having a single definition file ensures that there is a single source of truth for bytecode semantics. @@ -108,7 +109,10 @@ and a piece of C code describing its semantics:: NAME [":" type] [ "if" "(" C-expression ")" ] type: - NAME ["*"] + NAME ["*"] | type_prop + + type_prop: + "&" "(" NAME ["+" NAME] ")" stream: NAME "/" size @@ -138,7 +142,9 @@ The following definitions may occur: The optional `type` in an `object` is the C type. It defaults to `PyObject *`. The objects before the "--" are the objects on top of the stack at the start of the instruction. Those after the "--" are the objects on top of the stack at the -end of the instruction. +end of the instruction. When prefixed by a `&`, the type rule follows the +`type_prop` rule. This indicates the stack effect is of that specific type +after the operation. An `inst` without `stack_effect` is a transitional form to allow the original C code definitions to be copied. It lacks information to generate anything other than the @@ -158,6 +164,21 @@ By convention cache effects (`stream`) must precede the input effects. The name `oparg` is pre-defined as a 32 bit value fetched from the instruction stream. +### Special instruction annotations + +Instruction headers may be prefixed by one or more annotations. The non-exhaustive +list of annotations and their meanings are as follows: + +* `override`. For external use by other interpreter definitions to override the current + instruction definition. +* `pure`. This instruction has no side effects visible to the Python user. It may + still have side effects on the CPython interpreter state. All other instructions without + this annotation are assumed to be not pure. +* `passthrough`. This instruction does not modify its stack inputs/outputs. + I.e. the stack operands "pass through". +* `no_trivial_elimination`. This tells the Tier 2 optimizer's abstract interpreter that + this instruction cannot be eliminated trivially via constant or type propagation. + ### Special functions/macros The C code may include special functions that are understood by the tools as From 9a7ded007264d815d79639646340c1369662bfa6 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 9 Jan 2024 21:19:23 +0800 Subject: [PATCH 08/19] talk auxillary --- Tools/cases_generator/interpreter_definition.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index bd8d64040256f5..90a389cb2575b9 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -144,7 +144,9 @@ The objects before the "--" are the objects on top of the stack at the start of the instruction. Those after the "--" are the objects on top of the stack at the end of the instruction. When prefixed by a `&`, the type rule follows the `type_prop` rule. This indicates the stack effect is of that specific type -after the operation. +after the operation. In this case, the type may also contain auxillary information +that is fetched from a previously defined operand in the instruction header, such as +a type version tag. This follows the format `type + auxillary`. An `inst` without `stack_effect` is a transitional form to allow the original C code definitions to be copied. It lacks information to generate anything other than the From bca395c76b79ca6a09da370d4321f4f0c2b45712 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 23:01:03 +0000 Subject: [PATCH 09/19] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2024-01-09-23-01-00.gh-issue-113710.pe3flY.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2024-01-09-23-01-00.gh-issue-113710.pe3flY.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-01-09-23-01-00.gh-issue-113710.pe3flY.rst b/Misc/NEWS.d/next/Core and Builtins/2024-01-09-23-01-00.gh-issue-113710.pe3flY.rst new file mode 100644 index 00000000000000..cffc48bfa13639 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2024-01-09-23-01-00.gh-issue-113710.pe3flY.rst @@ -0,0 +1 @@ +Add typed stack effects to the interpreter DSL, along with various instruction annotations. From 33c3bbde8c2867202630ad27edd19674148c000d Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 10 Jan 2024 07:46:28 +0800 Subject: [PATCH 10/19] correct name --- Include/internal/pycore_opcode_metadata.h | 28 ++++----- Include/internal/pycore_uop_metadata.h | 68 +++++++++++----------- Tools/cases_generator/generators_common.py | 4 +- 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index bd91dac9c49995..c226411ccdfaaa 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1000,7 +1000,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [COMPARE_OP_STR] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [CONTAINS_OP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [CONVERT_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, - [COPY] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | PURE }, + [COPY] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_PURE_FLAG }, [COPY_FREE_VARS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [DELETE_ATTR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [DELETE_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1011,8 +1011,8 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [DICT_MERGE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [DICT_UPDATE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [END_ASYNC_FOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [END_FOR] = { true, INSTR_FMT_IX, PURE }, - [END_SEND] = { true, INSTR_FMT_IX, PURE }, + [END_FOR] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, + [END_SEND] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [EXIT_INIT_CHECK] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [EXTENDED_ARG] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, @@ -1071,10 +1071,10 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [LOAD_ATTR_SLOT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [LOAD_ATTR_WITH_HINT] = { true, INSTR_FMT_IBC00000000, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [LOAD_BUILD_CLASS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | PURE }, + [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG }, [LOAD_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE }, - [LOAD_FAST_AND_CLEAR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE }, + [LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG }, + [LOAD_FAST_AND_CLEAR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG }, [LOAD_FAST_CHECK] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG }, [LOAD_FAST_LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, [LOAD_FROM_DICT_OR_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1100,17 +1100,17 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, - [POP_TOP] = { true, INSTR_FMT_IX, PURE }, + [POP_TOP] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [PUSH_EXC_INFO] = { true, INSTR_FMT_IX, 0 }, - [PUSH_NULL] = { true, INSTR_FMT_IX, PURE }, + [PUSH_NULL] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [RAISE_VARARGS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RERAISE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RESERVED] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG | PASSTHROUGH }, - [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_ESCAPES_FLAG | PURE }, + [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG }, + [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, [RETURN_GENERATOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | PURE }, + [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, [SEND] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [SEND_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [SETUP_ANNOTATIONS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, @@ -1131,7 +1131,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [STORE_SUBSCR] = { true, INSTR_FMT_IXC, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_SUBSCR_DICT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [STORE_SUBSCR_LIST_INT] = { true, INSTR_FMT_IXC, HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, - [SWAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | PURE }, + [SWAP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_PURE_FLAG }, [TO_BOOL] = { true, INSTR_FMT_IXC00, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [TO_BOOL_ALWAYS_TRUE] = { true, INSTR_FMT_IXC00, HAS_DEOPT_FLAG }, [TO_BOOL_BOOL] = { true, INSTR_FMT_IXC00, HAS_DEOPT_FLAG }, @@ -1141,7 +1141,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [TO_BOOL_STR] = { true, INSTR_FMT_IXC00, HAS_DEOPT_FLAG }, [UNARY_INVERT] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [UNARY_NEGATIVE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [UNARY_NOT] = { true, INSTR_FMT_IX, PURE }, + [UNARY_NOT] = { true, INSTR_FMT_IX, HAS_PURE_FLAG }, [UNPACK_EX] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [UNPACK_SEQUENCE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [UNPACK_SEQUENCE_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, @@ -1151,7 +1151,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, [JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_NO_INTERRUPT] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG }, - [LOAD_CLOSURE] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE }, + [LOAD_CLOSURE] = { true, -1, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG }, [LOAD_METHOD] = { true, -1, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_SUPER_METHOD] = { true, -1, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_ZERO_SUPER_ATTR] = { true, -1, HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 96ba1a097439e9..4264c3401d64ae 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -17,20 +17,20 @@ extern const char * const _PyOpcode_uop_name[MAX_UOP_ID+1]; #ifdef NEED_OPCODE_METADATA const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_NOP] = 0, - [_RESUME_CHECK] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_RESUME_CHECK] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_FAST_CHECK] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG, - [_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE, - [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | PURE, + [_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG, + [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG, [_LOAD_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_LOAD_CONST] = HAS_ARG_FLAG | HAS_CONST_FLAG | PURE, + [_LOAD_CONST] = HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG, [_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_STORE_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_STORE_FAST_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, - [_POP_TOP] = PURE, - [_PUSH_NULL] = PURE, - [_END_SEND] = PURE, + [_POP_TOP] = HAS_PURE_FLAG, + [_PUSH_NULL] = HAS_PURE_FLAG, + [_END_SEND] = HAS_PURE_FLAG, [_UNARY_NEGATIVE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_UNARY_NOT] = PURE, + [_UNARY_NOT] = HAS_PURE_FLAG, [_TO_BOOL] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_TO_BOOL_BOOL] = HAS_DEOPT_FLAG, [_TO_BOOL_INT] = HAS_DEOPT_FLAG, @@ -39,16 +39,16 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_TO_BOOL_STR] = HAS_DEOPT_FLAG, [_TO_BOOL_ALWAYS_TRUE] = HAS_DEOPT_FLAG, [_UNARY_INVERT] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_GUARD_BOTH_INT] = HAS_DEOPT_FLAG | PASSTHROUGH, - [_BINARY_OP_MULTIPLY_INT] = HAS_ERROR_FLAG | PURE, - [_BINARY_OP_ADD_INT] = HAS_ERROR_FLAG | PURE, - [_BINARY_OP_SUBTRACT_INT] = HAS_ERROR_FLAG | PURE, - [_GUARD_BOTH_FLOAT] = HAS_DEOPT_FLAG | PASSTHROUGH, - [_BINARY_OP_MULTIPLY_FLOAT] = PURE, - [_BINARY_OP_ADD_FLOAT] = PURE, - [_BINARY_OP_SUBTRACT_FLOAT] = PURE, + [_GUARD_BOTH_INT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_BINARY_OP_MULTIPLY_INT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, + [_BINARY_OP_ADD_INT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, + [_BINARY_OP_SUBTRACT_INT] = HAS_ERROR_FLAG | HAS_PURE_FLAG, + [_GUARD_BOTH_FLOAT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_BINARY_OP_MULTIPLY_FLOAT] = HAS_PURE_FLAG, + [_BINARY_OP_ADD_FLOAT] = HAS_PURE_FLAG, + [_BINARY_OP_SUBTRACT_FLOAT] = HAS_PURE_FLAG, [_GUARD_BOTH_UNICODE] = HAS_DEOPT_FLAG, - [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | PURE, + [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_STORE_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -64,7 +64,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_DELETE_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_INTRINSIC_1] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_INTRINSIC_2] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_POP_FRAME] = HAS_ESCAPES_FLAG | PURE, + [_POP_FRAME] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_GET_AITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_ANEXT] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_AWAITABLE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -86,8 +86,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_FROM_DICT_OR_GLOBALS] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_GLOBAL] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_GUARD_GLOBALS_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, - [_GUARD_BUILTINS_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_GUARD_GLOBALS_VERSION] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_BUILTINS_VERSION] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_GLOBAL_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_LOAD_GLOBAL_BUILTINS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_DELETE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG, @@ -112,8 +112,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_SUPER_ATTR_ATTR] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_SUPER_ATTR_METHOD] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_GUARD_TYPE_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, - [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_GUARD_TYPE_VERSION] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_INSTANCE_VALUE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CHECK_ATTR_MODULE] = HAS_DEOPT_FLAG, [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, @@ -122,7 +122,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_SLOT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CHECK_ATTR_CLASS] = HAS_DEOPT_FLAG, [_LOAD_ATTR_CLASS] = HAS_ARG_FLAG, - [_GUARD_DORV_VALUES] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_GUARD_DORV_VALUES] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_STORE_ATTR_INSTANCE_VALUE] = HAS_ESCAPES_FLAG, [_STORE_ATTR_SLOT] = HAS_ESCAPES_FLAG, [_COMPARE_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -155,21 +155,21 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BEFORE_WITH] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_WITH_EXCEPT_START] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_PUSH_EXC_INFO] = 0, - [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = HAS_DEOPT_FLAG | PASSTHROUGH, - [_GUARD_KEYS_VERSION] = HAS_DEOPT_FLAG | PASSTHROUGH, + [_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_KEYS_VERSION] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_METHOD_WITH_VALUES] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR_METHOD_NO_DICT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = HAS_ARG_FLAG, [_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = HAS_ARG_FLAG, [_CHECK_ATTR_METHOD_LAZY_DICT] = HAS_DEOPT_FLAG, [_LOAD_ATTR_METHOD_LAZY_DICT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, - [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | PASSTHROUGH, + [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG, - [_CHECK_PEP_523] = HAS_DEOPT_FLAG | PASSTHROUGH, - [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | PASSTHROUGH, - [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | PASSTHROUGH, - [_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG | PURE, - [_PUSH_FRAME] = PURE, + [_CHECK_PEP_523] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG, + [_PUSH_FRAME] = HAS_PURE_FLAG, [_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CALL_STR_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_TUPLE_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -190,16 +190,16 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CONVERT_VALUE] = HAS_ARG_FLAG | HAS_ERROR_FLAG, [_FORMAT_SIMPLE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_FORMAT_WITH_SPEC] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_COPY] = HAS_ARG_FLAG | PURE, + [_COPY] = HAS_ARG_FLAG | HAS_PURE_FLAG, [_BINARY_OP] = HAS_ARG_FLAG | HAS_ERROR_FLAG, - [_SWAP] = HAS_ARG_FLAG | PURE, + [_SWAP] = HAS_ARG_FLAG | HAS_PURE_FLAG, [_GUARD_IS_TRUE_POP] = HAS_DEOPT_FLAG, [_GUARD_IS_FALSE_POP] = HAS_DEOPT_FLAG, [_GUARD_IS_NONE_POP] = HAS_DEOPT_FLAG, [_GUARD_IS_NOT_NONE_POP] = HAS_DEOPT_FLAG, [_JUMP_TO_TOP] = HAS_EVAL_BREAK_FLAG, [_SET_IP] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, - [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG | PASSTHROUGH, + [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG | HAS_PASSTHROUGH_FLAG, [_EXIT_TRACE] = HAS_DEOPT_FLAG, [_CHECK_VALIDITY] = HAS_DEOPT_FLAG, }; diff --git a/Tools/cases_generator/generators_common.py b/Tools/cases_generator/generators_common.py index c9e849884e1439..c6c602c7122b41 100644 --- a/Tools/cases_generator/generators_common.py +++ b/Tools/cases_generator/generators_common.py @@ -212,9 +212,9 @@ def cflags(p: Properties) -> str: if p.escapes: flags.append("HAS_ESCAPES_FLAG") if p.pure: - flags.append("PURE") + flags.append("HAS_PURE_FLAG") if p.passthrough: - flags.append("PASSTHROUGH") + flags.append("HAS_PASSTHROUGH_FLAG") if flags: return " | ".join(flags) else: From f3bf2129cca1d041752d784c7c93dfdccc70dbe7 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Wed, 10 Jan 2024 07:49:04 +0800 Subject: [PATCH 11/19] LOAD_FAST_AND_CLEAR is not pure --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_metadata.h | 2 +- Python/bytecodes.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index c226411ccdfaaa..dc03a0ffd903eb 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1074,7 +1074,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [LOAD_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG }, [LOAD_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG }, - [LOAD_FAST_AND_CLEAR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG }, + [LOAD_FAST_AND_CLEAR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, [LOAD_FAST_CHECK] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG }, [LOAD_FAST_LOAD_FAST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_LOCAL_FLAG }, [LOAD_FROM_DICT_OR_DEREF] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_FREE_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 4264c3401d64ae..5905997b83819d 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -20,7 +20,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_RESUME_CHECK] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_FAST_CHECK] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG, [_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG, - [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG, + [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_LOAD_FAST_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, [_LOAD_CONST] = HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_PURE_FLAG, [_STORE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 23edec6b6e88e0..847f1016e5ef03 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -214,7 +214,7 @@ dummy_func( Py_INCREF(value); } - pure inst(LOAD_FAST_AND_CLEAR, (-- value)) { + inst(LOAD_FAST_AND_CLEAR, (-- value)) { value = GETLOCAL(oparg); // do not use SETLOCAL here, it decrefs the old value GETLOCAL(oparg) = NULL; From c6f2d84a3e786a445593fe98ccda153318fdc072 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 11 Jan 2024 02:36:53 +0800 Subject: [PATCH 12/19] get rid of passthrough and no_trivial_elimination --- Include/internal/pycore_opcode_metadata.h | 2 +- Include/internal/pycore_uop_metadata.h | 34 +++++++++---------- Lib/test/test_generated_cases.py | 4 +-- Python/bytecodes.c | 34 +++++++++---------- Tools/cases_generator/analyzer.py | 20 ++++++++--- .../cases_generator/interpreter_definition.md | 4 --- Tools/cases_generator/lexer.py | 2 -- 7 files changed, 52 insertions(+), 48 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index dc03a0ffd903eb..fe41efe214aea9 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1107,7 +1107,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [RERAISE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RESERVED] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG }, + [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, [RETURN_GENERATOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index 5905997b83819d..cc79a30591410e 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -17,7 +17,7 @@ extern const char * const _PyOpcode_uop_name[MAX_UOP_ID+1]; #ifdef NEED_OPCODE_METADATA const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_NOP] = 0, - [_RESUME_CHECK] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_RESUME_CHECK] = HAS_DEOPT_FLAG, [_LOAD_FAST_CHECK] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG, [_LOAD_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_PURE_FLAG, [_LOAD_FAST_AND_CLEAR] = HAS_ARG_FLAG | HAS_LOCAL_FLAG, @@ -32,7 +32,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_UNARY_NEGATIVE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_UNARY_NOT] = HAS_PURE_FLAG, [_TO_BOOL] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_TO_BOOL_BOOL] = HAS_DEOPT_FLAG, + [_TO_BOOL_BOOL] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_TO_BOOL_INT] = HAS_DEOPT_FLAG, [_TO_BOOL_LIST] = HAS_DEOPT_FLAG, [_TO_BOOL_NONE] = HAS_DEOPT_FLAG, @@ -47,7 +47,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_BINARY_OP_MULTIPLY_FLOAT] = HAS_PURE_FLAG, [_BINARY_OP_ADD_FLOAT] = HAS_PURE_FLAG, [_BINARY_OP_SUBTRACT_FLOAT] = HAS_PURE_FLAG, - [_GUARD_BOTH_UNICODE] = HAS_DEOPT_FLAG, + [_GUARD_BOTH_UNICODE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_BINARY_OP_ADD_UNICODE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG, [_BINARY_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BINARY_SLICE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -86,8 +86,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_FROM_DICT_OR_GLOBALS] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_NAME] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_LOAD_GLOBAL] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_GUARD_GLOBALS_VERSION] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, - [_GUARD_BUILTINS_VERSION] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_GLOBALS_VERSION] = HAS_DEOPT_FLAG, + [_GUARD_BUILTINS_VERSION] = HAS_DEOPT_FLAG, [_LOAD_GLOBAL_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_LOAD_GLOBAL_BUILTINS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_DELETE_FAST] = HAS_ARG_FLAG | HAS_LOCAL_FLAG | HAS_ERROR_FLAG, @@ -115,12 +115,12 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_TYPE_VERSION] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_CHECK_MANAGED_OBJECT_HAS_VALUES] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_INSTANCE_VALUE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_CHECK_ATTR_MODULE] = HAS_DEOPT_FLAG, + [_CHECK_ATTR_MODULE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_MODULE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_CHECK_ATTR_WITH_HINT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_CHECK_ATTR_WITH_HINT] = HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_WITH_HINT] = HAS_ARG_FLAG | HAS_NAME_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR_SLOT] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_CHECK_ATTR_CLASS] = HAS_DEOPT_FLAG, + [_CHECK_ATTR_CLASS] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_CLASS] = HAS_ARG_FLAG, [_GUARD_DORV_VALUES] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_STORE_ATTR_INSTANCE_VALUE] = HAS_ESCAPES_FLAG, @@ -142,14 +142,14 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GET_ITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_YIELD_FROM_ITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_FOR_ITER_TIER_TWO] = HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_ITER_CHECK_LIST] = HAS_DEOPT_FLAG, - [_GUARD_NOT_EXHAUSTED_LIST] = HAS_DEOPT_FLAG, + [_ITER_CHECK_LIST] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_NOT_EXHAUSTED_LIST] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_ITER_NEXT_LIST] = 0, - [_ITER_CHECK_TUPLE] = HAS_DEOPT_FLAG, - [_GUARD_NOT_EXHAUSTED_TUPLE] = HAS_DEOPT_FLAG, + [_ITER_CHECK_TUPLE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_NOT_EXHAUSTED_TUPLE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_ITER_NEXT_TUPLE] = 0, - [_ITER_CHECK_RANGE] = HAS_DEOPT_FLAG, - [_GUARD_NOT_EXHAUSTED_RANGE] = HAS_DEOPT_FLAG, + [_ITER_CHECK_RANGE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_GUARD_NOT_EXHAUSTED_RANGE] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_ITER_NEXT_RANGE] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BEFORE_ASYNC_WITH] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_BEFORE_WITH] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -161,11 +161,11 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_LOAD_ATTR_METHOD_NO_DICT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_LOAD_ATTR_NONDESCRIPTOR_WITH_VALUES] = HAS_ARG_FLAG, [_LOAD_ATTR_NONDESCRIPTOR_NO_DICT] = HAS_ARG_FLAG, - [_CHECK_ATTR_METHOD_LAZY_DICT] = HAS_DEOPT_FLAG, + [_CHECK_ATTR_METHOD_LAZY_DICT] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_LOAD_ATTR_METHOD_LAZY_DICT] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_CHECK_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_INIT_CALL_BOUND_METHOD_EXACT_ARGS] = HAS_ARG_FLAG, - [_CHECK_PEP_523] = HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, + [_CHECK_PEP_523] = HAS_DEOPT_FLAG, [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG, @@ -199,7 +199,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GUARD_IS_NOT_NONE_POP] = HAS_DEOPT_FLAG, [_JUMP_TO_TOP] = HAS_EVAL_BREAK_FLAG, [_SET_IP] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, - [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG | HAS_PASSTHROUGH_FLAG, + [_SAVE_RETURN_OFFSET] = HAS_ARG_FLAG, [_EXIT_TRACE] = HAS_DEOPT_FLAG, [_CHECK_VALIDITY] = HAS_DEOPT_FLAG, }; diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index a9cc10a684d501..62e4d3641f9b1c 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -768,7 +768,7 @@ def test_annotated_inst(self): def test_annotated_op(self): input = """ - passthrough op(OP, (--)) { + pure op(OP, (--)) { spam(); } macro(M) = OP; @@ -785,7 +785,7 @@ def test_annotated_op(self): self.run_cases_test(input, output) input = """ - passthrough register specializing op(OP, (--)) { + pure register specializing op(OP, (--)) { spam(); } macro(M) = OP; diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 847f1016e5ef03..049d427986a6fc 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -161,7 +161,7 @@ dummy_func( } } - no_trivial_elimination passthrough inst(RESUME_CHECK, (--)) { + inst(RESUME_CHECK, (--)) { #if defined(__EMSCRIPTEN__) DEOPT_IF(_Py_emscripten_signal_clock == 0); _Py_emscripten_signal_clock -= Py_EMSCRIPTEN_SIGNAL_HANDLING; @@ -411,7 +411,7 @@ dummy_func( // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. }; - passthrough op(_GUARD_BOTH_INT, (left, right -- left: &(PYINT_TYPE), right: &(PYINT_TYPE))) { + op(_GUARD_BOTH_INT, (left, right -- left: &(PYINT_TYPE), right: &(PYINT_TYPE))) { DEOPT_IF(!PyLong_CheckExact(left)); DEOPT_IF(!PyLong_CheckExact(right)); } @@ -447,7 +447,7 @@ dummy_func( macro(BINARY_OP_SUBTRACT_INT) = _GUARD_BOTH_INT + unused/1 + _BINARY_OP_SUBTRACT_INT; - passthrough op(_GUARD_BOTH_FLOAT, (left, right -- left: &(PYFLOAT_TYPE), right: &(PYFLOAT_TYPE))) { + op(_GUARD_BOTH_FLOAT, (left, right -- left: &(PYFLOAT_TYPE), right: &(PYFLOAT_TYPE))) { DEOPT_IF(!PyFloat_CheckExact(left)); DEOPT_IF(!PyFloat_CheckExact(right)); } @@ -483,7 +483,7 @@ dummy_func( macro(BINARY_OP_SUBTRACT_FLOAT) = _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_SUBTRACT_FLOAT; - no_trivial_elimination op(_GUARD_BOTH_UNICODE, (left, right -- left: &(PYUNICODE_TYPE), right: &(PYUNICODE_TYPE))) { + op(_GUARD_BOTH_UNICODE, (left, right -- left: &(PYUNICODE_TYPE), right: &(PYUNICODE_TYPE))) { DEOPT_IF(!PyUnicode_CheckExact(left)); DEOPT_IF(!PyUnicode_CheckExact(right)); } @@ -1458,14 +1458,14 @@ dummy_func( builtins_version/1 + _LOAD_GLOBAL; - no_trivial_elimination passthrough op(_GUARD_GLOBALS_VERSION, (version/1 --)) { + op(_GUARD_GLOBALS_VERSION, (version/1 --)) { PyDictObject *dict = (PyDictObject *)GLOBALS(); DEOPT_IF(!PyDict_CheckExact(dict)); DEOPT_IF(dict->ma_keys->dk_version != version); assert(DK_IS_UNICODE(dict->ma_keys)); } - no_trivial_elimination passthrough op(_GUARD_BUILTINS_VERSION, (version/1 --)) { + op(_GUARD_BUILTINS_VERSION, (version/1 --)) { PyDictObject *dict = (PyDictObject *)BUILTINS(); DEOPT_IF(!PyDict_CheckExact(dict)); DEOPT_IF(dict->ma_keys->dk_version != version); @@ -1900,13 +1900,13 @@ dummy_func( LOAD_ATTR, }; - passthrough op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner: &(GUARD_TYPE_VERSION_TYPE + type_version))) { + op(_GUARD_TYPE_VERSION, (type_version/2, owner -- owner: &(GUARD_TYPE_VERSION_TYPE + type_version))) { PyTypeObject *tp = Py_TYPE(owner); assert(type_version != 0); DEOPT_IF(tp->tp_version_tag != type_version); } - no_trivial_elimination passthrough op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { + op(_CHECK_MANAGED_OBJECT_HAS_VALUES, (owner -- owner)) { assert(Py_TYPE(owner)->tp_dictoffset < 0); assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); @@ -2081,7 +2081,7 @@ dummy_func( DISPATCH_INLINED(new_frame); } - passthrough op(_GUARD_DORV_VALUES, (owner -- owner: &(GUARD_DORV_VALUES_TYPE))) { + op(_GUARD_DORV_VALUES, (owner -- owner: &(GUARD_DORV_VALUES_TYPE))) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); @@ -2879,13 +2879,13 @@ dummy_func( exc_info->exc_value = Py_NewRef(new_exc); } - passthrough op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner: &(GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE))) { + op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner: &(GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE))) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); } - passthrough op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner: &(GUARD_KEYS_VERSION_TYPE + keys_version))) { + op(_GUARD_KEYS_VERSION, (keys_version/2, owner -- owner: &(GUARD_KEYS_VERSION_TYPE + keys_version))) { PyTypeObject *owner_cls = Py_TYPE(owner); PyHeapTypeObject *owner_heap_type = (PyHeapTypeObject *)owner_cls; DEOPT_IF(owner_heap_type->ht_cached_keys->dk_version != keys_version); @@ -3100,7 +3100,7 @@ dummy_func( macro(CALL) = _SPECIALIZE_CALL + unused/2 + _CALL; - passthrough op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable: &(PYMETHOD_TYPE), null: &(NULL_TYPE), unused[oparg])) { + op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable: &(PYMETHOD_TYPE), null: &(NULL_TYPE), unused[oparg])) { DEOPT_IF(null != NULL); DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type); } @@ -3114,11 +3114,11 @@ dummy_func( Py_DECREF(callable); } - no_trivial_elimination passthrough op(_CHECK_PEP_523, (--)) { + op(_CHECK_PEP_523, (--)) { DEOPT_IF(tstate->interp->eval_frame); } - passthrough op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable: &(PYFUNCTION_TYPE_VERSION_TYPE + func_version), self_or_null, unused[oparg])) { + op(_CHECK_FUNCTION_EXACT_ARGS, (func_version/2, callable, self_or_null, unused[oparg] -- callable: &(PYFUNCTION_TYPE_VERSION_TYPE + func_version), self_or_null, unused[oparg])) { DEOPT_IF(!PyFunction_Check(callable)); PyFunctionObject *func = (PyFunctionObject *)callable; DEOPT_IF(func->func_version != func_version); @@ -3126,14 +3126,14 @@ dummy_func( DEOPT_IF(code->co_argcount != oparg + (self_or_null != NULL)); } - no_trivial_elimination passthrough op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { + op(_CHECK_STACK_SPACE, (callable, unused, unused[oparg] -- callable, unused, unused[oparg])) { PyFunctionObject *func = (PyFunctionObject *)callable; PyCodeObject *code = (PyCodeObject *)func->func_code; DEOPT_IF(!_PyThreadState_HasStackSpace(tstate, code->co_framesize)); DEOPT_IF(tstate->py_recursion_remaining <= 1); } - no_trivial_elimination pure op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) { + pure op(_INIT_CALL_PY_EXACT_ARGS, (callable, self_or_null, args[oparg] -- new_frame: _PyInterpreterFrame*)) { int argcount = oparg; if (self_or_null != NULL) { args--; @@ -4042,7 +4042,7 @@ dummy_func( frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame)) + oparg; } - no_trivial_elimination passthrough op(_SAVE_RETURN_OFFSET, (--)) { + op(_SAVE_RETURN_OFFSET, (--)) { #if TIER_ONE frame->return_offset = (uint16_t)(next_instr - this_instr); #endif diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index b6c6f5ee866cd8..417b6e55bde8f5 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -24,7 +24,6 @@ class Properties: pure: bool passthrough: bool - no_trivial_elimination: bool def dump(self, indent: str) -> None: print(indent, end="") @@ -51,7 +50,6 @@ def from_list(properties: list["Properties"]) -> "Properties": has_free=any(p.has_free for p in properties), pure=all(p.pure for p in properties), passthrough=all(p.passthrough for p in properties), - no_trivial_elimination=any(p.no_trivial_elimination for p in properties), ) @@ -73,7 +71,6 @@ def from_list(properties: list["Properties"]) -> "Properties": has_free=False, pure=False, passthrough=False, - no_trivial_elimination=False, ) @@ -431,6 +428,20 @@ def always_exits(op: parser.InstDef) -> bool: return False +def stack_effect_only_peeks(instr: parser.InstDef) -> bool: + stack_inputs = [s for s in instr.inputs if not isinstance(s, parser.CacheEffect)] + if len(stack_inputs) != len(instr.outputs): + return False + if len(stack_inputs) == 0: + return False + if any(s.cond for s in stack_inputs) or any(s.cond for s in instr.outputs): + return False + return all( + (s.name == other.name and s.type == other.type and s.size == other.size) + for s, other in zip(stack_inputs, instr.outputs) + ) + + def compute_properties(op: parser.InstDef) -> Properties: has_free = ( variable_used(op, "PyCell_New") @@ -455,8 +466,7 @@ def compute_properties(op: parser.InstDef) -> Properties: and not has_free, has_free=has_free, pure="pure" in op.annotations, - passthrough="passthrough" in op.annotations, - no_trivial_elimination="no_trivial_elimination" in op.annotations, + passthrough=stack_effect_only_peeks(op) and is_infallible(op), ) diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index 90a389cb2575b9..75ed59015972ca 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -176,10 +176,6 @@ list of annotations and their meanings are as follows: * `pure`. This instruction has no side effects visible to the Python user. It may still have side effects on the CPython interpreter state. All other instructions without this annotation are assumed to be not pure. -* `passthrough`. This instruction does not modify its stack inputs/outputs. - I.e. the stack operands "pass through". -* `no_trivial_elimination`. This tells the Tier 2 optimizer's abstract interpreter that - this instruction cannot be eliminated trivially via constant or type propagation. ### Special functions/macros diff --git a/Tools/cases_generator/lexer.py b/Tools/cases_generator/lexer.py index 6f21a9f529933a..4f8d01c5492f51 100644 --- a/Tools/cases_generator/lexer.py +++ b/Tools/cases_generator/lexer.py @@ -218,12 +218,10 @@ def choice(*opts: str) -> str: ANNOTATION = "ANNOTATION" annotations = { "specializing", - "passthrough", "override", "register", "replaced", "pure", - "no_trivial_elimination", } __all__ = [] From b775b0659f8e96195564914800113d0017888acb Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 11 Jan 2024 13:50:58 +0800 Subject: [PATCH 13/19] add guard to properties --- Tools/cases_generator/analyzer.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Tools/cases_generator/analyzer.py b/Tools/cases_generator/analyzer.py index 417b6e55bde8f5..7ed3b57136554f 100644 --- a/Tools/cases_generator/analyzer.py +++ b/Tools/cases_generator/analyzer.py @@ -24,6 +24,7 @@ class Properties: pure: bool passthrough: bool + guard: bool def dump(self, indent: str) -> None: print(indent, end="") @@ -50,6 +51,7 @@ def from_list(properties: list["Properties"]) -> "Properties": has_free=any(p.has_free for p in properties), pure=all(p.pure for p in properties), passthrough=all(p.passthrough for p in properties), + guard=all(p.guard for p in properties), ) @@ -71,6 +73,7 @@ def from_list(properties: list["Properties"]) -> "Properties": has_free=False, pure=False, passthrough=False, + guard=False, ) @@ -448,10 +451,13 @@ def compute_properties(op: parser.InstDef) -> Properties: or variable_used(op, "PyCell_GET") or variable_used(op, "PyCell_SET") ) + infallible = is_infallible(op) + deopts = variable_used(op, "DEOPT_IF") + passthrough = stack_effect_only_peeks(op) and infallible return Properties( escapes=makes_escaping_api_call(op), - infallible=is_infallible(op), - deopts=variable_used(op, "DEOPT_IF"), + infallible=infallible, + deopts=deopts, oparg=variable_used(op, "oparg"), jumps=variable_used(op, "JUMPBY"), eval_breaker=variable_used(op, "CHECK_EVAL_BREAKER"), @@ -466,7 +472,8 @@ def compute_properties(op: parser.InstDef) -> Properties: and not has_free, has_free=has_free, pure="pure" in op.annotations, - passthrough=stack_effect_only_peeks(op) and is_infallible(op), + passthrough=passthrough, + guard=passthrough and deopts, ) From f0ec15f94ad4f9aeff2c34b203edb44de6e436e5 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 12 Jan 2024 00:25:39 +0800 Subject: [PATCH 14/19] remove passthrough --- Lib/test/test_generated_cases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_generated_cases.py b/Lib/test/test_generated_cases.py index 62e4d3641f9b1c..ca1228ee7008a9 100644 --- a/Lib/test/test_generated_cases.py +++ b/Lib/test/test_generated_cases.py @@ -751,7 +751,7 @@ def test_override_op(self): def test_annotated_inst(self): input = """ - passthrough inst(OP, (--)) { + pure inst(OP, (--)) { ham(); } """ From 83d0dff70cecda507de8d8a390e779bb2f1eeaeb Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Fri, 12 Jan 2024 01:06:02 +0800 Subject: [PATCH 15/19] int -> long, document types --- Python/bytecodes.c | 10 +++++----- .../cases_generator/interpreter_definition.md | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 049d427986a6fc..35172683ddb07d 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -411,12 +411,12 @@ dummy_func( // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. }; - op(_GUARD_BOTH_INT, (left, right -- left: &(PYINT_TYPE), right: &(PYINT_TYPE))) { + op(_GUARD_BOTH_INT, (left, right -- left: &(PYLONG_TYPE), right: &(PYLONG_TYPE))) { DEOPT_IF(!PyLong_CheckExact(left)); DEOPT_IF(!PyLong_CheckExact(right)); } - pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res: &(PYINT_TYPE))) { + pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res: &(PYLONG_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -424,7 +424,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - pure op(_BINARY_OP_ADD_INT, (left, right -- res: &(PYINT_TYPE))) { + pure op(_BINARY_OP_ADD_INT, (left, right -- res: &(PYLONG_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -432,7 +432,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res: &(PYINT_TYPE))) { + pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res: &(PYLONG_TYPE))) { STAT_INC(BINARY_OP, hit); res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -2721,7 +2721,7 @@ dummy_func( DEOPT_IF(r->len <= 0); } - op(_ITER_NEXT_RANGE, (iter -- iter, next: &(PYINT_TYPE))) { + op(_ITER_NEXT_RANGE, (iter -- iter, next: &(PYLONG_TYPE))) { _PyRangeIterObject *r = (_PyRangeIterObject *)iter; assert(Py_TYPE(r) == &PyRangeIter_Type); assert(r->len > 0); diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index 75ed59015972ca..60a03e822d43c5 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -146,7 +146,8 @@ end of the instruction. When prefixed by a `&`, the type rule follows the `type_prop` rule. This indicates the stack effect is of that specific type after the operation. In this case, the type may also contain auxillary information that is fetched from a previously defined operand in the instruction header, such as -a type version tag. This follows the format `type + auxillary`. +a type version tag. This follows the format `type + auxillary`. For a list of the +types and what they correspond to, see [Appendix A - Types](#Appendix-A-Types). An `inst` without `stack_effect` is a transitional form to allow the original C code definitions to be copied. It lacks information to generate anything other than the @@ -442,3 +443,19 @@ rather than popping and pushing, such that `LOAD_ATTR_SLOT` would look something From the instruction definitions we can generate the stack marking code used in `frame.set_lineno()`, and the tables for use by disassemblers. + +## Appendix A: Types + +The following types correspond to the following information: +* `PYLONG_TYPE`: `&PyLong_Type` +* `PYFLOAT_TYPE`: `&PyFloat_Type` +* `PYUNICODE_TYPE`: `&PYUNICODE_TYPE` +* `NULL_TYPE`: `NULL` +* `GUARD_TYPE_VERSION_TYPE`: `type->tp_version_tag == auxillary` +* `GUARD_DORV_VALUES_TYPE`: `_PyDictOrValues_IsValues(obj)` +* `GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE`: + `_PyDictOrValues_IsValues(obj) || _PyObject_MakeInstanceAttributesFromDict(obj, dorv)` +* `GUARD_KEYS_VERSION_TYPE`: `owner_heap_type->ht_cached_keys->dk_version == auxillary` +* `PYMETHOD_TYPE`: `&PyMethod_Type` +* `PYFUNCTION_TYPE_VERSION_TYPE`: + `PyFunction_Check(callable) && func->func_version == auxillary && code->co_argcount == oparg + (self_or_null != NULL)` From aee39a48044838c26a0697b126f93da6ca34fd6c Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:33:15 +0800 Subject: [PATCH 16/19] Address review --- Python/bytecodes.c | 28 +++++++++---------- .../cases_generator/interpreter_definition.md | 22 +++++++-------- Tools/cases_generator/parsing.py | 5 ++-- 3 files changed, 27 insertions(+), 28 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 35172683ddb07d..374b06c9e6de17 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -411,12 +411,12 @@ dummy_func( // BINARY_OP_INPLACE_ADD_UNICODE, // See comments at that opcode. }; - op(_GUARD_BOTH_INT, (left, right -- left: &(PYLONG_TYPE), right: &(PYLONG_TYPE))) { + op(_GUARD_BOTH_INT, (left, right -- left: &PYLONG_TYPE, right: &PYLONG_TYPE)) { DEOPT_IF(!PyLong_CheckExact(left)); DEOPT_IF(!PyLong_CheckExact(right)); } - pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res: &(PYLONG_TYPE))) { + pure op(_BINARY_OP_MULTIPLY_INT, (left, right -- res: &PYLONG_TYPE)) { STAT_INC(BINARY_OP, hit); res = _PyLong_Multiply((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -424,7 +424,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - pure op(_BINARY_OP_ADD_INT, (left, right -- res: &(PYLONG_TYPE))) { + pure op(_BINARY_OP_ADD_INT, (left, right -- res: &PYLONG_TYPE)) { STAT_INC(BINARY_OP, hit); res = _PyLong_Add((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -432,7 +432,7 @@ dummy_func( ERROR_IF(res == NULL, error); } - pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res: &(PYLONG_TYPE))) { + pure op(_BINARY_OP_SUBTRACT_INT, (left, right -- res: &PYLONG_TYPE)) { STAT_INC(BINARY_OP, hit); res = _PyLong_Subtract((PyLongObject *)left, (PyLongObject *)right); _Py_DECREF_SPECIALIZED(right, (destructor)PyObject_Free); @@ -447,12 +447,12 @@ dummy_func( macro(BINARY_OP_SUBTRACT_INT) = _GUARD_BOTH_INT + unused/1 + _BINARY_OP_SUBTRACT_INT; - op(_GUARD_BOTH_FLOAT, (left, right -- left: &(PYFLOAT_TYPE), right: &(PYFLOAT_TYPE))) { + op(_GUARD_BOTH_FLOAT, (left, right -- left: &PYFLOAT_TYPE, right: &PYFLOAT_TYPE)) { DEOPT_IF(!PyFloat_CheckExact(left)); DEOPT_IF(!PyFloat_CheckExact(right)); } - pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res: &(PYFLOAT_TYPE))) { + pure op(_BINARY_OP_MULTIPLY_FLOAT, (left, right -- res: &PYFLOAT_TYPE)) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval * @@ -460,7 +460,7 @@ dummy_func( DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res: &(PYFLOAT_TYPE))) { + pure op(_BINARY_OP_ADD_FLOAT, (left, right -- res: &PYFLOAT_TYPE)) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval + @@ -468,7 +468,7 @@ dummy_func( DECREF_INPUTS_AND_REUSE_FLOAT(left, right, dres, res); } - pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res: &(PYFLOAT_TYPE))) { + pure op(_BINARY_OP_SUBTRACT_FLOAT, (left, right -- res: &PYFLOAT_TYPE)) { STAT_INC(BINARY_OP, hit); double dres = ((PyFloatObject *)left)->ob_fval - @@ -483,12 +483,12 @@ dummy_func( macro(BINARY_OP_SUBTRACT_FLOAT) = _GUARD_BOTH_FLOAT + unused/1 + _BINARY_OP_SUBTRACT_FLOAT; - op(_GUARD_BOTH_UNICODE, (left, right -- left: &(PYUNICODE_TYPE), right: &(PYUNICODE_TYPE))) { + op(_GUARD_BOTH_UNICODE, (left, right -- left: &PYUNICODE_TYPE, right: &PYUNICODE_TYPE)) { DEOPT_IF(!PyUnicode_CheckExact(left)); DEOPT_IF(!PyUnicode_CheckExact(right)); } - pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res: &(PYUNICODE_TYPE))) { + pure op(_BINARY_OP_ADD_UNICODE, (left, right -- res: &PYUNICODE_TYPE)) { STAT_INC(BINARY_OP, hit); res = PyUnicode_Concat(left, right); _Py_DECREF_SPECIALIZED(left, _PyUnicode_ExactDealloc); @@ -2081,7 +2081,7 @@ dummy_func( DISPATCH_INLINED(new_frame); } - op(_GUARD_DORV_VALUES, (owner -- owner: &(GUARD_DORV_VALUES_TYPE))) { + op(_GUARD_DORV_VALUES, (owner -- owner: &GUARD_DORV_VALUES_TYPE)) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(dorv)); @@ -2721,7 +2721,7 @@ dummy_func( DEOPT_IF(r->len <= 0); } - op(_ITER_NEXT_RANGE, (iter -- iter, next: &(PYLONG_TYPE))) { + op(_ITER_NEXT_RANGE, (iter -- iter, next: &PYLONG_TYPE)) { _PyRangeIterObject *r = (_PyRangeIterObject *)iter; assert(Py_TYPE(r) == &PyRangeIter_Type); assert(r->len > 0); @@ -2879,7 +2879,7 @@ dummy_func( exc_info->exc_value = Py_NewRef(new_exc); } - op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner: &(GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE))) { + op(_GUARD_DORV_VALUES_INST_ATTR_FROM_DICT, (owner -- owner: &GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE)) { assert(Py_TYPE(owner)->tp_flags & Py_TPFLAGS_MANAGED_DICT); PyDictOrValues *dorv = _PyObject_DictOrValuesPointer(owner); DEOPT_IF(!_PyDictOrValues_IsValues(*dorv) && !_PyObject_MakeInstanceAttributesFromDict(owner, dorv)); @@ -3100,7 +3100,7 @@ dummy_func( macro(CALL) = _SPECIALIZE_CALL + unused/2 + _CALL; - op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable: &(PYMETHOD_TYPE), null: &(NULL_TYPE), unused[oparg])) { + op(_CHECK_CALL_BOUND_METHOD_EXACT_ARGS, (callable, null, unused[oparg] -- callable: &PYMETHOD_TYPE, null: &NULL_TYPE, unused[oparg])) { DEOPT_IF(null != NULL); DEOPT_IF(Py_TYPE(callable) != &PyMethod_Type); } diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index 60a03e822d43c5..d93dd17149bd8c 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -142,9 +142,9 @@ The following definitions may occur: The optional `type` in an `object` is the C type. It defaults to `PyObject *`. The objects before the "--" are the objects on top of the stack at the start of the instruction. Those after the "--" are the objects on top of the stack at the -end of the instruction. When prefixed by a `&`, the type rule follows the -`type_prop` rule. This indicates the stack effect is of that specific type -after the operation. In this case, the type may also contain auxillary information +end of the instruction. When prefixed by a `&`, the `type` production rule follows the +`type_prop` production rule. This indicates the stack effect is of that specific type +after the operation. In this case, the type may also consist of auxillary information that is fetched from a previously defined operand in the instruction header, such as a type version tag. This follows the format `type + auxillary`. For a list of the types and what they correspond to, see [Appendix A - Types](#Appendix-A-Types). @@ -174,9 +174,7 @@ list of annotations and their meanings are as follows: * `override`. For external use by other interpreter definitions to override the current instruction definition. -* `pure`. This instruction has no side effects visible to the Python user. It may - still have side effects on the CPython interpreter state. All other instructions without - this annotation are assumed to be not pure. +* `pure`. This instruction has no side effects. ### Special functions/macros @@ -446,16 +444,16 @@ and the tables for use by disassemblers. ## Appendix A: Types -The following types correspond to the following information: -* `PYLONG_TYPE`: `&PyLong_Type` -* `PYFLOAT_TYPE`: `&PyFloat_Type` -* `PYUNICODE_TYPE`: `&PYUNICODE_TYPE` -* `NULL_TYPE`: `NULL` +The following types obeys the following predicates: +* `PYLONG_TYPE`: `Py_TYPE(val) == &PyLong_Type` +* `PYFLOAT_TYPE`: `Py_TYPE(val) == &PyFloat_Type` +* `PYUNICODE_TYPE`: `Py_TYPE(val) == &PYUNICODE_TYPE` +* `NULL_TYPE`: `val == NULL` * `GUARD_TYPE_VERSION_TYPE`: `type->tp_version_tag == auxillary` * `GUARD_DORV_VALUES_TYPE`: `_PyDictOrValues_IsValues(obj)` * `GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE`: `_PyDictOrValues_IsValues(obj) || _PyObject_MakeInstanceAttributesFromDict(obj, dorv)` * `GUARD_KEYS_VERSION_TYPE`: `owner_heap_type->ht_cached_keys->dk_version == auxillary` -* `PYMETHOD_TYPE`: `&PyMethod_Type` +* `PYMETHOD_TYPE`: `Py_TYPE(val) == &PyMethod_Type` * `PYFUNCTION_TYPE_VERSION_TYPE`: `PyFunction_Check(callable) && func->func_version == auxillary && code->co_argcount == oparg + (self_or_null != NULL)` diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index 6dab32e36235d4..c2fcf7958e82b5 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -271,13 +271,14 @@ def stack_effect(self) -> StackEffect | None: if self.expect(lx.TIMES): type_text += " *" if self.expect(lx.AND): - self.require(lx.LPAREN) + consumed_bracket = self.expect(lx.LPAREN) is not None type_prop_text = self.require(lx.IDENTIFIER).text.strip() aux = None if self.expect(lx.PLUS): aux = self.require(lx.IDENTIFIER).text.strip() type_prop = (type_prop_text, aux) - self.require(lx.RPAREN) + if consumed_bracket: + self.require(lx.RPAREN) cond_text = "" if self.expect(lx.IF): self.require(lx.LPAREN) From a7d7d4b93c1f67252be7d02646161d986f74f1d9 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:34:43 +0800 Subject: [PATCH 17/19] remove pure from push and pop frames --- Include/internal/pycore_opcode_metadata.h | 4 ++-- Include/internal/pycore_uop_metadata.h | 4 ++-- Python/bytecodes.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index fe41efe214aea9..ee0138aaec81b8 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1108,9 +1108,9 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[268] = { [RESERVED] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [RESUME] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RESUME_CHECK] = { true, INSTR_FMT_IX, HAS_DEOPT_FLAG }, - [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, + [RETURN_CONST] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_CONST_FLAG | HAS_ESCAPES_FLAG }, [RETURN_GENERATOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG | HAS_PURE_FLAG }, + [RETURN_VALUE] = { true, INSTR_FMT_IX, HAS_ESCAPES_FLAG }, [SEND] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [SEND_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [SETUP_ANNOTATIONS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index cc79a30591410e..5d0cbc3ca134b4 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -64,7 +64,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_DELETE_SUBSCR] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_INTRINSIC_1] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_INTRINSIC_2] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_POP_FRAME] = HAS_ESCAPES_FLAG | HAS_PURE_FLAG, + [_POP_FRAME] = HAS_ESCAPES_FLAG, [_GET_AITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_ANEXT] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_AWAITABLE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -169,7 +169,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_CHECK_FUNCTION_EXACT_ARGS] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_CHECK_STACK_SPACE] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_PASSTHROUGH_FLAG, [_INIT_CALL_PY_EXACT_ARGS] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG | HAS_PURE_FLAG, - [_PUSH_FRAME] = HAS_PURE_FLAG, + [_PUSH_FRAME] = 0, [_CALL_TYPE_1] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, [_CALL_STR_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_CALL_TUPLE_1] = HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG | HAS_DEOPT_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 374b06c9e6de17..e8ba4130424bf9 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -801,7 +801,7 @@ dummy_func( // We definitely pop the return value off the stack on entry. // We also push it onto the stack on exit, but that's a // different frame, and it's accounted for by _PUSH_FRAME. - pure op(_POP_FRAME, (retval --)) { + op(_POP_FRAME, (retval --)) { #if TIER_ONE assert(frame != &entry_frame); #endif @@ -3150,7 +3150,7 @@ dummy_func( // The 'unused' output effect represents the return value // (which will be pushed when the frame returns). // It is needed so CALL_PY_EXACT_ARGS matches its family. - pure op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused if (0))) { + op(_PUSH_FRAME, (new_frame: _PyInterpreterFrame* -- unused if (0))) { // Write it out explicitly because it's subtly different. // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); From 845f92ddb2312950c9a7ae7b0b91fddbddf2b6c2 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:35:53 +0800 Subject: [PATCH 18/19] auxillary is 64-bit --- Tools/cases_generator/interpreter_definition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index d93dd17149bd8c..c488afffe33288 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -144,7 +144,7 @@ The objects before the "--" are the objects on top of the stack at the start of the instruction. Those after the "--" are the objects on top of the stack at the end of the instruction. When prefixed by a `&`, the `type` production rule follows the `type_prop` production rule. This indicates the stack effect is of that specific type -after the operation. In this case, the type may also consist of auxillary information +after the operation. In this case, the type may also consist of 64-bit auxillary information that is fetched from a previously defined operand in the instruction header, such as a type version tag. This follows the format `type + auxillary`. For a list of the types and what they correspond to, see [Appendix A - Types](#Appendix-A-Types). From 362db1a6dbc2ce4a94090bc977eeaf63136abd34 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:53:16 +0800 Subject: [PATCH 19/19] use refinement --- .../cases_generator/interpreter_definition.md | 39 +++++++++---------- Tools/cases_generator/parsing.py | 8 ++-- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Tools/cases_generator/interpreter_definition.md b/Tools/cases_generator/interpreter_definition.md index c488afffe33288..e5a48999e962cb 100644 --- a/Tools/cases_generator/interpreter_definition.md +++ b/Tools/cases_generator/interpreter_definition.md @@ -143,11 +143,26 @@ The optional `type` in an `object` is the C type. It defaults to `PyObject *`. The objects before the "--" are the objects on top of the stack at the start of the instruction. Those after the "--" are the objects on top of the stack at the end of the instruction. When prefixed by a `&`, the `type` production rule follows the -`type_prop` production rule. This indicates the stack effect is of that specific type -after the operation. In this case, the type may also consist of 64-bit auxillary information +`type_prop` production rule. This indicates the type of the value is of that specific type +after the operation. In this case, the type may also contain 64-bit refinement information that is fetched from a previously defined operand in the instruction header, such as -a type version tag. This follows the format `type + auxillary`. For a list of the -types and what they correspond to, see [Appendix A - Types](#Appendix-A-Types). +a type version tag. This follows the format `type + refinement`. The list of possible types +and their refinements are below. They obey the following predicates: + + +* `PYLONG_TYPE`: `Py_TYPE(val) == &PyLong_Type` +* `PYFLOAT_TYPE`: `Py_TYPE(val) == &PyFloat_Type` +* `PYUNICODE_TYPE`: `Py_TYPE(val) == &PYUNICODE_TYPE` +* `NULL_TYPE`: `val == NULL` +* `GUARD_TYPE_VERSION_TYPE`: `type->tp_version_tag == auxillary` +* `GUARD_DORV_VALUES_TYPE`: `_PyDictOrValues_IsValues(obj)` +* `GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE`: + `_PyDictOrValues_IsValues(obj) || _PyObject_MakeInstanceAttributesFromDict(obj, dorv)` +* `GUARD_KEYS_VERSION_TYPE`: `owner_heap_type->ht_cached_keys->dk_version == auxillary` +* `PYMETHOD_TYPE`: `Py_TYPE(val) == &PyMethod_Type` +* `PYFUNCTION_TYPE_VERSION_TYPE`: + `PyFunction_Check(callable) && func->func_version == auxillary && code->co_argcount == oparg + (self_or_null != NULL)` + An `inst` without `stack_effect` is a transitional form to allow the original C code definitions to be copied. It lacks information to generate anything other than the @@ -441,19 +456,3 @@ rather than popping and pushing, such that `LOAD_ATTR_SLOT` would look something From the instruction definitions we can generate the stack marking code used in `frame.set_lineno()`, and the tables for use by disassemblers. - -## Appendix A: Types - -The following types obeys the following predicates: -* `PYLONG_TYPE`: `Py_TYPE(val) == &PyLong_Type` -* `PYFLOAT_TYPE`: `Py_TYPE(val) == &PyFloat_Type` -* `PYUNICODE_TYPE`: `Py_TYPE(val) == &PYUNICODE_TYPE` -* `NULL_TYPE`: `val == NULL` -* `GUARD_TYPE_VERSION_TYPE`: `type->tp_version_tag == auxillary` -* `GUARD_DORV_VALUES_TYPE`: `_PyDictOrValues_IsValues(obj)` -* `GUARD_DORV_VALUES_INST_ATTR_FROM_DICT_TYPE`: - `_PyDictOrValues_IsValues(obj) || _PyObject_MakeInstanceAttributesFromDict(obj, dorv)` -* `GUARD_KEYS_VERSION_TYPE`: `owner_heap_type->ht_cached_keys->dk_version == auxillary` -* `PYMETHOD_TYPE`: `Py_TYPE(val) == &PyMethod_Type` -* `PYFUNCTION_TYPE_VERSION_TYPE`: - `PyFunction_Check(callable) && func->func_version == auxillary && code->co_argcount == oparg + (self_or_null != NULL)` diff --git a/Tools/cases_generator/parsing.py b/Tools/cases_generator/parsing.py index c2fcf7958e82b5..307919cb37ce1e 100644 --- a/Tools/cases_generator/parsing.py +++ b/Tools/cases_generator/parsing.py @@ -75,7 +75,7 @@ class StackEffect(Node): size: str = "" # Optional `[size]` # Note: size cannot be combined with type or cond - # Optional `(type, aux)` + # Optional `(type, refinement)` type_prop: None | tuple[str, None | str] = field( default_factory=lambda: None, init=True, compare=False, hash=False ) @@ -273,10 +273,10 @@ def stack_effect(self) -> StackEffect | None: if self.expect(lx.AND): consumed_bracket = self.expect(lx.LPAREN) is not None type_prop_text = self.require(lx.IDENTIFIER).text.strip() - aux = None + refinement = None if self.expect(lx.PLUS): - aux = self.require(lx.IDENTIFIER).text.strip() - type_prop = (type_prop_text, aux) + refinement = self.require(lx.IDENTIFIER).text.strip() + type_prop = (type_prop_text, refinement) if consumed_bracket: self.require(lx.RPAREN) cond_text = ""