From 1a9d8c750be83e6abb65769d312361fe9742de02 Mon Sep 17 00:00:00 2001 From: Irit Katriel <1055913+iritkatriel@users.noreply.github.com> Date: Tue, 24 Jan 2023 22:39:13 +0000 Subject: gh-98831: rewrite pattern matching opcodes in the instruction definition DSL (#101287) --- Python/bytecodes.c | 50 ++++++++--------------------- Python/generated_cases.c.h | 57 +++++++++++++++++---------------- Python/opcode_metadata.h | 8 ++--- Tools/cases_generator/generate_cases.py | 8 ++--- Tools/cases_generator/test_generator.py | 14 ++++++++ 5 files changed, 64 insertions(+), 73 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index ac54791..d3e242b 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2092,61 +2092,37 @@ dummy_func( PUSH(len_o); } - // stack effect: (__0, __1 -- ) - inst(MATCH_CLASS) { + inst(MATCH_CLASS, (subject, type, names -- attrs)) { // Pop TOS and TOS1. Set TOS to a tuple of attributes on success, or // None on failure. - PyObject *names = POP(); - PyObject *type = POP(); - PyObject *subject = TOP(); assert(PyTuple_CheckExact(names)); - PyObject *attrs = match_class(tstate, subject, type, oparg, names); - Py_DECREF(names); - Py_DECREF(type); + attrs = match_class(tstate, subject, type, oparg, names); + DECREF_INPUTS(); if (attrs) { - // Success! - assert(PyTuple_CheckExact(attrs)); - SET_TOP(attrs); - } - else if (_PyErr_Occurred(tstate)) { - // Error! - goto error; + assert(PyTuple_CheckExact(attrs)); // Success! } else { - // Failure! - SET_TOP(Py_NewRef(Py_None)); + ERROR_IF(_PyErr_Occurred(tstate), error); // Error! + attrs = Py_NewRef(Py_None); // Failure! } - Py_DECREF(subject); } - // stack effect: ( -- __0) - inst(MATCH_MAPPING) { - PyObject *subject = TOP(); + inst(MATCH_MAPPING, (subject -- subject, res)) { int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); PREDICT(POP_JUMP_IF_FALSE); } - // stack effect: ( -- __0) - inst(MATCH_SEQUENCE) { - PyObject *subject = TOP(); + inst(MATCH_SEQUENCE, (subject -- subject, res)) { int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); PREDICT(POP_JUMP_IF_FALSE); } - // stack effect: ( -- __0) - inst(MATCH_KEYS) { + inst(MATCH_KEYS, (subject, keys -- subject, keys, values_or_none)) { // On successful match, PUSH(values). Otherwise, PUSH(None). - PyObject *keys = TOP(); - PyObject *subject = SECOND(); - PyObject *values_or_none = match_keys(tstate, subject, keys); - if (values_or_none == NULL) { - goto error; - } - PUSH(values_or_none); + values_or_none = match_keys(tstate, subject, keys); + ERROR_IF(values_or_none == NULL, error); } // stack effect: ( -- ) diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 5dcc8ee..7d3396a 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2479,59 +2479,60 @@ } 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 // None on failure. - PyObject *names = POP(); - PyObject *type = POP(); - PyObject *subject = TOP(); assert(PyTuple_CheckExact(names)); - PyObject *attrs = match_class(tstate, subject, type, oparg, names); - Py_DECREF(names); + attrs = match_class(tstate, subject, type, oparg, names); + Py_DECREF(subject); Py_DECREF(type); + Py_DECREF(names); if (attrs) { - // Success! - assert(PyTuple_CheckExact(attrs)); - SET_TOP(attrs); - } - else if (_PyErr_Occurred(tstate)) { - // Error! - goto error; + assert(PyTuple_CheckExact(attrs)); // Success! } else { - // Failure! - SET_TOP(Py_NewRef(Py_None)); + if (_PyErr_Occurred(tstate)) goto pop_3_error; + attrs = Py_NewRef(Py_None); // Failure! } - Py_DECREF(subject); + STACK_SHRINK(2); + POKE(1, attrs); DISPATCH(); } TARGET(MATCH_MAPPING) { - PyObject *subject = TOP(); + PyObject *subject = PEEK(1); + PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_MAPPING; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); + STACK_GROW(1); + POKE(1, res); PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_SEQUENCE) { - PyObject *subject = TOP(); + PyObject *subject = PEEK(1); + PyObject *res; int match = Py_TYPE(subject)->tp_flags & Py_TPFLAGS_SEQUENCE; - PyObject *res = match ? Py_True : Py_False; - PUSH(Py_NewRef(res)); + res = Py_NewRef(match ? Py_True : Py_False); + STACK_GROW(1); + POKE(1, res); PREDICT(POP_JUMP_IF_FALSE); DISPATCH(); } TARGET(MATCH_KEYS) { + PyObject *keys = PEEK(1); + PyObject *subject = PEEK(2); + PyObject *values_or_none; // On successful match, PUSH(values). Otherwise, PUSH(None). - PyObject *keys = TOP(); - PyObject *subject = SECOND(); - PyObject *values_or_none = match_keys(tstate, subject, keys); - if (values_or_none == NULL) { - goto error; - } - PUSH(values_or_none); + values_or_none = match_keys(tstate, subject, keys); + if (values_or_none == NULL) goto error; + STACK_GROW(1); + POKE(1, values_or_none); DISPATCH(); } diff --git a/Python/opcode_metadata.h b/Python/opcode_metadata.h index 1fb0acc..f9c6401 100644 --- a/Python/opcode_metadata.h +++ b/Python/opcode_metadata.h @@ -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_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 }, - [MATCH_CLASS] = { -1, -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_SEQUENCE] = { -1, -1, 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_CLASS] = { 3, 1, DIR_NONE, DIR_NONE, DIR_NONE, true, INSTR_FMT_IB }, + [MATCH_MAPPING] = { 1, 2, 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] = { 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_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 }, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 90f101a..429c9d3 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -26,7 +26,7 @@ DEFAULT_METADATA_OUTPUT = os.path.relpath( ) BEGIN_MARKER = "// BEGIN 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" BITS_PER_CODE_UNIT = 16 @@ -354,7 +354,7 @@ class Instruction: assert dedent <= 0 extra = " " * -dedent 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() # ERROR_IF() must pop the inputs from the stack. # The code block is responsible for DECREF()ing them. @@ -378,7 +378,7 @@ class Instruction: ) else: 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: space = m.group(1) 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 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)) blocklines.pop() diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index ae0c1e2..c59aae8 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -215,6 +215,20 @@ def test_error_if_plain(): """ 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(): input = """ inst(OP, (left, right -- res)) { -- cgit v0.12