1"""Generate opcode metadata for Python. 2Reads the instruction definitions from bytecodes.c. 3Writes the metadata to _opcode_metadata.py by default. 4""" 5 6import argparse 7 8from analyzer import ( 9 Analysis, 10 analyze_files, 11) 12from generators_common import ( 13 DEFAULT_INPUT, 14 ROOT, 15 root_relative_path, 16 write_header, 17) 18from cwriter import CWriter 19from typing import TextIO 20 21 22 23DEFAULT_OUTPUT = ROOT / "Lib/_opcode_metadata.py" 24 25 26def get_specialized(analysis: Analysis) -> set[str]: 27 specialized: set[str] = set() 28 for family in analysis.families.values(): 29 for member in family.members: 30 specialized.add(member.name) 31 return specialized 32 33 34def generate_specializations(analysis: Analysis, out: CWriter) -> None: 35 out.emit("_specializations = {\n") 36 for family in analysis.families.values(): 37 out.emit(f'"{family.name}": [\n') 38 for member in family.members: 39 out.emit(f' "{member.name}",\n') 40 out.emit("],\n") 41 out.emit("}\n\n") 42 43 44def generate_specialized_opmap(analysis: Analysis, out: CWriter) -> None: 45 out.emit("_specialized_opmap = {\n") 46 names = [] 47 for family in analysis.families.values(): 48 for member in family.members: 49 if member.name == family.name: 50 continue 51 names.append(member.name) 52 for name in sorted(names): 53 out.emit(f"'{name}': {analysis.opmap[name]},\n") 54 out.emit("}\n\n") 55 56 57def generate_opmap(analysis: Analysis, out: CWriter) -> None: 58 specialized = get_specialized(analysis) 59 out.emit("opmap = {\n") 60 for inst, op in analysis.opmap.items(): 61 if inst not in specialized: 62 out.emit(f"'{inst}': {analysis.opmap[inst]},\n") 63 out.emit("}\n\n") 64 65 66def generate_py_metadata( 67 filenames: list[str], analysis: Analysis, outfile: TextIO 68) -> None: 69 write_header(__file__, filenames, outfile, "#") 70 out = CWriter(outfile, 0, False) 71 generate_specializations(analysis, out) 72 generate_specialized_opmap(analysis, out) 73 generate_opmap(analysis, out) 74 out.emit(f"HAVE_ARGUMENT = {analysis.have_arg}\n") 75 out.emit(f"MIN_INSTRUMENTED_OPCODE = {analysis.min_instrumented}\n") 76 77 78arg_parser = argparse.ArgumentParser( 79 description="Generate the Python file with opcode metadata.", 80 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 81) 82 83arg_parser.add_argument( 84 "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT 85) 86 87arg_parser.add_argument( 88 "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" 89) 90 91if __name__ == "__main__": 92 args = arg_parser.parse_args() 93 if len(args.input) == 0: 94 args.input.append(DEFAULT_INPUT) 95 data = analyze_files(args.input) 96 with open(args.output, "w") as outfile: 97 generate_py_metadata(args.input, data, outfile) 98