cpython icon indicating copy to clipboard operation
cpython copied to clipboard

Incorrect output due to Tier 2 `_LOAD_CONST_INLINE`

Open gvanrossum opened this issue 1 year ago • 2 comments

Bug report

I've constructed a sneaky way to break the remove_globals() optimization, by executing a code object in two different globals dicts. (Credit goes to @brandtbucher who started the conversation about this topic, though this particular example is mine.)

Take this code:

src = """
def f():
    global x
    def inner(i):
        a = 1
        for _ in range(100):
            a = x + i
        return a
    return inner

func = f()
"""

co = compile(src, __file__, "exec")

ns1 = {"x": 1000}
eval(co, ns1)
func1 = ns1["func"]
for i in range(1000):
    x = func1(i)
print(x)

ns2 = {"x": 2000}
eval(co, ns2)
func2 = ns2["func"]
for i in range(1000):
    x = func2(i)
print(x)

This should print

1999
2999

(try it with Python 3.12, or Python 3.13 without JIT or -Xuops).

However with current main (EDIT: when using JIT or -Xuops) it prints

1999
1999

:-(

gvanrossum avatar Mar 20 '24 00:03 gvanrossum

git bisect points its sneaky little finger at GH-116460.

A workaround would be to comment out the remove_globals() call in _Py_uop_analyze_and_optimize().

gvanrossum avatar Mar 20 '24 00:03 gvanrossum

I was thinking that another workaround would be to leave in the first _GUARD_GLOBALS_VERSION. In fact, I'm not sure why that isn't kept by remove_globals()? (The algorithm there is hard for me to follow.) Of course it ought to be moved to the top, before the start of the loop, which would require https://github.com/python/cpython/issues/117062 first.

gvanrossum avatar Mar 20 '24 17:03 gvanrossum