This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: ceval traces code differently with USE_COMPUTED_GOTOS
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.9, Python 3.8
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Mark.Shannon, ammar2, nedbat, pablogsal
Priority: normal Keywords: 3.8regression, patch

Created on 2020-08-30 22:49 by nedbat, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 22026 merged Mark.Shannon, 2020-08-31 10:39
Messages (5)
msg376135 - (view) Author: Ned Batchelder (nedbat) * (Python triager) Date: 2020-08-30 22:49
Coverage.py bug reports https://github.com/nedbat/coveragepy/issues/1022 and https://github.com/nedbat/coveragepy/issues/959 demonstrate the same Python code, with the same disassembly, executing differently.

In https://discuss.python.org/t/same-python-version-different-optimizations-on-different-os/5098, Ammar Askar said:

> For any core developer who wants to look into this, based on my preliminary research this seems to be related to opcode prediction and computed GOTOS.
> 
> If you put #define USE_COMPUTED_GOTOS 0 above https://github.com/python/cpython/blob/master/Python/ceval.c#L1033 then this issue is re-creatable on Linux/Mac.
> 
> It seems to be an issue relating to how f_lasti is updated.
msg376137 - (view) Author: Ammar Askar (ammar2) * (Python committer) Date: 2020-08-30 23:01
minimal reproducer without coverage:


import sys

def f():
    try:
        for i in []: pass
        return 1
    except:
        return 2

def tracer(frame, event, _):
    if event == 'line':
        print("executing line {}".format(frame.f_lineno))
    return tracer

sys.settrace(tracer)
f()



With computed gotos this results in:

> executing line 4
> executing line 5
> executing line 6

but without:

> executing line 4
> executing line 5
msg376140 - (view) Author: Ammar Askar (ammar2) * (Python committer) Date: 2020-08-31 03:44
So I think this is a weird edge case with a set of opcode predictions (GET_ITER -> FOR_ITER -> POP_BLOCK) going outside of a line boundary.

The disassembly of the reproducer above is:

  4           0 SETUP_FINALLY           16 (to 18)

  5           2 LOAD_CONST               1 (())
              4 GET_ITER
        >>    6 FOR_ITER                 4 (to 12)
              8 STORE_FAST               0 (i)
             10 JUMP_ABSOLUTE            6

  6     >>   12 POP_BLOCK
             14 LOAD_CONST               2 (1)
             16 RETURN_VALUE

When computed gotos are disabled and there is a tracing function, instructions 0, 2, 4, 14 and 16 hit the `fast_next_opcode` label and have a chance to be traced as line hits. Note that `maybe_call_line_trace` will only cause a line hit if the instruction that starts a line (POP_BLOCK in this case) is being executed.

When computed gotos are enabled, DISPATCH is a no-op and there is a special case for when tracing is enabled that causes every opcode to go through `fast_next_opcode`: https://github.com/python/cpython/blob/c3a651ad2544d7d1be389b63e9a4a58a92a31623/Python/ceval.c#L1054-L1059

When computed gotos are not enabled, there is no similar check for PREDICT (and might be too costly to add) causing this issue: https://github.com/python/cpython/blob/c3a651ad2544d7d1be389b63e9a4a58a92a31623/Python/ceval.c#L1131-L1141
msg376146 - (view) Author: Mark Shannon (Mark.Shannon) * (Python committer) Date: 2020-08-31 10:01
A couple of things to fix here.

Firstly, the PREDICTion of POP_BLOCK in FOR_ITER shouldn't be there. POP_BLOCK doesn't normally occur after a loop and hasn't since we removed "pseudo exceptions" from the interpreter a couple of years ago.

Secondly, there is the issue of PREDICTs skipping tracing.
Either we can make sure that no PREDICTs cross a line boundary, which seems error prone, or we add the check for tracing into the PREDICT macro, which seems more robust.
msg377659 - (view) Author: Mark Shannon (Mark.Shannon) * (Python committer) Date: 2020-09-29 09:09
New changeset 17b5be0c0a3f74141014e06a660f1b5ddb002fec by Mark Shannon in branch 'master':
bpo-41670: Remove outdated predict macro invocation. (GH-22026)
https://github.com/python/cpython/commit/17b5be0c0a3f74141014e06a660f1b5ddb002fec
History
Date User Action Args
2022-04-11 14:59:35adminsetgithub: 85836
2021-04-02 15:50:43ammar2setstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2020-09-29 09:09:30Mark.Shannonsetmessages: + msg377659
2020-08-31 10:39:47Mark.Shannonsetkeywords: + patch
stage: patch review
pull_requests: + pull_request21126
2020-08-31 10:01:17Mark.Shannonsetmessages: + msg376146
2020-08-31 03:45:05ammar2settitle: ceval traces code differently based with USE_COMPUTED_GOTOS -> ceval traces code differently with USE_COMPUTED_GOTOS
2020-08-31 03:44:47ammar2setmessages: + msg376140
title: Windows and Linux execute the same code differently -> ceval traces code differently based with USE_COMPUTED_GOTOS
2020-08-31 03:31:35pablogsalsetnosy: + Mark.Shannon, pablogsal
2020-08-30 23:01:26ammar2setnosy: + ammar2
messages: + msg376137
2020-08-30 22:49:31nedbatcreate