Files
RustPython/tools/opcode_metadata/generate_py_opcode_metadata.py
2026-05-27 16:44:58 +09:00

108 lines
2.7 KiB
Python

"""
Generate Lib/_opcode_metadata.py for RustPython bytecode.
This file generates opcode metadata that is compatible with CPython 3.14.
"""
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",
}
INDENT = " " * 4
INDENT2 = INDENT * 2
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 (CPython 3.14 compatible opcode numbers).
# Do not edit!
""".lstrip()
)
# _specializations
out.write("\n")
out.write("_specializations = {\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, lst):
out.write(f'{INDENT2}"{item}",\n')
out.write(f"{INDENT}],\n")
out.write("}\n")
# _specialized_opmap
out.write("\n")
out.write("_specialized_opmap = {\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 = {\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()