Files
RustPython/tools/opcode_metadata/generate_py_opcode_metadata.py
Jeong, YunWonanddot 2a09ee02bd Align compiler and VM bytecode contracts with CPython 3.15 (#9105)
* Align compiler and VM with final CPython 3.15 bytecode

Reconstruct the compiler-only baseline from validated487ae47 on the current main and frozen mapping prerequisitec78d811. Preserve the full source delta while dropping obsolete prerequisite ancestry; detailed old-to-new provenance is retained in the recovery artifact.

Assisted-by: OpenAI Codex: model identifier unavailable

* Fix final script validation, exception cleanup and private cache contracts

Replay reviewed compiler CI repairs 2191a9d, 1f1fe04, a2dacaf on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure.

Assisted-by: OpenAI Codex: model identifier unavailable

* Align canonical compiler fixtures and generator frame state

Replay reviewed compiler CI repairs 476bb74 on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure.

Assisted-by: OpenAI Codex: model identifier unavailable

* Preserve code-name owners and final warning and constant-fold contracts

Replay reviewed compiler CI repairs 6482adf on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure.

Assisted-by: OpenAI Codex: model identifier unavailable

* Skip unavailable monitoring C API tests before tool registration

Replay reviewed compiler CI repairs 246c309 on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure.

Assisted-by: OpenAI Codex: model identifier unavailable

* Assert exact final constant-list iterable folding

Replay reviewed compiler CI repairs 7ecedcf on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure.

Assisted-by: OpenAI Codex: model identifier unavailable

* Align local monitoring events and exception lifecycle state with final 3.15

Replay reviewed compiler CI repairs ec206b1, 191fe58 on the frozen current prerequisite. Intermediate compile/diagnostic corrections are combined where they address the same failure.

Assisted-by: OpenAI Codex: model identifier unavailable

* Align 3.15 code objects and codegen with CPython

- Store co_consts, co_names, co_localsplusnames, co_localspluskinds,
  co_linetable, co_exceptiontable and co_name as objects on PyCode;
  build co_code and the varnames/cellvars/freevars tuples lazily in
  _co_cached, with co_code deoptimized through deopt_code.
- Move the compiler constant cache (_PyCode_ConstantKey,
  const_cache_insert) to builtins/code/const_cache.rs.
- Write every code field with w_object in marshal and pass the read
  field objects to MarshalBag::make_code through CodeFields.
- Emit RETURN_GENERATOR/POP_TOP in enter_scope and place SETUP_CLEANUP
  after them; drop the generator part of insert_prefix_instructions.
- Add PyCode::first_traceable (_co_firsttraceable) for
  _PyFrame_IsIncomplete and sys.monitoring.
- Follow the CPython DISABLE branches in sys.monitoring, the
  check_matched signature in warnings, the 3.15 % format error
  messages for str and bytes, code_returns_only_none, and the FOR_ITER
  stack analysis.
- Compute IMPORT_NAME flags with in_exception_handler.
- Replace the linetable/exceptiontable byte tests in codegen with
  dis(show_positions=True) insta snapshots in _opcode.
- Remove the opcode metadata verification tooling, byte-exact
  scripts, private cache tests and redundant snippets.
- Restore test_code.py and test_monitoring.py to v3.15.0 plus
  RUSTPYTHON markers.

Assisted-by: Claude Code:claude-opus-5-5

* Remove generator snippet checks covered by Lib/test

Drop extra_tests/snippets/generator_iterator_prefix.py. Its loop,
delegation, async, f_back and never-awaited checks are covered by
test_generators, test_coroutines, test_asyncgen, test_yield_from and
test_monitoring. Move the remaining checks into existing files:
genexpr prefix tracing and monitoring into stdlib_sys.py, and the
caller frame seen from the eager iterator into stdlib_sys_getframe.py.

Assisted-by: Claude Code:claude-opus-5-5

* Emit the generator prefix for top-level await modules

Module code compiled with PyCF_ALLOW_TOP_LEVEL_AWAIT and marked
ste_coroutine now starts with RETURN_GENERATOR; POP_TOP. The prefix and
RESUME emission move into emit_scope_prefix, shared by enter_scope and
the module entry points, as in codegen_enter_scope. Fixes
test_builtin.test_compile_top_level_await.

Assisted-by: Claude Code:claude-opus-5-5

* Allow the c_long cast in _opcode.stack_effect

c_long is i32 on Windows, where clippy reports the cast to i32 as
unnecessary.

Assisted-by: Claude Code:claude-opus-5-5

* Update malachite crates to 0.14.0

pymath requires malachite-bigint ^0, so the example projects, which
have no lock file, resolve it to 0.14.0 while the workspace pinned
0.13.0, and fail to build.

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: dot <dot@youknowone.org>
2026-10-14 22:38:42 +09:00

105 lines
2.6 KiB
Python

"""
Generate Lib/_opcode_metadata.py for RustPython bytecode.
"""
import functools
import io
import itertools
import operator
import pathlib
import typing
from opcodes import OpcodeInfo
from utils import DEFAULT_INPUT, ROOT, get_conf, to_pascal_case, to_upper_snake_case
OUT_FILE = ROOT / "Lib/_opcode_metadata.py"
# Opcodes that needs to be first, regardless of their opcode ID.
PRIORITY_OPMAP = {
"CACHE",
"RESERVED",
"RESUME",
"INSTRUMENTED_LINE",
"ENTER_EXECUTOR",
"TRACE_RECORD",
}
INDENT = " " * 4
def main():
override_conf = get_conf()
inp = DEFAULT_INPUT.read_text()
infos = tuple(OpcodeInfo.iter_infos(inp, override_conf))
opcodes = tuple(itertools.chain.from_iterable(info.opcodes for info in infos))
script_path = pathlib.Path(__file__).resolve().relative_to(ROOT).as_posix()
out = io.StringIO()
out.write(
f"""
# This file is generated by {script_path}
# for RustPython bytecode format.
# Do not edit!
""".lstrip()
)
# _specializations
out.write("_specializations = frozendict(\n")
deopts = functools.reduce(operator.ior, map(operator.attrgetter("deopts"), infos))
for key, lst in deopts.items():
key = to_upper_snake_case(key)
out.write(f"{INDENT}{key}=(\n")
for item in map(to_upper_snake_case, dict.fromkeys(lst)):
out.write(f'{INDENT * 3}"{item}",\n')
out.write(f"{INDENT}),\n")
out.write(")\n")
# _specialized_opmap
out.write("\n")
out.write("_specialized_opmap = frozendict(\n")
specialized = set(itertools.chain.from_iterable(deopts.values()))
for opcode in sorted(opcodes, key=lambda op: op.cpython_name):
if opcode.rust_name not in specialized:
continue
out.write(f"{INDENT}{opcode.cpython_name}={opcode.id},\n")
out.write(")\n")
# opmap
out.write("\n")
out.write("opmap = frozendict(\n")
key = lambda op: (op.cpython_name not in PRIORITY_OPMAP, op.id)
for opcode in sorted(opcodes, key=key):
if opcode.rust_name in specialized:
continue
out.write(f"{INDENT}{opcode.cpython_name}={opcode.id},\n")
out.write(")\n")
# min
out.write("\n")
have_argument = min(opcode.id for opcode in opcodes if opcode.have_argument) - 1
out.write(f"HAVE_ARGUMENT = {have_argument}\n")
min_instrumented = min(opcode.id for opcode in opcodes if opcode.is_instrumented)
out.write(f"MIN_INSTRUMENTED_OPCODE = {min_instrumented}\n")
# write output
generated = out.getvalue()
OUT_FILE.write_text(generated)
if __name__ == "__main__":
main()