Skip to content

bpo-45260: Add superinstruction, UNPACK_SEQUENCE__STORE_FAST #28519

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

Closed
wants to merge 8 commits into from
11 changes: 6 additions & 5 deletions Include/opcode.h

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

1 change: 1 addition & 0 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def jabs_op(name, op):
haslocal.append(125)
def_op('DELETE_FAST', 126) # Local variable number
haslocal.append(126)
def_op('UNPACK_SEQUENCE__STORE_FAST', 127)

def_op('GEN_START', 129) # Kind of generator/coroutine
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
Expand Down
3 changes: 2 additions & 1 deletion Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,8 @@ def test_widths(self):
for opcode, opname in enumerate(dis.opname):
if opname in ('BUILD_MAP_UNPACK_WITH_CALL',
'BUILD_TUPLE_UNPACK_WITH_CALL',
'JUMP_IF_NOT_EXC_MATCH'):
'JUMP_IF_NOT_EXC_MATCH',
'UNPACK_SEQUENCE__STORE_FAST'):
continue
with self.subTest(opname=opname):
width = dis._OPNAME_WIDTH
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add superinstruction UNPACK_SEQUENCE__STORE_FAST using the PEP 659 machinery.
46 changes: 46 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
}

TARGET(LOAD_FAST) {
PREDICTED(LOAD_FAST);
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
Expand All @@ -1730,6 +1731,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
}

TARGET(LOAD_FAST__LOAD_FAST) {
PREDICTED(LOAD_FAST__LOAD_FAST);
PyObject *value = GETLOCAL(oparg);
if (value == NULL) {
goto unbound_local_error;
Expand Down Expand Up @@ -2962,6 +2964,49 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
DISPATCH();
}

TARGET(UNPACK_SEQUENCE__STORE_FAST) {
PREDICTED(UNPACK_SEQUENCE__STORE_FAST);
PyObject *seq = POP(), *item, **items;
if (PyTuple_CheckExact(seq) &&
PyTuple_GET_SIZE(seq) == oparg) {
items = ((PyTupleObject *)seq)->ob_item;
int i = -1, next_oparg;
while (++i < oparg) {
item = items[i];
Py_INCREF(item);
next_oparg= _Py_OPARG(*(next_instr++));
SETLOCAL(next_oparg, item);
}
} else if (PyList_CheckExact(seq) &&
PyList_GET_SIZE(seq) == oparg) {
items = ((PyListObject *)seq)->ob_item;
int i = -1, next_oparg;
while (++i < oparg) {
item = items[i];
Py_INCREF(item);
next_oparg= _Py_OPARG(*(next_instr++));
SETLOCAL(next_oparg, item);
}
} else if (unpack_iterable(tstate, seq, oparg, -1,
stack_pointer + oparg)) {
items = stack_pointer + oparg - 1;
int i = -1, next_oparg;
while (++i < oparg) {
item = *(items - i);
next_oparg= _Py_OPARG(*(next_instr++));
SETLOCAL(next_oparg, item);
}
} else {
/* unpack_iterable() raised an exception */
Py_DECREF(seq);
goto error;
}
Py_DECREF(seq);
PREDICT(LOAD_FAST__LOAD_FAST);
PREDICT(LOAD_FAST);
DISPATCH();
}

TARGET(UNPACK_EX) {
int totalargs = 1 + (oparg & 0xFF) + (oparg >> 8);
PyObject *seq = POP();
Expand Down Expand Up @@ -4230,6 +4275,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
if (next != NULL) {
PUSH(next);
PREDICT(STORE_FAST);
PREDICT(UNPACK_SEQUENCE__STORE_FAST);
PREDICT(UNPACK_SEQUENCE);
DISPATCH();
}
Expand Down
31 changes: 31 additions & 0 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,7 @@ stack_effect(int opcode, int oparg, int jump)
case DELETE_NAME:
return 0;
case UNPACK_SEQUENCE:
case UNPACK_SEQUENCE__STORE_FAST:
return oparg-1;
case UNPACK_EX:
return (oparg&0xFF) + (oparg>>8);
Expand Down Expand Up @@ -4009,10 +4010,25 @@ assignment_helper(struct compiler *c, asdl_expr_seq *elts)
{
Py_ssize_t n = asdl_seq_LEN(elts);
RETURN_IF_FALSE(unpack_helper(c, elts));
basicblock *bb = c->u->u_curblock;
int idx = bb->b_iused-1;
for (Py_ssize_t i = 0; i < n; i++) {
expr_ty elt = asdl_seq_GET(elts, i);
VISIT(c, expr, elt->kind != Starred_kind ? elt : elt->v.Starred.value);
}
struct instr *stmt = &bb->b_instr[idx];
if (stmt->i_opcode == UNPACK_SEQUENCE) {
int count = stmt->i_oparg;
int i = 1;
for (; i <= count; ++i) {
if ((stmt + i)->i_opcode != STORE_FAST) {
break;
}
}
if (i > count) {
stmt->i_opcode = UNPACK_SEQUENCE__STORE_FAST;
}
}
return 1;
}

Expand Down Expand Up @@ -5962,6 +5978,8 @@ pattern_helper_sequence_unpack(struct compiler *c, asdl_pattern_seq *patterns,
Py_ssize_t star, pattern_context *pc)
{
RETURN_IF_FALSE(pattern_unpack_helper(c, patterns));
basicblock *bb = c->u->u_curblock;
int idx = bb->b_iused-1;
Py_ssize_t size = asdl_seq_LEN(patterns);
// We've now got a bunch of new subjects on the stack. They need to remain
// there after each subpattern match:
Expand All @@ -5972,6 +5990,19 @@ pattern_helper_sequence_unpack(struct compiler *c, asdl_pattern_seq *patterns,
pattern_ty pattern = asdl_seq_GET(patterns, i);
RETURN_IF_FALSE(compiler_pattern_subpattern(c, pattern, pc));
}
struct instr *stmt = &bb->b_instr[idx];
if (stmt->i_opcode == UNPACK_SEQUENCE) {
int count = stmt->i_oparg;
int i = 1;
for (; i <= count; ++i) {
if ((stmt + i)->i_opcode != STORE_FAST) {
break;
}
}
if (i > count) {
stmt->i_opcode = UNPACK_SEQUENCE__STORE_FAST;
}
}
return 1;
}

Expand Down
10 changes: 5 additions & 5 deletions Python/opcode_targets.h

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

4 changes: 4 additions & 0 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,10 @@ optimize(SpecializedCacheOrInstruction *quickened, int len)
/* Super instructions don't use the cache,
* so no need to update the offset. */
switch (opcode) {
case UNPACK_SEQUENCE__STORE_FAST: {
i += oparg;
break;
}
case JUMP_ABSOLUTE:
instructions[i] = _Py_MAKECODEUNIT(JUMP_ABSOLUTE_QUICK, oparg);
break;
Expand Down