gh-98831: rewrite pattern matching opcodes in the instruction definition DSL (#101287)

This commit is contained in:
Irit Katriel 2023-01-24 22:39:13 +00:00 committed by GitHub
parent f02fa64bf2
commit 1a9d8c750b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 73 deletions

View file

@ -2092,61 +2092,37 @@ dummy_func(
PUSH(len_o); PUSH(len_o);
} }
// stack effect: (__0, __1 -- ) inst(MATCH_CLASS, (subject, type, names -- attrs)) {
inst(MATCH_CLASS) {
// Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
// None on failure. // None on failure.
PyObject *names = POP();
PyObject *type = POP();
PyObject *subject = TOP();
assert(PyTuple_CheckExact(names)); assert(PyTuple_CheckExact(names));
PyObject *attrs = match_class(tstate, subject, type, oparg, names); attrs = match_class(tstate, subject, type, oparg, names);
Py_DECREF(names); DECREF_INPUTS();
Py_DECREF(type);
if (attrs) { if (attrs) {
// Success! assert(PyTuple_CheckExact(attrs)); // Success!
assert(PyTuple_CheckExact(attrs));
SET_TOP(attrs);
}
else if (_PyErr_Occurred(tstate)) {
// Error!
goto error;
} }
else { else {
// Failure! ERROR_IF(_PyErr_Occurred(tstate), error); // Error!
SET_TOP(Py_NewRef(Py_None)); attrs = Py_NewRef(Py_None); // Failure!
} }
Py_DECREF(subject);
} }
// stack effect: ( -- __0) inst(MATCH_MAPPING, (subject -- subject, res)) {
inst(MATCH_MAPPING) {
PyObject *subject = TOP();
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING;
PyObject *res = match ? Py_True : Py_False; res = Py_NewRef(match ? Py_True : Py_False);
PUSH(Py_NewRef(res));
PREDICT(POP_JUMP_IF_FALSE); PREDICT(POP_JUMP_IF_FALSE);
} }
// stack effect: ( -- __0) inst(MATCH_SEQUENCE, (subject -- subject, res)) {
inst(MATCH_SEQUENCE) {
PyObject *subject = TOP();
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE;
PyObject *res = match ? Py_True : Py_False; res = Py_NewRef(match ? Py_True : Py_False);
PUSH(Py_NewRef(res));
PREDICT(POP_JUMP_IF_FALSE); PREDICT(POP_JUMP_IF_FALSE);
} }
// stack effect: ( -- __0) inst(MATCH_KEYS, (subject, keys -- subject, keys, values_or_none)) {
inst(MATCH_KEYS) {
// On successful match, PUSH(values). Otherwise, PUSH(None). // On successful match, PUSH(values). Otherwise, PUSH(None).
PyObject *keys = TOP(); values_or_none = match_keys(tstate, subject, keys);
PyObject *subject = SECOND(); ERROR_IF(values_or_none == NULL, error);
PyObject *values_or_none = match_keys(tstate, subject, keys);
if (values_or_none == NULL) {
goto error;
}
PUSH(values_or_none);
} }
// stack effect: ( -- ) // stack effect: ( -- )

View file

@ -2479,59 +2479,60 @@
} }
TARGET(MATCH_CLASS) { TARGET(MATCH_CLASS) {
PyObject *names = PEEK(1);
PyObject *type = PEEK(2);
PyObject *subject = PEEK(3);
PyObject *attrs;
// Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or
// None on failure. // None on failure.
PyObject *names = POP();
PyObject *type = POP();
PyObject *subject = TOP();
assert(PyTuple_CheckExact(names)); assert(PyTuple_CheckExact(names));
PyObject *attrs = match_class(tstate, subject, type, oparg, names); attrs = match_class(tstate, subject, type, oparg, names);
Py_DECREF(names); Py_DECREF(subject);
Py_DECREF(type); Py_DECREF(type);
Py_DECREF(names);
if (attrs) { if (attrs) {
// Success! assert(PyTuple_CheckExact(attrs)); // Success!
assert(PyTuple_CheckExact(attrs));
SET_TOP(attrs);
}
else if (_PyErr_Occurred(tstate)) {
// Error!
goto error;
} }
else { else {
// Failure! if (_PyErr_Occurred(tstate)) goto pop_3_error;
SET_TOP(Py_NewRef(Py_None)); attrs = Py_NewRef(Py_None); // Failure!
} }
Py_DECREF(subject); STACK_SHRINK(2);
POKE(1, attrs);
DISPATCH(); DISPATCH();
} }
TARGET(MATCH_MAPPING) { TARGET(MATCH_MAPPING) {
PyObject *subject = TOP(); PyObject *subject = PEEK(1);
PyObject *res;
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING;
PyObject *res = match ? Py_True : Py_False; res = Py_NewRef(match ? Py_True : Py_False);
PUSH(Py_NewRef(res)); STACK_GROW(1);
POKE(1, res);
PREDICT(POP_JUMP_IF_FALSE); PREDICT(POP_JUMP_IF_FALSE);
DISPATCH(); DISPATCH();
} }
TARGET(MATCH_SEQUENCE) { TARGET(MATCH_SEQUENCE) {
PyObject *subject = TOP(); PyObject *subject = PEEK(1);
PyObject *res;
int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE;
PyObject *res = match ? Py_True : Py_False; res = Py_NewRef(match ? Py_True : Py_False);
PUSH(Py_NewRef(res)); STACK_GROW(1);
POKE(1, res);
PREDICT(POP_JUMP_IF_FALSE); PREDICT(POP_JUMP_IF_FALSE);
DISPATCH(); DISPATCH();
} }
TARGET(MATCH_KEYS) { TARGET(MATCH_KEYS) {
PyObject *keys = PEEK(1);
PyObject *subject = PEEK(2);
PyObject *values_or_none;
// On successful match, PUSH(values). Otherwise, PUSH(None). // On successful match, PUSH(values). Otherwise, PUSH(None).
PyObject *keys = TOP(); values_or_none = match_keys(tstate, subject, keys);
PyObject *subject = SECOND(); if (values_or_none == NULL) goto error;
PyObject *values_or_none = match_keys(tstate, subject, keys); STACK_GROW(1);
if (values_or_none == NULL) { POKE(1, values_or_none);
goto error;
}
PUSH(values_or_none);
DISPATCH(); DISPATCH();
} }

View file

@ -133,10 +133,10 @@ static const struct {
[JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [JUMP_IF_TRUE_OR_POP] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [JUMP_BACKWARD_NO_INTERRUPT] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_LEN] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[MATCH_CLASS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [MATCH_CLASS] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },
[MATCH_MAPPING] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [MATCH_MAPPING] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[MATCH_SEQUENCE] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [MATCH_SEQUENCE] = { 1, 2, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[MATCH_KEYS] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [MATCH_KEYS] = { 2, 3, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX }, [GET_YIELD_FROM_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IX },
[FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, [FOR_ITER] = { -1, -1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB },

View file

@ -26,7 +26,7 @@ DEFAULT_METADATA_OUTPUT = os.path.relpath(
) )
BEGIN_MARKER = "// BEGIN BYTECODES //" BEGIN_MARKER = "// BEGIN BYTECODES //"
END_MARKER = "// END BYTECODES //" END_MARKER = "// END BYTECODES //"
RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*$" RE_PREDICTED = r"^\s*(?:PREDICT\(|GO_TO_INSTRUCTION\(|DEOPT_IF\(.*?,\s*)(\w+)\);\s*(?://.*)?$"
UNUSED = "unused" UNUSED = "unused"
BITS_PER_CODE_UNIT = 16 BITS_PER_CODE_UNIT = 16
@ -354,7 +354,7 @@ class Instruction:
assert dedent <= 0 assert dedent <= 0
extra = " " * -dedent extra = " " * -dedent
for line in self.block_text: for line in self.block_text:
if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*$", line): if m := re.match(r"(\s*)ERROR_IF\((.+), (\w+)\);\s*(?://.*)?$", line):
space, cond, label = m.groups() space, cond, label = m.groups()
# ERROR_IF() must pop the inputs from the stack. # ERROR_IF() must pop the inputs from the stack.
# The code block is responsible for DECREF()ing them. # The code block is responsible for DECREF()ing them.
@ -378,7 +378,7 @@ class Instruction:
) )
else: else:
out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n") out.write_raw(f"{extra}{space}if ({cond}) goto {label};\n")
elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*$", line): elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*(?://.*)?$", line):
if not self.register: if not self.register:
space = m.group(1) space = m.group(1)
for ieff in self.input_effects: for ieff in self.input_effects:
@ -964,7 +964,7 @@ def extract_block_text(block: parser.Block) -> tuple[list[str], list[str]]:
# Separate PREDICT(...) macros from end # Separate PREDICT(...) macros from end
predictions: list[str] = [] predictions: list[str] = []
while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*$", blocklines[-1])): while blocklines and (m := re.match(r"^\s*PREDICT\((\w+)\);\s*(?://.*)?$", blocklines[-1])):
predictions.insert(0, m.group(1)) predictions.insert(0, m.group(1))
blocklines.pop() blocklines.pop()

View file

@ -215,6 +215,20 @@ def test_error_if_plain():
""" """
run_cases_test(input, output) run_cases_test(input, output)
def test_error_if_plain_with_comment():
input = """
inst(OP, (--)) {
ERROR_IF(cond, label); // Comment is ok
}
"""
output = """
TARGET(OP) {
if (cond) goto label;
DISPATCH();
}
"""
run_cases_test(input, output)
def test_error_if_pop(): def test_error_if_pop():
input = """ input = """
inst(OP, (left, right -- res)) { inst(OP, (left, right -- res)) {