Skip to content

gh-98831: Finish the UNPACK_SEQUENCE family #101666

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

Merged
merged 4 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 17 additions & 20 deletions Python/bytecodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,13 @@ dummy_func(
}
}

family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = {
UNPACK_SEQUENCE,
UNPACK_SEQUENCE_TWO_TUPLE,
UNPACK_SEQUENCE_TUPLE,
UNPACK_SEQUENCE_LIST,
};

inst(UNPACK_SEQUENCE, (unused/1, seq -- unused[oparg])) {
#if ENABLE_SPECIALIZATION
_PyUnpackSequenceCache *cache = (_PyUnpackSequenceCache *)next_instr;
Expand All @@ -877,43 +884,36 @@ dummy_func(
ERROR_IF(res == 0, error);
}

inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- v1, v0)) {
inst(UNPACK_SEQUENCE_TWO_TUPLE, (unused/1, seq -- values[oparg])) {
DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
DEOPT_IF(PyTuple_GET_SIZE(seq) != 2, UNPACK_SEQUENCE);
assert(oparg == 2);
STAT_INC(UNPACK_SEQUENCE, hit);
v1 = Py_NewRef(PyTuple_GET_ITEM(seq, 1));
v0 = Py_NewRef(PyTuple_GET_ITEM(seq, 0));
values[0] = Py_NewRef(PyTuple_GET_ITEM(seq, 1));
values[1] = Py_NewRef(PyTuple_GET_ITEM(seq, 0));
Py_DECREF(seq);
}

// stack effect: (__0 -- __array[oparg])
inst(UNPACK_SEQUENCE_TUPLE) {
PyObject *seq = TOP();
inst(UNPACK_SEQUENCE_TUPLE, (unused/1, seq -- values[oparg])) {
DEOPT_IF(!PyTuple_CheckExact(seq), UNPACK_SEQUENCE);
DEOPT_IF(PyTuple_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
STAT_INC(UNPACK_SEQUENCE, hit);
STACK_SHRINK(1);
PyObject **items = _PyTuple_ITEMS(seq);
while (oparg--) {
PUSH(Py_NewRef(items[oparg]));
for (int i = oparg; --i >= 0; ) {
*values++ = Py_NewRef(items[i]);
}
Py_DECREF(seq);
JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
}

// stack effect: (__0 -- __array[oparg])
inst(UNPACK_SEQUENCE_LIST) {
PyObject *seq = TOP();
inst(UNPACK_SEQUENCE_LIST, (unused/1, seq -- values[oparg])) {
DEOPT_IF(!PyList_CheckExact(seq), UNPACK_SEQUENCE);
DEOPT_IF(PyList_GET_SIZE(seq) != oparg, UNPACK_SEQUENCE);
STAT_INC(UNPACK_SEQUENCE, hit);
STACK_SHRINK(1);
PyObject **items = _PyList_ITEMS(seq);
while (oparg--) {
PUSH(Py_NewRef(items[oparg]));
for (int i = oparg; --i >= 0; ) {
*values++ = Py_NewRef(items[i]);
}
Py_DECREF(seq);
JUMPBY(INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE);
}

inst(UNPACK_EX, (seq -- unused[oparg & 0xFF], unused, unused[oparg >> 8])) {
Expand Down Expand Up @@ -3168,6 +3168,3 @@ family(call, INLINE_CACHE_ENTRIES_CALL) = {
CALL_NO_KW_METHOD_DESCRIPTOR_O, CALL_NO_KW_STR_1, CALL_NO_KW_TUPLE_1,
CALL_NO_KW_TYPE_1 };
family(store_fast) = { STORE_FAST, STORE_FAST__LOAD_FAST, STORE_FAST__STORE_FAST };
family(unpack_sequence, INLINE_CACHE_ENTRIES_UNPACK_SEQUENCE) = {
UNPACK_SEQUENCE, UNPACK_SEQUENCE_LIST,
UNPACK_SEQUENCE_TUPLE, UNPACK_SEQUENCE_TWO_TUPLE };
38 changes: 21 additions & 17 deletions Python/generated_cases.c.h

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

16 changes: 8 additions & 8 deletions Python/opcode_metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ _PyOpcode_num_popped(int opcode, int oparg, bool jump) {
case UNPACK_SEQUENCE_TWO_TUPLE:
return 1;
case UNPACK_SEQUENCE_TUPLE:
return -1;
return 1;
case UNPACK_SEQUENCE_LIST:
return -1;
return 1;
case UNPACK_EX:
return 1;
case STORE_ATTR:
Expand Down Expand Up @@ -469,11 +469,11 @@ _PyOpcode_num_pushed(int opcode, int oparg, bool jump) {
case UNPACK_SEQUENCE:
return oparg;
case UNPACK_SEQUENCE_TWO_TUPLE:
return 2;
return oparg;
case UNPACK_SEQUENCE_TUPLE:
return -1;
return oparg;
case UNPACK_SEQUENCE_LIST:
return -1;
return oparg;
case UNPACK_EX:
return (oparg & 0xFF) + (oparg >> 8) + 1;
case STORE_ATTR:
Expand Down Expand Up @@ -760,9 +760,9 @@ struct opcode_metadata {
[STORE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[DELETE_NAME] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[UNPACK_SEQUENCE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IXC },
[UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[UNPACK_SEQUENCE_TWO_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[UNPACK_SEQUENCE_TUPLE] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[UNPACK_SEQUENCE_LIST] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC },
[UNPACK_EX] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[STORE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IBC000 },
[DELETE_ATTR] = { DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
Expand Down
25 changes: 18 additions & 7 deletions Tools/cases_generator/generate_cases.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,8 @@ def assign(self, dst: StackEffect, src: StackEffect):
stmt = f"if ({src.cond}) {{ {stmt} }}"
self.emit(stmt)
elif m := re.match(r"^&PEEK\(.*\)$", dst.name):
# NOTE: MOVE_ITEMS() does not actually exist.
# The only supported output array forms are:
# - unused[...]
# - X[...] where X[...] matches an input array exactly
self.emit(f"MOVE_ITEMS({dst.name}, {src.name}, {src.size});")
# The user code is responsible for writing to the output array.
pass
elif m := re.match(r"^REG\(oparg(\d+)\)$", dst.name):
self.emit(f"Py_XSETREF({dst.name}, {cast}{src.name});")
else:
Expand Down Expand Up @@ -309,10 +306,24 @@ def write(self, out: Formatter) -> None:
out.declare(ieffect, src)

# Write output stack effect variable declarations
isize = string_effect_size(list_effect_size(self.input_effects))
input_names = {ieffect.name for ieffect in self.input_effects}
for oeffect in self.output_effects:
for i, oeffect in enumerate(self.output_effects):
if oeffect.name not in input_names:
out.declare(oeffect, None)
if oeffect.size:
osize = string_effect_size(
list_effect_size([oeff for oeff in self.output_effects[:i]])
)
offset = "stack_pointer"
if isize != osize:
if isize != "0":
offset += f" - ({isize})"
if osize != "0":
offset += f" + {osize}"
src = StackEffect(offset, "PyObject **")
out.declare(oeffect, src)
else:
out.declare(oeffect, None)

# out.emit(f"JUMPBY(OPSIZE({self.inst.name}) - 1);")

Expand Down
19 changes: 8 additions & 11 deletions Tools/cases_generator/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -424,20 +424,18 @@ def test_array_input():

def test_array_output():
input = """
inst(OP, (-- below, values[oparg*3], above)) {
spam();
inst(OP, (unused, unused -- below, values[oparg*3], above)) {
spam(values, oparg);
}
"""
output = """
TARGET(OP) {
PyObject *below;
PyObject **values;
PyObject **values = stack_pointer - (2) + 1;
PyObject *above;
spam();
STACK_GROW(2);
spam(values, oparg);
STACK_GROW(oparg*3);
POKE(1, above);
MOVE_ITEMS(&PEEK(1 + oparg*3), values, oparg*3);
POKE(2 + oparg*3, below);
DISPATCH();
}
Expand All @@ -446,18 +444,17 @@ def test_array_output():

def test_array_input_output():
input = """
inst(OP, (below, values[oparg] -- values[oparg], above)) {
spam();
inst(OP, (values[oparg] -- values[oparg], above)) {
spam(values, oparg);
}
"""
output = """
TARGET(OP) {
PyObject **values = &PEEK(oparg);
PyObject *below = PEEK(1 + oparg);
PyObject *above;
spam();
spam(values, oparg);
STACK_GROW(1);
POKE(1, above);
MOVE_ITEMS(&PEEK(1 + oparg), values, oparg);
DISPATCH();
}
"""
Expand Down