Skip to content

Commit f229b5b

Browse files
committed
optimize 2-arg super also
1 parent 0775efe commit f229b5b

File tree

11 files changed

+353
-265
lines changed

11 files changed

+353
-265
lines changed

Doc/library/dis.rst

+14-8
Original file line numberDiff line numberDiff line change
@@ -1036,14 +1036,20 @@ iterations of the loop.
10361036
pushed to the stack before the attribute or unbound method respectively.
10371037

10381038

1039-
.. opcode:: LOAD_ZERO_SUPER_ATTR (namei)
1040-
1041-
This opcode implements zero-argument :func:`super` (i.e. ``super().method()``
1042-
and ``super().attr``). It works the same as :opcode:`LOAD_ATTR`, except that
1043-
instead of expecting a single receiver on the stack, it expects three objects
1044-
(from top of stack down): ``self`` (the first argument to the current
1045-
method), ``cls`` (the class within which the current method was defined), and
1046-
the global ``super``.
1039+
.. opcode:: LOAD_SUPER_ATTR (namei)
1040+
1041+
This opcode implements :func:`super` (e.g. ``super().method()`` and
1042+
``super().attr``). It works the same as :opcode:`LOAD_ATTR`, except that
1043+
``namei`` is shifted left by 2 bits instead of 1, and instead of expecting a
1044+
single receiver on the stack, it expects three objects (from top of stack
1045+
down): ``self`` (the first argument to the current method), ``cls`` (the
1046+
class within which the current method was defined), and the global ``super``.
1047+
1048+
The low bit of ``namei`` signals to attempt a method load, as with
1049+
:opcode:`LOAD_ATTR`.
1050+
1051+
The second-low bit of ``namei``, if set, means that this was a zero-argument
1052+
call to :func:`super`.
10471053

10481054
.. versionadded:: 3.12
10491055

Include/internal/pycore_opcode.h

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

Include/opcode.h

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

Lib/dis.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
FOR_ITER = opmap['FOR_ITER']
4242
SEND = opmap['SEND']
4343
LOAD_ATTR = opmap['LOAD_ATTR']
44-
LOAD_ZERO_SUPER_ATTR = opmap['LOAD_ZERO_SUPER_ATTR']
44+
LOAD_SUPER_ATTR = opmap['LOAD_SUPER_ATTR']
4545

4646
CACHE = opmap["CACHE"]
4747

@@ -472,10 +472,14 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
472472
argval, argrepr = _get_name_info(arg//2, get_name)
473473
if (arg & 1) and argrepr:
474474
argrepr = "NULL + " + argrepr
475-
elif deop == LOAD_ATTR or deop == LOAD_ZERO_SUPER_ATTR:
475+
elif deop == LOAD_ATTR:
476476
argval, argrepr = _get_name_info(arg//2, get_name)
477477
if (arg & 1) and argrepr:
478478
argrepr = "NULL|self + " + argrepr
479+
elif deop == LOAD_SUPER_ATTR:
480+
argval, argrepr = _get_name_info(arg//4, get_name)
481+
if (arg & 1) and argrepr:
482+
argrepr = "NULL|self + " + argrepr
479483
else:
480484
argval, argrepr = _get_name_info(arg, get_name)
481485
elif deop in hasjabs:

Lib/opcode.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ def pseudo_op(name, op, real_ops):
196196
def_op('DELETE_DEREF', 139)
197197
hasfree.append(139)
198198
jrel_op('JUMP_BACKWARD', 140) # Number of words to skip (backwards)
199-
name_op('LOAD_ZERO_SUPER_ATTR', 141)
199+
name_op('LOAD_SUPER_ATTR', 141)
200200
def_op('CALL_FUNCTION_EX', 142) # Flags
201201

202202
def_op('EXTENDED_ARG', 144)
@@ -264,7 +264,9 @@ def pseudo_op(name, op, real_ops):
264264
pseudo_op('JUMP_NO_INTERRUPT', 261, ['JUMP_FORWARD', 'JUMP_BACKWARD_NO_INTERRUPT'])
265265

266266
pseudo_op('LOAD_METHOD', 262, ['LOAD_ATTR'])
267-
pseudo_op('LOAD_ZERO_SUPER_METHOD', 263, ['LOAD_ZERO_SUPER_ATTR'])
267+
pseudo_op('LOAD_SUPER_METHOD', 263, ['LOAD_SUPER_ATTR'])
268+
pseudo_op('LOAD_ZERO_SUPER_METHOD', 264, ['LOAD_SUPER_ATTR'])
269+
pseudo_op('LOAD_ZERO_SUPER_ATTR', 265, ['LOAD_SUPER_ATTR'])
268270

269271
MAX_PSEUDO_OPCODE = MIN_PSEUDO_OPCODE + len(_pseudo_ops) - 1
270272

Lib/test/test_super.py

+15
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,21 @@ def method(self):
362362
with patch("test.test_super.super", MySuper) as m:
363363
self.assertEqual(C().method(), "super super")
364364

365+
def test_shadowed_dynamic_two_arg(self):
366+
call_args = []
367+
class MySuper:
368+
def __init__(self, *args):
369+
call_args.append(args)
370+
msg = "super super"
371+
372+
class C:
373+
def method(self):
374+
return super(1, 2).msg
375+
376+
with patch("test.test_super.super", MySuper) as m:
377+
self.assertEqual(C().method(), "super super")
378+
self.assertEqual(call_args, [(1, 2)])
379+
365380
def test_attribute_error(self):
366381
class C:
367382
def method(self):

Python/bytecodes.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -1551,8 +1551,8 @@ dummy_func(
15511551
PREDICT(JUMP_BACKWARD);
15521552
}
15531553

1554-
inst(LOAD_ZERO_SUPER_ATTR, (global_super, class, self -- res2 if (oparg & 1), res)) {
1555-
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 1);
1554+
inst(LOAD_SUPER_ATTR, (global_super, class, self -- res2 if (oparg & 1), res)) {
1555+
PyObject *name = GETITEM(frame->f_code->co_names, oparg >> 2);
15561556
if (global_super == (PyObject *)&PySuper_Type) {
15571557
int meth_found = 0;
15581558
Py_DECREF(global_super);
@@ -1571,7 +1571,13 @@ dummy_func(
15711571
Py_DECREF(self);
15721572
}
15731573
} else {
1574-
PyObject *super = PyObject_Vectorcall(global_super, NULL, 0, NULL);
1574+
PyObject *super;
1575+
if (oparg & 2) {
1576+
super = PyObject_Vectorcall(global_super, NULL, 0, NULL);
1577+
} else {
1578+
PyObject *stack[] = {class, self};
1579+
super = PyObject_Vectorcall(global_super, stack, 2, NULL);
1580+
}
15751581
DECREF_INPUTS();
15761582
ERROR_IF(super == NULL, error);
15771583
res = PyObject_GetAttr(super, name);

Python/compile.c

+57-14
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,9 @@ stack_effect(int opcode, int oparg, int jump)
829829

830830
case LOAD_METHOD:
831831
return 1;
832+
case LOAD_SUPER_METHOD:
832833
case LOAD_ZERO_SUPER_METHOD:
834+
case LOAD_ZERO_SUPER_ATTR:
833835
return -1;
834836
default:
835837
return PY_INVALID_STACK_EFFECT;
@@ -1041,19 +1043,32 @@ compiler_addop_name(struct compiler_unit *u, location loc,
10411043
if (arg < 0) {
10421044
return ERROR;
10431045
}
1044-
if (opcode == LOAD_ATTR || opcode == LOAD_ZERO_SUPER_ATTR) {
1046+
if (opcode == LOAD_ATTR) {
10451047
arg <<= 1;
10461048
}
10471049
if (opcode == LOAD_METHOD) {
10481050
opcode = LOAD_ATTR;
10491051
arg <<= 1;
10501052
arg |= 1;
10511053
}
1052-
if (opcode == LOAD_ZERO_SUPER_METHOD) {
1053-
opcode = LOAD_ZERO_SUPER_ATTR;
1054-
arg <<= 1;
1054+
if (opcode == LOAD_SUPER_ATTR) {
1055+
arg <<= 2;
1056+
}
1057+
if (opcode == LOAD_SUPER_METHOD) {
1058+
opcode = LOAD_SUPER_ATTR;
1059+
arg <<= 2;
10551060
arg |= 1;
10561061
}
1062+
if (opcode == LOAD_ZERO_SUPER_ATTR) {
1063+
opcode = LOAD_SUPER_ATTR;
1064+
arg <<= 2;
1065+
arg |= 2;
1066+
}
1067+
if (opcode == LOAD_ZERO_SUPER_METHOD) {
1068+
opcode = LOAD_SUPER_ATTR;
1069+
arg <<= 2;
1070+
arg |= 3;
1071+
}
10571072
return codegen_addop_i(&u->u_instr_sequence, opcode, arg, loc);
10581073
}
10591074

@@ -4222,15 +4237,15 @@ is_import_originated(struct compiler *c, expr_ty e)
42224237
}
42234238

42244239
static int
4225-
is_zero_arg_super_call(struct compiler *c, expr_ty e)
4240+
can_optimize_super_call(struct compiler *c, expr_ty e)
42264241
{
42274242
if (e->kind != Call_kind ||
42284243
e->v.Call.func->kind != Name_kind ||
42294244
!_PyUnicode_EqualToASCIIString(e->v.Call.func->v.Name.id, "super") ||
4230-
asdl_seq_LEN(e->v.Call.args) != 0 ||
42314245
asdl_seq_LEN(e->v.Call.keywords) != 0) {
42324246
return 0;
42334247
}
4248+
Py_ssize_t num_args = asdl_seq_LEN(e->v.Call.args);
42344249

42354250
PyObject *super_name = e->v.Call.func->v.Name.id;
42364251
// try to detect statically-visible shadowing of 'super' name
@@ -4242,6 +4257,24 @@ is_zero_arg_super_call(struct compiler *c, expr_ty e)
42424257
if (scope != 0) {
42434258
return 0;
42444259
}
4260+
4261+
if (num_args == 2) {
4262+
for (Py_ssize_t i = 0; i < num_args; i++) {
4263+
expr_ty elt = asdl_seq_GET(e->v.Call.args, i);
4264+
if (elt->kind == Starred_kind) {
4265+
return 0;
4266+
}
4267+
}
4268+
// exactly two non-starred args; we can just load
4269+
// the provided args
4270+
return 1;
4271+
}
4272+
4273+
if (num_args != 0) {
4274+
return 0;
4275+
}
4276+
// we need the following for zero-arg super():
4277+
42454278
// enclosing function should have at least one argument
42464279
if (c->u->u_metadata.u_argcount == 0 &&
42474280
c->u->u_metadata.u_posonlyargcount == 0) {
@@ -4255,13 +4288,19 @@ is_zero_arg_super_call(struct compiler *c, expr_ty e)
42554288
}
42564289

42574290
static int
4258-
load_args_for_zero_super(struct compiler *c, expr_ty e) {
4291+
load_args_for_super(struct compiler *c, expr_ty e) {
42594292
location loc = LOC(e);
42604293

42614294
// load super() global
4262-
PyObject *super_name = e->v.Attribute.value->v.Call.func->v.Name.id;
4295+
PyObject *super_name = e->v.Call.func->v.Name.id;
42634296
RETURN_IF_ERROR(compiler_nameop(c, loc, super_name, Load));
42644297

4298+
if (asdl_seq_LEN(e->v.Call.args) == 2) {
4299+
VISIT(c, expr, asdl_seq_GET(e->v.Call.args, 0));
4300+
VISIT(c, expr, asdl_seq_GET(e->v.Call.args, 1));
4301+
return SUCCESS;
4302+
}
4303+
42654304
// load __class__ cell
42664305
PyObject *name = &_Py_ID(__class__);
42674306
assert(get_ref_type(c, name) == FREE);
@@ -4349,9 +4388,11 @@ maybe_optimize_method_call(struct compiler *c, expr_ty e)
43494388
/* Alright, we can optimize the code. */
43504389
location loc = LOC(meth);
43514390

4352-
if (is_zero_arg_super_call(c, meth->v.Attribute.value)) {
4353-
RETURN_IF_ERROR(load_args_for_zero_super(c, meth));
4354-
ADDOP_NAME(c, loc, LOAD_ZERO_SUPER_METHOD, meth->v.Attribute.attr, names);
4391+
if (can_optimize_super_call(c, meth->v.Attribute.value)) {
4392+
RETURN_IF_ERROR(load_args_for_super(c, meth->v.Attribute.value));
4393+
int opcode = asdl_seq_LEN(meth->v.Attribute.value->v.Call.args) ?
4394+
LOAD_SUPER_METHOD : LOAD_ZERO_SUPER_METHOD;
4395+
ADDOP_NAME(c, loc, opcode, meth->v.Attribute.attr, names);
43554396
} else {
43564397
VISIT(c, expr, meth->v.Attribute.value);
43574398
loc = update_start_location_to_match_attr(c, loc, meth);
@@ -5365,9 +5406,11 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
53655406
return compiler_formatted_value(c, e);
53665407
/* The following exprs can be assignment targets. */
53675408
case Attribute_kind:
5368-
if (e->v.Attribute.ctx == Load && is_zero_arg_super_call(c, e->v.Attribute.value)) {
5369-
RETURN_IF_ERROR(load_args_for_zero_super(c, e));
5370-
ADDOP_NAME(c, loc, LOAD_ZERO_SUPER_ATTR, e->v.Attribute.attr, names);
5409+
if (e->v.Attribute.ctx == Load && can_optimize_super_call(c, e->v.Attribute.value)) {
5410+
RETURN_IF_ERROR(load_args_for_super(c, e->v.Attribute.value));
5411+
int opcode = asdl_seq_LEN(e->v.Attribute.value->v.Call.args) ?
5412+
LOAD_SUPER_ATTR : LOAD_ZERO_SUPER_ATTR;
5413+
ADDOP_NAME(c, loc, opcode, e->v.Attribute.attr, names);
53715414
return SUCCESS;
53725415
}
53735416
VISIT(c, expr, e->v.Attribute.value);

0 commit comments

Comments
 (0)