| """Generate targets for computed goto dispatch |
| Reads the instruction definitions from bytecodes.c. |
| Writes the table to opcode_targets.h by default. |
| """ |
| |
| import argparse |
| |
| from analyzer import ( |
| Analysis, |
| analyze_files, |
| ) |
| from generators_common import ( |
| DEFAULT_INPUT, |
| ROOT, |
| ) |
| from tier1_generator import UNKNOWN_OPCODE_HANDLER |
| from cwriter import CWriter |
| |
| |
| DEFAULT_OUTPUT = ROOT / "Python/opcode_targets.h" |
| |
| |
| def write_opcode_targets(analysis: Analysis, out: CWriter) -> None: |
| """Write header file that defines the jump target table""" |
| targets = ["&&_unknown_opcode,\n"] * 256 |
| for name, op in analysis.opmap.items(): |
| if op < 256: |
| targets[op] = f"&&TARGET_{name},\n" |
| out.emit("#if !_Py_TAIL_CALL_INTERP\n") |
| out.emit("static void *opcode_targets_table[256] = {\n") |
| for target in targets: |
| out.emit(target) |
| out.emit("};\n") |
| targets = ["&&_unknown_opcode,\n"] * 256 |
| for name, op in analysis.opmap.items(): |
| if op < 256: |
| targets[op] = f"&&TARGET_TRACE_RECORD,\n" |
| out.emit("#if _Py_TIER2\n") |
| out.emit("static void *opcode_tracing_targets_table[256] = {\n") |
| for target in targets: |
| out.emit(target) |
| out.emit("};\n") |
| out.emit(f"#endif\n") |
| out.emit("#else /* _Py_TAIL_CALL_INTERP */\n") |
| |
| def function_proto(name: str) -> str: |
| return f"static PyObject *Py_PRESERVE_NONE_CC _TAIL_CALL_{name}(TAIL_CALL_PARAMS)" |
| |
| |
| def write_tailcall_dispatch_table(analysis: Analysis, out: CWriter) -> None: |
| out.emit("static py_tail_call_funcptr instruction_funcptr_handler_table[256];\n") |
| out.emit("\n") |
| out.emit("static py_tail_call_funcptr instruction_funcptr_tracing_table[256];\n") |
| out.emit("\n") |
| |
| # Emit function prototypes for labels. |
| for name in analysis.labels: |
| out.emit(f"{function_proto(name)};\n") |
| out.emit("\n") |
| |
| # Emit function prototypes for opcode handlers. |
| for name in sorted(analysis.instructions.keys()): |
| out.emit(f"{function_proto(name)};\n") |
| out.emit("\n") |
| |
| # Emit unknown opcode handler. |
| out.emit(function_proto("UNKNOWN_OPCODE")) |
| out.emit(" {\n") |
| out.emit("int opcode = next_instr->op.code;\n") |
| out.emit(UNKNOWN_OPCODE_HANDLER) |
| out.emit("}\n") |
| out.emit("\n") |
| |
| # Emit the dispatch table. |
| out.emit("static py_tail_call_funcptr instruction_funcptr_handler_table[256] = {\n") |
| for name in sorted(analysis.instructions.keys()): |
| out.emit(f"[{name}] = _TAIL_CALL_{name},\n") |
| named_values = analysis.opmap.values() |
| for rest in range(256): |
| if rest not in named_values: |
| out.emit(f"[{rest}] = _TAIL_CALL_UNKNOWN_OPCODE,\n") |
| out.emit("};\n") |
| |
| # Emit the tracing dispatch table. |
| out.emit("static py_tail_call_funcptr instruction_funcptr_tracing_table[256] = {\n") |
| for name in sorted(analysis.instructions.keys()): |
| out.emit(f"[{name}] = _TAIL_CALL_TRACE_RECORD,\n") |
| named_values = analysis.opmap.values() |
| for rest in range(256): |
| if rest not in named_values: |
| out.emit(f"[{rest}] = _TAIL_CALL_UNKNOWN_OPCODE,\n") |
| out.emit("};\n") |
| outfile.write("#endif /* _Py_TAIL_CALL_INTERP */\n") |
| |
| arg_parser = argparse.ArgumentParser( |
| description="Generate the file with dispatch targets.", |
| formatter_class=argparse.ArgumentDefaultsHelpFormatter, |
| ) |
| |
| arg_parser.add_argument( |
| "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT |
| ) |
| |
| arg_parser.add_argument( |
| "input", nargs=argparse.REMAINDER, help="Instruction definition file(s)" |
| ) |
| |
| if __name__ == "__main__": |
| args = arg_parser.parse_args() |
| if len(args.input) == 0: |
| args.input.append(DEFAULT_INPUT) |
| data = analyze_files(args.input) |
| with open(args.output, "w") as outfile: |
| out = CWriter(outfile, 0, False) |
| write_opcode_targets(data, out) |
| write_tailcall_dispatch_table(data, out) |