Skip to content

Commit a9f0144

Browse files
authored
gh-98831: Modernize CALL_FUNCTION_EX (#101627)
New generator feature: Move CHECK_EVAL_BREAKER() call to just before DISPATCH().
1 parent 790ff6b commit a9f0144

File tree

5 files changed

+41
-37
lines changed

5 files changed

+41
-37
lines changed

Python/bytecodes.c

+7-16
Original file line numberDiff line numberDiff line change
@@ -2951,26 +2951,21 @@ dummy_func(
29512951
CHECK_EVAL_BREAKER();
29522952
}
29532953

2954-
// error: CALL_FUNCTION_EX has irregular stack effect
2955-
inst(CALL_FUNCTION_EX) {
2956-
PyObject *func, *callargs, *kwargs = NULL, *result;
2957-
if (oparg & 0x01) {
2958-
kwargs = POP();
2954+
inst(CALL_FUNCTION_EX, (unused, func, callargs, kwargs if (oparg & 1) -- result)) {
2955+
if (oparg & 1) {
29592956
// DICT_MERGE is called before this opcode if there are kwargs.
29602957
// It converts all dict subtypes in kwargs into regular dicts.
29612958
assert(PyDict_CheckExact(kwargs));
29622959
}
2963-
callargs = POP();
2964-
func = TOP();
29652960
if (!PyTuple_CheckExact(callargs)) {
29662961
if (check_args_iterable(tstate, func, callargs) < 0) {
2967-
Py_DECREF(callargs);
29682962
goto error;
29692963
}
2970-
Py_SETREF(callargs, PySequence_Tuple(callargs));
2971-
if (callargs == NULL) {
2964+
PyObject *tuple = PySequence_Tuple(callargs);
2965+
if (tuple == NULL) {
29722966
goto error;
29732967
}
2968+
Py_SETREF(callargs, tuple);
29742969
}
29752970
assert(PyTuple_CheckExact(callargs));
29762971

@@ -2979,12 +2974,8 @@ dummy_func(
29792974
Py_DECREF(callargs);
29802975
Py_XDECREF(kwargs);
29812976

2982-
STACK_SHRINK(1);
2983-
assert(TOP() == NULL);
2984-
SET_TOP(result);
2985-
if (result == NULL) {
2986-
goto error;
2987-
}
2977+
assert(PEEK(3 + (oparg & 1)) == NULL);
2978+
ERROR_IF(result == NULL, error);
29882979
CHECK_EVAL_BREAKER();
29892980
}
29902981

Python/generated_cases.c.h

+13-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/opcode_metadata.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
325325
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
326326
return -1;
327327
case CALL_FUNCTION_EX:
328-
return -1;
328+
return ((oparg & 1) ? 1 : 0) + 3;
329329
case MAKE_FUNCTION:
330330
return ((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0) + 1;
331331
case RETURN_GENERATOR:
@@ -673,7 +673,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
673673
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
674674
return -1;
675675
case CALL_FUNCTION_EX:
676-
return -1;
676+
return 1;
677677
case MAKE_FUNCTION:
678678
return 1;
679679
case RETURN_GENERATOR:

Tools/cases_generator/generate_cases.py

+12-3
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@ def __init__(self, inst: parser.InstDef):
227227
self.kind = inst.kind
228228
self.name = inst.name
229229
self.block = inst.block
230-
self.block_text, self.predictions = extract_block_text(self.block)
230+
self.block_text, self.check_eval_breaker, self.predictions = \
231+
extract_block_text(self.block)
231232
self.always_exits = always_exits(self.block_text)
232233
self.cache_effects = [
233234
effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect)
@@ -1027,6 +1028,8 @@ def write_instr(self, instr: Instruction) -> None:
10271028
if not instr.always_exits:
10281029
for prediction in instr.predictions:
10291030
self.out.emit(f"PREDICT({prediction});")
1031+
if instr.check_eval_breaker:
1032+
self.out.emit("CHECK_EVAL_BREAKER();")
10301033
self.out.emit(f"DISPATCH();")
10311034

10321035
def write_super(self, sup: SuperInstruction) -> None:
@@ -1102,7 +1105,7 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
11021105
self.out.emit(f"DISPATCH();")
11031106

11041107

1105-
def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
1108+
def extract_block_text(block: parser.Block) -> tuple[list[str], bool, list[str]]:
11061109
# Get lines of text with proper dedent
11071110
blocklines = block.text.splitlines(True)
11081111

@@ -1122,6 +1125,12 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
11221125
while blocklines and not blocklines[-1].strip():
11231126
blocklines.pop()
11241127

1128+
# Separate CHECK_EVAL_BREAKER() macro from end
1129+
check_eval_breaker = \
1130+
blocklines != [] and blocklines[-1].strip() == "CHECK_EVAL_BREAKER();"
1131+
if check_eval_breaker:
1132+
del blocklines[-1]
1133+
11251134
# Separate PREDICT(...) macros from end
11261135
predictions: list[str] = []
11271136
while blocklines and (
@@ -1130,7 +1139,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
11301139
predictions.insert(0, m.group(1))
11311140
blocklines.pop()
11321141

1133-
return blocklines, predictions
1142+
return blocklines, check_eval_breaker, predictions
11341143

11351144

11361145
def always_exits(lines: list[str]) -> bool:

Tools/cases_generator/test_generator.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,16 @@ def test_overlap():
177177
"""
178178
run_cases_test(input, output)
179179

180-
def test_predictions():
180+
def test_predictions_and_eval_breaker():
181181
input = """
182182
inst(OP1, (--)) {
183183
}
184184
inst(OP2, (--)) {
185185
}
186-
inst(OP3, (--)) {
186+
inst(OP3, (arg -- res)) {
187187
DEOPT_IF(xxx, OP1);
188188
PREDICT(OP2);
189+
CHECK_EVAL_BREAKER();
189190
}
190191
"""
191192
output = """
@@ -200,8 +201,12 @@ def test_predictions():
200201
}
201202
202203
TARGET(OP3) {
204+
PyObject *arg = PEEK(1);
205+
PyObject *res;
203206
DEOPT_IF(xxx, OP1);
207+
POKE(1, res);
204208
PREDICT(OP2);
209+
CHECK_EVAL_BREAKER();
205210
DISPATCH();
206211
}
207212
"""

0 commit comments

Comments
 (0)