Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 214a25d

Browse files
authored
GH-104584: Miscellaneous fixes for -Xuops (GH-106908)
1 parent 009e8f0 commit 214a25d

File tree

10 files changed

+67
-18
lines changed

10 files changed

+67
-18
lines changed

Lib/dis.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,8 @@ def get_instructions(x, *, first_line=None, show_caches=False, adaptive=False):
430430
co.co_names, co.co_consts,
431431
linestarts, line_offset,
432432
co_positions=co.co_positions(),
433-
show_caches=show_caches)
433+
show_caches=show_caches,
434+
original_code=co.co_code)
434435

435436
def _get_const_value(op, arg, co_consts):
436437
"""Helper to get the value of the const in a hasconst op.
@@ -504,7 +505,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
504505
names=None, co_consts=None,
505506
linestarts=None, line_offset=0,
506507
exception_entries=(), co_positions=None,
507-
show_caches=False):
508+
show_caches=False, original_code=None):
508509
"""Iterate over the instructions in a bytecode string.
509510
510511
Generates a sequence of Instruction namedtuples giving the details of each
@@ -513,14 +514,18 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
513514
arguments.
514515
515516
"""
517+
# Use the basic, unadaptive code for finding labels and actually walking the
518+
# bytecode, since replacements like ENTER_EXECUTOR and INSTRUMENTED_* can
519+
# mess that logic up pretty badly:
520+
original_code = original_code or code
516521
co_positions = co_positions or iter(())
517522
get_name = None if names is None else names.__getitem__
518-
labels = set(findlabels(code))
523+
labels = set(findlabels(original_code))
519524
for start, end, target, _, _ in exception_entries:
520525
for i in range(start, end):
521526
labels.add(target)
522527
starts_line = None
523-
for offset, start_offset, op, arg in _unpack_opargs(code):
528+
for offset, start_offset, op, arg in _unpack_opargs(original_code):
524529
if linestarts is not None:
525530
starts_line = linestarts.get(offset, None)
526531
if starts_line is not None:
@@ -531,6 +536,7 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
531536
positions = Positions(*next(co_positions, ()))
532537
deop = _deoptop(op)
533538
caches = _inline_cache_entries[deop]
539+
op = code[offset]
534540
if arg is not None:
535541
# Set argval to the dereferenced value of the argument when
536542
# available, and argrepr to the string representation of argval.
@@ -591,7 +597,6 @@ def _get_instructions_bytes(code, varname_from_oparg=None,
591597
yield Instruction(_all_opname[op], op,
592598
arg, argval, argrepr,
593599
offset, start_offset, starts_line, is_jump_target, positions)
594-
caches = _inline_cache_entries[deop]
595600
if not caches:
596601
continue
597602
if not show_caches:
@@ -622,7 +627,8 @@ def disassemble(co, lasti=-1, *, file=None, show_caches=False, adaptive=False):
622627
lasti, co._varname_from_oparg,
623628
co.co_names, co.co_consts, linestarts, file=file,
624629
exception_entries=exception_entries,
625-
co_positions=co.co_positions(), show_caches=show_caches)
630+
co_positions=co.co_positions(), show_caches=show_caches,
631+
original_code=co.co_code)
626632

627633
def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adaptive=False):
628634
disassemble(co, file=file, show_caches=show_caches, adaptive=adaptive)
@@ -640,7 +646,7 @@ def _disassemble_recursive(co, *, file=None, depth=None, show_caches=False, adap
640646
def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
641647
names=None, co_consts=None, linestarts=None,
642648
*, file=None, line_offset=0, exception_entries=(),
643-
co_positions=None, show_caches=False):
649+
co_positions=None, show_caches=False, original_code=None):
644650
# Omit the line number column entirely if we have no line number info
645651
show_lineno = bool(linestarts)
646652
if show_lineno:
@@ -661,7 +667,8 @@ def _disassemble_bytes(code, lasti=-1, varname_from_oparg=None,
661667
line_offset=line_offset,
662668
exception_entries=exception_entries,
663669
co_positions=co_positions,
664-
show_caches=show_caches):
670+
show_caches=show_caches,
671+
original_code=original_code):
665672
new_source_line = (show_lineno and
666673
instr.starts_line is not None and
667674
instr.offset > 0)
@@ -823,7 +830,8 @@ def __iter__(self):
823830
line_offset=self._line_offset,
824831
exception_entries=self.exception_entries,
825832
co_positions=co.co_positions(),
826-
show_caches=self.show_caches)
833+
show_caches=self.show_caches,
834+
original_code=co.co_code)
827835

828836
def __repr__(self):
829837
return "{}({!r})".format(self.__class__.__name__,
@@ -859,7 +867,8 @@ def dis(self):
859867
lasti=offset,
860868
exception_entries=self.exception_entries,
861869
co_positions=co.co_positions(),
862-
show_caches=self.show_caches)
870+
show_caches=self.show_caches,
871+
original_code=co.co_code)
863872
return output.getvalue()
864873

865874

Lib/test/test_capi/test_misc.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,12 +2368,16 @@ def clear_executors(func):
23682368
class TestOptimizerAPI(unittest.TestCase):
23692369

23702370
def test_get_set_optimizer(self):
2371-
self.assertEqual(_testinternalcapi.get_optimizer(), None)
2371+
old = _testinternalcapi.get_optimizer()
23722372
opt = _testinternalcapi.get_counter_optimizer()
2373-
_testinternalcapi.set_optimizer(opt)
2374-
self.assertEqual(_testinternalcapi.get_optimizer(), opt)
2375-
_testinternalcapi.set_optimizer(None)
2376-
self.assertEqual(_testinternalcapi.get_optimizer(), None)
2373+
try:
2374+
_testinternalcapi.set_optimizer(opt)
2375+
self.assertEqual(_testinternalcapi.get_optimizer(), opt)
2376+
_testinternalcapi.set_optimizer(None)
2377+
self.assertEqual(_testinternalcapi.get_optimizer(), None)
2378+
finally:
2379+
_testinternalcapi.set_optimizer(old)
2380+
23772381

23782382
def test_counter_optimizer(self):
23792383
# Generate a new function at each call

Lib/test/test_dis.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1244,10 +1244,14 @@ def test_call_specialize(self):
12441244
@cpython_only
12451245
@requires_specialization
12461246
def test_loop_quicken(self):
1247+
import _testinternalcapi
12471248
# Loop can trigger a quicken where the loop is located
12481249
self.code_quicken(loop_test, 1)
12491250
got = self.get_disassembly(loop_test, adaptive=True)
1250-
self.do_disassembly_compare(got, dis_loop_test_quickened_code)
1251+
expected = dis_loop_test_quickened_code
1252+
if _testinternalcapi.get_optimizer():
1253+
expected = expected.replace("JUMP_BACKWARD ", "ENTER_EXECUTOR")
1254+
self.do_disassembly_compare(got, expected)
12511255

12521256
@cpython_only
12531257
def test_extended_arg_quick(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix various hangs, reference leaks, test failures, and tracing/introspection
2+
bugs when running with :envvar:`PYTHONUOPS` or :option:`-X uops <-X>`
3+
enabled.

Python/bytecodes.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2182,7 +2182,14 @@ dummy_func(
21822182
JUMPBY(1-oparg);
21832183
#if ENABLE_SPECIALIZATION
21842184
here[1].cache += (1 << OPTIMIZER_BITS_IN_COUNTER);
2185-
if (here[1].cache > tstate->interp->optimizer_backedge_threshold) {
2185+
if (here[1].cache > tstate->interp->optimizer_backedge_threshold &&
2186+
// Double-check that the opcode isn't instrumented or something:
2187+
here->op.code == JUMP_BACKWARD &&
2188+
// _PyOptimizer_BackEdge is going to change frame->prev_instr,
2189+
// which breaks line event calculations:
2190+
next_instr->op.code != INSTRUMENTED_LINE
2191+
)
2192+
{
21862193
OBJECT_STAT_INC(optimization_attempts);
21872194
frame = _PyOptimizer_BackEdge(frame, here, next_instr, stack_pointer);
21882195
if (frame == NULL) {

Python/executor_cases.c.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/optimizer.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ PyUnstable_SetOptimizer(_PyOptimizerObject *optimizer)
155155
_PyInterpreterFrame *
156156
_PyOptimizer_BackEdge(_PyInterpreterFrame *frame, _Py_CODEUNIT *src, _Py_CODEUNIT *dest, PyObject **stack_pointer)
157157
{
158+
assert(src->op.code == JUMP_BACKWARD);
158159
PyCodeObject *code = (PyCodeObject *)frame->f_executable;
159160
assert(PyCode_Check(code));
160161
PyInterpreterState *interp = _PyInterpreterState_GET();

Python/pylifecycle.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,11 @@ init_interp_main(PyThreadState *tstate)
11921192
}
11931193
if (enabled) {
11941194
PyObject *opt = PyUnstable_Optimizer_NewUOpOptimizer();
1195+
if (opt == NULL) {
1196+
return _PyStatus_ERR("can't initialize optimizer");
1197+
}
11951198
PyUnstable_SetOptimizer((_PyOptimizerObject *)opt);
1199+
Py_DECREF(opt);
11961200
}
11971201
}
11981202

Tools/cases_generator/generate_cases.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,8 @@ def write_executor_instructions(self) -> None:
15061506
self.out.emit("")
15071507
with self.out.block(f"case {thing.name}:"):
15081508
instr.write(self.out, tier=TIER_TWO)
1509+
if instr.check_eval_breaker:
1510+
self.out.emit("CHECK_EVAL_BREAKER();")
15091511
self.out.emit("break;")
15101512
# elif instr.kind != "op":
15111513
# print(f"NOTE: {thing.name} is not a viable uop")

0 commit comments

Comments
 (0)