From 01881a45fccf1751f92973405d41b93cfdbd544a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 4 Jan 2023 16:02:20 -0800 Subject: [PATCH 1/3] Add some tests for generate_cases.py - This doesn't cover everything (far from it) but it's a start. - This uses pytest, which isn't ideal, but was quickest to get started. --- Tools/cases_generator/test_generator.py | 310 ++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 Tools/cases_generator/test_generator.py diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py new file mode 100644 index 00000000000000..3f8e99f63aae2e --- /dev/null +++ b/Tools/cases_generator/test_generator.py @@ -0,0 +1,310 @@ +# Sorry for using pytest, these tests are mostly just for me. +# Use pytest -vv for best results. + +import tempfile + +import generate_cases + + +def run_test(input: str, expected: str): + temp_input = tempfile.NamedTemporaryFile("w+") + temp_input.write(generate_cases.BEGIN_MARKER) + temp_input.write(input) + temp_input.write(generate_cases.END_MARKER) + temp_input.flush() + temp_output = tempfile.NamedTemporaryFile("w+") + a = generate_cases.Analyzer(temp_input.name, temp_output.name) + a.parse() + a.analyze() + if a.errors: + raise RuntimeError("Found {a.errors} errors") + a.write_instructions() + temp_output.seek(0) + lines = temp_output.readlines() + while lines and lines[0].startswith("// "): + lines.pop(0) + actual = "".join(lines) + assert actual.rstrip() == expected.rstrip() + +def test_legacy(): + input = """ + inst(OP) { + spam(); + } + """ + output = """ + TARGET(OP) { + spam(); + DISPATCH(); + } + """ + run_test(input, output) + +def test_inst_no_args(): + input = """ + inst(OP, (--)) { + spam(); + } + """ + output = """ + TARGET(OP) { + spam(); + DISPATCH(); + } + """ + run_test(input, output) + +def test_inst_one_pop(): + input = """ + inst(OP, (value --)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *value = PEEK(1); + spam(); + STACK_SHRINK(1); + DISPATCH(); + } + """ + run_test(input, output) + +def test_inst_one_push(): + input = """ + inst(OP, (-- res)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *res; + spam(); + STACK_GROW(1); + POKE(1, res); + DISPATCH(); + } + """ + run_test(input, output) + +def test_inst_one_push_one_pop(): + input = """ + inst(OP, (value -- res)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *value = PEEK(1); + PyObject *res; + spam(); + POKE(1, res); + DISPATCH(); + } + """ + run_test(input, output) + +def test_binary_op(): + input = """ + inst(OP, (left, right -- res)) { + spam(); + } + """ + output = """ + TARGET(OP) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *res; + spam(); + STACK_SHRINK(1); + POKE(1, res); + DISPATCH(); + } + """ + run_test(input, output) + +def test_predictions(): + input = """ + inst(OP1, (--)) { + } + inst(OP2, (--)) { + } + inst(OP3, (--)) { + DEOPT_IF(xxx, OP1); + PREDICT(OP2); + } + """ + output = """ + TARGET(OP1) { + PREDICTED(OP1); + DISPATCH(); + } + + TARGET(OP2) { + PREDICTED(OP2); + DISPATCH(); + } + + TARGET(OP3) { + DEOPT_IF(xxx, OP1); + PREDICT(OP2); + DISPATCH(); + } + """ + run_test(input, output) + +def test_error_if_plain(): + input = """ + inst(OP, (--)) { + ERROR_IF(cond, label); + } + """ + output = """ + TARGET(OP) { + if (cond) goto label; + DISPATCH(); + } + """ + run_test(input, output) + +def test_error_if_pop(): + input = """ + inst(OP, (left, right -- res)) { + ERROR_IF(cond, label); + } + """ + output = """ + TARGET(OP) { + PyObject *right = PEEK(1); + PyObject *left = PEEK(2); + PyObject *res; + if (cond) goto pop_2_label; + STACK_SHRINK(1); + POKE(1, res); + DISPATCH(); + } + """ + run_test(input, output) + +def test_cache_effect(): + input = """ + inst(OP, (counter/1, extra/2, value --)) { + } + """ + output = """ + TARGET(OP) { + PyObject *value = PEEK(1); + uint16_t counter = read_u16(&next_instr[0].cache); + uint32_t extra = read_u32(&next_instr[1].cache); + STACK_SHRINK(1); + JUMPBY(3); + DISPATCH(); + } + """ + run_test(input, output) + +def test_suppress_dispatch(): + input = """ + inst(OP, (--)) { + goto somewhere; + } + """ + output = """ + TARGET(OP) { + goto somewhere; + } + """ + run_test(input, output) + +def test_super_instruction(): + # TODO: Test cache effect + input = """ + inst(OP1, (counter/1, arg --)) { + op1(); + } + inst(OP2, (extra/2, arg --)) { + op2(); + } + super(OP) = OP1 + OP2; + """ + output = """ + TARGET(OP1) { + PyObject *arg = PEEK(1); + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + STACK_SHRINK(1); + JUMPBY(1); + DISPATCH(); + } + + TARGET(OP2) { + PyObject *arg = PEEK(1); + uint32_t extra = read_u32(&next_instr[0].cache); + op2(); + STACK_SHRINK(1); + JUMPBY(2); + DISPATCH(); + } + + TARGET(OP) { + PyObject *_tmp_1 = PEEK(1); + PyObject *_tmp_2 = PEEK(2); + { + PyObject *arg = _tmp_1; + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + } + JUMPBY(1); + NEXTOPARG(); + JUMPBY(1); + { + PyObject *arg = _tmp_2; + uint32_t extra = read_u32(&next_instr[0].cache); + op2(); + } + JUMPBY(2); + STACK_SHRINK(2); + DISPATCH(); + } + """ + run_test(input, output) + +def test_macro_instruction(): + input = """ + inst(OP1, (counter/1, arg --)) { + op1(); + } + op(OP2, (extra/2, arg --)) { + op2(); + } + macro(OP) = OP1 + cache/2 + OP2; + """ + output = """ + TARGET(OP1) { + PyObject *arg = PEEK(1); + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + STACK_SHRINK(1); + JUMPBY(1); + DISPATCH(); + } + + TARGET(OP) { + PyObject *_tmp_1 = PEEK(1); + PyObject *_tmp_2 = PEEK(2); + { + PyObject *arg = _tmp_1; + uint16_t counter = read_u16(&next_instr[0].cache); + op1(); + } + { + PyObject *arg = _tmp_2; + uint32_t extra = read_u32(&next_instr[3].cache); + op2(); + } + JUMPBY(5); + STACK_SHRINK(2); + DISPATCH(); + } + """ + run_test(input, output) From f422ea9305a0c68d38102ec4aa6c2cb323a02aff Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 5 Jan 2023 14:08:26 -0800 Subject: [PATCH 2/3] Rename run_test() to run_cases_test() In anticipation of run_opcode_test(). --- Tools/cases_generator/test_generator.py | 28 ++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 3f8e99f63aae2e..4d06a49bf27e6b 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -6,7 +6,7 @@ import generate_cases -def run_test(input: str, expected: str): +def run_cases_test(input: str, expected: str): temp_input = tempfile.NamedTemporaryFile("w+") temp_input.write(generate_cases.BEGIN_MARKER) temp_input.write(input) @@ -38,7 +38,7 @@ def test_legacy(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_inst_no_args(): input = """ @@ -52,7 +52,7 @@ def test_inst_no_args(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_inst_one_pop(): input = """ @@ -68,7 +68,7 @@ def test_inst_one_pop(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_inst_one_push(): input = """ @@ -85,7 +85,7 @@ def test_inst_one_push(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_inst_one_push_one_pop(): input = """ @@ -102,7 +102,7 @@ def test_inst_one_push_one_pop(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_binary_op(): input = """ @@ -121,7 +121,7 @@ def test_binary_op(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_predictions(): input = """ @@ -151,7 +151,7 @@ def test_predictions(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_error_if_plain(): input = """ @@ -165,7 +165,7 @@ def test_error_if_plain(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_error_if_pop(): input = """ @@ -184,7 +184,7 @@ def test_error_if_pop(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_cache_effect(): input = """ @@ -201,7 +201,7 @@ def test_cache_effect(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_suppress_dispatch(): input = """ @@ -214,7 +214,7 @@ def test_suppress_dispatch(): goto somewhere; } """ - run_test(input, output) + run_cases_test(input, output) def test_super_instruction(): # TODO: Test cache effect @@ -267,7 +267,7 @@ def test_super_instruction(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) def test_macro_instruction(): input = """ @@ -307,4 +307,4 @@ def test_macro_instruction(): DISPATCH(); } """ - run_test(input, output) + run_cases_test(input, output) From e06aa072a25ac89d49c38694f6a10ba4ba007909 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 5 Jan 2023 12:54:06 -0800 Subject: [PATCH 3/3] Fix f-string Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --- Tools/cases_generator/test_generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/cases_generator/test_generator.py b/Tools/cases_generator/test_generator.py index 4d06a49bf27e6b..e707a533426814 100644 --- a/Tools/cases_generator/test_generator.py +++ b/Tools/cases_generator/test_generator.py @@ -17,7 +17,7 @@ def run_cases_test(input: str, expected: str): a.parse() a.analyze() if a.errors: - raise RuntimeError("Found {a.errors} errors") + raise RuntimeError(f"Found {a.errors} errors") a.write_instructions() temp_output.seek(0) lines = temp_output.readlines()