summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorKen Jin <28750310+Fidget-Spinner@users.noreply.github.com>2021-05-15 15:15:23 (GMT)
committerGitHub <noreply@github.com>2021-05-15 15:15:23 (GMT)
commitf24afda5917ce1710ad08ca34b2509f1f2b16de2 (patch)
tree72513ebad5e8235d451cffdb27f67182606b686f /Python/compile.c
parente4e931a67e49cf3c61263dc94fb0806c34f972cd (diff)
downloadcpython-f24afda5917ce1710ad08ca34b2509f1f2b16de2.zip
cpython-f24afda5917ce1710ad08ca34b2509f1f2b16de2.tar.gz
cpython-f24afda5917ce1710ad08ca34b2509f1f2b16de2.tar.bz2
bpo-26110: Add ``CALL_METHOD_KW`` opcode to speedup method calls with keywords (GH-26014)
* Add CALL_METHOD_KW * Make CALL_METHOD branchless too since it shares the same code * Place parentheses in STACK_SHRINK
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c81
1 files changed, 63 insertions, 18 deletions
diff --git a/Python/compile.c b/Python/compile.c
index 071dabc..94f2e82 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -309,6 +309,10 @@ static int are_all_items_const(asdl_expr_seq *, Py_ssize_t, Py_ssize_t);
static int compiler_with(struct compiler *, stmt_ty, int);
static int compiler_async_with(struct compiler *, stmt_ty, int);
static int compiler_async_for(struct compiler *, stmt_ty);
+static int validate_keywords(struct compiler *c, asdl_keyword_seq *keywords);
+static int compiler_call_simple_kw_helper(struct compiler *c,
+ asdl_keyword_seq *keywords,
+ Py_ssize_t nkwelts);
static int compiler_call_helper(struct compiler *c, int n,
asdl_expr_seq *args,
asdl_keyword_seq *keywords);
@@ -1176,6 +1180,8 @@ stack_effect(int opcode, int oparg, int jump)
return -oparg;
case CALL_METHOD:
return -oparg-1;
+ case CALL_METHOD_KW:
+ return -oparg-2;
case CALL_FUNCTION_KW:
return -oparg-1;
case CALL_FUNCTION_EX:
@@ -4266,19 +4272,19 @@ check_index(struct compiler *c, expr_ty e, expr_ty s)
static int
maybe_optimize_method_call(struct compiler *c, expr_ty e)
{
- Py_ssize_t argsl, i;
+ Py_ssize_t argsl, i, kwdsl;
expr_ty meth = e->v.Call.func;
asdl_expr_seq *args = e->v.Call.args;
+ asdl_keyword_seq *kwds = e->v.Call.keywords;
- /* Check that the call node is an attribute access, and that
- the call doesn't have keyword parameters. */
- if (meth->kind != Attribute_kind || meth->v.Attribute.ctx != Load ||
- asdl_seq_LEN(e->v.Call.keywords)) {
+ /* Check that the call node is an attribute access */
+ if (meth->kind != Attribute_kind || meth->v.Attribute.ctx != Load) {
return -1;
}
/* Check that there aren't too many arguments */
argsl = asdl_seq_LEN(args);
- if (argsl >= STACK_USE_GUIDELINE) {
+ kwdsl = asdl_seq_LEN(kwds);
+ if (argsl + kwdsl + (kwdsl != 0) >= STACK_USE_GUIDELINE) {
return -1;
}
/* Check that there are no *varargs types of arguments. */
@@ -4289,13 +4295,28 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
}
}
+ for (i = 0; i < kwdsl; i++) {
+ keyword_ty kw = asdl_seq_GET(kwds, i);
+ if (kw->arg == NULL) {
+ return -1;
+ }
+ }
/* Alright, we can optimize the code. */
VISIT(c, expr, meth->v.Attribute.value);
int old_lineno = c->u->u_lineno;
c->u->u_lineno = meth->end_lineno;
ADDOP_NAME(c, LOAD_METHOD, meth->v.Attribute.attr, names);
VISIT_SEQ(c, expr, e->v.Call.args);
- ADDOP_I(c, CALL_METHOD, asdl_seq_LEN(e->v.Call.args));
+
+ if (kwdsl) {
+ if (!compiler_call_simple_kw_helper(c, kwds, kwdsl)) {
+ return 0;
+ };
+ ADDOP_I(c, CALL_METHOD_KW, argsl + kwdsl);
+ }
+ else {
+ ADDOP_I(c, CALL_METHOD, argsl);
+ }
c->u->u_lineno = old_lineno;
return 1;
}
@@ -4327,6 +4348,9 @@ validate_keywords(struct compiler *c, asdl_keyword_seq *keywords)
static int
compiler_call(struct compiler *c, expr_ty e)
{
+ if (validate_keywords(c, e->v.Call.keywords) == -1) {
+ return 0;
+ }
int ret = maybe_optimize_method_call(c, e);
if (ret >= 0) {
return ret;
@@ -4458,6 +4482,36 @@ compiler_subkwargs(struct compiler *c, asdl_keyword_seq *keywords, Py_ssize_t be
return 1;
}
+/* Used by compiler_call_helper and maybe_optimize_method_call to emit
+LOAD_CONST kw1
+LOAD_CONST kw2
+...
+LOAD_CONST <tuple of kwnames>
+before a CALL_(FUNCTION|METHOD)_KW.
+
+Returns 1 on success, 0 on error.
+*/
+static int
+compiler_call_simple_kw_helper(struct compiler *c,
+ asdl_keyword_seq *keywords,
+ Py_ssize_t nkwelts)
+{
+ PyObject *names;
+ VISIT_SEQ(c, keyword, keywords);
+ names = PyTuple_New(nkwelts);
+ if (names == NULL) {
+ return 0;
+ }
+ for (int i = 0; i < nkwelts; i++) {
+ keyword_ty kw = asdl_seq_GET(keywords, i);
+ Py_INCREF(kw->arg);
+ PyTuple_SET_ITEM(names, i, kw->arg);
+ }
+ ADDOP_LOAD_CONST_NEW(c, names);
+ return 1;
+}
+
+
/* shared code between compiler_call and compiler_class */
static int
compiler_call_helper(struct compiler *c,
@@ -4497,18 +4551,9 @@ compiler_call_helper(struct compiler *c,
VISIT(c, expr, elt);
}
if (nkwelts) {
- PyObject *names;
- VISIT_SEQ(c, keyword, keywords);
- names = PyTuple_New(nkwelts);
- if (names == NULL) {
+ if (!compiler_call_simple_kw_helper(c, keywords, nkwelts)) {
return 0;
- }
- for (i = 0; i < nkwelts; i++) {
- keyword_ty kw = asdl_seq_GET(keywords, i);
- Py_INCREF(kw->arg);
- PyTuple_SET_ITEM(names, i, kw->arg);
- }
- ADDOP_LOAD_CONST_NEW(c, names);
+ };
ADDOP_I(c, CALL_FUNCTION_KW, n + nelts + nkwelts);
return 1;
}