From 44db70146bdbebcb666f8c7365eb24399ac957e8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 6 Sep 2023 16:33:33 -0700 Subject: [PATCH 01/15] inst() and macro() may need cache size metadata --- Tools/cases_generator/generate_cases.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 1335c0c29ebbf6..6842934c58f819 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -549,10 +549,23 @@ def write_metadata(self, metadata_filename: str, pymetadata_filename: str) -> No "=", ";", ): - for name, _ in self.families.items(): + family_member_names: set[str] = set() + for name, family in self.families.items(): + family_member_names.update(family.members) instr = self.instrs[name] if instr.cache_offset > 0: - self.out.emit(f'[{name}] = {instr.cache_offset},') + self.out.emit(f"[{name}] = {instr.cache_offset},") + for instr in self.instrs.values(): + if ( + not instr.family + and instr.cache_offset + and instr.kind == "inst" + and not instr.name.startswith("INSTRUMENTED_") + ): + self.out.emit(f"[{instr.name}] = {instr.cache_offset},") + for mac in self.macro_instrs.values(): + if mac.name not in family_member_names and mac.cache_offset > 0: + self.out.emit(f"[{mac.name}] = {mac.cache_offset},") # Irregular case: self.out.emit('[JUMP_BACKWARD] = 1,') From ff29ab38b557351d40a643462d19df060cb60fef Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 6 Sep 2023 16:39:13 -0700 Subject: [PATCH 02/15] Add cache entry to *POP_JUMP_IF_* instructions --- Include/internal/pycore_opcode_metadata.h | 20 +++++---- Python/bytecodes.c | 47 +++++++++++++------ Python/generated_cases.c.h | 55 +++++++++++++++++++---- 3 files changed, 92 insertions(+), 30 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index fa4cbd9ed31c01..ce5ce53a436873 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1368,11 +1368,11 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [JUMP] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [JUMP_NO_INTERRUPT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [ENTER_EXECUTOR] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG }, - [POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, - [POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, + [POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, + [POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [IS_NONE] = { true, INSTR_FMT_IX, 0 }, - [POP_JUMP_IF_NONE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, - [POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, + [POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, + [POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [JUMP_BACKWARD_NO_INTERRUPT] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_JUMP_FLAG }, [GET_LEN] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG }, [MATCH_CLASS] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG }, @@ -1456,10 +1456,10 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[OPCODE_METADATA_SIZE] = { [INSTRUMENTED_INSTRUCTION] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG }, [INSTRUMENTED_JUMP_FORWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [INSTRUMENTED_JUMP_BACKWARD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_EVAL_BREAK_FLAG }, - [INSTRUMENTED_POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, - [INSTRUMENTED_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, - [INSTRUMENTED_POP_JUMP_IF_NONE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, - [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, + [INSTRUMENTED_POP_JUMP_IF_TRUE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, + [INSTRUMENTED_POP_JUMP_IF_FALSE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, + [INSTRUMENTED_POP_JUMP_IF_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, + [INSTRUMENTED_POP_JUMP_IF_NOT_NONE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG }, [EXTENDED_ARG] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, [CACHE] = { true, INSTR_FMT_IX, 0 }, [RESERVED] = { true, INSTR_FMT_IX, 0 }, @@ -1905,6 +1905,10 @@ const uint8_t _PyOpcode_Caches[256] = { [COMPARE_OP] = 1, [FOR_ITER] = 1, [CALL] = 3, + [POP_JUMP_IF_FALSE] = 1, + [POP_JUMP_IF_TRUE] = 1, + [POP_JUMP_IF_NONE] = 1, + [POP_JUMP_IF_NOT_NONE] = 1, [JUMP_BACKWARD] = 1, }; #endif // NEED_OPCODE_METADATA diff --git a/Python/bytecodes.c b/Python/bytecodes.c index fae1da31875d66..f270053000b2d6 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -2253,14 +2253,22 @@ dummy_func( goto resume_frame; } - inst(POP_JUMP_IF_FALSE, (cond -- )) { + inst(POP_JUMP_IF_FALSE, (unused/1, cond -- )) { assert(PyBool_Check(cond)); - JUMPBY(oparg * Py_IsFalse(cond)); + int flag = Py_IsFalse(cond); + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif + JUMPBY(oparg * flag); } - inst(POP_JUMP_IF_TRUE, (cond -- )) { + inst(POP_JUMP_IF_TRUE, (unused/1, cond -- )) { assert(PyBool_Check(cond)); - JUMPBY(oparg * Py_IsTrue(cond)); + int flag = Py_IsTrue(cond); + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif + JUMPBY(oparg * flag); } op(IS_NONE, (value -- b)) { @@ -3712,47 +3720,60 @@ dummy_func( INSTRUMENTED_JUMP(next_instr-1, next_instr+1-oparg, PY_MONITORING_EVENT_JUMP); } - inst(INSTRUMENTED_POP_JUMP_IF_TRUE, ( -- )) { + inst(INSTRUMENTED_POP_JUMP_IF_TRUE, (unused/1 -- )) { PyObject *cond = POP(); assert(PyBool_Check(cond)); _Py_CODEUNIT *here = next_instr - 1; - int offset = Py_IsTrue(cond) * oparg; + int flag = Py_IsTrue(cond); + int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } - inst(INSTRUMENTED_POP_JUMP_IF_FALSE, ( -- )) { + inst(INSTRUMENTED_POP_JUMP_IF_FALSE, (unused/1 -- )) { PyObject *cond = POP(); assert(PyBool_Check(cond)); _Py_CODEUNIT *here = next_instr - 1; - int offset = Py_IsFalse(cond) * oparg; + int flag = Py_IsFalse(cond); + int offset = flag * oparg; INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } - inst(INSTRUMENTED_POP_JUMP_IF_NONE, ( -- )) { + inst(INSTRUMENTED_POP_JUMP_IF_NONE, (unused/1 -- )) { PyObject *value = POP(); - _Py_CODEUNIT *here = next_instr-1; + _Py_CODEUNIT *here = next_instr - 1; + int flag = Py_IsNone(value); int offset; - if (Py_IsNone(value)) { + if (flag) { offset = oparg; } else { Py_DECREF(value); offset = 0; } + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } - inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, ( -- )) { + inst(INSTRUMENTED_POP_JUMP_IF_NOT_NONE, (unused/1 -- )) { PyObject *value = POP(); _Py_CODEUNIT *here = next_instr-1; int offset; - if (Py_IsNone(value)) { + int flag = Py_IsNone(value); + if (flag) { offset = 0; } else { Py_DECREF(value); offset = oparg; } + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | !flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 34cea84245f591..a1a9ed8ed8cdac 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -2997,8 +2997,13 @@ PyObject *cond; cond = stack_pointer[-1]; assert(PyBool_Check(cond)); - JUMPBY(oparg * Py_IsFalse(cond)); + int flag = Py_IsFalse(cond); + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif + JUMPBY(oparg * flag); STACK_SHRINK(1); + next_instr += 1; DISPATCH(); } @@ -3006,8 +3011,13 @@ PyObject *cond; cond = stack_pointer[-1]; assert(PyBool_Check(cond)); - JUMPBY(oparg * Py_IsTrue(cond)); + int flag = Py_IsTrue(cond); + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif + JUMPBY(oparg * flag); STACK_SHRINK(1); + next_instr += 1; DISPATCH(); } @@ -3030,9 +3040,14 @@ cond = b; { assert(PyBool_Check(cond)); - JUMPBY(oparg * Py_IsTrue(cond)); + int flag = Py_IsTrue(cond); + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif + JUMPBY(oparg * flag); } STACK_SHRINK(1); + next_instr += 1; DISPATCH(); } @@ -3055,9 +3070,14 @@ cond = b; { assert(PyBool_Check(cond)); - JUMPBY(oparg * Py_IsFalse(cond)); + int flag = Py_IsFalse(cond); + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif + JUMPBY(oparg * flag); } STACK_SHRINK(1); + next_instr += 1; DISPATCH(); } @@ -4922,8 +4942,13 @@ PyObject *cond = POP(); assert(PyBool_Check(cond)); _Py_CODEUNIT *here = next_instr - 1; - int offset = Py_IsTrue(cond) * oparg; + int flag = Py_IsTrue(cond); + int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + next_instr += 1; DISPATCH(); } @@ -4931,23 +4956,30 @@ PyObject *cond = POP(); assert(PyBool_Check(cond)); _Py_CODEUNIT *here = next_instr - 1; - int offset = Py_IsFalse(cond) * oparg; + int flag = Py_IsFalse(cond); + int offset = flag * oparg; INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + next_instr += 1; DISPATCH(); } TARGET(INSTRUMENTED_POP_JUMP_IF_NONE) { PyObject *value = POP(); - _Py_CODEUNIT *here = next_instr-1; + _Py_CODEUNIT *here = next_instr - 1; + int flag = Py_IsNone(value); int offset; - if (Py_IsNone(value)) { + if (flag) { offset = oparg; } else { Py_DECREF(value); offset = 0; } + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + next_instr += 1; DISPATCH(); } @@ -4955,14 +4987,19 @@ PyObject *value = POP(); _Py_CODEUNIT *here = next_instr-1; int offset; - if (Py_IsNone(value)) { + int flag = Py_IsNone(value); + if (flag) { offset = 0; } else { Py_DECREF(value); offset = oparg; } + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | !flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); + next_instr += 1; DISPATCH(); } From ebc91a270cae48307d6bca0cb9c31b232de45601 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 6 Sep 2023 17:40:27 -0700 Subject: [PATCH 03/15] Fix test_dis (also fixes test_peepholer) --- Lib/opcode.py | 12 +++ Lib/test/test_dis.py | 219 ++++++++++++++++++++++--------------------- 2 files changed, 123 insertions(+), 108 deletions(-) diff --git a/Lib/opcode.py b/Lib/opcode.py index 386a2fba396a6a..88f4df7c0e8c38 100644 --- a/Lib/opcode.py +++ b/Lib/opcode.py @@ -93,6 +93,18 @@ "counter": 1, "version": 2, }, + "POP_JUMP_IF_TRUE": { + "counter": 1, + }, + "POP_JUMP_IF_FALSE": { + "counter": 1, + }, + "POP_JUMP_IF_NONE": { + "counter": 1, + }, + "POP_JUMP_IF_NOT_NONE": { + "counter": 1, + }, } _inline_cache_entries = { diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index dacd6f6da2c5a9..8748a53c2f8e48 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -404,7 +404,7 @@ def wrap_func_w_kwargs(): %4d LOAD_GLOBAL 0 (Exception) CHECK_EXC_MATCH - POP_JUMP_IF_FALSE 23 (to 80) + POP_JUMP_IF_FALSE 23 (to 82) STORE_FAST 0 (e) %4d LOAD_FAST 0 (e) @@ -492,7 +492,7 @@ def _with(c): %4d >> PUSH_EXC_INFO WITH_EXCEPT_START TO_BOOL - POP_JUMP_IF_TRUE 1 (to 50) + POP_JUMP_IF_TRUE 1 (to 52) RERAISE 2 >> POP_TOP POP_EXCEPT @@ -579,7 +579,7 @@ async def _asyncwith(c): >> CLEANUP_THROW >> END_SEND TO_BOOL - POP_JUMP_IF_TRUE 1 (to 116) + POP_JUMP_IF_TRUE 1 (to 118) RERAISE 2 >> POP_TOP POP_EXCEPT @@ -1713,7 +1713,7 @@ def _prepare_test_cases(): Instruction(opname='LOAD_CONST', opcode=142, arg=1, argval=10, argrepr='10', offset=12, start_offset=12, starts_line=False, line_number=3, is_jump_target=False, positions=None), Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=14, start_offset=14, starts_line=False, line_number=3, is_jump_target=False, positions=None), Instruction(opname='GET_ITER', opcode=31, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, is_jump_target=False, positions=None), - Instruction(opname='FOR_ITER', opcode=114, arg=28, argval=84, argrepr='to 84', offset=24, start_offset=24, starts_line=False, line_number=3, is_jump_target=True, positions=None), + Instruction(opname='FOR_ITER', opcode=114, arg=30, argval=88, argrepr='to 88', offset=24, start_offset=24, starts_line=False, line_number=3, is_jump_target=True, positions=None), Instruction(opname='STORE_FAST', opcode=176, arg=0, argval='i', argrepr='i', offset=28, start_offset=28, starts_line=False, line_number=3, is_jump_target=False, positions=None), Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=30, start_offset=30, starts_line=True, line_number=4, is_jump_target=False, positions=None), Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=40, start_offset=40, starts_line=False, line_number=4, is_jump_target=False, positions=None), @@ -1722,108 +1722,108 @@ def _prepare_test_cases(): Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=52, start_offset=52, starts_line=True, line_number=5, is_jump_target=False, positions=None), Instruction(opname='LOAD_CONST', opcode=142, arg=2, argval=4, argrepr='4', offset=54, start_offset=54, starts_line=False, line_number=5, is_jump_target=False, positions=None), Instruction(opname='COMPARE_OP', opcode=97, arg=18, argval='<', argrepr='bool(<)', offset=56, start_offset=56, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=2, argval=66, argrepr='to 66', offset=60, start_offset=60, starts_line=False, line_number=5, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=123, arg=21, argval=24, argrepr='to 24', offset=62, start_offset=62, starts_line=True, line_number=6, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=66, start_offset=66, starts_line=True, line_number=7, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=3, argval=6, argrepr='6', offset=68, start_offset=68, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=97, arg=148, argval='>', argrepr='bool(>)', offset=70, start_offset=70, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=163, arg=2, argval=80, argrepr='to 80', offset=74, start_offset=74, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=123, arg=28, argval=24, argrepr='to 24', offset=76, start_offset=76, starts_line=False, line_number=7, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=80, start_offset=80, starts_line=True, line_number=8, is_jump_target=True, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=125, arg=12, argval=108, argrepr='to 108', offset=82, start_offset=82, starts_line=False, line_number=8, is_jump_target=False, positions=None), - Instruction(opname='END_FOR', opcode=24, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=3, is_jump_target=True, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=86, start_offset=86, starts_line=True, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=96, start_offset=96, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=98, start_offset=98, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=106, start_offset=106, starts_line=False, line_number=10, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST_CHECK', opcode=146, arg=0, argval='i', argrepr='i', offset=108, start_offset=108, starts_line=True, line_number=11, is_jump_target=True, positions=None), - Instruction(opname='TO_BOOL', opcode=56, arg=None, argval=None, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=37, argval=194, argrepr='to 194', offset=118, start_offset=118, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=120, start_offset=120, starts_line=True, line_number=12, is_jump_target=True, positions=None), - Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=130, start_offset=130, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=132, start_offset=132, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=140, start_offset=140, starts_line=False, line_number=12, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=142, start_offset=142, starts_line=True, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=5, argval=1, argrepr='1', offset=144, start_offset=144, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=67, arg=23, argval=23, argrepr='-=', offset=146, start_offset=146, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=176, arg=0, argval='i', argrepr='i', offset=150, start_offset=150, starts_line=False, line_number=13, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=152, start_offset=152, starts_line=True, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=3, argval=6, argrepr='6', offset=154, start_offset=154, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=97, arg=148, argval='>', argrepr='bool(>)', offset=156, start_offset=156, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=2, argval=166, argrepr='to 166', offset=160, start_offset=160, starts_line=False, line_number=14, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=123, arg=29, argval=108, argrepr='to 108', offset=162, start_offset=162, starts_line=True, line_number=15, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=166, start_offset=166, starts_line=True, line_number=16, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=2, argval=4, argrepr='4', offset=168, start_offset=168, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='COMPARE_OP', opcode=97, arg=18, argval='<', argrepr='bool(<)', offset=170, start_offset=170, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=1, argval=178, argrepr='to 178', offset=174, start_offset=174, starts_line=False, line_number=16, is_jump_target=False, positions=None), - Instruction(opname='JUMP_FORWARD', opcode=125, arg=19, argval=216, argrepr='to 216', offset=176, start_offset=176, starts_line=True, line_number=17, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=178, start_offset=178, starts_line=True, line_number=11, is_jump_target=True, positions=None), - Instruction(opname='TO_BOOL', opcode=56, arg=None, argval=None, argrepr='', offset=180, start_offset=180, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=2, argval=194, argrepr='to 194', offset=188, start_offset=188, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=123, arg=37, argval=120, argrepr='to 120', offset=190, start_offset=190, starts_line=False, line_number=11, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=194, start_offset=194, starts_line=True, line_number=19, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=204, start_offset=204, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=206, start_offset=206, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=214, start_offset=214, starts_line=False, line_number=19, is_jump_target=False, positions=None), - Instruction(opname='NOP', opcode=42, arg=None, argval=None, argrepr='', offset=216, start_offset=216, starts_line=True, line_number=20, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=5, argval=1, argrepr='1', offset=218, start_offset=218, starts_line=True, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=7, argval=0, argrepr='0', offset=220, start_offset=220, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='BINARY_OP', opcode=67, arg=11, argval=11, argrepr='/', offset=222, start_offset=222, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=21, is_jump_target=False, positions=None), - Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=228, start_offset=228, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='BEFORE_WITH', opcode=2, arg=None, argval=None, argrepr='', offset=230, start_offset=230, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='STORE_FAST', opcode=176, arg=1, argval='dodgy', argrepr='dodgy', offset=232, start_offset=232, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=234, start_offset=234, starts_line=True, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=244, start_offset=244, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=246, start_offset=246, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=254, start_offset=254, starts_line=False, line_number=26, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=0, argval=None, argrepr='None', offset=256, start_offset=256, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=0, argval=None, argrepr='None', offset=258, start_offset=258, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=0, argval=None, argrepr='None', offset=260, start_offset=260, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=2, argval=2, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=270, start_offset=270, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=272, start_offset=272, starts_line=True, line_number=28, is_jump_target=True, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=282, start_offset=282, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=284, start_offset=284, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=292, start_offset=292, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='RETURN_CONST', opcode=167, arg=0, argval=None, argrepr='None', offset=294, start_offset=294, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=45, arg=None, argval=None, argrepr='', offset=296, start_offset=296, starts_line=True, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='WITH_EXCEPT_START', opcode=66, arg=None, argval=None, argrepr='', offset=298, start_offset=298, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='TO_BOOL', opcode=56, arg=None, argval=None, argrepr='', offset=300, start_offset=300, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_TRUE', opcode=163, arg=1, argval=312, argrepr='to 312', offset=308, start_offset=308, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=165, arg=2, argval=2, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, is_jump_target=True, positions=None), - Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=314, start_offset=314, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=316, start_offset=316, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=318, start_offset=318, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=123, arg=26, argval=272, argrepr='to 272', offset=320, start_offset=320, starts_line=False, line_number=25, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=103, arg=3, argval=3, argrepr='', offset=324, start_offset=324, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=165, arg=1, argval=1, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=45, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=332, start_offset=332, starts_line=True, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='CHECK_EXC_MATCH', opcode=20, arg=None, argval=None, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=15, argval=376, argrepr='to 376', offset=344, start_offset=344, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=346, start_offset=346, starts_line=False, line_number=22, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=348, start_offset=348, starts_line=True, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=358, start_offset=358, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=360, start_offset=360, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=368, start_offset=368, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=370, start_offset=370, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='JUMP_BACKWARD', opcode=123, arg=52, argval=272, argrepr='to 272', offset=372, start_offset=372, starts_line=False, line_number=23, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=165, arg=0, argval=0, argrepr='', offset=376, start_offset=376, starts_line=True, line_number=22, is_jump_target=True, positions=None), - Instruction(opname='COPY', opcode=103, arg=3, argval=3, argrepr='', offset=378, start_offset=378, starts_line=True, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=380, start_offset=380, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=165, arg=1, argval=1, argrepr='', offset=382, start_offset=382, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='PUSH_EXC_INFO', opcode=45, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=None, is_jump_target=False, positions=None), - Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=386, start_offset=386, starts_line=True, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='LOAD_CONST', opcode=142, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=396, start_offset=396, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=406, start_offset=406, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=165, arg=0, argval=0, argrepr='', offset=408, start_offset=408, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='COPY', opcode=103, arg=3, argval=3, argrepr='', offset=410, start_offset=410, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=412, start_offset=412, starts_line=False, line_number=28, is_jump_target=False, positions=None), - Instruction(opname='RERAISE', opcode=165, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=2, argval=68, argrepr='to 68', offset=60, start_offset=60, starts_line=False, line_number=5, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=123, arg=22, argval=24, argrepr='to 24', offset=64, start_offset=64, starts_line=True, line_number=6, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=68, start_offset=68, starts_line=True, line_number=7, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=3, argval=6, argrepr='6', offset=70, start_offset=70, starts_line=False, line_number=7, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_OP', opcode=97, arg=148, argval='>', argrepr='bool(>)', offset=72, start_offset=72, starts_line=False, line_number=7, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=163, arg=2, argval=84, argrepr='to 84', offset=76, start_offset=76, starts_line=False, line_number=7, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=123, arg=30, argval=24, argrepr='to 24', offset=80, start_offset=80, starts_line=False, line_number=7, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=84, start_offset=84, starts_line=True, line_number=8, is_jump_target=True, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=125, arg=12, argval=112, argrepr='to 112', offset=86, start_offset=86, starts_line=False, line_number=8, is_jump_target=False, positions=None), + Instruction(opname='END_FOR', opcode=24, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, is_jump_target=True, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=90, start_offset=90, starts_line=True, line_number=10, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=100, start_offset=100, starts_line=False, line_number=10, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=102, start_offset=102, starts_line=False, line_number=10, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=110, start_offset=110, starts_line=False, line_number=10, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST_CHECK', opcode=146, arg=0, argval='i', argrepr='i', offset=112, start_offset=112, starts_line=True, line_number=11, is_jump_target=True, positions=None), + Instruction(opname='TO_BOOL', opcode=56, arg=None, argval=None, argrepr='', offset=114, start_offset=114, starts_line=False, line_number=11, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=40, argval=206, argrepr='to 206', offset=122, start_offset=122, starts_line=False, line_number=11, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=126, start_offset=126, starts_line=True, line_number=12, is_jump_target=True, positions=None), + Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=136, start_offset=136, starts_line=False, line_number=12, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=138, start_offset=138, starts_line=False, line_number=12, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=146, start_offset=146, starts_line=False, line_number=12, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=148, start_offset=148, starts_line=True, line_number=13, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=5, argval=1, argrepr='1', offset=150, start_offset=150, starts_line=False, line_number=13, is_jump_target=False, positions=None), + Instruction(opname='BINARY_OP', opcode=67, arg=23, argval=23, argrepr='-=', offset=152, start_offset=152, starts_line=False, line_number=13, is_jump_target=False, positions=None), + Instruction(opname='STORE_FAST', opcode=176, arg=0, argval='i', argrepr='i', offset=156, start_offset=156, starts_line=False, line_number=13, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=158, start_offset=158, starts_line=True, line_number=14, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=3, argval=6, argrepr='6', offset=160, start_offset=160, starts_line=False, line_number=14, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_OP', opcode=97, arg=148, argval='>', argrepr='bool(>)', offset=162, start_offset=162, starts_line=False, line_number=14, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=2, argval=174, argrepr='to 174', offset=166, start_offset=166, starts_line=False, line_number=14, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=123, arg=31, argval=112, argrepr='to 112', offset=170, start_offset=170, starts_line=True, line_number=15, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=174, start_offset=174, starts_line=True, line_number=16, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=2, argval=4, argrepr='4', offset=176, start_offset=176, starts_line=False, line_number=16, is_jump_target=False, positions=None), + Instruction(opname='COMPARE_OP', opcode=97, arg=18, argval='<', argrepr='bool(<)', offset=178, start_offset=178, starts_line=False, line_number=16, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=1, argval=188, argrepr='to 188', offset=182, start_offset=182, starts_line=False, line_number=16, is_jump_target=False, positions=None), + Instruction(opname='JUMP_FORWARD', opcode=125, arg=20, argval=228, argrepr='to 228', offset=186, start_offset=186, starts_line=True, line_number=17, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=188, start_offset=188, starts_line=True, line_number=11, is_jump_target=True, positions=None), + Instruction(opname='TO_BOOL', opcode=56, arg=None, argval=None, argrepr='', offset=190, start_offset=190, starts_line=False, line_number=11, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=2, argval=206, argrepr='to 206', offset=198, start_offset=198, starts_line=False, line_number=11, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=123, arg=40, argval=126, argrepr='to 126', offset=202, start_offset=202, starts_line=False, line_number=11, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=206, start_offset=206, starts_line=True, line_number=19, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=216, start_offset=216, starts_line=False, line_number=19, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=218, start_offset=218, starts_line=False, line_number=19, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=226, start_offset=226, starts_line=False, line_number=19, is_jump_target=False, positions=None), + Instruction(opname='NOP', opcode=42, arg=None, argval=None, argrepr='', offset=228, start_offset=228, starts_line=True, line_number=20, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=5, argval=1, argrepr='1', offset=230, start_offset=230, starts_line=True, line_number=21, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=7, argval=0, argrepr='0', offset=232, start_offset=232, starts_line=False, line_number=21, is_jump_target=False, positions=None), + Instruction(opname='BINARY_OP', opcode=67, arg=11, argval=11, argrepr='/', offset=234, start_offset=234, starts_line=False, line_number=21, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=238, start_offset=238, starts_line=False, line_number=21, is_jump_target=False, positions=None), + Instruction(opname='LOAD_FAST', opcode=144, arg=0, argval='i', argrepr='i', offset=240, start_offset=240, starts_line=True, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='BEFORE_WITH', opcode=2, arg=None, argval=None, argrepr='', offset=242, start_offset=242, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='STORE_FAST', opcode=176, arg=1, argval='dodgy', argrepr='dodgy', offset=244, start_offset=244, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=246, start_offset=246, starts_line=True, line_number=26, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=256, start_offset=256, starts_line=False, line_number=26, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=258, start_offset=258, starts_line=False, line_number=26, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=266, start_offset=266, starts_line=False, line_number=26, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=0, argval=None, argrepr='None', offset=268, start_offset=268, starts_line=True, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=0, argval=None, argrepr='None', offset=270, start_offset=270, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=0, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=2, argval=2, argrepr='', offset=274, start_offset=274, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=282, start_offset=282, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=284, start_offset=284, starts_line=True, line_number=28, is_jump_target=True, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=294, start_offset=294, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=296, start_offset=296, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=304, start_offset=304, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='RETURN_CONST', opcode=167, arg=0, argval=None, argrepr='None', offset=306, start_offset=306, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=45, arg=None, argval=None, argrepr='', offset=308, start_offset=308, starts_line=True, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='WITH_EXCEPT_START', opcode=66, arg=None, argval=None, argrepr='', offset=310, start_offset=310, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='TO_BOOL', opcode=56, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_TRUE', opcode=163, arg=1, argval=326, argrepr='to 326', offset=320, start_offset=320, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=165, arg=2, argval=2, argrepr='', offset=324, start_offset=324, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=326, start_offset=326, starts_line=False, line_number=25, is_jump_target=True, positions=None), + Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=328, start_offset=328, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=330, start_offset=330, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=332, start_offset=332, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=123, arg=27, argval=284, argrepr='to 284', offset=334, start_offset=334, starts_line=False, line_number=25, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=103, arg=3, argval=3, argrepr='', offset=338, start_offset=338, starts_line=True, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=340, start_offset=340, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=165, arg=1, argval=1, argrepr='', offset=342, start_offset=342, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=45, arg=None, argval=None, argrepr='', offset=344, start_offset=344, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=346, start_offset=346, starts_line=True, line_number=22, is_jump_target=False, positions=None), + Instruction(opname='CHECK_EXC_MATCH', opcode=20, arg=None, argval=None, argrepr='', offset=356, start_offset=356, starts_line=False, line_number=22, is_jump_target=False, positions=None), + Instruction(opname='POP_JUMP_IF_FALSE', opcode=160, arg=15, argval=392, argrepr='to 392', offset=358, start_offset=358, starts_line=False, line_number=22, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=362, start_offset=362, starts_line=False, line_number=22, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=364, start_offset=364, starts_line=True, line_number=23, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=374, start_offset=374, starts_line=False, line_number=23, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=376, start_offset=376, starts_line=False, line_number=23, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=384, start_offset=384, starts_line=False, line_number=23, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=386, start_offset=386, starts_line=False, line_number=23, is_jump_target=False, positions=None), + Instruction(opname='JUMP_BACKWARD', opcode=123, arg=54, argval=284, argrepr='to 284', offset=388, start_offset=388, starts_line=False, line_number=23, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=165, arg=0, argval=0, argrepr='', offset=392, start_offset=392, starts_line=True, line_number=22, is_jump_target=True, positions=None), + Instruction(opname='COPY', opcode=103, arg=3, argval=3, argrepr='', offset=394, start_offset=394, starts_line=True, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=396, start_offset=396, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=165, arg=1, argval=1, argrepr='', offset=398, start_offset=398, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='PUSH_EXC_INFO', opcode=45, arg=None, argval=None, argrepr='', offset=400, start_offset=400, starts_line=False, line_number=None, is_jump_target=False, positions=None), + Instruction(opname='LOAD_GLOBAL', opcode=150, arg=3, argval='print', argrepr='print + NULL', offset=402, start_offset=402, starts_line=True, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='LOAD_CONST', opcode=142, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=412, start_offset=412, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='CALL', opcode=75, arg=1, argval=1, argrepr='', offset=414, start_offset=414, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='POP_TOP', opcode=44, arg=None, argval=None, argrepr='', offset=422, start_offset=422, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=165, arg=0, argval=0, argrepr='', offset=424, start_offset=424, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='COPY', opcode=103, arg=3, argval=3, argrepr='', offset=426, start_offset=426, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='POP_EXCEPT', opcode=43, arg=None, argval=None, argrepr='', offset=428, start_offset=428, starts_line=False, line_number=28, is_jump_target=False, positions=None), + Instruction(opname='RERAISE', opcode=165, arg=1, argval=1, argrepr='', offset=430, start_offset=430, starts_line=False, line_number=28, is_jump_target=False, positions=None), ] # One last piece of inspect fodder to check the default line number handling @@ -2026,6 +2026,7 @@ def test_start_offset(self): opcode.opmap["EXTENDED_ARG"], 0x01, opcode.opmap["EXTENDED_ARG"], 0x01, opcode.opmap["POP_JUMP_IF_TRUE"], 0xFF, + opcode.opmap["CACHE"], 0x00, ]) jump = list(dis._get_instructions_bytes(code))[-1] self.assertEqual(8, jump.offset) @@ -2035,18 +2036,20 @@ def test_start_offset(self): opcode.opmap["LOAD_FAST"], 0x00, opcode.opmap["EXTENDED_ARG"], 0x01, opcode.opmap["POP_JUMP_IF_TRUE"], 0xFF, + opcode.opmap["CACHE"], 0x00, opcode.opmap["EXTENDED_ARG"], 0x01, opcode.opmap["EXTENDED_ARG"], 0x01, opcode.opmap["EXTENDED_ARG"], 0x01, opcode.opmap["POP_JUMP_IF_TRUE"], 0xFF, + opcode.opmap["CACHE"], 0x00, ]) instructions = list(dis._get_instructions_bytes(code)) # 1st jump self.assertEqual(4, instructions[2].offset) self.assertEqual(2, instructions[2].start_offset) # 2nd jump - self.assertEqual(12, instructions[6].offset) - self.assertEqual(6, instructions[6].start_offset) + self.assertEqual(14, instructions[6].offset) + self.assertEqual(8, instructions[6].start_offset) def test_cache_offset_and_end_offset(self): code = bytes([ From 072bb3833807b51a38941904efb3f085c196a1f1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 6 Sep 2023 17:45:55 -0700 Subject: [PATCH 04/15] Fix test_monitoring --- Lib/test/test_monitoring.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py index 95fe8d03b0b29a..8732945d52f4dc 100644 --- a/Lib/test/test_monitoring.py +++ b/Lib/test/test_monitoring.py @@ -1348,10 +1348,10 @@ def func(): self.check_events(func, recorders = JUMP_AND_BRANCH_RECORDERS, expected = [ ('branch', 'func', 2, 2), - ('branch', 'func', 3, 6), + ('branch', 'func', 3, 4), ('jump', 'func', 6, 2), ('branch', 'func', 2, 2), - ('branch', 'func', 3, 4), + ('branch', 'func', 3, 3), ('jump', 'func', 4, 2), ('branch', 'func', 2, 2)]) @@ -1361,13 +1361,13 @@ def func(): ('line', 'func', 2), ('branch', 'func', 2, 2), ('line', 'func', 3), - ('branch', 'func', 3, 6), + ('branch', 'func', 3, 4), ('line', 'func', 6), ('jump', 'func', 6, 2), ('line', 'func', 2), ('branch', 'func', 2, 2), ('line', 'func', 3), - ('branch', 'func', 3, 4), + ('branch', 'func', 3, 3), ('line', 'func', 4), ('jump', 'func', 4, 2), ('line', 'func', 2), @@ -1400,8 +1400,8 @@ def func(): ('line', 'func', 5), ('line', 'meth', 1), ('jump', 'func', 5, 5), - ('jump', 'func', 5, '[offset=112]'), - ('branch', 'func', '[offset=118]', '[offset=120]'), + ('jump', 'func', 5, '[offset=114]'), + ('branch', 'func', '[offset=120]', '[offset=122]'), ('line', 'get_events', 11)]) self.check_events(func, recorders = FLOW_AND_LINE_RECORDERS, expected = [ @@ -1416,8 +1416,8 @@ def func(): ('line', 'meth', 1), ('return', None), ('jump', 'func', 5, 5), - ('jump', 'func', 5, '[offset=112]'), - ('branch', 'func', '[offset=118]', '[offset=120]'), + ('jump', 'func', 5, '[offset=114]'), + ('branch', 'func', '[offset=120]', '[offset=122]'), ('return', None), ('line', 'get_events', 11)]) From 896ae53bd04615c768d46a04487c358477807409 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 6 Sep 2023 21:12:43 -0700 Subject: [PATCH 05/15] Include pycore_bitutils.h in instrumentation.c --- Include/internal/pycore_instruments.h | 1 - Python/instrumentation.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_instruments.h b/Include/internal/pycore_instruments.h index fad475c3dafb99..97dcfb9f8672f7 100644 --- a/Include/internal/pycore_instruments.h +++ b/Include/internal/pycore_instruments.h @@ -5,7 +5,6 @@ # error "this header requires Py_BUILD_CORE define" #endif -#include "pycore_bitutils.h" // _Py_popcount32 #include "pycore_frame.h" // _PyInterpreterFrame #ifdef __cplusplus diff --git a/Python/instrumentation.c b/Python/instrumentation.c index 9065043f55d8a7..810bf395515a00 100644 --- a/Python/instrumentation.c +++ b/Python/instrumentation.c @@ -2,6 +2,7 @@ #include "opcode_ids.h" +#include "pycore_bitutils.h" // _Py_popcount32 #include "pycore_call.h" #include "pycore_frame.h" #include "pycore_interp.h" From 0eb5b909fa9a4d5342ae50f6c06a980681d4e275 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Sep 2023 10:14:11 -0700 Subject: [PATCH 06/15] Follow likely jumps in trace --- Python/optimizer.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 8aaf9f9fd7cb14..a58d28a243b1cd 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1,6 +1,7 @@ #include "Python.h" #include "opcode.h" #include "pycore_interp.h" +#include "pycore_bitutils.h" // _Py_popcount32() #include "pycore_opcode_metadata.h" // _PyOpcode_OpName() #include "pycore_opcode_utils.h" // MAX_REAL_OPCODE #include "pycore_optimizer.h" // _Py_uop_analyze_and_optimize() @@ -500,7 +501,7 @@ translate_bytecode_to_trace( code->co_firstlineno, 2 * INSTR_IP(initial_instr, code)); -top: // Jump here after _PUSH_FRAME +top: // Jump here after _PUSH_FRAME or likely branches for (;;) { RESERVE_RAW(2, "epilogue"); // Always need space for SAVE_IP and EXIT_TRACE ADD_TO_TRACE(SAVE_IP, INSTR_IP(instr, code), 0); @@ -546,16 +547,26 @@ translate_bytecode_to_trace( case POP_JUMP_IF_TRUE: { pop_jump_if_bool: - // Assume jump unlikely (TODO: handle jump likely case) RESERVE(1, 2); - _Py_CODEUNIT *target_instr = - instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]] + oparg; max_length -= 2; // Really the start of the stubs - uint32_t uopcode = opcode == POP_JUMP_IF_TRUE ? + int counter = instr[1].cache; + int bitcount = _Py_popcount32(counter); + bool jump_likely = bitcount > 8; + bool jump_sense = opcode == POP_JUMP_IF_TRUE; + uint32_t uopcode = jump_sense ^ jump_likely ? _POP_JUMP_IF_TRUE : _POP_JUMP_IF_FALSE; + _Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; + _Py_CODEUNIT *target_instr = next_instr + oparg; + _Py_CODEUNIT *stub_target = jump_likely ? next_instr : target_instr; ADD_TO_TRACE(uopcode, max_length, 0); - ADD_TO_STUB(max_length, SAVE_IP, INSTR_IP(target_instr, code), 0); + ADD_TO_STUB(max_length, SAVE_IP, INSTR_IP(stub_target, code), 0); ADD_TO_STUB(max_length + 1, EXIT_TRACE, 0, 0); + if (jump_likely) { + DPRINTF(2, "Jump likely (%x = %d bits), continue at byte offset %d\n", + instr[1].cache, bitcount, 2 * INSTR_IP(target_instr, code)); + instr = target_instr; + goto top; + } break; } From a9c0805c4fe15be91109c92c734a1604bd9fcbf6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Sep 2023 15:01:23 -0700 Subject: [PATCH 07/15] Require 16 iterations before optimizing This is needed so branch prediction can work. --- Python/optimizer.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 713ed10651733b..8eca37a7e4a283 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -559,6 +559,9 @@ translate_bytecode_to_trace( _Py_CODEUNIT *next_instr = instr + 1 + _PyOpcode_Caches[_PyOpcode_Deopt[opcode]]; _Py_CODEUNIT *target_instr = next_instr + oparg; _Py_CODEUNIT *stub_target = jump_likely ? next_instr : target_instr; + DPRINTF(4, "%s(%d): counter=%x, bitcount=%d, likely=%d, sense=%d, uopcode=%s\n", + uop_name(opcode), oparg, + counter, bitcount, jump_likely, jump_sense, uop_name(uopcode)); ADD_TO_TRACE(uopcode, max_length, 0); ADD_TO_STUB(max_length, SAVE_IP, INSTR_IP(stub_target, code), 0); ADD_TO_STUB(max_length + 1, EXIT_TRACE, 0, 0); @@ -938,6 +941,6 @@ PyUnstable_Optimizer_NewUOpOptimizer(void) opt->resume_threshold = UINT16_MAX; // Need at least 3 iterations to settle specializations. // A few lower bits of the counter are reserved for other flags. - opt->backedge_threshold = 3 << OPTIMIZER_BITS_IN_COUNTER; + opt->backedge_threshold = 16 << OPTIMIZER_BITS_IN_COUNTER; return (PyObject *)opt; } From 7dfb94c2bdea4c0736b89c21c540ce883c711209 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Sep 2023 15:03:49 -0700 Subject: [PATCH 08/15] Fix existing uops tests --- Lib/test/test_capi/test_misc.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 004ce397696556..b5284b6fdd4f44 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2455,7 +2455,7 @@ def testfunc(x): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(10) + testfunc(20) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2470,7 +2470,7 @@ def testfunc(n): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(10) + testfunc(20) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2485,7 +2485,7 @@ def testfunc(a): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(range(10)) + testfunc(range(20)) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2495,12 +2495,13 @@ def testfunc(a): def test_pop_jump_if_not_none(self): def testfunc(a): for x in a: + x = None if x is not None: x = 0 opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(range(10)) + testfunc(range(20)) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2515,7 +2516,7 @@ def testfunc(n): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(10) + testfunc(20) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2530,7 +2531,7 @@ def testfunc(n): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(10) + testfunc(20) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2550,7 +2551,7 @@ def testfunc(n): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(10) + testfunc(20) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2568,8 +2569,8 @@ def testfunc(n): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - total = testfunc(10) - self.assertEqual(total, 45) + total = testfunc(20) + self.assertEqual(total, 190) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2589,9 +2590,9 @@ def testfunc(a): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - a = list(range(10)) + a = list(range(20)) total = testfunc(a) - self.assertEqual(total, 45) + self.assertEqual(total, 190) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2611,9 +2612,9 @@ def testfunc(a): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - a = tuple(range(10)) + a = tuple(range(20)) total = testfunc(a) - self.assertEqual(total, 45) + self.assertEqual(total, 190) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2647,7 +2648,7 @@ def dummy(x): opt = _testinternalcapi.get_uop_optimizer() with temporary_optimizer(opt): - testfunc(10) + testfunc(20) ex = get_first_executor(testfunc) self.assertIsNotNone(ex) @@ -2656,6 +2657,5 @@ def dummy(x): self.assertIn("_BINARY_OP_ADD_INT", uops) - if __name__ == "__main__": unittest.main() From 73eb60ff251ea8b18a206ed750e69dd05c8c9de1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Sep 2023 15:04:13 -0700 Subject: [PATCH 09/15] Add test for branch prediction --- Lib/test/test_capi/test_misc.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index b5284b6fdd4f44..964886ad1ca0d8 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -2656,6 +2656,23 @@ def dummy(x): self.assertIn("_PUSH_FRAME", uops) self.assertIn("_BINARY_OP_ADD_INT", uops) + def test_branch_taken(self): + def testfunc(n): + for i in range(n): + if i < 0: + i = 0 + else: + i = 1 + + opt = _testinternalcapi.get_uop_optimizer() + with temporary_optimizer(opt): + testfunc(20) + + ex = get_first_executor(testfunc) + self.assertIsNotNone(ex) + uops = {opname for opname, _, _ in ex} + self.assertIn("_POP_JUMP_IF_TRUE", uops) + if __name__ == "__main__": unittest.main() From fbd322a9fab382ea094e2006fad2849b4e940fe6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Sep 2023 15:16:38 -0700 Subject: [PATCH 10/15] Initialize POP_JUMP_IF* counters to 0x5555 --- Python/specialize.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Python/specialize.c b/Python/specialize.c index 8b4aac2f890930..91243419ec6fb0 100644 --- a/Python/specialize.c +++ b/Python/specialize.c @@ -338,9 +338,23 @@ _PyCode_Quicken(PyCodeObject *code) assert(opcode < MIN_INSTRUMENTED_OPCODE); int caches = _PyOpcode_Caches[opcode]; if (caches) { - // JUMP_BACKWARD counter counts up from 0 until it is > backedge_threshold - instructions[i + 1].cache = - opcode == JUMP_BACKWARD ? 0 : adaptive_counter_warmup(); + // The initial value depends on the opcode + int initial_value; + switch (opcode) { + case JUMP_BACKWARD: + initial_value = 0; + break; + case POP_JUMP_IF_FALSE: + case POP_JUMP_IF_TRUE: + case POP_JUMP_IF_NONE: + case POP_JUMP_IF_NOT_NONE: + initial_value = 0x5555; // Alternating 0, 1 bits + break; + default: + initial_value = adaptive_counter_warmup(); + break; + } + instructions[i + 1].cache = initial_value; i += caches; } } From ed045d74f9f54b85ce2e2386f4716ebb78a11c8f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Sep 2023 17:10:10 -0700 Subject: [PATCH 11/15] Update counter in INSTRUMENTED_POP_JUMP_IF_FALSE Alas, this goes untested (how to test it?). In INSTRUMENTED_POP_JUMP_IF_NOT_NONE, rename flag to nflag to emphasise that its sense is reversed (this is the only op that jumps if the flag is false, because there's no Py_IsNotNone() function). (Alternatively, we could have changed the sense of the flag, but that would have been more work.) --- Python/bytecodes.c | 9 ++++++--- Python/generated_cases.c.h | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 838499a4bf069c..ab275734863bd2 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -3751,6 +3751,9 @@ dummy_func( _Py_CODEUNIT *here = next_instr - 1; int flag = Py_IsFalse(cond); int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } @@ -3776,8 +3779,8 @@ dummy_func( PyObject *value = POP(); _Py_CODEUNIT *here = next_instr-1; int offset; - int flag = Py_IsNone(value); - if (flag) { + int nflag = Py_IsNone(value); + if (nflag) { offset = 0; } else { @@ -3785,7 +3788,7 @@ dummy_func( offset = oparg; } #if ENABLE_SPECIALIZATION - next_instr->cache = (next_instr->cache << 1) | !flag; + next_instr->cache = (next_instr->cache << 1) | !nflag; #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index a0487a2e951eb4..0a37c8367f3688 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4970,6 +4970,9 @@ _Py_CODEUNIT *here = next_instr - 1; int flag = Py_IsFalse(cond); int offset = flag * oparg; + #if ENABLE_SPECIALIZATION + next_instr->cache = (next_instr->cache << 1) | flag; + #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); next_instr += 1; DISPATCH(); @@ -4999,8 +5002,8 @@ PyObject *value = POP(); _Py_CODEUNIT *here = next_instr-1; int offset; - int flag = Py_IsNone(value); - if (flag) { + int nflag = Py_IsNone(value); + if (nflag) { offset = 0; } else { @@ -5008,7 +5011,7 @@ offset = oparg; } #if ENABLE_SPECIALIZATION - next_instr->cache = (next_instr->cache << 1) | !flag; + next_instr->cache = (next_instr->cache << 1) | !nflag; #endif INSTRUMENTED_JUMP(here, next_instr + offset, PY_MONITORING_EVENT_BRANCH); next_instr += 1; From 1850988c476d04a4b6129b9e8161f86a4c72a0d4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Sep 2023 17:21:32 -0700 Subject: [PATCH 12/15] Simplify writing of _PyOpcode_Caches --- Include/internal/pycore_opcode_metadata.h | 6 +++--- Tools/cases_generator/generate_cases.py | 9 +++------ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 20697000c9c1a0..1100805640396b 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -1899,7 +1899,6 @@ extern const uint8_t _PyOpcode_Caches[256]; #ifdef NEED_OPCODE_METADATA const uint8_t _PyOpcode_Caches[256] = { [TO_BOOL] = 3, - [BINARY_OP] = 1, [BINARY_SUBSCR] = 1, [STORE_SUBSCR] = 1, [SEND] = 1, @@ -1909,10 +1908,11 @@ const uint8_t _PyOpcode_Caches[256] = { [LOAD_SUPER_ATTR] = 1, [LOAD_ATTR] = 9, [COMPARE_OP] = 1, - [FOR_ITER] = 1, - [CALL] = 3, [POP_JUMP_IF_FALSE] = 1, [POP_JUMP_IF_TRUE] = 1, + [FOR_ITER] = 1, + [CALL] = 3, + [BINARY_OP] = 1, [POP_JUMP_IF_NONE] = 1, [POP_JUMP_IF_NOT_NONE] = 1, [JUMP_BACKWARD] = 1, diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py index 6842934c58f819..ad4a99931d9af6 100644 --- a/Tools/cases_generator/generate_cases.py +++ b/Tools/cases_generator/generate_cases.py @@ -550,15 +550,12 @@ def write_metadata(self, metadata_filename: str, pymetadata_filename: str) -> No ";", ): family_member_names: set[str] = set() - for name, family in self.families.items(): + for family in self.families.values(): family_member_names.update(family.members) - instr = self.instrs[name] - if instr.cache_offset > 0: - self.out.emit(f"[{name}] = {instr.cache_offset},") for instr in self.instrs.values(): if ( - not instr.family - and instr.cache_offset + instr.name not in family_member_names + and instr.cache_offset > 0 and instr.kind == "inst" and not instr.name.startswith("INSTRUMENTED_") ): From 4f1684c65ca1a91d98af8c052247f8141f2d32ff Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 8 Sep 2023 09:18:18 -0700 Subject: [PATCH 13/15] Fix test_huntrleaks under -Xuops This time by disabling the optimizer. --- Lib/test/support/__init__.py | 17 +++++++++++++++++ Lib/test/test_regrtest.py | 7 ++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 38ad965e155302..baf75b35044425 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -62,6 +62,7 @@ "LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT", "Py_DEBUG", "EXCEEDS_RECURSION_LIMIT", "C_RECURSION_LIMIT", "skip_on_s390x", + "without_optimizer", ] @@ -2538,3 +2539,19 @@ def adjust_int_max_str_digits(max_digits): 'skipped on s390x') Py_TRACE_REFS = hasattr(sys, 'getobjects') + +# Decorator to disable optimizer while a function run +def without_optimizer(func): + try: + import _testinternalcapi + except ImportError: + return func + @functools.wraps(func) + def wrapper(*args, **kwargs): + save_opt = _testinternalcapi.get_optimizer() + try: + _testinternalcapi.set_optimizer(None) + return func(*args, **kwargs) + finally: + _testinternalcapi.set_optimizer(save_opt) + return wrapper diff --git a/Lib/test/test_regrtest.py b/Lib/test/test_regrtest.py index aff5404408f8d0..8cc714193c84b6 100644 --- a/Lib/test/test_regrtest.py +++ b/Lib/test/test_regrtest.py @@ -20,7 +20,7 @@ import unittest from test import libregrtest from test import support -from test.support import os_helper, TestStats +from test.support import os_helper, TestStats, without_optimizer from test.libregrtest import utils, setup from test.libregrtest.runtest import normalize_test_name @@ -1018,17 +1018,18 @@ def test_run(self): stats=TestStats(4, 1), forever=True) + @without_optimizer def check_leak(self, code, what): test = self.create_test('huntrleaks', code=code) filename = 'reflog.txt' self.addCleanup(os_helper.unlink, filename) - output = self.run_tests('--huntrleaks', '6:3:', test, + output = self.run_tests('--huntrleaks', '3:3:', test, exitcode=EXITCODE_BAD_TEST, stderr=subprocess.STDOUT) self.check_executed_tests(output, [test], failed=test, stats=1) - line = 'beginning 9 repetitions\n123456789\n.........\n' + line = 'beginning 6 repetitions\n123456\n......\n' self.check_line(output, re.escape(line)) line2 = '%s leaked [1, 1, 1] %s, sum=3\n' % (test, what) From cb2cf1227f3112375a0d25a327089e92f969be51 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 8 Sep 2023 09:39:13 -0700 Subject: [PATCH 14/15] Fix test_dis under -Xuops --- Lib/test/test_dis.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py index 829577e6eca4e6..c3ad105fb2a3b5 100644 --- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -1273,7 +1273,8 @@ def test_loop_quicken(self): got = self.get_disassembly(loop_test, adaptive=True) expected = dis_loop_test_quickened_code if _testinternalcapi.get_optimizer(): - expected = expected.replace("JUMP_BACKWARD ", "ENTER_EXECUTOR") + # We *may* see ENTER_EXECUTOR in the disassembly + got = got.replace("ENTER_EXECUTOR", "JUMP_BACKWARD ") self.do_disassembly_compare(got, expected) @cpython_only From 41463a547ecd46d451b9d7985738b7079db90d80 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 11 Sep 2023 10:54:41 -0700 Subject: [PATCH 15/15] Update magic number --- Lib/importlib/_bootstrap_external.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 7a83a58d0874df..843af300500169 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -456,6 +456,7 @@ def _write_atomic(path, data, mode=0o666): # Python 3.13a1 3558 (Reorder the stack items for CALL) # Python 3.13a1 3559 (Generate opcode IDs from bytecodes.c) # Python 3.13a1 3560 (Add RESUME_CHECK instruction) +# Python 3.13a1 3561 (Add cache entry to branch instructions) # Python 3.14 will start with 3600 @@ -472,7 +473,7 @@ def _write_atomic(path, data, mode=0o666): # Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array # in PC/launcher.c must also be updated. -MAGIC_NUMBER = (3560).to_bytes(2, 'little') + b'\r\n' +MAGIC_NUMBER = (3561).to_bytes(2, 'little') + b'\r\n' _RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c