summaryrefslogtreecommitdiffstats
path: root/Tools/clinic/libclinic
diff options
context:
space:
mode:
authorSergey B Kirpichev <skirpichev@gmail.com>2024-10-31 10:37:03 (GMT)
committerGitHub <noreply@github.com>2024-10-31 10:37:03 (GMT)
commit8c22eba877225ab29cd64672dcb97f09a5f7336f (patch)
treeee74e354c3f99cacf2e3a14d56dbc49e8a08d0a5 /Tools/clinic/libclinic
parentd07dcce6935364cab807e0df931ed09b088ade69 (diff)
downloadcpython-8c22eba877225ab29cd64672dcb97f09a5f7336f.zip
cpython-8c22eba877225ab29cd64672dcb97f09a5f7336f.tar.gz
cpython-8c22eba877225ab29cd64672dcb97f09a5f7336f.tar.bz2
gh-90370: Argument Clinic: avoid temporary tuple creation for varargs (#126064)
Avoid temporary tuple creation when all arguments either positional-only or vararg. Objects/setobject.c and Modules/gcmodule.c adapted. This fixes slight performance regression for set methods, introduced by gh-115112.
Diffstat (limited to 'Tools/clinic/libclinic')
-rw-r--r--Tools/clinic/libclinic/clanguage.py15
-rw-r--r--Tools/clinic/libclinic/parse_args.py35
2 files changed, 24 insertions, 26 deletions
diff --git a/Tools/clinic/libclinic/clanguage.py b/Tools/clinic/libclinic/clanguage.py
index 73d4783..32aba81 100644
--- a/Tools/clinic/libclinic/clanguage.py
+++ b/Tools/clinic/libclinic/clanguage.py
@@ -15,7 +15,7 @@ from libclinic.function import (
Module, Class, Function, Parameter,
permute_optional_groups,
GETTER, SETTER, METHOD_INIT)
-from libclinic.converters import self_converter
+from libclinic.converters import defining_class_converter, self_converter
from libclinic.parse_args import ParseArgsCodeGen
if TYPE_CHECKING:
from libclinic.app import Clinic
@@ -396,6 +396,12 @@ class CLanguage(Language):
first_optional = len(selfless)
positional = selfless and selfless[-1].is_positional_only()
has_option_groups = False
+ requires_defining_class = (len(selfless)
+ and isinstance(selfless[0].converter,
+ defining_class_converter))
+ pass_vararg_directly = (all(p.is_positional_only() or p.is_vararg()
+ for p in selfless)
+ and not requires_defining_class)
# offset i by -1 because first_optional needs to ignore self
for i, p in enumerate(parameters, -1):
@@ -404,7 +410,7 @@ class CLanguage(Language):
if (i != -1) and (p.default is not unspecified):
first_optional = min(first_optional, i)
- if p.is_vararg():
+ if p.is_vararg() and not pass_vararg_directly:
data.cleanup.append(f"Py_XDECREF({c.parser_name});")
# insert group variable
@@ -418,6 +424,11 @@ class CLanguage(Language):
data.impl_parameters.append("int " + group_name)
has_option_groups = True
+ if p.is_vararg() and pass_vararg_directly:
+ data.impl_arguments.append('nvararg')
+ data.impl_parameters.append('Py_ssize_t nargs')
+ p.converter.type = 'PyObject *const *'
+
c.render(p, data)
if has_option_groups and (not positional):
diff --git a/Tools/clinic/libclinic/parse_args.py b/Tools/clinic/libclinic/parse_args.py
index 96c9b91..559d4fb 100644
--- a/Tools/clinic/libclinic/parse_args.py
+++ b/Tools/clinic/libclinic/parse_args.py
@@ -469,7 +469,11 @@ class ParseArgsCodeGen:
nargs = 'PyTuple_GET_SIZE(args)'
argname_fmt = 'PyTuple_GET_ITEM(args, %d)'
- left_args = f"{nargs} - {self.max_pos}"
+ if self.vararg != NO_VARARG:
+ self.declarations = f"Py_ssize_t nvararg = {nargs} - {self.max_pos};"
+ else:
+ self.declarations = ""
+
max_args = NO_VARARG if (self.vararg != NO_VARARG) else self.max_pos
if self.limited_capi:
parser_code = []
@@ -518,30 +522,13 @@ class ParseArgsCodeGen:
use_parser_code = True
for i, p in enumerate(self.parameters):
if p.is_vararg():
+ var = p.converter.parser_name
if self.fastcall:
- parser_code.append(libclinic.normalize_snippet("""
- %s = PyTuple_New(%s);
- if (!%s) {{
- goto exit;
- }}
- for (Py_ssize_t i = 0; i < %s; ++i) {{
- PyTuple_SET_ITEM(%s, i, Py_NewRef(args[%d + i]));
- }}
- """ % (
- p.converter.parser_name,
- left_args,
- p.converter.parser_name,
- left_args,
- p.converter.parser_name,
- self.max_pos
- ), indent=4))
+ code = f"{var} = args + {self.vararg};"
else:
- parser_code.append(libclinic.normalize_snippet("""
- %s = PyTuple_GetSlice(%d, -1);
- """ % (
- p.converter.parser_name,
- self.max_pos
- ), indent=4))
+ code = f"{var} = _PyTuple_CAST(args)->ob_item;"
+ formatted_code = libclinic.normalize_snippet(code, indent=4)
+ parser_code.append(formatted_code)
continue
displayname = p.get_displayname(i+1)
@@ -588,7 +575,7 @@ class ParseArgsCodeGen:
goto exit;
}}
""", indent=4)]
- self.parser_body(*parser_code)
+ self.parser_body(*parser_code, declarations=self.declarations)
def parse_general(self, clang: CLanguage) -> None:
parsearg: str | None