Files
RustPython/scripts/generate_opcode_metadata.py
Jeong, YunWon eee360822c Rework compiler about exception handling (#6638)
* asyncgen

* fix coroutine

* rewrite compiler

* tests

* set pyc magic number
2026-01-04 09:15:38 +09:00

247 lines
8.3 KiB
Python

"""Generate _opcode_metadata.py for RustPython bytecode.
This file generates opcode metadata that is compatible with CPython 3.13.
RustPython's Instruction enum is now ordered to match CPython opcode numbers exactly.
"""
import re
# Read the bytecode.rs file to get instruction names
with open("crates/compiler-core/src/bytecode.rs", "r") as f:
content = f.read()
# Find the Instruction enum
match = re.search(r"pub enum Instruction \{(.+?)\n\}", content, re.DOTALL)
if not match:
raise ValueError("Could not find Instruction enum")
enum_body = match.group(1)
# Extract variant names
variants = []
for line in enum_body.split("\n"):
if line.strip().startswith("///") or line.strip().startswith("//"):
continue
m = re.match(r"^\s+([A-Z][a-zA-Z0-9]*)", line)
if m:
variants.append(m.group(1))
print(f"Found {len(variants)} instruction variants")
# Map RustPython variant names to CPython-compatible names
# The opcode number is the index in the Instruction enum
name_mapping = {
# Dummy/placeholder instructions
"Cache": "CACHE",
"Reserved3": "RESERVED",
"Reserved17": "RESERVED",
"Reserved141": "RESERVED",
"Reserved142": "RESERVED",
"Reserved143": "RESERVED",
"Reserved144": "RESERVED",
"Reserved145": "RESERVED",
"Reserved146": "RESERVED",
"Reserved147": "RESERVED",
"Reserved148": "RESERVED",
"BinarySlice": "BINARY_SLICE",
"EndFor": "END_FOR",
"ExitInitCheck": "EXIT_INIT_CHECK",
"GetYieldFromIter": "GET_YIELD_FROM_ITER",
"InterpreterExit": "INTERPRETER_EXIT",
"LoadAssertionError": "LOAD_ASSERTION_ERROR",
"LoadLocals": "LOAD_LOCALS",
"PushNull": "PUSH_NULL",
"ReturnGenerator": "RETURN_GENERATOR",
"StoreSlice": "STORE_SLICE",
"UnaryInvert": "UNARY_INVERT",
"UnaryNegative": "UNARY_NEGATIVE",
"UnaryNot": "UNARY_NOT",
"BuildConstKeyMap": "BUILD_CONST_KEY_MAP",
"CopyFreeVars": "COPY_FREE_VARS",
"DictMerge": "DICT_MERGE",
"EnterExecutor": "ENTER_EXECUTOR",
"JumpBackward": "JUMP_BACKWARD",
"JumpBackwardNoInterrupt": "JUMP_BACKWARD_NO_INTERRUPT",
"JumpForward": "JUMP_FORWARD",
"ListExtend": "LIST_EXTEND",
"LoadFastCheck": "LOAD_FAST_CHECK",
"LoadFastLoadFast": "LOAD_FAST_LOAD_FAST",
"LoadFromDictOrDeref": "LOAD_FROM_DICT_OR_DEREF",
"LoadFromDictOrGlobals": "LOAD_FROM_DICT_OR_GLOBALS",
"LoadSuperAttr": "LOAD_SUPER_ATTR",
"MakeCell": "MAKE_CELL",
"PopJumpIfNone": "POP_JUMP_IF_NONE",
"PopJumpIfNotNone": "POP_JUMP_IF_NOT_NONE",
"SetUpdate": "SET_UPDATE",
"StoreFastStoreFast": "STORE_FAST_STORE_FAST",
# Real instructions
"BeforeAsyncWith": "BEFORE_ASYNC_WITH",
"BeforeWith": "BEFORE_WITH",
"BinaryOp": "BINARY_OP",
"BinarySubscript": "BINARY_SUBSCR",
"Break": "BREAK",
"BuildList": "BUILD_LIST",
"BuildListFromTuples": "BUILD_LIST_UNPACK",
"BuildMap": "BUILD_MAP",
"BuildMapForCall": "BUILD_MAP_FOR_CALL",
"BuildSet": "BUILD_SET",
"BuildSetFromTuples": "BUILD_SET_UNPACK",
"BuildSlice": "BUILD_SLICE",
"BuildString": "BUILD_STRING",
"BuildTuple": "BUILD_TUPLE",
"BuildTupleFromIter": "BUILD_TUPLE_ITER",
"BuildTupleFromTuples": "BUILD_TUPLE_UNPACK",
"CallFunctionEx": "CALL_FUNCTION_EX",
"CallFunctionKeyword": "CALL_KW",
"CallFunctionPositional": "CALL",
"CallIntrinsic1": "CALL_INTRINSIC_1",
"CallIntrinsic2": "CALL_INTRINSIC_2",
"CallMethodEx": "CALL_METHOD_EX",
"CallMethodKeyword": "CALL_METHOD_KW",
"CallMethodPositional": "CALL_METHOD",
"CheckEgMatch": "CHECK_EG_MATCH",
"CheckExcMatch": "CHECK_EXC_MATCH",
"CleanupThrow": "CLEANUP_THROW",
"CompareOperation": "COMPARE_OP",
"ContainsOp": "CONTAINS_OP",
"Continue": "CONTINUE",
"ConvertValue": "CONVERT_VALUE",
"CopyItem": "COPY",
"DeleteAttr": "DELETE_ATTR",
"DeleteDeref": "DELETE_DEREF",
"DeleteFast": "DELETE_FAST",
"DeleteGlobal": "DELETE_GLOBAL",
"DeleteLocal": "DELETE_NAME",
"DeleteSubscript": "DELETE_SUBSCR",
"DictUpdate": "DICT_UPDATE",
"EndAsyncFor": "END_ASYNC_FOR",
"EndSend": "END_SEND",
"ExtendedArg": "EXTENDED_ARG",
"ForIter": "FOR_ITER",
"FormatSimple": "FORMAT_SIMPLE",
"FormatWithSpec": "FORMAT_WITH_SPEC",
"GetAIter": "GET_AITER",
"GetANext": "GET_ANEXT",
"GetAwaitable": "GET_AWAITABLE",
"GetIter": "GET_ITER",
"GetLen": "GET_LEN",
"ImportFrom": "IMPORT_FROM",
"ImportName": "IMPORT_NAME",
"IsOp": "IS_OP",
"Jump": "JUMP",
"JumpIfFalseOrPop": "JUMP_IF_FALSE_OR_POP",
"JumpIfNotExcMatch": "JUMP_IF_NOT_EXC_MATCH",
"JumpIfTrueOrPop": "JUMP_IF_TRUE_OR_POP",
"ListAppend": "LIST_APPEND",
"LoadAttr": "LOAD_ATTR",
"LoadBuildClass": "LOAD_BUILD_CLASS",
"LoadClassDeref": "LOAD_CLASSDEREF",
"LoadClosure": "LOAD_CLOSURE",
"LoadConst": "LOAD_CONST",
"LoadDeref": "LOAD_DEREF",
"LoadFast": "LOAD_FAST",
"LoadFastAndClear": "LOAD_FAST_AND_CLEAR",
"LoadGlobal": "LOAD_GLOBAL",
"LoadMethod": "LOAD_METHOD",
"LoadNameAny": "LOAD_NAME",
"MakeFunction": "MAKE_FUNCTION",
"MapAdd": "MAP_ADD",
"MatchClass": "MATCH_CLASS",
"MatchKeys": "MATCH_KEYS",
"MatchMapping": "MATCH_MAPPING",
"MatchSequence": "MATCH_SEQUENCE",
"Nop": "NOP",
"PopBlock": "POP_BLOCK",
"PopException": "POP_EXCEPT",
"PopJumpIfFalse": "POP_JUMP_IF_FALSE",
"PopJumpIfTrue": "POP_JUMP_IF_TRUE",
"PopTop": "POP_TOP",
"PushExcInfo": "PUSH_EXC_INFO",
"Raise": "RAISE_VARARGS",
"Reraise": "RERAISE",
"Resume": "RESUME",
"ReturnConst": "RETURN_CONST",
"ReturnValue": "RETURN_VALUE",
"Reverse": "REVERSE",
"Send": "SEND",
"SetAdd": "SET_ADD",
"SetExcInfo": "SET_EXC_INFO",
"SetFunctionAttribute": "SET_FUNCTION_ATTRIBUTE",
"SetupAnnotation": "SETUP_ANNOTATIONS",
"StoreAttr": "STORE_ATTR",
"StoreDeref": "STORE_DEREF",
"StoreFast": "STORE_FAST",
"StoreFastLoadFast": "STORE_FAST_LOAD_FAST",
"StoreGlobal": "STORE_GLOBAL",
"StoreLocal": "STORE_NAME",
"StoreSubscript": "STORE_SUBSCR",
"Subscript": "SUBSCRIPT",
"Swap": "SWAP",
"ToBool": "TO_BOOL",
"UnaryOperation": "UNARY_OP",
"UnpackEx": "UNPACK_EX",
"UnpackSequence": "UNPACK_SEQUENCE",
"WithExceptStart": "WITH_EXCEPT_START",
"YieldValue": "YIELD_VALUE",
}
# Build opmap with RustPython instruction indices
opmap = {}
rust_to_cpython_name = {}
for i, variant in enumerate(variants):
cpython_name = name_mapping.get(variant, variant.upper())
# Skip adding duplicates (RESERVED appears multiple times)
if cpython_name == "RESERVED":
# Use unique names for reserved slots
cpython_name = f"RESERVED_{i}"
if cpython_name not in opmap:
opmap[cpython_name] = i
rust_to_cpython_name[variant] = cpython_name
# Find specific instruction indices for categorization
def find_opcode(cpython_name):
return opmap.get(cpython_name, -1)
# Generate the output file
output = """# This file is generated by scripts/generate_opcode_metadata.py
# for RustPython bytecode format (CPython 3.13 compatible opcode numbers).
# Do not edit!
_specializations = {}
_specialized_opmap = {}
opmap = {
"""
for name, num in sorted(opmap.items(), key=lambda x: x[1]):
output += f" '{name}': {num},\n"
output += """}
# CPython 3.13 compatible: opcodes < 44 have no argument
HAVE_ARGUMENT = 44
MIN_INSTRUMENTED_OPCODE = 236
"""
with open("Lib/_opcode_metadata.py", "w") as f:
f.write(output)
print("Generated Lib/_opcode_metadata.py")
print("\nKey opcode indices (matching CPython 3.13):")
print(f" CACHE = {find_opcode('CACHE')} (expected: 0)")
print(f" BEFORE_ASYNC_WITH = {find_opcode('BEFORE_ASYNC_WITH')} (expected: 1)")
print(f" BINARY_SUBSCR = {find_opcode('BINARY_SUBSCR')} (expected: 5)")
print(
f" WITH_EXCEPT_START = {find_opcode('WITH_EXCEPT_START')} (expected: 44, HAVE_ARGUMENT)"
)
print(f" BINARY_OP = {find_opcode('BINARY_OP')} (expected: 45)")
print(f" LOAD_CONST = {find_opcode('LOAD_CONST')} (expected: 83)")
print(f" LOAD_FAST = {find_opcode('LOAD_FAST')} (expected: 85)")
print(f" LOAD_GLOBAL = {find_opcode('LOAD_GLOBAL')} (expected: 91)")
print(f" STORE_FAST = {find_opcode('STORE_FAST')} (expected: 110)")
print(f" YIELD_VALUE = {find_opcode('YIELD_VALUE')} (expected: 118)")
print(f" RESUME = {find_opcode('RESUME')} (expected: 149)")