mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-98831: Modernize CALL_FUNCTION_EX (#101627)
New generator feature: Move CHECK_EVAL_BREAKER() call to just before DISPATCH().
This commit is contained in:
parent
790ff6bc6a
commit
a9f01448a9
5 changed files with 41 additions and 37 deletions
|
@ -2951,26 +2951,21 @@ dummy_func(
|
||||||
CHECK_EVAL_BREAKER();
|
CHECK_EVAL_BREAKER();
|
||||||
}
|
}
|
||||||
|
|
||||||
// error: CALL_FUNCTION_EX has irregular stack effect
|
inst(CALL_FUNCTION_EX, (unused, func, callargs, kwargs if (oparg & 1) -- result)) {
|
||||||
inst(CALL_FUNCTION_EX) {
|
if (oparg & 1) {
|
||||||
PyObject *func, *callargs, *kwargs = NULL, *result;
|
|
||||||
if (oparg & 0x01) {
|
|
||||||
kwargs = POP();
|
|
||||||
// DICT_MERGE is called before this opcode if there are kwargs.
|
// DICT_MERGE is called before this opcode if there are kwargs.
|
||||||
// It converts all dict subtypes in kwargs into regular dicts.
|
// It converts all dict subtypes in kwargs into regular dicts.
|
||||||
assert(PyDict_CheckExact(kwargs));
|
assert(PyDict_CheckExact(kwargs));
|
||||||
}
|
}
|
||||||
callargs = POP();
|
|
||||||
func = TOP();
|
|
||||||
if (!PyTuple_CheckExact(callargs)) {
|
if (!PyTuple_CheckExact(callargs)) {
|
||||||
if (check_args_iterable(tstate, func, callargs) < 0) {
|
if (check_args_iterable(tstate, func, callargs) < 0) {
|
||||||
Py_DECREF(callargs);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
Py_SETREF(callargs, PySequence_Tuple(callargs));
|
PyObject *tuple = PySequence_Tuple(callargs);
|
||||||
if (callargs == NULL) {
|
if (tuple == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
Py_SETREF(callargs, tuple);
|
||||||
}
|
}
|
||||||
assert(PyTuple_CheckExact(callargs));
|
assert(PyTuple_CheckExact(callargs));
|
||||||
|
|
||||||
|
@ -2979,12 +2974,8 @@ dummy_func(
|
||||||
Py_DECREF(callargs);
|
Py_DECREF(callargs);
|
||||||
Py_XDECREF(kwargs);
|
Py_XDECREF(kwargs);
|
||||||
|
|
||||||
STACK_SHRINK(1);
|
assert(PEEK(3 + (oparg & 1)) == NULL);
|
||||||
assert(TOP() == NULL);
|
ERROR_IF(result == NULL, error);
|
||||||
SET_TOP(result);
|
|
||||||
if (result == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
CHECK_EVAL_BREAKER();
|
CHECK_EVAL_BREAKER();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
Python/generated_cases.c.h
generated
27
Python/generated_cases.c.h
generated
|
@ -3587,24 +3587,24 @@
|
||||||
|
|
||||||
TARGET(CALL_FUNCTION_EX) {
|
TARGET(CALL_FUNCTION_EX) {
|
||||||
PREDICTED(CALL_FUNCTION_EX);
|
PREDICTED(CALL_FUNCTION_EX);
|
||||||
PyObject *func, *callargs, *kwargs = NULL, *result;
|
PyObject *kwargs = (oparg & 1) ? PEEK(((oparg & 1) ? 1 : 0)) : NULL;
|
||||||
if (oparg & 0x01) {
|
PyObject *callargs = PEEK(1 + ((oparg & 1) ? 1 : 0));
|
||||||
kwargs = POP();
|
PyObject *func = PEEK(2 + ((oparg & 1) ? 1 : 0));
|
||||||
|
PyObject *result;
|
||||||
|
if (oparg & 1) {
|
||||||
// DICT_MERGE is called before this opcode if there are kwargs.
|
// DICT_MERGE is called before this opcode if there are kwargs.
|
||||||
// It converts all dict subtypes in kwargs into regular dicts.
|
// It converts all dict subtypes in kwargs into regular dicts.
|
||||||
assert(PyDict_CheckExact(kwargs));
|
assert(PyDict_CheckExact(kwargs));
|
||||||
}
|
}
|
||||||
callargs = POP();
|
|
||||||
func = TOP();
|
|
||||||
if (!PyTuple_CheckExact(callargs)) {
|
if (!PyTuple_CheckExact(callargs)) {
|
||||||
if (check_args_iterable(tstate, func, callargs) < 0) {
|
if (check_args_iterable(tstate, func, callargs) < 0) {
|
||||||
Py_DECREF(callargs);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
Py_SETREF(callargs, PySequence_Tuple(callargs));
|
PyObject *tuple = PySequence_Tuple(callargs);
|
||||||
if (callargs == NULL) {
|
if (tuple == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
Py_SETREF(callargs, tuple);
|
||||||
}
|
}
|
||||||
assert(PyTuple_CheckExact(callargs));
|
assert(PyTuple_CheckExact(callargs));
|
||||||
|
|
||||||
|
@ -3613,12 +3613,11 @@
|
||||||
Py_DECREF(callargs);
|
Py_DECREF(callargs);
|
||||||
Py_XDECREF(kwargs);
|
Py_XDECREF(kwargs);
|
||||||
|
|
||||||
STACK_SHRINK(1);
|
assert(PEEK(3 + (oparg & 1)) == NULL);
|
||||||
assert(TOP() == NULL);
|
if (result == NULL) { STACK_SHRINK(((oparg & 1) ? 1 : 0)); goto pop_3_error; }
|
||||||
SET_TOP(result);
|
STACK_SHRINK(((oparg & 1) ? 1 : 0));
|
||||||
if (result == NULL) {
|
STACK_SHRINK(2);
|
||||||
goto error;
|
POKE(1, result);
|
||||||
}
|
|
||||||
CHECK_EVAL_BREAKER();
|
CHECK_EVAL_BREAKER();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
|
@ -325,7 +325,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
|
||||||
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
|
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
|
||||||
return -1;
|
return -1;
|
||||||
case CALL_FUNCTION_EX:
|
case CALL_FUNCTION_EX:
|
||||||
return -1;
|
return ((oparg & 1) ? 1 : 0) + 3;
|
||||||
case MAKE_FUNCTION:
|
case MAKE_FUNCTION:
|
||||||
return ((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0) + 1;
|
return ((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0) + 1;
|
||||||
case RETURN_GENERATOR:
|
case RETURN_GENERATOR:
|
||||||
|
@ -673,7 +673,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
|
||||||
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
|
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
|
||||||
return -1;
|
return -1;
|
||||||
case CALL_FUNCTION_EX:
|
case CALL_FUNCTION_EX:
|
||||||
return -1;
|
return 1;
|
||||||
case MAKE_FUNCTION:
|
case MAKE_FUNCTION:
|
||||||
return 1;
|
return 1;
|
||||||
case RETURN_GENERATOR:
|
case RETURN_GENERATOR:
|
||||||
|
|
|
@ -227,7 +227,8 @@ class Instruction:
|
||||||
self.kind = inst.kind
|
self.kind = inst.kind
|
||||||
self.name = inst.name
|
self.name = inst.name
|
||||||
self.block = inst.block
|
self.block = inst.block
|
||||||
self.block_text, self.predictions = extract_block_text(self.block)
|
self.block_text, self.check_eval_breaker, self.predictions = \
|
||||||
|
extract_block_text(self.block)
|
||||||
self.always_exits = always_exits(self.block_text)
|
self.always_exits = always_exits(self.block_text)
|
||||||
self.cache_effects = [
|
self.cache_effects = [
|
||||||
effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect)
|
effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect)
|
||||||
|
@ -1027,6 +1028,8 @@ class Analyzer:
|
||||||
if not instr.always_exits:
|
if not instr.always_exits:
|
||||||
for prediction in instr.predictions:
|
for prediction in instr.predictions:
|
||||||
self.out.emit(f"PREDICT({prediction});")
|
self.out.emit(f"PREDICT({prediction});")
|
||||||
|
if instr.check_eval_breaker:
|
||||||
|
self.out.emit("CHECK_EVAL_BREAKER();")
|
||||||
self.out.emit(f"DISPATCH();")
|
self.out.emit(f"DISPATCH();")
|
||||||
|
|
||||||
def write_super(self, sup: SuperInstruction) -> None:
|
def write_super(self, sup: SuperInstruction) -> None:
|
||||||
|
@ -1102,7 +1105,7 @@ class Analyzer:
|
||||||
self.out.emit(f"DISPATCH();")
|
self.out.emit(f"DISPATCH();")
|
||||||
|
|
||||||
|
|
||||||
def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
|
def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str]]:
|
||||||
# Get lines of text with proper dedent
|
# Get lines of text with proper dedent
|
||||||
blocklines = block.text.splitlines(True)
|
blocklines = block.text.splitlines(True)
|
||||||
|
|
||||||
|
@ -1122,6 +1125,12 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
|
||||||
while blocklines and not blocklines[-1].strip():
|
while blocklines and not blocklines[-1].strip():
|
||||||
blocklines.pop()
|
blocklines.pop()
|
||||||
|
|
||||||
|
# Separate CHECK_EVAL_BREAKER() macro from end
|
||||||
|
check_eval_breaker = \
|
||||||
|
blocklines != [] and blocklines[-1].strip() == "CHECK_EVAL_BREAKER();"
|
||||||
|
if check_eval_breaker:
|
||||||
|
del blocklines[-1]
|
||||||
|
|
||||||
# Separate PREDICT(...) macros from end
|
# Separate PREDICT(...) macros from end
|
||||||
predictions: list[str] = []
|
predictions: list[str] = []
|
||||||
while blocklines and (
|
while blocklines and (
|
||||||
|
@ -1130,7 +1139,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
|
||||||
predictions.insert(0, m.group(1))
|
predictions.insert(0, m.group(1))
|
||||||
blocklines.pop()
|
blocklines.pop()
|
||||||
|
|
||||||
return blocklines, predictions
|
return blocklines, check_eval_breaker, predictions
|
||||||
|
|
||||||
|
|
||||||
def always_exits(lines: list[str]) -> bool:
|
def always_exits(lines: list[str]) -> bool:
|
||||||
|
|
|
@ -177,15 +177,16 @@ def test_overlap():
|
||||||
"""
|
"""
|
||||||
run_cases_test(input, output)
|
run_cases_test(input, output)
|
||||||
|
|
||||||
def test_predictions():
|
def test_predictions_and_eval_breaker():
|
||||||
input = """
|
input = """
|
||||||
inst(OP1, (--)) {
|
inst(OP1, (--)) {
|
||||||
}
|
}
|
||||||
inst(OP2, (--)) {
|
inst(OP2, (--)) {
|
||||||
}
|
}
|
||||||
inst(OP3, (--)) {
|
inst(OP3, (arg -- res)) {
|
||||||
DEOPT_IF(xxx, OP1);
|
DEOPT_IF(xxx, OP1);
|
||||||
PREDICT(OP2);
|
PREDICT(OP2);
|
||||||
|
CHECK_EVAL_BREAKER();
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
output = """
|
output = """
|
||||||
|
@ -200,8 +201,12 @@ def test_predictions():
|
||||||
}
|
}
|
||||||
|
|
||||||
TARGET(OP3) {
|
TARGET(OP3) {
|
||||||
|
PyObject *arg = PEEK(1);
|
||||||
|
PyObject *res;
|
||||||
DEOPT_IF(xxx, OP1);
|
DEOPT_IF(xxx, OP1);
|
||||||
|
POKE(1, res);
|
||||||
PREDICT(OP2);
|
PREDICT(OP2);
|
||||||
|
CHECK_EVAL_BREAKER();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue