mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			232 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """Generate the cases for the tier 2 optimizer.
 | |
| Reads the instruction definitions from bytecodes.c and optimizer_bytecodes.c
 | |
| Writes the cases to optimizer_cases.c.h, which is #included in Python/optimizer_analysis.c.
 | |
| """
 | |
| 
 | |
| import argparse
 | |
| 
 | |
| from analyzer import (
 | |
|     Analysis,
 | |
|     Instruction,
 | |
|     Uop,
 | |
|     analyze_files,
 | |
|     StackItem,
 | |
|     analysis_error,
 | |
| )
 | |
| from generators_common import (
 | |
|     DEFAULT_INPUT,
 | |
|     ROOT,
 | |
|     write_header,
 | |
|     emit_tokens,
 | |
|     replace_sync_sp,
 | |
| )
 | |
| from cwriter import CWriter
 | |
| from typing import TextIO, Iterator
 | |
| from lexer import Token
 | |
| from stack import Stack, SizeMismatch
 | |
| 
 | |
| DEFAULT_OUTPUT = ROOT / "Python/optimizer_cases.c.h"
 | |
| DEFAULT_ABSTRACT_INPUT = (ROOT / "Python/optimizer_bytecodes.c").absolute().as_posix()
 | |
| 
 | |
| 
 | |
| def validate_uop(override: Uop, uop: Uop) -> None:
 | |
|     # To do
 | |
|     pass
 | |
| 
 | |
| 
 | |
| def type_name(var: StackItem) -> str:
 | |
|     if var.is_array():
 | |
|         return f"_Py_UopsSymbol **"
 | |
|     if var.type:
 | |
|         return var.type
 | |
|     return f"_Py_UopsSymbol *"
 | |
| 
 | |
| 
 | |
| def declare_variables(uop: Uop, out: CWriter, skip_inputs: bool) -> None:
 | |
|     variables = {"unused"}
 | |
|     if not skip_inputs:
 | |
|         for var in reversed(uop.stack.inputs):
 | |
|             if var.name not in variables:
 | |
|                 variables.add(var.name)
 | |
|                 if var.condition:
 | |
|                     out.emit(f"{type_name(var)}{var.name} = NULL;\n")
 | |
|                 else:
 | |
|                     out.emit(f"{type_name(var)}{var.name};\n")
 | |
|     for var in uop.stack.outputs:
 | |
|         if var.peek:
 | |
|             continue
 | |
|         if var.name not in variables:
 | |
|             variables.add(var.name)
 | |
|             if var.condition:
 | |
|                 out.emit(f"{type_name(var)}{var.name} = NULL;\n")
 | |
|             else:
 | |
|                 out.emit(f"{type_name(var)}{var.name};\n")
 | |
| 
 | |
| 
 | |
| def decref_inputs(
 | |
|     out: CWriter,
 | |
|     tkn: Token,
 | |
|     tkn_iter: Iterator[Token],
 | |
|     uop: Uop,
 | |
|     stack: Stack,
 | |
|     inst: Instruction | None,
 | |
| ) -> None:
 | |
|     next(tkn_iter)
 | |
|     next(tkn_iter)
 | |
|     next(tkn_iter)
 | |
|     out.emit_at("", tkn)
 | |
| 
 | |
| 
 | |
| def emit_default(out: CWriter, uop: Uop) -> None:
 | |
|     for i, var in enumerate(uop.stack.outputs):
 | |
|         if var.name != "unused" and not var.peek:
 | |
|             if var.is_array():
 | |
|                 out.emit(f"for (int _i = {var.size}; --_i >= 0;) {{\n")
 | |
|                 out.emit(f"{var.name}[_i] = sym_new_not_null(ctx);\n")
 | |
|                 out.emit("}\n")
 | |
|             elif var.name == "null":
 | |
|                 out.emit(f"{var.name} = sym_new_null(ctx);\n")
 | |
|             else:
 | |
|                 out.emit(f"{var.name} = sym_new_not_null(ctx);\n")
 | |
| 
 | |
| 
 | |
| def write_uop(
 | |
|     override: Uop | None,
 | |
|     uop: Uop,
 | |
|     out: CWriter,
 | |
|     stack: Stack,
 | |
|     debug: bool,
 | |
|     skip_inputs: bool,
 | |
| ) -> None:
 | |
|     try:
 | |
|         prototype = override if override else uop
 | |
|         is_override = override is not None
 | |
|         out.start_line()
 | |
|         for var in reversed(prototype.stack.inputs):
 | |
|             res = stack.pop(var)
 | |
|             if not skip_inputs:
 | |
|                 out.emit(res)
 | |
|         if not prototype.properties.stores_sp:
 | |
|             for i, var in enumerate(prototype.stack.outputs):
 | |
|                 res = stack.push(var)
 | |
|                 if not var.peek or is_override:
 | |
|                     out.emit(res)
 | |
|         if debug:
 | |
|             args = []
 | |
|             for var in prototype.stack.inputs:
 | |
|                 if not var.peek or is_override:
 | |
|                     args.append(var.name)
 | |
|             out.emit(f'DEBUG_PRINTF({", ".join(args)});\n')
 | |
|         if override:
 | |
|             for cache in uop.caches:
 | |
|                 if cache.name != "unused":
 | |
|                     if cache.size == 4:
 | |
|                         type = cast = "PyObject *"
 | |
|                     else:
 | |
|                         type = f"uint{cache.size*16}_t "
 | |
|                         cast = f"uint{cache.size*16}_t"
 | |
|                     out.emit(f"{type}{cache.name} = ({cast})this_instr->operand;\n")
 | |
|         if override:
 | |
|             replacement_funcs = {
 | |
|                 "DECREF_INPUTS": decref_inputs,
 | |
|                 "SYNC_SP": replace_sync_sp,
 | |
|             }
 | |
|             emit_tokens(out, override, stack, None, replacement_funcs)
 | |
|         else:
 | |
|             emit_default(out, uop)
 | |
| 
 | |
|         if prototype.properties.stores_sp:
 | |
|             for i, var in enumerate(prototype.stack.outputs):
 | |
|                 if not var.peek or is_override:
 | |
|                     out.emit(stack.push(var))
 | |
|         out.start_line()
 | |
|         stack.flush(out, cast_type="_Py_UopsSymbol *")
 | |
|     except SizeMismatch as ex:
 | |
|         raise analysis_error(ex.args[0], uop.body[0])
 | |
| 
 | |
| 
 | |
| SKIPS = ("_EXTENDED_ARG",)
 | |
| 
 | |
| 
 | |
| def generate_abstract_interpreter(
 | |
|     filenames: list[str],
 | |
|     abstract: Analysis,
 | |
|     base: Analysis,
 | |
|     outfile: TextIO,
 | |
|     debug: bool,
 | |
| ) -> None:
 | |
|     write_header(__file__, filenames, outfile)
 | |
|     out = CWriter(outfile, 2, False)
 | |
|     out.emit("\n")
 | |
|     base_uop_names = set([uop.name for uop in base.uops.values()])
 | |
|     for abstract_uop_name in abstract.uops:
 | |
|         assert abstract_uop_name in base_uop_names,\
 | |
|             f"All abstract uops should override base uops, but {abstract_uop_name} is not."
 | |
| 
 | |
|     for uop in base.uops.values():
 | |
|         override: Uop | None = None
 | |
|         if uop.name in abstract.uops:
 | |
|             override = abstract.uops[uop.name]
 | |
|             validate_uop(override, uop)
 | |
|         if uop.properties.tier == 1:
 | |
|             continue
 | |
|         if uop.replicates:
 | |
|             continue
 | |
|         if uop.is_super():
 | |
|             continue
 | |
|         if not uop.is_viable():
 | |
|             out.emit(f"/* {uop.name} is not a viable micro-op for tier 2 */\n\n")
 | |
|             continue
 | |
|         out.emit(f"case {uop.name}: {{\n")
 | |
|         if override:
 | |
|             declare_variables(override, out, skip_inputs=False)
 | |
|         else:
 | |
|             declare_variables(uop, out, skip_inputs=True)
 | |
|         stack = Stack()
 | |
|         write_uop(override, uop, out, stack, debug, skip_inputs=(override is None))
 | |
|         out.start_line()
 | |
|         out.emit("break;\n")
 | |
|         out.emit("}")
 | |
|         out.emit("\n\n")
 | |
| 
 | |
| 
 | |
| def generate_tier2_abstract_from_files(
 | |
|     filenames: list[str], outfilename: str, debug: bool=False
 | |
| ) -> None:
 | |
|     assert len(filenames) == 2, "Need a base file and an abstract cases file."
 | |
|     base = analyze_files([filenames[0]])
 | |
|     abstract = analyze_files([filenames[1]])
 | |
|     with open(outfilename, "w") as outfile:
 | |
|         generate_abstract_interpreter(filenames, abstract, base, outfile, debug)
 | |
| 
 | |
| 
 | |
| arg_parser = argparse.ArgumentParser(
 | |
|     description="Generate the code for the tier 2 interpreter.",
 | |
|     formatter_class=argparse.ArgumentDefaultsHelpFormatter,
 | |
| )
 | |
| 
 | |
| arg_parser.add_argument(
 | |
|     "-o", "--output", type=str, help="Generated code", default=DEFAULT_OUTPUT
 | |
| )
 | |
| 
 | |
| 
 | |
| arg_parser.add_argument("input", nargs='*', help="Abstract interpreter definition file")
 | |
| 
 | |
| arg_parser.add_argument(
 | |
|     "base", nargs="*", help="The base instruction definition file(s)"
 | |
| )
 | |
| 
 | |
| arg_parser.add_argument("-d", "--debug", help="Insert debug calls", action="store_true")
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     args = arg_parser.parse_args()
 | |
|     if not args.input:
 | |
|         args.base.append(DEFAULT_INPUT)
 | |
|         args.input.append(DEFAULT_ABSTRACT_INPUT)
 | |
|     else:
 | |
|         args.base.append(args.input[-1])
 | |
|         args.input.pop()
 | |
|     abstract = analyze_files(args.input)
 | |
|     base = analyze_files(args.base)
 | |
|     with open(args.output, "w") as outfile:
 | |
|         generate_abstract_interpreter(args.input, abstract, base, outfile, args.debug)
 | 
