python / python/cpython

Tier 2 optimizer may use canonical builtins for functions with a copied __builtins__ dictionary

Đang mở
#157,468 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

interpreter-core topic-JIT type-bug
Ngôn ngữ chính
Python
Star
77.2k
Fork
35.9k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

Bug report

Bug description:

A function can use a builtins dictionary other than the interpreter’s canonical builtins dictionary. With the JIT enabled, _LOAD_GLOBAL_BUILTINS may nevertheless be constant-folded using interp->builtins.

A dictionary created by vars(builtins).copy() can share its keys table and keys version with the canonical dictionary while storing independent values. Replacing an existing value such as len does not necessarily change that keys version.

The optimizer validates and watches interp->builtins, then obtains the constant from that dictionary. It does not first verify that the optimized function’s func_builtins is the same dictionary. Consequently, an optimized executor can continue using the
canonical len after the function’s own builtins dictionary has been changed.

I reproduced this on main at commit a60343ed17785ebbcd43de9080cadd8e2541db6f. The non-JIT interpreter produces the expected result.

The direct reproducer is a regression introduced by GH-138379 and first appears in Python 3.15.0a1.
A related case involving distinct functions with the same function/code version but different builtins dictionaries dates back to GH-116460 and Python 3.13.0a5.

Minimal reproducer

import builtins
from _testinternalcapi import TIER2_THRESHOLD

namespace = {"__builtins__": vars(builtins).copy()}

exec(
    """
def size(value):
    return len(value)

def run(value, n):
    for _ in range(n):
        result = size(value)
    return result
""",
    namespace,
)

print(namespace["run"]([0], TIER2_THRESHOLD))

namespace["__builtins__"]["len"] = lambda value: 42

print(namespace["run"]([0], 8))

Run it with a JIT-enabled build:

$ PYTHON_JIT=1 ./python repro.py
1
1

Expected output:

1
42

With the JIT disabled, the expected result is produced:

$ PYTHON_JIT=0 ./python repro.py
1
42

Proposed fix

Only constant-fold _LOAD_GLOBAL_BUILTINS when the current function uses the interpreter’s canonical builtins dictionary:

ctx->frame->func != NULL &&
ctx->frame->func->func_builtins == interp->builtins

For a custom builtins dictionary, retain the ordinary _LOAD_GLOBAL_BUILTINS operation so that it reads and guards the
function’s actual mapping.

A runtime identity guard should also accompany constants folded from the canonical builtins dictionary:

DEOPT_IF(BUILTINS() != tstate->interp->builtins);

The runtime guard is needed because function or code version checks alone do not identify the function’s builtins mapping.
A distinct function created from the same code object can use a different __builtins__ dictionary while satisfying the existing version guard.

Regression tests should cover:

  1. Mutating an existing value in a copied builtins dictionary after an executor has been created.
  2. Calling a different function with the same code/version but a different builtins dictionary through an existing optimized
    executor.
CPython versions tested on:

3.15, CPython main branch

Operating systems tested on:

Linux

Linked PRs
  • gh-157766

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu bằng cách chạy reproducer tối thiểu với PYTHON_JIT=1 và so sánh với kết quả không dùng JIT. Theo dõi cách Tier 2 optimizer xử lý _LOAD_GLOBAL_BUILTINS và các runtime guard hiện có, sau đó bổ sung kiểm thử hồi quy cho cả các dictionary builtins được sao chép và các hàm khác nhau dùng chung code/version. Công việc được hoàn tất khi cả hai trường hợp đều tạo ra kết quả custom-builtin mong đợi sau khi tối ưu hóa.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
python
Lĩnh vực
compilers, testing-qa
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Đặc tả rõ ràng
Mức phù hợp với người mới
30/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.