Skip to content

gh-98831: Modernize CALL_FUNCTION_EX #101627

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 7 additions & 16 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2949,26 +2949,21 @@ dummy_func(
CHECK_EVAL_BREAKER();
}

// error: CALL_FUNCTION_EX has irregular stack effect
inst(CALL_FUNCTION_EX) {
PyObject *func, *callargs, *kwargs = NULL, *result;
if (oparg & 0x01) {
kwargs = POP();
inst(CALL_FUNCTION_EX, (unused, func, callargs, kwargs if (oparg & 1) -- result)) {
if (oparg & 1) {
// DICT_MERGE is called before this opcode if there are kwargs.
// It converts all dict subtypes in kwargs into regular dicts.
assert(PyDict_CheckExact(kwargs));
}
callargs = POP();
func = TOP();
if (!PyTuple_CheckExact(callargs)) {
if (check_args_iterable(tstate, func, callargs) < 0) {
Py_DECREF(callargs);
goto error;
}
Py_SETREF(callargs, PySequence_Tuple(callargs));
if (callargs == NULL) {
PyObject *tuple = PySequence_Tuple(callargs);
if (tuple == NULL) {
goto error;
}
Py_SETREF(callargs, tuple);
}
assert(PyTuple_CheckExact(callargs));

Expand All @@ -2977,12 +2972,8 @@ dummy_func(
Py_DECREF(callargs);
Py_XDECREF(kwargs);

STACK_SHRINK(1);
assert(TOP() == NULL);
SET_TOP(result);
if (result == NULL) {
goto error;
}
assert(PEEK(3 + (oparg & 1)) == NULL);
ERROR_IF(result == NULL, error);
CHECK_EVAL_BREAKER();
}

Expand Down
27 changes: 13 additions & 14 deletions Python/generated_cases.c.h

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

4 changes: 2 additions & 2 deletions Python/opcode_metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
return -1;
case CALL_FUNCTION_EX:
return -1;
return ((oparg & 1) ? 1 : 0) + 3;
case MAKE_FUNCTION:
return ((oparg & 0x01) ? 1 : 0) + ((oparg & 0x02) ? 1 : 0) + ((oparg & 0x04) ? 1 : 0) + ((oparg & 0x08) ? 1 : 0) + 1;
case RETURN_GENERATOR:
Expand Down Expand Up @@ -669,7 +669,7 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
case CALL_NO_KW_METHOD_DESCRIPTOR_FAST:
return -1;
case CALL_FUNCTION_EX:
return -1;
return 1;
case MAKE_FUNCTION:
return 1;
case RETURN_GENERATOR:
Expand Down
15 changes: 12 additions & 3 deletions Tools/cases_generator/generate_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ def __init__(self, inst: parser.InstDef):
self.kind = inst.kind
self.name = inst.name
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.cache_effects = [
effect for effect in inst.inputs if isinstance(effect, parser.CacheEffect)
Expand Down Expand Up @@ -1016,6 +1017,8 @@ def write_instr(self, instr: Instruction) -> None:
if not instr.always_exits:
for prediction in instr.predictions:
self.out.emit(f"PREDICT({prediction});")
if instr.check_eval_breaker:
self.out.emit("CHECK_EVAL_BREAKER();")
self.out.emit(f"DISPATCH();")

def write_super(self, sup: SuperInstruction) -> None:
Expand Down Expand Up @@ -1091,7 +1094,7 @@ def wrap_super_or_macro(self, up: SuperOrMacroInstruction):
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
blocklines = block.text.splitlines(True)

Expand All @@ -1111,6 +1114,12 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
while blocklines and not blocklines[-1].strip():
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
predictions: list[str] = []
while blocklines and (
Expand All @@ -1119,7 +1128,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
predictions.insert(0, m.group(1))
blocklines.pop()

return blocklines, predictions
return blocklines, check_eval_breaker, predictions


def always_exits(lines: list[str]) -> bool:
Expand Down
9 changes: 7 additions & 2 deletions Tools/cases_generator/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,16 @@ def test_overlap():
"""
run_cases_test(input, output)

def test_predictions():
def test_predictions_and_eval_breaker():
input = """
inst(OP1, (--)) {
}
inst(OP2, (--)) {
}
inst(OP3, (--)) {
inst(OP3, (arg -- res)) {
DEOPT_IF(xxx, OP1);
PREDICT(OP2);
CHECK_EVAL_BREAKER();
}
"""
output = """
Expand All @@ -200,8 +201,12 @@ def test_predictions():
}

TARGET(OP3) {
PyObject *arg = PEEK(1);
PyObject *res;
DEOPT_IF(xxx, OP1);
POKE(1, res);
PREDICT(OP2);
CHECK_EVAL_BREAKER();
DISPATCH();
}
"""
Expand Down