gh-155141: Improve stack-use estimation for keyword calls (#155785)

This commit is contained in:
cui fliter authored and GitHub committed 2026-09-03 13:06:35 +00:00
1 parent 50ed1ec6ca
commit e13ecaeca2
3 files changed
+32 -2

No files matched your search

+24
View File
@@ -63,6 +63,30 @@ class TestSpecifics(unittest.TestCase):
self.assertRaises(SyntaxError, exec, 'def f(a = 0, a = 1): pass')
self.assertRaises(SyntaxError, exec, 'def f(a): global a; a = 1')
def test_call_opcode_stack_use_limit(self):
def get_call_opcode(positional_count, keyword_count):
args = ["0"] * positional_count
args.extend(f"a{i}=0" for i in range(keyword_count))
code = compile(f"f({', '.join(args)})", "<test>", "exec")
return next(
instr.opname for instr in dis.get_instructions(code)
if instr.opname.startswith("CALL")
)
for positional_count, keyword_count, expected_opcode in [
(0, 16, "CALL_KW"),
(15, 14, "CALL_KW"),
(15, 15, "CALL_FUNCTION_EX"),
]:
with self.subTest(
positional_count=positional_count,
keyword_count=keyword_count,
):
self.assertEqual(
get_call_opcode(positional_count, keyword_count),
expected_opcode,
)
def test_syntax_error(self):
self.assertRaises(SyntaxError, compile, "1+*3", "filename", "exec")
@@ -0,0 +1,3 @@
Compile pure-keyword calls with 16 to 29 keyword arguments using the faster
``CALL_KW`` instruction. This includes common cases such as dataclass
constructors with many fields.
+5 -2
View File
@@ -87,6 +87,9 @@ typedef _PyCompile_FBlockInfo fblockinfo;
#define LOC(x) SRC_LOCATION_FROM_AST(x)
#define CALL_STACK_USE(nargs, nkwds) \
((nargs) + (nkwds) + ((nkwds) != 0))
#define NEW_JUMP_TARGET_LABEL(C, NAME) \
jump_target_label NAME = _PyInstructionSequence_NewLabel(INSTR_SEQUENCE(C)); \
if (!IS_JUMP_TARGET_LABEL(NAME)) { \
@@ -4147,7 +4150,7 @@ maybe_optimize_method_call(compiler *c, expr_ty e)
/* Check that there aren't too many arguments */
argsl = asdl_seq_LEN(args);
kwdsl = asdl_seq_LEN(kwds);
if (argsl + kwdsl + (kwdsl != 0) >= _PY_STACK_USE_GUIDELINE) {
if (CALL_STACK_USE(argsl, kwdsl) >= _PY_STACK_USE_GUIDELINE) {
return 0;
}
/* Check that there are no *varargs types of arguments. */
@@ -4440,7 +4443,7 @@ codegen_call_helper_impl(compiler *c, location loc,
nelts = asdl_seq_LEN(args);
nkwelts = asdl_seq_LEN(keywords);
if (nelts + nkwelts*2 > _PY_STACK_USE_GUIDELINE) {
if (CALL_STACK_USE(nelts, nkwelts) > _PY_STACK_USE_GUIDELINE) {
goto ex_call;
}
for (i = 0; i < nelts; i++) {