mirror of
https://github.com/python/cpython.git
synced 2025-10-10 00:43:41 +00:00
gh-98831: Use opcode metadata for stack_effect() (#101704)
* Write output and metadata in a single run This halves the time to run the cases generator (most of the time goes into parsing the input). * Declare or define opcode metadata based on NEED_OPCODE_TABLES * Use generated metadata for stack_effect() * compile.o depends on opcode_metadata.h * Return -1 from _PyOpcode_num_popped/pushed for unknown opcode
This commit is contained in:
parent
0e0c5d8baa
commit
65b7b6bd23
4 changed files with 88 additions and 256 deletions
|
@ -43,10 +43,7 @@ arg_parser.add_argument(
|
|||
"-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"-m",
|
||||
"--metadata",
|
||||
action="store_true",
|
||||
help=f"Generate metadata instead, changes output default to {DEFAULT_METADATA_OUTPUT}",
|
||||
"-m", "--metadata", type=str, help="Generated metadata", default=DEFAULT_METADATA_OUTPUT
|
||||
)
|
||||
|
||||
|
||||
|
@ -498,13 +495,15 @@ class Analyzer:
|
|||
|
||||
filename: str
|
||||
output_filename: str
|
||||
metadata_filename: str
|
||||
src: str
|
||||
errors: int = 0
|
||||
|
||||
def __init__(self, filename: str, output_filename: str):
|
||||
def __init__(self, filename: str, output_filename: str, metadata_filename: str):
|
||||
"""Read the input file."""
|
||||
self.filename = filename
|
||||
self.output_filename = output_filename
|
||||
self.metadata_filename = metadata_filename
|
||||
with open(filename) as f:
|
||||
self.src = f.read()
|
||||
|
||||
|
@ -889,21 +888,25 @@ class Analyzer:
|
|||
def write_function(
|
||||
direction: str, data: list[tuple[AnyInstruction, str]]
|
||||
) -> None:
|
||||
self.out.emit("\n#ifndef NDEBUG")
|
||||
self.out.emit("static int")
|
||||
self.out.emit("")
|
||||
self.out.emit("#ifndef NEED_OPCODE_TABLES")
|
||||
self.out.emit(f"extern int _PyOpcode_num_{direction}(int opcode, int oparg, bool jump);")
|
||||
self.out.emit("#else")
|
||||
self.out.emit("int")
|
||||
self.out.emit(f"_PyOpcode_num_{direction}(int opcode, int oparg, bool jump) {{")
|
||||
self.out.emit(" switch(opcode) {")
|
||||
for instr, effect in data:
|
||||
self.out.emit(f" case {instr.name}:")
|
||||
self.out.emit(f" return {effect};")
|
||||
self.out.emit(" default:")
|
||||
self.out.emit(" Py_UNREACHABLE();")
|
||||
self.out.emit(" return -1;")
|
||||
self.out.emit(" }")
|
||||
self.out.emit("}")
|
||||
self.out.emit("#endif")
|
||||
|
||||
write_function("popped", popped_data)
|
||||
write_function("pushed", pushed_data)
|
||||
self.out.emit("")
|
||||
|
||||
def write_metadata(self) -> None:
|
||||
"""Write instruction metadata to output file."""
|
||||
|
@ -924,7 +927,7 @@ class Analyzer:
|
|||
# Turn it into a list of enum definitions.
|
||||
format_enums = [INSTR_FMT_PREFIX + format for format in sorted(all_formats)]
|
||||
|
||||
with open(self.output_filename, "w") as f:
|
||||
with open(self.metadata_filename, "w") as f:
|
||||
# Write provenance header
|
||||
f.write(f"// This file is generated by {THIS} --metadata\n")
|
||||
f.write(f"// from {os.path.relpath(self.filename, ROOT)}\n")
|
||||
|
@ -935,7 +938,7 @@ class Analyzer:
|
|||
|
||||
self.write_stack_effect_functions()
|
||||
|
||||
# Write variable definition
|
||||
# Write type definitions
|
||||
self.out.emit("enum Direction { DIR_NONE, DIR_READ, DIR_WRITE };")
|
||||
self.out.emit(f"enum InstructionFormat {{ {', '.join(format_enums)} }};")
|
||||
self.out.emit("struct opcode_metadata {")
|
||||
|
@ -945,7 +948,14 @@ class Analyzer:
|
|||
self.out.emit("enum Direction dir_op3;")
|
||||
self.out.emit("bool valid_entry;")
|
||||
self.out.emit("enum InstructionFormat instr_format;")
|
||||
self.out.emit("} _PyOpcode_opcode_metadata[256] = {")
|
||||
self.out.emit("};")
|
||||
self.out.emit("")
|
||||
|
||||
# Write metadata array declaration
|
||||
self.out.emit("#ifndef NEED_OPCODE_TABLES")
|
||||
self.out.emit("extern const struct opcode_metadata _PyOpcode_opcode_metadata[256];")
|
||||
self.out.emit("#else")
|
||||
self.out.emit("const struct opcode_metadata _PyOpcode_opcode_metadata[256] = {")
|
||||
|
||||
# Write metadata for each instruction
|
||||
for thing in self.everything:
|
||||
|
@ -962,6 +972,7 @@ class Analyzer:
|
|||
|
||||
# Write end of array
|
||||
self.out.emit("};")
|
||||
self.out.emit("#endif")
|
||||
|
||||
def write_metadata_for_inst(self, instr: Instruction) -> None:
|
||||
"""Write metadata for a single instruction."""
|
||||
|
@ -1184,18 +1195,13 @@ def variable_used(node: parser.Node, name: str) -> bool:
|
|||
def main():
|
||||
"""Parse command line, parse input, analyze, write output."""
|
||||
args = arg_parser.parse_args() # Prints message and sys.exit(2) on error
|
||||
if args.metadata:
|
||||
if args.output == DEFAULT_OUTPUT:
|
||||
args.output = DEFAULT_METADATA_OUTPUT
|
||||
a = Analyzer(args.input, args.output) # Raises OSError if input unreadable
|
||||
a = Analyzer(args.input, args.output, args.metadata) # Raises OSError if input unreadable
|
||||
a.parse() # Raises SyntaxError on failure
|
||||
a.analyze() # Prints messages and sets a.errors on failure
|
||||
if a.errors:
|
||||
sys.exit(f"Found {a.errors} errors")
|
||||
if args.metadata:
|
||||
a.write_metadata()
|
||||
else:
|
||||
a.write_instructions() # Raises OSError if output can't be written
|
||||
a.write_instructions() # Raises OSError if output can't be written
|
||||
a.write_metadata()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue